'use client'; import { create } from 'zustand'; import type { XiaobaoVersionRisk } from '@/lib/xiaobao-risk'; import { markXiaobaoWarningRead, type XiaobaoWarningReadState, } from '@/lib/xiaobao-warning-view'; import { loadServerData, saveServerData } from '@/lib/server-data'; interface XiaobaoWarningReadStoreState { readStates: XiaobaoWarningReadState[]; readStateLoaded: boolean; error?: string; fetchReadStates: () => Promise; markRiskRead: (userId: string, risk: XiaobaoVersionRisk, readAt?: string) => Promise; } let readStateSaveQueue: Promise = Promise.resolve(); async function loadReadStates(): Promise { try { const rows = await loadServerData('xiaobao-warning-views'); return normalizeReadStates(rows); } catch {} return null; } function normalizeReadStates(rows: unknown): XiaobaoWarningReadState[] { if (!Array.isArray(rows)) return []; return rows.filter(isReadState); } function isReadState(row: unknown): row is XiaobaoWarningReadState { if (!row || typeof row !== 'object') return false; const item = row as Partial; return ( typeof item.userId === 'string' && typeof item.versionId === 'string' && typeof item.signature === 'string' && typeof item.readAt === 'string' ); } function mergeReadStates( remoteRows: XiaobaoWarningReadState[] = [], localRows: XiaobaoWarningReadState[] = [], ): XiaobaoWarningReadState[] { const byKey = new Map(); for (const row of [...remoteRows, ...localRows]) { const key = `${row.userId}::${row.versionId}`; const existing = byKey.get(key); if (!existing || row.readAt.localeCompare(existing.readAt) >= 0) { byKey.set(key, row); } } return Array.from(byKey.values()).sort((a, b) => b.readAt.localeCompare(a.readAt)); } export const useXiaobaoWarningReadStore = create((set, get) => ({ readStates: [], readStateLoaded: false, error: undefined, fetchReadStates: async () => { const rows = await loadReadStates(); set({ readStates: rows ? mergeReadStates(rows, get().readStates) : get().readStates, readStateLoaded: rows !== null, error: rows === null ? '小宝预警查看状态加载失败' : undefined, }); }, markRiskRead: async (userId, risk, readAt = new Date().toISOString()) => { if (!userId) return; const optimistic = markXiaobaoWarningRead(get().readStates, userId, risk, readAt); set({ readStates: optimistic, error: undefined }); const task = readStateSaveQueue.then(async () => { const remote = await loadServerData('xiaobao-warning-views'); const merged = mergeReadStates(normalizeReadStates(remote), get().readStates); set({ readStates: merged, readStateLoaded: true, error: undefined }); await saveServerData('xiaobao-warning-views', merged); }); readStateSaveQueue = task.catch(() => undefined); try { await task; } catch (error) { set({ error: '小宝预警查看状态保存失败' }); throw error; } }, }));