Properly handle blacklisted claims. (#7665)
* Properly handle blacklisted claims. * Identify blacklist cause and display the proper message.
This commit is contained in:
parent
b92fb03856
commit
6b1069f02a
7 changed files with 142 additions and 47 deletions
|
@ -5,7 +5,7 @@
|
||||||
// involve moving it from 'extras' to 'ui' (big change).
|
// involve moving it from 'extras' to 'ui' (big change).
|
||||||
|
|
||||||
import { createCachedSelector } from 're-reselect';
|
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 { selectMutedChannels } from 'redux/selectors/blocked';
|
||||||
import { selectModerationBlockList } from 'redux/selectors/comments';
|
import { selectModerationBlockList } from 'redux/selectors/comments';
|
||||||
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
||||||
|
@ -18,7 +18,8 @@ export const selectBanStateForUri = createCachedSelector(
|
||||||
selectFilteredOutpointMap,
|
selectFilteredOutpointMap,
|
||||||
selectMutedChannels,
|
selectMutedChannels,
|
||||||
selectModerationBlockList,
|
selectModerationBlockList,
|
||||||
(claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist) => {
|
(state, uri) => makeSelectIsBlacklisted(uri)(state),
|
||||||
|
(claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist, isBlacklisted) => {
|
||||||
const banState = {};
|
const banState = {};
|
||||||
|
|
||||||
if (!claim) {
|
if (!claim) {
|
||||||
|
@ -27,6 +28,10 @@ export const selectBanStateForUri = createCachedSelector(
|
||||||
|
|
||||||
const channelClaim = getChannelFromClaim(claim);
|
const channelClaim = getChannelFromClaim(claim);
|
||||||
|
|
||||||
|
if (isBlacklisted) {
|
||||||
|
banState['blacklisted'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// This will be replaced once blocking is done at the wallet server level.
|
// This will be replaced once blocking is done at the wallet server level.
|
||||||
if (blackListedOutpointMap) {
|
if (blackListedOutpointMap) {
|
||||||
if (
|
if (
|
||||||
|
|
37
flow-typed/Claim.js
vendored
37
flow-typed/Claim.js
vendored
|
@ -145,12 +145,49 @@ declare type PurchaseReceipt = {
|
||||||
type: 'purchase',
|
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 = {
|
declare type ClaimActionResolveInfo = {
|
||||||
[string]: {
|
[string]: {
|
||||||
stream: ?StreamClaim,
|
stream: ?StreamClaim,
|
||||||
channel: ?ChannelClaim,
|
channel: ?ChannelClaim,
|
||||||
claimsInChannel: ?number,
|
claimsInChannel: ?number,
|
||||||
collection: ?CollectionClaim,
|
collection: ?CollectionClaim,
|
||||||
|
errorCensor: ?ClaimErrorCensor,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ import {
|
||||||
selectTitleForUri,
|
selectTitleForUri,
|
||||||
selectClaimIsMine,
|
selectClaimIsMine,
|
||||||
makeSelectClaimIsPending,
|
makeSelectClaimIsPending,
|
||||||
|
makeSelectIsBlacklisted,
|
||||||
|
makeSelectBlacklistedDueToDMCA,
|
||||||
|
makeSelectClaimErrorCensor,
|
||||||
} from 'redux/selectors/claims';
|
} from 'redux/selectors/claims';
|
||||||
import {
|
import {
|
||||||
makeSelectCollectionForId,
|
makeSelectCollectionForId,
|
||||||
|
@ -23,7 +26,6 @@ import { normalizeURI } from 'util/lbryURI';
|
||||||
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
import * as COLLECTIONS_CONSTS from 'constants/collections';
|
||||||
import { push } from 'connected-react-router';
|
import { push } from 'connected-react-router';
|
||||||
import { makeSelectChannelInSubscriptions } from 'redux/selectors/subscriptions';
|
import { makeSelectChannelInSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { selectBlackListedOutpoints } from 'lbryinc';
|
|
||||||
import ShowPage from './view';
|
import ShowPage from './view';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
|
@ -72,7 +74,6 @@ const select = (state, props) => {
|
||||||
uri,
|
uri,
|
||||||
claim,
|
claim,
|
||||||
isResolvingUri: selectIsUriResolving(state, uri),
|
isResolvingUri: selectIsUriResolving(state, uri),
|
||||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
|
||||||
totalPages: makeSelectTotalPagesForChannel(uri, PAGE_SIZE)(state),
|
totalPages: makeSelectTotalPagesForChannel(uri, PAGE_SIZE)(state),
|
||||||
isSubscribed: makeSelectChannelInSubscriptions(uri)(state),
|
isSubscribed: makeSelectChannelInSubscriptions(uri)(state),
|
||||||
title: selectTitleForUri(state, uri),
|
title: selectTitleForUri(state, uri),
|
||||||
|
@ -82,6 +83,9 @@ const select = (state, props) => {
|
||||||
collectionId: collectionId,
|
collectionId: collectionId,
|
||||||
collectionUrls: makeSelectUrlsForCollectionId(collectionId)(state),
|
collectionUrls: makeSelectUrlsForCollectionId(collectionId)(state),
|
||||||
isResolvingCollection: makeSelectIsResolvingCollectionForId(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,
|
uri: string,
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
location: UrlLocation,
|
location: UrlLocation,
|
||||||
blackListedOutpoints: Array<{
|
|
||||||
txid: string,
|
|
||||||
nout: number,
|
|
||||||
}>,
|
|
||||||
title: string,
|
title: string,
|
||||||
claimIsMine: boolean,
|
claimIsMine: boolean,
|
||||||
claimIsPending: boolean,
|
claimIsPending: boolean,
|
||||||
|
@ -35,6 +31,9 @@ type Props = {
|
||||||
collectionUrls: Array<string>,
|
collectionUrls: Array<string>,
|
||||||
isResolvingCollection: boolean,
|
isResolvingCollection: boolean,
|
||||||
fetchCollectionItems: (string) => void,
|
fetchCollectionItems: (string) => void,
|
||||||
|
isBlacklisted: boolean,
|
||||||
|
isBlacklistedDueToDMCA: boolean,
|
||||||
|
errorCensor: ?ClaimErrorCensor,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ShowPage(props: Props) {
|
function ShowPage(props: Props) {
|
||||||
|
@ -43,7 +42,6 @@ function ShowPage(props: Props) {
|
||||||
resolveUri,
|
resolveUri,
|
||||||
uri,
|
uri,
|
||||||
claim,
|
claim,
|
||||||
blackListedOutpoints,
|
|
||||||
location,
|
location,
|
||||||
claimIsMine,
|
claimIsMine,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
|
@ -54,11 +52,13 @@ function ShowPage(props: Props) {
|
||||||
collection,
|
collection,
|
||||||
collectionUrls,
|
collectionUrls,
|
||||||
isResolvingCollection,
|
isResolvingCollection,
|
||||||
|
isBlacklisted,
|
||||||
|
isBlacklistedDueToDMCA,
|
||||||
|
errorCensor,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
|
|
||||||
const signingChannel = claim && claim.signing_channel;
|
|
||||||
const canonicalUrl = claim && claim.canonical_url;
|
const canonicalUrl = claim && claim.canonical_url;
|
||||||
const claimExists = claim !== null && claim !== undefined;
|
const claimExists = claim !== null && claim !== undefined;
|
||||||
const haventFetchedYet = claim === undefined;
|
const haventFetchedYet = claim === undefined;
|
||||||
|
@ -103,7 +103,7 @@ function ShowPage(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let innerContent = '';
|
let innerContent = '';
|
||||||
if (!claim || (claim && !claim.name)) {
|
if ((!claim || (claim && !claim.name)) && !isBlacklisted) {
|
||||||
innerContent = (
|
innerContent = (
|
||||||
<Page>
|
<Page>
|
||||||
{(claim === undefined ||
|
{(claim === undefined ||
|
||||||
|
@ -142,38 +142,42 @@ function ShowPage(props: Props) {
|
||||||
{!isResolvingUri && isSubscribed && claim === null && <AbandonedChannelPreview uri={uri} type={'large'} />}
|
{!isResolvingUri && isSubscribed && claim === null && <AbandonedChannelPreview uri={uri} type={'large'} />}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
} else if (claim.name.length && claim.name[0] === '@') {
|
} else if (claim && claim.name.length && claim.name[0] === '@') {
|
||||||
innerContent = <ChannelPage uri={uri} location={location} />;
|
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) {
|
} else if (claim) {
|
||||||
let isClaimBlackListed = false;
|
innerContent = <FilePage uri={uri} location={location} />;
|
||||||
|
|
||||||
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} />;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <React.Fragment>{innerContent}</React.Fragment>;
|
return <React.Fragment>{innerContent}</React.Fragment>;
|
||||||
|
|
|
@ -87,7 +87,7 @@ export function doResolveUris(
|
||||||
if (uriResolveInfo) {
|
if (uriResolveInfo) {
|
||||||
if (uriResolveInfo.error) {
|
if (uriResolveInfo.error) {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
resolveInfo[uri] = { ...fallbackResolveInfo };
|
resolveInfo[uri] = { ...fallbackResolveInfo, errorCensor: uriResolveInfo.error.censor };
|
||||||
} else {
|
} else {
|
||||||
if (checkReposts) {
|
if (checkReposts) {
|
||||||
if (uriResolveInfo.reposted_claim) {
|
if (uriResolveInfo.reposted_claim) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ type State = {
|
||||||
createCollectionError: ?string,
|
createCollectionError: ?string,
|
||||||
channelClaimCounts: { [string]: number },
|
channelClaimCounts: { [string]: number },
|
||||||
claimsByUri: { [string]: string },
|
claimsByUri: { [string]: string },
|
||||||
|
blacklistedByUri: { [string]: ClaimErrorCensor },
|
||||||
byId: { [string]: Claim },
|
byId: { [string]: Claim },
|
||||||
pendingById: { [string]: Claim }, // keep pending claims
|
pendingById: { [string]: Claim }, // keep pending claims
|
||||||
resolvingUris: Array<string>,
|
resolvingUris: Array<string>,
|
||||||
|
@ -67,6 +68,7 @@ type State = {
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
|
blacklistedByUri: {},
|
||||||
byId: {},
|
byId: {},
|
||||||
claimsByUri: {},
|
claimsByUri: {},
|
||||||
paginatedClaimsByChannel: {},
|
paginatedClaimsByChannel: {},
|
||||||
|
@ -118,6 +120,7 @@ const defaultState = {
|
||||||
function handleClaimAction(state: State, action: any): State {
|
function handleClaimAction(state: State, action: any): State {
|
||||||
const { resolveInfo }: ClaimActionResolveInfo = action.data;
|
const { resolveInfo }: ClaimActionResolveInfo = action.data;
|
||||||
|
|
||||||
|
const blacklistedByUri = Object.assign({}, state.blacklistedByUri);
|
||||||
const byUri = Object.assign({}, state.claimsByUri);
|
const byUri = Object.assign({}, state.claimsByUri);
|
||||||
const byId = Object.assign({}, state.byId);
|
const byId = Object.assign({}, state.byId);
|
||||||
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
||||||
|
@ -127,9 +130,11 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
|
|
||||||
Object.entries(resolveInfo).forEach(([url, resolveResponse]) => {
|
Object.entries(resolveInfo).forEach(([url, resolveResponse]) => {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const { claimsInChannel, stream, channel: channelFromResolve, collection } = resolveResponse;
|
const { claimsInChannel, stream, channel: channelFromResolve, collection, errorCensor } = resolveResponse;
|
||||||
const channel = channelFromResolve || (stream && stream.signing_channel);
|
const channel = channelFromResolve || (stream && stream.signing_channel);
|
||||||
|
|
||||||
|
blacklistedByUri[url] = errorCensor;
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
if (pendingById[stream.claim_id]) {
|
if (pendingById[stream.claim_id]) {
|
||||||
byId[stream.claim_id] = mergeClaim(stream, byId[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, {
|
return Object.assign({}, state, {
|
||||||
|
blacklistedByUri,
|
||||||
byId,
|
byId,
|
||||||
claimsByUri: byUri,
|
claimsByUri: byUri,
|
||||||
channelClaimCounts,
|
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 => {
|
reducers[ACTIONS.UPDATE_CONFIRMED_CLAIMS] = (state: State, action: any): State => {
|
||||||
const {
|
const { claims: confirmedClaims, pending: pendingClaims }: { claims: Array<Claim>, pending: { [string]: Claim } } =
|
||||||
claims: confirmedClaims,
|
action.data;
|
||||||
pending: pendingClaims,
|
|
||||||
}: { claims: Array<Claim>, pending: { [string]: Claim } } = action.data;
|
|
||||||
const byId = Object.assign({}, state.byId);
|
const byId = Object.assign({}, state.byId);
|
||||||
const byUri = Object.assign({}, state.claimsByUri);
|
const byUri = Object.assign({}, state.claimsByUri);
|
||||||
//
|
//
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { createCachedSelector } from 're-reselect';
|
import { createCachedSelector } from 're-reselect';
|
||||||
import { isClaimNsfw, filterClaims } from 'util/claim';
|
import { isClaimNsfw, filterClaims } from 'util/claim';
|
||||||
|
import { selectBlackListedOutpoints } from 'lbryinc';
|
||||||
import * as CLAIM from 'constants/claim';
|
import * as CLAIM from 'constants/claim';
|
||||||
|
|
||||||
type State = { claims: any };
|
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 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 makeSelectClaimForClaimId = (claimId: string) => createSelector(selectClaimsById, (byId) => byId[claimId]);
|
||||||
|
|
||||||
export const selectClaimForUri = createCachedSelector(
|
export const selectClaimForUri = createCachedSelector(
|
||||||
|
|
Loading…
Reference in a new issue