New moderation tools: block & mute #5572

Merged
neb-b merged 9 commits from block into master 2021-03-03 19:50:17 +01:00
6 changed files with 59 additions and 21 deletions
Showing only changes of commit e22d1c0d1a - Show all commits

View file

@ -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}

View file

@ -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:

View file

@ -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>

View file

@ -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);
} }

View file

@ -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) => ({

View file

@ -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>
); );
} }