Properly handle blacklisted claims. (#7665)

* Properly handle blacklisted claims.

* Identify blacklist cause and display the proper message.
This commit is contained in:
Franco Montenegro 2022-08-09 11:19:23 -04:00 committed by GitHub
parent b92fb03856
commit 6b1069f02a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 142 additions and 47 deletions

View file

@ -5,7 +5,7 @@
// involve moving it from 'extras' to 'ui' (big change).
import { createCachedSelector } from 're-reselect';
import { selectClaimForUri } from 'redux/selectors/claims';
import { selectClaimForUri, makeSelectIsBlacklisted } from 'redux/selectors/claims';
import { selectMutedChannels } from 'redux/selectors/blocked';
import { selectModerationBlockList } from 'redux/selectors/comments';
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
@ -18,7 +18,8 @@ export const selectBanStateForUri = createCachedSelector(
selectFilteredOutpointMap,
selectMutedChannels,
selectModerationBlockList,
(claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist) => {
(state, uri) => makeSelectIsBlacklisted(uri)(state),
(claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist, isBlacklisted) => {
const banState = {};
if (!claim) {
@ -27,6 +28,10 @@ export const selectBanStateForUri = createCachedSelector(
const channelClaim = getChannelFromClaim(claim);
if (isBlacklisted) {
banState['blacklisted'] = true;
}
// This will be replaced once blocking is done at the wallet server level.
if (blackListedOutpointMap) {
if (

37
flow-typed/Claim.js vendored
View file

@ -145,12 +145,49 @@ declare type PurchaseReceipt = {
type: 'purchase',
};
declare type ClaimErrorCensor = {
address: string,
amount: string,
canonical_url: string,
claim_id: string,
claim_op: string,
confirmations: number,
has_signing_key: boolean,
height: number,
meta: {
activation_height: number,
claims_in_channel: number,
creation_height: number,
creation_timestamp: number,
effective_amount: string,
expiration_height: number,
is_controlling: boolean,
reposted: number,
support_amount: string,
take_over_height: number,
},
name: string,
normalized_name: string,
nout: number,
permanent_url: string,
short_url: string,
timestamp: number,
txid: string,
type: string,
value: {
public_key: string,
public_key_id: string,
},
value_type: string,
}
declare type ClaimActionResolveInfo = {
[string]: {
stream: ?StreamClaim,
channel: ?ChannelClaim,
claimsInChannel: ?number,
collection: ?CollectionClaim,
errorCensor: ?ClaimErrorCensor,
},
}

View file

@ -10,6 +10,9 @@ import {
selectTitleForUri,
selectClaimIsMine,
makeSelectClaimIsPending,
makeSelectIsBlacklisted,
makeSelectBlacklistedDueToDMCA,
makeSelectClaimErrorCensor,
} from 'redux/selectors/claims';
import {
makeSelectCollectionForId,
@ -23,7 +26,6 @@ import { normalizeURI } from 'util/lbryURI';
import * as COLLECTIONS_CONSTS from 'constants/collections';
import { push } from 'connected-react-router';
import { makeSelectChannelInSubscriptions } from 'redux/selectors/subscriptions';
import { selectBlackListedOutpoints } from 'lbryinc';
import ShowPage from './view';
const select = (state, props) => {
@ -72,7 +74,6 @@ const select = (state, props) => {
uri,
claim,
isResolvingUri: selectIsUriResolving(state, uri),
blackListedOutpoints: selectBlackListedOutpoints(state),
totalPages: makeSelectTotalPagesForChannel(uri, PAGE_SIZE)(state),
isSubscribed: makeSelectChannelInSubscriptions(uri)(state),
title: selectTitleForUri(state, uri),
@ -82,6 +83,9 @@ const select = (state, props) => {
collectionId: collectionId,
collectionUrls: makeSelectUrlsForCollectionId(collectionId)(state),
isResolvingCollection: makeSelectIsResolvingCollectionForId(collectionId)(state),
isBlacklisted: makeSelectIsBlacklisted(uri)(state),
isBlacklistedDueToDMCA: makeSelectBlacklistedDueToDMCA(uri)(state),
errorCensor: makeSelectClaimErrorCensor(uri)(state),
};
};

View file

@ -22,10 +22,6 @@ type Props = {
uri: string,
claim: StreamClaim,
location: UrlLocation,
blackListedOutpoints: Array<{
txid: string,
nout: number,
}>,
title: string,
claimIsMine: boolean,
claimIsPending: boolean,
@ -35,6 +31,9 @@ type Props = {
collectionUrls: Array<string>,
isResolvingCollection: boolean,
fetchCollectionItems: (string) => void,
isBlacklisted: boolean,
isBlacklistedDueToDMCA: boolean,
errorCensor: ?ClaimErrorCensor,
};
function ShowPage(props: Props) {
@ -43,7 +42,6 @@ function ShowPage(props: Props) {
resolveUri,
uri,
claim,
blackListedOutpoints,
location,
claimIsMine,
isSubscribed,
@ -54,11 +52,13 @@ function ShowPage(props: Props) {
collection,
collectionUrls,
isResolvingCollection,
isBlacklisted,
isBlacklistedDueToDMCA,
errorCensor,
} = props;
const { search } = location;
const signingChannel = claim && claim.signing_channel;
const canonicalUrl = claim && claim.canonical_url;
const claimExists = claim !== null && claim !== undefined;
const haventFetchedYet = claim === undefined;
@ -103,7 +103,7 @@ function ShowPage(props: Props) {
}
let innerContent = '';
if (!claim || (claim && !claim.name)) {
if ((!claim || (claim && !claim.name)) && !isBlacklisted) {
innerContent = (
<Page>
{(claim === undefined ||
@ -142,38 +142,42 @@ function ShowPage(props: Props) {
{!isResolvingUri && isSubscribed && claim === null && <AbandonedChannelPreview uri={uri} type={'large'} />}
</Page>
);
} else if (claim.name.length && claim.name[0] === '@') {
} else if (claim && claim.name.length && claim.name[0] === '@') {
innerContent = <ChannelPage uri={uri} location={location} />;
} else if (isBlacklisted && !claimIsMine) {
innerContent = isBlacklistedDueToDMCA ? (
<Page>
<Card
title={uri}
subtitle={__(
'In response to a complaint we received under the US Digital Millennium Copyright Act, hub have blocked access to this content from our applications.'
)}
actions={
<div className="section__actions">
<Button button="link" href="https://lbry.com/faq/dmca" label={__('Read More')} />
</div>
}
/>
</Page>
) : (
<Page>
<Card
title={uri}
subtitle={
<>
{__('Your hub has blocked this content because it has subscribed to the following channel:')}{' '}
<Button
button="link"
href={errorCensor && errorCensor.canonical_url}
label={errorCensor && errorCensor.name}
/>
</>
}
/>
</Page>
);
} else if (claim) {
let isClaimBlackListed = false;
isClaimBlackListed =
blackListedOutpoints &&
blackListedOutpoints.some(
(outpoint) =>
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
);
if (isClaimBlackListed && !claimIsMine) {
innerContent = (
<Page>
<Card
title={uri}
subtitle={__(
'In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications.'
)}
actions={
<div className="section__actions">
<Button button="link" href="https://lbry.com/faq/dmca" label={__('Read More')} />
</div>
}
/>
</Page>
);
} else {
innerContent = <FilePage uri={uri} location={location} />;
}
innerContent = <FilePage uri={uri} location={location} />;
}
return <React.Fragment>{innerContent}</React.Fragment>;

View file

@ -87,7 +87,7 @@ export function doResolveUris(
if (uriResolveInfo) {
if (uriResolveInfo.error) {
// $FlowFixMe
resolveInfo[uri] = { ...fallbackResolveInfo };
resolveInfo[uri] = { ...fallbackResolveInfo, errorCensor: uriResolveInfo.error.censor };
} else {
if (checkReposts) {
if (uriResolveInfo.reposted_claim) {

View file

@ -16,6 +16,7 @@ type State = {
createCollectionError: ?string,
channelClaimCounts: { [string]: number },
claimsByUri: { [string]: string },
blacklistedByUri: { [string]: ClaimErrorCensor },
byId: { [string]: Claim },
pendingById: { [string]: Claim }, // keep pending claims
resolvingUris: Array<string>,
@ -67,6 +68,7 @@ type State = {
const reducers = {};
const defaultState = {
blacklistedByUri: {},
byId: {},
claimsByUri: {},
paginatedClaimsByChannel: {},
@ -118,6 +120,7 @@ const defaultState = {
function handleClaimAction(state: State, action: any): State {
const { resolveInfo }: ClaimActionResolveInfo = action.data;
const blacklistedByUri = Object.assign({}, state.blacklistedByUri);
const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId);
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
@ -127,9 +130,11 @@ function handleClaimAction(state: State, action: any): State {
Object.entries(resolveInfo).forEach(([url, resolveResponse]) => {
// $FlowFixMe
const { claimsInChannel, stream, channel: channelFromResolve, collection } = resolveResponse;
const { claimsInChannel, stream, channel: channelFromResolve, collection, errorCensor } = resolveResponse;
const channel = channelFromResolve || (stream && stream.signing_channel);
blacklistedByUri[url] = errorCensor;
if (stream) {
if (pendingById[stream.claim_id]) {
byId[stream.claim_id] = mergeClaim(stream, byId[stream.claim_id]);
@ -197,6 +202,7 @@ function handleClaimAction(state: State, action: any): State {
});
return Object.assign({}, state, {
blacklistedByUri,
byId,
claimsByUri: byUri,
channelClaimCounts,
@ -518,10 +524,8 @@ reducers[ACTIONS.UPDATE_PENDING_CLAIMS] = (state: State, action: any): State =>
};
reducers[ACTIONS.UPDATE_CONFIRMED_CLAIMS] = (state: State, action: any): State => {
const {
claims: confirmedClaims,
pending: pendingClaims,
}: { claims: Array<Claim>, pending: { [string]: Claim } } = action.data;
const { claims: confirmedClaims, pending: pendingClaims }: { claims: Array<Claim>, pending: { [string]: Claim } } =
action.data;
const byId = Object.assign({}, state.byId);
const byUri = Object.assign({}, state.claimsByUri);
//

View file

@ -4,6 +4,7 @@ import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
import { createSelector } from 'reselect';
import { createCachedSelector } from 're-reselect';
import { isClaimNsfw, filterClaims } from 'util/claim';
import { selectBlackListedOutpoints } from 'lbryinc';
import * as CLAIM from 'constants/claim';
type State = { claims: any };
@ -83,6 +84,46 @@ export const selectClaimIdForUri = (state: State, uri: string) => selectClaimIds
export const selectReflectingById = (state: State) => selectState(state).reflectingById;
export const makeSelectBlacklistedDueToDMCA = (claimUri: string) =>
createSelector(makeSelectClaimErrorCensor(claimUri), (claimError) => {
if (!claimError) {
return false;
}
return claimError.name === '@LBRY-DMCA';
});
export const makeSelectClaimErrorCensor = (claimUri: string) =>
createSelector(selectState, (state) => state.blacklistedByUri[claimUri]);
export const makeSelectIsBlacklisted = (claimUri: string) =>
createSelector(
makeSelectClaimErrorCensor(claimUri),
selectBlackListedOutpoints,
makeSelectClaimForUri(claimUri),
(errorCensor, legacyBlacklistedList, claim) => {
if (errorCensor) {
return true;
}
// Fallback to legacy just in case.
if (!claim) {
return false;
}
if (!legacyBlacklistedList) {
return false;
}
const signingChannel = claim.signing_channel;
if (!signingChannel) {
return false;
}
const isInLegacyBlacklist = legacyBlacklistedList.some(
(outpoint) =>
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
);
return isInLegacyBlacklist;
}
);
export const makeSelectClaimForClaimId = (claimId: string) => createSelector(selectClaimsById, (byId) => byId[claimId]);
export const selectClaimForUri = createCachedSelector(