- 新增 /api 重写代理,支持 LOTTERY_API_PROXY_TARGET 配置 - 玩家首页切换为 EntryGate,并移除 layout 对 PlayerAppShell 的包裹 - 请求层拆分语言头与玩家鉴权注入逻辑,引入 zustand 依赖 - 允许提交 .env.example 供本地配置参考
102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { create } from "zustand";
|
|
|
|
import { setPlayerBearerToken } from "@/lib/lottery-auth";
|
|
import {
|
|
clearPersistedPlayerBearerToken,
|
|
persistPlayerBearerToken,
|
|
readPersistedPlayerBearerToken,
|
|
} from "@/lib/player-session";
|
|
import type { PlayerMeData } from "@/types/api/player-me";
|
|
|
|
export type PlayerEntryPhase = "loading" | "error" | "success";
|
|
export type PlayerEntryStepId = "token" | "account" | "hall";
|
|
export type PlayerEntryStepStatus = "pending" | "active" | "done";
|
|
|
|
export type PlayerEntryStep = {
|
|
id: PlayerEntryStepId;
|
|
label: string;
|
|
status: PlayerEntryStepStatus;
|
|
};
|
|
|
|
type PlayerSessionState = {
|
|
bearerToken: string | null;
|
|
profile: PlayerMeData | null;
|
|
phase: PlayerEntryPhase;
|
|
progress: number;
|
|
errorMessage: string | null;
|
|
steps: PlayerEntryStep[];
|
|
setBearerToken: (token: string | null) => void;
|
|
restoreBearerToken: () => string | null;
|
|
clearBearerToken: () => void;
|
|
setProfile: (profile: PlayerMeData | null) => void;
|
|
setPhase: (phase: PlayerEntryPhase) => void;
|
|
setProgress: (progress: number) => void;
|
|
setErrorMessage: (message: string | null) => void;
|
|
updateStep: (id: PlayerEntryStepId, status: PlayerEntryStepStatus) => void;
|
|
resetEntryFlow: () => void;
|
|
};
|
|
|
|
function initialSteps(): PlayerEntryStep[] {
|
|
return [
|
|
{ id: "token", label: "校验登录凭证", status: "active" },
|
|
{ id: "account", label: "同步玩家账号", status: "pending" },
|
|
{ id: "hall", label: "加载彩票大厅", status: "pending" },
|
|
];
|
|
}
|
|
|
|
export const usePlayerSessionStore = create<PlayerSessionState>((set) => ({
|
|
bearerToken: null,
|
|
profile: null,
|
|
phase: "loading",
|
|
progress: 0,
|
|
errorMessage: null,
|
|
steps: initialSteps(),
|
|
|
|
setBearerToken: (token) => {
|
|
const normalized = token?.trim() || null;
|
|
setPlayerBearerToken(normalized);
|
|
if (normalized) {
|
|
persistPlayerBearerToken(normalized);
|
|
} else {
|
|
clearPersistedPlayerBearerToken();
|
|
}
|
|
set({ bearerToken: normalized });
|
|
},
|
|
|
|
restoreBearerToken: () => {
|
|
const raw = readPersistedPlayerBearerToken();
|
|
const normalized = raw?.trim() || null;
|
|
if (normalized) {
|
|
setPlayerBearerToken(normalized);
|
|
set({ bearerToken: normalized });
|
|
return normalized;
|
|
}
|
|
setPlayerBearerToken(null);
|
|
set({ bearerToken: null });
|
|
return null;
|
|
},
|
|
|
|
clearBearerToken: () => {
|
|
setPlayerBearerToken(null);
|
|
clearPersistedPlayerBearerToken();
|
|
set({ bearerToken: null, profile: null });
|
|
},
|
|
|
|
setProfile: (profile) => set({ profile }),
|
|
setPhase: (phase) => set({ phase }),
|
|
setProgress: (progress) => set({ progress: Math.max(0, Math.min(100, progress)) }),
|
|
setErrorMessage: (errorMessage) => set({ errorMessage }),
|
|
updateStep: (id, status) =>
|
|
set((state) => ({
|
|
steps: state.steps.map((step) =>
|
|
step.id === id ? { ...step, status } : step,
|
|
),
|
|
})),
|
|
resetEntryFlow: () =>
|
|
set({
|
|
phase: "loading",
|
|
progress: 0,
|
|
errorMessage: null,
|
|
steps: initialSteps(),
|
|
}),
|
|
})); |