- 将类型定义从各个模块统一到 type 文件中进行管理 - 移除 auth-session 中不再使用的 AuthSessionInput 和 AuthUser 类型导入 - 移除 game store 中多余的类型导入如 BetSelection、StartAutoHostingInput 等 - 将 i18n 模块中的 AppLanguage 类型改为从 type 文件导入 - 移除 mobile-header 中未使用的 MessageBroadcast 组件导入 - 统一各组件中的类型引用路径,全部指向 type 文件 - 修复 withdraw 组件中 currencies 映射的类型注解问题 - 更新 modal-store 中移除未使用的 ModalKey 类型导入
284 lines
9.6 KiB
TypeScript
284 lines
9.6 KiB
TypeScript
import {
|
|
Info,
|
|
Mail,
|
|
UserKey,
|
|
UserRoundPlus,
|
|
Volume2,
|
|
VolumeX,
|
|
} from 'lucide-react'
|
|
import { motion } from 'motion/react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import avatar from '@/assets/system/avatar.webp'
|
|
import chatImage from '@/assets/system/chat.webp'
|
|
import diamond from '@/assets/system/diamond.webp'
|
|
import logo from '@/assets/system/logo.webp'
|
|
import { SmartImage } from '@/components/smart-image.tsx'
|
|
import { useHeaderClockLabel, useHeaderVm } from '@/hooks/use-header-vm'
|
|
import { useModalStore } from '@/store'
|
|
|
|
function MobileHeaderClock() {
|
|
const systemTimeLabel = useHeaderClockLabel()
|
|
|
|
return (
|
|
<div className="mt-design-3 whitespace-nowrap text-design-7 font-bold text-[#D2FCFF]">
|
|
{systemTimeLabel}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function SignalBars({
|
|
activeBars,
|
|
toneClassName,
|
|
}: {
|
|
activeBars: number
|
|
toneClassName: string
|
|
}) {
|
|
const barHeights = [
|
|
'h-[calc(var(--design-unit)*4)]',
|
|
'h-[calc(var(--design-unit)*6)]',
|
|
'h-[calc(var(--design-unit)*8)]',
|
|
'h-[calc(var(--design-unit)*10)]',
|
|
] as const
|
|
|
|
return (
|
|
<div
|
|
className="flex h-design-11 w-design-16 items-end gap-[1px]"
|
|
aria-hidden="true"
|
|
>
|
|
{barHeights.map((heightClassName, index) => {
|
|
const isActive = index < activeBars
|
|
|
|
return (
|
|
<div
|
|
key={heightClassName}
|
|
className={[
|
|
'w-[calc(var(--design-unit)*2)] rounded-t-[1px] transition-colors',
|
|
heightClassName,
|
|
isActive ? `bg-current ${toneClassName}` : 'bg-white/18',
|
|
].join(' ')}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function MobileHeader() {
|
|
const { t } = useTranslation()
|
|
const setModalOpen = useModalStore((state) => state.setModalOpen)
|
|
const {
|
|
authStatus,
|
|
currentLanguageLabel,
|
|
currentLanguageOption,
|
|
currentUser,
|
|
isSoundEnabled,
|
|
onOpenLanguage,
|
|
onOpenLogin,
|
|
onOpenNotice,
|
|
onOpenProcedures,
|
|
onOpenRegister,
|
|
onOpenRules,
|
|
onOpenUserInfo,
|
|
signalPresentation,
|
|
toggleSoundEnabled,
|
|
} = useHeaderVm()
|
|
|
|
const actionButtonClassName =
|
|
'common-neon-inset flex h-design-19 shrink-0 cursor-pointer items-center justify-center gap-design-5 !rounded-[3px] !px-design-5 !py-0 text-design-7 leading-none transition-opacity hover:opacity-85'
|
|
const accountButtonClassName =
|
|
'common-neon-inset flex h-design-19 items-center justify-end !rounded-[3px] !py-0 text-design-7 leading-none transition-[opacity,transform] duration-150 group-hover:opacity-90 group-active:scale-[0.98]'
|
|
|
|
return (
|
|
<header className="relative z-30 h-design-72">
|
|
<div className="border-b-2 border-[#787553] bg-[#020B14] flex h-design-33 w-full items-center px-design-10">
|
|
<div className="flex h-design-23 w-design-130 pr-design-10 shrink-0 items-center justify-center border-r border-[rgba(128,223,231,0.45)]">
|
|
<SmartImage
|
|
src={logo}
|
|
alt="logo"
|
|
priority
|
|
className="h-full w-full"
|
|
imgClassName="object-contain"
|
|
/>
|
|
</div>
|
|
{authStatus === 'authenticated' ? (
|
|
<div className="flex h-full min-w-0 flex-1 items-center justify-end gap-design-7 px-design-9">
|
|
<button
|
|
type="button"
|
|
onClick={onOpenUserInfo}
|
|
className="group relative flex min-w-0 items-center justify-center transition-transform duration-150 hover:-translate-y-[1px] active:translate-y-[1px]"
|
|
>
|
|
<SmartImage
|
|
src={avatar}
|
|
alt="avatar"
|
|
priority
|
|
className="absolute left-0 z-20 h-design-25 w-design-25 rounded-full"
|
|
/>
|
|
<div
|
|
className={`${accountButtonClassName} w-design-92 pl-design-28 pr-design-6`}
|
|
>
|
|
<span className="truncate">
|
|
{currentUser?.username || '--'}
|
|
</span>
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={onOpenProcedures}
|
|
className="group relative flex min-w-0 items-center justify-center transition-transform duration-150 hover:-translate-y-[1px] active:translate-y-[1px]"
|
|
>
|
|
<SmartImage
|
|
src={diamond}
|
|
alt="diamond"
|
|
priority
|
|
className="absolute left-0 z-20 h-design-25 w-design-25"
|
|
/>
|
|
<div
|
|
className={`${accountButtonClassName} w-design-90 gap-design-4 pl-design-26 pr-design-4`}
|
|
>
|
|
<span className="min-w-0 truncate">
|
|
{currentUser?.coin || '--'}
|
|
</span>
|
|
{/*<div className="common-neon-inset flex h-design-15 w-design-15 shrink-0 items-center justify-center !rounded-[3px] !p-0">*/}
|
|
{/* <Plus*/}
|
|
{/* aria-hidden="true"*/}
|
|
{/* className="h-design-10 w-design-10 shrink-0"*/}
|
|
{/* color="#57B8BF"*/}
|
|
{/* strokeWidth={2.5}*/}
|
|
{/* />*/}
|
|
{/*</div>*/}
|
|
</div>
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<div className="flex h-full min-w-0 flex-1 items-center justify-end gap-design-8 px-design-9">
|
|
<button
|
|
type="button"
|
|
className={`${actionButtonClassName} w-design-72`}
|
|
onClick={onOpenLogin}
|
|
>
|
|
<UserKey
|
|
className="h-design-8 w-design-8 shrink-0"
|
|
color="#57B8BF"
|
|
/>
|
|
<div className="min-w-0 truncate">
|
|
{t('gameDesktop.header.login')}
|
|
</div>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`${actionButtonClassName} w-design-78`}
|
|
onClick={onOpenRegister}
|
|
>
|
|
<UserRoundPlus
|
|
className="h-design-8 w-design-8 shrink-0"
|
|
color="#57B8BF"
|
|
/>
|
|
<div className="min-w-0 truncate">
|
|
{t('gameDesktop.header.register')}
|
|
</div>
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<motion.button
|
|
type="button"
|
|
onClick={() => setModalOpen('desktopSupport', true)}
|
|
whileTap={{
|
|
scale: 0.95,
|
|
}}
|
|
whileHover={{ scale: 1.05 }}
|
|
>
|
|
<SmartImage
|
|
className={'h-design-20 w-design-20 cursor-pointer'}
|
|
alt={'chatImage'}
|
|
src={chatImage}
|
|
/>
|
|
</motion.button>
|
|
</div>
|
|
|
|
<div className={'w-full px-design-10 '}>
|
|
<div className="flex h-design-29 w-full items-center gap-design-5 my-design-5 rounded-sm border-[#0A353E] border-1">
|
|
<div className="flex h-design-19 w-design-43 shrink-0 items-center justify-center !rounded-[3px] !px-design-6 !py-0">
|
|
<div className={signalPresentation.toneClassName}>
|
|
<SignalBars
|
|
activeBars={signalPresentation.activeBars}
|
|
toneClassName={signalPresentation.toneClassName}
|
|
/>
|
|
</div>
|
|
<div
|
|
className={`${signalPresentation.toneClassName} whitespace-nowrap text-design-7 font-bold leading-none`}
|
|
>
|
|
{signalPresentation.latencyLabel}
|
|
<span className="pl-[1px] text-design-6">ms</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col h-full w-design-66 shrink-0 items-center justify-center border-r border-[rgba(128,223,231,0.32)] pr-design-7 text-design-7 leading-none">
|
|
<div className="text-[#B4E4E9]">
|
|
{t('gameDesktop.header.systemTime')}
|
|
</div>
|
|
<MobileHeaderClock />
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={onOpenRules}
|
|
className={`${actionButtonClassName} !px-design-8`}
|
|
>
|
|
<Info className="h-design-8 w-design-8 shrink-0" color="#57B8BF" />
|
|
<div className="min-w-0 truncate">
|
|
{t('gameDesktop.header.rules')}
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={onOpenNotice}
|
|
className={`${actionButtonClassName} !px-design-8`}
|
|
>
|
|
<Mail className="h-design-8 w-design-8 shrink-0" color="#57B8BF" />
|
|
<div className="min-w-0 truncate">
|
|
{t('gameDesktop.header.message')}
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={toggleSoundEnabled}
|
|
className={`${actionButtonClassName} !px-design-8`}
|
|
>
|
|
{isSoundEnabled ? (
|
|
<Volume2
|
|
className="h-design-8 w-design-8 shrink-0"
|
|
color="#57B8BF"
|
|
/>
|
|
) : (
|
|
<VolumeX
|
|
className="h-design-8 w-design-8 shrink-0"
|
|
color="#57B8BF"
|
|
/>
|
|
)}
|
|
<div className="min-w-0 truncate">
|
|
{t('gameDesktop.header.bgm')}
|
|
</div>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={onOpenLanguage}
|
|
className={`${actionButtonClassName} !px-design-8 justify-between`}
|
|
>
|
|
<SmartImage
|
|
src={currentLanguageOption.icon}
|
|
alt={currentLanguageLabel}
|
|
className="h-design-14 w-design-14 shrink-0 rounded-full"
|
|
imgClassName="object-cover"
|
|
/>
|
|
<div className="min-w-0 truncate">{currentLanguageLabel}</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
)
|
|
}
|