mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-23 17:47:24 +00:00
Commentron Moderation Delegation API
This commit is contained in:
parent
510056a479
commit
b4b45ffbdc
7 changed files with 864 additions and 97 deletions
42
flow-typed/Comment.js
vendored
42
flow-typed/Comment.js
vendored
|
@ -37,10 +37,18 @@ declare type CommentsState = {
|
||||||
myReactsByCommentId: any,
|
myReactsByCommentId: any,
|
||||||
othersReactsByCommentId: any,
|
othersReactsByCommentId: any,
|
||||||
pendingCommentReactions: Array<string>,
|
pendingCommentReactions: Array<string>,
|
||||||
moderationBlockList: ?Array<string>,
|
moderationBlockList: ?Array<string>, // @KP rename to "personalBlockList"?
|
||||||
|
adminBlockList: ?Array<string>,
|
||||||
|
moderatorBlockList: ?Array<string>,
|
||||||
|
moderatorBlockListDelegatorsMap: {[string]: Array<string>}, // {"blockedUri": ["delegatorUri1", ""delegatorUri2", ...]}
|
||||||
fetchingModerationBlockList: boolean,
|
fetchingModerationBlockList: boolean,
|
||||||
|
moderationDelegatesById: { [string]: Array<{ channelId: string, channelName: string }> },
|
||||||
|
fetchingModerationDelegates: boolean,
|
||||||
|
moderationDelegatorsById: { [string]: { global: boolean, delegators: { name: string, claimId: string } }},
|
||||||
|
fetchingModerationDelegators: boolean,
|
||||||
blockingByUri: {},
|
blockingByUri: {},
|
||||||
unBlockingByUri: {},
|
unBlockingByUri: {},
|
||||||
|
togglingForDelegatorMap: {[string]: Array<string>}, // {"blockedUri": ["delegatorUri1", ""delegatorUri2", ...]}
|
||||||
commentsDisabledChannelIds: Array<string>,
|
commentsDisabledChannelIds: Array<string>,
|
||||||
settingsByChannelId: { [string]: PerChannelSettings }, // ChannelID -> settings
|
settingsByChannelId: { [string]: PerChannelSettings }, // ChannelID -> settings
|
||||||
fetchingSettings: boolean,
|
fetchingSettings: boolean,
|
||||||
|
@ -88,6 +96,38 @@ declare type SuperListParams = {};
|
||||||
|
|
||||||
declare type ModerationBlockParams = {};
|
declare type ModerationBlockParams = {};
|
||||||
|
|
||||||
|
declare type ModerationAddDelegateParams = {
|
||||||
|
mod_channel_id: string,
|
||||||
|
mod_channel_name: string,
|
||||||
|
creator_channel_id: string,
|
||||||
|
creator_channel_name: string,
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ModerationRemoveDelegateParams = {
|
||||||
|
mod_channel_id: string,
|
||||||
|
mod_channel_name: string,
|
||||||
|
creator_channel_id: string,
|
||||||
|
creator_channel_name: string,
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ModerationListDelegatesParams = {
|
||||||
|
creator_channel_id: string,
|
||||||
|
creator_channel_name: string,
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type ModerationAmIParams = {
|
||||||
|
channel_name: string,
|
||||||
|
channel_id: string,
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string
|
||||||
|
};
|
||||||
|
|
||||||
declare type SettingsParams = {
|
declare type SettingsParams = {
|
||||||
channel_name: string,
|
channel_name: string,
|
||||||
channel_id: string,
|
channel_id: string,
|
||||||
|
|
|
@ -8,6 +8,12 @@ const Comments = {
|
||||||
moderation_block: (params: ModerationBlockParams) => fetchCommentsApi('moderation.Block', params),
|
moderation_block: (params: ModerationBlockParams) => fetchCommentsApi('moderation.Block', params),
|
||||||
moderation_unblock: (params: ModerationBlockParams) => fetchCommentsApi('moderation.UnBlock', params),
|
moderation_unblock: (params: ModerationBlockParams) => fetchCommentsApi('moderation.UnBlock', params),
|
||||||
moderation_block_list: (params: ModerationBlockParams) => fetchCommentsApi('moderation.BlockedList', params),
|
moderation_block_list: (params: ModerationBlockParams) => fetchCommentsApi('moderation.BlockedList', params),
|
||||||
|
moderation_add_delegate: (params: ModerationAddDelegateParams) => fetchCommentsApi('moderation.AddDelegate', params),
|
||||||
|
moderation_remove_delegate: (params: ModerationRemoveDelegateParams) =>
|
||||||
|
fetchCommentsApi('moderation.RemoveDelegate', params),
|
||||||
|
moderation_list_delegates: (params: ModerationListDelegatesParams) =>
|
||||||
|
fetchCommentsApi('moderation.ListDelegates', params),
|
||||||
|
moderation_am_i: (params: ModerationAmIParams) => fetchCommentsApi('moderation.AmI', params),
|
||||||
comment_list: (params: CommentListParams) => fetchCommentsApi('comment.List', params),
|
comment_list: (params: CommentListParams) => fetchCommentsApi('comment.List', params),
|
||||||
comment_abandon: (params: CommentAbandonParams) => fetchCommentsApi('comment.Abandon', params),
|
comment_abandon: (params: CommentAbandonParams) => fetchCommentsApi('comment.Abandon', params),
|
||||||
comment_create: (params: CommentCreateParams) => fetchCommentsApi('comment.Create', params),
|
comment_create: (params: CommentCreateParams) => fetchCommentsApi('comment.Create', params),
|
||||||
|
|
|
@ -278,6 +278,12 @@ export const COMMENT_MODERATION_BLOCK_FAILED = 'COMMENT_MODERATION_BLOCK_FAILED'
|
||||||
export const COMMENT_MODERATION_UN_BLOCK_STARTED = 'COMMENT_MODERATION_UN_BLOCK_STARTED';
|
export const COMMENT_MODERATION_UN_BLOCK_STARTED = 'COMMENT_MODERATION_UN_BLOCK_STARTED';
|
||||||
export const COMMENT_MODERATION_UN_BLOCK_COMPLETE = 'COMMENT_MODERATION_UN_BLOCK_COMPLETE';
|
export const COMMENT_MODERATION_UN_BLOCK_COMPLETE = 'COMMENT_MODERATION_UN_BLOCK_COMPLETE';
|
||||||
export const COMMENT_MODERATION_UN_BLOCK_FAILED = 'COMMENT_MODERATION_UN_BLOCK_FAILED';
|
export const COMMENT_MODERATION_UN_BLOCK_FAILED = 'COMMENT_MODERATION_UN_BLOCK_FAILED';
|
||||||
|
export const COMMENT_FETCH_MODERATION_DELEGATES_STARTED = 'COMMENT_FETCH_MODERATION_DELEGATES_STARTED';
|
||||||
|
export const COMMENT_FETCH_MODERATION_DELEGATES_FAILED = 'COMMENT_FETCH_MODERATION_DELEGATES_FAILED';
|
||||||
|
export const COMMENT_FETCH_MODERATION_DELEGATES_COMPLETED = 'COMMENT_FETCH_MODERATION_DELEGATES_COMPLETED';
|
||||||
|
export const COMMENT_MODERATION_AM_I_LIST_STARTED = 'COMMENT_MODERATION_AM_I_LIST_STARTED';
|
||||||
|
export const COMMENT_MODERATION_AM_I_LIST_FAILED = 'COMMENT_MODERATION_AM_I_LIST_FAILED';
|
||||||
|
export const COMMENT_MODERATION_AM_I_LIST_COMPLETED = 'COMMENT_MODERATION_AM_I_LIST_COMPLETED';
|
||||||
export const COMMENT_FETCH_SETTINGS_STARTED = 'COMMENT_FETCH_SETTINGS_STARTED';
|
export const COMMENT_FETCH_SETTINGS_STARTED = 'COMMENT_FETCH_SETTINGS_STARTED';
|
||||||
export const COMMENT_FETCH_SETTINGS_FAILED = 'COMMENT_FETCH_SETTINGS_FAILED';
|
export const COMMENT_FETCH_SETTINGS_FAILED = 'COMMENT_FETCH_SETTINGS_FAILED';
|
||||||
export const COMMENT_FETCH_SETTINGS_COMPLETED = 'COMMENT_FETCH_SETTINGS_COMPLETED';
|
export const COMMENT_FETCH_SETTINGS_COMPLETED = 'COMMENT_FETCH_SETTINGS_COMPLETED';
|
||||||
|
|
|
@ -3,3 +3,9 @@ export const LINKED_COMMENT_QUERY_PARAM = 'lc';
|
||||||
export const SORT_COMMENTS_NEW = 'new';
|
export const SORT_COMMENTS_NEW = 'new';
|
||||||
export const SORT_COMMENTS_BEST = 'best';
|
export const SORT_COMMENTS_BEST = 'best';
|
||||||
export const SORT_COMMENTS_CONTROVERSIAL = 'controversial';
|
export const SORT_COMMENTS_CONTROVERSIAL = 'controversial';
|
||||||
|
|
||||||
|
export const BLOCK_LEVEL = {
|
||||||
|
SELF: 'self',
|
||||||
|
MODERATOR: 'moderator',
|
||||||
|
ADMIN: 'admin',
|
||||||
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import * as REACTION_TYPES from 'constants/reactions';
|
import * as REACTION_TYPES from 'constants/reactions';
|
||||||
import { Lbry, parseURI, buildURI, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux';
|
import { BLOCK_LEVEL } from 'constants/comment';
|
||||||
|
import { Lbry, parseURI, buildURI, selectClaimsById, selectClaimsByUri, selectMyChannelClaims } from 'lbry-redux';
|
||||||
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
|
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
|
||||||
import {
|
import {
|
||||||
makeSelectCommentIdsForUri,
|
makeSelectCommentIdsForUri,
|
||||||
|
@ -9,6 +10,7 @@ import {
|
||||||
makeSelectOthersReactionsForComment,
|
makeSelectOthersReactionsForComment,
|
||||||
selectPendingCommentReacts,
|
selectPendingCommentReacts,
|
||||||
selectModerationBlockList,
|
selectModerationBlockList,
|
||||||
|
selectModerationDelegatorsById,
|
||||||
} from 'redux/selectors/comments';
|
} from 'redux/selectors/comments';
|
||||||
import { makeSelectNotificationForCommentId } from 'redux/selectors/notifications';
|
import { makeSelectNotificationForCommentId } from 'redux/selectors/notifications';
|
||||||
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
||||||
|
@ -522,42 +524,87 @@ async function channelSignName(channelClaimId: string, channelName: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hides a users comments from all creator's claims and prevent them from commenting in the future
|
// Hides a users comments from all creator's claims and prevent them from commenting in the future
|
||||||
export function doCommentModToggleBlock(channelUri: string, showLink: boolean, unblock: boolean = false) {
|
function doCommentModToggleBlock(
|
||||||
|
unblock: boolean,
|
||||||
|
commenterUri: string,
|
||||||
|
creatorId: string,
|
||||||
|
blockerIds: Array<string>, // [] = use all my channels
|
||||||
|
blockLevel: string,
|
||||||
|
showLink: boolean = false
|
||||||
|
) {
|
||||||
return async (dispatch: Dispatch, getState: GetState) => {
|
return async (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const myChannels = selectMyChannelClaims(state);
|
|
||||||
const claim = selectClaimsByUri(state)[channelUri];
|
|
||||||
|
|
||||||
if (!claim) {
|
let blockerChannelClaims = selectMyChannelClaims(state);
|
||||||
|
if (blockerIds.length === 0) {
|
||||||
|
// Specific blockers not provided, so find one based on block-level.
|
||||||
|
switch (blockLevel) {
|
||||||
|
case BLOCK_LEVEL.MODERATOR:
|
||||||
|
{
|
||||||
|
// Find the first channel that is a moderator for 'creatorId'.
|
||||||
|
const delegatorsById = selectModerationDelegatorsById(state);
|
||||||
|
blockerChannelClaims = [
|
||||||
|
blockerChannelClaims.find((x) => {
|
||||||
|
const delegatorDataForId = delegatorsById[x.claim_id];
|
||||||
|
return delegatorDataForId && Object.values(delegatorDataForId.delegators).includes(creatorId);
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.ADMIN:
|
||||||
|
{
|
||||||
|
// Find the first admin channel and use that.
|
||||||
|
const delegatorsById = selectModerationDelegatorsById(state);
|
||||||
|
blockerChannelClaims = [
|
||||||
|
blockerChannelClaims.find((x) => delegatorsById[x.claim_id] && delegatorsById[x.claim_id].global),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blockerChannelClaims = blockerChannelClaims.filter((x) => blockerIds.includes(x.claim_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
const commenterClaim = selectClaimsByUri(state)[commenterUri];
|
||||||
|
if (!commenterClaim) {
|
||||||
console.error("Can't find claim to block"); // eslint-disable-line
|
console.error("Can't find claim to block"); // eslint-disable-line
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const creatorClaim = selectClaimsById(state)[creatorId];
|
||||||
|
if (creatorId && !creatorClaim) {
|
||||||
|
console.error("Can't find creator claim"); // eslint-disable-line
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_STARTED : ACTIONS.COMMENT_MODERATION_BLOCK_STARTED,
|
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_STARTED : ACTIONS.COMMENT_MODERATION_BLOCK_STARTED,
|
||||||
data: {
|
data: {
|
||||||
uri: channelUri,
|
blockedUri: commenterUri,
|
||||||
|
creatorUri: creatorClaim ? creatorClaim.permanent_url : undefined,
|
||||||
|
blockLevel: blockLevel,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const creatorIdForAction = claim ? claim.claim_id : null;
|
const commenterIdForAction = commenterClaim ? commenterClaim.claim_id : null;
|
||||||
const creatorNameForAction = claim ? claim.name : null;
|
const commenterNameForAction = commenterClaim ? commenterClaim.name : null;
|
||||||
|
|
||||||
let channelSignatures = [];
|
let channelSignatures = [];
|
||||||
|
|
||||||
const sharedModBlockParams = unblock
|
const sharedModBlockParams = unblock
|
||||||
? {
|
? {
|
||||||
un_blocked_channel_id: creatorIdForAction,
|
un_blocked_channel_id: commenterIdForAction,
|
||||||
un_blocked_channel_name: creatorNameForAction,
|
un_blocked_channel_name: commenterNameForAction,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
blocked_channel_id: creatorIdForAction,
|
blocked_channel_id: commenterIdForAction,
|
||||||
blocked_channel_name: creatorNameForAction,
|
blocked_channel_name: commenterNameForAction,
|
||||||
};
|
};
|
||||||
|
|
||||||
const commentAction = unblock ? Comments.moderation_unblock : Comments.moderation_block;
|
const commentAction = unblock ? Comments.moderation_unblock : Comments.moderation_block;
|
||||||
|
|
||||||
return Promise.all(myChannels.map((channel) => channelSignName(channel.claim_id, channel.name)))
|
return Promise.all(blockerChannelClaims.map((x) => channelSignName(x.claim_id, x.name)))
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
channelSignatures = response;
|
channelSignatures = response;
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
|
@ -566,49 +613,174 @@ export function doCommentModToggleBlock(channelUri: string, showLink: boolean, u
|
||||||
.filter((x) => x !== undefined && x !== null)
|
.filter((x) => x !== undefined && x !== null)
|
||||||
.map((signatureData) =>
|
.map((signatureData) =>
|
||||||
commentAction({
|
commentAction({
|
||||||
|
// $FlowFixMe
|
||||||
mod_channel_id: signatureData.claim_id,
|
mod_channel_id: signatureData.claim_id,
|
||||||
|
// $FlowFixMe
|
||||||
mod_channel_name: signatureData.name,
|
mod_channel_name: signatureData.name,
|
||||||
|
// $FlowFixMe
|
||||||
signature: signatureData.signature,
|
signature: signatureData.signature,
|
||||||
|
// $FlowFixMe
|
||||||
signing_ts: signatureData.signing_ts,
|
signing_ts: signatureData.signing_ts,
|
||||||
|
creator_channel_id: creatorClaim ? creatorClaim.claim_id : undefined,
|
||||||
|
creator_channel_name: creatorClaim ? creatorClaim.name : undefined,
|
||||||
|
block_all: unblock ? undefined : blockLevel === BLOCK_LEVEL.ADMIN,
|
||||||
|
global_un_block: unblock ? blockLevel === BLOCK_LEVEL.ADMIN : undefined,
|
||||||
...sharedModBlockParams,
|
...sharedModBlockParams,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then((response) => {
|
||||||
dispatch({
|
const failures = [];
|
||||||
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_COMPLETE : ACTIONS.COMMENT_MODERATION_BLOCK_COMPLETE,
|
|
||||||
data: { channelUri },
|
response.forEach((res, index) => {
|
||||||
|
if (res.status === 'rejected') {
|
||||||
|
// TODO: This should be error codes
|
||||||
|
if (res.reason.message !== 'validation is disallowed for non controlling channels') {
|
||||||
|
// $FlowFixMe
|
||||||
|
failures.push(channelSignatures[index].name + ': ' + res.reason.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(doToast({
|
if (failures.length !== 0) {
|
||||||
message: __(!unblock ? 'Channel blocked. They will not interact with you again.' : 'Channel unblocked!'),
|
dispatch(doToast({ message: failures.join(), isError: true }));
|
||||||
linkText: __(showLink ? 'See All' : ''),
|
dispatch({
|
||||||
linkTarget: '/settings/block_and_mute',
|
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED : ACTIONS.COMMENT_MODERATION_BLOCK_FAILED,
|
||||||
}));
|
data: {
|
||||||
|
blockedUri: commenterUri,
|
||||||
|
creatorUri: creatorClaim ? creatorClaim.permanent_url : undefined,
|
||||||
|
blockLevel: blockLevel,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_COMPLETE : ACTIONS.COMMENT_MODERATION_BLOCK_COMPLETE,
|
||||||
|
data: {
|
||||||
|
blockedUri: commenterUri,
|
||||||
|
creatorUri: creatorClaim ? creatorClaim.permanent_url : undefined,
|
||||||
|
blockLevel: blockLevel,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
doToast({
|
||||||
|
message: unblock
|
||||||
|
? __('Channel unblocked!')
|
||||||
|
: __('Channel "%channel%" blocked.', { channel: commenterNameForAction }),
|
||||||
|
linkText: __(showLink ? 'See All' : ''),
|
||||||
|
linkTarget: '/settings/block_and_mute',
|
||||||
|
})
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED : ACTIONS.COMMENT_MODERATION_BLOCK_FAILED,
|
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED : ACTIONS.COMMENT_MODERATION_BLOCK_FAILED,
|
||||||
|
data: {
|
||||||
|
blockedUri: commenterUri,
|
||||||
|
creatorUri: creatorClaim ? creatorClaim.permanent_url : undefined,
|
||||||
|
blockLevel: blockLevel,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED : ACTIONS.COMMENT_MODERATION_BLOCK_FAILED,
|
type: unblock ? ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED : ACTIONS.COMMENT_MODERATION_BLOCK_FAILED,
|
||||||
|
data: {
|
||||||
|
blockedUri: commenterUri,
|
||||||
|
creatorUri: creatorClaim ? creatorClaim.permanent_url : undefined,
|
||||||
|
blockLevel: blockLevel,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doCommentModBlock(commentAuthor: string, showLink: boolean = true) {
|
/**
|
||||||
|
* Blocks the commenter for all channels that I own.
|
||||||
|
*
|
||||||
|
* @param commenterUri
|
||||||
|
* @param showLink
|
||||||
|
* @returns {function(Dispatch): *}
|
||||||
|
*/
|
||||||
|
export function doCommentModBlock(commenterUri: string, showLink: boolean = true) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
return dispatch(doCommentModToggleBlock(commentAuthor, showLink));
|
return dispatch(doCommentModToggleBlock(false, commenterUri, '', [], BLOCK_LEVEL.SELF, showLink));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doCommentModUnBlock(commentAuthor: string, showLink: boolean = true) {
|
/**
|
||||||
|
* Blocks the commenter using the given channel that has Global privileges.
|
||||||
|
*
|
||||||
|
* @param commenterUri
|
||||||
|
* @param blockerId
|
||||||
|
* @returns {function(Dispatch): *}
|
||||||
|
*/
|
||||||
|
export function doCommentModBlockAsAdmin(commenterUri: string, blockerId: string) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
return dispatch(doCommentModToggleBlock(commentAuthor, showLink, true));
|
return dispatch(doCommentModToggleBlock(false, commenterUri, '', blockerId ? [blockerId] : [], BLOCK_LEVEL.ADMIN));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks the commenter using the given channel that has been granted
|
||||||
|
* moderation rights by the creator.
|
||||||
|
*
|
||||||
|
* @param commenterUri
|
||||||
|
* @param creatorId
|
||||||
|
* @param blockerId
|
||||||
|
* @returns {function(Dispatch): *}
|
||||||
|
*/
|
||||||
|
export function doCommentModBlockAsModerator(commenterUri: string, creatorId: string, blockerId: string) {
|
||||||
|
return (dispatch: Dispatch) => {
|
||||||
|
return dispatch(
|
||||||
|
doCommentModToggleBlock(false, commenterUri, creatorId, blockerId ? [blockerId] : [], BLOCK_LEVEL.MODERATOR)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblocks the commenter for all channels that I own.
|
||||||
|
*
|
||||||
|
* @param commenterUri
|
||||||
|
* @param showLink
|
||||||
|
* @returns {function(Dispatch): *}
|
||||||
|
*/
|
||||||
|
export function doCommentModUnBlock(commenterUri: string, showLink: boolean = true) {
|
||||||
|
return (dispatch: Dispatch) => {
|
||||||
|
return dispatch(doCommentModToggleBlock(true, commenterUri, '', [], BLOCK_LEVEL.SELF, showLink));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblocks the commenter using the given channel that has Global privileges.
|
||||||
|
*
|
||||||
|
* @param commenterUri
|
||||||
|
* @param blockerId
|
||||||
|
* @returns {function(Dispatch): *}
|
||||||
|
*/
|
||||||
|
export function doCommentModUnBlockAsAdmin(commenterUri: string, blockerId: string) {
|
||||||
|
return (dispatch: Dispatch) => {
|
||||||
|
return dispatch(doCommentModToggleBlock(true, commenterUri, '', blockerId ? [blockerId] : [], BLOCK_LEVEL.ADMIN));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblocks the commenter using the given channel that has been granted
|
||||||
|
* moderation rights by the creator.
|
||||||
|
*
|
||||||
|
* @param commenterUri
|
||||||
|
* @param creatorId
|
||||||
|
* @param blockerId
|
||||||
|
* @returns {function(Dispatch): *}
|
||||||
|
*/
|
||||||
|
export function doCommentModUnBlockAsModerator(commenterUri: string, creatorId: string, blockerId: string) {
|
||||||
|
return (dispatch: Dispatch) => {
|
||||||
|
return dispatch(
|
||||||
|
doCommentModToggleBlock(true, commenterUri, creatorId, blockerId ? [blockerId] : [], BLOCK_LEVEL.MODERATOR)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,39 +812,80 @@ export function doFetchModBlockedList() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const blockLists = res.map((r) => r.value);
|
let personalBlockList = [];
|
||||||
let globalBlockList = [];
|
let adminBlockList = [];
|
||||||
blockLists
|
let moderatorBlockList = [];
|
||||||
|
let moderatorBlockListDelegatorsMap = {};
|
||||||
|
|
||||||
|
const blockListsPerChannel = res.map((r) => r.value);
|
||||||
|
blockListsPerChannel
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return 1;
|
return 1;
|
||||||
})
|
})
|
||||||
.forEach((channelBlockListData) => {
|
.forEach((channelBlockLists) => {
|
||||||
const blockListForChannel = channelBlockListData && channelBlockListData.blocked_channels;
|
const storeList = (fetchedList, blockedList, blockedByMap) => {
|
||||||
if (blockListForChannel) {
|
if (fetchedList) {
|
||||||
blockListForChannel.forEach((blockedChannel) => {
|
fetchedList.forEach((blockedChannel) => {
|
||||||
if (blockedChannel.blocked_channel_name) {
|
if (blockedChannel.blocked_channel_name) {
|
||||||
const channelUri = buildURI({
|
const channelUri = buildURI({
|
||||||
channelName: blockedChannel.blocked_channel_name,
|
channelName: blockedChannel.blocked_channel_name,
|
||||||
claimId: blockedChannel.blocked_channel_id,
|
claimId: blockedChannel.blocked_channel_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!globalBlockList.find((blockedChannel) => blockedChannel.channelUri === channelUri)) {
|
if (!blockedList.find((blockedChannel) => blockedChannel.channelUri === channelUri)) {
|
||||||
globalBlockList.push({ channelUri, blockedAt: blockedChannel.blocked_at });
|
blockedList.push({ channelUri, blockedAt: blockedChannel.blocked_at });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockedByMap !== undefined) {
|
||||||
|
const blockedByChannelUri = buildURI({
|
||||||
|
channelName: blockedChannel.blocked_by_channel_name,
|
||||||
|
claimId: blockedChannel.blocked_by_channel_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockedByMap[channelUri]) {
|
||||||
|
if (!blockedByMap[channelUri].includes(blockedByChannelUri)) {
|
||||||
|
blockedByMap[channelUri].push(blockedByChannelUri);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blockedByMap[channelUri] = [blockedByChannelUri];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const blocked_channels = channelBlockLists && channelBlockLists.blocked_channels;
|
||||||
|
const globally_blocked_channels = channelBlockLists && channelBlockLists.globally_blocked_channels;
|
||||||
|
const delegated_blocked_channels = channelBlockLists && channelBlockLists.delegated_blocked_channels;
|
||||||
|
|
||||||
|
storeList(blocked_channels, personalBlockList);
|
||||||
|
storeList(globally_blocked_channels, adminBlockList);
|
||||||
|
storeList(delegated_blocked_channels, moderatorBlockList, moderatorBlockListDelegatorsMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.COMMENT_MODERATION_BLOCK_LIST_COMPLETED,
|
type: ACTIONS.COMMENT_MODERATION_BLOCK_LIST_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
blockList:
|
personalBlockList:
|
||||||
globalBlockList.length > 0
|
personalBlockList.length > 0
|
||||||
? globalBlockList
|
? personalBlockList
|
||||||
.sort((a, b) => new Date(a.blockedAt) - new Date(b.blockedAt))
|
.sort((a, b) => new Date(a.blockedAt) - new Date(b.blockedAt))
|
||||||
.map((blockedChannel) => blockedChannel.channelUri)
|
.map((blockedChannel) => blockedChannel.channelUri)
|
||||||
: null,
|
: null,
|
||||||
|
adminBlockList:
|
||||||
|
adminBlockList.length > 0
|
||||||
|
? adminBlockList
|
||||||
|
.sort((a, b) => new Date(a.blockedAt) - new Date(b.blockedAt))
|
||||||
|
.map((blockedChannel) => blockedChannel.channelUri)
|
||||||
|
: null,
|
||||||
|
moderatorBlockList:
|
||||||
|
moderatorBlockList.length > 0
|
||||||
|
? moderatorBlockList
|
||||||
|
.sort((a, b) => new Date(a.blockedAt) - new Date(b.blockedAt))
|
||||||
|
.map((blockedChannel) => blockedChannel.channelUri)
|
||||||
|
: null,
|
||||||
|
moderatorBlockListDelegatorsMap: moderatorBlockListDelegatorsMap,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -729,6 +942,190 @@ export const doUpdateBlockListForPublishedChannel = (channelClaim: ChannelClaim)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function doCommentModAddDelegate(
|
||||||
|
modChannelId: string,
|
||||||
|
modChannelName: string,
|
||||||
|
creatorChannelClaim: ChannelClaim
|
||||||
|
) {
|
||||||
|
return async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
let signature: ?{
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
signature = await Lbry.channel_sign({
|
||||||
|
channel_id: creatorChannelClaim.claim_id,
|
||||||
|
hexdata: toHex(creatorChannelClaim.name),
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (!signature) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Comments.moderation_add_delegate({
|
||||||
|
mod_channel_id: modChannelId,
|
||||||
|
mod_channel_name: modChannelName,
|
||||||
|
creator_channel_id: creatorChannelClaim.claim_id,
|
||||||
|
creator_channel_name: creatorChannelClaim.name,
|
||||||
|
signature: signature.signature,
|
||||||
|
signing_ts: signature.signing_ts,
|
||||||
|
}).catch((err) => {
|
||||||
|
dispatch(
|
||||||
|
doToast({
|
||||||
|
message: err.message,
|
||||||
|
isError: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCommentModRemoveDelegate(
|
||||||
|
modChannelId: string,
|
||||||
|
modChannelName: string,
|
||||||
|
creatorChannelClaim: ChannelClaim
|
||||||
|
) {
|
||||||
|
return async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
let signature: ?{
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
signature = await Lbry.channel_sign({
|
||||||
|
channel_id: creatorChannelClaim.claim_id,
|
||||||
|
hexdata: toHex(creatorChannelClaim.name),
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (!signature) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Comments.moderation_remove_delegate({
|
||||||
|
mod_channel_id: modChannelId,
|
||||||
|
mod_channel_name: modChannelName,
|
||||||
|
creator_channel_id: creatorChannelClaim.claim_id,
|
||||||
|
creator_channel_name: creatorChannelClaim.name,
|
||||||
|
signature: signature.signature,
|
||||||
|
signing_ts: signature.signing_ts,
|
||||||
|
}).catch((err) => {
|
||||||
|
dispatch(
|
||||||
|
doToast({
|
||||||
|
message: err.message,
|
||||||
|
isError: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCommentModListDelegates(channelClaim: ChannelClaim) {
|
||||||
|
return async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
let signature: ?{
|
||||||
|
signature: string,
|
||||||
|
signing_ts: string,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
signature = await Lbry.channel_sign({
|
||||||
|
channel_id: channelClaim.claim_id,
|
||||||
|
hexdata: toHex(channelClaim.name),
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (!signature) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_FAILED,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Comments.moderation_list_delegates({
|
||||||
|
creator_channel_id: channelClaim.claim_id,
|
||||||
|
creator_channel_name: channelClaim.name,
|
||||||
|
signature: signature.signature,
|
||||||
|
signing_ts: signature.signing_ts,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_COMPLETED,
|
||||||
|
data: {
|
||||||
|
id: channelClaim.claim_id,
|
||||||
|
delegates: response.Delegates,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_FAILED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doFetchCommentModAmIList(channelClaim: ChannelClaim) {
|
||||||
|
return async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
const state = getState();
|
||||||
|
const myChannels = selectMyChannelClaims(state);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_MODERATION_AM_I_LIST_STARTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
let channelSignatures = [];
|
||||||
|
|
||||||
|
return Promise.all(myChannels.map((channel) => channelSignName(channel.claim_id, channel.name)))
|
||||||
|
.then((response) => {
|
||||||
|
channelSignatures = response;
|
||||||
|
// $FlowFixMe
|
||||||
|
return Promise.allSettled(
|
||||||
|
channelSignatures
|
||||||
|
.filter((x) => x !== undefined && x !== null)
|
||||||
|
.map((signatureData) =>
|
||||||
|
Comments.moderation_am_i({
|
||||||
|
channel_name: signatureData.name,
|
||||||
|
channel_id: signatureData.claim_id,
|
||||||
|
signature: signatureData.signature,
|
||||||
|
signing_ts: signatureData.signing_ts,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
const delegatorsById = {};
|
||||||
|
|
||||||
|
channelSignatures.forEach((chanSig, index) => {
|
||||||
|
if (chanSig && res[index]) {
|
||||||
|
const value = res[index].value;
|
||||||
|
delegatorsById[chanSig.claim_id] = {
|
||||||
|
global: value ? value.type === 'Global' : false,
|
||||||
|
delegators: value && value.authorized_channels ? value.authorized_channels : {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_MODERATION_AM_I_LIST_COMPLETED,
|
||||||
|
data: delegatorsById,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_MODERATION_AM_I_LIST_FAILED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.COMMENT_MODERATION_AM_I_LIST_FAILED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const doFetchCreatorSettings = (channelClaimIds: Array<string> = []) => {
|
export const doFetchCreatorSettings = (channelClaimIds: Array<string> = []) => {
|
||||||
return async (dispatch: Dispatch, getState: GetState) => {
|
return async (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import { handleActions } from 'util/redux-utils';
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
import { BLOCK_LEVEL } from 'constants/comment';
|
||||||
|
|
||||||
const defaultState: CommentsState = {
|
const defaultState: CommentsState = {
|
||||||
commentById: {}, // commentId -> Comment
|
commentById: {}, // commentId -> Comment
|
||||||
|
@ -21,9 +22,17 @@ const defaultState: CommentsState = {
|
||||||
myReactsByCommentId: undefined,
|
myReactsByCommentId: undefined,
|
||||||
othersReactsByCommentId: undefined,
|
othersReactsByCommentId: undefined,
|
||||||
moderationBlockList: undefined,
|
moderationBlockList: undefined,
|
||||||
|
adminBlockList: undefined,
|
||||||
|
moderatorBlockList: undefined,
|
||||||
|
moderatorBlockListDelegatorsMap: {},
|
||||||
fetchingModerationBlockList: false,
|
fetchingModerationBlockList: false,
|
||||||
|
moderationDelegatesById: {},
|
||||||
|
fetchingModerationDelegates: false,
|
||||||
|
moderationDelegatorsById: {},
|
||||||
|
fetchingModerationDelegators: false,
|
||||||
blockingByUri: {},
|
blockingByUri: {},
|
||||||
unBlockingByUri: {},
|
unBlockingByUri: {},
|
||||||
|
togglingForDelegatorMap: {},
|
||||||
commentsDisabledChannelIds: [],
|
commentsDisabledChannelIds: [],
|
||||||
settingsByChannelId: {}, // ChannelId -> PerChannelSettings
|
settingsByChannelId: {}, // ChannelId -> PerChannelSettings
|
||||||
fetchingSettings: false,
|
fetchingSettings: false,
|
||||||
|
@ -391,11 +400,14 @@ export default handleActions(
|
||||||
fetchingModerationBlockList: true,
|
fetchingModerationBlockList: true,
|
||||||
}),
|
}),
|
||||||
[ACTIONS.COMMENT_MODERATION_BLOCK_LIST_COMPLETED]: (state: CommentsState, action: any) => {
|
[ACTIONS.COMMENT_MODERATION_BLOCK_LIST_COMPLETED]: (state: CommentsState, action: any) => {
|
||||||
const { blockList } = action.data;
|
const { personalBlockList, adminBlockList, moderatorBlockList, moderatorBlockListDelegatorsMap } = action.data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
moderationBlockList: blockList,
|
moderationBlockList: personalBlockList,
|
||||||
|
adminBlockList: adminBlockList,
|
||||||
|
moderatorBlockList: moderatorBlockList,
|
||||||
|
moderatorBlockListDelegatorsMap: moderatorBlockListDelegatorsMap,
|
||||||
fetchingModerationBlockList: false,
|
fetchingModerationBlockList: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -404,75 +416,312 @@ export default handleActions(
|
||||||
fetchingModerationBlockList: false,
|
fetchingModerationBlockList: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[ACTIONS.COMMENT_MODERATION_BLOCK_STARTED]: (state: CommentsState, action: any) => ({
|
[ACTIONS.COMMENT_MODERATION_BLOCK_STARTED]: (state: CommentsState, action: any) => {
|
||||||
...state,
|
const { blockedUri, creatorUri, blockLevel } = action.data;
|
||||||
blockingByUri: {
|
|
||||||
...state.blockingByUri,
|
|
||||||
[action.data.uri]: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
[ACTIONS.COMMENT_MODERATION_UN_BLOCK_STARTED]: (state: CommentsState, action: any) => ({
|
switch (blockLevel) {
|
||||||
...state,
|
default:
|
||||||
unBlockingByUri: {
|
case BLOCK_LEVEL.SELF:
|
||||||
...state.unBlockingByUri,
|
case BLOCK_LEVEL.ADMIN:
|
||||||
[action.data.uri]: true,
|
return {
|
||||||
},
|
...state,
|
||||||
}),
|
blockingByUri: {
|
||||||
[ACTIONS.COMMENT_MODERATION_BLOCK_FAILED]: (state: CommentsState, action: any) => ({
|
...state.blockingByUri,
|
||||||
...state,
|
[blockedUri]: true,
|
||||||
blockingByUri: {
|
},
|
||||||
...state.blockingByUri,
|
};
|
||||||
[action.data.uri]: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
[ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED]: (state: CommentsState, action: any) => ({
|
case BLOCK_LEVEL.MODERATOR:
|
||||||
...state,
|
const newMap = Object.assign({}, state.togglingForDelegatorMap);
|
||||||
unBlockingByUri: {
|
const togglingDelegatorsForBlockedUri = newMap[blockedUri];
|
||||||
...state.unBlockingByUri,
|
if (togglingDelegatorsForBlockedUri) {
|
||||||
[action.data.uri]: false,
|
if (!togglingDelegatorsForBlockedUri.includes(creatorUri)) {
|
||||||
},
|
togglingDelegatorsForBlockedUri.push(creatorUri);
|
||||||
}),
|
}
|
||||||
|
} else {
|
||||||
|
newMap[blockedUri] = [creatorUri];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
togglingForDelegatorMap: newMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_MODERATION_UN_BLOCK_STARTED]: (state: CommentsState, action: any) => {
|
||||||
|
const { blockedUri, creatorUri, blockLevel } = action.data;
|
||||||
|
|
||||||
|
switch (blockLevel) {
|
||||||
|
default:
|
||||||
|
case BLOCK_LEVEL.SELF:
|
||||||
|
case BLOCK_LEVEL.ADMIN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unBlockingByUri: {
|
||||||
|
...state.unBlockingByUri,
|
||||||
|
[blockedUri]: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.MODERATOR:
|
||||||
|
const newMap = Object.assign({}, state.togglingForDelegatorMap);
|
||||||
|
const togglingDelegatorsForBlockedUri = newMap[blockedUri];
|
||||||
|
if (togglingDelegatorsForBlockedUri) {
|
||||||
|
if (!togglingDelegatorsForBlockedUri.includes(creatorUri)) {
|
||||||
|
togglingDelegatorsForBlockedUri.push(creatorUri);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newMap[blockedUri] = [creatorUri];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
togglingForDelegatorMap: newMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_MODERATION_BLOCK_FAILED]: (state: CommentsState, action: any) => {
|
||||||
|
const { blockedUri, creatorUri, blockLevel } = action.data;
|
||||||
|
|
||||||
|
switch (blockLevel) {
|
||||||
|
default:
|
||||||
|
case BLOCK_LEVEL.SELF:
|
||||||
|
case BLOCK_LEVEL.ADMIN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
blockingByUri: {
|
||||||
|
...state.blockingByUri,
|
||||||
|
[blockedUri]: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.MODERATOR:
|
||||||
|
const newMap = Object.assign({}, state.togglingForDelegatorMap);
|
||||||
|
const togglingDelegatorsForBlockedUri = newMap[blockedUri];
|
||||||
|
if (togglingDelegatorsForBlockedUri) {
|
||||||
|
newMap[blockedUri] = togglingDelegatorsForBlockedUri.filter((x) => x !== creatorUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
togglingForDelegatorMap: newMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_MODERATION_UN_BLOCK_FAILED]: (state: CommentsState, action: any) => {
|
||||||
|
const { blockedUri, creatorUri, blockLevel } = action.data;
|
||||||
|
|
||||||
|
switch (blockLevel) {
|
||||||
|
default:
|
||||||
|
case BLOCK_LEVEL.SELF:
|
||||||
|
case BLOCK_LEVEL.ADMIN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unBlockingByUri: {
|
||||||
|
...state.unBlockingByUri,
|
||||||
|
[blockedUri]: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.MODERATOR:
|
||||||
|
const newMap = Object.assign({}, state.togglingForDelegatorMap);
|
||||||
|
const togglingDelegatorsForBlockedUri = newMap[blockedUri];
|
||||||
|
if (togglingDelegatorsForBlockedUri) {
|
||||||
|
newMap[blockedUri] = togglingDelegatorsForBlockedUri.filter((x) => x !== creatorUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
togglingForDelegatorMap: newMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
[ACTIONS.COMMENT_MODERATION_BLOCK_COMPLETE]: (state: CommentsState, action: any) => {
|
[ACTIONS.COMMENT_MODERATION_BLOCK_COMPLETE]: (state: CommentsState, action: any) => {
|
||||||
const { channelUri } = action.data;
|
const { blockedUri, creatorUri, blockLevel } = action.data;
|
||||||
const commentById = Object.assign({}, state.commentById);
|
const commentById = Object.assign({}, state.commentById);
|
||||||
const blockingByUri = Object.assign({}, state.blockingByUri);
|
const blockingByUri = Object.assign({}, state.blockingByUri);
|
||||||
const moderationBlockList = state.moderationBlockList || [];
|
|
||||||
const newModerationBlockList = moderationBlockList.slice();
|
|
||||||
|
|
||||||
for (const commentId in commentById) {
|
for (const commentId in commentById) {
|
||||||
const comment = commentById[commentId];
|
const comment = commentById[commentId];
|
||||||
|
|
||||||
if (channelUri === comment.channel_url) {
|
if (blockedUri === comment.channel_url) {
|
||||||
delete commentById[comment.comment_id];
|
delete commentById[comment.comment_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete blockingByUri[channelUri];
|
switch (blockLevel) {
|
||||||
|
case BLOCK_LEVEL.SELF: {
|
||||||
|
const blockList = state.moderationBlockList || [];
|
||||||
|
const newBlockList = blockList.slice();
|
||||||
|
newBlockList.push(blockedUri);
|
||||||
|
delete blockingByUri[blockedUri];
|
||||||
|
|
||||||
newModerationBlockList.push(channelUri);
|
return {
|
||||||
|
...state,
|
||||||
|
commentById,
|
||||||
|
blockingByUri,
|
||||||
|
moderationBlockList: newBlockList,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
case BLOCK_LEVEL.MODERATOR: {
|
||||||
...state,
|
const blockList = state.moderatorBlockList || [];
|
||||||
commentById,
|
const newBlockList = blockList.slice();
|
||||||
blockingByUri,
|
|
||||||
moderationBlockList: newModerationBlockList,
|
// Update main block list
|
||||||
};
|
if (!newBlockList.includes(blockedUri)) {
|
||||||
|
newBlockList.push(blockedUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update list of delegators
|
||||||
|
const moderatorBlockListDelegatorsMap = Object.assign({}, state.moderatorBlockListDelegatorsMap);
|
||||||
|
const delegatorUrisForBlockedUri = moderatorBlockListDelegatorsMap[blockedUri];
|
||||||
|
if (delegatorUrisForBlockedUri) {
|
||||||
|
if (!delegatorUrisForBlockedUri.includes(creatorUri)) {
|
||||||
|
delegatorUrisForBlockedUri.push(creatorUri);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
moderatorBlockListDelegatorsMap[blockedUri] = [creatorUri];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "toggling" flag
|
||||||
|
const togglingMap = Object.assign({}, state.togglingForDelegatorMap);
|
||||||
|
const togglingDelegatorsForBlockedUri = togglingMap[blockedUri];
|
||||||
|
if (togglingDelegatorsForBlockedUri) {
|
||||||
|
togglingMap[blockedUri] = togglingDelegatorsForBlockedUri.filter((x) => x !== creatorUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
commentById,
|
||||||
|
moderatorBlockList: newBlockList,
|
||||||
|
moderatorBlockListDelegatorsMap,
|
||||||
|
togglingForDelegatorMap: togglingMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.ADMIN:
|
||||||
|
const blockList = state.adminBlockList || [];
|
||||||
|
const newBlockList = blockList.slice();
|
||||||
|
newBlockList.push(blockedUri);
|
||||||
|
delete blockingByUri[blockedUri];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
commentById,
|
||||||
|
blockingByUri,
|
||||||
|
adminBlockList: newBlockList,
|
||||||
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[ACTIONS.COMMENT_MODERATION_UN_BLOCK_COMPLETE]: (state: CommentsState, action: any) => {
|
[ACTIONS.COMMENT_MODERATION_UN_BLOCK_COMPLETE]: (state: CommentsState, action: any) => {
|
||||||
const { channelUri } = action.data;
|
const { blockedUri, creatorUri, blockLevel } = action.data;
|
||||||
const unBlockingByUri = Object.assign(state.unBlockingByUri, {});
|
const unBlockingByUri = Object.assign(state.unBlockingByUri, {});
|
||||||
const moderationBlockList = state.moderationBlockList || [];
|
|
||||||
const newModerationBlockList = moderationBlockList.slice().filter((uri) => uri !== channelUri);
|
|
||||||
|
|
||||||
delete unBlockingByUri[channelUri];
|
switch (blockLevel) {
|
||||||
|
case BLOCK_LEVEL.SELF: {
|
||||||
|
const blockList = state.moderationBlockList || [];
|
||||||
|
delete unBlockingByUri[blockedUri];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unBlockingByUri,
|
||||||
|
moderationBlockList: blockList.slice().filter((uri) => uri !== blockedUri),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.ADMIN: {
|
||||||
|
const blockList = state.adminBlockList || [];
|
||||||
|
delete unBlockingByUri[blockedUri];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
unBlockingByUri,
|
||||||
|
adminBlockList: blockList.slice().filter((uri) => uri !== blockedUri),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case BLOCK_LEVEL.MODERATOR: {
|
||||||
|
const blockList = state.moderatorBlockList || [];
|
||||||
|
const newBlockList = blockList.slice();
|
||||||
|
const togglingMap = Object.assign({}, state.togglingForDelegatorMap);
|
||||||
|
|
||||||
|
const moderatorBlockListDelegatorsMap = Object.assign({}, state.moderatorBlockListDelegatorsMap);
|
||||||
|
const delegatorUrisForBlockedUri = moderatorBlockListDelegatorsMap[blockedUri];
|
||||||
|
if (delegatorUrisForBlockedUri) {
|
||||||
|
const index = delegatorUrisForBlockedUri.indexOf(creatorUri);
|
||||||
|
if (index > -1) {
|
||||||
|
// Remove from delegators list
|
||||||
|
delegatorUrisForBlockedUri.splice(index, 1);
|
||||||
|
|
||||||
|
// // Remove blocked entry if it was removed for all delegators
|
||||||
|
// if (delegatorUrisForBlockedUri.length === 0) {
|
||||||
|
// delete moderatorBlockListDelegatorsMap[blockedUri];
|
||||||
|
// newBlockList = newBlockList.filter((uri) => uri !== blockedUri);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Remove from "toggling" flag
|
||||||
|
const togglingDelegatorsForBlockedUri = togglingMap[blockedUri];
|
||||||
|
if (togglingDelegatorsForBlockedUri) {
|
||||||
|
togglingMap[blockedUri] = togglingDelegatorsForBlockedUri.filter((x) => x !== creatorUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
moderatorBlockList: newBlockList,
|
||||||
|
togglingForDelegatorMap: togglingMap,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_STARTED]: (state: CommentsState, action: any) => ({
|
||||||
|
...state,
|
||||||
|
fetchingModerationDelegates: true,
|
||||||
|
}),
|
||||||
|
[ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_FAILED]: (state: CommentsState, action: any) => ({
|
||||||
|
...state,
|
||||||
|
fetchingModerationDelegates: false,
|
||||||
|
}),
|
||||||
|
[ACTIONS.COMMENT_FETCH_MODERATION_DELEGATES_COMPLETED]: (state: CommentsState, action: any) => {
|
||||||
|
const moderationDelegatesById = Object.assign({}, state.moderationDelegatesById);
|
||||||
|
if (action.data.delegates) {
|
||||||
|
moderationDelegatesById[action.data.id] = action.data.delegates.map((delegate) => {
|
||||||
|
return {
|
||||||
|
channelId: delegate.channel_id,
|
||||||
|
channelName: delegate.channel_name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
moderationDelegatesById[action.data.id] = [];
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
unBlockingByUri,
|
fetchingModerationDelegates: false,
|
||||||
moderationBlockList: newModerationBlockList,
|
moderationDelegatesById: moderationDelegatesById,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_MODERATION_AM_I_LIST_STARTED]: (state: CommentsState, action: any) => ({
|
||||||
|
...state,
|
||||||
|
fetchingModerationDelegators: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_MODERATION_AM_I_LIST_FAILED]: (state: CommentsState, action: any) => ({
|
||||||
|
...state,
|
||||||
|
fetchingModerationDelegators: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
[ACTIONS.COMMENT_MODERATION_AM_I_LIST_COMPLETED]: (state: CommentsState, action: any) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
fetchingModerationDelegators: true,
|
||||||
|
moderationDelegatorsById: action.data,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,24 @@ export const selectCommentsDisabledChannelIds = createSelector(
|
||||||
(state) => state.commentsDisabledChannelIds
|
(state) => state.commentsDisabledChannelIds
|
||||||
);
|
);
|
||||||
export const selectOthersReactsById = createSelector(selectState, (state) => state.othersReactsByCommentId);
|
export const selectOthersReactsById = createSelector(selectState, (state) => state.othersReactsByCommentId);
|
||||||
|
|
||||||
export const selectModerationBlockList = createSelector(selectState, (state) =>
|
export const selectModerationBlockList = createSelector(selectState, (state) =>
|
||||||
state.moderationBlockList ? state.moderationBlockList.reverse() : []
|
state.moderationBlockList ? state.moderationBlockList.reverse() : []
|
||||||
);
|
);
|
||||||
|
export const selectAdminBlockList = createSelector(selectState, (state) =>
|
||||||
|
state.adminBlockList ? state.adminBlockList.reverse() : []
|
||||||
|
);
|
||||||
|
export const selectModeratorBlockList = createSelector(selectState, (state) =>
|
||||||
|
state.moderatorBlockList ? state.moderatorBlockList.reverse() : []
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectModeratorBlockListDelegatorsMap = createSelector(
|
||||||
|
selectState,
|
||||||
|
(state) => state.moderatorBlockListDelegatorsMap
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectTogglingForDelegatorMap = createSelector(selectState, (state) => state.togglingForDelegatorMap);
|
||||||
|
|
||||||
export const selectBlockingByUri = createSelector(selectState, (state) => state.blockingByUri);
|
export const selectBlockingByUri = createSelector(selectState, (state) => state.blockingByUri);
|
||||||
export const selectUnBlockingByUri = createSelector(selectState, (state) => state.unBlockingByUri);
|
export const selectUnBlockingByUri = createSelector(selectState, (state) => state.unBlockingByUri);
|
||||||
export const selectFetchingModerationBlockList = createSelector(
|
export const selectFetchingModerationBlockList = createSelector(
|
||||||
|
@ -26,6 +41,32 @@ export const selectFetchingModerationBlockList = createSelector(
|
||||||
(state) => state.fetchingModerationBlockList
|
(state) => state.fetchingModerationBlockList
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectModerationDelegatesById = createSelector(selectState, (state) => state.moderationDelegatesById);
|
||||||
|
export const selectIsFetchingModerationDelegates = createSelector(
|
||||||
|
selectState,
|
||||||
|
(state) => state.fetchingModerationDelegates
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectModerationDelegatorsById = createSelector(selectState, (state) => state.moderationDelegatorsById);
|
||||||
|
export const selectIsFetchingModerationDelegators = createSelector(
|
||||||
|
selectState,
|
||||||
|
(state) => state.fetchingModerationDelegators
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectHasAdminChannel = createSelector(selectState, (state) => {
|
||||||
|
const myChannelIds = Object.keys(state.moderationDelegatorsById);
|
||||||
|
for (let i = 0; i < myChannelIds.length; ++i) {
|
||||||
|
const id = myChannelIds[i];
|
||||||
|
if (state.moderationDelegatorsById[id] && state.moderationDelegatorsById[id].global) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/// Lint doesn't like this:
|
||||||
|
// return Object.values(state.moderationDelegatorsById).some((x) => x.global);
|
||||||
|
});
|
||||||
|
|
||||||
export const selectCommentsByClaimId = createSelector(selectState, selectCommentsById, (state, byId) => {
|
export const selectCommentsByClaimId = createSelector(selectState, selectCommentsById, (state, byId) => {
|
||||||
const byClaimId = state.byId || {};
|
const byClaimId = state.byId || {};
|
||||||
const comments = {};
|
const comments = {};
|
||||||
|
@ -298,6 +339,7 @@ export const makeSelectTotalCommentsCountForUri = (uri: string) =>
|
||||||
return comments ? comments.length : 0;
|
return comments ? comments.length : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Personal list
|
||||||
export const makeSelectChannelIsBlocked = (uri: string) =>
|
export const makeSelectChannelIsBlocked = (uri: string) =>
|
||||||
createSelector(selectModerationBlockList, (blockedChannelUris) => {
|
createSelector(selectModerationBlockList, (blockedChannelUris) => {
|
||||||
if (!blockedChannelUris || !blockedChannelUris) {
|
if (!blockedChannelUris || !blockedChannelUris) {
|
||||||
|
@ -307,6 +349,27 @@ export const makeSelectChannelIsBlocked = (uri: string) =>
|
||||||
return blockedChannelUris.includes(uri);
|
return blockedChannelUris.includes(uri);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const makeSelectChannelIsAdminBlocked = (uri: string) =>
|
||||||
|
createSelector(selectAdminBlockList, (list) => {
|
||||||
|
return list ? list.includes(uri) : false;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const makeSelectChannelIsModeratorBlocked = (uri: string) =>
|
||||||
|
createSelector(selectModeratorBlockList, (list) => {
|
||||||
|
return list ? list.includes(uri) : false;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const makeSelectChannelIsModeratorBlockedForCreator = (uri: string, creatorUri: string) =>
|
||||||
|
createSelector(selectModeratorBlockList, selectModeratorBlockListDelegatorsMap, (blockList, delegatorsMap) => {
|
||||||
|
if (!blockList) return false;
|
||||||
|
return blockList.includes(uri) && delegatorsMap[uri] && delegatorsMap[uri].includes(creatorUri);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const makeSelectIsTogglingForDelegator = (uri: string, creatorUri: string) =>
|
||||||
|
createSelector(selectTogglingForDelegatorMap, (togglingForDelegatorMap) => {
|
||||||
|
return togglingForDelegatorMap[uri] && togglingForDelegatorMap[uri].includes(creatorUri);
|
||||||
|
});
|
||||||
|
|
||||||
export const makeSelectUriIsBlockingOrUnBlocking = (uri: string) =>
|
export const makeSelectUriIsBlockingOrUnBlocking = (uri: string) =>
|
||||||
createSelector(selectBlockingByUri, selectUnBlockingByUri, (blockingByUri, unBlockingByUri) => {
|
createSelector(selectBlockingByUri, selectUnBlockingByUri, (blockingByUri, unBlockingByUri) => {
|
||||||
return blockingByUri[uri] || unBlockingByUri[uri];
|
return blockingByUri[uri] || unBlockingByUri[uri];
|
||||||
|
|
Loading…
Add table
Reference in a new issue