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:
Andrei Alikov
2019-02-12 09:35:14 -06:00
committed by Umputun
parent 643f255832
commit 653125d525
5 changed files with 88 additions and 21 deletions
+1 -1
View File
@@ -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;
}
+32 -10
View File
@@ -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)}
+3 -10
View File
@@ -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 }));
}
+44
View File
@@ -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;
}