feat: enhance API error handling and UI improvements
Some checks failed
lotteryfront CI / build (push) Has been cancelled
Some checks failed
lotteryfront CI / build (push) Has been cancelled
- Updated `getPublicCurrencies` and `getPlayerAuthCaptcha` functions to include `skipGlobalServerError` option, improving error handling for API requests. - Refactored the NotFoundPage component to enhance mobile responsiveness and UI consistency, integrating `PlayerMobileViewport`. - Improved the NetworkStatusBanner and OfflineBanner components by adding player banner height reference for better layout management. - Updated Wallet components to utilize `PlayerMoneyDisplay` for consistent currency representation and added credit mode handling in various screens. - Enhanced the WalletScreen and Transfer screens with loading panels and credit guard logic for better user experience during wallet operations.
This commit is contained in:
@@ -13,6 +13,8 @@ import {
|
||||
TransferInDialog,
|
||||
TransferOutDialog,
|
||||
} from "@/features/wallet/wallet-transfer-dialogs";
|
||||
import { WalletPendingReconcileBanner } from "@/features/wallet/wallet-pending-reconcile-banner";
|
||||
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";
|
||||
@@ -36,8 +38,8 @@ export function WalletScreen() {
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const loadMoreRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const fetchPassRef = useRef(true);
|
||||
const filterInitializedRef = useRef(false);
|
||||
const prevFilterRef = useRef("");
|
||||
|
||||
const loadLogs = useCallback(async (targetPage = 1, append = false) => {
|
||||
const nextLogs = await getWalletLogs({
|
||||
@@ -60,21 +62,21 @@ export function WalletScreen() {
|
||||
|
||||
void (async () => {
|
||||
setError(null);
|
||||
if (fetchPassRef.current) {
|
||||
setLoading(true);
|
||||
fetchPassRef.current = false;
|
||||
} else {
|
||||
setLogsLoading(true);
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
// 并行请求余额和日志,避免瀑布式串行等待
|
||||
const [b, nextLogs] = await Promise.all([
|
||||
getWalletBalance({ currency }),
|
||||
loadLogs(1, false),
|
||||
getWalletLogs({
|
||||
page: 1,
|
||||
size: WALLET_LOGS_PAGE_SIZE,
|
||||
type: filter || undefined,
|
||||
currency,
|
||||
}),
|
||||
]);
|
||||
if (cancelled) return;
|
||||
setBalance(b);
|
||||
setLogs(nextLogs);
|
||||
dispatchWalletLogsRefresh(nextLogs.pending_reconcile ?? []);
|
||||
} catch (e) {
|
||||
if (!cancelled) {
|
||||
setError(formatWalletClientError(e, t));
|
||||
@@ -90,7 +92,50 @@ export function WalletScreen() {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [currency, loadLogs, t]);
|
||||
}, [currency, t]); // eslint-disable-line react-hooks/exhaustive-deps -- 币种切换整页刷新;流水筛选见下方 effect
|
||||
|
||||
useEffect(() => {
|
||||
if (!filterInitializedRef.current) {
|
||||
filterInitializedRef.current = true;
|
||||
prevFilterRef.current = filter;
|
||||
return;
|
||||
}
|
||||
if (prevFilterRef.current === filter) {
|
||||
return;
|
||||
}
|
||||
prevFilterRef.current = filter;
|
||||
|
||||
let cancelled = false;
|
||||
setError(null);
|
||||
setLogsLoading(true);
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
const nextLogs = await getWalletLogs({
|
||||
page: 1,
|
||||
size: WALLET_LOGS_PAGE_SIZE,
|
||||
type: filter || undefined,
|
||||
currency,
|
||||
});
|
||||
if (!cancelled) {
|
||||
setLogs(nextLogs);
|
||||
dispatchWalletLogsRefresh(nextLogs.pending_reconcile ?? []);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!cancelled) {
|
||||
setError(formatWalletClientError(e, t));
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setLogsLoading(false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [currency, filter, t]);
|
||||
|
||||
const refreshAll = useCallback(async () => {
|
||||
setError(null);
|
||||
@@ -181,7 +226,7 @@ export function WalletScreen() {
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="relative flex items-center gap-3">
|
||||
<div className="flex size-13 shrink-0 items-center justify-center rounded-full bg-white text-[#d81435] shadow-sm">
|
||||
<div className="flex size-14 shrink-0 items-center justify-center rounded-full bg-white text-[#d81435] shadow-sm">
|
||||
<Wallet className="size-7" aria-hidden />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -193,9 +238,11 @@ export function WalletScreen() {
|
||||
{loading ? (
|
||||
<Skeleton className="mt-2 h-8 w-44 rounded-md bg-white/25" />
|
||||
) : (
|
||||
<p className="mt-1 text-2xl font-black leading-none tabular-nums tracking-normal">
|
||||
{formatMinorAsCurrency(displayMinor, currency)}
|
||||
</p>
|
||||
<PlayerMoneyDisplay
|
||||
amountMinor={displayMinor}
|
||||
currency={currency}
|
||||
className="mt-1 text-white"
|
||||
/>
|
||||
)}
|
||||
<p className="mt-2 text-xs text-white/75">
|
||||
{isCreditPlayer
|
||||
@@ -220,7 +267,9 @@ export function WalletScreen() {
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<>
|
||||
<WalletPendingReconcileBanner />
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<TransferInDialog
|
||||
idPrefix="wallet-"
|
||||
currency={currency}
|
||||
@@ -245,6 +294,7 @@ export function WalletScreen() {
|
||||
triggerClassName="h-14 rounded-2xl text-base font-black"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<WalletLogsBlock
|
||||
|
||||
Reference in New Issue
Block a user