Files
Kokorone/apps/mobile/lib/unread-context.tsx
T
jiantw83andClaude Sonnet 5 1bea06e3cd feat(遊戲化): 新增大廳、附身、時間流、後日談進度等遊戲化系統功能
新增角色大廳(lobby)邀約與通話、附身(possession)、時鐘(clock)抽象、
時間流與睡眠負債、後日談進度、行為選擇與旁觀反應等模組,並補齊對應冒煙
測試(R~V)與單元測試;同步調整 web/mobile 對應頁面與元件。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 09:10:26 +08:00

45 lines
2.1 KiB
TypeScript

import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from "react";
interface UnreadContextValue {
hasUnread: boolean; // 推播觸發的提示 或 任一房間有未讀,兩者只要有一個成立即為 true
markUnread: () => void; // Q-4:收到推播時呼叫
markRead: () => void; // Q-4:使用者切回聊天分頁時呼叫,只清推播提示,不動各房未讀數
roomUnreadCounts: Record<string, number>; // T20-P2:各進行中房間的未讀數(來自大廳聚合查詢)
setRoomUnreadCounts: (counts: Record<string, number>) => void;
totalRoomUnread: number;
}
const UnreadContext = createContext<UnreadContextValue | null>(null);
// Q-4:聊天分頁的未讀/推播提示紅點狀態,由根佈局的推播接收監聽器寫入,
// 由聊天分頁在取得焦點時清除(見 app/(tabs)/index.tsx 的 useFocusEffect)。
// T20-P2:擴充承接大廳多房未讀——分頁籤紅點現在同時反映「有推播尚未看」與「任一房間有未讀訊息」,
// 兩個來源各自獨立更新,互不覆蓋(推播清除不會把房間未讀數也清空,反之亦然)。
export function UnreadProvider({ children }: { children: ReactNode }) {
const [pushUnread, setPushUnread] = useState(false);
const [roomUnreadCounts, setRoomUnreadCounts] = useState<Record<string, number>>({});
const markUnread = useCallback(() => setPushUnread(true), []);
const markRead = useCallback(() => setPushUnread(false), []);
const totalRoomUnread = useMemo(
() => Object.values(roomUnreadCounts).reduce((sum, count) => sum + count, 0),
[roomUnreadCounts],
);
const hasUnread = pushUnread || totalRoomUnread > 0;
return (
<UnreadContext.Provider
value={{ hasUnread, markUnread, markRead, roomUnreadCounts, setRoomUnreadCounts, totalRoomUnread }}
>
{children}
</UnreadContext.Provider>
);
}
export function useUnread(): UnreadContextValue {
const ctx = useContext(UnreadContext);
if (!ctx) {
throw new Error("useUnread 必須在 UnreadProvider 底下使用");
}
return ctx;
}