Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions static/app-strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2983,6 +2983,10 @@
"Creator only": "Creator only",
"Would you like to enable them? Homepage recommendations placement can be configured from the homepage customization.": "Would you like to enable them? Homepage recommendations placement can be configured from the homepage customization.",
"Homepage recommendations available": "Homepage recommendations available",
"Remove this user as a moderator of your channel.": "Remove this user as a moderator of your channel.",
"Remove this user as a moderator of %channel%.": "Remove this user as a moderator of %channel%.",
"Remove as moderator": "Remove as moderator",
"Removed %user% from moderators of %myChannel%": "Removed %user% from moderators of %myChannel%",

"--end--": "--end--"
}
7 changes: 6 additions & 1 deletion ui/component/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ import {
} from 'redux/actions/settings';
import { doSyncLoop } from 'redux/actions/sync';
import { doSignIn, doSetIncognito, doSetAssignedLbrynetServer, doOpenModal } from 'redux/actions/app';
import { doFetchModBlockedList, doFetchCommentModAmIList } from 'redux/actions/comments';
import {
doFetchModBlockedList,
doFetchCommentModAmIList,
doCommentModListDelegatesForMyChannels,
} from 'redux/actions/comments';
import App from './view';

const select = (state) => ({
Expand Down Expand Up @@ -68,6 +72,7 @@ const perform = {
setIncognito: doSetIncognito,
fetchModBlockedList: doFetchModBlockedList,
fetchModAmIList: doFetchCommentModAmIList,
fetchDelegatesForMyChannels: doCommentModListDelegatesForMyChannels,
doOpenAnnouncements,
doSetLastViewedAnnouncement,
doSetDefaultChannel,
Expand Down
3 changes: 3 additions & 0 deletions ui/component/app/view.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type Props = {
setIncognito: (boolean) => void,
fetchModBlockedList: () => void,
fetchModAmIList: () => void,
fetchDelegatesForMyChannels: () => void,
homepageFetched: boolean,
defaultChannelClaim: ?any,
nagsShown: boolean,
Expand Down Expand Up @@ -134,6 +135,7 @@ function App(props: Props) {
setIncognito,
fetchModBlockedList,
fetchModAmIList,
fetchDelegatesForMyChannels,
defaultChannelClaim,
announcement,
homepageOrder,
Expand Down Expand Up @@ -357,6 +359,7 @@ function App(props: Props) {
if (hasMyChannels) {
fetchModBlockedList();
fetchModAmIList();
fetchDelegatesForMyChannels();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasMyChannels, hasNoChannels, setIncognito]);
Expand Down
7 changes: 5 additions & 2 deletions ui/component/commentMenuList/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { connect } from 'react-redux';
import { doChannelMute } from 'redux/actions/blocked';
import { doCommentPin, doCommentModAddDelegate } from 'redux/actions/comments';
import { doCommentPin, doCommentModAddDelegate, doCommentModRemoveDelegate } from 'redux/actions/comments';
import { doOpenModal, doSetActiveChannel } from 'redux/actions/app';
import { doClearPlayingUri } from 'redux/actions/content';
import { doToast } from 'redux/actions/notifications';
import { selectClaimIsMine, selectClaimForUri } from 'redux/selectors/claims';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { selectModerationDelegatorsById } from 'redux/selectors/comments';
import { selectModerationDelegatorsById, selectModerationDelegatesById } from 'redux/selectors/comments';
import { selectPlayingUri } from 'redux/selectors/content';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import CommentMenuList from './view';
Expand All @@ -25,6 +25,7 @@ const select = (state, props) => {
channelIsMine: selectClaimIsMine(state, authorClaim),
playingUri: selectPlayingUri(state),
moderationDelegatorsById: selectModerationDelegatorsById(state),
moderationDelegatesById: selectModerationDelegatesById(state),
authorCanonicalUri,
authorId,
};
Expand All @@ -38,6 +39,8 @@ const perform = (dispatch) => ({
pinComment: (commentId, claimId, remove) => dispatch(doCommentPin(commentId, claimId, remove)),
commentModAddDelegate: (modChanId, modChanName, creatorChannelClaim) =>
dispatch(doCommentModAddDelegate(modChanId, modChanName, creatorChannelClaim, true)),
commentModRemoveDelegate: (modChanId, modChanName, creatorChannelClaim) =>
dispatch(doCommentModRemoveDelegate(modChanId, modChanName, creatorChannelClaim, true)),
doSetActiveChannel: (authorId) => dispatch(doSetActiveChannel(authorId)),
});

Expand Down
46 changes: 39 additions & 7 deletions ui/component/commentMenuList/view.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Props = {
activeChannelClaim: ?ChannelClaim,
playingUri: PlayingUri,
moderationDelegatorsById: { [string]: { global: boolean, delegators: { name: string, claimId: string } } },
moderationDelegatesById: { [?string]: ?Array<Object> },
authorCanonicalUri: ?string,
authorId: string,
// --- perform ---
Expand All @@ -47,6 +48,7 @@ type Props = {
doSetActiveChannel: (string) => void,
pinComment: (string, string, boolean) => Promise<any>,
commentModAddDelegate: (string, string, ChannelClaim) => void,
commentModRemoveDelegate: (string, string, ChannelClaim) => void,
setQuickReply: (any) => void,
handleDismissPin?: () => void,
};
Expand All @@ -66,6 +68,7 @@ function CommentMenuList(props: Props) {
isPinned,
playingUri,
moderationDelegatorsById,
moderationDelegatesById,
authorCanonicalUri,
isAuthenticated,
disableEdit,
Expand All @@ -81,6 +84,7 @@ function CommentMenuList(props: Props) {
doSetActiveChannel,
pinComment,
commentModAddDelegate,
commentModRemoveDelegate,
setQuickReply,
handleDismissPin,
} = props;
Expand All @@ -95,6 +99,8 @@ function CommentMenuList(props: Props) {
const contentChannelClaim = getChannelFromClaim(claim);
const contentChannelPermanentUrl = contentChannelClaim && contentChannelClaim.permanent_url;

const delegates = moderationDelegatesById[contentChannelClaim?.claim_id];

const activeModeratorInfo = activeChannelClaim && moderationDelegatorsById[activeChannelClaim.claim_id];
const activeChannelIsCreator = activeChannelClaim && activeChannelClaim.permanent_url === contentChannelPermanentUrl;
const activeChannelIsAdmin = activeChannelClaim && activeModeratorInfo && activeModeratorInfo.global;
Expand All @@ -103,6 +109,10 @@ function CommentMenuList(props: Props) {
contentChannelClaim &&
activeModeratorInfo &&
Object.values(activeModeratorInfo.delegators).includes(contentChannelClaim.claim_id);
const authorIsModerator =
claimIsMine && // only check for own claims
Array.isArray(delegates) &&
delegates.some((delegate) => delegate.channelId === authorId);

function handleDeleteComment() {
if (playingUri.source === 'comment') {
Expand All @@ -126,6 +136,13 @@ function CommentMenuList(props: Props) {
}
}

function removeModerator() {
if (activeChannelClaim && authorUri) {
const { channelName, channelClaimId } = parseURI(authorUri);
if (channelName && channelClaimId) commentModRemoveDelegate(channelClaimId, channelName, activeChannelClaim);
}
}

function getBlockOptionElem() {
const isPersonalBlockTheOnlyOption = !activeChannelIsModerator && !activeChannelIsAdmin;
const isTimeoutBlockAvailable = claimIsMine || activeChannelIsModerator;
Expand Down Expand Up @@ -256,17 +273,32 @@ function CommentMenuList(props: Props) {
{__('Dismiss Pin')}
</MenuItem>
)}
{/* todo: filter out already active mods (bug with activeModeratorInfo?) */}
{activeChannelIsCreator && activeChannelClaim && activeChannelClaim.permanent_url !== authorUri && (
<MenuItem className="comment__menu-option" onSelect={assignAsModerator}>
{activeChannelIsCreator &&
activeChannelClaim &&
activeChannelClaim.permanent_url !== authorUri &&
!authorIsModerator && (
<MenuItem className="comment__menu-option" onSelect={assignAsModerator}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.ADD} />
{__('Add as moderator')}
</div>
<span className="comment__menu-help">
{activeChannelClaim
? __('Assign this user to moderate %channel%.', { channel: activeChannelClaim.name })
: __('Assign this user to moderate your channel.')}
</span>
</MenuItem>
)}
{activeChannelIsCreator && authorIsModerator && (
<MenuItem className="comment__menu-option" onSelect={removeModerator}>
<div className="menu__link">
<Icon aria-hidden icon={ICONS.ADD} />
{__('Add as moderator')}
<Icon aria-hidden icon={ICONS.REMOVE} />
{__('Remove as moderator')}
</div>
<span className="comment__menu-help">
{activeChannelClaim
? __('Assign this user to moderate %channel%.', { channel: activeChannelClaim.name })
: __('Assign this user to moderate your channel.')}
? __('Remove this user as a moderator of %channel%.', { channel: activeChannelClaim.name })
: __('Remove this user as a moderator of your channel.')}
</span>
</MenuItem>
)}
Expand Down
8 changes: 8 additions & 0 deletions ui/constants/action_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ export const COMMENT_PIN_STARTED = 'COMMENT_PIN_STARTED';
export const COMMENT_PIN_COMPLETED = 'COMMENT_PIN_COMPLETED';
export const COMMENT_PIN_FAILED = 'COMMENT_PIN_FAILED';
export const COMMENT_MARK_AS_REMOVED = 'COMMENT_MARK_AS_REMOVED';
export const ADD_MODERATOR_COMPLETED = 'ADD_MODERATOR_COMPLETED';
export const REMOVE_MODERATOR_COMPLETED = 'REMOVE_MODERATOR_COMPLETED';
export const COMMENT_MODERATION_BLOCK_LIST_STARTED = 'COMMENT_MODERATION_BLOCK_LIST_STARTED';
export const COMMENT_MODERATION_BLOCK_LIST_COMPLETED = 'COMMENT_MODERATION_BLOCK_LIST_COMPLETED';
export const COMMENT_MODERATION_BLOCK_LIST_FAILED = 'COMMENT_MODERATION_BLOCK_LIST_FAILED';
Expand All @@ -568,6 +570,12 @@ export const COMMENT_FETCH_MODERATION_DELEGATES_COMPLETED = 'COMMENT_FETCH_MODER
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_MODERATION_DELEGATES_FOR_MY_CHANNELS_STARTED =
'COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_STARTED';
export const COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_FAILED =
'COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_FAILED';
export const COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_COMPLETED =
'COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_COMPLETED';
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_COMPLETED = 'COMMENT_FETCH_SETTINGS_COMPLETED';
Expand Down
88 changes: 83 additions & 5 deletions ui/redux/actions/comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -1712,7 +1712,11 @@ export function doCommentModAddDelegate(
channel_name: creatorChannelClaim.name,
...signature,
})
.then(() => {
.then((res) => {
dispatch({
type: ACTIONS.ADD_MODERATOR_COMPLETED,
data: { newDelegates: res?.Delegates, creatorChannelId: creatorChannelClaim.claim_id },
});
if (showToast) {
dispatch(
doToast({
Expand All @@ -1735,7 +1739,8 @@ export function doCommentModAddDelegate(
export function doCommentModRemoveDelegate(
modChannelId: string,
modChannelName: string,
creatorChannelClaim: ChannelClaim
creatorChannelClaim: ChannelClaim,
showToast: boolean = false
) {
return async (dispatch: Dispatch, getState: GetState) => {
const signature = await ChannelSign.sign(creatorChannelClaim.claim_id, creatorChannelClaim.name, false);
Expand All @@ -1750,9 +1755,28 @@ export function doCommentModRemoveDelegate(
channel_id: creatorChannelClaim.claim_id,
channel_name: creatorChannelClaim.name,
...signature,
}).catch((err) => {
dispatch(doToast({ message: err.message, isError: true }));
});
})
.then(() => {
dispatch({
type: ACTIONS.REMOVE_MODERATOR_COMPLETED,
data: { removedDelegateId: modChannelId, creatorChannelId: creatorChannelClaim.claim_id },
});
if (showToast) {
dispatch(
doToast({
message: __('Removed %user% from moderators of %myChannel%', {
user: modChannelName,
myChannel: creatorChannelClaim.name,
}),
linkText: __('Manage'),
linkTarget: `/${PAGES.SETTINGS_CREATOR}`,
})
);
}
})
.catch((err) => {
dispatch(doToast({ message: err.message, isError: true }));
});
};
}

Expand Down Expand Up @@ -1788,6 +1812,60 @@ export function doCommentModListDelegates(channelClaim: ChannelClaim) {
};
}

export function doCommentModListDelegatesForMyChannels() {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const myChannels = selectMyChannelClaims(state);
if (!myChannels) {
dispatch({ type: ACTIONS.COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_FAILED });
return;
}

dispatch({ type: ACTIONS.COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_STARTED });

let channelSignatures = [];

return Promise.all(myChannels.map((channel) => channelSignName(channel.claim_id, channel.name, true)))
.then((response) => {
channelSignatures = response;
// $FlowFixMe
return Promise.allSettled(
channelSignatures
.filter((x) => x !== undefined && x !== null)
.map((signatureData) =>
Comments.moderation_list_delegates({
channel_name: signatureData.name,
channel_id: signatureData.claim_id,
signature: signatureData.signature,
signing_ts: signatureData.signing_ts,
}).then((value) => ({ signatureData, value }))
)
)
.then((results) => {
const delegatesById = {};

results.forEach((result) => {
if (result.status === PROMISE_FULFILLED) {
const { signatureData, value } = result.value;
delegatesById[signatureData.claim_id] = value.Delegates;
}
});
dispatch({
type: ACTIONS.COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_COMPLETED,
data: delegatesById,
});
})
.catch((err) => {
devToast(dispatch, `Fetch delegates for my channels: ${err}`);
dispatch({ type: ACTIONS.COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_FAILED });
});
})
.catch(() => {
dispatch({ type: ACTIONS.COMMENT_MODERATION_DELEGATES_FOR_MY_CHANNELS_FAILED });
});
};
}

export function doFetchCommentModAmIList(channelClaim: ChannelClaim) {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
Expand Down
Loading
Loading