From 3ad7ba2c09cea4c2ddf001ca8e04aa06cee65a74 Mon Sep 17 00:00:00 2001 From: Pavel Mineev Date: Thu, 6 May 2021 00:38:38 +0300 Subject: [PATCH] make form available after failed commit sending --- backend/app/rest/api/rest_private.go | 2 +- backend/app/rest/httperrors.go | 39 +++--- frontend/app/common/fetcher.ts | 12 +- .../components/comment-form/comment-form.tsx | 1 + frontend/app/utils/errorUtils.ts | 124 +++--------------- 5 files changed, 46 insertions(+), 132 deletions(-) diff --git a/backend/app/rest/api/rest_private.go b/backend/app/rest/api/rest_private.go index 8c422dd0..d2140509 100644 --- a/backend/app/rest/api/rest_private.go +++ b/backend/app/rest/api/rest_private.go @@ -101,7 +101,7 @@ func (s *private) createCommentCtrl(w http.ResponseWriter, r *http.Request) { id, err := s.dataService.Create(comment) if err == service.ErrRestrictedWordsFound { - rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "invalid comment", rest.ErrCommentValidation) + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "invalid comment", rest.ErrCommentRestrictWords) return } if err != nil { diff --git a/backend/app/rest/httperrors.go b/backend/app/rest/httperrors.go index 59cc7fc8..3a094b8e 100644 --- a/backend/app/rest/httperrors.go +++ b/backend/app/rest/httperrors.go @@ -19,25 +19,26 @@ import ( // All error codes for UI mapping and translation const ( - ErrInternal = 0 // any internal error - ErrCommentNotFound = 1 // can't find comment - ErrDecode = 2 // failed to unmarshal incoming request - ErrNoAccess = 3 // rejected by auth - ErrCommentValidation = 4 // validation failed - ErrPostNotFound = 5 // can't find post - ErrSiteNotFound = 6 // can't find site - ErrUserBlocked = 7 // user blocked - ErrReadOnly = 8 // write failed on read only - ErrCommentRejected = 9 // general error on rejected comment change - ErrCommentEditExpired = 10 // too late for edit - ErrCommentEditChanged = 11 // parent comment cannot be changed - ErrVoteRejected = 12 // general error on vote rejected - ErrVoteSelf = 13 // vote for own comment - ErrVoteDbl = 14 // already voted for the comment - ErrVoteMax = 15 // too many votes for the comment - ErrVoteMinScore = 16 // min score reached for the comment - ErrActionRejected = 17 // general error for rejected actions - ErrAssetNotFound = 18 // requested file not found + ErrInternal = 0 // any internal error + ErrCommentNotFound = 1 // can't find comment + ErrDecode = 2 // failed to unmarshal incoming request + ErrNoAccess = 3 // rejected by auth + ErrCommentValidation = 4 // validation failed + ErrPostNotFound = 5 // can't find post + ErrSiteNotFound = 6 // can't find site + ErrUserBlocked = 7 // user blocked + ErrReadOnly = 8 // write failed on read only + ErrCommentRejected = 9 // general error on rejected comment change + ErrCommentEditExpired = 10 // too late for edit + ErrCommentEditChanged = 11 // parent comment cannot be changed + ErrVoteRejected = 12 // general error on vote rejected + ErrVoteSelf = 13 // vote for own comment + ErrVoteDbl = 14 // already voted for the comment + ErrVoteMax = 15 // too many votes for the comment + ErrVoteMinScore = 16 // min score reached for the comment + ErrActionRejected = 17 // general error for rejected actions + ErrAssetNotFound = 18 // requested file not found + ErrCommentRestrictWords = 19 // restricted words in a comment ) // errTmplData store data for error message diff --git a/frontend/app/common/fetcher.ts b/frontend/app/common/fetcher.ts index c77412a2..1689d484 100644 --- a/frontend/app/common/fetcher.ts +++ b/frontend/app/common/fetcher.ts @@ -1,4 +1,4 @@ -import { httpErrorMap, httpMessages, RequestError } from 'utils/errorUtils'; +import { errorMessages, RequestError } from 'utils/errorUtils'; import { siteId } from './settings'; import { getCookie } from './cookies'; @@ -77,10 +77,10 @@ const createFetcher = (baseUrl: string = ''): Methods => { } if (res.status >= 400) { - if (httpErrorMap.has(res.status)) { - const descriptor = httpErrorMap.get(res.status) || httpMessages.unexpectedError; + const descriptor = errorMessages[res.status]; - throw new RequestError(descriptor.defaultMessage, res.status); + if (descriptor) { + throw new RequestError(descriptor.defaultMessage as string, res.status); } return res.text().then((text) => { @@ -88,7 +88,7 @@ const createFetcher = (baseUrl: string = ''): Methods => { try { err = JSON.parse(text); } catch (e) { - throw new RequestError(httpMessages.unexpectedError.defaultMessage, 0); + throw new RequestError(errorMessages[500].defaultMessage as string, 0); } throw err; }); @@ -101,7 +101,7 @@ const createFetcher = (baseUrl: string = ''): Methods => { return res.text(); } catch (e) { if (e?.message === 'Failed to fetch') { - throw new RequestError(e.message, -2); + throw new RequestError(e.message, 'fetch-error'); } throw e; diff --git a/frontend/app/components/comment-form/comment-form.tsx b/frontend/app/components/comment-form/comment-form.tsx index 64416d61..5ea44e56 100644 --- a/frontend/app/components/comment-form/comment-form.tsx +++ b/frontend/app/components/comment-form/comment-form.tsx @@ -215,6 +215,7 @@ export class CommentForm extends Component { await this.props.onSubmit(text, pageTitle || document.title); } catch (e) { this.setState({ + isDisabled: false, isErrorShown: true, errorMessage: extractErrorMessageFromResponse(e, this.props.intl), }); diff --git a/frontend/app/utils/errorUtils.ts b/frontend/app/utils/errorUtils.ts index fb76c808..3e0db38d 100644 --- a/frontend/app/utils/errorUtils.ts +++ b/frontend/app/utils/errorUtils.ts @@ -1,186 +1,108 @@ -import { IntlShape, defineMessages, MessageDescriptor } from 'react-intl'; +import { IntlShape, defineMessages } from 'react-intl'; -const messages = defineMessages({ - failedFetch: { +export const errorMessages = defineMessages({ + 'fetch-error': { id: 'errors.failed-fetch', defaultMessage: 'Failed to fetch. Please check your internet connection or try again a bit later', - description: { - code: -2, - }, }, 0: { id: 'errors.0', defaultMessage: 'Something went wrong. Please try again a bit later.', - description: { - code: 0, - }, }, 1: { id: 'errors.1', defaultMessage: 'Comment cannot be found. Please refresh the page and try again.', - description: { - code: 1, - }, }, 2: { id: 'errors.2', defaultMessage: 'Failed to unmarshal incoming request.', - description: { - code: 2, - }, }, 3: { id: 'errors.3', defaultMessage: `You don't have permission for this operation.`, - description: { - code: 3, - }, }, 4: { id: 'errors.4', defaultMessage: `Invalid comment data.`, - description: { - code: 4, - }, }, 5: { id: 'errors.5', - defaultMessage: `Comment cannot be found. Please refresh the page and try again.`, - description: { - code: 5, - }, + defaultMessage: `Comment cannot be found. Please refresh the page and try again.`, }, 6: { id: 'errors.6', - defaultMessage: `Site cannot be found. Please refresh the page and try again.`, - description: { - code: 6, - }, + defaultMessage: `Site cannot be found. Please refresh the page and try again.`, }, 7: { id: 'errors.7', defaultMessage: `User has been blocked.`, - description: { - code: 7, - }, }, 8: { id: 'errors.8', defaultMessage: `User has been blocked.`, - description: { - code: 8, - }, }, 9: { id: 'errors.9', defaultMessage: `Comment changing failed. Please try again a bit later.`, - description: { - code: 9, - }, }, 10: { id: 'errors.10', defaultMessage: `It is too late to edit the comment.`, - description: { - code: 10, - }, }, 11: { id: 'errors.11', defaultMessage: `Comment already has reply, editing is not possible.`, - description: { - code: 11, - }, }, 12: { id: 'errors.12', defaultMessage: `Cannot save voting result. Please try again a bit later.`, - description: { - code: 12, - }, }, 13: { id: 'errors.13', defaultMessage: `You cannot vote for your own comment.`, - description: { - code: 13, - }, }, 14: { id: 'errors.14', defaultMessage: `You have already voted for the comment.`, - description: { - code: 14, - }, }, 15: { id: 'errors.15', defaultMessage: `Too many votes for the comment.`, - description: { - code: 15, - }, }, 16: { id: 'errors.16', defaultMessage: `Min score reached for the comment.`, - description: { - code: 16, - }, }, 17: { id: 'errors.17', defaultMessage: `Action rejected. Please try again a bit later.`, - description: { - code: 17, - }, }, 18: { id: 'errors.18', defaultMessage: `Requested file cannot be found.`, - description: { - code: 18, - }, }, -}); - -/** - * map of codes that server returns in its response in case of error - * to client readable version - */ -const errorMessageForCodes = new Map(); - -Object.entries(messages).forEach(([, messageDescriptor]) => { - errorMessageForCodes.set(messageDescriptor.description.code, messageDescriptor); -}); - -export const httpMessages = defineMessages({ - notAuthorized: { + 19: { + id: 'errors.19', + defaultMessage: 'Comment contains restricted words.', + }, + 401: { id: 'errors.not-authorized', defaultMessage: 'Not authorized.', }, - forbidden: { + 403: { id: 'errors.forbidden', defaultMessage: 'Forbidden.', }, - toManyRequest: { + 429: { id: 'errors.to-many-request', defaultMessage: 'You have reached maximum request limit.', }, - unexpectedError: { + 500: { id: 'errors.unexpected-error', defaultMessage: 'Something went wrong.', }, }); -/** - * map of http rest codes to ui label, used by fetcher to generate error with `-1` code - */ -export const httpErrorMap = new Map([ - [401, httpMessages.notAuthorized], - [403, httpMessages.forbidden], - [429, httpMessages.toManyRequest], -]); - export type FetcherError = | string | { @@ -195,32 +117,22 @@ export type FetcherError = }; export function extractErrorMessageFromResponse(response: FetcherError, intl: IntlShape): string { - const defaultErrorMessage = intl.formatMessage(errorMessageForCodes.get(0) || messages['0']); - if (!response) { - return defaultErrorMessage; - } - if (typeof response === 'string') { return response; } - if ( - typeof response.code === 'number' && - (errorMessageForCodes.has(response.code) || httpErrorMap.has(response.code)) - ) { - const messageDescriptor = - errorMessageForCodes.get(response.code) || httpErrorMap.get(response.code) || messages['0']; - return intl.formatMessage(messageDescriptor); + if (typeof response.code === 'number' && errorMessages[response.code]) { + return intl.formatMessage(errorMessages[response.code]); } - return defaultErrorMessage; + return intl.formatMessage(errorMessages[0]); } export class RequestError extends Error { - code: number; + code: number | string; error: string; - constructor(message: string, code: number) { + constructor(message: string, code: number | string) { super(message); this.code = code;