- 移除 useGameBoardVm 数据层实施说明文档 - 移除核心玩法与前端规则摘要文档 - 移除游戏模块数据与界面分层第一阶段实施稿文档 - 清理与数据层重构相关的技术方案说明 - 删除关于 PC 和 Mobile 界面分离的设计规划 - 移除 view-model hooks 架构设计相关内容
230 lines
7.3 KiB
TypeScript
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
|