PluginProbe
Code Snippets / 4.0.0-beta.3
Code Snippets v4.0.0-beta.3
4.0.0-beta.3 4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 All 66 releases
code-snippets / js / hooks / useSubmitSnippet.tsx

useSubmitSnippet.tsx in Code Snippets 4.0.0-beta.3, at js/hooks/useSubmitSnippet.tsx

185 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from '@wordpress/i18n'
2 import { isAxiosError } from 'axios'
3 import React, { useCallback } from 'react'
4 import { describeRequestError, isUnconfirmedRequest } from '../utils/errors'
5 import { useSnippetForm } from '../components/EditMenu/SnippetForm/WithSnippetFormContext'
6 import { createSnippetObject, isCondition } from '../utils/snippets/snippets'
7 import { buildUrl } from '../utils/urls'
8 import { useSnippetsAPI } from './useSnippetsAPI'
9 import type { Snippet } from '../types/Snippet'
10 import type { ScreenNotice } from '../types/ScreenNotice'
11
12 const snippetMessages = {
13 addNew: __('Create New Snippet', 'code-snippets'),
14 edit: __('Edit Snippet', 'code-snippets'),
15 created: __('Snippet <strong>created</strong>.', 'code-snippets'),
16 updated: __('Snippet <strong>updated</strong>.', 'code-snippets'),
17 createdActivated: __('Snippet <strong>created</strong> and <strong>activated</strong>.', 'code-snippets'),
18 updatedActivated: __('Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets'),
19 updatedDeactivated: __('Snippet <strong>updated</strong> and <strong>deactivated</strong>'),
20 updatedExecuted: __('Snippet <strong>updated</strong> and <strong>executed</strong>.', 'code-snippets'),
21 failedCreate: __('Could not create snippet.', 'code-snippets'),
22 failedUpdate: __('Could not update snippet.', 'code-snippets'),
23 } as const
24
25 const conditionCreated = __('Condition <strong>created</strong>.', 'code-snippets')
26 const conditionUpdated = __('Condition <strong>updated</strong>.', 'code-snippets')
27
28 /** A save that did not produce a snippet, and whether its outcome is actually known. */
29 interface SubmitFailure {
30 message?: string
31 unconfirmed: boolean
32 }
33
34 const isSubmitFailure = (result: Snippet | SubmitFailure): result is SubmitFailure =>
35 'unconfirmed' in result
36
37 interface SubmitFailureNotice {
38 failure: SubmitFailure
39 messages: typeof snippetMessages
40 isUpdate: boolean
41 }
42
43 /**
44 * Word a save that did not return a snippet.
45 *
46 * A request that never received a response says nothing about whether the write
47 * happened, so it is reported on its own rather than behind a heading stating
48 * the snippet was not saved.
49 */
50 const describeSubmitFailure = ({ failure, messages, isUpdate }: SubmitFailureNotice): string =>
51 failure.unconfirmed
52 ? failure.message ?? ''
53 : [
54 isUpdate ? messages.failedUpdate : messages.failedCreate,
55 failure.message ?? __('The server did not send a valid response.', 'code-snippets')
56 ].filter(Boolean).join(' ')
57
58 const conditionMessages: typeof snippetMessages = {
59 addNew: __('Create New Condition', 'code-snippets'),
60 edit: __('Edit Condition', 'code-snippets'),
61 created: conditionCreated,
62 updated: conditionUpdated,
63 createdActivated: conditionCreated,
64 updatedActivated: conditionUpdated,
65 updatedDeactivated: conditionUpdated,
66 updatedExecuted: conditionUpdated,
67 failedCreate: __('Could not create condition.', 'code-snippets'),
68 failedUpdate: __('Could not update condition.', 'code-snippets')
69 }
70
71 export enum SubmitSnippetAction {
72 SAVE = 'save_snippet',
73 SAVE_AND_ACTIVATE = 'save_snippet_activate',
74 SAVE_AND_EXECUTE = 'save_snippet_execute',
75 SAVE_AND_DEACTIVATE = 'save_snippet_deactivate'
76 }
77
78 const getSuccessNotice = (
79 request: Partial<Snippet>,
80 response: Snippet,
81 action: SubmitSnippetAction = SubmitSnippetAction.SAVE
82 ): string => {
83 const messages = 'condition' === request.scope ? conditionMessages : snippetMessages
84 const wasCreated = 0 === request.id
85
86 switch (action) {
87 case SubmitSnippetAction.SAVE:
88 return wasCreated ? messages.created : messages.updated
89
90 case SubmitSnippetAction.SAVE_AND_EXECUTE:
91 return messages.updatedExecuted
92
93 case SubmitSnippetAction.SAVE_AND_ACTIVATE:
94 if ('single-use' === response.scope) {
95 return messages.updatedExecuted
96 } else {
97 return wasCreated
98 ? messages.createdActivated
99 : messages.updatedActivated
100 }
101
102 case SubmitSnippetAction.SAVE_AND_DEACTIVATE:
103 return messages.updatedDeactivated
104 }
105 }
106
107 const getActivationErrorNotice = (snippet: Snippet): ScreenNotice => [
108 'error',
109 __('Snippet could not be activated.', 'code-snippets'),
110 <span key="code-snippets-activation-error">
111 {__('The snippet was saved, but remains inactive due to this error:', 'code-snippets')}
112 {' '}
113 <strong>{snippet.code_error?.[0] ?? ''}</strong>
114 </span>,
115 snippet.code_error_trace ?? undefined
116 ]
117
118 export interface UseSubmitSnippet {
119 submitSnippet: (snippet: Partial<Snippet> & Pick<Snippet, 'network'>, action?: SubmitSnippetAction) => Promise<Snippet | undefined>
120 }
121
122 export const useSubmitSnippet = (): UseSubmitSnippet => {
123 const api = useSnippetsAPI()
124 const { acceptSnippet, setIsWorking, setCurrentNotice } = useSnippetForm()
125
126 const submitSnippet: UseSubmitSnippet['submitSnippet'] = useCallback(async (snippet, action) => {
127 setCurrentNotice(undefined)
128 setIsWorking(true)
129
130 const request = { ...snippet }
131
132 if (SubmitSnippetAction.SAVE_AND_ACTIVATE === action) {
133 request.active = true
134 } else if (SubmitSnippetAction.SAVE_AND_DEACTIVATE === action) {
135 request.active = false
136 }
137
138 const result = await (async (): Promise<Snippet | SubmitFailure> => {
139 try {
140 const { id } = request
141
142 const response = await (undefined === id || 0 === id
143 ? api.create(request)
144 : api.update({ ...request, id }))
145
146 return response.id ? createSnippetObject(response) : { unconfirmed: false }
147 } catch (error: unknown) {
148 return {
149 message: isAxiosError(error) ? describeRequestError(error) : undefined,
150 unconfirmed: isUnconfirmedRequest(error)
151 }
152 } finally {
153 setIsWorking(false)
154 }
155 })()
156
157 const messages = isCondition(snippet) ? conditionMessages : snippetMessages
158
159 if (isSubmitFailure(result)) {
160 setCurrentNotice([
161 'error',
162 describeSubmitFailure({ failure: result, messages, isUpdate: Boolean(request.id) })
163 ])
164 return undefined
165 }
166
167 acceptSnippet(result)
168
169 if (result.code_error && SubmitSnippetAction.SAVE_AND_ACTIVATE === action) {
170 setCurrentNotice(getActivationErrorNotice(result))
171 } else {
172 setCurrentNotice(['updated', getSuccessNotice(snippet, result, action)])
173 }
174
175 if (request.id && result.id) {
176 window.document.title = window.document.title.replace(snippetMessages.addNew, messages.edit)
177 window.history.replaceState({}, '', buildUrl(window.CODE_SNIPPETS?.urls.edit, { id: result.id }))
178 }
179
180 return result
181 }, [acceptSnippet, api, setIsWorking, setCurrentNotice])
182
183 return { submitSnippet }
184 }
185