comment reporting for moderation
This commit is contained in:
parent
1a87cadc4d
commit
8ef9a7e5e4
|
@ -79,6 +79,24 @@ def get_proposal_comments(proposal_id, page, filters, search, sort):
|
|||
return page
|
||||
|
||||
|
||||
@blueprint.route("/<proposal_id>/comments/<comment_id>/report", methods=["PUT"])
|
||||
@requires_email_verified_auth
|
||||
@endpoint.api()
|
||||
def report_proposal_comment(proposal_id, comment_id):
|
||||
# Make sure proposal exists
|
||||
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
||||
if not proposal:
|
||||
return {"message": "No proposal matching id"}, 404
|
||||
|
||||
comment = Comment.query.filter_by(id=comment_id).first()
|
||||
if not comment:
|
||||
return {"message": "Comment doesn’t exist"}, 404
|
||||
|
||||
comment.report(True)
|
||||
db.session.commit()
|
||||
return None, 200
|
||||
|
||||
|
||||
@blueprint.route("/<proposal_id>/comments", methods=["POST"])
|
||||
@requires_email_verified_auth
|
||||
@endpoint.api(
|
||||
|
@ -138,23 +156,6 @@ def post_proposal_comments(proposal_id, comment, parent_comment_id):
|
|||
return dumped_comment, 201
|
||||
|
||||
|
||||
@blueprint.route("/<proposal_id>/comments/<comment_id>/report", methods=["PUT"])
|
||||
@requires_email_verified_auth
|
||||
@endpoint.api
|
||||
def report_proposal_comment(proposal_id, comment_id):
|
||||
# Make sure proposal exists
|
||||
proposal = Proposal.query.filter_by(id=proposal_id).first()
|
||||
if not proposal:
|
||||
return {"message": "No proposal matching id"}, 404
|
||||
|
||||
comment = Comment.query.filter_by(id=comment_id).first()
|
||||
if not comment:
|
||||
return {"message": "Comment doesn’t exist"}, 404
|
||||
|
||||
comment.report(True)
|
||||
return None, 200
|
||||
|
||||
|
||||
@blueprint.route("/", methods=["GET"])
|
||||
@endpoint.api(
|
||||
parameter('page', type=int, required=False),
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
import { Button } from 'antd';
|
||||
import { Button, message } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Markdown from 'components/Markdown';
|
||||
import UserAvatar from 'components/UserAvatar';
|
||||
import MarkdownEditor, { MARKDOWN_TYPE } from 'components/MarkdownEditor';
|
||||
import { postProposalComment } from 'modules/proposals/actions';
|
||||
import { postProposalComment, reportProposalComment } from 'modules/proposals/actions';
|
||||
import { getIsSignedIn } from 'modules/auth/selectors';
|
||||
import { Comment as IComment } from 'types';
|
||||
import { AppState } from 'store/reducers';
|
||||
|
@ -24,6 +24,7 @@ interface StateProps {
|
|||
|
||||
interface DispatchProps {
|
||||
postProposalComment: typeof postProposalComment;
|
||||
reportProposalComment: typeof reportProposalComment;
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps & DispatchProps;
|
||||
|
@ -76,7 +77,12 @@ class Comment extends React.Component<Props> {
|
|||
<a className="Comment-controls-button" onClick={this.toggleReply}>
|
||||
{isReplying ? 'Cancel' : 'Reply'}
|
||||
</a>
|
||||
{/*<a className="Comment-controls-button">Report</a>*/}
|
||||
{!comment.hidden &&
|
||||
!comment.reported && (
|
||||
<a className="Comment-controls-button" onClick={this.report}>
|
||||
Report
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -120,6 +126,16 @@ class Comment extends React.Component<Props> {
|
|||
const { reply } = this.state;
|
||||
this.props.postProposalComment(comment.proposalId, reply, comment.id);
|
||||
};
|
||||
|
||||
private report = async () => {
|
||||
const { proposalId, id } = this.props.comment;
|
||||
const res = await this.props.reportProposalComment(proposalId, id);
|
||||
if ((res as any).error) {
|
||||
message.error('Problem reporting comment: ' + (res as any).payload);
|
||||
} else {
|
||||
message.success('Comment reported');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const ConnectedComment = connect<StateProps, DispatchProps, OwnProps, AppState>(
|
||||
|
@ -130,6 +146,7 @@ const ConnectedComment = connect<StateProps, DispatchProps, OwnProps, AppState>(
|
|||
}),
|
||||
{
|
||||
postProposalComment,
|
||||
reportProposalComment,
|
||||
},
|
||||
)(Comment);
|
||||
|
||||
|
|
|
@ -3,11 +3,7 @@ import { connect } from 'react-redux';
|
|||
import { Button, message, Skeleton, Alert } from 'antd';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Proposal } from 'types';
|
||||
import {
|
||||
fetchProposalComments,
|
||||
postProposalComment,
|
||||
reportProposalComment,
|
||||
} from 'modules/proposals/actions';
|
||||
import { fetchProposalComments, postProposalComment } from 'modules/proposals/actions';
|
||||
import { getIsVerified, getIsSignedIn } from 'modules/auth/selectors';
|
||||
import Comments from 'components/Comments';
|
||||
import Placeholder from 'components/Placeholder';
|
||||
|
@ -29,7 +25,6 @@ interface StateProps {
|
|||
interface DispatchProps {
|
||||
fetchProposalComments: typeof fetchProposalComments;
|
||||
postProposalComment: typeof postProposalComment;
|
||||
reportProposalComment: typeof reportProposalComment;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & OwnProps & StateProps;
|
||||
|
@ -182,6 +177,5 @@ export default connect<StateProps, DispatchProps, OwnProps, AppState>(
|
|||
{
|
||||
fetchProposalComments,
|
||||
postProposalComment,
|
||||
reportProposalComment,
|
||||
},
|
||||
)(ProposalComments);
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
getProposal,
|
||||
getProposalComments,
|
||||
getProposalUpdates,
|
||||
reportProposalComment as apiReportProposalComment,
|
||||
getProposalContributions,
|
||||
postProposalComment as apiPostProposalComment,
|
||||
requestProposalPayout,
|
||||
|
@ -209,9 +210,23 @@ export function reportProposalComment(
|
|||
proposalId: Proposal['proposalId'],
|
||||
commentId: Comment['id'],
|
||||
) {
|
||||
return (dispatch: Dispatch<any>) => {
|
||||
// dispatch({
|
||||
// type: types.POST_PROPOSAL_COMMENT,
|
||||
// })
|
||||
return async (dispatch: Dispatch<any>) => {
|
||||
dispatch({ type: types.REPORT_PROPOSAL_COMMENT_PENDING, payload: { commentId } });
|
||||
|
||||
try {
|
||||
await apiReportProposalComment(proposalId, commentId);
|
||||
return dispatch({
|
||||
type: types.REPORT_PROPOSAL_COMMENT_FULFILLED,
|
||||
payload: {
|
||||
commentId,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
return dispatch({
|
||||
type: types.REPORT_PROPOSAL_COMMENT_REJECTED,
|
||||
payload: err.message || err.toString(),
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -172,6 +172,42 @@ function addPostedComment(state: ProposalState, payload: PostCommentPayload) {
|
|||
};
|
||||
}
|
||||
|
||||
function updateCommentInStore(
|
||||
state: ProposalState,
|
||||
commentId: Comment['id'],
|
||||
update: Partial<Comment>,
|
||||
) {
|
||||
// clone so we can mutate with great abandon!
|
||||
const pages = cloneDeep(state.detailComments.pages);
|
||||
// recursive populate replies for nested comment
|
||||
const f = (id: number, p: Comment) => {
|
||||
if (p.id === id) {
|
||||
Object.entries(update).forEach(([k, v]) => {
|
||||
(p as any)[k] = v;
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
p.replies.forEach(x => f(id, x));
|
||||
}
|
||||
};
|
||||
// pages > page > comments
|
||||
pages.forEach(p =>
|
||||
p.forEach(c => {
|
||||
f(commentId, c);
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
isPostCommentPending: false,
|
||||
detailComments: {
|
||||
...state.detailComments,
|
||||
pages,
|
||||
total: state.detailComments.total + 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default (state = INITIAL_STATE, action: any) => {
|
||||
const { payload } = action;
|
||||
switch (action.type) {
|
||||
|
@ -336,6 +372,9 @@ export default (state = INITIAL_STATE, action: any) => {
|
|||
},
|
||||
};
|
||||
|
||||
case types.REPORT_PROPOSAL_COMMENT_FULFILLED:
|
||||
return updateCommentInStore(state, payload.commentId, { reported: true });
|
||||
|
||||
case types.PROPOSAL_UPDATES_PENDING:
|
||||
return {
|
||||
...state,
|
||||
|
|
Loading…
Reference in New Issue