refactor(game): 重构项目结构,优化链路, 移动端适配
- 移除 useGameBoardVm 数据层实施说明文档 - 移除核心玩法与前端规则摘要文档 - 移除游戏模块数据与界面分层第一阶段实施稿文档 - 清理与数据层重构相关的技术方案说明 - 删除关于 PC 和 Mobile 界面分离的设计规划 - 移除 view-model hooks 架构设计相关内容
This commit is contained in:
@@ -3,9 +3,11 @@ import dayjs from 'dayjs'
|
||||
import { RotateCw } from 'lucide-react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { getNoticeList } from '@/api'
|
||||
import lengthGreenBtn from '@/assets/system/length-green-btn.webp'
|
||||
import checkIcon from '@/assets/system/right.webp'
|
||||
import { CenterModal } from '@/components/center-modal.tsx'
|
||||
import { MobileCenterModal } from '@/components/mobile-center-modal.tsx'
|
||||
import { SmartBackground } from '@/components/smart-background.tsx'
|
||||
import { SmartImage } from '@/components/smart-image.tsx'
|
||||
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator.tsx'
|
||||
@@ -13,7 +15,6 @@ import {
|
||||
ENTRY_NOTICE_CONFIRM_INTERVAL_MS,
|
||||
ENTRY_NOTICE_LAST_CONFIRMED_AT_KEY,
|
||||
} from '@/constants'
|
||||
import { getNoticeList } from '@/features/game/api'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import { useModalStore } from '@/store/modal'
|
||||
@@ -36,8 +37,15 @@ function setLastConfirmedAt(storageKey: string, timestamp: number) {
|
||||
localStorage.setItem(storageKey, String(timestamp))
|
||||
}
|
||||
|
||||
export function EntryNoticeGateModal() {
|
||||
interface EntryNoticeGateModalProps {
|
||||
variant?: 'desktop' | 'mobile'
|
||||
}
|
||||
|
||||
export function EntryNoticeGateModal({
|
||||
variant = 'desktop',
|
||||
}: EntryNoticeGateModalProps = {}) {
|
||||
const { t } = useTranslation()
|
||||
const isMobile = variant === 'mobile'
|
||||
const authStatus = useAuthStore((state) => state.status)
|
||||
const authIsHydrated = useAuthStore((state) => state.isHydrated)
|
||||
const accessToken = useAuthStore((state) => state.accessToken)
|
||||
@@ -118,29 +126,64 @@ export function EntryNoticeGateModal() {
|
||||
return null
|
||||
}
|
||||
|
||||
const Modal = isMobile ? MobileCenterModal : CenterModal
|
||||
|
||||
return (
|
||||
<CenterModal
|
||||
<Modal
|
||||
open={shouldShowModal}
|
||||
isShowClose={false}
|
||||
isNormalBg={true}
|
||||
title={
|
||||
<div className="modal-title-glow text-design-26">
|
||||
<div
|
||||
className={cn(
|
||||
'modal-title-glow',
|
||||
isMobile ? 'text-design-16' : 'text-design-26',
|
||||
)}
|
||||
>
|
||||
{t('game.modals.entryNotice.title')}
|
||||
</div>
|
||||
}
|
||||
titleAlign="left"
|
||||
className="h-design-700 w-design-1000 max-h-[92vh] max-w-[92vw]"
|
||||
className={
|
||||
isMobile
|
||||
? 'h-design-400'
|
||||
: 'h-design-700 w-design-1000 max-h-[92vh] max-w-[92vw]'
|
||||
}
|
||||
>
|
||||
<div className="flex h-full w-full flex-col gap-design-20 px-design-14 pb-design-30 pt-design-8">
|
||||
<div className="min-h-0 flex-1 overflow-y-auto rounded-md border border-[#2B8CA3]/45 bg-[#001B24]/70 p-design-18 shadow-[inset_0_0_calc(var(--design-unit)*18)_rgba(39,175,205,0.1)]">
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-full w-full flex-col',
|
||||
isMobile
|
||||
? 'gap-design-10 px-design-8 pb-design-8 pt-design-4'
|
||||
: 'gap-design-20 px-design-14 pb-design-30 pt-design-8',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'min-h-0 flex-1 overflow-y-auto rounded-md border border-[#2B8CA3]/45 bg-[#001B24]/70 shadow-[inset_0_0_calc(var(--design-unit)*18)_rgba(39,175,205,0.1)]',
|
||||
isMobile ? 'p-design-8' : 'p-design-18',
|
||||
)}
|
||||
>
|
||||
{noticeListQuery.isPending ? (
|
||||
<DataLoadingIndicator
|
||||
label={t('game.modals.entryNotice.loading')}
|
||||
className="h-full min-h-[calc(var(--design-unit)*320)]"
|
||||
className={cn(
|
||||
'h-full',
|
||||
isMobile
|
||||
? 'min-h-[calc(var(--design-unit)*220)] text-design-12'
|
||||
: 'min-h-[calc(var(--design-unit)*320)]',
|
||||
)}
|
||||
/>
|
||||
) : noticeListQuery.isError ? (
|
||||
<div className="flex h-full min-h-[calc(var(--design-unit)*320)] flex-col items-center justify-center gap-design-18 text-center text-[#9CE8F2]">
|
||||
<div className="text-design-22">
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-full flex-col items-center justify-center text-center text-[#9CE8F2]',
|
||||
isMobile
|
||||
? 'min-h-[calc(var(--design-unit)*220)] gap-design-10'
|
||||
: 'min-h-[calc(var(--design-unit)*320)] gap-design-18',
|
||||
)}
|
||||
>
|
||||
<div className={isMobile ? 'text-design-13' : 'text-design-22'}>
|
||||
{t('game.modals.entryNotice.loadFailed')}
|
||||
</div>
|
||||
<button
|
||||
@@ -148,30 +191,75 @@ export function EntryNoticeGateModal() {
|
||||
onClick={() => {
|
||||
void noticeListQuery.refetch()
|
||||
}}
|
||||
className="inline-flex items-center gap-design-8 rounded-md border border-[#4AC6DE]/45 bg-[#0B4454] px-design-18 py-design-10 text-design-18 text-[#D7FFFF] transition hover:bg-[#0E576D]"
|
||||
className={cn(
|
||||
'inline-flex items-center rounded-md border border-[#4AC6DE]/45 bg-[#0B4454] text-[#D7FFFF] transition hover:bg-[#0E576D]',
|
||||
isMobile
|
||||
? 'gap-design-5 px-design-10 py-design-6 text-design-12'
|
||||
: 'gap-design-8 px-design-18 py-design-10 text-design-18',
|
||||
)}
|
||||
>
|
||||
<RotateCw className="h-design-18 w-design-18" />
|
||||
<RotateCw
|
||||
className={
|
||||
isMobile
|
||||
? 'h-design-13 w-design-13'
|
||||
: 'h-design-18 w-design-18'
|
||||
}
|
||||
/>
|
||||
{t('game.modals.entryNotice.retry')}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-design-16">
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col',
|
||||
isMobile ? 'gap-design-8' : 'gap-design-16',
|
||||
)}
|
||||
>
|
||||
{popoutNotices.map((notice, index) => (
|
||||
<article
|
||||
key={notice.notice_id}
|
||||
className="rounded-md border border-[#2B8CA3]/45 bg-[linear-gradient(180deg,rgba(9,63,78,0.96)_0%,rgba(6,42,53,0.98)_100%)] p-design-20"
|
||||
className={cn(
|
||||
'rounded-md border border-[#2B8CA3]/45 bg-[linear-gradient(180deg,rgba(9,63,78,0.96)_0%,rgba(6,42,53,0.98)_100%)]',
|
||||
isMobile ? 'p-design-9' : 'p-design-20',
|
||||
)}
|
||||
>
|
||||
<div className="mb-design-12 flex flex-wrap items-center justify-between gap-design-12">
|
||||
<div className="min-w-0 flex-1 text-design-24 font-semibold leading-tight text-white">
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-wrap items-center justify-between',
|
||||
isMobile
|
||||
? 'mb-design-6 gap-design-6'
|
||||
: 'mb-design-12 gap-design-12',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'min-w-0 flex-1 font-semibold leading-tight text-white',
|
||||
isMobile ? 'text-design-13' : 'text-design-24',
|
||||
)}
|
||||
>
|
||||
{index + 1}. {notice.title}
|
||||
</div>
|
||||
<div className="rounded-full border border-[#51BCD1]/35 bg-[#0A4252]/80 px-design-12 py-design-5 text-design-15 text-[#9CE8F2]">
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-full border border-[#51BCD1]/35 bg-[#0A4252]/80 text-[#9CE8F2]',
|
||||
isMobile
|
||||
? 'px-design-7 py-design-3 text-design-9'
|
||||
: 'px-design-12 py-design-5 text-design-15',
|
||||
)}
|
||||
>
|
||||
{dayjs(notice.publish_time * 1000).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="whitespace-pre-wrap text-design-18 leading-[1.8] text-[#C4F2F7]">
|
||||
<div
|
||||
className={cn(
|
||||
'whitespace-pre-wrap text-[#C4F2F7]',
|
||||
isMobile
|
||||
? 'text-design-11 leading-[1.55]'
|
||||
: 'text-design-18 leading-[1.8]',
|
||||
)}
|
||||
>
|
||||
{notice.content ?? ''}
|
||||
</div>
|
||||
</article>
|
||||
@@ -180,8 +268,20 @@ export function EntryNoticeGateModal() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 flex-col items-center justify-center gap-design-20">
|
||||
<label className="inline-flex cursor-pointer items-center justify-center gap-design-12 text-design-20 text-[#C4F2F7]">
|
||||
<div
|
||||
className={cn(
|
||||
'flex shrink-0 flex-col items-center justify-center',
|
||||
isMobile ? 'gap-design-8' : 'gap-design-20',
|
||||
)}
|
||||
>
|
||||
<label
|
||||
className={cn(
|
||||
'inline-flex cursor-pointer items-center justify-center text-[#C4F2F7]',
|
||||
isMobile
|
||||
? 'gap-design-6 text-design-11'
|
||||
: 'gap-design-12 text-design-20',
|
||||
)}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={hasAgreed}
|
||||
@@ -192,7 +292,10 @@ export function EntryNoticeGateModal() {
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'flex h-design-32 w-design-32 shrink-0 items-center justify-center rounded-[calc(var(--design-unit)*5)] border transition',
|
||||
'flex shrink-0 items-center justify-center border transition',
|
||||
isMobile
|
||||
? 'h-design-20 w-design-20 rounded-[calc(var(--design-unit)*4)]'
|
||||
: 'h-design-32 w-design-32 rounded-[calc(var(--design-unit)*5)]',
|
||||
hasAgreed
|
||||
? 'border-[#4AFF49]/80 bg-[#071F11]'
|
||||
: 'border-[#6CCDCF]/70 bg-[#031D25]',
|
||||
@@ -204,7 +307,12 @@ export function EntryNoticeGateModal() {
|
||||
alt=""
|
||||
priority={true}
|
||||
showSkeleton={false}
|
||||
className="h-design-34 w-design-38 overflow-visible"
|
||||
className={cn(
|
||||
'overflow-visible',
|
||||
isMobile
|
||||
? 'h-design-22 w-design-24'
|
||||
: 'h-design-34 w-design-38',
|
||||
)}
|
||||
imgClassName="object-contain"
|
||||
/>
|
||||
) : null}
|
||||
@@ -230,7 +338,10 @@ export function EntryNoticeGateModal() {
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
'flex h-design-72 w-design-270 items-center justify-center rounded-md pb-design-5 text-design-22 font-bold transition',
|
||||
'flex items-center justify-center rounded-md font-bold transition',
|
||||
isMobile
|
||||
? 'h-design-42 w-design-156 pb-design-3 text-design-14'
|
||||
: 'h-design-72 w-design-270 pb-design-5 text-design-22',
|
||||
canEnter
|
||||
? 'modal-title-glow cursor-pointer text-white hover:brightness-110 active:brightness-95'
|
||||
: 'cursor-not-allowed text-white opacity-80 grayscale',
|
||||
@@ -248,6 +359,10 @@ export function EntryNoticeGateModal() {
|
||||
</SmartBackground>
|
||||
</div>
|
||||
</div>
|
||||
</CenterModal>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export function MobileEntryNoticeGateModal() {
|
||||
return <EntryNoticeGateModal variant="mobile" />
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator'
|
||||
import type { PeriodHistoryDisplayItem } from '@/features/game/hooks/use-period-history-vm'
|
||||
import type { PeriodHistoryDisplayItem } from '@/hooks/use-period-history-vm'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface PeriodHistoryListLabels {
|
||||
|
||||
Reference in New Issue
Block a user