diff --git a/src/features/wallet/wallet-pending-reconcile-section.tsx b/src/features/wallet/wallet-pending-reconcile-section.tsx deleted file mode 100644 index ea91578..0000000 --- a/src/features/wallet/wallet-pending-reconcile-section.tsx +++ /dev/null @@ -1,153 +0,0 @@ -"use client"; - -import { BellRing, CheckCheck } from "lucide-react"; -import { useTranslation } from "react-i18next"; - -import { Button } from "@/components/ui/button"; -import { usePendingWalletReconcile } from "@/hooks/use-pending-wallet-reconcile"; -import { formatPlayerInstant } from "@/lib/player-datetime"; -import { formatMinorAsCurrency } from "@/lib/money"; -import { isCreditFundingPlayer } from "@/lib/player-funding-mode"; -import { - pendingReconcileDescriptionKey, - pendingReconcileTitleKey, -} from "@/lib/pending-reconcile-notification"; -import { usePlayerSessionStore } from "@/stores/player-session-store"; -import { cn } from "@/lib/utils"; - -type WalletPendingReconcileSectionProps = { - onViewLogs?: () => void; -}; - -/** 钱包页内嵌:待对账提醒(原独立 /notifications 页) */ -export function WalletPendingReconcileSection({ - onViewLogs, -}: WalletPendingReconcileSectionProps) { - const { t } = useTranslation("player"); - const creditMode = isCreditFundingPlayer(usePlayerSessionStore((state) => state.profile)); - const { pending, unreadPending, unreadCount, loading, markAsRead, markAllAsRead } = - usePendingWalletReconcile(); - const unreadSet = new Set(unreadPending.map((item) => item.transfer_no)); - - if (creditMode) { - return null; - } - - return ( -
-
-
- -

- {t("notifications.title")} - {unreadCount > 0 ? ( - - ({t("notifications.unreadCount", { count: unreadCount })}) - - ) : null} -

-
- -
- - {loading && pending.length === 0 ? ( -
- {t("actions.loading")} -
- ) : null} - - {!loading && pending.length === 0 ? ( -
-

{t("notifications.empty")}

-
- ) : null} - - {pending.length > 0 ? ( - - ) : null} -
- ); -} \ No newline at end of file diff --git a/src/features/wallet/wallet-screen.tsx b/src/features/wallet/wallet-screen.tsx index 2f36ab4..b5cdd14 100644 --- a/src/features/wallet/wallet-screen.tsx +++ b/src/features/wallet/wallet-screen.tsx @@ -14,10 +14,8 @@ import { TransferInDialog, TransferOutDialog, } from "@/features/wallet/wallet-transfer-dialogs"; -import { WalletPendingReconcileSection } from "@/features/wallet/wallet-pending-reconcile-section"; import { PlayerMoneyDisplay } from "@/components/player-money-display"; import { WalletLogsBlock } from "@/features/wallet/wallet-logs-block"; -import { dispatchWalletLogsRefresh } from "@/hooks/use-pending-wallet-reconcile"; import { useActivePlayerCurrency } from "@/hooks/use-active-player-currency"; import { isCreditFundingPlayer } from "@/lib/player-funding-mode"; import { formatMinorAsCurrency } from "@/lib/money"; @@ -80,10 +78,7 @@ export function WalletScreen() { if (section !== "logs" && section !== "pending") return; sectionDeepLinkHandledRef.current = true; - const targetId = - section === "logs" || (section === "pending" && isCreditPlayer) - ? "wallet-logs" - : "wallet-pending"; + const targetId = "wallet-logs"; const timer = window.setTimeout(() => { scrollToWalletSection(targetId); router.replace("/wallet", { scroll: false }); @@ -103,7 +98,6 @@ export function WalletScreen() { ? { ...nextLogs, items: [...current.items, ...nextLogs.items] } : nextLogs, ); - dispatchWalletLogsRefresh(nextLogs.pending_reconcile ?? []); return nextLogs; }, [currency, filter]); @@ -126,7 +120,6 @@ export function WalletScreen() { if (cancelled) return; setBalance(b); setLogs(nextLogs); - dispatchWalletLogsRefresh(nextLogs.pending_reconcile ?? []); } catch (e) { if (!cancelled) { setError(formatWalletClientError(e, t)); @@ -169,7 +162,6 @@ export function WalletScreen() { }); if (!cancelled) { setLogs(nextLogs); - dispatchWalletLogsRefresh(nextLogs.pending_reconcile ?? []); } } catch (e) { if (!cancelled) { @@ -340,10 +332,6 @@ export function WalletScreen() { ) : null} - 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"; -}