feat: 增强开奖 API 的币种支持并优化钱包处理逻辑

更新 getDrawCurrent、getDrawResults 与 getDrawResultByNo 方法,新增币种参数支持,以适配玩家币种偏好。
优化 HallBettingGrid 及相关组件:支持币种切换时自动刷新钱包数据。
重构钱包处理逻辑,简化余额更新流程并提升用户体验。
新增会话过期相关多语言提示文案,并优化现有翻译内容,提升多语言环境下的提示清晰度。
This commit is contained in:
2026-05-27 16:52:12 +08:00
parent adae4a0be1
commit 58afa8e844
34 changed files with 373 additions and 379 deletions

View File

@@ -68,6 +68,15 @@ function initialSteps(): EntryStep[] {
];
}
function stripSearchParamFromBrowserUrl(name: string): void {
if (typeof window === "undefined") return;
const url = new URL(window.location.href);
if (!url.searchParams.has(name)) return;
url.searchParams.delete(name);
const next = `${url.pathname}${url.search}${url.hash}`;
window.history.replaceState(null, "", next);
}
function shouldRetryEntryRequest(error: unknown): boolean {
if (error instanceof LotteryApiBizError) {
return false;
@@ -93,12 +102,22 @@ export function EntryGate() {
const { t: tc } = useTranslation("common");
const tokenFromUrl = searchParams.get("token") ?? "";
const sessionExpired = searchParams.get("session") === "expired";
const { bearerToken, setBearerToken, setProfile, setCurrencies, clearBearerToken } =
usePlayerSessionStore();
const [phase, setPhase] = useState<Phase>("loading");
const [failureDetails, setFailureDetails] = useState<FailureRow[]>([]);
const [phase, setPhase] = useState<Phase>(sessionExpired ? "failed" : "loading");
const [failureDetails, setFailureDetails] = useState<FailureRow[]>(() =>
sessionExpired
? [
{
code: "SESSION_EXPIRED",
detailKey: "errors.sessionExpiredDetail",
},
]
: [],
);
const [steps, setSteps] = useState<EntryStep[]>(initialSteps());
const effectiveToken = tokenFromUrl || bearerToken;
@@ -113,12 +132,6 @@ export function EntryGate() {
return Math.round(((doneCount + inProgressCount * 0.5) / steps.length) * 100);
}, [steps]);
const handleRetry = useCallback(() => {
setPhase("loading");
setFailureDetails([]);
setSteps(initialSteps());
}, []);
const doEntry = useCallback(async () => {
if (!effectiveToken) {
setPhase("failed");
@@ -128,6 +141,7 @@ export function EntryGate() {
if (tokenFromUrl) {
setBearerToken(tokenFromUrl);
stripSearchParamFromBrowserUrl("token");
}
setSteps((prev) =>
@@ -231,12 +245,26 @@ export function EntryGate() {
t,
]);
const handleRetry = useCallback(() => {
setPhase("loading");
setFailureDetails([]);
setSteps(initialSteps());
void doEntry();
}, [doEntry]);
useEffect(() => {
if (!sessionExpired) return;
clearBearerToken();
stripSearchParamFromBrowserUrl("session");
}, [sessionExpired, clearBearerToken]);
useEffect(() => {
if (sessionExpired) return;
const tmr = window.setTimeout(() => {
void doEntry();
}, 300);
return () => window.clearTimeout(tmr);
}, [doEntry]);
}, [doEntry, sessionExpired]);
return (
<div className="relative flex min-h-dvh flex-col bg-white">