add public work share links
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-27 22:49:13 +08:00
parent 271db02e4a
commit 1348b2e940
23 changed files with 1038 additions and 248 deletions

View File

@@ -2,8 +2,12 @@ import { describe, expect, it } from 'vitest';
import {
APP_RUNTIME_ROUTES,
buildPublicWorkDetailPath,
buildPublicWorkDetailUrl,
buildPublicWorkStagePath,
isKnownMainAppPagePath,
normalizeAppPath,
readPublicWorkCodeFromLocationSearch,
resolvePathForSelectionStage,
resolveSelectionStageFromPath,
} from './appPageRoutes';
@@ -45,4 +49,22 @@ describe('appPageRoutes', () => {
).toBe(true);
expect(isKnownMainAppPagePath('/runtime/rpg/adventure/')).toBe(true);
});
it('builds and reads public work detail query routes', () => {
expect(buildPublicWorkDetailPath('CW-00000001')).toBe(
'/worlds/detail?work=CW-00000001',
);
expect(buildPublicWorkDetailUrl('CW-00000001', 'https://example.test')).toBe(
'https://example.test/worlds/detail?work=CW-00000001',
);
expect(readPublicWorkCodeFromLocationSearch('?work=CW-00000001')).toBe(
'CW-00000001',
);
expect(
buildPublicWorkStagePath('puzzle-gallery-detail', 'PZ-00000002'),
).toBe('/gallery/puzzle/detail?work=PZ-00000002');
expect(buildPublicWorkStagePath('big-fish-runtime', 'BF-00000003')).toBe(
'/runtime/big-fish?work=BF-00000003',
);
});
});

View File

@@ -2,6 +2,8 @@ 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', '/'],
['detail', '/worlds/detail'],
@@ -49,6 +51,37 @@ 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('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);
@@ -59,11 +92,14 @@ export function isKnownMainAppPagePath(pathname: string) {
}
export function pushAppHistoryPath(path: string) {
const normalizedPath = normalizeAppPath(path);
if (normalizeAppPath(window.location.pathname) === normalizedPath) {
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, '', normalizedPath);
window.history.pushState(null, '', nextRelativeUrl);
}