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 RuntimeLoadingFallback() {
return (
);
}
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}
/>
);
}