Allow moderators to delete comment (#235)
## Ticket 223 Add ability for delegated moderators to delete comments ## Changes - Refactored doCommentAbandon's signature so we don't end up converting between "uri" and "Claim" several times and needing to lookup redux when the client can already provide us the exact values that we need. - Pass the new moderator fields to the API. - Remove the need to call 'makeSelectChannelPermUrlForClaimUri' since it's a simple field query when we already have the claim.
This commit is contained in:
parent
9138e508c6
commit
cfd67b1c8d
6 changed files with 59 additions and 47 deletions
6
flow-typed/Comment.js
vendored
6
flow-typed/Comment.js
vendored
|
@ -169,8 +169,10 @@ declare type CommentAbandonParams = {
|
|||
comment_id: string,
|
||||
creator_channel_id?: string,
|
||||
creator_channel_name?: string,
|
||||
channel_id?: string,
|
||||
hexdata?: string,
|
||||
signature?: string,
|
||||
signing_ts?: string,
|
||||
mod_channel_id?: string,
|
||||
mod_channel_name?: string,
|
||||
};
|
||||
|
||||
declare type CommentCreateParams = {
|
||||
|
|
|
@ -4,11 +4,7 @@ import { doCommentPin, doCommentModAddDelegate } from 'redux/actions/comments';
|
|||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { doSetPlayingUri } from 'redux/actions/content';
|
||||
import { doToast } from 'redux/actions/notifications';
|
||||
import {
|
||||
makeSelectChannelPermUrlForClaimUri,
|
||||
makeSelectClaimIsMine,
|
||||
makeSelectClaimForUri,
|
||||
} from 'redux/selectors/claims';
|
||||
import { makeSelectClaimIsMine, makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
||||
import { selectModerationDelegatorsById } from 'redux/selectors/comments';
|
||||
import { selectPlayingUri } from 'redux/selectors/content';
|
||||
|
@ -17,7 +13,6 @@ import CommentMenuList from './view';
|
|||
const select = (state, props) => ({
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||
contentChannelPermanentUrl: makeSelectChannelPermUrlForClaimUri(props.uri)(state),
|
||||
activeChannelClaim: selectActiveChannelClaim(state),
|
||||
playingUri: selectPlayingUri(state),
|
||||
moderationDelegatorsById: selectModerationDelegatorsById(state),
|
||||
|
|
|
@ -24,7 +24,6 @@ type Props = {
|
|||
// --- select ---
|
||||
claim: ?Claim,
|
||||
claimIsMine: boolean,
|
||||
contentChannelPermanentUrl: any,
|
||||
activeChannelClaim: ?ChannelClaim,
|
||||
playingUri: ?PlayingUri,
|
||||
moderationDelegatorsById: { [string]: { global: boolean, delegators: { name: string, claimId: string } } },
|
||||
|
@ -47,7 +46,6 @@ function CommentMenuList(props: Props) {
|
|||
commentIsMine,
|
||||
commentId,
|
||||
activeChannelClaim,
|
||||
contentChannelPermanentUrl,
|
||||
isTopLevel,
|
||||
isPinned,
|
||||
playingUri,
|
||||
|
@ -71,6 +69,8 @@ function CommentMenuList(props: Props) {
|
|||
} = useHistory();
|
||||
|
||||
const contentChannelClaim = getChannelFromClaim(claim);
|
||||
const contentChannelPermanentUrl = contentChannelClaim && contentChannelClaim.permanent_url;
|
||||
|
||||
const activeModeratorInfo = activeChannelClaim && moderationDelegatorsById[activeChannelClaim.claim_id];
|
||||
const activeChannelIsCreator = activeChannelClaim && activeChannelClaim.permanent_url === contentChannelPermanentUrl;
|
||||
const activeChannelIsAdmin = activeChannelClaim && activeModeratorInfo && activeModeratorInfo.global;
|
||||
|
@ -84,10 +84,12 @@ function CommentMenuList(props: Props) {
|
|||
if (playingUri && playingUri.source === 'comment') {
|
||||
clearPlayingUri();
|
||||
}
|
||||
|
||||
openModal(MODALS.CONFIRM_REMOVE_COMMENT, {
|
||||
commentId,
|
||||
commentIsMine,
|
||||
contentChannelPermanentUrl,
|
||||
deleterClaim: activeChannelClaim,
|
||||
deleterIsModOrAdmin: activeChannelIsAdmin || activeChannelIsModerator,
|
||||
creatorClaim: commentIsMine ? undefined : contentChannelClaim,
|
||||
supportAmount,
|
||||
setQuickReply,
|
||||
});
|
||||
|
@ -201,7 +203,8 @@ function CommentMenuList(props: Props) {
|
|||
|
||||
{!disableRemove &&
|
||||
activeChannelClaim &&
|
||||
(activeChannelClaim.permanent_url === authorUri ||
|
||||
(activeChannelIsModerator ||
|
||||
activeChannelClaim.permanent_url === authorUri ||
|
||||
activeChannelClaim.permanent_url === contentChannelPermanentUrl) && (
|
||||
<MenuItem className="comment__menu-option" onSelect={handleDeleteComment}>
|
||||
<div className="menu__link">
|
||||
|
|
|
@ -3,9 +3,9 @@ import { doHideModal } from 'redux/actions/app';
|
|||
import ModalRemoveComment from './view';
|
||||
import { doCommentAbandon } from 'redux/actions/comments';
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
closeModal: () => dispatch(doHideModal()),
|
||||
deleteComment: (commentId, creatorChannelUrl) => dispatch(doCommentAbandon(commentId, creatorChannelUrl)),
|
||||
});
|
||||
const perform = {
|
||||
doHideModal,
|
||||
doCommentAbandon,
|
||||
};
|
||||
|
||||
export default connect(null, perform)(ModalRemoveComment);
|
||||
|
|
|
@ -6,27 +6,30 @@ import Card from 'component/common/card';
|
|||
|
||||
type Props = {
|
||||
commentId: string, // sha256 digest identifying the comment
|
||||
commentIsMine: boolean, // if this comment was signed by an owned channel
|
||||
contentChannelPermanentUrl: any,
|
||||
closeModal: () => void,
|
||||
deleteComment: (string, ?string) => void,
|
||||
deleterClaim: Claim,
|
||||
deleterIsModOrAdmin?: boolean,
|
||||
creatorClaim?: Claim,
|
||||
supportAmount?: any,
|
||||
setQuickReply: (any) => void,
|
||||
// --- redux ---
|
||||
doHideModal: () => void,
|
||||
doCommentAbandon: (string, Claim, ?boolean, ?Claim) => void,
|
||||
};
|
||||
|
||||
function ModalRemoveComment(props: Props) {
|
||||
const {
|
||||
commentId,
|
||||
commentIsMine,
|
||||
contentChannelPermanentUrl,
|
||||
closeModal,
|
||||
deleteComment,
|
||||
deleterClaim,
|
||||
deleterIsModOrAdmin,
|
||||
creatorClaim,
|
||||
supportAmount,
|
||||
setQuickReply,
|
||||
doHideModal,
|
||||
doCommentAbandon,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Modal isOpen contentLabel={__('Confirm Comment Deletion')} type="card" onAborted={closeModal}>
|
||||
<Modal isOpen contentLabel={__('Confirm Comment Deletion')} type="card" onAborted={doHideModal}>
|
||||
<Card
|
||||
title={__('Remove Comment')}
|
||||
body={
|
||||
|
@ -46,12 +49,14 @@ function ModalRemoveComment(props: Props) {
|
|||
button="primary"
|
||||
label={__('Remove')}
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
deleteComment(commentId, commentIsMine ? undefined : contentChannelPermanentUrl);
|
||||
if (setQuickReply) setQuickReply(undefined);
|
||||
doHideModal();
|
||||
doCommentAbandon(commentId, deleterClaim, deleterIsModOrAdmin, creatorClaim);
|
||||
if (setQuickReply) {
|
||||
setQuickReply(undefined);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button button="link" label={__('Cancel')} onClick={closeModal} />
|
||||
<Button button="link" label={__('Cancel')} onClick={doHideModal} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -690,33 +690,40 @@ export function doCommentPin(commentId: string, claimId: string, remove: boolean
|
|||
};
|
||||
}
|
||||
|
||||
export function doCommentAbandon(commentId: string, creatorChannelUri?: string) {
|
||||
/**
|
||||
* Deletes a comment in Commentron.
|
||||
*
|
||||
* @param commentId The comment ID to delete.
|
||||
* @param deleterClaim The channel-claim of the person doing the deletion. Defaults to the active channel if not provided.
|
||||
* @param deleterIsModOrAdmin Is the deleter a mod or admin for the content?
|
||||
* @param creatorClaim The channel-claim for the content where the comment resides. Not required if the deleter owns the comment (i.e. deleting own comment).
|
||||
* @returns {function(Dispatch): *}
|
||||
*/
|
||||
export function doCommentAbandon(
|
||||
commentId: string,
|
||||
deleterClaim?: Claim,
|
||||
deleterIsModOrAdmin?: boolean,
|
||||
creatorClaim?: Claim
|
||||
) {
|
||||
return async (dispatch: Dispatch, getState: GetState) => {
|
||||
if (!deleterClaim) {
|
||||
const state = getState();
|
||||
const claim = creatorChannelUri ? selectClaimsByUri(state)[creatorChannelUri] : undefined;
|
||||
const creatorChannelId = claim ? claim.claim_id : null;
|
||||
const creatorChannelName = claim ? claim.name : null;
|
||||
const activeChannelClaim = selectActiveChannelClaim(state);
|
||||
deleterClaim = selectActiveChannelClaim(state);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.COMMENT_ABANDON_STARTED,
|
||||
});
|
||||
|
||||
let commentIdSignature;
|
||||
if (activeChannelClaim) {
|
||||
try {
|
||||
commentIdSignature = await Lbry.channel_sign({
|
||||
channel_id: activeChannelClaim.claim_id,
|
||||
hexdata: toHex(commentId),
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
const commentIdSignature = await channelSignData(deleterClaim.claim_id, commentId);
|
||||
|
||||
return Comments.comment_abandon({
|
||||
comment_id: commentId,
|
||||
...(creatorChannelId ? { creator_channel_id: creatorChannelId } : {}),
|
||||
...(creatorChannelName ? { creator_channel_name: creatorChannelName } : {}),
|
||||
creator_channel_id: creatorClaim ? creatorClaim.claim_id : undefined,
|
||||
creator_channel_name: creatorClaim ? creatorClaim.name : undefined,
|
||||
...(commentIdSignature || {}),
|
||||
mod_channel_id: deleterClaim && deleterIsModOrAdmin ? deleterClaim.claim_id : undefined,
|
||||
mod_channel_name: deleterClaim && deleterIsModOrAdmin ? deleterClaim.name : undefined,
|
||||
})
|
||||
.then((result: CommentAbandonResponse) => {
|
||||
// Comment may not be deleted if the signing channel can't be signed.
|
||||
|
|
Loading…
Reference in a new issue