Properly handle blacklisted claims. #7665
7 changed files with 142 additions and 47 deletions
|
@ -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
37
flow-typed/Claim.js
vendored
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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,26 +142,15 @@ 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 (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 = (
|
||||
} 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, we have blocked access to this content from our applications.'
|
||||
'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">
|
||||
|
@ -170,10 +159,25 @@ function ShowPage(props: Props) {
|
|||
}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
} else {
|
||||
innerContent = <FilePage uri={uri} location={location} />;
|
||||
) : (
|
||||
<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) {
|
||||
innerContent = <FilePage uri={uri} location={location} />;
|
||||
}
|
||||
|
||||
return <React.Fragment>{innerContent}</React.Fragment>;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
//
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue