"use client"; import { useCallback, useEffect, useState } from "react"; import { getWalletLogs } from "@/api/wallet"; import { usePlayerSessionStore } from "@/stores/player-session-store"; import type { WalletPendingTransfer } from "@/types/api/wallet-logs"; export const WALLET_LOGS_REFRESH_EVENT = "lottery:wallet-logs-refreshed"; const WALLET_NOTIFICATION_READ_KEY = "lottery:wallet-notification-read-transfer-nos"; let pendingReconcileInFlight: Promise | null = null; let pendingReconcileCache: WalletPendingTransfer[] | null = null; let pendingReconcileFetchedAtMs = 0; const PENDING_RECONCILE_CACHE_TTL_MS = 5_000; async function fetchPendingWalletReconcile(): Promise { const now = Date.now(); if ( pendingReconcileCache !== null && now - pendingReconcileFetchedAtMs < PENDING_RECONCILE_CACHE_TTL_MS ) { return pendingReconcileCache; } if (pendingReconcileInFlight) { return pendingReconcileInFlight; } pendingReconcileInFlight = getWalletLogs({ page: 1, size: 1 }) .then((data) => { pendingReconcileCache = data.pending_reconcile ?? []; pendingReconcileFetchedAtMs = Date.now(); return pendingReconcileCache; }) .catch(() => []) .finally(() => { pendingReconcileInFlight = null; }); return pendingReconcileInFlight; } type WalletLogsRefreshDetail = { pending?: WalletPendingTransfer[]; }; export function usePendingWalletReconcile(): { pending: WalletPendingTransfer[]; unreadPending: WalletPendingTransfer[]; unreadCount: number; hasPending: boolean; hasUnread: boolean; loading: boolean; refresh: () => Promise; markAsRead: (transferNo: string) => void; markAllAsRead: () => void; } { const bearerToken = usePlayerSessionStore((s) => s.bearerToken); const [pending, setPending] = useState([]); const [readTransferNos, setReadTransferNos] = useState>(() => { if (typeof window === "undefined") return new Set(); try { const raw = window.localStorage.getItem(WALLET_NOTIFICATION_READ_KEY); if (!raw) return new Set(); const parsed = JSON.parse(raw); if (!Array.isArray(parsed)) return new Set(); return new Set( parsed.filter((value): value is string => typeof value === "string" && value.trim() !== ""), ); } catch { return new Set(); } }); const [loading, setLoading] = useState(false); useEffect(() => { if (typeof window === "undefined") return; try { window.localStorage.setItem(WALLET_NOTIFICATION_READ_KEY, JSON.stringify(Array.from(readTransferNos))); } catch { // ignore storage quota or privacy mode errors } }, [readTransferNos]); const refresh = useCallback(async (): Promise => { if (!bearerToken?.trim()) { setPending([]); pendingReconcileCache = null; pendingReconcileFetchedAtMs = 0; return; } setLoading(true); try { const nextPending = await fetchPendingWalletReconcile(); setPending(nextPending); } finally { setLoading(false); } }, [bearerToken]); useEffect(() => { queueMicrotask(() => { void refresh(); }); function onWalletLogsRefreshed(event: Event): void { const detail = (event as CustomEvent).detail; if (Array.isArray(detail?.pending)) { setPending(detail.pending); return; } void refresh(); } window.addEventListener(WALLET_LOGS_REFRESH_EVENT, onWalletLogsRefreshed); return () => window.removeEventListener(WALLET_LOGS_REFRESH_EVENT, onWalletLogsRefreshed); }, [refresh]); const markAsRead = useCallback((transferNo: string): void => { const normalized = transferNo.trim(); if (!normalized) return; setReadTransferNos((prev) => { if (prev.has(normalized)) return prev; const next = new Set(prev); next.add(normalized); return next; }); }, []); const markAllAsRead = useCallback((): void => { if (pending.length === 0) return; setReadTransferNos(new Set(pending.map((item) => item.transfer_no))); }, [pending]); const unreadPending = pending.filter((item) => !readTransferNos.has(item.transfer_no)); return { pending, unreadPending, unreadCount: unreadPending.length, hasPending: pending.length > 0, hasUnread: unreadPending.length > 0, loading, refresh, markAsRead, markAllAsRead, }; } export function dispatchWalletLogsRefresh(pending: WalletPendingTransfer[]): void { if (typeof window === "undefined") return; window.dispatchEvent( new CustomEvent(WALLET_LOGS_REFRESH_EVENT, { detail: { pending }, }), ); }