import { Archive, ArrowRight, Bell, BookOpen, Camera, ChevronRight, Clock3, Coins, Copy, Crown, House, MessageCircle, Pencil, Search, Settings, Sparkles, Ticket, UserPlus, UserRound, } from 'lucide-react'; import { type ComponentType, type ReactNode, useEffect, useMemo, useState, } from 'react'; import type { CustomWorldGalleryCard, CustomWorldLibraryEntry, PlatformBrowseHistoryEntry, ProfileDashboardCardKey, ProfileDashboardSummary, ProfileSaveArchiveSummary, } from '../../../packages/shared/src/contracts/runtime'; import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes'; import type { AuthUser } from '../../services/authService'; import type { CustomWorldProfile } from '../../types'; import { useAuthUi } from '../auth/AuthUiContext'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; import { RpgEntryBrandLogo } from './RpgEntryBrandLogo'; import { buildPlatformWorldTags, describePlatformThemeLabel, formatPlatformWorldTime, type PlatformWorldCardLike, resolvePlatformWorldCoverImage, resolvePlatformWorldLeadPortrait, } from './rpgEntryWorldPresentation'; export type PlatformHomeTab = 'home' | 'create' | 'saves' | 'profile'; export interface RpgEntryHomeViewProps { activeTab: PlatformHomeTab; onTabChange: (tab: PlatformHomeTab) => void; hasSavedGame: boolean; savedSnapshot: HydratedSavedGameSnapshot | null; saveEntries: ProfileSaveArchiveSummary[]; saveError: string | null; featuredEntries: CustomWorldGalleryCard[]; latestEntries: CustomWorldGalleryCard[]; myEntries: CustomWorldLibraryEntry[]; historyEntries: PlatformBrowseHistoryEntry[]; profileDashboard: ProfileDashboardSummary | null; isLoadingPlatform: boolean; isLoadingDashboard: boolean; isResumingSaveWorldKey: string | null; platformError: string | null; dashboardError: string | null; onContinueGame: (snapshot?: HydratedSavedGameSnapshot | null) => void; onResumeSave: (entry: ProfileSaveArchiveSummary) => void; onOpenCreateWorld: () => void; onOpenCreateTypePicker: () => void; onOpenGalleryDetail: (entry: CustomWorldGalleryCard) => void; onOpenLibraryDetail: ( entry: CustomWorldLibraryEntry, ) => void; onOpenProfileDashboardCard?: (cardKey: ProfileDashboardCardKey) => void; createTabContent?: ReactNode; } const PANEL_SURFACE_CLASS = 'platform-surface platform-surface--soft'; const HERO_SURFACE_CLASS = 'platform-surface platform-surface--hero platform-interactive-card'; const MOBILE_PAGE_STAGE_CLASS = 'platform-page-stage platform-remap-surface space-y-4 pb-2'; const DESKTOP_PAGE_STAGE_CLASS = 'platform-page-stage platform-remap-surface space-y-5 pb-4'; const DESKTOP_LAYOUT_QUERY = '(min-width: 1024px)'; const PLATFORM_HOME_TABS: PlatformHomeTab[] = [ 'home', 'create', 'saves', 'profile', ]; function usePlatformDesktopLayout() { const [isDesktopLayout, setIsDesktopLayout] = useState(() => { if ( typeof window === 'undefined' || typeof window.matchMedia !== 'function' ) { return false; } return window.matchMedia(DESKTOP_LAYOUT_QUERY).matches; }); useEffect(() => { if ( typeof window === 'undefined' || typeof window.matchMedia !== 'function' ) { return; } const mediaQuery = window.matchMedia(DESKTOP_LAYOUT_QUERY); const updateLayout = (event?: MediaQueryListEvent) => { setIsDesktopLayout(event?.matches ?? mediaQuery.matches); }; updateLayout(); // 平台页只挂载当前断点外壳,避免隐藏的移动端/桌面端内容重复抢占查询。 if (typeof mediaQuery.addEventListener === 'function') { mediaQuery.addEventListener('change', updateLayout); return () => mediaQuery.removeEventListener('change', updateLayout); } mediaQuery.addListener(updateLayout); return () => mediaQuery.removeListener(updateLayout); }, []); return isDesktopLayout; } function ResolvedAssetBackdrop({ src, alt, className, ariaHidden = false, }: { src?: string | null; alt: string; className: string; ariaHidden?: boolean; }) { return ( ); } function SectionHeader({ title, detail }: { title: string; detail: string }) { return (
{detail}
{title}
); } function EmptyShelf({ text }: { text: string }) { return (
{text}
); } function SaveArchivePreview({ entry, label, className, }: { entry: ProfileSaveArchiveSummary; label: string; className: string; }) { return (