mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-31 01:11:26 +00:00
Temporary revert "Blocklist: paginate + search (#7055)"
- This reverts commit 345d9e76b5
.
- It to allow me to put back the original commits.
This commit is contained in:
parent
e8d8dfa76b
commit
33c52be56c
7 changed files with 168 additions and 331 deletions
|
@ -56,7 +56,6 @@
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"humanize-duration": "^3.27.0",
|
"humanize-duration": "^3.27.0",
|
||||||
"if-env": "^1.0.4",
|
"if-env": "^1.0.4",
|
||||||
"match-sorter": "^6.3.0",
|
|
||||||
"parse-duration": "^1.0.0",
|
"parse-duration": "^1.0.0",
|
||||||
"react-datetime-picker": "^3.2.1",
|
"react-datetime-picker": "^3.2.1",
|
||||||
"react-plastic": "^1.1.1",
|
"react-plastic": "^1.1.1",
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
import BlockList from './view';
|
|
||||||
export default BlockList;
|
|
|
@ -1,204 +0,0 @@
|
||||||
// @flow
|
|
||||||
import { Combobox, ComboboxInput, ComboboxPopover, ComboboxList, ComboboxOption } from '@reach/combobox';
|
|
||||||
// import '@reach/combobox/styles.css'; --> 'scss/third-party.scss'
|
|
||||||
import { matchSorter } from 'match-sorter';
|
|
||||||
import React from 'react';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import Button from 'component/button';
|
|
||||||
import ClaimList from 'component/claimList';
|
|
||||||
import Icon from 'component/common/icon';
|
|
||||||
import Paginate from 'component/common/paginate';
|
|
||||||
import Yrbl from 'component/yrbl';
|
|
||||||
import * as ICONS from 'constants/icons';
|
|
||||||
import useThrottle from 'effects/use-throttle';
|
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
|
||||||
|
|
||||||
function reduceUriToChannelName(uri: string) {
|
|
||||||
// 'parseURI' is too slow to handle a large list. Since our list should be
|
|
||||||
// kosher in the first place, just do a quick substring call. Add a
|
|
||||||
// try-catch just in case.
|
|
||||||
try {
|
|
||||||
return uri.substring(uri.indexOf('@') + 1, uri.indexOf('#'));
|
|
||||||
} catch {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************
|
|
||||||
// BlockList
|
|
||||||
// ****************************************************************************
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
uris: Array<string>,
|
|
||||||
help: string,
|
|
||||||
titleEmptyList: string,
|
|
||||||
subtitleEmptyList: string,
|
|
||||||
getActionButtons?: (url: string) => React$Node,
|
|
||||||
className: ?string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function BlockList(props: Props) {
|
|
||||||
const { uris: list, help, titleEmptyList, subtitleEmptyList, getActionButtons, className } = props;
|
|
||||||
|
|
||||||
// Keep a local list to allow for undoing actions in this component
|
|
||||||
const [localList, setLocalList] = React.useState(undefined);
|
|
||||||
const stringifiedList = JSON.stringify(list);
|
|
||||||
const hasLocalList = localList && localList.length > 0;
|
|
||||||
const justBlocked = list && localList && localList.length < list.length;
|
|
||||||
|
|
||||||
const [searchList, setSearchList] = React.useState(null); // null: not searching; []: no results;
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
|
|
||||||
let totalPages = 0;
|
|
||||||
let paginatedLocalList;
|
|
||||||
if (localList) {
|
|
||||||
paginatedLocalList = localList.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
|
||||||
totalPages = Math.ceil(localList.length / PAGE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
function getRenderActions() {
|
|
||||||
if (getActionButtons) {
|
|
||||||
return (claim) => <div className="section__actions">{getActionButtons(claim.permanent_url)}</div>;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSearchSuggestion(suggestion: string) {
|
|
||||||
return reduceUriToChannelName(suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterSearchResults(results: ?Array<string>) {
|
|
||||||
setSearchList(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const list = stringifiedList && JSON.parse(stringifiedList);
|
|
||||||
if (!hasLocalList) {
|
|
||||||
setLocalList(list && list.length > 0 ? list : []);
|
|
||||||
}
|
|
||||||
}, [stringifiedList, hasLocalList]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (justBlocked && stringifiedList) {
|
|
||||||
setLocalList(JSON.parse(stringifiedList));
|
|
||||||
}
|
|
||||||
}, [stringifiedList, justBlocked, setLocalList]);
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
if (paginatedLocalList === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasLocalList) {
|
|
||||||
return (
|
|
||||||
<div className="main--empty">
|
|
||||||
<Yrbl
|
|
||||||
title={titleEmptyList}
|
|
||||||
subtitle={subtitleEmptyList}
|
|
||||||
actions={
|
|
||||||
<div className="section__actions">
|
|
||||||
<Button button="primary" label={__('Go Home')} navigate="/" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="help--notice">{help}</div>
|
|
||||||
<div className="section">
|
|
||||||
<SearchList
|
|
||||||
list={localList}
|
|
||||||
placeholder={__('e.g. odysee')}
|
|
||||||
formatter={formatSearchSuggestion}
|
|
||||||
onResultsUpdated={filterSearchResults}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={classnames('section block-list', className)}>
|
|
||||||
<ClaimList
|
|
||||||
uris={searchList || paginatedLocalList}
|
|
||||||
showUnresolvedClaims
|
|
||||||
showHiddenByUser
|
|
||||||
hideMenu
|
|
||||||
renderActions={getRenderActions()}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Paginate totalPages={totalPages} disableHistory onPageChange={(p) => setPage(p)} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************
|
|
||||||
// SearchList
|
|
||||||
// ****************************************************************************
|
|
||||||
|
|
||||||
type LsbProps = {
|
|
||||||
list: ?Array<string>,
|
|
||||||
placeholder?: string,
|
|
||||||
formatter?: (suggestion: string) => string,
|
|
||||||
onResultsUpdated?: (?Array<string>) => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
function SearchList(props: LsbProps) {
|
|
||||||
const { list, placeholder, formatter, onResultsUpdated } = props;
|
|
||||||
const [term, setTerm] = React.useState('');
|
|
||||||
const results = useAuthorMatch(term, list);
|
|
||||||
const handleChange = (event) => setTerm(event.target.value);
|
|
||||||
const handleSelect = (e) => setTerm(e);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (onResultsUpdated) {
|
|
||||||
onResultsUpdated(results);
|
|
||||||
}
|
|
||||||
}, [results]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="wunderbar__wrapper">
|
|
||||||
<label>{__('Search blocked channel name')}</label>
|
|
||||||
<Combobox className="wunderbar" onSelect={handleSelect}>
|
|
||||||
<Icon icon={ICONS.SEARCH} />
|
|
||||||
<ComboboxInput selectOnClick className="wunderbar__input" onChange={handleChange} placeholder={placeholder} />
|
|
||||||
{results && (
|
|
||||||
<ComboboxPopover className="wunderbar__suggestions" portal={false}>
|
|
||||||
{results.length > 0 ? (
|
|
||||||
<ComboboxList>
|
|
||||||
{results.slice(0, 10).map((result, index) => (
|
|
||||||
<ComboboxOption
|
|
||||||
className="wunderbar__more-results"
|
|
||||||
key={index}
|
|
||||||
value={formatter ? formatter(result) : result}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</ComboboxList>
|
|
||||||
) : (
|
|
||||||
<span style={{ display: 'block', margin: 8 }}>{__('No results found')}</span>
|
|
||||||
)}
|
|
||||||
</ComboboxPopover>
|
|
||||||
)}
|
|
||||||
</Combobox>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function useAuthorMatch(term, list) {
|
|
||||||
const throttledTerm = useThrottle(term, 200);
|
|
||||||
return React.useMemo(() => {
|
|
||||||
return !throttledTerm || throttledTerm.trim() === ''
|
|
||||||
? null
|
|
||||||
: matchSorter(list, throttledTerm, {
|
|
||||||
keys: [(item) => reduceUriToChannelName(item)],
|
|
||||||
threshold: matchSorter.rankings.CONTAINS,
|
|
||||||
});
|
|
||||||
}, [throttledTerm]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
}
|
|
|
@ -10,18 +10,16 @@ const PAGINATE_PARAM = 'page';
|
||||||
type Props = {
|
type Props = {
|
||||||
totalPages: number,
|
totalPages: number,
|
||||||
location: { search: string },
|
location: { search: string },
|
||||||
history: { push: (string) => void },
|
history: { push: string => void },
|
||||||
onPageChange?: (number) => void,
|
onPageChange?: number => void,
|
||||||
disableHistory?: boolean, // Disables the use of '&page=' param and history stack.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function Paginate(props: Props) {
|
function Paginate(props: Props) {
|
||||||
const { totalPages = 1, location, history, onPageChange, disableHistory } = props;
|
const { totalPages = 1, location, history, onPageChange } = props;
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
const [textValue, setTextValue] = React.useState('');
|
const [textValue, setTextValue] = React.useState('');
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const initialPage = disableHistory ? 1 : Number(urlParams.get(PAGINATE_PARAM)) || 1;
|
const currentPage = Number(urlParams.get(PAGINATE_PARAM)) || 1;
|
||||||
const [currentPage, setCurrentPage] = React.useState(initialPage);
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
function handleChangePage(newPageNumber: number) {
|
function handleChangePage(newPageNumber: number) {
|
||||||
|
@ -30,13 +28,9 @@ function Paginate(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPage !== newPageNumber) {
|
if (currentPage !== newPageNumber) {
|
||||||
setCurrentPage(newPageNumber);
|
const params = new URLSearchParams(search);
|
||||||
|
params.set(PAGINATE_PARAM, newPageNumber.toString());
|
||||||
if (!disableHistory) {
|
history.push('?' + params.toString());
|
||||||
const params = new URLSearchParams(search);
|
|
||||||
params.set(PAGINATE_PARAM, newPageNumber.toString());
|
|
||||||
history.push('?' + params.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +58,7 @@ function Paginate(props: Props) {
|
||||||
nextClassName="pagination__item pagination__item--next"
|
nextClassName="pagination__item pagination__item--next"
|
||||||
breakClassName="pagination__item pagination__item--break"
|
breakClassName="pagination__item pagination__item--break"
|
||||||
marginPagesDisplayed={2}
|
marginPagesDisplayed={2}
|
||||||
onPageChange={(e) => handleChangePage(e.selected + 1)}
|
onPageChange={e => handleChangePage(e.selected + 1)}
|
||||||
forcePage={currentPage - 1}
|
forcePage={currentPage - 1}
|
||||||
initialPage={currentPage - 1}
|
initialPage={currentPage - 1}
|
||||||
containerClassName="pagination"
|
containerClassName="pagination"
|
||||||
|
@ -73,7 +67,7 @@ function Paginate(props: Props) {
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<FormField
|
<FormField
|
||||||
value={textValue}
|
value={textValue}
|
||||||
onChange={(e) => setTextValue(e.target.value)}
|
onChange={e => setTextValue(e.target.value)}
|
||||||
className="paginate-channel"
|
className="paginate-channel"
|
||||||
label={__('Go to page:')}
|
label={__('Go to page:')}
|
||||||
type="text"
|
type="text"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import humanizeDuration from 'humanize-duration';
|
import humanizeDuration from 'humanize-duration';
|
||||||
import BlockList from 'component/blockList';
|
import ClaimList from 'component/claimList';
|
||||||
import ClaimPreview from 'component/claimPreview';
|
import ClaimPreview from 'component/claimPreview';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import Spinner from 'component/spinner';
|
import Spinner from 'component/spinner';
|
||||||
|
@ -13,6 +13,7 @@ import Button from 'component/button';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
import ChannelBlockButton from 'component/channelBlockButton';
|
import ChannelBlockButton from 'component/channelBlockButton';
|
||||||
import ChannelMuteButton from 'component/channelMuteButton';
|
import ChannelMuteButton from 'component/channelMuteButton';
|
||||||
|
import Yrbl from 'component/yrbl';
|
||||||
|
|
||||||
const VIEW = {
|
const VIEW = {
|
||||||
BLOCKED: 'blocked',
|
BLOCKED: 'blocked',
|
||||||
|
@ -46,7 +47,7 @@ function ListBlocked(props: Props) {
|
||||||
personalTimeoutMap,
|
personalTimeoutMap,
|
||||||
adminTimeoutMap,
|
adminTimeoutMap,
|
||||||
moderatorTimeoutMap,
|
moderatorTimeoutMap,
|
||||||
moderatorBlockListDelegatorsMap: delegatorsMap,
|
moderatorBlockListDelegatorsMap,
|
||||||
fetchingModerationBlockList,
|
fetchingModerationBlockList,
|
||||||
fetchModBlockedList,
|
fetchModBlockedList,
|
||||||
fetchModAmIList,
|
fetchModAmIList,
|
||||||
|
@ -55,36 +56,55 @@ function ListBlocked(props: Props) {
|
||||||
} = props;
|
} = props;
|
||||||
const [viewMode, setViewMode] = usePersistedState('blocked-muted:display', VIEW.BLOCKED);
|
const [viewMode, setViewMode] = usePersistedState('blocked-muted:display', VIEW.BLOCKED);
|
||||||
|
|
||||||
const [localDelegatorsMap, setLocalDelegatorsMap] = React.useState(undefined);
|
// Keep a local list to allow for undoing actions in this component
|
||||||
|
const [localPersonalList, setLocalPersonalList] = React.useState(undefined);
|
||||||
|
const [localAdminList, setLocalAdminList] = React.useState(undefined);
|
||||||
|
const [localModeratorList, setLocalModeratorList] = React.useState(undefined);
|
||||||
|
const [localModeratorListDelegatorsMap, setLocalModeratorListDelegatorsMap] = React.useState(undefined);
|
||||||
|
const [localMutedList, setLocalMutedList] = React.useState(undefined);
|
||||||
|
|
||||||
const stringifiedDelegatorsMap = JSON.stringify(delegatorsMap);
|
const hasLocalMuteList = localMutedList && localMutedList.length > 0;
|
||||||
const stringifiedLocalDelegatorsMap = JSON.stringify(localDelegatorsMap);
|
const hasLocalPersonalList = localPersonalList && localPersonalList.length > 0;
|
||||||
|
|
||||||
|
const stringifiedMutedList = JSON.stringify(mutedUris);
|
||||||
|
const stringifiedPersonalList = JSON.stringify(personalBlockList);
|
||||||
|
const stringifiedAdminList = JSON.stringify(adminBlockList);
|
||||||
|
const stringifiedModeratorList = JSON.stringify(moderatorBlockList);
|
||||||
|
const stringifiedModeratorListDelegatorsMap = JSON.stringify(moderatorBlockListDelegatorsMap);
|
||||||
|
|
||||||
|
const stringifiedLocalAdminList = JSON.stringify(localAdminList);
|
||||||
|
const stringifiedLocalModeratorList = JSON.stringify(localModeratorList);
|
||||||
|
const stringifiedLocalModeratorListDelegatorsMap = JSON.stringify(localModeratorListDelegatorsMap);
|
||||||
|
|
||||||
|
const justMuted = localMutedList && mutedUris && localMutedList.length < mutedUris.length;
|
||||||
|
const justPersonalBlocked =
|
||||||
|
localPersonalList && personalBlockList && localPersonalList.length < personalBlockList.length;
|
||||||
|
|
||||||
const isAdmin =
|
const isAdmin =
|
||||||
myChannelClaims && myChannelClaims.some((c) => delegatorsById[c.claim_id] && delegatorsById[c.claim_id].global);
|
myChannelClaims && myChannelClaims.some((c) => delegatorsById[c.claim_id] && delegatorsById[c.claim_id].global);
|
||||||
|
|
||||||
const isModerator =
|
const isModerator =
|
||||||
myChannelClaims &&
|
myChannelClaims &&
|
||||||
myChannelClaims.some(
|
myChannelClaims.some(
|
||||||
(c) => delegatorsById[c.claim_id] && Object.keys(delegatorsById[c.claim_id].delegators).length > 0
|
(c) => delegatorsById[c.claim_id] && Object.keys(delegatorsById[c.claim_id].delegators).length > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
// **************************************************************************
|
const listForView = getLocalList(viewMode);
|
||||||
|
const showUris = listForView && listForView.length > 0;
|
||||||
|
|
||||||
function getList(view) {
|
function getLocalList(view) {
|
||||||
switch (view) {
|
switch (view) {
|
||||||
case VIEW.BLOCKED:
|
case VIEW.BLOCKED:
|
||||||
return personalBlockList;
|
return localPersonalList;
|
||||||
case VIEW.ADMIN:
|
case VIEW.ADMIN:
|
||||||
return adminBlockList;
|
return localAdminList;
|
||||||
case VIEW.MODERATOR:
|
case VIEW.MODERATOR:
|
||||||
return moderatorBlockList;
|
return localModeratorList;
|
||||||
case VIEW.MUTED:
|
case VIEW.MUTED:
|
||||||
return mutedUris;
|
return localMutedList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActionButtons(uri) {
|
function getButtons(view, uri) {
|
||||||
const getDurationStr = (durationNs) => {
|
const getDurationStr = (durationNs) => {
|
||||||
const NANO_TO_MS = 1000000;
|
const NANO_TO_MS = 1000000;
|
||||||
return humanizeDuration(durationNs / NANO_TO_MS, { round: true });
|
return humanizeDuration(durationNs / NANO_TO_MS, { round: true });
|
||||||
|
@ -107,7 +127,7 @@ function ListBlocked(props: Props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (viewMode) {
|
switch (view) {
|
||||||
case VIEW.BLOCKED:
|
case VIEW.BLOCKED:
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -126,21 +146,18 @@ function ListBlocked(props: Props) {
|
||||||
);
|
);
|
||||||
|
|
||||||
case VIEW.MODERATOR:
|
case VIEW.MODERATOR:
|
||||||
const delegatorUrisForBlockedUri = localDelegatorsMap && localDelegatorsMap[uri];
|
const delegatorUrisForBlockedUri = localModeratorListDelegatorsMap && localModeratorListDelegatorsMap[uri];
|
||||||
if (!delegatorUrisForBlockedUri) return null;
|
if (!delegatorUrisForBlockedUri) return null;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{delegatorUrisForBlockedUri.map((delegatorUri) => {
|
{delegatorUrisForBlockedUri.map((delegatorUri) => {
|
||||||
return (
|
return (
|
||||||
<div className="block-list--delegator" key={delegatorUri}>
|
<div className="block-list--delegator" key={delegatorUri}>
|
||||||
<label>{__('Blocked on behalf of:')}</label>
|
<ul className="section content__non-clickable">
|
||||||
<ul className="section">
|
<ClaimPreview uri={delegatorUri} hideMenu hideActions type="small" />
|
||||||
<div className="content__non-clickable">
|
|
||||||
<ClaimPreview uri={delegatorUri} hideMenu hideActions type="inline" properties={false} />
|
|
||||||
{moderatorTimeoutMap[uri] && getBanInfoElem(moderatorTimeoutMap[uri])}
|
|
||||||
</div>
|
|
||||||
<ChannelBlockButton uri={uri} blockLevel={BLOCK_LEVEL.MODERATOR} creatorUri={delegatorUri} />
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<ChannelBlockButton uri={uri} blockLevel={BLOCK_LEVEL.MODERATOR} creatorUri={delegatorUri} />
|
||||||
|
{moderatorTimeoutMap[uri] && getBanInfoElem(moderatorTimeoutMap[uri])}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -201,43 +218,52 @@ function ListBlocked(props: Props) {
|
||||||
return source && (!local || local.length < source.length);
|
return source && (!local || local.length < source.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getViewElem(view, label, icon) {
|
React.useEffect(() => {
|
||||||
return (
|
const jsonMutedChannels = stringifiedMutedList && JSON.parse(stringifiedMutedList);
|
||||||
<Button
|
if (!hasLocalMuteList && jsonMutedChannels && jsonMutedChannels.length > 0) {
|
||||||
icon={icon}
|
setLocalMutedList(jsonMutedChannels);
|
||||||
button="alt"
|
}
|
||||||
label={__(label)}
|
}, [stringifiedMutedList, hasLocalMuteList]);
|
||||||
className={classnames(`button-toggle`, {
|
|
||||||
'button-toggle--active': viewMode === view,
|
|
||||||
})}
|
|
||||||
onClick={() => setViewMode(view)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRefreshElem() {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
icon={ICONS.REFRESH}
|
|
||||||
button="alt"
|
|
||||||
label={__('Refresh')}
|
|
||||||
onClick={() => {
|
|
||||||
fetchModBlockedList();
|
|
||||||
fetchModAmIList();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (stringifiedDelegatorsMap && isSourceListLarger(stringifiedDelegatorsMap, stringifiedLocalDelegatorsMap)) {
|
const jsonBlockedChannels = stringifiedPersonalList && JSON.parse(stringifiedPersonalList);
|
||||||
setLocalDelegatorsMap(JSON.parse(stringifiedDelegatorsMap));
|
if (!hasLocalPersonalList && jsonBlockedChannels && jsonBlockedChannels.length > 0) {
|
||||||
|
setLocalPersonalList(jsonBlockedChannels);
|
||||||
}
|
}
|
||||||
}, [stringifiedDelegatorsMap, stringifiedLocalDelegatorsMap]);
|
}, [stringifiedPersonalList, hasLocalPersonalList]);
|
||||||
|
|
||||||
// **************************************************************************
|
React.useEffect(() => {
|
||||||
|
if (stringifiedAdminList && isSourceListLarger(stringifiedAdminList, stringifiedLocalAdminList)) {
|
||||||
|
setLocalAdminList(JSON.parse(stringifiedAdminList));
|
||||||
|
}
|
||||||
|
}, [stringifiedAdminList, stringifiedLocalAdminList]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (stringifiedModeratorList && isSourceListLarger(stringifiedModeratorList, stringifiedLocalModeratorList)) {
|
||||||
|
setLocalModeratorList(JSON.parse(stringifiedModeratorList));
|
||||||
|
}
|
||||||
|
}, [stringifiedModeratorList, stringifiedLocalModeratorList]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (
|
||||||
|
stringifiedModeratorListDelegatorsMap &&
|
||||||
|
isSourceListLarger(stringifiedModeratorListDelegatorsMap, stringifiedLocalModeratorListDelegatorsMap)
|
||||||
|
) {
|
||||||
|
setLocalModeratorListDelegatorsMap(JSON.parse(stringifiedModeratorListDelegatorsMap));
|
||||||
|
}
|
||||||
|
}, [stringifiedModeratorListDelegatorsMap, stringifiedLocalModeratorListDelegatorsMap]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (justMuted && stringifiedMutedList) {
|
||||||
|
setLocalMutedList(JSON.parse(stringifiedMutedList));
|
||||||
|
}
|
||||||
|
}, [stringifiedMutedList, justMuted, setLocalMutedList]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (justPersonalBlocked && stringifiedPersonalList) {
|
||||||
|
setLocalPersonalList(JSON.parse(stringifiedPersonalList));
|
||||||
|
}
|
||||||
|
}, [stringifiedPersonalList, justPersonalBlocked, setLocalPersonalList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
|
@ -256,23 +282,87 @@ function ListBlocked(props: Props) {
|
||||||
<>
|
<>
|
||||||
<div className="section__header--actions">
|
<div className="section__header--actions">
|
||||||
<div className="section__actions--inline">
|
<div className="section__actions--inline">
|
||||||
{getViewElem(VIEW.BLOCKED, 'Blocked', ICONS.BLOCK)}
|
<Button
|
||||||
{isAdmin && getViewElem(VIEW.ADMIN, 'Global', ICONS.BLOCK)}
|
icon={ICONS.BLOCK}
|
||||||
{isModerator && getViewElem(VIEW.MODERATOR, 'Moderator', ICONS.BLOCK)}
|
button="alt"
|
||||||
{getViewElem(VIEW.MUTED, 'Muted', ICONS.MUTE)}
|
label={__('Blocked')}
|
||||||
|
className={classnames(`button-toggle`, {
|
||||||
|
'button-toggle--active': viewMode === VIEW.BLOCKED,
|
||||||
|
})}
|
||||||
|
onClick={() => setViewMode(VIEW.BLOCKED)}
|
||||||
|
/>
|
||||||
|
{isAdmin && (
|
||||||
|
<Button
|
||||||
|
icon={ICONS.BLOCK}
|
||||||
|
button="alt"
|
||||||
|
label={__('Global')}
|
||||||
|
className={classnames(`button-toggle`, {
|
||||||
|
'button-toggle--active': viewMode === VIEW.ADMIN,
|
||||||
|
})}
|
||||||
|
onClick={() => setViewMode(VIEW.ADMIN)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isModerator && (
|
||||||
|
<Button
|
||||||
|
icon={ICONS.BLOCK}
|
||||||
|
button="alt"
|
||||||
|
label={__('Moderator')}
|
||||||
|
className={classnames(`button-toggle`, {
|
||||||
|
'button-toggle--active': viewMode === VIEW.MODERATOR,
|
||||||
|
})}
|
||||||
|
onClick={() => setViewMode(VIEW.MODERATOR)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
icon={ICONS.MUTE}
|
||||||
|
button="alt"
|
||||||
|
label={__('Muted')}
|
||||||
|
className={classnames(`button-toggle`, {
|
||||||
|
'button-toggle--active': viewMode === VIEW.MUTED,
|
||||||
|
})}
|
||||||
|
onClick={() => setViewMode(VIEW.MUTED)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="section__actions--inline">
|
||||||
|
<Button
|
||||||
|
icon={ICONS.REFRESH}
|
||||||
|
button="alt"
|
||||||
|
label={__('Refresh')}
|
||||||
|
onClick={() => {
|
||||||
|
fetchModBlockedList();
|
||||||
|
fetchModAmIList();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="section__actions--inline">{getRefreshElem()}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BlockList
|
{showUris && <div className="help--notice">{getHelpText(viewMode)}</div>}
|
||||||
key={viewMode}
|
|
||||||
uris={getList(viewMode)}
|
{showUris ? (
|
||||||
help={getHelpText(viewMode)}
|
<div className={viewMode === VIEW.MODERATOR ? 'block-list--moderator' : 'block-list'}>
|
||||||
titleEmptyList={getEmptyListTitle(viewMode)}
|
<ClaimList
|
||||||
subtitle={getEmptyListSubtitle(viewMode)}
|
uris={getLocalList(viewMode)}
|
||||||
getActionButtons={getActionButtons}
|
showUnresolvedClaims
|
||||||
className={viewMode === VIEW.MODERATOR ? 'block-list--moderator' : undefined}
|
showHiddenByUser
|
||||||
/>
|
hideMenu
|
||||||
|
renderActions={(claim) => {
|
||||||
|
return <div className="section__actions">{getButtons(viewMode, claim.permanent_url)}</div>;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="main--empty">
|
||||||
|
<Yrbl
|
||||||
|
title={getEmptyListTitle(viewMode)}
|
||||||
|
subtitle={getEmptyListSubtitle(viewMode)}
|
||||||
|
actions={
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" label={__('Go Home')} navigate="/" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
@ -15,33 +15,6 @@
|
||||||
.button--secondary {
|
.button--secondary {
|
||||||
margin-top: var(--spacing-xxs);
|
margin-top: var(--spacing-xxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.claim-preview__wrapper {
|
|
||||||
border-bottom: none !important;
|
|
||||||
margin-bottom: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.claim-preview--inline {
|
|
||||||
.channel-thumbnail {
|
|
||||||
@include handleChannelGif(3rem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.claim-preview__title {
|
|
||||||
font-size: var(--font-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-name {
|
|
||||||
font-size: var(--font-xsmall);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
margin-left: var(--spacing-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-modal--values {
|
.block-modal--values {
|
||||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -10707,14 +10707,6 @@ marked@^1.2.3:
|
||||||
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.8.tgz#5008ece15cfa43e653e85845f3525af4beb6bdd4"
|
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.8.tgz#5008ece15cfa43e653e85845f3525af4beb6bdd4"
|
||||||
integrity sha512-lzmFjGnzWHkmbk85q/ILZjFoHHJIQGF+SxGEfIdGk/XhiTPhqGs37gbru6Kkd48diJnEyYwnG67nru0Z2gQtuQ==
|
integrity sha512-lzmFjGnzWHkmbk85q/ILZjFoHHJIQGF+SxGEfIdGk/XhiTPhqGs37gbru6Kkd48diJnEyYwnG67nru0Z2gQtuQ==
|
||||||
|
|
||||||
match-sorter@^6.3.0:
|
|
||||||
version "6.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01"
|
|
||||||
integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.12.5"
|
|
||||||
remove-accents "0.4.2"
|
|
||||||
|
|
||||||
matcher@^3.0.0:
|
matcher@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
|
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
|
||||||
|
@ -14055,11 +14047,6 @@ remark@^9.0.0:
|
||||||
remark-stringify "^5.0.0"
|
remark-stringify "^5.0.0"
|
||||||
unified "^6.0.0"
|
unified "^6.0.0"
|
||||||
|
|
||||||
remove-accents@0.4.2:
|
|
||||||
version "0.4.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
|
|
||||||
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
|
|
||||||
|
|
||||||
remove-markdown@^0.3.0:
|
remove-markdown@^0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/remove-markdown/-/remove-markdown-0.3.0.tgz#5e4b667493a93579728f3d52ecc1db9ca505dc98"
|
resolved "https://registry.yarnpkg.com/remove-markdown/-/remove-markdown-0.3.0.tgz#5e4b667493a93579728f3d52ecc1db9ca505dc98"
|
||||||
|
|
Loading…
Add table
Reference in a new issue