feat: read indicator — NEW divider line between read and unread messages

This commit is contained in:
2026-07-06 19:41:19 +00:00
parent ca64608e12
commit 6d08016451
2 changed files with 63 additions and 34 deletions
+36 -21
View File
@@ -326,6 +326,7 @@ export function ChatArea() {
const members = activeServerId ? membersByServer[activeServerId] || [] : [];
const memberUsernames = useMemo(() => new Set(members.map((m) => m.username)), [members]);
const markRead = useReadStatesStore((s) => s.markRead);
const readStates = useReadStatesStore((s) => s.states);
const [showPinned, setShowPinned] = useState(false);
const [showFeatureRequests, setShowFeatureRequests] = useState(false);
@@ -828,27 +829,41 @@ export function ChatArea() {
{!isLoading && messages.length === 0 && (
<p className="text-gb-fg-f">[no messages in this channel]</p>
)}
{messages.map((message) => (
<MessageItem
key={message.id}
message={message}
memberUsernames={memberUsernames}
selectMode={selectMode}
isSelected={selectedIds.has(message.id)}
onAuthorClick={handleAuthorClick}
onToggleSelect={handleToggleSelect}
onContextMenu={handleMessageContextMenu}
onAddReaction={handleAddReaction}
currentUserId={currentUser?.id}
activeReactionMessageId={activeReactionMessageId}
setActiveReactionMessageId={setActiveReactionMessageId}
activeNativeReactionMessageId={activeNativeReactionMessageId}
setActiveNativeReactionMessageId={setActiveNativeReactionMessageId}
members={members}
parentMessage={message.reply_to ? messages.find((m) => m.id === message.reply_to) : null}
onJumpToParent={handleJumpToMessage}
/>
))}
{messages.map((message, i) => {
// ponytail: last-read divider — show after the message matching the user's read state
const lastReadId = activeChannelId ? readStates[activeChannelId] : undefined;
const showDivider = lastReadId && message.id === lastReadId && i < messages.length - 1;
return (
<>
<MessageItem
key={message.id}
message={message}
memberUsernames={memberUsernames}
selectMode={selectMode}
isSelected={selectedIds.has(message.id)}
onAuthorClick={handleAuthorClick}
onToggleSelect={handleToggleSelect}
onContextMenu={handleMessageContextMenu}
onAddReaction={handleAddReaction}
currentUserId={currentUser?.id}
activeReactionMessageId={activeReactionMessageId}
setActiveReactionMessageId={setActiveReactionMessageId}
activeNativeReactionMessageId={activeNativeReactionMessageId}
setActiveNativeReactionMessageId={setActiveNativeReactionMessageId}
members={members}
parentMessage={message.reply_to ? messages.find((m) => m.id === message.reply_to) : null}
onJumpToParent={handleJumpToMessage}
/>
{showDivider && (
<div className="flex items-center gap-2 my-1">
<span className="flex-1 border-t border-gb-red"></span>
<span className="text-gb-red text-xs font-bold shrink-0"> NEW </span>
<span className="flex-1 border-t border-gb-red"></span>
</div>
)}
</>
);
})}
<div ref={bottomRef} />
</div>
<div className="px-3 pt-1 text-xs text-gb-fg-f font-mono italic h-5 select-none">