feat: read indicator — NEW divider line between read and unread messages
This commit is contained in:
@@ -326,6 +326,7 @@ export function ChatArea() {
|
|||||||
const members = activeServerId ? membersByServer[activeServerId] || [] : [];
|
const members = activeServerId ? membersByServer[activeServerId] || [] : [];
|
||||||
const memberUsernames = useMemo(() => new Set(members.map((m) => m.username)), [members]);
|
const memberUsernames = useMemo(() => new Set(members.map((m) => m.username)), [members]);
|
||||||
const markRead = useReadStatesStore((s) => s.markRead);
|
const markRead = useReadStatesStore((s) => s.markRead);
|
||||||
|
const readStates = useReadStatesStore((s) => s.states);
|
||||||
|
|
||||||
const [showPinned, setShowPinned] = useState(false);
|
const [showPinned, setShowPinned] = useState(false);
|
||||||
const [showFeatureRequests, setShowFeatureRequests] = useState(false);
|
const [showFeatureRequests, setShowFeatureRequests] = useState(false);
|
||||||
@@ -828,7 +829,12 @@ export function ChatArea() {
|
|||||||
{!isLoading && messages.length === 0 && (
|
{!isLoading && messages.length === 0 && (
|
||||||
<p className="text-gb-fg-f">[no messages in this channel]</p>
|
<p className="text-gb-fg-f">[no messages in this channel]</p>
|
||||||
)}
|
)}
|
||||||
{messages.map((message) => (
|
{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
|
<MessageItem
|
||||||
key={message.id}
|
key={message.id}
|
||||||
message={message}
|
message={message}
|
||||||
@@ -848,7 +854,16 @@ export function ChatArea() {
|
|||||||
parentMessage={message.reply_to ? messages.find((m) => m.id === message.reply_to) : null}
|
parentMessage={message.reply_to ? messages.find((m) => m.id === message.reply_to) : null}
|
||||||
onJumpToParent={handleJumpToMessage}
|
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 ref={bottomRef} />
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 pt-1 text-xs text-gb-fg-f font-mono italic h-5 select-none">
|
<div className="px-3 pt-1 text-xs text-gb-fg-f font-mono italic h-5 select-none">
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export function DMChat() {
|
|||||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const lastTypingRef = useRef<number>(0);
|
const lastTypingRef = useRef<number>(0);
|
||||||
const markConvRead = useReadStatesStore((s) => s.markConvRead);
|
const markConvRead = useReadStatesStore((s) => s.markConvRead);
|
||||||
|
const convStates = useReadStatesStore((s) => s.convStates);
|
||||||
|
|
||||||
const id = conversationId || activeId;
|
const id = conversationId || activeId;
|
||||||
const conversation = conversations.find((c) => c.id === id);
|
const conversation = conversations.find((c) => c.id === id);
|
||||||
@@ -321,7 +322,11 @@ export function DMChat() {
|
|||||||
{isLoadingOlder && <p className="text-center text-gb-fg-f text-xs">[loading older messages...]</p>}
|
{isLoadingOlder && <p className="text-center text-gb-fg-f text-xs">[loading older messages...]</p>}
|
||||||
{!id && <p className="text-gb-fg-f">[select a conversation]</p>}
|
{!id && <p className="text-gb-fg-f">[select a conversation]</p>}
|
||||||
{id && messages.length === 0 && <p className="text-gb-fg-f">[no messages]</p>}
|
{id && messages.length === 0 && <p className="text-gb-fg-f">[no messages]</p>}
|
||||||
{messages.map((msg: ConversationMessage) => (
|
{messages.map((msg: ConversationMessage, i: number) => {
|
||||||
|
const lastReadId = id ? convStates[id] : undefined;
|
||||||
|
const showDivider = lastReadId && msg.id === lastReadId && i < messages.length - 1;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<DMMessageItem
|
<DMMessageItem
|
||||||
key={msg.id}
|
key={msg.id}
|
||||||
msg={msg}
|
msg={msg}
|
||||||
@@ -333,7 +338,16 @@ export function DMChat() {
|
|||||||
currentUserId={currentUser?.id}
|
currentUserId={currentUser?.id}
|
||||||
conversationId={id || ""}
|
conversationId={id || ""}
|
||||||
/>
|
/>
|
||||||
))}
|
{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 ref={bottomRef} />
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 pt-1 text-xs text-gb-fg-f font-mono italic h-5 select-none">
|
<div className="px-3 pt-1 text-xs text-gb-fg-f font-mono italic h-5 select-none">
|
||||||
|
|||||||
Reference in New Issue
Block a user