Cleanup Comment and CommentsList and fix new pages

- Fixes new pages not fetching at all inside a drawer component
- Fixes fetching multiple pages at once some times
This commit is contained in:
Rafael 2022-02-09 12:27:11 -03:00 committed by Thomas Zarebczan
parent f9a1fcc6a7
commit aaf36e88dd
7 changed files with 118 additions and 134 deletions

View file

@ -4,11 +4,12 @@ import {
makeSelectClaimForUri,
selectThumbnailForUri,
selectHasChannels,
selectMyClaimIdsRaw,
} from 'redux/selectors/claims';
import { doCommentUpdate, doCommentList } from 'redux/actions/comments';
import { makeSelectChannelIsMuted } from 'redux/selectors/blocked';
import { doToast } from 'redux/actions/notifications';
import { doSetPlayingUri } from 'redux/actions/content';
import { doClearPlayingUri } from 'redux/actions/content';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import {
selectLinkedCommentAncestors,
@ -20,31 +21,34 @@ import { selectPlayingUri } from 'redux/selectors/content';
import Comment from './view';
const select = (state, props) => {
const { comment, uri } = props;
const { comment_id, author_uri } = comment || {};
const activeChannelClaim = selectActiveChannelClaim(state);
const activeChannelId = activeChannelClaim && activeChannelClaim.claim_id;
const reactionKey = activeChannelId ? `${props.commentId}:${activeChannelId}` : props.commentId;
const reactionKey = activeChannelId ? `${comment_id}:${activeChannelId}` : comment_id;
return {
claim: makeSelectClaimForUri(props.uri)(state),
thumbnail: props.authorUri && selectThumbnailForUri(state, props.authorUri),
channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state),
myChannelIds: selectMyClaimIdsRaw(state),
claim: makeSelectClaimForUri(uri)(state),
thumbnail: author_uri && selectThumbnailForUri(state, author_uri),
channelIsBlocked: author_uri && makeSelectChannelIsMuted(author_uri)(state),
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
othersReacts: selectOthersReactsForComment(state, reactionKey),
activeChannelClaim,
hasChannels: selectHasChannels(state),
playingUri: selectPlayingUri(state),
stakedLevel: selectStakedLevelForChannelUri(state, props.authorUri),
stakedLevel: selectStakedLevelForChannelUri(state, author_uri),
linkedCommentAncestors: selectLinkedCommentAncestors(state),
totalReplyPages: makeSelectTotalReplyPagesForParentId(props.commentId)(state),
totalReplyPages: makeSelectTotalReplyPagesForParentId(comment_id)(state),
};
};
const perform = (dispatch) => ({
clearPlayingUri: () => dispatch(doSetPlayingUri({ uri: null })),
updateComment: (commentId, comment) => dispatch(doCommentUpdate(commentId, comment)),
fetchReplies: (uri, parentId, page, pageSize, sortBy) =>
dispatch(doCommentList(uri, parentId, page, pageSize, sortBy)),
doToast: (options) => dispatch(doToast(options)),
});
const perform = {
doClearPlayingUri,
doCommentUpdate,
fetchReplies: doCommentList,
doToast,
};
export default connect(select, perform)(Comment);

View file

