Hanlde limiter rejection on UI side (#275)
- Adds common function to get error message from the http response - Adds error message for the voting displayed under the voting buttons - Adds restoring of voting data after voting request failed fixed re-writing error message when user tries to vote for the same comment
This commit is contained in:
@@ -25,7 +25,7 @@ const (
|
||||
ErrReadOnly = 8 // write failed on read only
|
||||
ErrCommentRejected = 9 // general error on rejected comment change
|
||||
ErrCommentEditExpired = 10 // too late for edit
|
||||
ErrCommentEditChanged = 11 // parent commend changed
|
||||
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
|
||||
|
||||
@@ -9,3 +9,11 @@
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.voting__error {
|
||||
color: #9a0000;
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { url } from 'common/settings';
|
||||
import store from 'common/store';
|
||||
import copy from 'common/copy';
|
||||
import debounce from 'utils/debounce';
|
||||
import { extractErrorMessageFromResponse } from 'utils/errorUtils';
|
||||
|
||||
import Input from 'components/input';
|
||||
|
||||
@@ -23,6 +24,7 @@ export default class Comment extends Component {
|
||||
isEditing: false,
|
||||
isUserVerified: false,
|
||||
editTimeLeft: null,
|
||||
voteErrorMessage: null,
|
||||
};
|
||||
|
||||
this.votingPromise = Promise.resolve();
|
||||
@@ -49,6 +51,8 @@ export default class Comment extends Component {
|
||||
this.isGuest = this.isGuest.bind(this);
|
||||
this.getUpvoteDisabledReason = this.getUpvoteDisabledReason.bind(this);
|
||||
this.getDownvoteDisabledReason = this.getDownvoteDisabledReason.bind(this);
|
||||
this.handleVoteError = this.handleVoteError.bind(this);
|
||||
this.sendVotingRequest = this.sendVotingRequest.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@@ -264,6 +268,23 @@ export default class Comment extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleVoteError(e, originalVotingState) {
|
||||
this.setState({
|
||||
...originalVotingState,
|
||||
voteErrorMessage: extractErrorMessageFromResponse(e.response),
|
||||
});
|
||||
}
|
||||
|
||||
sendVotingRequest(id, votingValue, originalVotingState) {
|
||||
this.votingPromise = this.votingPromise
|
||||
.then(() => {
|
||||
return api.putCommentVote({ id, url, value: votingValue }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
})
|
||||
.catch(e => this.handleVoteError(e, originalVotingState));
|
||||
}
|
||||
|
||||
increaseScore() {
|
||||
const { score, scoreIncreased, scoreDecreased } = this.state;
|
||||
const { id } = this.props.data;
|
||||
@@ -274,13 +295,10 @@ export default class Comment extends Component {
|
||||
scoreIncreased: !scoreDecreased,
|
||||
scoreDecreased: false,
|
||||
score: score + 1,
|
||||
voteErrorMessage: null,
|
||||
});
|
||||
|
||||
this.votingPromise = this.votingPromise.then(() => {
|
||||
return api.putCommentVote({ id, url, value: 1 }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
});
|
||||
this.sendVotingRequest(id, 1, { score, scoreIncreased, scoreDecreased });
|
||||
}
|
||||
|
||||
decreaseScore() {
|
||||
@@ -293,13 +311,10 @@ export default class Comment extends Component {
|
||||
scoreDecreased: !scoreIncreased,
|
||||
scoreIncreased: false,
|
||||
score: score - 1,
|
||||
voteErrorMessage: null,
|
||||
});
|
||||
|
||||
this.votingPromise = this.votingPromise.then(() => {
|
||||
return api.putCommentVote({ id, url, value: -1 }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
});
|
||||
this.sendVotingRequest(id, -1, { score, scoreIncreased, scoreDecreased });
|
||||
}
|
||||
|
||||
onReply(...rest) {
|
||||
@@ -412,6 +427,7 @@ export default class Comment extends Component {
|
||||
isEditing,
|
||||
isUserVerified,
|
||||
editTimeLeft,
|
||||
voteErrorMessage,
|
||||
}
|
||||
) {
|
||||
const { data, mods = {}, isCommentsDisabled } = props;
|
||||
@@ -607,6 +623,12 @@ export default class Comment extends Component {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{!!voteErrorMessage && (
|
||||
<div className="voting__error" role="alert">
|
||||
Voting error: {voteErrorMessage}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={b('comment__text', { mix: b('raw-content', {}, { theme: mods.theme }) })}
|
||||
ref={r => (this.textNode = r)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { siteId, url, pageTitle } from 'common/settings';
|
||||
|
||||
import api from 'common/api';
|
||||
import store from 'common/store';
|
||||
import { extractErrorMessageFromResponse } from 'utils/errorUtils';
|
||||
import TextareaAutosize from 'components/input/textarea-autosize';
|
||||
|
||||
const RSS_THREAD_URL = `${BASE_URL}${API_BASE}/rss/post?site=${siteId}&url=${url}`;
|
||||
@@ -92,16 +93,8 @@ export default class Input extends Component {
|
||||
this.setState({ preview: null, text: '' });
|
||||
})
|
||||
.catch(e => {
|
||||
if (
|
||||
e.response &&
|
||||
e.response.data &&
|
||||
typeof e.response.data.error === 'string' &&
|
||||
e.response.data.error.indexOf("parent comment with reply can't be edited") === 0
|
||||
) {
|
||||
this.setState({ isErrorShown: true, errorMessage: 'Comment has reply, editing is not possible' });
|
||||
return;
|
||||
}
|
||||
this.setState({ isErrorShown: true, errorMessage: null });
|
||||
const errorMessage = extractErrorMessageFromResponse(e.response);
|
||||
this.setState({ isErrorShown: true, errorMessage });
|
||||
})
|
||||
.finally(() => this.setState({ isDisabled: false }));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
const errorMessageForCodes = new Map([
|
||||
[0, 'Something went wrong. Please try again a bit later.'],
|
||||
[1, 'Comment cannot be found. Please refresh the page and try again.'],
|
||||
[2, 'Failed to unmarshal incoming request.'],
|
||||
[3, "You don't have permission for this operaton."],
|
||||
[4, 'Invalid comment data.'],
|
||||
[5, 'Comment cannot be found. Please refresh the page and try again.'],
|
||||
[6, 'Site cannot be found. Please refresh the page and try again.'],
|
||||
[7, 'User has been blocked.'],
|
||||
[8, 'This post is read only.'],
|
||||
[9, 'Comment changing failed. Please try again a bit later.'],
|
||||
[10, 'It is too late to edit the comment.'],
|
||||
[11, 'Comment already has reply, editing is not possible.'],
|
||||
[12, 'Cannot save voting result. Please try again a bit later.'],
|
||||
[13, 'You cannot vote for your own comment.'],
|
||||
[14, 'You have already voted for the comment.'],
|
||||
[15, 'Too many votes for the comment.'],
|
||||
[16, 'Min score reached for the comment.'],
|
||||
[17, 'Action rejected. Please try again a bit later.'],
|
||||
[18, 'Requested file cannot be found.'],
|
||||
]);
|
||||
|
||||
export function extractErrorMessageFromResponse(response) {
|
||||
const defatulErrorMessage = 'Something went wrong. Please try again a bit later.';
|
||||
if (!(response && response.data)) {
|
||||
return defatulErrorMessage;
|
||||
}
|
||||
|
||||
const responseData = response.data;
|
||||
|
||||
if (typeof responseData.code === 'number' && errorMessageForCodes.has(responseData.code)) {
|
||||
return errorMessageForCodes.get(responseData.code);
|
||||
}
|
||||
|
||||
if (typeof responseData.details === 'string') {
|
||||
return responseData.details;
|
||||
}
|
||||
|
||||
if (typeof responseData === 'string') {
|
||||
return responseData;
|
||||
}
|
||||
|
||||
return defatulErrorMessage;
|
||||
}
|
||||
Reference in New Issue
Block a user