| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | import { __ } from '@wordpress/i18n' |
| 2 | 2 | import { isAxiosError } from 'axios' |
| 3 | 3 | import React, { useCallback } from 'react' |
| 4 | +import { describeRequestError, isUnconfirmedRequest } from '../utils/errors' | |
| 4 | 5 | import { useSnippetForm } from '../components/EditMenu/SnippetForm/WithSnippetFormContext' |
| 5 | 6 | import { createSnippetObject, isCondition } from '../utils/snippets/snippets' |
| 6 | 7 | import { buildUrl } from '../utils/urls' |
| 7 | 8 | import { useSnippetsAPI } from './useSnippetsAPI' |
| @@ -17,14 +18,44 @@ | ||
| 17 | 18 | updatedActivated: __('Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets'), |
| 18 | 19 | updatedDeactivated: __('Snippet <strong>updated</strong> and <strong>deactivated</strong>'), |
| 19 | 20 | updatedExecuted: __('Snippet <strong>updated</strong> and <strong>executed</strong>.', 'code-snippets'), |
| 20 | 21 | failedCreate: __('Could not create snippet.', 'code-snippets'), |
| 21 | - failedUpdate: __('Could not update snippet.', 'code-snippets') | |
| 22 | + failedUpdate: __('Could not update snippet.', 'code-snippets'), | |
| 22 | 23 | } as const |
| 23 | 24 | |
| 24 | 25 | const conditionCreated = __('Condition <strong>created</strong>.', 'code-snippets') |
| 25 | 26 | const conditionUpdated = __('Condition <strong>updated</strong>.', 'code-snippets') |
| 26 | 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 | + | |
| 27 | 58 | const conditionMessages: typeof snippetMessages = { |
| 28 | 59 | addNew: __('Create New Condition', 'code-snippets'), |
| 29 | 60 | edit: __('Edit Condition', 'code-snippets'), |
| 30 | 61 | created: conditionCreated, |
| @@ -89,9 +120,9 @@ | ||
| 89 | 120 | } |
| 90 | 121 | |
| 91 | 122 | export const useSubmitSnippet = (): UseSubmitSnippet => { |
| 92 | 123 | const api = useSnippetsAPI() |
| 93 | - const { setIsWorking, setCurrentNotice, setSnippet } = useSnippetForm() | |
| 124 | + const { acceptSnippet, setIsWorking, setCurrentNotice } = useSnippetForm() | |
| 94 | 125 | |
| 95 | 126 | const submitSnippet: UseSubmitSnippet['submitSnippet'] = useCallback(async (snippet, action) => { |
| 96 | 127 | setCurrentNotice(undefined) |
| 97 | 128 | setIsWorking(true) |
| @@ -103,9 +134,9 @@ | ||
| 103 | 134 | } else if (SubmitSnippetAction.SAVE_AND_DEACTIVATE === action) { |
| 104 | 135 | request.active = false |
| 105 | 136 | } |
| 106 | 137 | |
| 107 | - const result = await (async (): Promise<Snippet | string | undefined> => { | |
| 138 | + const result = await (async (): Promise<Snippet | SubmitFailure> => { | |
| 108 | 139 | try { |
| 109 | 140 | const { id } = request |
| 110 | 141 | |
| 111 | 142 | const response = await (undefined === id || 0 === id |
| @@ -111,11 +142,14 @@ | ||
| 111 | 142 | const response = await (undefined === id || 0 === id |
| 112 | 143 | ? api.create(request) |
| 113 | 144 | : api.update({ ...request, id })) |
| 114 | 145 | |
| 115 | - return response.id ? createSnippetObject(response) : undefined | |
| 116 | - } catch (error) { | |
| 117 | - return isAxiosError(error) ? error.message : undefined | |
| 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 | + } | |
| 118 | 152 | } finally { |
| 119 | 153 | setIsWorking(false) |
| 120 | 154 | } |
| 121 | 155 | })() |
| @@ -121,19 +155,17 @@ | ||
| 121 | 155 | })() |
| 122 | 156 | |
| 123 | 157 | const messages = isCondition(snippet) ? conditionMessages : snippetMessages |
| 124 | 158 | |
| 125 | - if (undefined === result || 'string' === typeof result) { | |
| 126 | - const message = [ | |
| 127 | - request.id ? messages.failedUpdate : messages.failedCreate, | |
| 128 | - result ?? __('The server did not send a valid response.', 'code-snippets') | |
| 129 | - ] | |
| 130 | - | |
| 131 | - setCurrentNotice(['error', message.filter(Boolean).join(' ')]) | |
| 159 | + if (isSubmitFailure(result)) { | |
| 160 | + setCurrentNotice([ | |
| 161 | + 'error', | |
| 162 | + describeSubmitFailure({ failure: result, messages, isUpdate: Boolean(request.id) }) | |
| 163 | + ]) | |
| 132 | 164 | return undefined |
| 133 | 165 | } |
| 134 | 166 | |
| 135 | - setSnippet(result) | |
| 167 | + acceptSnippet(result) | |
| 136 | 168 | |
| 137 | 169 | if (result.code_error && SubmitSnippetAction.SAVE_AND_ACTIVATE === action) { |
| 138 | 170 | setCurrentNotice(getActivationErrorNotice(result)) |
| 139 | 171 | } else { |
| @@ -145,8 +177,8 @@ | ||
| 145 | 177 | window.history.replaceState({}, '', buildUrl(window.CODE_SNIPPETS?.urls.edit, { id: result.id })) |
| 146 | 178 | } |
| 147 | 179 | |
| 148 | 180 | return result |
| 149 | - }, [api, setIsWorking, setCurrentNotice, setSnippet]) | |
| 181 | + }, [acceptSnippet, api, setIsWorking, setCurrentNotice]) | |
| 150 | 182 | |
| 151 | 183 | return { submitSnippet } |
| 152 | 184 | } |