feat(web): D4 会话未读徽标 — 打开即已读 + 消费 session_read
- SessionMeta.unreadCount(可选,兼容旧后端)+ api/sessions.markSessionRead - lib/sessionRead.ts:会话历史落地后再推游标(best-effort,失败静默,下次 GET /sessions 收敛)+ 帧归属判定(ws.ts 因此留在 500 行内) - store.clearUnread 无变化时返回同一 state,避免多余重渲染;Nav 徽标沿用 Miuix token(mu-radius-pill/mu-err/mu-rise-in) - 逻辑测试 35 → 54 项,含变异校验(改成 clearUnread(activeSessionId) 即 FAIL)
This commit is contained in:
1 parent
e8eccd2bb2
commit
a828edacc5
9 files changed
+235
-11
No files matched your search
@@ -63,6 +63,11 @@ export interface AppStore {
|
||||
|
||||
/** Replace the session metadata list. */
|
||||
setSessions: (list: SessionMeta[]) => void;
|
||||
/** Zero one session's unread badge locally (Android 协同 B3/D4). Called when the
|
||||
* user opens the session and its page has loaded, and on the `session_read`
|
||||
* frame another end broadcasts — a no-op for an already-read or unknown
|
||||
* session, so neither path can invent a row or re-render the list needlessly. */
|
||||
clearUnread: (sessionId: string) => void;
|
||||
/** Focus a session and clear its message cache (refilled by the caller). */
|
||||
setActiveSession: (id: string) => void;
|
||||
/** Replace the active session's message list (initial page on switch). */
|
||||
@@ -144,6 +149,14 @@ export const useAppStore = create<AppStore>()(
|
||||
sessions: Object.fromEntries(list.map((s) => [s.id, s])),
|
||||
}),
|
||||
setActiveSession: (id) => set({ activeSessionId: id, messages: [] }),
|
||||
clearUnread: (sessionId) =>
|
||||
set((s) => {
|
||||
const meta = s.sessions[sessionId];
|
||||
if (!meta?.unreadCount) return s;
|
||||
return {
|
||||
sessions: { ...s.sessions, [sessionId]: { ...meta, unreadCount: 0 } },
|
||||
};
|
||||
}),
|
||||
setMessages: (list) => set({ messages: list }),
|
||||
prependMessages: (list) =>
|
||||
set((s) => {
|
||||
|
||||
Reference in new issue
Block a user