Blocklist Page: show the timeout ban duration
- 'humanize-duration' is used because 'moment''s humanizer sucks.
This commit is contained in:
parent
663376e970
commit
0c1554e453
9 changed files with 101 additions and 7 deletions
3
flow-typed/Comment.js
vendored
3
flow-typed/Comment.js
vendored
|
@ -60,6 +60,9 @@ declare type CommentsState = {
|
|||
fetchingModerationDelegators: boolean,
|
||||
blockingByUri: {},
|
||||
unBlockingByUri: {},
|
||||
personalTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
|
||||
adminTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
|
||||
moderatorTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
|
||||
togglingForDelegatorMap: {[string]: Array<string>}, // {"blockedUri": ["delegatorUri1", ""delegatorUri2", ...]}
|
||||
settingsByChannelId: { [string]: PerChannelSettings }, // ChannelID -> settings
|
||||
fetchingSettings: boolean,
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"electron-notarize": "^1.0.0",
|
||||
"electron-updater": "^4.2.4",
|
||||
"express": "^4.17.1",
|
||||
"humanize-duration": "^3.27.0",
|
||||
"if-env": "^1.0.4",
|
||||
"parse-duration": "^1.0.0",
|
||||
"react-datetime-picker": "^3.2.1",
|
||||
|
|
|
@ -1749,6 +1749,7 @@
|
|||
"Invalid duration.": "Invalid duration.",
|
||||
"Permanent": "Permanent",
|
||||
"Timeout --[time-based ban instead of permanent]--": "Timeout",
|
||||
"(Remaining: %duration%) --[timeout ban duration]--": "(Remaining: %duration%)",
|
||||
"Create a channel to change this setting.": "Create a channel to change this setting.",
|
||||
"Invalid channel URL \"%url%\"": "Invalid channel URL \"%url%\"",
|
||||
"Delegation": "Delegation",
|
||||
|
|
|
@ -8,6 +8,9 @@ import {
|
|||
selectModeratorBlockListDelegatorsMap,
|
||||
selectFetchingModerationBlockList,
|
||||
selectModerationDelegatorsById,
|
||||
selectAdminTimeoutMap,
|
||||
selectModeratorTimeoutMap,
|
||||
selectPersonalTimeoutMap,
|
||||
} from 'redux/selectors/comments';
|
||||
import { selectMyChannelClaims } from 'lbry-redux';
|
||||
import ListBlocked from './view';
|
||||
|
@ -17,6 +20,9 @@ const select = (state) => ({
|
|||
personalBlockList: selectModerationBlockList(state),
|
||||
adminBlockList: selectAdminBlockList(state),
|
||||
moderatorBlockList: selectModeratorBlockList(state),
|
||||
personalTimeoutMap: selectPersonalTimeoutMap(state),
|
||||
adminTimeoutMap: selectAdminTimeoutMap(state),
|
||||
moderatorTimeoutMap: selectModeratorTimeoutMap(state),
|
||||
moderatorBlockListDelegatorsMap: selectModeratorBlockListDelegatorsMap(state),
|
||||
delegatorsById: selectModerationDelegatorsById(state),
|
||||
myChannelClaims: selectMyChannelClaims(state),
|
||||
|
|
|
@ -3,6 +3,8 @@ import * as ICONS from 'constants/icons';
|
|||
import { BLOCK_LEVEL } from 'constants/comment';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import moment from 'moment';
|
||||
import humanizeDuration from 'humanize-duration';
|
||||
import ClaimList from 'component/claimList';
|
||||
import ClaimPreview from 'component/claimPreview';
|
||||
import Page from 'component/page';
|
||||
|
@ -25,6 +27,9 @@ type Props = {
|
|||
personalBlockList: ?Array<string>,
|
||||
adminBlockList: ?Array<string>,
|
||||
moderatorBlockList: ?Array<string>,
|
||||
personalTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
|
||||
adminTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
|
||||
moderatorTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
|
||||
moderatorBlockListDelegatorsMap: { [string]: Array<string> },
|
||||
fetchingModerationBlockList: boolean,
|
||||
fetchModBlockedList: () => void,
|
||||
|
@ -39,6 +44,9 @@ function ListBlocked(props: Props) {
|
|||
personalBlockList,
|
||||
adminBlockList,
|
||||
moderatorBlockList,
|
||||
personalTimeoutMap,
|
||||
adminTimeoutMap,
|
||||
moderatorTimeoutMap,
|
||||
moderatorBlockListDelegatorsMap,
|
||||
fetchingModerationBlockList,
|
||||
fetchModBlockedList,
|
||||
|
@ -97,17 +105,45 @@ function ListBlocked(props: Props) {
|
|||
}
|
||||
|
||||
function getButtons(view, uri) {
|
||||
const getDurationStr = (durationNs) => {
|
||||
const NANO_TO_MS = 1000000;
|
||||
return humanizeDuration(durationNs / NANO_TO_MS, { round: true });
|
||||
};
|
||||
|
||||
const getBanInfoElem = (timeoutInfo) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="help">
|
||||
<blockquote>
|
||||
{moment(timeoutInfo.blockedAt).format('MMMM Do, YYYY @ HH:mm')}
|
||||
<br />
|
||||
{getDurationStr(timeoutInfo.bannedFor)}{' '}
|
||||
{__('(Remaining: %duration%) --[timeout ban duration]--', {
|
||||
duration: getDurationStr(timeoutInfo.banRemaining),
|
||||
})}
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
switch (view) {
|
||||
case VIEW.BLOCKED:
|
||||
return (
|
||||
<>
|
||||
<ChannelBlockButton uri={uri} />
|
||||
<ChannelMuteButton uri={uri} />
|
||||
{personalTimeoutMap[uri] && getBanInfoElem(personalTimeoutMap[uri])}
|
||||
</>
|
||||
);
|
||||
|
||||
case VIEW.ADMIN:
|
||||
return <ChannelBlockButton uri={uri} blockLevel={BLOCK_LEVEL.ADMIN} />;
|
||||
return (
|
||||
<>
|
||||
<ChannelBlockButton uri={uri} blockLevel={BLOCK_LEVEL.ADMIN} />
|
||||
{adminTimeoutMap[uri] && getBanInfoElem(adminTimeoutMap[uri])}
|
||||
</>
|
||||
);
|
||||
|
||||
case VIEW.MODERATOR:
|
||||
const delegatorUrisForBlockedUri = localModeratorListDelegatorsMap && localModeratorListDelegatorsMap[uri];
|
||||
|
@ -121,6 +157,7 @@ function ListBlocked(props: Props) {
|
|||
<ClaimPreview uri={delegatorUri} hideMenu hideActions type="small" />
|
||||
</ul>
|
||||
<ChannelBlockButton uri={uri} blockLevel={BLOCK_LEVEL.MODERATOR} creatorUri={delegatorUri} />
|
||||
{moderatorTimeoutMap[uri] && getBanInfoElem(moderatorTimeoutMap[uri])}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -1054,13 +1054,20 @@ export function doFetchModBlockedList() {
|
|||
let moderatorBlockList = [];
|
||||
let moderatorBlockListDelegatorsMap = {};
|
||||
|
||||
// These should just be part of the block list above, but it is
|
||||
// separated for now because there are too many clients that we need
|
||||
// to update.
|
||||
const personalTimeoutMap = {};
|
||||
const adminTimeoutMap = {};
|
||||
const moderatorTimeoutMap = {};
|
||||
|
||||
const blockListsPerChannel = res.map((r) => r.value);
|
||||
blockListsPerChannel
|
||||
.sort((a, b) => {
|
||||
return 1;
|
||||
})
|
||||
.forEach((channelBlockLists) => {
|
||||
const storeList = (fetchedList, blockedList, blockedByMap) => {
|
||||
const storeList = (fetchedList, blockedList, timeoutMap, blockedByMap) => {
|
||||
if (fetchedList) {
|
||||
fetchedList.forEach((blockedChannel) => {
|
||||
if (blockedChannel.blocked_channel_name) {
|
||||
|
@ -1071,6 +1078,14 @@ export function doFetchModBlockedList() {
|
|||
|
||||
if (!blockedList.find((blockedChannel) => isURIEqual(blockedChannel.channelUri, channelUri))) {
|
||||
blockedList.push({ channelUri, blockedAt: blockedChannel.blocked_at });
|
||||
|
||||
if (blockedChannel.banned_for) {
|
||||
timeoutMap[channelUri] = {
|
||||
blockedAt: blockedChannel.blocked_at,
|
||||
bannedFor: blockedChannel.banned_for,
|
||||
banRemaining: blockedChannel.ban_remaining,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (blockedByMap !== undefined) {
|
||||
|
@ -1096,9 +1111,14 @@ export function doFetchModBlockedList() {
|
|||
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);
|
||||
storeList(blocked_channels, personalBlockList, personalTimeoutMap);
|
||||
storeList(globally_blocked_channels, adminBlockList, adminTimeoutMap);
|
||||
storeList(
|
||||
delegated_blocked_channels,
|
||||
moderatorBlockList,
|
||||
moderatorTimeoutMap,
|
||||
moderatorBlockListDelegatorsMap
|
||||
);
|
||||
});
|
||||
|
||||
dispatch({
|
||||
|
@ -1123,6 +1143,9 @@ export function doFetchModBlockedList() {
|
|||
.map((blockedChannel) => blockedChannel.channelUri)
|
||||
: null,
|
||||
moderatorBlockListDelegatorsMap: moderatorBlockListDelegatorsMap,
|
||||
personalTimeoutMap,
|
||||
adminTimeoutMap,
|
||||
moderatorTimeoutMap,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
|
@ -40,6 +40,9 @@ const defaultState: CommentsState = {
|
|||
fetchingModerationDelegators: false,
|
||||
blockingByUri: {},
|
||||
unBlockingByUri: {},
|
||||
personalTimeoutMap: {},
|
||||
adminTimeoutMap: {},
|
||||
moderatorTimeoutMap: {},
|
||||
togglingForDelegatorMap: {},
|
||||
settingsByChannelId: {}, // ChannelId -> PerChannelSettings
|
||||
fetchingSettings: false,
|
||||
|
@ -671,14 +674,25 @@ export default handleActions(
|
|||
fetchingModerationBlockList: true,
|
||||
}),
|
||||
[ACTIONS.COMMENT_MODERATION_BLOCK_LIST_COMPLETED]: (state: CommentsState, action: any) => {
|
||||
const { personalBlockList, adminBlockList, moderatorBlockList, moderatorBlockListDelegatorsMap } = action.data;
|
||||
const {
|
||||
personalBlockList,
|
||||
adminBlockList,
|
||||
moderatorBlockList,
|
||||
moderatorBlockListDelegatorsMap,
|
||||
personalTimeoutMap,
|
||||
adminTimeoutMap,
|
||||
moderatorTimeoutMap,
|
||||
} = action.data;
|
||||
|
||||
return {
|
||||
...state,
|
||||
moderationBlockList: personalBlockList,
|
||||
adminBlockList: adminBlockList,
|
||||
moderatorBlockList: moderatorBlockList,
|
||||
moderatorBlockListDelegatorsMap: moderatorBlockListDelegatorsMap,
|
||||
moderatorBlockListDelegatorsMap,
|
||||
personalTimeoutMap,
|
||||
adminTimeoutMap,
|
||||
moderatorTimeoutMap,
|
||||
fetchingModerationBlockList: false,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -45,6 +45,10 @@ export const selectModeratorBlockList = createSelector(selectState, (state) =>
|
|||
state.moderatorBlockList ? state.moderatorBlockList.reverse() : []
|
||||
);
|
||||
|
||||
export const selectPersonalTimeoutMap = createSelector(selectState, (state) => state.personalTimeoutMap);
|
||||
export const selectAdminTimeoutMap = createSelector(selectState, (state) => state.adminTimeoutMap);
|
||||
export const selectModeratorTimeoutMap = createSelector(selectState, (state) => state.moderatorTimeoutMap);
|
||||
|
||||
export const selectModeratorBlockListDelegatorsMap = createSelector(
|
||||
selectState,
|
||||
(state) => state.moderatorBlockListDelegatorsMap
|
||||
|
|
|
@ -8558,6 +8558,11 @@ https-proxy-agent@^4.0.0:
|
|||
agent-base "5"
|
||||
debug "4"
|
||||
|
||||
humanize-duration@^3.27.0:
|
||||
version "3.27.0"
|
||||
resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.27.0.tgz#3f781b7cf8022ad587f76b9839b60bc2b29636b2"
|
||||
integrity sha512-qLo/08cNc3Tb0uD7jK0jAcU5cnqCM0n568918E7R2XhMr/+7F37p4EY062W/stg7tmzvknNn9b/1+UhVRzsYrQ==
|
||||
|
||||
humanize-plus@^1.8.1:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030"
|
||||
|
|
Loading…
Reference in a new issue