commit
7e2676d38a
29 changed files with 272 additions and 31 deletions
|
@ -124,7 +124,7 @@
|
||||||
"jsmediatags": "^3.8.1",
|
"jsmediatags": "^3.8.1",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#469d3b70cbe39f28aafee4e072fdfc5bac604c4b",
|
"lbry-redux": "lbryio/lbry-redux#8f12baa88f6f057eb3b7d0cf04d6e4bb0eb11763",
|
||||||
"lbryinc": "lbryio/lbryinc#a93596c51c8fb0a226cb84df04c26a6bb60a45fb",
|
"lbryinc": "lbryio/lbryinc#a93596c51c8fb0a226cb84df04c26a6bb60a45fb",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
|
|
|
@ -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
|
||||||
|
|
16
src/ui/component/blockButton/index.js
Normal file
16
src/ui/component/blockButton/index.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectChannelIsBlocked, doToggleBlockChannel, doToast, makeSelectShortUrlForUri } from 'lbry-redux';
|
||||||
|
import BlockButton from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
channelIsBlocked: selectChannelIsBlocked(props.uri)(state),
|
||||||
|
shortUrl: makeSelectShortUrlForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
{
|
||||||
|
toggleBlockChannel: doToggleBlockChannel,
|
||||||
|
doToast,
|
||||||
|
}
|
||||||
|
)(BlockButton);
|
41
src/ui/component/blockButton/view.jsx
Normal file
41
src/ui/component/blockButton/view.jsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// @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,
|
||||||
|
shortUrl: 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, shortUrl, 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={ICONS.BLOCK}
|
||||||
|
button={'alt'}
|
||||||
|
label={blockedOverride || blockLabel}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!channelIsBlocked) {
|
||||||
|
doToast({ message: `Blocked ${shortUrl}`, 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="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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,25 @@ type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
className?: string,
|
className?: string,
|
||||||
thumbnailPreview: ?string,
|
thumbnailPreview: ?string,
|
||||||
|
obscure?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChannelThumbnail(props: Props) {
|
function ChannelThumbnail(props: Props) {
|
||||||
const { thumbnail, uri, className, thumbnailPreview } = props;
|
const { thumbnail, uri, className, thumbnailPreview, obscure } = props;
|
||||||
|
|
||||||
// Generate a random color class based on the first letter of the channel name
|
// Generate a random color class based on the first letter of the channel name
|
||||||
const { channelName } = parseURI(uri);
|
const { channelName } = parseURI(uri);
|
||||||
const initializer = channelName.charCodeAt(0) - 65; // will be between 0 and 57
|
const initializer = channelName.charCodeAt(0) - 65; // will be between 0 and 57
|
||||||
const colorClassName = `channel-thumbnail__default--${initializer % 4}`;
|
const colorClassName = `channel-thumbnail__default--${initializer % 4}`;
|
||||||
|
const showThumb = !obscure && !!thumbnail;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('channel-thumbnail', className, {
|
className={classnames('channel-thumbnail', className, {
|
||||||
[colorClassName]: !thumbnail,
|
[colorClassName]: !showThumb,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{!thumbnail && <img className="channel-thumbnail__default" src={thumbnailPreview || Gerbil} />}
|
{!showThumb && <img className="channel-thumbnail__default" src={thumbnailPreview || Gerbil} />}
|
||||||
{thumbnail && <img className="channel-thumbnail__custom" src={thumbnailPreview || thumbnail} />}
|
{showThumb && <img className="channel-thumbnail__custom" src={thumbnailPreview || thumbnail} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ type Props = {
|
||||||
id?: string,
|
id?: string,
|
||||||
// If using the default header, this is a unique ID needed to persist the state of the filter setting
|
// If using the default header, this is a unique ID needed to persist the state of the filter setting
|
||||||
persistedStorageKey?: string,
|
persistedStorageKey?: string,
|
||||||
|
showHiddenByUser: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ClaimList(props: Props) {
|
export default function ClaimList(props: Props) {
|
||||||
|
@ -43,6 +44,7 @@ export default function ClaimList(props: Props) {
|
||||||
pageSize,
|
pageSize,
|
||||||
page,
|
page,
|
||||||
id,
|
id,
|
||||||
|
showHiddenByUser,
|
||||||
} = props;
|
} = props;
|
||||||
const [scrollBottomCbMap, setScrollBottomCbMap] = useState({});
|
const [scrollBottomCbMap, setScrollBottomCbMap] = useState({});
|
||||||
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
|
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
|
||||||
|
@ -113,7 +115,7 @@ export default function ClaimList(props: Props) {
|
||||||
<ul className="ul--no-style">
|
<ul className="ul--no-style">
|
||||||
{sortedUris.map((uri, index) => (
|
{sortedUris.map((uri, index) => (
|
||||||
<React.Fragment key={uri}>
|
<React.Fragment key={uri}>
|
||||||
<ClaimPreview uri={uri} type={type} />
|
<ClaimPreview uri={uri} type={type} showUserBlocked={showHiddenByUser} />
|
||||||
{index === 4 && injectedItem && injectedItem}
|
{index === 4 && injectedItem && injectedItem}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doClaimSearch, selectClaimSearchByQuery, selectFetchingClaimSearch, doToggleTagFollow } from 'lbry-redux';
|
import {
|
||||||
|
doClaimSearch,
|
||||||
|
selectClaimSearchByQuery,
|
||||||
|
selectFetchingClaimSearch,
|
||||||
|
doToggleTagFollow,
|
||||||
|
selectBlockedChannels,
|
||||||
|
} from 'lbry-redux';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import ClaimListDiscover from './view';
|
import ClaimListDiscover from './view';
|
||||||
|
@ -10,6 +16,7 @@ const select = state => ({
|
||||||
loading: selectFetchingClaimSearch(state),
|
loading: selectFetchingClaimSearch(state),
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
||||||
|
hiddenUris: selectBlockedChannels(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = {
|
const perform = {
|
||||||
|
|
|
@ -44,6 +44,7 @@ type Props = {
|
||||||
claimSearchByQuery: {
|
claimSearchByQuery: {
|
||||||
[string]: Array<string>,
|
[string]: Array<string>,
|
||||||
},
|
},
|
||||||
|
hiddenUris: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimListDiscover(props: Props) {
|
function ClaimListDiscover(props: Props) {
|
||||||
|
@ -59,6 +60,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
showNsfw,
|
showNsfw,
|
||||||
history,
|
history,
|
||||||
location,
|
location,
|
||||||
|
hiddenUris,
|
||||||
} = props;
|
} = props;
|
||||||
const didNavigateForward = history.action === 'PUSH';
|
const didNavigateForward = history.action === 'PUSH';
|
||||||
const { search, pathname } = location;
|
const { search, pathname } = location;
|
||||||
|
@ -75,6 +77,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
no_totals: boolean,
|
no_totals: boolean,
|
||||||
any_tags: Array<string>,
|
any_tags: Array<string>,
|
||||||
channel_ids: Array<string>,
|
channel_ids: Array<string>,
|
||||||
|
not_channel_ids: Array<string>,
|
||||||
not_tags: Array<string>,
|
not_tags: Array<string>,
|
||||||
order_by: Array<string>,
|
order_by: Array<string>,
|
||||||
release_time?: string,
|
release_time?: string,
|
||||||
|
@ -86,6 +89,7 @@ function ClaimListDiscover(props: Props) {
|
||||||
no_totals: true,
|
no_totals: true,
|
||||||
any_tags: (personalView && personalSort === SEARCH_SORT_YOU) || !personalView ? tags : [],
|
any_tags: (personalView && personalSort === SEARCH_SORT_YOU) || !personalView ? tags : [],
|
||||||
channel_ids: personalSort === SEARCH_SORT_CHANNELS ? subscribedChannels.map(sub => sub.uri.split('#')[1]) : [],
|
channel_ids: personalSort === SEARCH_SORT_CHANNELS ? subscribedChannels.map(sub => sub.uri.split('#')[1]) : [],
|
||||||
|
not_channel_ids: hiddenUris && hiddenUris.length ? hiddenUris.map(hiddenUri => hiddenUri.split('#')[1]) : [],
|
||||||
not_tags: !showNsfw ? MATURE_TAGS : [],
|
not_tags: !showNsfw ? MATURE_TAGS : [],
|
||||||
order_by:
|
order_by:
|
||||||
typeSort === TYPE_TRENDING
|
typeSort === TYPE_TRENDING
|
||||||
|
|
|
@ -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,15 @@ function ClaimPreview(props: Props) {
|
||||||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// block stream claims
|
||||||
|
if (claim && !shouldHide && !showUserBlocked && blockedChannelUris.length && signingChannel) {
|
||||||
|
shouldHide = blockedChannelUris.some(blockedUri => blockedUri === signingChannel.permanent_url);
|
||||||
|
}
|
||||||
|
// block channel claims if we can't control for them in claim search
|
||||||
|
// e.g. fetchRecommendedSubscriptions
|
||||||
|
if (claim && isChannel && !shouldHide && !showUserBlocked && blockedChannelUris.length && isChannel) {
|
||||||
|
shouldHide = blockedChannelUris.some(blockedUri => blockedUri === claim.permanent_url);
|
||||||
|
}
|
||||||
|
|
||||||
function handleContextMenu(e) {
|
function handleContextMenu(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -144,15 +162,18 @@ function ClaimPreview(props: Props) {
|
||||||
'claim-preview--pending': pending,
|
'claim-preview--pending': pending,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isChannel ? <ChannelThumbnail uri={uri} /> : <CardMedia thumbnail={thumbnail} />}
|
{isChannel ? <ChannelThumbnail uri={uri} obscure={channelIsBlocked} /> : <CardMedia thumbnail={thumbnail} />}
|
||||||
<div className="claim-preview-metadata">
|
<div className="claim-preview-metadata">
|
||||||
<div className="claim-preview-info">
|
<div className="claim-preview-info">
|
||||||
<div className="claim-preview-title">
|
<div className="claim-preview-title">
|
||||||
{claim ? <TruncatedText text={title || claim.name} lines={1} /> : <span>{__('Nothing here')}</span>}
|
{claim ? <TruncatedText text={title || claim.name} lines={1} /> : <span>{__('Nothing here')}</span>}
|
||||||
</div>
|
</div>
|
||||||
{!hideActions && (
|
{!hideActions && (
|
||||||
<div>
|
<div className="card__actions--inline">
|
||||||
{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';
|
||||||
|
@ -14,6 +15,7 @@ import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
import ChannelEdit from 'component/channelEdit';
|
import ChannelEdit from 'component/channelEdit';
|
||||||
import ClaimUri from 'component/claimUri';
|
import ClaimUri from 'component/claimUri';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
const PAGE_VIEW_QUERY = `view`;
|
const PAGE_VIEW_QUERY = `view`;
|
||||||
const ABOUT_PAGE = `about`;
|
const ABOUT_PAGE = `about`;
|
||||||
|
@ -29,10 +31,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);
|
||||||
|
@ -62,11 +78,18 @@ function ChannelPage(props: Props) {
|
||||||
<Page>
|
<Page>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<header className="channel-cover">
|
<header className="channel-cover">
|
||||||
{!editing && cover && <img className="channel-cover__custom" src={cover} />}
|
{!editing && cover && (
|
||||||
|
<img
|
||||||
|
className={classnames('channel-cover__custom', { 'channel__image--blurred': channelIsBlocked })}
|
||||||
|
src={cover}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{editing && <img className="channel-cover__custom" src={coverPreview} />}
|
{editing && <img className="channel-cover__custom" src={coverPreview} />}
|
||||||
{/* component that offers select/upload */}
|
{/* component that offers select/upload */}
|
||||||
<div className="channel__primary-info ">
|
<div className="channel__primary-info ">
|
||||||
{!editing && <ChannelThumbnail className="channel__thumbnail--channel-page" uri={uri} />}
|
{!editing && (
|
||||||
|
<ChannelThumbnail className="channel__thumbnail--channel-page" uri={uri} obscure={channelIsBlocked} />
|
||||||
|
)}
|
||||||
{editing && (
|
{editing && (
|
||||||
<ChannelThumbnail
|
<ChannelThumbnail
|
||||||
className="channel__thumbnail--channel-page"
|
className="channel__thumbnail--channel-page"
|
||||||
|
@ -91,7 +114,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>
|
||||||
|
{uris && uris.length ? (
|
||||||
|
<div className="card">
|
||||||
|
<ClaimList
|
||||||
|
header={<h1>{__('Your Blocked Channels')}</h1>}
|
||||||
|
persistedStorageKey="block-list-published"
|
||||||
|
uris={uris}
|
||||||
|
defaultSort
|
||||||
|
showHiddenByUser
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="main--empty">
|
||||||
|
<section className="card card--section">
|
||||||
|
<h2 className="card__title">{__('You aren’t blocking any channels')}</h2>
|
||||||
|
<p className="card__subtitle">When you block a channel, all content from that channel will be hidden.</p>
|
||||||
|
</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,
|
||||||
|
blockedReducer,
|
||||||
publishReducer,
|
publishReducer,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
|
@ -25,7 +26,6 @@ import contentReducer from 'redux/reducers/content';
|
||||||
import settingsReducer from 'redux/reducers/settings';
|
import settingsReducer from 'redux/reducers/settings';
|
||||||
import subscriptionsReducer from 'redux/reducers/subscriptions';
|
import subscriptionsReducer from 'redux/reducers/subscriptions';
|
||||||
|
|
||||||
|
|
||||||
export default history =>
|
export default history =>
|
||||||
combineReducers({
|
combineReducers({
|
||||||
router: connectRouter(history),
|
router: connectRouter(history),
|
||||||
|
@ -47,6 +47,7 @@ export default history =>
|
||||||
stats: statsReducer,
|
stats: statsReducer,
|
||||||
subscriptions: subscriptionsReducer,
|
subscriptions: subscriptionsReducer,
|
||||||
tags: tagsReducer,
|
tags: tagsReducer,
|
||||||
|
blocked: blockedReducer,
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
wallet: walletReducer,
|
wallet: walletReducer,
|
||||||
});
|
});
|
||||||
|
|
|
@ -102,3 +102,7 @@ $metadata-z-index: 1;
|
||||||
color: rgba($lbry-white, 0.75);
|
color: rgba($lbry-white, 0.75);
|
||||||
margin-right: var(--spacing-large);
|
margin-right: var(--spacing-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel__image--blurred {
|
||||||
|
filter: blur(16px);
|
||||||
|
}
|
||||||
|
|
|
@ -231,6 +231,14 @@ $border-color--dark: var(--dm-color-04);
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.claim-preview-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
.claim-preview__button {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.claim-preview-properties {
|
.claim-preview-properties {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -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('blocked', ['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',
|
||||||
|
'blocked',
|
||||||
];
|
];
|
||||||
|
|
||||||
const transforms = [
|
const transforms = [
|
||||||
|
@ -66,6 +68,7 @@ const transforms = [
|
||||||
walletFilter,
|
walletFilter,
|
||||||
contentFilter,
|
contentFilter,
|
||||||
fileInfoFilter,
|
fileInfoFilter,
|
||||||
|
blockedFilter,
|
||||||
// @endif
|
// @endif
|
||||||
appFilter,
|
appFilter,
|
||||||
searchFilter,
|
searchFilter,
|
||||||
|
|
|
@ -6653,9 +6653,9 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
|
||||||
yargs "^13.2.2"
|
yargs "^13.2.2"
|
||||||
zstd-codec "^0.1.1"
|
zstd-codec "^0.1.1"
|
||||||
|
|
||||||
lbry-redux@lbryio/lbry-redux#469d3b70cbe39f28aafee4e072fdfc5bac604c4b:
|
lbry-redux@lbryio/lbry-redux#8f12baa88f6f057eb3b7d0cf04d6e4bb0eb11763:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/469d3b70cbe39f28aafee4e072fdfc5bac604c4b"
|
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/8f12baa88f6f057eb3b7d0cf04d6e4bb0eb11763"
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
Loading…
Add table
Reference in a new issue