feat: 增加 Jackpot 爆池实时弹层与奖池信息展示
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
} from "@/features/hall/hall-bet-rules";
|
||||
import type { HallDrawLiveSnapshot } from "@/features/hall/use-hall-draw-live";
|
||||
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 { cn } from "@/lib/utils";
|
||||
@@ -63,6 +64,16 @@ type ClosedPlayCleanupData = {
|
||||
cleanup_lines?: Array<{ client_line_no?: number; play_code?: string }>;
|
||||
};
|
||||
|
||||
type PlayToggleWsEvent = {
|
||||
play_code?: string;
|
||||
enabled?: boolean;
|
||||
action?: string;
|
||||
};
|
||||
|
||||
type OddsUpdateWsEvent = {
|
||||
message?: string;
|
||||
};
|
||||
|
||||
type CellRiskState = "open" | "warning" | "sold_out";
|
||||
type QuickFillState = Record<HallCategory, { favorites: string[]; history: string[] }>;
|
||||
|
||||
@@ -482,6 +493,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
);
|
||||
|
||||
const alertRows = display?.risk_pool_alerts ?? [];
|
||||
const jackpot = display?.jackpot;
|
||||
const currentQuickFill = quickFillState[activeCategory] ?? { favorites: [], history: [] };
|
||||
const favorites = currentQuickFill.favorites;
|
||||
const historyNumbers = currentQuickFill.history;
|
||||
@@ -577,6 +589,79 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
});
|
||||
};
|
||||
|
||||
const clearAmountsForPlay = useCallback((playCode: string): boolean => {
|
||||
let removed = false;
|
||||
setRows((current) =>
|
||||
current.map((row) => {
|
||||
const nextAmounts = { ...row.amounts };
|
||||
let changed = false;
|
||||
Object.keys(nextAmounts).forEach((amountKey) => {
|
||||
const keyPlayCode = amountKey.split("@")[0];
|
||||
if (keyPlayCode !== playCode || !nextAmounts[amountKey]) return;
|
||||
nextAmounts[amountKey] = "";
|
||||
changed = true;
|
||||
removed = true;
|
||||
});
|
||||
return changed ? { ...row, amounts: nextAmounts } : row;
|
||||
}),
|
||||
);
|
||||
|
||||
return removed;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const echo = getLotteryEcho();
|
||||
if (!echo) return;
|
||||
|
||||
const channel = echo.channel("lottery-hall");
|
||||
|
||||
const onPlayToggle = (evt: PlayToggleWsEvent) => {
|
||||
void loadCatalog();
|
||||
if (evt.enabled === false && typeof evt.play_code === "string") {
|
||||
const removed = clearAmountsForPlay(evt.play_code);
|
||||
setPreviewOpen(false);
|
||||
setPreviewData(null);
|
||||
toast.warning(
|
||||
removed
|
||||
? t("hall.playConfig.playClosedDraftCleared", {
|
||||
playCode: evt.play_code,
|
||||
defaultValue: "{{playCode}} 已关闭,相关草稿金额已清除。",
|
||||
})
|
||||
: t("hall.playConfig.playClosed", {
|
||||
playCode: evt.play_code,
|
||||
defaultValue: "{{playCode}} 已关闭。",
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
toast.message(
|
||||
t("hall.playConfig.updated", {
|
||||
defaultValue: "玩法配置已更新,已刷新下注表格。",
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onOddsUpdate = (evt: OddsUpdateWsEvent) => {
|
||||
void loadCatalog();
|
||||
setPreviewOpen(false);
|
||||
setPreviewData(null);
|
||||
toast.message(
|
||||
evt.message ??
|
||||
t("hall.playConfig.oddsUpdated", {
|
||||
defaultValue: "赔率已更新,请重新预览注单。",
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
channel.listen(".play.toggle", onPlayToggle);
|
||||
channel.listen(".odds.update", onOddsUpdate);
|
||||
|
||||
return () => {
|
||||
channel.stopListening(".play.toggle");
|
||||
channel.stopListening(".odds.update");
|
||||
};
|
||||
}, [clearAmountsForPlay, loadCatalog, t]);
|
||||
|
||||
const collectEntries = useCallback((): DraftEntry[] => {
|
||||
if (activeCategory === "JACKPOT") return [];
|
||||
const entries: DraftEntry[] = [];
|
||||
@@ -847,6 +932,29 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{jackpot?.enabled ? (
|
||||
<div className="rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 via-white to-[#f8fbff] px-4 py-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<div>
|
||||
<p className="text-[11px] font-black uppercase tracking-normal text-amber-700">
|
||||
{t("results.jackpotLabel", { defaultValue: "Jackpot" })}
|
||||
</p>
|
||||
<p className="font-mono text-xl font-black tabular-nums text-[#07459f]">
|
||||
{formatMinorAsCurrency(jackpot.current_amount_minor, jackpot.currency_code)}
|
||||
</p>
|
||||
</div>
|
||||
{jackpot.draws_since_last_burst !== null ? (
|
||||
<p className="rounded-full bg-amber-100 px-3 py-1 text-xs font-bold text-amber-800">
|
||||
{t("results.jackpotGap", {
|
||||
count: jackpot.draws_since_last_burst,
|
||||
defaultValue: "距上次爆池 {{count}} 期",
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{activeCategory === "JACKPOT" ? (
|
||||
<div className="rounded-xl border border-[#edf1f7] bg-[#f7f9fc] p-7 text-center text-slate-500">
|
||||
<div className="mx-auto flex size-14 items-center justify-center rounded-full bg-slate-200 text-slate-600">
|
||||
|
||||
Reference in New Issue
Block a user