import { lazy, Suspense, useCallback, useEffect, useRef, useState, } from 'react'; import { useAuthUi } from './components/auth/AuthUiContext'; import { PlatformEntryFlowShell } from './components/platform-entry/PlatformEntryFlowShell'; import type { CustomWorldRuntimeLaunchOptions, SelectionStage, } from './components/platform-entry/platformEntryTypes'; import type { HydratedSavedGameSnapshot } from './persistence/runtimeSnapshotTypes'; import { APP_RUNTIME_ROUTES, normalizeAppPath, pushAppHistoryPath, readPublicWorkCodeFromLocationSearch, resolvePathForSelectionStage, resolveSelectionStageFromPath, } from './routing/appPageRoutes'; import type { RpgRuntimeAppIntent } from './RpgRuntimeApp'; import type { CustomWorldProfile } from './types'; const RpgRuntimeApp = lazy(async () => { const module = await import('./RpgRuntimeApp'); return { default: module.RpgRuntimeApp, }; }); function isRpgRuntimeRoute(pathname: string) { const normalizedPath = normalizeAppPath(pathname); return ( normalizedPath === APP_RUNTIME_ROUTES['rpg-character-select'] || normalizedPath === APP_RUNTIME_ROUTES['rpg-adventure'] ); } export default function App() { const authUi = useAuthUi(); const runtimeIntentTokenRef = useRef(0); const [runtimeIntent, setRuntimeIntent] = useState(null); const [isRuntimeActive, setIsRuntimeActive] = useState(() => isRpgRuntimeRoute(window.location.pathname), ); const [selectionStage, setRawSelectionStage] = useState(() => resolveSelectionStageFromPath(window.location.pathname), ); const [runtimeReturnStage, setRuntimeReturnStage] = useState('platform'); const [initialPublicWorkCode] = useState(() => readPublicWorkCodeFromLocationSearch(window.location.search), ); const setSelectionStage = useCallback((stage: SelectionStage) => { setRawSelectionStage(stage); pushAppHistoryPath(resolvePathForSelectionStage(stage)); }, []); useEffect(() => { const syncStageFromHistory = () => { if (isRpgRuntimeRoute(window.location.pathname)) { setIsRuntimeActive(true); return; } setIsRuntimeActive(false); setRawSelectionStage( resolveSelectionStageFromPath(window.location.pathname), ); }; window.addEventListener('popstate', syncStageFromHistory); return () => window.removeEventListener('popstate', syncStageFromHistory); }, []); const createRuntimeIntent = useCallback( (intent: Omit) => { runtimeIntentTokenRef.current += 1; setRuntimeIntent({ ...intent, token: runtimeIntentTokenRef.current, }); setIsRuntimeActive(true); }, [], ); const handleContinueGame = useCallback( (snapshot?: HydratedSavedGameSnapshot | null) => { createRuntimeIntent({ kind: 'snapshot', snapshot: snapshot ?? null, }); }, [createRuntimeIntent], ); const handleCustomWorldSelect = useCallback( ( customWorldProfile: CustomWorldProfile, options?: CustomWorldRuntimeLaunchOptions, ) => { // 中文注释:作品测试需要在结束测试后精确返回启动它的结果页; // 正式进入世界仍保持既有平台首页返回语义。 setRuntimeReturnStage(options?.returnStage ?? 'platform'); createRuntimeIntent({ kind: 'custom-world', profile: customWorldProfile, mode: options?.mode ?? 'play', disablePersistence: options?.disablePersistence, exitToResult: options?.returnStage === 'custom-world-result', }); }, [createRuntimeIntent], ); const platformThemeClass = authUi?.platformTheme === 'dark' ? 'platform-theme--dark' : 'platform-theme--light'; if (isRuntimeActive) { return ( { setIsRuntimeActive(false); setSelectionStage(runtimeReturnStage); }} /> ); } return (
{}} handleCustomWorldSelect={handleCustomWorldSelect} />
); }