scrollToWalletSection("wallet-logs")}
- />
-
| 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 creditMode = isCreditFundingPlayer(usePlayerSessionStore((s) => s.profile));
- 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() || creditMode) {
- setPending([]);
- pendingReconcileCache = null;
- pendingReconcileFetchedAtMs = 0;
- return;
- }
- setLoading(true);
- try {
- const nextPending = await fetchPendingWalletReconcile();
- setPending(nextPending);
- } finally {
- setLoading(false);
- }
- }, [bearerToken, creditMode]);
-
- 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 },
- }),
- );
-}
diff --git a/src/lib/pending-reconcile-notification.ts b/src/lib/pending-reconcile-notification.ts
deleted file mode 100644
index 53069d4..0000000
--- a/src/lib/pending-reconcile-notification.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/** 待对账划转通知文案(与流水类型「转入/转出」区分,避免误解为已成功) */
-
-export function pendingReconcileTitleKey(type: string): string {
- return type === "transfer_out"
- ? "notifications.pendingTitle.transfer_out"
- : "notifications.pendingTitle.transfer_in";
-}
-
-export function pendingReconcileDescriptionKey(type: string): string {
- return type === "transfer_out"
- ? "notifications.pendingDescription.transfer_out"
- : "notifications.pendingDescription.transfer_in";
-}