From d3976e33f8351a4aea45f5138106db6c8d9158f0 Mon Sep 17 00:00:00 2001 From: Hampus Date: Thu, 27 Aug 2026 02:02:45 +0200 Subject: [PATCH] fix(app): keep unread channel opens anchored to the divider --- .../components/BlockedMessageGroups.tsx | 22 +++ .../components/ChannelMessageStream.tsx | 2 + .../channel/components/ChannelMessages.tsx | 2 +- .../utils/MessageGroupingUtils.test.ts | 137 ++++++++++++++++++ .../messaging/utils/MessageGroupingUtils.ts | 19 ++- .../utils/scroll_manager/shared.test.ts | 42 ++++++ .../features/read_state/state/ReadStates.ts | 4 + .../state/read_states/ReadStateEntry.ts | 2 +- 8 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 fluxer_app/src/features/messaging/utils/MessageGroupingUtils.test.ts diff --git a/fluxer_app/src/features/channel/components/BlockedMessageGroups.tsx b/fluxer_app/src/features/channel/components/BlockedMessageGroups.tsx index ef60a08d7..3d4337483 100644 --- a/fluxer_app/src/features/channel/components/BlockedMessageGroups.tsx +++ b/fluxer_app/src/features/channel/components/BlockedMessageGroups.tsx @@ -23,6 +23,7 @@ interface BlockedMessageGroupsProps { messageGroups: Array; onReveal: (messageId: string | null) => void; revealed: boolean; + hasUnread?: boolean; compact: boolean; messageGroupSpacing: number; variant: 'blocked' | 'spammer'; @@ -33,11 +34,13 @@ interface BlockedMessageGroupsProps { messageActionsClassName?: string; renderMessageActions?: (message: Message) => React.ReactNode; renderMessageWrapper?: (props: MessageGroupRenderWrapperProps) => React.ReactNode; + suppressUnreadIndicator?: boolean; } const arePropsEqual = (prevProps: BlockedMessageGroupsProps, nextProps: BlockedMessageGroupsProps): boolean => { if (prevProps.channel.id !== nextProps.channel.id) return false; if (prevProps.revealed !== nextProps.revealed) return false; + if (prevProps.hasUnread !== nextProps.hasUnread) return false; if (prevProps.compact !== nextProps.compact) return false; if (prevProps.messageGroupSpacing !== nextProps.messageGroupSpacing) return false; if (prevProps.variant !== nextProps.variant) return false; @@ -49,6 +52,7 @@ const arePropsEqual = (prevProps: BlockedMessageGroupsProps, nextProps: BlockedM if (prevProps.messageActionsClassName !== nextProps.messageActionsClassName) return false; if (prevProps.renderMessageActions !== nextProps.renderMessageActions) return false; if (prevProps.renderMessageWrapper !== nextProps.renderMessageWrapper) return false; + if (prevProps.suppressUnreadIndicator !== nextProps.suppressUnreadIndicator) return false; if (prevProps.messageGroups.length !== nextProps.messageGroups.length) return false; for (let i = 0; i < prevProps.messageGroups.length; i++) { const prevGroup = prevProps.messageGroups[i]; @@ -69,6 +73,7 @@ export const BlockedMessageGroups = React.memo((props channel, compact, revealed, + hasUnread = false, messageGroupSpacing, onReveal, variant, @@ -79,6 +84,7 @@ export const BlockedMessageGroups = React.memo((props messageActionsClassName, renderMessageActions, renderMessageWrapper, + suppressUnreadIndicator, } = props; const containerRef = useRef(null); const scrollToBottomFrameRef = useRef(null); @@ -169,6 +175,12 @@ export const BlockedMessageGroups = React.memo((props }; messageGroups.forEach((item, itemIndex) => { if (item.type === ChannelStreamType.DIVIDER) { + if (item.unreadId && suppressUnreadIndicator) { + return; + } + if (itemIndex === 0 && item.unreadId) { + return; + } flushGroup(); nodes.push( ((props messageActionsClassName, renderMessageActions, renderMessageWrapper, + suppressUnreadIndicator, ]); + const leadingUnreadDivider = messageGroups[0]?.type === ChannelStreamType.DIVIDER && !!messageGroups[0].unreadId; return (
+ {hasUnread && (!revealed || leadingUnreadDivider) && ( + + )}