Comments: simplify blocked replies display

Previously, we decide when to display "Show More" based on the current fetched reply count vs. total replies in Commentron. It was already troublesome as `comment.List` and `comment.replies` give different values (one includes blocked content, while another does not).

Now, we are further filtering the list with Commentron blocklists (personal, admin, moderator), so it is not feasible to run the logic based on reply count.

## Solution
- Keep track of number of remaining pages instead and use that to determine when to display "Show More".
  - While it doesn't solve the "Show N replies" mismatch (YT has this problem too), it prevents the button from lingering.
- In the event that all replies are blocked, just show an empty space (same as YT). I didn't like the previous version that cluttered the space with "comment(s) blocked".
This commit is contained in:
infinite-persistence 2021-08-04 23:01:31 +08:00
parent e899a5de65
commit 74986a8b3a
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
7 changed files with 21 additions and 50 deletions

View file

@ -35,7 +35,7 @@ declare type CommentsState = {
byId: { [string]: Array<string> }, // ClaimID -> list of fetched comment IDs. byId: { [string]: Array<string> }, // ClaimID -> list of fetched comment IDs.
totalCommentsById: {}, // ClaimId -> ultimate total (including replies) in commentron. totalCommentsById: {}, // ClaimId -> ultimate total (including replies) in commentron.
repliesByParentId: { [string]: Array<string> }, // ParentCommentID -> list of fetched replies. 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. 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. 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. 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 { doToast } from 'redux/actions/notifications';
import { doSetPlayingUri } from 'redux/actions/content'; import { doSetPlayingUri } from 'redux/actions/content';
import { selectUserVerifiedEmail } from 'redux/selectors/user'; 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 { selectActiveChannelClaim } from 'redux/selectors/app';
import { selectPlayingUri } from 'redux/selectors/content'; import { selectPlayingUri } from 'redux/selectors/content';
import Comment from './view'; import Comment from './view';
@ -31,6 +35,7 @@ const select = (state, props) => {
playingUri: selectPlayingUri(state), playingUri: selectPlayingUri(state),
stakedLevel: makeSelectStakedLevelForChannelUri(props.authorUri)(state), stakedLevel: makeSelectStakedLevelForChannelUri(props.authorUri)(state),
linkedCommentAncestors: selectLinkedCommentAncestors(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 commentIsMine: boolean, // if this comment was signed by an owned channel
updateComment: (string, string) => void, updateComment: (string, string) => void,
fetchReplies: (string, string, number, number, number) => void, fetchReplies: (string, string, number, number, number) => void,
totalReplyPages: number,
commentModBlock: (string) => void, commentModBlock: (string) => void,
linkedCommentId?: string, linkedCommentId?: string,
linkedCommentAncestors: { [string]: Array<string> }, linkedCommentAncestors: { [string]: Array<string> },
@ -82,6 +83,7 @@ function Comment(props: Props) {
commentId, commentId,
updateComment, updateComment,
fetchReplies, fetchReplies,
totalReplyPages,
linkedCommentId, linkedCommentId,
linkedCommentAncestors, linkedCommentAncestors,
commentingEnabled, commentingEnabled,
@ -417,6 +419,7 @@ function Comment(props: Props) {
linkedCommentId={linkedCommentId} linkedCommentId={linkedCommentId}
numDirectReplies={numDirectReplies} numDirectReplies={numDirectReplies}
onShowMore={() => setPage(page + 1)} onShowMore={() => setPage(page + 1)}
hasMore={page < totalReplyPages}
/> />
)} )}
</li> </li>

View file

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

View file

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

View file

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

View file

@ -322,9 +322,9 @@ const makeSelectFilteredComments = (comments: Array<Comment>) =>
} }
); );
export const makeSelectTotalRepliesForParentId = (parentId: string) => export const makeSelectTotalReplyPagesForParentId = (parentId: string) =>
createSelector(selectState, (state) => { createSelector(selectState, (state) => {
return state.totalRepliesByParentId[parentId] || 0; return state.repliesTotalPagesByParentId[parentId] || 0;
}); });
export const makeSelectTotalCommentsCountForUri = (uri: string) => export const makeSelectTotalCommentsCountForUri = (uri: string) =>