adds user ability to block channels
small changes blocked channels page most features done tweaks
This commit is contained in:
parent
223a68413f
commit
5ab165131f
21 changed files with 217 additions and 16 deletions
|
@ -89,6 +89,14 @@ const analytics: Analytics = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
channelBlockEvent: (uri, blocked, location) => {
|
||||||
|
if (analyticsEnabled) {
|
||||||
|
ReactGA.event({
|
||||||
|
category: blocked ? 'Channel-Hidden' : 'Channel-Unhidden',
|
||||||
|
action: uri,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize google analytics
|
// Initialize google analytics
|
||||||
|
|
15
src/ui/component/blockButton/index.js
Normal file
15
src/ui/component/blockButton/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectChannelIsBlocked, doToggleBlockChannel, doToast } from 'lbry-redux';
|
||||||
|
import BlockButton from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
channelIsBlocked: selectChannelIsBlocked(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
{
|
||||||
|
toggleBlockChannel: doToggleBlockChannel,
|
||||||
|
doToast,
|
||||||
|
}
|
||||||
|
)(BlockButton);
|
40
src/ui/component/blockButton/view.jsx
Normal file
40
src/ui/component/blockButton/view.jsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// @flow
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import useHover from 'util/use-hover';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
uri: string,
|
||||||
|
isSubscribed: boolean,
|
||||||
|
toggleBlockChannel: (uri: string) => void,
|
||||||
|
channelIsBlocked: boolean,
|
||||||
|
doToast: ({ message: string, linkText: string, linkTarget: string }) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function BlockButton(props: Props) {
|
||||||
|
const { uri, toggleBlockChannel, channelIsBlocked, doToast } = props;
|
||||||
|
|
||||||
|
const blockRef = useRef();
|
||||||
|
const isHovering = useHover(blockRef);
|
||||||
|
const blockLabel = channelIsBlocked ? __('Blocked') : __('Block');
|
||||||
|
const blockedOverride = channelIsBlocked && isHovering && __('Unblock');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
ref={blockRef}
|
||||||
|
iconColor="red"
|
||||||
|
icon={blockedOverride ? ICONS.UNBLOCK : ICONS.BLOCK}
|
||||||
|
button={'alt'}
|
||||||
|
label={blockedOverride || blockLabel}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!channelIsBlocked) {
|
||||||
|
doToast({ message: `Blocked ${uri}`, linkText: 'Manage', linkTarget: `/${PAGES.BLOCKED}` });
|
||||||
|
}
|
||||||
|
toggleBlockChannel(uri);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import {
|
||||||
makeSelectFetchingChannelClaims,
|
makeSelectFetchingChannelClaims,
|
||||||
makeSelectClaimIsMine,
|
makeSelectClaimIsMine,
|
||||||
makeSelectTotalPagesForChannel,
|
makeSelectTotalPagesForChannel,
|
||||||
|
selectChannelIsBlocked,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import ChannelPage from './view';
|
import ChannelPage from './view';
|
||||||
|
@ -19,6 +20,7 @@ const select = (state, props) => {
|
||||||
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
|
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
|
||||||
totalPages: makeSelectTotalPagesForChannel(props.uri, PAGE_SIZE)(state),
|
totalPages: makeSelectTotalPagesForChannel(props.uri, PAGE_SIZE)(state),
|
||||||
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
|
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
|
channelIsBlocked: selectChannelIsBlocked(props.uri)(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,13 @@ type Props = {
|
||||||
fetching: boolean,
|
fetching: boolean,
|
||||||
params: { page: number },
|
params: { page: number },
|
||||||
claimsInChannel: Array<StreamClaim>,
|
claimsInChannel: Array<StreamClaim>,
|
||||||
|
channelIsBlocked: boolean,
|
||||||
channelIsMine: boolean,
|
channelIsMine: boolean,
|
||||||
fetchClaims: (string, number) => void,
|
fetchClaims: (string, number) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChannelContent(props: Props) {
|
function ChannelContent(props: Props) {
|
||||||
const { uri, fetching, claimsInChannel, totalPages, channelIsMine, fetchClaims } = props;
|
const { uri, fetching, claimsInChannel, totalPages, channelIsMine, channelIsBlocked, fetchClaims } = props;
|
||||||
const hasContent = Boolean(claimsInChannel && claimsInChannel.length);
|
const hasContent = Boolean(claimsInChannel && claimsInChannel.length);
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -27,21 +28,30 @@ function ChannelContent(props: Props) {
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!fetching && !hasContent && (
|
{!fetching && !hasContent && !channelIsBlocked && (
|
||||||
<div className="card--section">
|
<div className="card--section">
|
||||||
<h2 className="help">{__("This channel hasn't uploaded anything.")}</h2>
|
<h2 className="help">{__("This channel hasn't uploaded anything.")}</h2>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!fetching && channelIsBlocked && (
|
||||||
|
<div className="card--section">
|
||||||
|
<h2 className="card__content help">{__('You have blocked this channel content.')}</h2>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!channelIsMine && <HiddenNsfwClaims className="card__subtitle" uri={uri} />}
|
{!channelIsMine && <HiddenNsfwClaims className="card__subtitle" uri={uri} />}
|
||||||
|
|
||||||
{hasContent && <ClaimList header={false} uris={claimsInChannel.map(claim => claim.permanent_url)} />}
|
{hasContent && !channelIsBlocked && (
|
||||||
|
<ClaimList header={false} uris={claimsInChannel.map(claim => claim.permanent_url)} />
|
||||||
<Paginate
|
)}
|
||||||
onPageChange={page => fetchClaims(uri, page)}
|
{!channelIsBlocked && (
|
||||||
totalPages={totalPages}
|
<Paginate
|
||||||
loading={fetching && !hasContent}
|
onPageChange={page => fetchClaims(uri, page)}
|
||||||
/>
|
totalPages={totalPages}
|
||||||
|
loading={fetching && !hasContent}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
|
selectBlockedChannels,
|
||||||
|
selectChannelIsBlocked,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||||
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
||||||
|
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||||
import ClaimPreview from './view';
|
import ClaimPreview from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -25,7 +28,10 @@ const select = (state, props) => ({
|
||||||
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
|
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
|
||||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||||
filteredOutpoints: selectFilteredOutpoints(state),
|
filteredOutpoints: selectFilteredOutpoints(state),
|
||||||
|
blockedChannelUris: selectBlockedChannels(state),
|
||||||
hasVisitedUri: makeSelectHasVisitedUri(props.uri)(state),
|
hasVisitedUri: makeSelectHasVisitedUri(props.uri)(state),
|
||||||
|
channelIsBlocked: selectChannelIsBlocked(props.uri)(state),
|
||||||
|
isSubscribed: makeSelectIsSubscribed(props.uri, true)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -13,12 +13,14 @@ import FileProperties from 'component/fileProperties';
|
||||||
import ClaimTags from 'component/claimTags';
|
import ClaimTags from 'component/claimTags';
|
||||||
import SubscribeButton from 'component/subscribeButton';
|
import SubscribeButton from 'component/subscribeButton';
|
||||||
import ChannelThumbnail from 'component/channelThumbnail';
|
import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
|
import BlockButton from 'component/blockButton';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
claim: ?Claim,
|
claim: ?Claim,
|
||||||
obscureNsfw: boolean,
|
obscureNsfw: boolean,
|
||||||
|
showUserBlocked: boolean,
|
||||||
claimIsMine: boolean,
|
claimIsMine: boolean,
|
||||||
pending?: boolean,
|
pending?: boolean,
|
||||||
resolveUri: string => void,
|
resolveUri: string => void,
|
||||||
|
@ -39,6 +41,9 @@ type Props = {
|
||||||
txid: string,
|
txid: string,
|
||||||
nout: number,
|
nout: number,
|
||||||
}>,
|
}>,
|
||||||
|
blockedChannelUris: Array<string>,
|
||||||
|
channelIsBlocked: boolean,
|
||||||
|
isSubscribed: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimPreview(props: Props) {
|
function ClaimPreview(props: Props) {
|
||||||
|
@ -58,7 +63,11 @@ function ClaimPreview(props: Props) {
|
||||||
type,
|
type,
|
||||||
blackListedOutpoints,
|
blackListedOutpoints,
|
||||||
filteredOutpoints,
|
filteredOutpoints,
|
||||||
|
blockedChannelUris,
|
||||||
hasVisitedUri,
|
hasVisitedUri,
|
||||||
|
showUserBlocked,
|
||||||
|
channelIsBlocked,
|
||||||
|
isSubscribed,
|
||||||
} = props;
|
} = props;
|
||||||
const haventFetched = claim === undefined;
|
const haventFetched = claim === undefined;
|
||||||
const abandoned = !isResolvingUri && !claim;
|
const abandoned = !isResolvingUri && !claim;
|
||||||
|
@ -93,6 +102,10 @@ function ClaimPreview(props: Props) {
|
||||||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// if showUserBlocked wasnt passed to claimPreview (for blocked page) hide user-blocked channels
|
||||||
|
if (claim && !shouldHide && !showUserBlocked && blockedChannelUris.length && signingChannel) {
|
||||||
|
shouldHide = blockedChannelUris.some(blockedUri => blockedUri === signingChannel.permanent_url);
|
||||||
|
}
|
||||||
|
|
||||||
function handleContextMenu(e) {
|
function handleContextMenu(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -152,7 +165,10 @@ function ClaimPreview(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
{!hideActions && (
|
{!hideActions && (
|
||||||
<div>
|
<div>
|
||||||
{isChannel && <SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />}
|
{isChannel && !channelIsBlocked && (
|
||||||
|
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
||||||
|
)}
|
||||||
|
{isChannel && !isSubscribed && <BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />}
|
||||||
{!isChannel && <FileProperties uri={uri} />}
|
{!isChannel && <FileProperties uri={uri} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -128,6 +128,17 @@ export const icons = {
|
||||||
<line x1="12" y1="17" x2="12" y2="17" />
|
<line x1="12" y1="17" x2="12" y2="17" />
|
||||||
</g>
|
</g>
|
||||||
),
|
),
|
||||||
|
[ICONS.BLOCK]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.UNBLOCK]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
[ICONS.LIGHT]: buildIcon(
|
[ICONS.LIGHT]: buildIcon(
|
||||||
<g>
|
<g>
|
||||||
<circle cx="12" cy="12" r="5" />
|
<circle cx="12" cy="12" r="5" />
|
||||||
|
|
|
@ -21,6 +21,7 @@ import WalletPage from 'page/wallet';
|
||||||
import NavigationHistory from 'page/navigationHistory';
|
import NavigationHistory from 'page/navigationHistory';
|
||||||
import TagsPage from 'page/tags';
|
import TagsPage from 'page/tags';
|
||||||
import FollowingPage from 'page/following';
|
import FollowingPage from 'page/following';
|
||||||
|
import ListBlocked from 'page/listBlocked';
|
||||||
|
|
||||||
// Tell the browser we are handling scroll restoration
|
// Tell the browser we are handling scroll restoration
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
|
@ -64,6 +65,7 @@ function AppRouter(props: Props) {
|
||||||
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
|
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
|
||||||
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={FollowingPage} />
|
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={FollowingPage} />
|
||||||
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
||||||
|
<Route path={`/$/${PAGES.BLOCKED}`} exact component={ListBlocked} />
|
||||||
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
||||||
<Route path="/:claimName" exact component={ShowPage} />
|
<Route path="/:claimName" exact component={ShowPage} />
|
||||||
<Route path="/:claimName/:contentName" exact component={ShowPage} />
|
<Route path="/:claimName/:contentName" exact component={ShowPage} />
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { parseURI } from 'lbry-redux';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import useHover from 'util/use-hover';
|
import useHover from 'util/use-hover';
|
||||||
|
|
||||||
type SubscribtionArgs = {
|
type SubscriptionArgs = {
|
||||||
channelName: string,
|
channelName: string,
|
||||||
uri: string,
|
uri: string,
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@ type Props = {
|
||||||
isSubscribed: boolean,
|
isSubscribed: boolean,
|
||||||
subscriptions: Array<string>,
|
subscriptions: Array<string>,
|
||||||
doChannelSubscribe: ({ channelName: string, uri: string }) => void,
|
doChannelSubscribe: ({ channelName: string, uri: string }) => void,
|
||||||
doChannelUnsubscribe: SubscribtionArgs => void,
|
doChannelUnsubscribe: SubscriptionArgs => void,
|
||||||
doOpenModal: (id: string) => void,
|
doOpenModal: (id: string) => void,
|
||||||
showSnackBarOnSubscribe: boolean,
|
showSnackBarOnSubscribe: boolean,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
|
|
|
@ -71,3 +71,5 @@ export const DARK = 'Moon';
|
||||||
export const LIBRARY = 'Folder';
|
export const LIBRARY = 'Folder';
|
||||||
export const TAG = 'Tag';
|
export const TAG = 'Tag';
|
||||||
export const SUPPORT = 'TrendingUp';
|
export const SUPPORT = 'TrendingUp';
|
||||||
|
export const BLOCK = 'Slash';
|
||||||
|
export const UNBLOCK = 'Circle';
|
||||||
|
|
|
@ -20,3 +20,4 @@ export const SEARCH = 'search';
|
||||||
export const TRANSACTIONS = 'transactions';
|
export const TRANSACTIONS = 'transactions';
|
||||||
export const TAGS = 'tags';
|
export const TAGS = 'tags';
|
||||||
export const WALLET = 'wallet';
|
export const WALLET = 'wallet';
|
||||||
|
export const BLOCKED = 'blocked';
|
||||||
|
|
|
@ -6,7 +6,9 @@ import {
|
||||||
makeSelectCoverForUri,
|
makeSelectCoverForUri,
|
||||||
selectCurrentChannelPage,
|
selectCurrentChannelPage,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
|
selectChannelIsBlocked,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||||
import ChannelPage from './view';
|
import ChannelPage from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -16,6 +18,8 @@ const select = (state, props) => ({
|
||||||
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
|
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
page: selectCurrentChannelPage(state),
|
page: selectCurrentChannelPage(state),
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
isSubscribed: makeSelectIsSubscribed(props.uri, true)(state),
|
||||||
|
channelIsBlocked: selectChannelIsBlocked(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -3,6 +3,7 @@ import React, { useState } from 'react';
|
||||||
import { parseURI } from 'lbry-redux';
|
import { parseURI } from 'lbry-redux';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import SubscribeButton from 'component/subscribeButton';
|
import SubscribeButton from 'component/subscribeButton';
|
||||||
|
import BlockButton from 'component/blockButton';
|
||||||
import ShareButton from 'component/shareButton';
|
import ShareButton from 'component/shareButton';
|
||||||
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
|
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
|
@ -29,10 +30,24 @@ type Props = {
|
||||||
history: { push: string => void },
|
history: { push: string => void },
|
||||||
match: { params: { attribute: ?string } },
|
match: { params: { attribute: ?string } },
|
||||||
channelIsMine: boolean,
|
channelIsMine: boolean,
|
||||||
|
isSubscribed: boolean,
|
||||||
|
channelIsBlocked: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChannelPage(props: Props) {
|
function ChannelPage(props: Props) {
|
||||||
const { uri, title, cover, history, location, page, channelIsMine, thumbnail, claim } = props;
|
const {
|
||||||
|
uri,
|
||||||
|
title,
|
||||||
|
cover,
|
||||||
|
history,
|
||||||
|
location,
|
||||||
|
page,
|
||||||
|
channelIsMine,
|
||||||
|
thumbnail,
|
||||||
|
claim,
|
||||||
|
isSubscribed,
|
||||||
|
channelIsBlocked,
|
||||||
|
} = props;
|
||||||
const { channelName } = parseURI(uri);
|
const { channelName } = parseURI(uri);
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
|
@ -91,7 +106,8 @@ function ChannelPage(props: Props) {
|
||||||
<Tab>{editing ? __('Editing Your Channel') : __('About')}</Tab>
|
<Tab>{editing ? __('Editing Your Channel') : __('About')}</Tab>
|
||||||
<div className="card__actions--inline">
|
<div className="card__actions--inline">
|
||||||
<ShareButton uri={uri} />
|
<ShareButton uri={uri} />
|
||||||
<SubscribeButton uri={permanentUrl} />
|
{!channelIsBlocked && <SubscribeButton uri={permanentUrl} />}
|
||||||
|
{!isSubscribed && <BlockButton uri={permanentUrl} />}
|
||||||
</div>
|
</div>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Props = {
|
||||||
channelUri: string,
|
channelUri: string,
|
||||||
viewCount: number,
|
viewCount: number,
|
||||||
prepareEdit: ({}, string, {}) => void,
|
prepareEdit: ({}, string, {}) => void,
|
||||||
openModal: (id: string, { uri: string, claimIsMine: boolean, isSupport: boolean }) => void,
|
openModal: (id: string, { [key: string]: any }) => void,
|
||||||
markSubscriptionRead: (string, string) => void,
|
markSubscriptionRead: (string, string) => void,
|
||||||
fetchViewCount: string => void,
|
fetchViewCount: string => void,
|
||||||
balance: number,
|
balance: number,
|
||||||
|
|
12
src/ui/page/listBlocked/index.js
Normal file
12
src/ui/page/listBlocked/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectBlockedChannels } from 'lbry-redux';
|
||||||
|
import ListBlocked from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
uris: selectBlockedChannels(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
null
|
||||||
|
)(ListBlocked);
|
37
src/ui/page/listBlocked/view.jsx
Normal file
37
src/ui/page/listBlocked/view.jsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import ClaimList from 'component/claimList';
|
||||||
|
import Page from 'component/page';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
uris: Array<string>,
|
||||||
|
};
|
||||||
|
|
||||||
|
function ListBlocked(props: Props) {
|
||||||
|
const { uris } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page notContained>
|
||||||
|
{uris && uris.length ? (
|
||||||
|
<div className="card">
|
||||||
|
<ClaimList
|
||||||
|
header={<h1>{__('Your Blocked Channels')}</h1>}
|
||||||
|
persistedStorageKey="block-list-published"
|
||||||
|
uris={uris}
|
||||||
|
defaultSort
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="main--empty">
|
||||||
|
<section className="card card--section">
|
||||||
|
<header className="card__header">
|
||||||
|
<h2 className="card__title">{__('It looks like you have no blocked channels.')}</h2>
|
||||||
|
</header>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListBlocked;
|
|
@ -8,7 +8,7 @@ import {
|
||||||
selectLanguages,
|
selectLanguages,
|
||||||
selectosNotificationsEnabled,
|
selectosNotificationsEnabled,
|
||||||
} from 'redux/selectors/settings';
|
} from 'redux/selectors/settings';
|
||||||
import { doWalletStatus, selectWalletIsEncrypted } from 'lbry-redux';
|
import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount } from 'lbry-redux';
|
||||||
import SettingsPage from './view';
|
import SettingsPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -26,6 +26,7 @@ const select = state => ({
|
||||||
osNotificationsEnabled: selectosNotificationsEnabled(state),
|
osNotificationsEnabled: selectosNotificationsEnabled(state),
|
||||||
autoDownload: makeSelectClientSetting(settings.AUTO_DOWNLOAD)(state),
|
autoDownload: makeSelectClientSetting(settings.AUTO_DOWNLOAD)(state),
|
||||||
supportOption: makeSelectClientSetting(settings.SUPPORT_OPTION)(state),
|
supportOption: makeSelectClientSetting(settings.SUPPORT_OPTION)(state),
|
||||||
|
userBlockedChannelsCount: selectBlockedChannelsCount(state),
|
||||||
hideBalance: makeSelectClientSetting(settings.HIDE_BALANCE)(state),
|
hideBalance: makeSelectClientSetting(settings.HIDE_BALANCE)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { FormField, FormFieldPrice, Form } from 'component/common/form';
|
import { FormField, FormFieldPrice, Form } from 'component/common/form';
|
||||||
|
@ -44,6 +45,7 @@ type Props = {
|
||||||
walletEncrypted: boolean,
|
walletEncrypted: boolean,
|
||||||
osNotificationsEnabled: boolean,
|
osNotificationsEnabled: boolean,
|
||||||
supportOption: boolean,
|
supportOption: boolean,
|
||||||
|
userBlockedChannelsCount?: number,
|
||||||
hideBalance: boolean,
|
hideBalance: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,6 +155,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
setClientSetting,
|
setClientSetting,
|
||||||
supportOption,
|
supportOption,
|
||||||
hideBalance,
|
hideBalance,
|
||||||
|
userBlockedChannelsCount,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
||||||
|
@ -278,6 +281,16 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
</Form>
|
</Form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="card card--section">
|
||||||
|
<h2 className="card__title">{__('Blocked Channels')}</h2>
|
||||||
|
<p className="card__subtitle card__help ">
|
||||||
|
{__('You have')} {userBlockedChannelsCount} {__('blocked')}{' '}
|
||||||
|
{userBlockedChannelsCount === 1 && __('channel')}
|
||||||
|
{userBlockedChannelsCount !== 1 && __('channels')}.{' '}
|
||||||
|
<Button button="link" label={__('Manage')} navigate={`/$/${PAGES.BLOCKED}`} />
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<h2 className="card__title">{__('Notifications')}</h2>
|
<h2 className="card__title">{__('Notifications')}</h2>
|
||||||
<Form>
|
<Form>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
notificationsReducer,
|
notificationsReducer,
|
||||||
tagsReducer,
|
tagsReducer,
|
||||||
commentReducer,
|
commentReducer,
|
||||||
|
blockChannelReducer,
|
||||||
publishReducer,
|
publishReducer,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
|
@ -47,6 +48,7 @@ export default history =>
|
||||||
stats: statsReducer,
|
stats: statsReducer,
|
||||||
subscriptions: subscriptionsReducer,
|
subscriptions: subscriptionsReducer,
|
||||||
tags: tagsReducer,
|
tags: tagsReducer,
|
||||||
|
blockedChannels: blockChannelReducer,
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
wallet: walletReducer,
|
wallet: walletReducer,
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,6 +48,7 @@ const appFilter = createFilter('app', ['hasClickedComment', 'searchOptionsExpand
|
||||||
const walletFilter = createFilter('wallet', ['receiveAddress']);
|
const walletFilter = createFilter('wallet', ['receiveAddress']);
|
||||||
const searchFilter = createFilter('search', ['options']);
|
const searchFilter = createFilter('search', ['options']);
|
||||||
const tagsFilter = createFilter('tags', ['followedTags']);
|
const tagsFilter = createFilter('tags', ['followedTags']);
|
||||||
|
const blockedFilter = createFilter('blockedChannels', ['blockedChannels']);
|
||||||
const whiteListedReducers = [
|
const whiteListedReducers = [
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
'publish',
|
'publish',
|
||||||
|
@ -59,6 +60,7 @@ const whiteListedReducers = [
|
||||||
'app',
|
'app',
|
||||||
'search',
|
'search',
|
||||||
'tags',
|
'tags',
|
||||||
|
'blockedChannels',
|
||||||
];
|
];
|
||||||
|
|
||||||
const transforms = [
|
const transforms = [
|
||||||
|
@ -66,6 +68,7 @@ const transforms = [
|
||||||
walletFilter,
|
walletFilter,
|
||||||
contentFilter,
|
contentFilter,
|
||||||
fileInfoFilter,
|
fileInfoFilter,
|
||||||
|
blockedFilter,
|
||||||
// @endif
|
// @endif
|
||||||
appFilter,
|
appFilter,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
|
|
Loading…
Reference in a new issue