New moderation tools: block & mute #5572
6 changed files with 59 additions and 21 deletions
|
@ -29,6 +29,7 @@ type Props = {
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean,
|
||||||
showMature: boolean,
|
showMature: boolean,
|
||||||
tileLayout: boolean,
|
tileLayout: boolean,
|
||||||
|
viewBlockedChannel: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChannelContent(props: Props) {
|
function ChannelContent(props: Props) {
|
||||||
|
@ -44,6 +45,7 @@ function ChannelContent(props: Props) {
|
||||||
defaultInfiniteScroll = true,
|
defaultInfiniteScroll = true,
|
||||||
showMature,
|
showMature,
|
||||||
tileLayout,
|
tileLayout,
|
||||||
|
viewBlockedChannel,
|
||||||
} = props;
|
} = props;
|
||||||
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
|
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
|
||||||
const [searchQuery, setSearchQuery] = React.useState('');
|
const [searchQuery, setSearchQuery] = React.useState('');
|
||||||
|
@ -120,6 +122,7 @@ function ChannelContent(props: Props) {
|
||||||
|
|
||||||
{claim && claimsInChannel > 0 ? (
|
{claim && claimsInChannel > 0 ? (
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
|
showHiddenByUser={viewBlockedChannel}
|
||||||
forceShowReposts
|
forceShowReposts
|
||||||
tileLayout={tileLayout}
|
tileLayout={tileLayout}
|
||||||
uris={searchResults}
|
uris={searchResults}
|
||||||
|
|
|
@ -100,7 +100,8 @@ export default function ClaimList(props: Props) {
|
||||||
|
|
||||||
return tileLayout && !header ? (
|
return tileLayout && !header ? (
|
||||||
<section className="claim-grid">
|
<section className="claim-grid">
|
||||||
{urisLength > 0 && uris.map((uri) => <ClaimPreviewTile key={uri} uri={uri} />)}
|
{urisLength > 0 &&
|
||||||
|
uris.map((uri) => <ClaimPreviewTile key={uri} uri={uri} showHiddenByUser={showHiddenByUser} />)}
|
||||||
{!timedOut && urisLength === 0 && !loading && <div className="empty main--empty">{empty || noResultMsg}</div>}
|
{!timedOut && urisLength === 0 && !loading && <div className="empty main--empty">{empty || noResultMsg}</div>}
|
||||||
{timedOut && timedOutMessage && <div className="empty main--empty">{timedOutMessage}</div>}
|
{timedOut && timedOutMessage && <div className="empty main--empty">{timedOutMessage}</div>}
|
||||||
</section>
|
</section>
|
||||||
|
@ -155,6 +156,7 @@ export default function ClaimList(props: Props) {
|
||||||
properties={renderProperties || (type !== 'small' ? undefined : false)}
|
properties={renderProperties || (type !== 'small' ? undefined : false)}
|
||||||
renderActions={renderActions}
|
renderActions={renderActions}
|
||||||
showUserBlocked={showHiddenByUser}
|
showUserBlocked={showHiddenByUser}
|
||||||
|
showHiddenByUser={showHiddenByUser}
|
||||||
customShouldHide={(claim: StreamClaim) => {
|
customShouldHide={(claim: StreamClaim) => {
|
||||||
// Hack to hide spee.ch thumbnail publishes
|
// Hack to hide spee.ch thumbnail publishes
|
||||||
// If it meets these requirements, it was probably uploaded here:
|
// If it meets these requirements, it was probably uploaded here:
|
||||||
|
|
|
@ -64,6 +64,7 @@ type Props = {
|
||||||
languageSetting: string,
|
languageSetting: string,
|
||||||
searchInLanguage: boolean,
|
searchInLanguage: boolean,
|
||||||
scrollAnchor?: string,
|
scrollAnchor?: string,
|
||||||
|
showHiddenByUser?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimListDiscover(props: Props) {
|
function ClaimListDiscover(props: Props) {
|
||||||
|
@ -112,6 +113,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
languageSetting,
|
languageSetting,
|
||||||
searchInLanguage,
|
searchInLanguage,
|
||||||
scrollAnchor,
|
scrollAnchor,
|
||||||
|
showHiddenByUser = false,
|
||||||
} = props;
|
} = props;
|
||||||
const didNavigateForward = history.action === 'PUSH';
|
const didNavigateForward = history.action === 'PUSH';
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
|
@ -482,6 +484,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
renderProperties={renderProperties}
|
renderProperties={renderProperties}
|
||||||
includeSupportAction={includeSupportAction}
|
includeSupportAction={includeSupportAction}
|
||||||
injectedItem={injectedItem}
|
injectedItem={injectedItem}
|
||||||
|
showHiddenByUser={showHiddenByUser}
|
||||||
/>
|
/>
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className="claim-grid">
|
<div className="claim-grid">
|
||||||
|
@ -509,6 +512,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
renderProperties={renderProperties}
|
renderProperties={renderProperties}
|
||||||
includeSupportAction={includeSupportAction}
|
includeSupportAction={includeSupportAction}
|
||||||
injectedItem={injectedItem}
|
injectedItem={injectedItem}
|
||||||
|
showHiddenByUser={showHiddenByUser}
|
||||||
/>
|
/>
|
||||||
{loading && new Array(dynamicPageSize).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
|
{loading && new Array(dynamicPageSize).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -43,6 +43,7 @@ type Props = {
|
||||||
streamingUrl: string,
|
streamingUrl: string,
|
||||||
isMature: boolean,
|
isMature: boolean,
|
||||||
showMature: boolean,
|
showMature: boolean,
|
||||||
|
showHiddenByUser?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimPreviewTile(props: Props) {
|
function ClaimPreviewTile(props: Props) {
|
||||||
|
@ -62,6 +63,7 @@ function ClaimPreviewTile(props: Props) {
|
||||||
blockedChannelUris,
|
blockedChannelUris,
|
||||||
isMature,
|
isMature,
|
||||||
showMature,
|
showMature,
|
||||||
|
showHiddenByUser,
|
||||||
} = props;
|
} = props;
|
||||||
const isRepost = claim && claim.repost_channel_url;
|
const isRepost = claim && claim.repost_channel_url;
|
||||||
const shouldFetch = claim === undefined;
|
const shouldFetch = claim === undefined;
|
||||||
|
@ -130,12 +132,12 @@ function ClaimPreviewTile(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// block stream claims
|
// block stream claims
|
||||||
if (claim && !shouldHide && blockedChannelUris.length && signingChannel) {
|
if (claim && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) {
|
||||||
shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === signingChannel.permanent_url);
|
shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === signingChannel.permanent_url);
|
||||||
}
|
}
|
||||||
// block channel claims if we can't control for them in claim search
|
// block channel claims if we can't control for them in claim search
|
||||||
// e.g. fetchRecommendedSubscriptions
|
// e.g. fetchRecommendedSubscriptions
|
||||||
if (claim && isChannel && !shouldHide && blockedChannelUris.length) {
|
if (claim && isChannel && !shouldHide && !showHiddenByUser && blockedChannelUris.length) {
|
||||||
shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === claim.permanent_url);
|
shouldHide = blockedChannelUris.some((blockedUri) => blockedUri === claim.permanent_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||||
import { selectBlackListedOutpoints, doFetchSubCount, makeSelectSubCountForUri } from 'lbryinc';
|
import { selectBlackListedOutpoints, doFetchSubCount, makeSelectSubCountForUri } from 'lbryinc';
|
||||||
import { selectYoutubeChannels } from 'redux/selectors/user';
|
import { selectYoutubeChannels } from 'redux/selectors/user';
|
||||||
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||||
|
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import ChannelPage from './view';
|
import ChannelPage from './view';
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ const select = (state, props) => ({
|
||||||
subCount: makeSelectSubCountForUri(props.uri)(state),
|
subCount: makeSelectSubCountForUri(props.uri)(state),
|
||||||
pending: makeSelectClaimIsPending(props.uri)(state),
|
pending: makeSelectClaimIsPending(props.uri)(state),
|
||||||
youtubeChannels: selectYoutubeChannels(state),
|
youtubeChannels: selectYoutubeChannels(state),
|
||||||
|
blockedChannels: selectModerationBlockList(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
const perform = (dispatch) => ({
|
||||||
|
|
|
@ -21,6 +21,7 @@ import HelpLink from 'component/common/help-link';
|
||||||
import ClaimSupportButton from 'component/claimSupportButton';
|
import ClaimSupportButton from 'component/claimSupportButton';
|
||||||
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
||||||
import ClaimMenuList from 'component/claimMenuList';
|
import ClaimMenuList from 'component/claimMenuList';
|
||||||
|
import Yrbl from 'component/yrbl';
|
||||||
|
|
||||||
export const PAGE_VIEW_QUERY = `view`;
|
export const PAGE_VIEW_QUERY = `view`;
|
||||||
const ABOUT_PAGE = `about`;
|
const ABOUT_PAGE = `about`;
|
||||||
|
@ -46,6 +47,7 @@ type Props = {
|
||||||
subCount: number,
|
subCount: number,
|
||||||
pending: boolean,
|
pending: boolean,
|
||||||
youtubeChannels: ?Array<{ channel_claim_id: string, sync_status: string, transfer_state: string }>,
|
youtubeChannels: ?Array<{ channel_claim_id: string, sync_status: string, transfer_state: string }>,
|
||||||
|
blockedChannels: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChannelPage(props: Props) {
|
function ChannelPage(props: Props) {
|
||||||
|
@ -63,12 +65,14 @@ function ChannelPage(props: Props) {
|
||||||
subCount,
|
subCount,
|
||||||
pending,
|
pending,
|
||||||
youtubeChannels,
|
youtubeChannels,
|
||||||
|
blockedChannels,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
push,
|
push,
|
||||||
goBack,
|
goBack,
|
||||||
location: { search },
|
location: { search },
|
||||||
} = useHistory();
|
} = useHistory();
|
||||||
|
const [viewBlockedChannel, setViewBlockedChannel] = React.useState(false);
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const currentView = urlParams.get(PAGE_VIEW_QUERY) || undefined;
|
const currentView = urlParams.get(PAGE_VIEW_QUERY) || undefined;
|
||||||
const [discussionWasMounted, setDiscussionWasMounted] = React.useState(false);
|
const [discussionWasMounted, setDiscussionWasMounted] = React.useState(false);
|
||||||
|
@ -77,6 +81,7 @@ function ChannelPage(props: Props) {
|
||||||
const { permanent_url: permanentUrl } = claim;
|
const { permanent_url: permanentUrl } = claim;
|
||||||
const claimId = claim.claim_id;
|
const claimId = claim.claim_id;
|
||||||
const formattedSubCount = Number(subCount).toLocaleString();
|
const formattedSubCount = Number(subCount).toLocaleString();
|
||||||
|
const isBlocked = claim && blockedChannels.includes(claim.permanent_url);
|
||||||
const isMyYouTubeChannel =
|
const isMyYouTubeChannel =
|
||||||
claim &&
|
claim &&
|
||||||
youtubeChannels &&
|
youtubeChannels &&
|
||||||
|
@ -203,24 +208,44 @@ function ChannelPage(props: Props) {
|
||||||
<div className="channel-cover__gradient" />
|
<div className="channel-cover__gradient" />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<Tabs onChange={onTabChange} index={tabIndex}>
|
{isBlocked && !viewBlockedChannel ? (
|
||||||
<TabList className="tabs__list--channel-page">
|
<div className="main--empty">
|
||||||
<Tab disabled={editing}>{__('Content')}</Tab>
|
<Yrbl
|
||||||
<Tab>{editing ? __('Editing Your Channel') : __('About --[tab title in Channel Page]--')}</Tab>
|
title={__('This channel is blocked')}
|
||||||
<Tab disabled={editing}>{__('Community')}</Tab>
|
subtitle={__('Are you sure you want to view this content? Viewing will not unblock @%channel%', {
|
||||||
</TabList>
|
channel: channelName,
|
||||||
<TabPanels>
|
})}
|
||||||
<TabPanel>
|
actions={
|
||||||
<ChannelContent uri={uri} channelIsBlackListed={channelIsBlackListed} />
|
<div className="section__actions">
|
||||||
</TabPanel>
|
<Button button="primary" label={__('View Content')} onClick={() => setViewBlockedChannel(true)} />
|
||||||
<TabPanel>
|
</div>
|
||||||
<ChannelAbout uri={uri} />
|
}
|
||||||
</TabPanel>
|
/>
|
||||||
<TabPanel>
|
</div>
|
||||||
{(discussionWasMounted || currentView === DISCUSSION_PAGE) && <ChannelDiscussion uri={uri} />}
|
) : (
|
||||||
</TabPanel>
|
<Tabs onChange={onTabChange} index={tabIndex}>
|
||||||
</TabPanels>
|
<TabList className="tabs__list--channel-page">
|
||||||
</Tabs>
|
<Tab disabled={editing}>{__('Content')}</Tab>
|
||||||
|
<Tab>{editing ? __('Editing Your Channel') : __('About --[tab title in Channel Page]--')}</Tab>
|
||||||
|
<Tab disabled={editing}>{__('Community')}</Tab>
|
||||||
|
</TabList>
|
||||||
|
<TabPanels>
|
||||||
|
<TabPanel>
|
||||||
|
<ChannelContent
|
||||||
|
uri={uri}
|
||||||
|
channelIsBlackListed={channelIsBlackListed}
|
||||||
|
viewBlockedChannel={viewBlockedChannel}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
<ChannelAbout uri={uri} />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
{(discussionWasMounted || currentView === DISCUSSION_PAGE) && <ChannelDiscussion uri={uri} />}
|
||||||
|
</TabPanel>
|
||||||
|
</TabPanels>
|
||||||
|
</Tabs>
|
||||||
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue