Files
36-character-flower/src/modal/desktop/desktop-auto-setting-modal.tsx
JiaJun bfb4b76611 refactor(game): 重构项目结构,优化链路, 移动端适配
- 移除 useGameBoardVm 数据层实施说明文档
- 移除核心玩法与前端规则摘要文档
- 移除游戏模块数据与界面分层第一阶段实施稿文档
- 清理与数据层重构相关的技术方案说明
- 删除关于 PC 和 Mobile 界面分离的设计规划
- 移除 view-model hooks 架构设计相关内容
2026-06-03 17:21:13 +08:00

230 lines
7.3 KiB
TypeScript

import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartBackground } from '@/components/smart-background.tsx'
import { Input } from '@/components/ui/input.tsx'
import { Switch } from '@/components/ui/switch.tsx'
import { AUTO_HOSTING_DEFAULT_SINGLE_WIN_THRESHOLD } from '@/constants'
import { notify } from '@/lib/notify'
import { useModalStore } from '@/store'
import { useAuthStore } from '@/store/auth'
import {
type AutoHostingStopRules,
selectSelectionTotal,
useGameAutoHostingStore,
useGameRoundStore,
useGameSessionStore,
} from '@/store/game'
function parseAmount(value: string) {
const parsed = Number(value)
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0
}
function parseBalance(value: string | number | null | undefined) {
if (typeof value === 'number') {
return Number.isFinite(value) ? value : 0
}
if (typeof value !== 'string') {
return 0
}
const parsed = Number(value)
return Number.isFinite(parsed) ? parsed : 0
}
function DesktopAutoSettingModal() {
const { t } = useTranslation()
const open = useModalStore((state) => state.modals.desktopAutoSetting)
const setModalOpen = useModalStore((state) => state.setModalOpen)
const currentUser = useAuthStore((state) => state.currentUser)
const round = useGameRoundStore((state) => state.round)
const selections = useGameRoundStore((state) => state.selections)
const totalBetAmount = useGameRoundStore(selectSelectionTotal)
const tableLimitMax = useGameSessionStore(
(state) => state.dashboard.tableLimitMax,
)
const startHosting = useGameAutoHostingStore((state) => state.startHosting)
const [balanceLimitEnabled, setBalanceLimitEnabled] = useState(false)
const [balanceLimitValue, setBalanceLimitValue] = useState('0')
const [singleWinLimitEnabled, setSingleWinLimitEnabled] = useState(false)
const [singleWinLimitValue, setSingleWinLimitValue] = useState(
String(AUTO_HOSTING_DEFAULT_SINGLE_WIN_THRESHOLD),
)
const [jackpotStopEnabled, setJackpotStopEnabled] = useState(false)
function handleClose() {
setModalOpen('desktopAutoSetting', false)
}
function handleSubmit() {
if (round.phase !== 'betting' || !round.id) {
notify.warning(t('commonUi.toast.betUnavailable'))
handleClose()
return
}
if (selections.length === 0) {
notify.warning(t('commonUi.toast.selectNumbersBeforeAutoHosting'))
handleClose()
return
}
const balance = parseBalance(currentUser?.coin)
if (tableLimitMax > 0 && totalBetAmount > tableLimitMax) {
notify.warning(t('commonUi.toast.betLimitExceeded'))
return
}
if (totalBetAmount > balance) {
notify.warning(t('commonUi.toast.insufficientBalance'))
return
}
const rules: AutoHostingStopRules = {
stopIfBalanceBelow: {
amount: parseAmount(balanceLimitValue),
enabled: balanceLimitEnabled,
},
stopIfSingleWinAbove: {
amount: parseAmount(singleWinLimitValue),
enabled: singleWinLimitEnabled,
},
stopOnJackpot: jackpotStopEnabled,
}
startHosting({
balanceAfterBet: balance,
rules,
selections,
})
notify.success(t('commonUi.toast.autoHostingStarted'))
handleClose()
}
return (
<CenterModal
open={open}
onClose={handleClose}
title={
<div className={'modal-title-glow text-design-26 uppercase'}>
{t('game.modals.autoSetting.title')}
</div>
}
isNormalBg={true}
titleAlign="left"
className={'w-design-835 !h-design-500'}
>
<div
className={
'flex h-full w-full flex-col justify-between px-design-18 pt-design-30 pb-design-60'
}
>
<div className={'flex w-full flex-col gap-design-26'}>
<div className={'flex items-center justify-between gap-design-30'}>
<div
className={
'w-design-300 shrink-0 text-design-22 leading-[1.1] text-[#9CF7FF]'
}
>
{t('game.modals.autoSetting.rows.stopIfBalanceLowerThan')}
</div>
<div
className={
'game-setting-input-shell flex h-design-58 w-design-410 items-center justify-between pl-design-18 pr-design-10'
}
>
<Input
value={balanceLimitValue}
inputMode="decimal"
onChange={(event) => setBalanceLimitValue(event.target.value)}
className={
'game-setting-input h-full w-design-280 text-design-18'
}
/>
<Switch
size={'sm'}
checked={balanceLimitEnabled}
onCheckedChange={setBalanceLimitEnabled}
/>
</div>
</div>
<div className={'flex items-center justify-between gap-design-30'}>
<div
className={
'w-design-300 shrink-0 text-design-22 leading-[1.1] text-[#9CF7FF]'
}
>
{t('game.modals.autoSetting.rows.stopIfSingleWinExceeds')}
</div>
<div
className={
'game-setting-input-shell flex h-design-58 w-design-410 items-center justify-between pl-design-18 pr-design-10'
}
>
<Input
value={singleWinLimitValue}
inputMode="decimal"
onChange={(event) => setSingleWinLimitValue(event.target.value)}
className={
'game-setting-input h-full w-design-280 text-design-18'
}
/>
<Switch
size={'sm'}
checked={singleWinLimitEnabled}
onCheckedChange={setSingleWinLimitEnabled}
/>
</div>
</div>
<div className={'flex items-center justify-between gap-design-30'}>
<div
className={
'w-design-300 shrink-0 text-design-22 leading-[1.1] text-[#9CF7FF]'
}
>
{t('game.modals.autoSetting.rows.stopOnAnyJackpot')}
</div>
<div className={'flex w-design-410 justify-end pr-design-2'}>
<Switch
size={'sm'}
checked={jackpotStopEnabled}
onCheckedChange={setJackpotStopEnabled}
/>
</div>
</div>
</div>
<div className={'flex w-full justify-center'}>
<SmartBackground
as="button"
src={lengthBlueBtn}
size="100% 100%"
repeat="no-repeat"
position="center"
type="button"
onClick={handleSubmit}
className={
'w-design-300 h-design-72 pb-design-4 flex cursor-pointer items-center justify-center text-design-24 font-bold tracking-wide text-[#E7FBFF] transition-transform hover:-translate-y-[1px] active:translate-y-0'
}
>
{t('game.modals.autoSetting.startAutoSpin')}
</SmartBackground>
</div>
</div>
</CenterModal>
)
}
export default DesktopAutoSettingModal