import { Archive, ArrowRight, Bell, BookOpen, Camera, ChevronDown, ChevronRight, Clock3, Coins, Copy, Heart, House, LogIn, MessageCircle, Pencil, Search, Settings, SlidersHorizontal, Sparkles, Star, Ticket, Trophy, UserPlus, UserRound, } from 'lucide-react'; import { type ComponentType, type PointerEvent, type ReactNode, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import communityQqQrImage from '../../../media/social-media-group/qq.png'; import communityWechatQrImage from '../../../media/social-media-group/wechat.png'; import type { CustomWorldLibraryEntry, PlatformBrowseHistoryEntry, ProfileDashboardCardKey, ProfileDashboardSummary, ProfilePlayedWorkSummary, ProfilePlayStatsResponse, ProfileReferralInviteCenterResponse, ProfileSaveArchiveSummary, ProfileWalletLedgerResponse, RedeemProfileReferralInviteCodeResponse, RedeemProfileRewardCodeResponse, } from '../../../packages/shared/src/contracts/runtime'; import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes'; import type { AuthUser } from '../../services/authService'; import { updateAuthProfile } from '../../services/authService'; import { copyTextToClipboard } from '../../services/clipboard'; import { getRpgProfileReferralInviteCenter, getRpgProfileWalletLedger, redeemRpgProfileReferralInviteCode, redeemRpgProfileRewardCode, } from '../../services/rpg-entry/rpgProfileClient'; import type { CustomWorldProfile } from '../../types'; import { useAuthUi } from '../auth/AuthUiContext'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; import { RpgEntryBrandLogo } from './RpgEntryBrandLogo'; import { buildPlatformWorldDisplayTags, describePlatformThemeLabel, formatPlatformWorkDisplayName, formatPlatformWorkDisplayTag, formatPlatformWorldTime, isBigFishGalleryEntry, isPuzzleGalleryEntry, type PlatformPublicGalleryCard, type PlatformWorldCardLike, resolvePlatformWorldCoverImage, resolvePlatformWorldLeadPortrait, } from './rpgEntryWorldPresentation'; export type PlatformHomeTab = | 'home' | 'category' | 'create' | 'saves' | 'profile'; export interface RpgEntryHomeViewProps { activeTab: PlatformHomeTab; onTabChange: (tab: PlatformHomeTab) => void; hasSavedGame: boolean; savedSnapshot: HydratedSavedGameSnapshot | null; saveEntries: ProfileSaveArchiveSummary[]; saveError: string | null; featuredEntries: PlatformPublicGalleryCard[]; latestEntries: PlatformPublicGalleryCard[]; 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: PlatformPublicGalleryCard) => void; onOpenLibraryDetail: ( entry: CustomWorldLibraryEntry, ) => void; onDeleteLibraryEntry?: ( entry: CustomWorldLibraryEntry, ) => void; deletingLibraryEntryId?: string | null; onSearchPublicCode?: (keyword: string) => void | Promise; isSearchingPublicCode?: boolean; onOpenProfileDashboardCard?: (cardKey: ProfileDashboardCardKey) => void; profilePlayStats?: ProfilePlayStatsResponse | null; isProfilePlayStatsOpen?: boolean; isProfilePlayStatsLoading?: boolean; profilePlayStatsError?: string | null; onCloseProfilePlayStats?: () => void; onOpenPlayedWork?: (work: ProfilePlayedWorkSummary) => void; onRechargeSuccess?: () => void | Promise; createTabContent?: ReactNode; } const PANEL_SURFACE_CLASS = 'platform-surface platform-surface--soft'; const HERO_SURFACE_CLASS = 'platform-surface platform-surface--hero platform-interactive-card min-w-0'; const MOBILE_PAGE_STAGE_CLASS = 'platform-page-stage platform-remap-surface min-w-0 space-y-4 overflow-hidden pb-2'; const DESKTOP_PAGE_STAGE_CLASS = 'platform-page-stage platform-remap-surface min-w-0 space-y-5 pb-4'; const DESKTOP_LAYOUT_QUERY = '(min-width: 1024px)'; const PLATFORM_HOME_TABS: PlatformHomeTab[] = [ 'home', 'category', 'create', 'saves', 'profile', ]; const AVATAR_MAX_FILE_SIZE = 5 * 1024 * 1024; const AVATAR_OUTPUT_SIZE = 256; const AVATAR_ALLOWED_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']); type ProfilePopupPanel = 'invite' | 'redeem' | 'community'; type MobileHomeChannel = 'recommend' | 'today' | 'category'; type PlatformRankingTab = 'hot' | 'remix' | 'new' | 'like'; const COMMUNITY_QR_CODES = [ { label: '微信群', src: communityWechatQrImage, alt: '玩家社区微信群二维码', }, { label: 'QQ群', src: communityQqQrImage, alt: '玩家社区 QQ 群二维码', }, ] as const; const MOBILE_HOME_CHANNELS: Array<{ id: MobileHomeChannel; label: string; }> = [ { id: 'recommend', label: '推荐' }, { id: 'today', label: '今日游戏' }, { id: 'category', label: '游戏分类' }, ]; const PLATFORM_RANKING_TABS: Array<{ id: PlatformRankingTab; label: string; metricLabel: string; emptyText: string; }> = [ { id: 'hot', label: '热门榜', metricLabel: '游玩', emptyText: '公开广场暂时还没有热门作品。', }, { id: 'remix', label: '改造榜', metricLabel: '改造', emptyText: '公开广场暂时还没有改造作品。', }, { id: 'new', label: '新品榜', metricLabel: '近7日', emptyText: '近 7 日暂时还没有新品。', }, { id: 'like', label: '点赞榜', metricLabel: '点赞', emptyText: '公开广场暂时还没有点赞作品。', }, ]; 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 PublicCodeSearchBar({ value, onChange, onSubmit, isSearching, className, }: { value: string; onChange: (value: string) => void; onSubmit: () => void; isSearching: boolean; className?: string; }) { return (
onChange(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); onSubmit(); } }} placeholder="输入 SY / CW / BF / PZ 编号" className="w-full min-w-0 bg-transparent text-sm text-[var(--platform-text-strong)] outline-none placeholder:text-[var(--platform-text-soft)]" />
); } function EmptyShelf({ text }: { text: string }) { return (
{text}
); } function SaveArchivePreview({ entry, label, className, }: { entry: ProfileSaveArchiveSummary; label: string; className: string; }) { return (