feat: 联调充值和提现接口
This commit is contained in:
@@ -1,17 +1,11 @@
|
||||
import { TriangleAlert } from 'lucide-react'
|
||||
import { motion } from 'motion/react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import diamondIcon from '@/assets/system/diamond.webp'
|
||||
import { SmartImage } from '@/components/smart-image'
|
||||
import { notify } from '@/lib/notify'
|
||||
import { useAnimalVm } from '@/features/game/hooks/use-animal-vm'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useAudioStore, useAuthStore, useModalStore } from '@/store'
|
||||
import {
|
||||
selectSelectionTotal,
|
||||
useGameRoundStore,
|
||||
useGameSessionStore,
|
||||
} from '@/store/game'
|
||||
|
||||
const animalModules = import.meta.glob('../../../../assets/animal/*.webp', {
|
||||
eager: true,
|
||||
@@ -30,42 +24,6 @@ const animalImageList = Object.entries(animalModules)
|
||||
.filter((item) => item.id > 0)
|
||||
.sort((left, right) => left.id - right.id)
|
||||
|
||||
function getNextMarqueeId(currentId: number | null) {
|
||||
if (animalImageList.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (animalImageList.length === 1) {
|
||||
return animalImageList[0]?.id ?? null
|
||||
}
|
||||
|
||||
let nextId = currentId
|
||||
|
||||
while (nextId === currentId) {
|
||||
nextId =
|
||||
animalImageList[Math.floor(Math.random() * animalImageList.length)]?.id ??
|
||||
currentId
|
||||
}
|
||||
|
||||
return nextId
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type CellWarningType = 'balance' | 'limit'
|
||||
|
||||
interface DesktopAnimalProps {
|
||||
activeId?: number | null
|
||||
className?: string
|
||||
@@ -82,155 +40,17 @@ export function DesktopAnimal({
|
||||
onSelect,
|
||||
}: DesktopAnimalProps) {
|
||||
const { t } = useTranslation()
|
||||
const authStatus = useAuthStore((state) => state.status)
|
||||
const currentUser = useAuthStore((state) => state.currentUser)
|
||||
const markSoundPlaybackUnlocked = useAudioStore(
|
||||
(state) => state.markSoundPlaybackUnlocked,
|
||||
)
|
||||
const setModalOpen = useModalStore((state) => state.setModalOpen)
|
||||
const activeChipId = useGameRoundStore((state) => state.activeChipId)
|
||||
const chips = useGameRoundStore((state) => state.chips)
|
||||
const clearSelections = useGameRoundStore((state) => state.clearSelections)
|
||||
const maxSelectionCount = useGameRoundStore(
|
||||
(state) => state.maxSelectionCount,
|
||||
)
|
||||
const placeBet = useGameRoundStore((state) => state.placeBet)
|
||||
const removeSelectionsForCell = useGameRoundStore(
|
||||
(state) => state.removeSelectionsForCell,
|
||||
)
|
||||
const selections = useGameRoundStore((state) => state.selections)
|
||||
const totalBetAmount = useGameRoundStore(selectSelectionTotal)
|
||||
const connection = useGameSessionStore((state) => state.connection)
|
||||
const requestRealtimeConnection = useGameSessionStore(
|
||||
(state) => state.requestRealtimeConnection,
|
||||
)
|
||||
const shouldConnectRealtime = useGameSessionStore(
|
||||
(state) => state.shouldConnectRealtime,
|
||||
)
|
||||
const [marqueeId, setMarqueeId] = useState<number | null>(() =>
|
||||
getNextMarqueeId(null),
|
||||
)
|
||||
const [cellWarning, setCellWarning] = useState<{
|
||||
cellId: number
|
||||
type: CellWarningType
|
||||
} | null>(null)
|
||||
const activeChip = useMemo(
|
||||
() => chips.find((chip) => chip.id === activeChipId) ?? chips[0] ?? null,
|
||||
[activeChipId, chips],
|
||||
)
|
||||
const balance = parseBalance(currentUser?.coin)
|
||||
const selectionByCell = useMemo(() => {
|
||||
return selections.reduce<Record<number, { amount: number; count: number }>>(
|
||||
(accumulator, selection) => {
|
||||
const current = accumulator[selection.cellId] ?? { amount: 0, count: 0 }
|
||||
|
||||
accumulator[selection.cellId] = {
|
||||
amount: current.amount + selection.amount,
|
||||
count: current.count + 1,
|
||||
}
|
||||
|
||||
return accumulator
|
||||
},
|
||||
{},
|
||||
)
|
||||
}, [selections])
|
||||
|
||||
const isRealtimeConnected = connection.status === 'connected'
|
||||
const isRealtimeConnecting =
|
||||
shouldConnectRealtime &&
|
||||
(connection.status === 'connecting' || connection.status === 'reconnecting')
|
||||
const showStandbyState = !shouldConnectRealtime || !isRealtimeConnected
|
||||
const lockInteraction = showStandbyState
|
||||
const isSelectedCell = (animalId: number) =>
|
||||
Boolean(selectionByCell[animalId])
|
||||
const selectedCellCount = Object.keys(selectionByCell).length
|
||||
|
||||
const handleStart = () => {
|
||||
if (authStatus !== 'authenticated') {
|
||||
notify.warning(t('commonUi.toast.loginRequired'))
|
||||
setModalOpen('desktopLogin', true)
|
||||
return
|
||||
}
|
||||
|
||||
clearSelections()
|
||||
markSoundPlaybackUnlocked()
|
||||
requestRealtimeConnection()
|
||||
}
|
||||
|
||||
const handleSelect = (animalId: number) => {
|
||||
if (showStandbyState) {
|
||||
return
|
||||
}
|
||||
|
||||
if (onSelect) {
|
||||
onSelect(animalId)
|
||||
return
|
||||
}
|
||||
|
||||
if (isSelectedCell(animalId)) {
|
||||
removeSelectionsForCell(animalId)
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedCellCount >= maxSelectionCount) {
|
||||
setCellWarning({
|
||||
cellId: animalId,
|
||||
type: 'limit',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (totalBetAmount + (activeChip?.amount ?? 0) > balance) {
|
||||
setCellWarning({
|
||||
cellId: animalId,
|
||||
type: 'balance',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
placeBet(animalId)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (cellWarning === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const timerId = window.setTimeout(() => {
|
||||
setCellWarning((currentWarning) =>
|
||||
currentWarning?.cellId === cellWarning.cellId &&
|
||||
currentWarning.type === cellWarning.type
|
||||
? null
|
||||
: currentWarning,
|
||||
)
|
||||
}, 1200)
|
||||
|
||||
return () => {
|
||||
window.clearTimeout(timerId)
|
||||
}
|
||||
}, [cellWarning])
|
||||
|
||||
useEffect(() => {
|
||||
if (!showStandbyState) {
|
||||
setMarqueeId(null)
|
||||
return
|
||||
}
|
||||
|
||||
setMarqueeId((currentId) => getNextMarqueeId(currentId))
|
||||
|
||||
let timerId = 0
|
||||
|
||||
const loop = () => {
|
||||
setMarqueeId((currentId) => getNextMarqueeId(currentId))
|
||||
timerId = window.setTimeout(loop, 180 + Math.floor(Math.random() * 220))
|
||||
}
|
||||
|
||||
timerId = window.setTimeout(loop, 220)
|
||||
|
||||
return () => {
|
||||
window.clearTimeout(timerId)
|
||||
}
|
||||
}, [showStandbyState])
|
||||
const animalIds = useMemo(() => animalImageList.map((item) => item.id), [])
|
||||
const {
|
||||
cellWarning,
|
||||
handleSelect,
|
||||
handleStart,
|
||||
isRealtimeConnecting,
|
||||
lockInteraction,
|
||||
marqueeId,
|
||||
selectionByCell,
|
||||
showStandbyState,
|
||||
} = useAnimalVm(animalIds, onSelect)
|
||||
|
||||
return (
|
||||
<section
|
||||
|
||||
Reference in New Issue
Block a user