Hide comments based on own blocklist #6878

Merged
infinite-persistence merged 2 commits from ip/hide.blocked.comments into master 2021-08-24 10:59:05 +02:00
7 changed files with 95 additions and 175 deletions

View file

@ -35,7 +35,7 @@ declare type CommentsState = {
byId: { [string]: Array<string> }, // ClaimID -> list of fetched comment IDs.
totalCommentsById: {}, // ClaimId -> ultimate total (including replies) in commentron.
repliesByParentId: { [string]: Array<string> }, // ParentCommentID -> list of fetched replies.
totalRepliesByParentId: {}, // ParentCommentID -> total replies in commentron.
repliesTotalPagesByParentId: {}, // ParentCommentID -> total number of reply pages for a parentId in commentron.
topLevelCommentsById: { [string]: Array<string> }, // ClaimID -> list of fetched top level comments.
topLevelTotalPagesById: { [string]: number }, // ClaimID -> total number of top-level pages in commentron. Based on COMMENT_PAGE_SIZE_TOP_LEVEL.
topLevelTotalCommentsById: { [string]: number }, // ClaimID -> total top level comments in commentron.

View file

@ -10,7 +10,11 @@ import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
import { doToast } from 'redux/actions/notifications';
import { doSetPlayingUri } from 'redux/actions/content';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectLinkedCommentAncestors, makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
import {
selectLinkedCommentAncestors,
makeSelectOthersReactionsForComment,
makeSelectTotalReplyPagesForParentId,
} from 'redux/selectors/comments';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { selectPlayingUri } from 'redux/selectors/content';
import Comment from './view';
@ -31,6 +35,7 @@ const select = (state, props) => {
playingUri: selectPlayingUri(state),
stakedLevel: makeSelectStakedLevelForChannelUri(props.authorUri)(state),
linkedCommentAncestors: selectLinkedCommentAncestors(state),
totalReplyPages: makeSelectTotalReplyPagesForParentId(props.commentId)(state),
};
};

View file

