import { motion } from 'motion/react' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import reduce from '@/assets/game/add.webp' import chipBg from '@/assets/game/chip-bg.webp' import chipLineBg from '@/assets/game/chip-line-bg.webp' import chipLock from '@/assets/game/chip-lock.webp' import confirmRedBg from '@/assets/game/confirm-red-bg.png' import controlBg from '@/assets/game/control-bg.png' import leftBottomBg from '@/assets/game/left-bg.webp' import mobileAddReduceBg from '@/assets/game/mobile-add-reduce-bg.webp' import mobileConfirmBg from '@/assets/game/mobile-contro-comfirm.webp' import mobileTotalBg from '@/assets/game/mobile-control-number.webp' import add from '@/assets/game/reduce.webp' import diamond from '@/assets/system/diamond.webp' import { SmartBackground } from '@/components/smart-background.tsx' import { SmartImage } from '@/components/smart-image.tsx' import { ACTION_OPTIONS } from '@/constants' import { useGameControlVm } from '@/hooks/use-game-control-vm.ts' import { cn } from '@/lib/utils' import { useModalStore } from '@/store' export function MobileControl() { const { t } = useTranslation() const setModalOpen = useModalStore((state) => state.setModalOpen) const { acceptingBets, actionsEnabled, canClear, canDecreaseBetQuantity, chips, confirmLabel, confirmState, isConfirmClickable, maxSelectionCountLabel, onChipSelect, onConfirm, onClearSelections, onDecreaseBetQuantity, onIncreaseBetQuantity, onOpenAutoSetting, onRepeatSelections, selectedBetQuantityLabel, selectedChipId, selectedCountLabel, totalBetAmountLabel, } = useGameControlVm() const [clickedId, setClickedId] = useState(null) const [hidingId, setHidingId] = useState(null) const [confirmClicked, setConfirmClicked] = useState(false) const clickResetTimerRef = useRef(null) const hideResetTimerRef = useRef(null) const confirmResetTimerRef = useRef(null) const isConfirmWarning = confirmState === 'insufficient' || confirmState === 'limit' useEffect(() => { return () => { if (clickResetTimerRef.current !== null) { window.clearTimeout(clickResetTimerRef.current) } if (hideResetTimerRef.current !== null) { window.clearTimeout(hideResetTimerRef.current) } if (confirmResetTimerRef.current !== null) { window.clearTimeout(confirmResetTimerRef.current) } } }, []) const handleChipClick = (chipId: string) => { if (!acceptingBets) { return } onChipSelect(chipId) } const handleActionClick = useCallback( (id: string) => { if (!actionsEnabled) { return } if (id === 'clear' && canClear) { onClearSelections() } if (id === 'repeat') { onRepeatSelections() } if (id === 'auto-spin') { onOpenAutoSetting() } setClickedId(id) if (clickResetTimerRef.current !== null) { window.clearTimeout(clickResetTimerRef.current) } clickResetTimerRef.current = window.setTimeout(() => { setClickedId(null) setHidingId(id) if (hideResetTimerRef.current !== null) { window.clearTimeout(hideResetTimerRef.current) } hideResetTimerRef.current = window.setTimeout(() => { setHidingId(null) }, 180) }, 200) }, [ actionsEnabled, canClear, onClearSelections, onOpenAutoSetting, onRepeatSelections, ], ) const handleConfirmClick = useCallback(() => { if (!isConfirmClickable) { void onConfirm() return } setConfirmClicked(true) if (confirmResetTimerRef.current !== null) { window.clearTimeout(confirmResetTimerRef.current) } confirmResetTimerRef.current = window.setTimeout(() => { setConfirmClicked(false) }, 200) void onConfirm() }, [isConfirmClickable, onConfirm]) return (
{chips.map((chip) => { const isSelected = chip.id === selectedChipId const showLockedState = !acceptingBets return ( handleChipClick(chip.id)} disabled={showLockedState} whileTap={showLockedState ? undefined : { scale: 0.94 }} transition={{ layout: { type: 'spring', stiffness: 360, damping: 26, }, duration: 0.26, }} className="relative flex h-design-32 w-design-32 shrink-0 items-center justify-center rounded-full" style={ showLockedState ? { WebkitFilter: 'grayscale(100%)', filter: 'grayscale(100%)', } : undefined } > {showLockedState && ( chip-locked )} {chip.valueLabel} ) })}
{selectedBetQuantityLabel}
setModalOpen('desktopPeriodHistory', true)} aria-label={t('gameDesktop.history.title')} className="relative z-20 flex h-full w-design-60 shrink-0 items-center justify-center bg-center bg-no-repeat px-design-6 text-center !text-design-8 font-bold leading-[1.15] text-[#D5FBFF] transition-[filter] duration-200 hover:brightness-125 focus-visible:ring-2 focus-visible:ring-[#4FEAFF]" > {t('gameDesktop.history.title')}
{t('gameDesktop.control.selected')}: {selectedCountLabel}/ {maxSelectionCountLabel}
{t('gameDesktop.control.totalBet')}: {totalBetAmountLabel}
{ACTION_OPTIONS.map(({ id, labelKey, Icon, bg }) => { const isClicked = clickedId === id const isHiding = hidingId === id const showBg = actionsEnabled && (isClicked || isHiding) return ( handleActionClick(id)} whileHover={actionsEnabled ? { y: -1, scale: 1.01 } : undefined} whileTap={actionsEnabled ? { scale: 0.96 } : undefined} className={cn( 'relative flex h-full min-w-0 flex-1 items-center justify-center overflow-hidden', actionsEnabled ? 'cursor-pointer' : 'cursor-not-allowed', id === 'auto-spin' && '-translate-x-[calc(var(--design-unit)*1.5)]', )} > {showBg && ( )}
{t(labelKey)}
) })}
{confirmClicked && ( )} {confirmLabel}
) }