feat: add jackpot animations and enhance currency handling across components

- Introduced new CSS animations for jackpot effects to improve visual engagement.
- Integrated CurrencySwitcher into PlayerPanel and HallScreen for better currency management.
- Updated various components to utilize active player currency for consistent display.
- Enhanced event handling for currency changes to ensure real-time updates across the application.
This commit is contained in:
2026-05-25 14:31:38 +08:00
parent 2bf44e4c29
commit 9bd7cc9b9e
37 changed files with 1030 additions and 180 deletions

View File

@@ -6,6 +6,12 @@ import {
persistPlayerBearerToken,
readPersistedPlayerBearerToken,
} from "@/lib/player-session";
import {
dispatchPlayerCurrencyChange,
persistPlayerSelectedCurrency,
readPersistedPlayerSelectedCurrency,
} from "@/lib/player-currency-preference";
import { pickActivePlayerCurrency } from "@/lib/player-currency-options";
import type { PublicCurrencyRow } from "@/types/api/currency";
import type { PlayerMeData } from "@/types/api/player-me";
@@ -23,6 +29,8 @@ type PlayerSessionState = {
bearerToken: string | null;
profile: PlayerMeData | null;
currencies: PublicCurrencyRow[];
/** 玩家在大厅选择的可下注币种localStorage 持久化) */
selectedCurrency: string | null;
phase: PlayerEntryPhase;
progress: number;
errorMessage: string | null;
@@ -32,6 +40,9 @@ type PlayerSessionState = {
clearBearerToken: () => void;
setProfile: (profile: PlayerMeData | null) => void;
setCurrencies: (currencies: PublicCurrencyRow[]) => void;
/** 根据档案与币种目录校准当前选中币种 */
reconcileSelectedCurrency: () => void;
setSelectedCurrency: (code: string) => void;
setPhase: (phase: PlayerEntryPhase) => void;
setProgress: (progress: number) => void;
setErrorMessage: (message: string | null) => void;
@@ -47,10 +58,11 @@ function initialSteps(): PlayerEntryStep[] {
];
}
export const usePlayerSessionStore = create<PlayerSessionState>((set) => ({
export const usePlayerSessionStore = create<PlayerSessionState>((set, get) => ({
bearerToken: null,
profile: null,
currencies: [],
selectedCurrency: null,
phase: "loading",
progress: 0,
errorMessage: null,
@@ -83,11 +95,46 @@ export const usePlayerSessionStore = create<PlayerSessionState>((set) => ({
clearBearerToken: () => {
setPlayerBearerToken(null);
clearPersistedPlayerBearerToken();
set({ bearerToken: null, profile: null });
set({ bearerToken: null, profile: null, selectedCurrency: null });
},
setProfile: (profile) => set({ profile }),
setCurrencies: (currencies) => set({ currencies }),
setProfile: (profile) => {
set({ profile });
get().reconcileSelectedCurrency();
},
setCurrencies: (currencies) => {
set({ currencies });
get().reconcileSelectedCurrency();
},
reconcileSelectedCurrency: () => {
const { profile, currencies } = get();
const next = pickActivePlayerCurrency(
profile,
currencies,
readPersistedPlayerSelectedCurrency(),
);
const prev = get().selectedCurrency;
if (prev === next) {
return;
}
persistPlayerSelectedCurrency(next);
set({ selectedCurrency: next });
},
setSelectedCurrency: (code) => {
const normalized = code.trim().toUpperCase();
const allowed = get().currencies.some(
(row) => row.is_bettable && row.code === normalized,
);
if (!allowed) {
return;
}
persistPlayerSelectedCurrency(normalized);
set({ selectedCurrency: normalized });
dispatchPlayerCurrencyChange(normalized);
},
setPhase: (phase) => set({ phase }),
setProgress: (progress) => set({ progress: Math.max(0, Math.min(100, progress)) }),
setErrorMessage: (errorMessage) => set({ errorMessage }),