124 lines
4.5 KiB
TypeScript
124 lines
4.5 KiB
TypeScript
import type { SelectionStage } from '../components/platform-entry';
|
|
|
|
export type RuntimePageRoute = 'rpg-character-select' | 'rpg-adventure';
|
|
|
|
export const PUBLIC_WORK_QUERY_PARAM = 'work';
|
|
|
|
const STAGE_ROUTE_ENTRIES = [
|
|
['platform', '/'],
|
|
['profile-feedback', '/profile/feedback'],
|
|
['work-detail', '/works/detail'],
|
|
['detail', '/worlds/detail'],
|
|
['agent-workspace', '/creation/rpg/agent'],
|
|
['custom-world-generating', '/creation/rpg/generating'],
|
|
['custom-world-result', '/creation/rpg/result'],
|
|
['big-fish-agent-workspace', '/creation/big-fish/agent'],
|
|
['big-fish-result', '/creation/big-fish/result'],
|
|
['big-fish-runtime', '/runtime/big-fish'],
|
|
['match3d-agent-workspace', '/creation/match3d/agent'],
|
|
['match3d-result', '/creation/match3d/result'],
|
|
['match3d-runtime', '/runtime/match3d'],
|
|
['square-hole-agent-workspace', '/creation/square-hole/agent'],
|
|
['square-hole-result', '/creation/square-hole/result'],
|
|
['square-hole-runtime', '/runtime/square-hole'],
|
|
['creative-agent-workspace', '/creation/creative-agent'],
|
|
['visual-novel-agent-workspace', '/creation/visual-novel/agent'],
|
|
['visual-novel-result', '/creation/visual-novel/result'],
|
|
['visual-novel-gallery-detail', '/gallery/visual-novel/detail'],
|
|
['visual-novel-runtime', '/runtime/visual-novel'],
|
|
['baby-object-match-workspace', '/creation/baby-object-match'],
|
|
['baby-object-match-generating', '/creation/baby-object-match/generating'],
|
|
['baby-object-match-result', '/creation/baby-object-match/result'],
|
|
['baby-object-match-runtime', '/runtime/baby-object-match'],
|
|
['baby-love-drawing-runtime', '/runtime/baby-love-drawing'],
|
|
['puzzle-agent-workspace', '/creation/puzzle/agent'],
|
|
['puzzle-result', '/creation/puzzle/result'],
|
|
['puzzle-gallery-detail', '/gallery/puzzle/detail'],
|
|
['puzzle-runtime', '/runtime/puzzle'],
|
|
] as const satisfies readonly (readonly [SelectionStage, string])[];
|
|
|
|
export const APP_STAGE_ROUTES: Record<SelectionStage, string> =
|
|
Object.fromEntries(STAGE_ROUTE_ENTRIES) as Record<SelectionStage, string>;
|
|
|
|
export const APP_RUNTIME_ROUTES: Record<RuntimePageRoute, string> = {
|
|
'rpg-character-select': '/runtime/rpg/characters',
|
|
'rpg-adventure': '/runtime/rpg/adventure',
|
|
};
|
|
|
|
const ROUTE_STAGE_BY_PATH = new Map(
|
|
STAGE_ROUTE_ENTRIES.map(([stage, path]) => [path, stage] as const),
|
|
) as Map<string, SelectionStage>;
|
|
|
|
export function normalizeAppPath(pathname: string) {
|
|
const trimmedPathname = pathname.trim().toLowerCase();
|
|
|
|
if (!trimmedPathname || trimmedPathname === '/') {
|
|
return '/';
|
|
}
|
|
|
|
return trimmedPathname.replace(/\/+$/u, '');
|
|
}
|
|
|
|
export function resolveSelectionStageFromPath(
|
|
pathname: string,
|
|
): SelectionStage {
|
|
return ROUTE_STAGE_BY_PATH.get(normalizeAppPath(pathname)) ?? 'platform';
|
|
}
|
|
|
|
export function resolvePathForSelectionStage(stage: SelectionStage) {
|
|
return APP_STAGE_ROUTES[stage] ?? APP_STAGE_ROUTES.platform;
|
|
}
|
|
|
|
export function readPublicWorkCodeFromLocationSearch(search: string) {
|
|
const params = new URLSearchParams(search);
|
|
return params.get(PUBLIC_WORK_QUERY_PARAM)?.trim() || null;
|
|
}
|
|
|
|
export function buildPublicWorkDetailPath(publicWorkCode: string) {
|
|
return buildPublicWorkStagePath('work-detail', publicWorkCode);
|
|
}
|
|
|
|
export function buildPublicWorkStagePath(
|
|
stage: SelectionStage,
|
|
publicWorkCode: string,
|
|
) {
|
|
const code = publicWorkCode.trim();
|
|
const stagePath = resolvePathForSelectionStage(stage);
|
|
if (!code) {
|
|
return stagePath;
|
|
}
|
|
|
|
const params = new URLSearchParams();
|
|
params.set(PUBLIC_WORK_QUERY_PARAM, code);
|
|
return `${stagePath}?${params.toString()}`;
|
|
}
|
|
|
|
export function buildPublicWorkDetailUrl(
|
|
publicWorkCode: string,
|
|
origin = window.location.origin,
|
|
) {
|
|
return new URL(buildPublicWorkDetailPath(publicWorkCode), origin).href;
|
|
}
|
|
|
|
export function isKnownMainAppPagePath(pathname: string) {
|
|
const normalizedPath = normalizeAppPath(pathname);
|
|
const runtimePaths: readonly string[] = Object.values(APP_RUNTIME_ROUTES);
|
|
return (
|
|
ROUTE_STAGE_BY_PATH.has(normalizedPath) ||
|
|
runtimePaths.includes(normalizedPath)
|
|
);
|
|
}
|
|
|
|
export function pushAppHistoryPath(path: string) {
|
|
const nextUrl = new URL(path, window.location.origin);
|
|
const normalizedPath = normalizeAppPath(nextUrl.pathname);
|
|
const nextRelativeUrl = `${normalizedPath}${nextUrl.search}`;
|
|
const currentRelativeUrl = `${normalizeAppPath(window.location.pathname)}${window.location.search}`;
|
|
if (currentRelativeUrl === nextRelativeUrl) {
|
|
return;
|
|
}
|
|
|
|
// 页面阶段变化是用户可感知导航,写入 history 以支持前进后退。
|
|
window.history.pushState(null, '', nextRelativeUrl);
|
|
}
|