feat: 接入公开币种目录并统一多币种金额与语言初始化处理

This commit is contained in:
2026-05-21 15:14:00 +08:00
parent 626914feb6
commit 6b18e25766
27 changed files with 277 additions and 94 deletions

View File

@@ -21,11 +21,14 @@ import {
ticketNumberSpec,
} from "@/features/hall/hall-bet-rules";
import type { HallDrawLiveSnapshot } from "@/features/hall/use-hall-draw-live";
import { useCurrencyCatalog } from "@/hooks/use-currency-catalog";
import { triggerWalletPollingAfterBet } from "@/hooks/use-wallet-polling";
import { getLotteryEcho } from "@/lib/lottery-echo";
import { getLotteryRequestLocale } from "@/lib/lottery-locale";
import { formatMinorAsCurrency, parseDecimalInputToMinor } from "@/lib/money";
import { resolvePlayerCurrency } from "@/lib/player-currency";
import { cn } from "@/lib/utils";
import { usePlayerSessionStore } from "@/stores/player-session-store";
import { LotteryApiBizError } from "@/types/api/errors";
import type { PlayEffectivePayload, PlayEffectivePlayRow } from "@/types/api/play-effective";
import type { TicketLineInput, TicketPlaceData, TicketPreviewData } from "@/types/api/ticket";
@@ -391,6 +394,7 @@ function quickFillKeys(category: HallCategory): { favorites: string; history: st
export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }) {
const { display, isBettable, reload: reloadDraw } = drawLive;
const { t } = useTranslation("player");
const profile = usePlayerSessionStore((s) => s.profile);
const [activeCategory, setActiveCategory] = useState<HallCategory>("D2");
const [rows, setRows] = useState<DraftRow[]>(() => [newDraftRow()]);
@@ -414,18 +418,14 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
number: null,
longPress: false,
});
useCurrencyCatalog();
const currencyParam = useMemo(() => {
const fromEnv = process.env.NEXT_PUBLIC_LOTTERY_PLAY_CURRENCY?.trim();
return fromEnv !== undefined && fromEnv !== "" ? fromEnv : undefined;
}, []);
const currencyParam = useMemo(() => resolvePlayerCurrency(profile), [profile]);
const loadCatalog = useCallback(async () => {
setCatalogState((s) => (s.kind === "ok" ? s : { kind: "loading" }));
try {
const data = await getPlayEffective(
currencyParam !== undefined ? { currency: currencyParam } : undefined,
);
const data = await getPlayEffective({ currency: currencyParam });
setCatalogState({ kind: "ok", data });
} catch (e) {
const msg = e instanceof LotteryApiBizError ? e.message : t("hall.loadingError");
@@ -435,9 +435,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
const refreshWallet = useCallback(async () => {
try {
const wallet = await getWalletBalance(
currencyParam !== undefined ? { currency: currencyParam } : undefined,
);
const wallet = await getWalletBalance({ currency: currencyParam });
setAvailableMinor(Number(wallet.available_balance ?? 0));
} catch {
setAvailableMinor(0);
@@ -470,7 +468,8 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
);
}, [activeCategory, catalogState]);
const currencyCode = catalogState.kind === "ok" ? catalogState.data.currency_code : "NPR";
const currencyCode =
catalogState.kind === "ok" ? catalogState.data.currency_code : currencyParam;
const categoryPlays = useMemo(() => {
if (catalogState.kind !== "ok") return [];
@@ -656,7 +655,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
const entries: DraftEntry[] = [];
rows.forEach((row, rowIndex) => {
playColumns.forEach((column) => {
const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "");
const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode);
if (amount === null || amount <= 0) return;
const line = lineForPlay(activeCategory, column.play, row.number, amount, column.digitSlot);
if (!line) return;
@@ -673,7 +672,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
});
});
return entries;
}, [activeCategory, playColumns, rows]);
}, [activeCategory, currencyCode, playColumns, rows]);
const draftEntries = collectEntries();
const draftSummary = useMemo(() => {

View File

@@ -22,7 +22,9 @@ import {
TableRow,
} from "@/components/ui/table";
import { getLotteryRequestLocale } from "@/lib/lottery-locale";
import { resolvePlayerCurrency } from "@/lib/player-currency";
import { cn } from "@/lib/utils";
import { usePlayerSessionStore } from "@/stores/player-session-store";
import { LotteryApiBizError } from "@/types/api/errors";
import type {
PlayEffectivePayload,
@@ -77,19 +79,15 @@ function formatMoneyAmount(n: number): string {
}
export function HallPlayCatalogPanel() {
const profile = usePlayerSessionStore((s) => s.profile);
const { t } = useTranslation("player");
const [state, setState] = useState<LoadState>({ kind: "loading" });
const currencyParam = useMemo(() => {
const fromEnv = process.env.NEXT_PUBLIC_LOTTERY_PLAY_CURRENCY?.trim();
return fromEnv !== undefined && fromEnv !== "" ? fromEnv : undefined;
}, []);
const currencyParam = useMemo(() => resolvePlayerCurrency(profile), [profile]);
const load = useCallback(async () => {
setState((s) => (s.kind === "ok" ? s : { kind: "loading" }));
try {
const data = await getPlayEffective(
currencyParam !== undefined ? { currency: currencyParam } : undefined,
);
const data = await getPlayEffective({ currency: currencyParam });
setState({ kind: "ok", data });
} catch (e) {
if (e instanceof LotteryApiBizError && e.code === 9004) {

View File

@@ -12,6 +12,7 @@ import {
TransferOutDialog,
} from "@/features/wallet/wallet-transfer-dialogs";
import { formatMinorAsCurrency } from "@/lib/money";
import { resolvePlayerCurrency } from "@/lib/player-currency";
import { cn } from "@/lib/utils";
import { useNetworkConnectionStore } from "@/stores/network-connection-store";
import { usePlayerSessionStore } from "@/stores/player-session-store";
@@ -26,9 +27,8 @@ export function HallWalletStrip() {
const degradedWalletPollRef = useRef<number | null>(null);
const currency = useMemo(
() =>
(balance?.currency_code ?? profile?.default_currency ?? "NPR").toUpperCase(),
[balance?.currency_code, profile?.default_currency],
() => resolvePlayerCurrency(profile, balance),
[balance, profile],
);
const refresh = useCallback(async () => {