@ -34,17 +34,13 @@ import { useIsMobile } from 'effects/use-screensize';
const AUTO_EXPAND_ALL_REPLIES = false;
type Props = {
comment: Comment,
myChannelIds: ?Array<string>,
clearPlayingUri: () => void,
uri: string,
claim: StreamClaim,
author: ?string, // LBRY Channel Name, e.g. @channel
authorUri: string, // full LBRY Channel URI: lbry://@channel#123...
commentId: string, // sha256 digest identifying the comment
message: string, // comment body
timePosted: number, // Comment timestamp
channelIsBlocked: boolean, // if the channel is blacklisted in the app
claimIsMine: boolean, // if you control the claim which this comment was posted on
commentIsMine: boolean, // if this comment was signed by an owned channel
updateComment: (string, string) => void,
fetchReplies: (string, string, number, number, number) => void,
totalReplyPages: number,
@ -57,7 +53,6 @@ type Props = {
isTopLevel?: boolean,
threadDepth: number,
hideActions?: boolean,
isPinned: boolean,
othersReacts: ?{
like: number,
dislike: number,
@ -66,11 +61,6 @@ type Props = {
activeChannelClaim: ?ChannelClaim,
playingUri: ?PlayingUri,
stakedLevel: number,
supportAmount: number,
numDirectReplies: number,
isModerator: boolean,
isGlobalMod: boolean,
isFiat: boolean,
supportDisabled: boolean,
setQuickReply: (any) => void,
quickReply: any,
@ -78,18 +68,14 @@ type Props = {
const LENGTH_TO_COLLAPSE = 300;
function Comment(props: Props) {
function CommentView(props: Props) {
const {
comment,
myChannelIds,
clearPlayingUri,
claim,
uri,
author,
authorUri,
timePosted,
message,
channelIsBlocked,
commentIsMine,
commentId,
updateComment,
fetchReplies,
totalReplyPages,
@ -101,20 +87,32 @@ function Comment(props: Props) {
isTopLevel,
threadDepth,
hideActions,
isPinned,
othersReacts,
playingUri,
stakedLevel,
supportAmount,
numDirectReplies,
isModerator,
isGlobalMod,
isFiat,
supportDisabled,
setQuickReply,
quickReply,
} = props;
const {
channel_url: authorUri,
channel_name: author,
channel_id: channelId,
comment_id: commentId,
comment: message,
is_fiat: isFiat,
is_global_mod: isGlobalMod,
is_moderator: isModerator,
is_pinned: isPinned,
support_amount: supportAmount,
replies: numDirectReplies,
timestamp,
} = comment;
const timePosted = timestamp * 1000;
const commentIsMine = channelId && myChannelIds && myChannelIds.includes(channelId);
const isMobile = useIsMobile();
const {
@ -454,4 +452,4 @@ function Comment(props: Props) {
);
}
export default Comment;
export default CommentView;

View file

@ -5,7 +5,6 @@ import {
makeSelectClaimForUri,
selectClaimIsMine,
selectFetchingMyChannels,
selectMyClaimIdsRaw,
} from 'redux/selectors/claims';
import {
selectTopLevelCommentsForUri,
@ -22,12 +21,17 @@ import {
} from 'redux/selectors/comments';
import { doCommentReset, doCommentList, doCommentById, doCommentReactList } from 'redux/actions/comments';
import { selectActiveChannelClaim } from 'redux/selectors/app';
import { getChannelIdFromClaim } from 'util/claim';
import CommentsList from './view';
const select = (state, props) => {
const claim = selectClaimForUri(state, props.uri);
const { uri } = props;
const claim = selectClaimForUri(state, uri);
const channelId = getChannelIdFromClaim(claim);
const activeChannelClaim = selectActiveChannelClaim(state);
const topLevelComments = selectTopLevelCommentsForUri(state, props.uri);
const topLevelComments = selectTopLevelCommentsForUri(state, uri);
const resolvedComments =
topLevelComments && topLevelComments.length > 0
@ -37,12 +41,12 @@ const select = (state, props) => {
return {
topLevelComments,
resolvedComments,
myChannelIds: selectMyClaimIdsRaw(state),
allCommentIds: selectCommentIdsForUri(state, props.uri),
pinnedComments: selectPinnedCommentsForUri(state, props.uri),
topLevelTotalPages: makeSelectTopLevelTotalPagesForUri(props.uri)(state),
totalComments: makeSelectTotalCommentsCountForUri(props.uri)(state),
claim,
allCommentIds: selectCommentIdsForUri(state, uri),
pinnedComments: selectPinnedCommentsForUri(state, uri),
topLevelTotalPages: makeSelectTopLevelTotalPagesForUri(uri)(state),
totalComments: makeSelectTotalCommentsCountForUri(uri)(state),
claimId: claim && claim.claim_id,
channelId,
claimIsMine: selectClaimIsMine(state, claim),
isFetchingComments: selectIsFetchingComments(state),
isFetchingCommentsById: selectIsFetchingCommentsById(state),
@ -55,12 +59,12 @@ const select = (state, props) => {
};
};
const perform = (dispatch) => ({
fetchTopLevelComments: (uri, page, pageSize, sortBy) => dispatch(doCommentList(uri, '', page, pageSize, sortBy)),
fetchComment: (commentId) => dispatch(doCommentById(commentId)),
fetchReacts: (commentIds) => dispatch(doCommentReactList(commentIds)),
resetComments: (claimId) => dispatch(doCommentReset(claimId)),
doResolveUris: (uris) => dispatch(doResolveUris(uris, true)),
});
const perform = {
fetchTopLevelComments: doCommentList,
fetchComment: doCommentById,
fetchReacts: doCommentReactList,
resetComments: doCommentReset,
doResolveUris,
};
export default connect(select, perform)(CommentsList);

View file

@ -1,7 +1,6 @@
// @flow
import { COMMENT_PAGE_SIZE_TOP_LEVEL, SORT_BY } from 'constants/comment';
import { ENABLE_COMMENT_REACTIONS } from 'config';
import { getChannelIdFromClaim } from 'util/claim';
import { useIsMobile, useIsMediumScreen } from 'effects/use-screensize';
import { getCommentsListTitle } from 'util/comments';
import * as ICONS from 'constants/icons';
@ -34,9 +33,9 @@ type Props = {
resolvedComments: Array<Comment>,
topLevelTotalPages: number,
uri: string,
claim: ?Claim,
claimId?: string,
channelId?: string,
claimIsMine: boolean,
myChannelIds: ?Array<string>,
isFetchingComments: boolean,
isFetchingCommentsById: boolean,
isFetchingReacts: boolean,
@ -48,14 +47,14 @@ type Props = {
activeChannelId: ?string,
settingsByChannelId: { [channelId: string]: PerChannelSettings },
commentsAreExpanded?: boolean,
fetchReacts: (Array<string>) => Promise<any>,
doResolveUris: (Array<string>) => void,
fetchTopLevelComments: (string, number, number, number) => void,
fetchComment: (string) => void,
resetComments: (string) => void,
fetchTopLevelComments: (uri: string, parentId: string, page: number, pageSize: number, sortBy: number) => void,
fetchComment: (commentId: string) => void,
fetchReacts: (commentIds: Array<string>) => Promise<any>,
resetComments: (claimId: string) => void,
doResolveUris: (uris: Array<string>, returnCachedClaims: boolean) => void,
};
function CommentList(props: Props) {
export default function CommentList(props: Props) {
const {
allCommentIds,
uri,
@ -63,9 +62,9 @@ function CommentList(props: Props) {
topLevelComments,
resolvedComments,
topLevelTotalPages,
claim,
claimId,
channelId,
claimIsMine,
myChannelIds,
isFetchingComments,
isFetchingReacts,
linkedCommentId,
@ -76,11 +75,11 @@ function CommentList(props: Props) {
activeChannelId,
settingsByChannelId,
commentsAreExpanded,
fetchReacts,
doResolveUris,
fetchTopLevelComments,
fetchComment,
fetchReacts,
resetComments,
doResolveUris,
} = props;
const isMobile = useIsMobile();
@ -91,10 +90,11 @@ function CommentList(props: Props) {
const [sort, setSort] = usePersistedState('comment-sort-by', DEFAULT_SORT);
const [page, setPage] = React.useState(0);
const [commentsToDisplay, setCommentsToDisplay] = React.useState(topLevelComments);
const [didInitialPageFetch, setInitialPageFetch] = React.useState(false);
const hasDefaultExpansion = commentsAreExpanded || !isMediumScreen || isMobile;
const [expandedComments, setExpandedComments] = React.useState(hasDefaultExpansion);
const totalFetchedComments = allCommentIds ? allCommentIds.length : 0;
const channelId = getChannelIdFromClaim(claim);
const channelSettings = channelId ? settingsByChannelId[channelId] : undefined;
const moreBelow = page < topLevelTotalPages;
const isResolvingComments = topLevelComments && resolvedComments.length !== topLevelComments.length;
@ -118,8 +118,8 @@ function CommentList(props: Props) {
// Reset comments
useEffect(() => {
if (page === 0) {
if (claim) {
resetComments(claim.claim_id);
if (claimId) {
resetComments(claimId);
}
setPage(1);
}
@ -133,7 +133,7 @@ function CommentList(props: Props) {
fetchComment(linkedCommentId);
}
fetchTopLevelComments(uri, page, COMMENT_PAGE_SIZE_TOP_LEVEL, sort);
fetchTopLevelComments(uri, '', page, COMMENT_PAGE_SIZE_TOP_LEVEL, sort);
}
}, [fetchComment, fetchTopLevelComments, linkedCommentId, page, sort, uri]);
@ -182,7 +182,7 @@ function CommentList(props: Props) {
// Infinite scroll
useEffect(() => {
function shouldFetchNextPage(page, topLevelTotalPages, window, document, yPrefetchPx = 1000) {
function shouldFetchNextPage(page, topLevelTotalPages, yPrefetchPx = 1000) {
if (!spinnerRef || !spinnerRef.current) return false;
const rect = spinnerRef.current.getBoundingClientRect(); // $FlowFixMe
@ -205,23 +205,33 @@ function CommentList(props: Props) {
}
const handleCommentScroll = debounce(() => {
if (shouldFetchNextPage(page, topLevelTotalPages, window, document)) {
if (shouldFetchNextPage(page, topLevelTotalPages)) {
setPage(page + 1);
setInitialPageFetch(true);
}
}, DEBOUNCE_SCROLL_HANDLER_MS);
if (!didInitialPageFetch) {
handleCommentScroll();
setInitialPageFetch(true);
}
if (hasDefaultExpansion && !isFetchingComments && canDisplayComments && readyToDisplayComments && moreBelow) {
if (shouldFetchNextPage(page, topLevelTotalPages, window, document, 0)) {
setPage(page + 1);
} else {
window.addEventListener('scroll', handleCommentScroll);
return () => window.removeEventListener('scroll', handleCommentScroll);
const commentsInDrawer = Boolean(document.querySelector('.MuiDrawer-root .card--enable-overflow'));
const scrollingElement = commentsInDrawer ? document.querySelector('.card--enable-overflow') : window;
if (scrollingElement) {
scrollingElement.addEventListener('scroll', handleCommentScroll);
return () => scrollingElement.removeEventListener('scroll', handleCommentScroll);
}
}
}, [
canDisplayComments,
hasDefaultExpansion,
didInitialPageFetch,
isFetchingComments,
isMobile,
moreBelow,
page,
readyToDisplayComments,
@ -241,34 +251,10 @@ function CommentList(props: Props) {
const urisToResolve = [];
topLevelComments.map(({ channel_url }) => channel_url !== undefined && urisToResolve.push(channel_url));
if (urisToResolve.length > 0) doResolveUris(urisToResolve);
if (urisToResolve.length > 0) doResolveUris(urisToResolve, true);
}, [alreadyResolved, doResolveUris, topLevelComments]);
const getCommentElems = (comments) =>
comments.map((comment) => (
<CommentView
isTopLevel
threadDepth={3}
key={comment.comment_id}
uri={uri}
authorUri={comment.channel_url}
author={comment.channel_name}
claimId={comment.claim_id}
commentId={comment.comment_id}
message={comment.comment}
timePosted={comment.timestamp * 1000}
claimIsMine={claimIsMine}
commentIsMine={comment.channel_id && myChannelIds && myChannelIds.includes(comment.channel_id)}
linkedCommentId={linkedCommentId}
isPinned={comment.is_pinned}
supportAmount={comment.support_amount}
numDirectReplies={comment.replies}
isModerator={comment.is_moderator}
isGlobalMod={comment.is_global_mod}
isFiat={comment.is_fiat}
/>
));
const commentProps = { isTopLevel: true, threadDepth: 3, uri, claimIsMine, linkedCommentId };
const actionButtonsProps = { totalComments, sort, changeSort, setPage };
return (
@ -292,8 +278,13 @@ function CommentList(props: Props) {
'comments--contracted': isMediumScreen && !expandedComments,
})}
>
{readyToDisplayComments && pinnedComments && getCommentElems(pinnedComments)}
{readyToDisplayComments && commentsToDisplay && getCommentElems(commentsToDisplay)}
{readyToDisplayComments && (
<>
{pinnedComments && <CommentElements comments={pinnedComments} {...commentProps} />}
{commentsToDisplay && <CommentElements comments={commentsToDisplay} {...commentProps} />}
</>
)}
</ul>
{!hasDefaultExpansion && (
@ -328,7 +319,15 @@ function CommentList(props: Props) {
);
}
export default CommentList;
type CommentProps = {
comments: Array<Comment>,
};
const CommentElements = (commentProps: CommentProps) => {
const { comments, ...commentsProps } = commentProps;
return comments.map((comment) => <CommentView key={comment.comment_id} comment={comment} {...commentsProps} />);
};
type ActionButtonsProps = {
totalComments: number,

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { doResolveUris } from 'redux/actions/claims';
import { selectClaimIsMineForUri, selectMyChannelClaimIds, makeSelectClaimForUri } from 'redux/selectors/claims';
import { selectClaimIsMineForUri, makeSelectClaimForUri } from 'redux/selectors/claims';
import { selectIsFetchingCommentsByParentId, selectRepliesForParentId } from 'redux/selectors/comments';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import CommentsReplies from './view';
@ -17,7 +17,6 @@ const select = (state, props) => {
resolvedReplies,
claimIsMine: selectClaimIsMineForUri(state, props.uri),
userCanComment: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
myChannelIds: selectMyChannelClaimIds(state),
isFetchingByParentId: selectIsFetchingCommentsByParentId(state),
};
};

View file

@ -11,7 +11,6 @@ type Props = {
uri: string,
parentId: string,
claimIsMine: boolean,
myChannelIds: ?Array<string>,
linkedCommentId?: string,
userCanComment: boolean,
threadDepth: number,
@ -30,7 +29,6 @@ function CommentsReplies(props: Props) {
fetchedReplies,
resolvedReplies,
claimIsMine,
myChannelIds,
linkedCommentId,
userCanComment,
threadDepth,
@ -85,23 +83,13 @@ function CommentsReplies(props: Props) {
commentsToDisplay.length > 0 &&
commentsToDisplay.map((comment) => (
<Comment
key={comment.comment_id}
threadDepth={threadDepth}
uri={uri}
authorUri={comment.channel_url}
author={comment.channel_name}
claimId={comment.claim_id}
commentId={comment.comment_id}
key={comment.comment_id}
message={comment.comment}
timePosted={comment.timestamp * 1000}
comment={comment}
claimIsMine={claimIsMine}
commentIsMine={comment.channel_id && myChannelIds && myChannelIds.includes(comment.channel_id)}
linkedCommentId={linkedCommentId}
commentingEnabled={userCanComment}
supportAmount={comment.support_amount}
numDirectReplies={comment.replies}
isModerator={comment.is_moderator}
isGlobalMod={comment.is_global_mod}
supportDisabled={supportDisabled}
/>
))}

View file

@ -83,17 +83,9 @@ export default function OwnComments(props: Props) {
<Comment
isTopLevel
hideActions
authorUri={comment.channel_url}
author={comment.channel_name}
commentId={comment.comment_id}
message={comment.comment}
timePosted={comment.timestamp * 1000}
comment={comment}
commentIsMine
supportAmount={comment.support_amount}
numDirectReplies={0} // Don't show replies here
isModerator={comment.is_moderator}
isGlobalMod={comment.is_global_mod}
isFiat={comment.is_fiat}
/>
</React.Suspense>
</div>