Reduce impact of scanning blocklists (#121)
## Issue - Each tile was checking against 4 blocklists (blacklisted, filtered, muted, commentron) on every render. Loading the front-page with Cheese alone caused 1400 calls. - This is also part of the reason why pressing Back into the tile list takes forever. ## Fix Since we still need to perform the checks at the app side for now, tried to memoize the operation through a selector.
This commit is contained in:
parent
dad7264636
commit
a90c516c71
6 changed files with 117 additions and 122 deletions
|
@ -56,6 +56,7 @@ export { selectFilteredOutpoints, selectFilteredOutpointMap } from './redux/sele
|
|||
// selectFetchingTrendingUris,
|
||||
// } from './redux/selectors/homepage';
|
||||
export { selectViewCount, makeSelectViewCountForUri, makeSelectSubCountForUri } from './redux/selectors/stats';
|
||||
export { selectBanStateForUri } from './redux/selectors/ban';
|
||||
export {
|
||||
selectHasSyncedWallet,
|
||||
selectSyncData,
|
||||
|
|
96
extras/lbryinc/redux/selectors/ban.js
Normal file
96
extras/lbryinc/redux/selectors/ban.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
// @flow
|
||||
|
||||
// TODO: This should be in 'redux/selectors/claim.js'. Temporarily putting it
|
||||
// here to get past importing issues with 'lbryinc', which the real fix might
|
||||
// involve moving it from 'extras' to 'ui' (big change).
|
||||
|
||||
import { createCachedSelector } from 're-reselect';
|
||||
import { selectClaimForUri } from 'redux/selectors/claims';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
import { isURIEqual } from 'util/lbryURI';
|
||||
|
||||
const selectClaimExistsForUri = (state, uri) => {
|
||||
return Boolean(selectClaimForUri(state, uri));
|
||||
};
|
||||
|
||||
const selectTxidForUri = (state, uri) => {
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
return signingChannel ? signingChannel.txid : claim ? claim.txid : undefined;
|
||||
};
|
||||
|
||||
const selectNoutForUri = (state, uri) => {
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
return signingChannel ? signingChannel.nout : claim ? claim.nout : undefined;
|
||||
};
|
||||
|
||||
const selectPermanentUrlForUri = (state, uri) => {
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
return signingChannel ? signingChannel.permanent_url : claim ? claim.permanent_url : undefined;
|
||||
};
|
||||
|
||||
export const selectBanStateForUri = createCachedSelector(
|
||||
// Break apart 'selectClaimForUri' into 4 cheaper selectors that return
|
||||
// primitives for values that we care about. The Claim object itself is easily
|
||||
// invalidated due to constantly-changing fields like 'confirmation'.
|
||||
selectClaimExistsForUri,
|
||||
selectTxidForUri,
|
||||
selectNoutForUri,
|
||||
selectPermanentUrlForUri,
|
||||
selectBlackListedOutpoints,
|
||||
selectFilteredOutpoints,
|
||||
selectMutedChannels,
|
||||
selectModerationBlockList,
|
||||
(
|
||||
claimExists,
|
||||
txid,
|
||||
nout,
|
||||
permanentUrl,
|
||||
blackListedOutpoints,
|
||||
filteredOutpoints,
|
||||
mutedChannelUris,
|
||||
personalBlocklist
|
||||
) => {
|
||||
const banState = {};
|
||||
|
||||
if (!claimExists) {
|
||||
return banState;
|
||||
}
|
||||
|
||||
// This will be replaced once blocking is done at the wallet server level.
|
||||
if (blackListedOutpoints) {
|
||||
if (blackListedOutpoints.some((outpoint) => outpoint.txid === txid && outpoint.nout === nout)) {
|
||||
banState['blacklisted'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We're checking to see if the stream outpoint or signing channel outpoint
|
||||
// is in the filter list.
|
||||
if (filteredOutpoints) {
|
||||
if (filteredOutpoints.some((outpoint) => outpoint.txid === txid && outpoint.nout === nout)) {
|
||||
banState['filtered'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// block stream claims
|
||||
// block channel claims if we can't control for them in claim search
|
||||
if (mutedChannelUris.length) {
|
||||
if (mutedChannelUris.some((blockedUri) => isURIEqual(blockedUri, permanentUrl))) {
|
||||
banState['muted'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Commentron blocklist
|
||||
if (personalBlocklist.length) {
|
||||
if (personalBlocklist.some((blockedUri) => isURIEqual(blockedUri, permanentUrl))) {
|
||||
banState['blocked'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return banState;
|
||||
}
|
||||
)((state, uri) => String(uri));
|
|
@ -21,13 +21,11 @@ import {
|
|||
import { doResolveUri } from 'redux/actions/claims';
|
||||
import { doCollectionEdit } from 'redux/actions/collections';
|
||||
import { doFileGet } from 'redux/actions/file';
|
||||
import { selectMutedChannels, makeSelectChannelIsMuted } from 'redux/selectors/blocked';
|
||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
import { selectBanStateForUri } from 'lbryinc';
|
||||
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
||||
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
|
||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||
import ClaimPreview from './view';
|
||||
import formatMediaDuration from 'util/formatMediaDuration';
|
||||
|
||||
|
@ -48,12 +46,8 @@ const select = (state, props) => {
|
|||
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
|
||||
isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state),
|
||||
nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state),
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
mutedUris: selectMutedChannels(state),
|
||||
blockedUris: selectModerationBlockList(state),
|
||||
banState: selectBanStateForUri(state, props.uri),
|
||||
hasVisitedUri: props.uri && makeSelectHasVisitedUri(props.uri)(state),
|
||||
channelIsBlocked: props.uri && makeSelectChannelIsMuted(props.uri)(state),
|
||||
isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state),
|
||||
streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state),
|
||||
wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state),
|
||||
|
|
|
@ -5,7 +5,7 @@ import { NavLink, withRouter } from 'react-router-dom';
|
|||
import { isEmpty } from 'util/object';
|
||||
import { lazyImport } from 'util/lazyImport';
|
||||
import classnames from 'classnames';
|
||||
import { isURIEqual, isURIValid } from 'util/lbryURI';
|
||||
import { isURIValid } from 'util/lbryURI';
|
||||
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
||||
|
@ -52,18 +52,9 @@ type Props = {
|
|||
nsfw: boolean,
|
||||
placeholder: string,
|
||||
type: string,
|
||||
banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean },
|
||||
hasVisitedUri: boolean,
|
||||
blackListedOutpoints: Array<{
|
||||
txid: string,
|
||||
nout: number,
|
||||
}>,
|
||||
filteredOutpoints: Array<{
|
||||
txid: string,
|
||||
nout: number,
|
||||
}>,
|
||||
mutedUris: Array<string>,
|
||||
blockedUris: Array<string>,
|
||||
channelIsBlocked: boolean,
|
||||
actions: boolean | Node | string | number,
|
||||
properties: boolean | Node | string | number | ((Claim) => Node),
|
||||
empty?: Node,
|
||||
|
@ -113,7 +104,6 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
streamingUrl,
|
||||
mediaDuration,
|
||||
// user properties
|
||||
channelIsBlocked,
|
||||
hasVisitedUri,
|
||||
// component
|
||||
history,
|
||||
|
@ -139,10 +129,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
properties,
|
||||
onClick,
|
||||
actions,
|
||||
mutedUris,
|
||||
blockedUris,
|
||||
blackListedOutpoints,
|
||||
filteredOutpoints,
|
||||
banState,
|
||||
includeSupportAction,
|
||||
renderActions,
|
||||
hideMenu = false,
|
||||
|
@ -246,28 +233,13 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
((abandoned && !showUnresolvedClaim) || (!claimIsMine && obscureNsfw && nsfw));
|
||||
|
||||
// This will be replaced once blocking is done at the wallet server level
|
||||
if (claim && !claimIsMine && !shouldHide && blackListedOutpoints) {
|
||||
shouldHide = blackListedOutpoints.some(
|
||||
(outpoint) =>
|
||||
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
|
||||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
||||
);
|
||||
}
|
||||
// We're checking to see if the stream outpoint
|
||||
// or signing channel outpoint is in the filter list
|
||||
if (claim && !claimIsMine && !shouldHide && filteredOutpoints) {
|
||||
shouldHide = filteredOutpoints.some(
|
||||
(outpoint) =>
|
||||
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
|
||||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
||||
);
|
||||
if (!shouldHide && !claimIsMine && (banState.blacklisted || banState.filtered)) {
|
||||
shouldHide = true;
|
||||
}
|
||||
|
||||
// block stream claims
|
||||
if (claim && !shouldHide && !showUserBlocked && mutedUris.length && signingChannel) {
|
||||
shouldHide = mutedUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url));
|
||||
}
|
||||
if (claim && !shouldHide && !showUserBlocked && blockedUris.length && signingChannel) {
|
||||
shouldHide = blockedUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url));
|
||||
if (!shouldHide && !showUserBlocked && (banState.muted || banState.blocked)) {
|
||||
shouldHide = true;
|
||||
}
|
||||
|
||||
if (!shouldHide && customShouldHide && claim) {
|
||||
|
@ -482,7 +454,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{isChannelUri && !channelIsBlocked && !claimIsMine && (
|
||||
{isChannelUri && !banState.muted && !claimIsMine && (
|
||||
<SubscribeButton
|
||||
uri={repostedChannelUri || (uri.startsWith('lbry://') ? uri : `lbry://${uri}`)}
|
||||
/>
|
||||
|
|
|
@ -11,8 +11,7 @@ import {
|
|||
} from 'redux/selectors/claims';
|
||||
import { doFileGet } from 'redux/actions/file';
|
||||
import { doResolveUri } from 'redux/actions/claims';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { makeSelectViewCountForUri, selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
import { makeSelectViewCountForUri, selectBanStateForUri } from 'lbryinc';
|
||||
import { makeSelectIsActiveLivestream } from 'redux/selectors/livestream';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import ClaimPreviewTile from './view';
|
||||
|
@ -31,9 +30,7 @@ const select = (state, props) => {
|
|||
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
|
||||
thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state),
|
||||
title: props.uri && makeSelectTitleForUri(props.uri)(state),
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
blockedChannelUris: selectMutedChannels(state),
|
||||
banState: selectBanStateForUri(state, props.uri),
|
||||
showMature: selectShowMatureContent(state),
|
||||
isMature: makeSelectClaimIsNsfw(props.uri)(state),
|
||||
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
|
|
|
@ -12,7 +12,7 @@ import SubscribeButton from 'component/subscribeButton';
|
|||
import useGetThumbnail from 'effects/use-get-thumbnail';
|
||||
import { formatLbryUrlForWeb, generateListSearchUrlParams } from 'util/url';
|
||||
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
|
||||
import { parseURI, isURIEqual } from 'util/lbryURI';
|
||||
import { parseURI } from 'util/lbryURI';
|
||||
import PreviewOverlayProperties from 'component/previewOverlayProperties';
|
||||
import FileDownloadLink from 'component/fileDownloadLink';
|
||||
import FileWatchLaterLink from 'component/fileWatchLaterLink';
|
||||
|
@ -33,15 +33,7 @@ type Props = {
|
|||
thumbnail: string,
|
||||
title: string,
|
||||
placeholder: boolean,
|
||||
blackListedOutpoints: Array<{
|
||||
txid: string,
|
||||
nout: number,
|
||||
}>,
|
||||
filteredOutpoints: Array<{
|
||||
txid: string,
|
||||
nout: number,
|
||||
}>,
|
||||
blockedChannelUris: Array<string>,
|
||||
banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean },
|
||||
getFile: (string) => void,
|
||||
streamingUrl: string,
|
||||
isMature: boolean,
|
||||
|
@ -67,11 +59,9 @@ function ClaimPreviewTile(props: Props) {
|
|||
resolveUri,
|
||||
claim,
|
||||
placeholder,
|
||||
blackListedOutpoints,
|
||||
filteredOutpoints,
|
||||
banState,
|
||||
getFile,
|
||||
streamingUrl,
|
||||
blockedChannelUris,
|
||||
isMature,
|
||||
showMature,
|
||||
showHiddenByUser,
|
||||
|
@ -145,34 +135,9 @@ function ClaimPreviewTile(props: Props) {
|
|||
// Unfortunately needed until this is resolved
|
||||
// https://github.com/lbryio/lbry-sdk/issues/2785
|
||||
shouldHide = true;
|
||||
}
|
||||
|
||||
// This will be replaced once blocking is done at the wallet server level
|
||||
if (claim && !shouldHide && blackListedOutpoints) {
|
||||
shouldHide = blackListedOutpoints.some(
|
||||
(outpoint) =>
|
||||
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
|
||||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
||||
);
|
||||
}
|
||||
// We're checking to see if the stream outpoint
|
||||
// or signing channel outpoint is in the filter list
|
||||
if (claim && !shouldHide && filteredOutpoints) {
|
||||
shouldHide = filteredOutpoints.some(
|
||||
(outpoint) =>
|
||||
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
|
||||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
|
||||
);
|
||||
}
|
||||
|
||||
// block stream claims
|
||||
if (claim && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) {
|
||||
shouldHide = blockedChannelUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url));
|
||||
}
|
||||
// block channel claims if we can't control for them in claim search
|
||||
// e.g. fetchRecommendedSubscriptions
|
||||
if (claim && isChannel && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) {
|
||||
shouldHide = blockedChannelUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url));
|
||||
} else {
|
||||
shouldHide =
|
||||
banState.blacklisted || banState.filtered || (!showHiddenByUser && (banState.muted || banState.blocked));
|
||||
}
|
||||
|
||||
if (shouldHide || (isLivestream && !showNoSourceClaims)) {
|
||||
|
@ -290,34 +255,4 @@ function ClaimPreviewTile(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default React.memo<Props>(withRouter(ClaimPreviewTile), areEqual);
|
||||
|
||||
const BLOCKLIST_KEYS = ['blackListedOutpoints', 'filteredOutpoints', 'blockedChannelUris'];
|
||||
const HANDLED_KEYS = [...BLOCKLIST_KEYS, 'date'];
|
||||
|
||||
function areEqual(prev: Props, next: Props) {
|
||||
for (let i = 0; i < BLOCKLIST_KEYS.length; ++i) {
|
||||
const key = BLOCKLIST_KEYS[i];
|
||||
const a = prev[key];
|
||||
const b = next[key];
|
||||
|
||||
if (((!a || !b) && a !== b) || (a && b && a.length !== b.length)) {
|
||||
// The arrays are huge, so just compare the length instead of each entry.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Number(prev.date) !== Number(next.date)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const propKeys = Object.keys(next);
|
||||
for (let i = 0; i < propKeys.length; ++i) {
|
||||
const pk = propKeys[i];
|
||||
if (!HANDLED_KEYS.includes(pk) && prev[pk] !== next[pk]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
export default withRouter(ClaimPreviewTile);
|
||||
|
|
Loading…
Reference in a new issue