feat: unread markers for channels and DMs
- Read states store: smarter hasUnread compares against latest message - ConversationList: orange dot + bold name for unread DMs - DMChat: auto-mark-read when viewing conversation - WS handler: mark DM read on new message while active+focused
This commit is contained in:
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useConversationStore } from "../stores/conversation.ts";
|
||||
import { useAuthStore } from "../stores/auth.ts";
|
||||
import { useReadStatesStore } from "../stores/readStates.ts";
|
||||
import { NewConversationModal } from "./NewConversationModal.tsx";
|
||||
|
||||
function otherMemberName(conv: { members: { id: string; username: string }[] }, currentUserId: string) {
|
||||
@@ -18,7 +19,10 @@ export function ConversationList() {
|
||||
const activeId = useConversationStore((s) => s.activeConversationId);
|
||||
const fetchConversations = useConversationStore((s) => s.fetchConversations);
|
||||
const createConversation = useConversationStore((s) => s.createConversation);
|
||||
const messagesByConv = useConversationStore((s) => s.messagesByConversation);
|
||||
const currentUser = useAuthStore((s) => s.user);
|
||||
const hasConvUnread = useReadStatesStore((s) => s.hasConvUnread);
|
||||
const markConvRead = useReadStatesStore((s) => s.markConvRead);
|
||||
const navigate = useNavigate();
|
||||
const [showNew, setShowNew] = useState(false);
|
||||
const [notesError, setNotesError] = useState<string | null>(null);
|
||||
@@ -28,15 +32,19 @@ export function ConversationList() {
|
||||
}, [fetchConversations]);
|
||||
|
||||
const openConversation = (convId: string) => {
|
||||
// mark as read when opening
|
||||
const msgs = messagesByConv[convId] || [];
|
||||
if (msgs.length > 0) {
|
||||
markConvRead(convId, msgs[msgs.length - 1].id);
|
||||
}
|
||||
navigate(`/dm/${convId}`);
|
||||
};
|
||||
|
||||
const openNotes = async () => {
|
||||
setNotesError(null);
|
||||
// Find existing self-DM or create one
|
||||
const existing = conversations.find((c) => isSelfDM(c, currentUser?.id || ""));
|
||||
if (existing) {
|
||||
navigate(`/dm/${existing.id}`);
|
||||
openConversation(existing.id);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -50,6 +58,11 @@ export function ConversationList() {
|
||||
}
|
||||
};
|
||||
|
||||
const getLatestMessageId = (convId: string): string | undefined => {
|
||||
const msgs = messagesByConv[convId] || [];
|
||||
return msgs.length > 0 ? msgs[msgs.length - 1].id : undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full w-56 bg-gb-bg-s border-r border-gb-bg-t flex flex-col">
|
||||
<div className="terminal-border border-t-0 border-x-0 px-3 py-2 text-gb-fg truncate flex items-center justify-between">
|
||||
@@ -82,6 +95,7 @@ export function ConversationList() {
|
||||
: conv.type === "group_dm"
|
||||
? conv.name || conv.members.map((m) => m.username).join(", ")
|
||||
: otherMemberName(conv, currentUser?.id || "");
|
||||
const unread = hasConvUnread(conv.id, getLatestMessageId(conv.id));
|
||||
return (
|
||||
<button
|
||||
key={conv.id}
|
||||
@@ -93,7 +107,10 @@ export function ConversationList() {
|
||||
}`}
|
||||
>
|
||||
<span className="text-gb-fg-f">{self ? "📝" : "@"}</span>
|
||||
<span className="truncate">{name}</span>
|
||||
<span className={`truncate flex-1 ${unread ? "text-gb-fg font-bold" : ""}`}>{name}</span>
|
||||
{unread && (
|
||||
<span className="text-gb-orange text-xs font-bold shrink-0" title="Unread messages">●</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user