Livestream: stop pinned comments from appearing as latest #6888

Merged
infinite-persistence merged 1 commit from ip/livestream.pin.fix into master 2021-08-17 18:09:55 +02:00
6 changed files with 101 additions and 74 deletions

View file

@ -15,6 +15,7 @@ import {
selectMyReactionsByCommentId,
makeSelectCommentIdsForUri,
selectSettingsByChannelId,
makeSelectPinnedCommentsForUri,
} from 'redux/selectors/comments';
import { doCommentReset, doCommentList, doCommentById, doCommentReactList } from 'redux/actions/comments';
import { selectActiveChannelClaim } from 'redux/selectors/app';
@ -25,6 +26,7 @@ const select = (state, props) => {
return {
myChannels: selectMyChannelClaims(state),
allCommentIds: makeSelectCommentIdsForUri(props.uri)(state),
pinnedComments: makeSelectPinnedCommentsForUri(props.uri)(state),
topLevelComments: makeSelectTopLevelCommentsForUri(props.uri)(state),
topLevelTotalPages: makeSelectTopLevelTotalPagesForUri(props.uri)(state),
totalComments: makeSelectTotalCommentsCountForUri(props.uri)(state),

View file

@ -28,6 +28,7 @@ function scaleToDevicePixelRatio(value) {
type Props = {
allCommentIds: any,
pinnedComments: Array<Comment>,
topLevelComments: Array<Comment>,
topLevelTotalPages: number,
fetchTopLevelComments: (string, number, number, number) => void,
@ -57,6 +58,7 @@ function CommentList(props: Props) {
fetchReacts,
resetComments,
uri,
pinnedComments,
topLevelComments,
topLevelTotalPages,
claim,
@ -111,6 +113,34 @@ function CommentList(props: Props) {
}
}
function getCommentElems(comments) {
return comments.map((comment) => {
return (
<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 && isMyComment(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}
/>
);
});
}
// Reset comments
useEffect(() => {
if (page === 0) {
@ -223,8 +253,6 @@ function CommentList(props: Props) {
topLevelTotalPages,
]);
const displayedComments = readyToDisplayComments ? topLevelComments : [];
return (
<Card
title={
@ -295,33 +323,8 @@ function CommentList(props: Props) {
})}
ref={commentRef}
>
{topLevelComments &&
displayedComments &&
displayedComments.map((comment) => {
return (
<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 && isMyComment(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}
/>
);
})}
{readyToDisplayComments && pinnedComments && getCommentElems(pinnedComments)}
{readyToDisplayComments && topLevelComments && getCommentElems(topLevelComments)}
</ul>
{isMobile && (

View file

@ -3,22 +3,22 @@ import { makeSelectClaimForUri, selectMyChannelClaims } from 'lbry-redux';
import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket';
import { doCommentList, doSuperChatList } from 'redux/actions/comments';
import {
selectPinnedCommentsById,
makeSelectTopLevelCommentsForUri,
selectIsFetchingComments,
makeSelectSuperChatsForUri,
makeSelectSuperChatTotalAmountForUri,
makeSelectPinnedCommentsForUri,
} from 'redux/selectors/comments';
import LivestreamComments from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
comments: makeSelectTopLevelCommentsForUri(props.uri)(state).slice(0, 75),
pinnedComments: makeSelectPinnedCommentsForUri(props.uri)(state),
fetchingComments: selectIsFetchingComments(state),
superChats: makeSelectSuperChatsForUri(props.uri)(state),
superChatsTotalAmount: makeSelectSuperChatTotalAmountForUri(props.uri)(state),
myChannels: selectMyChannelClaims(state),
pinnedCommentsById: selectPinnedCommentsById(state),
});
export default connect(select, {

View file

@ -19,11 +19,11 @@ type Props = {
doCommentSocketDisconnect: (string) => void,
doCommentList: (string, string, number, number) => void,
comments: Array<Comment>,
pinnedComments: Array<Comment>,
fetchingComments: boolean,
doSuperChatList: (string) => void,
superChats: Array<Comment>,
myChannels: ?Array<ChannelClaim>,
pinnedCommentsById: { [claimId: string]: Array<string> },
};
const VIEW_MODE_CHAT = 'view_chat';
@ -39,12 +39,12 @@ export default function LivestreamComments(props: Props) {
doCommentSocketConnect,
doCommentSocketDisconnect,
comments: commentsByChronologicalOrder,
pinnedComments,
doCommentList,
fetchingComments,
doSuperChatList,
myChannels,
superChats: superChatsByTipAmount,
pinnedCommentsById,
} = props;
let superChatsFiatAmount, superChatsTotalAmount;
@ -60,11 +60,7 @@ export default function LivestreamComments(props: Props) {
const discussionElement = document.querySelector('.livestream__comments');
const commentElement = document.querySelector('.livestream-comment');
let pinnedComment;
const pinnedCommentIds = (claimId && pinnedCommentsById[claimId]) || [];
if (pinnedCommentIds.length > 0) {
pinnedComment = commentsByChronologicalOrder.find((c) => c.comment_id === pinnedCommentIds[0]);
}
const pinnedComment = pinnedComments.length > 0 ? pinnedComments[0] : null;
React.useEffect(() => {
if (claimId) {

View file

@ -301,9 +301,10 @@ export default handleActions(
for (let i = 0; i < comments.length; ++i) {
const comment = comments[i];
commonUpdateAction(comment, commentById, commentIds, i);
pushToArrayInObject(topLevelCommentsById, claimId, comment.comment_id);
if (comment.is_pinned) {
pushToArrayInObject(pinnedCommentsById, claimId, comment.comment_id);
} else {
pushToArrayInObject(topLevelCommentsById, claimId, comment.comment_id);
}
}
}
@ -615,45 +616,50 @@ export default handleActions(
const topLevelCommentsById = Object.assign({}, state.topLevelCommentsById);
const pinnedCommentsById = Object.assign({}, state.pinnedCommentsById);
if (pinnedComment && topLevelCommentsById[claimId]) {
const index = topLevelCommentsById[claimId].indexOf(pinnedComment.comment_id);
if (index > -1) {
topLevelCommentsById[claimId].splice(index, 1);
if (pinnedCommentsById[claimId]) {
// Remove here so that the 'unshift' below will be a unique entry.
pinnedCommentsById[claimId] = pinnedCommentsById[claimId].filter((x) => x !== pinnedComment.comment_id);
} else {
pinnedCommentsById[claimId] = [];
if (pinnedComment) {
if (topLevelCommentsById[claimId]) {
const index = topLevelCommentsById[claimId].indexOf(pinnedComment.comment_id);
if (index > -1) {
topLevelCommentsById[claimId].splice(index, 1);
}
} else {
topLevelCommentsById[claimId] = [];
}
if (unpin) {
// Without the sort score, I have no idea where to put it. Just
// dump it at the bottom. Users can refresh if they want it back to
// the correct sorted position.
topLevelCommentsById[claimId].push(pinnedComment.comment_id);
} else {
topLevelCommentsById[claimId].unshift(pinnedComment.comment_id);
pinnedCommentsById[claimId].unshift(pinnedComment.comment_id);
if (pinnedCommentsById[claimId]) {
const index = pinnedCommentsById[claimId].indexOf(pinnedComment.comment_id);
if (index > -1) {
pinnedCommentsById[claimId].splice(index, 1);
}
} else {
pinnedCommentsById[claimId] = [];
}
if (commentById[pinnedComment.comment_id]) {
// Commentron's `comment.Pin` response places the creator's credentials
// in the 'channel_*' fields, which doesn't make sense. Maybe it is to
// show who signed/pinned it, but even if so, it shouldn't overload
// these variables which are already used by existing comment data structure.
// Ensure we don't override the existing/correct values, but fallback
// to whatever was given.
const { channel_id, channel_name, channel_url } = commentById[pinnedComment.comment_id];
commentById[pinnedComment.comment_id] = {
...pinnedComment,
channel_id: channel_id || pinnedComment.channel_id,
channel_name: channel_name || pinnedComment.channel_name,
channel_url: channel_url || pinnedComment.channel_url,
};
} else {
commentById[pinnedComment.comment_id] = pinnedComment;
}
if (unpin) {
// Without the sort score, I have no idea where to put it. Just
// dump it at the top. Users can refresh if they want it back to
// the correct sorted position.
topLevelCommentsById[claimId].unshift(pinnedComment.comment_id);
} else {
pinnedCommentsById[claimId].unshift(pinnedComment.comment_id);
}
if (commentById[pinnedComment.comment_id]) {
// Commentron's `comment.Pin` response places the creator's credentials
// in the 'channel_*' fields, which doesn't make sense. Maybe it is to
// show who signed/pinned it, but even if so, it shouldn't overload
// these variables which are already used by existing comment data structure.
// Ensure we don't override the existing/correct values, but fallback
// to whatever was given.
const { channel_id, channel_name, channel_url } = commentById[pinnedComment.comment_id];
commentById[pinnedComment.comment_id] = {
...pinnedComment,
channel_id: channel_id || pinnedComment.channel_id,
channel_name: channel_name || pinnedComment.channel_name,
channel_url: channel_url || pinnedComment.channel_url,
};
} else {
commentById[pinnedComment.comment_id] = pinnedComment;
}
}

View file

@ -13,7 +13,27 @@ export const selectIsFetchingCommentsByParentId = createSelector(selectState, (s
export const selectIsPostingComment = createSelector(selectState, (state) => state.isCommenting);
export const selectIsFetchingReacts = createSelector(selectState, (state) => state.isFetchingReacts);
export const selectOthersReactsById = createSelector(selectState, (state) => state.othersReactsByCommentId);
export const selectPinnedCommentsById = createSelector(selectState, (state) => state.pinnedCommentsById);
export const makeSelectPinnedCommentsForUri = (uri: string) =>
createSelector(
selectCommentsByUri,
selectCommentsById,
selectPinnedCommentsById,
(byUri, byId, pinnedCommentsById) => {
const claimId = byUri[uri];
const pinnedCommentIds = pinnedCommentsById && pinnedCommentsById[claimId];
const pinnedComments = [];
if (pinnedCommentIds) {
pinnedCommentIds.forEach((commentId) => {
pinnedComments.push(byId[commentId]);
});
}
return pinnedComments;
}
);
export const selectModerationBlockList = createSelector(selectState, (state) =>
state.moderationBlockList ? state.moderationBlockList.reverse() : []