@ -41,6 +41,7 @@ type Props = {
commentIsMine: boolean, // if this comment was signed by an owned channel
updateComment: (string, string) => void,
fetchReplies: (string, string, number, number, number) => void,
totalReplyPages: number,
commentModBlock: (string) => void,
linkedCommentId?: string,
linkedCommentAncestors: { [string]: Array<string> },
@ -82,6 +83,7 @@ function Comment(props: Props) {
commentId,
updateComment,
fetchReplies,
totalReplyPages,
linkedCommentId,
linkedCommentAncestors,
commentingEnabled,
@ -417,6 +419,7 @@ function Comment(props: Props) {
linkedCommentId={linkedCommentId}
numDirectReplies={numDirectReplies}
onShowMore={() => setPage(page + 1)}
hasMore={page < totalReplyPages}
/>
)}
</li>

View file

@ -1,16 +1,11 @@
import { connect } from 'react-redux';
import { makeSelectClaimIsMine, selectMyChannelClaims } from 'lbry-redux';
import {
selectIsFetchingCommentsByParentId,
makeSelectRepliesForParentId,
makeSelectTotalRepliesForParentId,
} from 'redux/selectors/comments';
import { selectIsFetchingCommentsByParentId, makeSelectRepliesForParentId } from 'redux/selectors/comments';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import CommentsReplies from './view';
const select = (state, props) => ({
fetchedReplies: makeSelectRepliesForParentId(props.parentId)(state),
totalReplies: makeSelectTotalRepliesForParentId(props.parentId)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
myChannels: selectMyChannelClaims(state),

View file

@ -4,11 +4,9 @@ import React from 'react';
import Comment from 'component/comment';
import Button from 'component/button';
import Spinner from 'component/spinner';
import ChannelThumbnail from 'component/channelThumbnail';
type Props = {
fetchedReplies: Array<any>,
totalReplies: number,
uri: string,
parentId: string,
claimIsMine: boolean,
@ -16,9 +14,10 @@ type Props = {
linkedCommentId?: string,
commentingEnabled: boolean,
threadDepth: number,
numDirectReplies: number,
numDirectReplies: number, // Total replies for parentId as reported by 'comment[replies]'. Includes blocked items.
isFetchingByParentId: { [string]: boolean },
onShowMore?: () => void,
hasMore: boolean,
};
function CommentsReplies(props: Props) {
@ -26,7 +25,6 @@ function CommentsReplies(props: Props) {
uri,
parentId,
fetchedReplies,
totalReplies,
claimIsMine,
myChannels,
linkedCommentId,
@ -35,6 +33,7 @@ function CommentsReplies(props: Props) {
numDirectReplies,
isFetchingByParentId,
onShowMore,
hasMore,
} = props;
const [isExpanded, setExpanded] = React.useState(true);
@ -102,28 +101,11 @@ function CommentsReplies(props: Props) {
/>
);
})}
{!isFetchingByParentId[parentId] && totalReplies < numDirectReplies && (
<li className="comment comment--reply">
<div className="comment__content">
<div className="comment__thumbnail-wrapper">
<ChannelThumbnail xsmall className="comment__author-thumbnail" />
</div>
<div className="comment__body-container comment--blocked">
<div className="comment__meta">
<em>---</em>
</div>
<div>
<em>{__('Comment(s) blocked.')}</em>
</div>
</div>
</div>
</li>
)}
</ul>
</div>
</div>
)}
{isExpanded && fetchedReplies && displayedComments.length < totalReplies && (
{isExpanded && fetchedReplies && hasMore && (
<div className="comment__actions--nested">
<Button button="link" label={__('Show more')} onClick={showMore} className="button--uri-indicator" />
</div>

View file

@ -9,7 +9,7 @@ const defaultState: CommentsState = {
byId: {}, // ClaimID -> list of fetched comment IDs.
totalCommentsById: {}, // ClaimId -> ultimate total (including replies) in commentron.
repliesByParentId: {}, // ParentCommentID -> list of fetched replies.
totalRepliesByParentId: {}, // ParentCommentID -> total replies in commentron.
repliesTotalPagesByParentId: {}, // ParentCommentID -> total number of reply pages for a parentId in commentron.
topLevelCommentsById: {}, // ClaimID -> list of fetched top level comments.
topLevelTotalPagesById: {}, // ClaimID -> total number of top-level pages in commentron. Based on COMMENT_PAGE_SIZE_TOP_LEVEL.
topLevelTotalCommentsById: {}, // ClaimID -> total top level comments in commentron.
@ -79,7 +79,6 @@ export default handleActions(
const totalCommentsById = Object.assign({}, state.totalCommentsById);
const topLevelCommentsById = Object.assign({}, state.topLevelCommentsById); // was byId {ClaimId -> [commentIds...]}
const repliesByParentId = Object.assign({}, state.repliesByParentId); // {ParentCommentID -> [commentIds...] } list of reply comments
const totalRepliesByParentId = Object.assign({}, state.totalRepliesByParentId);
const commentsByUri = Object.assign({}, state.commentsByUri);
const comments = byId[claimId] || [];
const newCommentIds = comments.slice();
@ -104,12 +103,6 @@ export default handleActions(
repliesByParentId[comment.parent_id].unshift(comment.comment_id);
}
if (!totalRepliesByParentId[comment.parent_id]) {
totalRepliesByParentId[comment.parent_id] = 1;
} else {
totalRepliesByParentId[comment.parent_id] += 1;
}
// Update the parent's "replies" value
if (commentById[comment.parent_id]) {
commentById[comment.parent_id].replies = (commentById[comment.parent_id].replies || 0) + 1;
@ -128,7 +121,6 @@ export default handleActions(
...state,
topLevelCommentsById,
repliesByParentId,
totalRepliesByParentId,
commentById,
byId,
totalCommentsById,
@ -262,7 +254,7 @@ export default handleActions(
const repliesByParentId = Object.assign({}, state.repliesByParentId);
const totalCommentsById = Object.assign({}, state.totalCommentsById);
const pinnedCommentsById = Object.assign({}, state.pinnedCommentsById);
const totalRepliesByParentId = Object.assign({}, state.totalRepliesByParentId);
const repliesTotalPagesByParentId = Object.assign({}, state.repliesTotalPagesByParentId);
const isLoadingByParentId = Object.assign({}, state.isLoadingByParentId);
const settingsByChannelId = Object.assign({}, state.settingsByChannelId);
@ -277,7 +269,7 @@ export default handleActions(
if (!disabled) {
if (parentId) {
totalRepliesByParentId[parentId] = totalFilteredItems;
repliesTotalPagesByParentId[parentId] = totalPages;
} else {
totalCommentsById[claimId] = totalItems;
topLevelTotalCommentsById[claimId] = totalFilteredItems;
@ -330,7 +322,7 @@ export default handleActions(
repliesByParentId,
totalCommentsById,
pinnedCommentsById,
totalRepliesByParentId,
repliesTotalPagesByParentId,
byId,
commentById,
commentsByUri,
@ -548,7 +540,6 @@ export default handleActions(
const commentById = Object.assign({}, state.commentById);
const byId = Object.assign({}, state.byId);
const repliesByParentId = Object.assign({}, state.repliesByParentId); // {ParentCommentID -> [commentIds...] } list of reply comments
const totalRepliesByParentId = Object.assign({}, state.totalRepliesByParentId);
const totalCommentsById = Object.assign({}, state.totalCommentsById);
const comment = commentById[comment_id];
@ -571,10 +562,6 @@ export default handleActions(
if (commentById[comment.parent_id]) {
commentById[comment.parent_id].replies = Math.max(0, (commentById[comment.parent_id].replies || 0) - 1);
}
if (totalRepliesByParentId[comment.parent_id]) {
totalRepliesByParentId[comment.parent_id] = Math.max(0, totalRepliesByParentId[comment.parent_id] - 1);
}
}
}
@ -590,7 +577,6 @@ export default handleActions(
byId,
totalCommentsById,
repliesByParentId,
totalRepliesByParentId,
isLoading: false,
};
},

View file

@ -204,18 +204,82 @@ export const selectFetchingBlockedWords = createSelector(selectState, (state) =>
export const makeSelectCommentsForUri = (uri: string) =>
createSelector(
(state) => state,
selectCommentsByClaimId,
selectCommentsByUri,
(state, byClaimId, byUri) => {
const claimId = byUri[uri];
const comments = byClaimId && byClaimId[claimId];
return makeSelectFilteredComments(comments)(state);
}
);
export const makeSelectTopLevelCommentsForUri = (uri: string) =>
createSelector(
(state) => state,
selectTopLevelCommentsByClaimId,
selectCommentsByUri,
(state, byClaimId, byUri) => {
const claimId = byUri[uri];
const comments = byClaimId && byClaimId[claimId];
return makeSelectFilteredComments(comments)(state);
}
);
export const makeSelectTopLevelTotalCommentsForUri = (uri: string) =>
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
const claimId = byUri[uri];
return state.topLevelTotalCommentsById[claimId] || 0;
});
export const makeSelectTopLevelTotalPagesForUri = (uri: string) =>
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
const claimId = byUri[uri];
return state.topLevelTotalPagesById[claimId] || 0;
});
export const makeSelectRepliesForParentId = (id: string) =>
createSelector(
(state) => state,
selectCommentsById,
(state, commentsById) => {
// const claimId = byUri[uri]; // just parentId (id)
const replyIdsByParentId = state.comments.repliesByParentId;
const replyIdsForParent = replyIdsByParentId[id] || [];
if (!replyIdsForParent.length) return null;
const comments = [];
replyIdsForParent.forEach((cid) => {
comments.push(commentsById[cid]);
});
// const comments = byParentId && byParentId[id];
return makeSelectFilteredComments(comments)(state);
}
);
const makeSelectFilteredComments = (comments: Array<Comment>) =>
createSelector(
selectClaimsById,
selectMyActiveClaims,
selectMutedChannels,
selectModerationBlockList,
selectAdminBlockList,
selectModeratorBlockList,
selectBlacklistedOutpointMap,
selectFilteredOutpointMap,
selectShowMatureContent,
(byClaimId, byUri, claimsById, myClaims, blockedChannels, blacklistedMap, filteredMap, showMatureContent) => {
const claimId = byUri[uri];
const comments = byClaimId && byClaimId[claimId];
(
claimsById,
myClaims,
mutedChannels,
personalBlockList,
adminBlockList,
moderatorBlockList,
blacklistedMap,
filteredMap,
showMatureContent
) => {
return comments
? comments.filter((comment) => {
if (!comment) {
@ -247,135 +311,20 @@ export const makeSelectCommentsForUri = (uri: string) =>
}
}
return !blockedChannels.includes(comment.channel_url);
return !(
mutedChannels.includes(comment.channel_url) ||
personalBlockList.includes(comment.channel_url) ||
adminBlockList.includes(comment.channel_url) ||
moderatorBlockList.includes(comment.channel_url)
);
})
: [];
}
);
export const makeSelectTopLevelCommentsForUri = (uri: string) =>
createSelector(
selectTopLevelCommentsByClaimId,
selectCommentsByUri,
selectClaimsById,
selectMyActiveClaims,
selectMutedChannels,
selectBlacklistedOutpointMap,
selectFilteredOutpointMap,
selectShowMatureContent,
(byClaimId, byUri, claimsById, myClaims, blockedChannels, blacklistedMap, filteredMap, showMatureContent) => {
const claimId = byUri[uri];
const comments = byClaimId && byClaimId[claimId];
return comments
? comments.filter((comment) => {
if (!comment) {
return false;
}
const channelClaim = claimsById[comment.channel_id];
// Return comment if `channelClaim` doesn't exist so the component knows to resolve the author
if (channelClaim) {
if (myClaims && myClaims.size > 0) {
const claimIsMine = channelClaim.is_my_output || myClaims.has(channelClaim.claim_id);
if (claimIsMine) {
return true;
}
}
const outpoint = `${channelClaim.txid}:${channelClaim.nout}`;
if (blacklistedMap[outpoint] || filteredMap[outpoint]) {
return false;
}
if (!showMatureContent) {
const claimIsMature = isClaimNsfw(channelClaim);
if (claimIsMature) {
return false;
}
}
}
return !blockedChannels.includes(comment.channel_url);
})
: [];
}
);
export const makeSelectTopLevelTotalCommentsForUri = (uri: string) =>
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
const claimId = byUri[uri];
return state.topLevelTotalCommentsById[claimId] || 0;
});
export const makeSelectTopLevelTotalPagesForUri = (uri: string) =>
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
const claimId = byUri[uri];
return state.topLevelTotalPagesById[claimId] || 0;
});
export const makeSelectRepliesForParentId = (id: string) =>
createSelector(
selectState, // no selectRepliesByParentId
selectCommentsById,
selectClaimsById,
selectMyActiveClaims,
selectMutedChannels,
selectBlacklistedOutpointMap,
selectFilteredOutpointMap,
selectShowMatureContent,
(state, commentsById, claimsById, myClaims, blockedChannels, blacklistedMap, filteredMap, showMatureContent) => {
// const claimId = byUri[uri]; // just parentId (id)
const replyIdsByParentId = state.repliesByParentId;
const replyIdsForParent = replyIdsByParentId[id] || [];
if (!replyIdsForParent.length) return null;
const comments = [];
replyIdsForParent.forEach((cid) => {
comments.push(commentsById[cid]);
});
// const comments = byParentId && byParentId[id];
return comments
? comments.filter((comment) => {
if (!comment) {
return false;
}
const channelClaim = claimsById[comment.channel_id];
// Return comment if `channelClaim` doesn't exist so the component knows to resolve the author
if (channelClaim) {
if (myClaims && myClaims.size > 0) {
const claimIsMine = channelClaim.is_my_output || myClaims.has(channelClaim.claim_id);
if (claimIsMine) {
return true;
}
}
const outpoint = `${channelClaim.txid}:${channelClaim.nout}`;
if (blacklistedMap[outpoint] || filteredMap[outpoint]) {
return false;
}
if (!showMatureContent) {
const claimIsMature = isClaimNsfw(channelClaim);
if (claimIsMature) {
return false;
}
}
}
return !blockedChannels.includes(comment.channel_url);
})
: [];
}
);
export const makeSelectTotalRepliesForParentId = (parentId: string) =>
export const makeSelectTotalReplyPagesForParentId = (parentId: string) =>
createSelector(selectState, (state) => {
return state.totalRepliesByParentId[parentId] || 0;
return state.repliesTotalPagesByParentId[parentId] || 0;
});
export const makeSelectTotalCommentsCountForUri = (uri: string) =>