refactor: update environment configuration and enhance notification handling

- Refactored ecosystem configuration to utilize .env for sensitive variables, improving security and flexibility.
- Replaced direct notification button implementation with a dedicated PlayerNotificationBell component for better code organization.
- Updated various screens to integrate the new notification component and adjusted prefetch settings for links to optimize performance.
- Added new translations for notifications and wallet logs to enhance user experience across multiple languages.
This commit is contained in:
2026-06-01 09:29:02 +08:00
parent 9f43d07778
commit 10bee1b857
16 changed files with 241 additions and 59 deletions

View File

@@ -17,6 +17,7 @@ import { useTranslation } from "react-i18next";
import { getPublicCurrencies } from "@/api/currency";
import { getPlayerMe, getPlayerPing } from "@/api/player";
import { isInIframe } from "@/components/iframe-bridge";
import { LanguageSwitcher } from "@/components/language-switcher";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
@@ -25,6 +26,8 @@ import { LotteryApiBizError } from "@/types/api/errors";
const RETRY_ATTEMPTS = 3;
const RETRY_DELAY_MS = 2000;
/** 嵌入主站 iframe 时,等待父页 postMessage 下发 token 的最长时间 */
const IFRAME_TOKEN_WAIT_MS = 15_000;
const MAIN_SITE_URL = process.env.NEXT_PUBLIC_MAIN_SITE_URL?.trim() ?? "";
@@ -134,11 +137,18 @@ export function EntryGate() {
const doEntry = useCallback(async () => {
if (!effectiveToken) {
// 主站 iframetoken 由 MAIN_INIT_TOKEN 稍后到达,勿先闪「授权失败」
if (typeof window !== "undefined" && isInIframe() && !tokenFromUrl) {
return;
}
setPhase("failed");
setFailureDetails([{ code: "NO_TOKEN", detailKey: "errors.noTokenDetail" }]);
return;
}
setPhase("loading");
setFailureDetails([]);
if (tokenFromUrl) {
setBearerToken(tokenFromUrl);
stripSearchParamFromBrowserUrl("token");
@@ -260,11 +270,32 @@ export function EntryGate() {
useEffect(() => {
if (sessionExpired) return;
const embedded = typeof window !== "undefined" && isInIframe() && !tokenFromUrl;
if (embedded && !effectiveToken) {
setPhase("loading");
return;
}
const tmr = window.setTimeout(() => {
void doEntry();
}, 300);
return () => window.clearTimeout(tmr);
}, [doEntry, sessionExpired]);
}, [doEntry, sessionExpired, effectiveToken, tokenFromUrl]);
useEffect(() => {
if (sessionExpired) return;
if (tokenFromUrl || effectiveToken) return;
if (typeof window === "undefined" || !isInIframe()) return;
const tmr = window.setTimeout(() => {
if (usePlayerSessionStore.getState().bearerToken?.trim()) return;
setFailureDetails([{ code: "NO_TOKEN", detailKey: "errors.noTokenDetail" }]);
setPhase("failed");
}, IFRAME_TOKEN_WAIT_MS);
return () => window.clearTimeout(tmr);
}, [sessionExpired, effectiveToken, tokenFromUrl]);
return (
<div className="relative flex min-h-dvh flex-col bg-white">