make form available after failed commit sending
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -215,6 +215,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||
await this.props.onSubmit(text, pageTitle || document.title);
|
||||
} catch (e) {
|
||||
this.setState({
|
||||
isDisabled: false,
|
||||
isErrorShown: true,
|
||||
errorMessage: extractErrorMessageFromResponse(e, this.props.intl),
|
||||
});
|
||||
|
||||
@@ -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<string | number>({
|
||||
'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<number, MessageDescriptor>();
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user