Files
Genarrative/src/components/platform-entry/platformCreationUrlStateModel.ts
T

432 lines
13 KiB
TypeScript

import type { BarkBattleDraftConfig } from '../../../packages/shared/src/contracts/barkBattle';
import type { BigFishSessionSnapshotResponse } from '../../../packages/shared/src/contracts/bigFish';
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import type { Match3DAgentSessionSnapshot } from '../../../packages/shared/src/contracts/match3dAgent';
import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { SquareHoleSessionSnapshot } from '../../../packages/shared/src/contracts/squareHoleAgent';
import type { VisualNovelAgentSessionSnapshot } from '../../../packages/shared/src/contracts/visualNovel';
import {
type CreationUrlState,
isCreationRestorePath,
} from '../../services/creationUrlState';
import type {
JumpHopSessionSnapshotResponse,
JumpHopWorkProfileResponse,
} from '../../services/jump-hop/jumpHopClient';
import { buildPuzzlePublicWorkCode } from '../../services/publicWorkCode';
import type { PuzzleRuntimeUrlState } from '../../services/puzzleRuntimeUrlState';
import type {
WoodenFishSessionSnapshotResponse,
WoodenFishWorkProfileResponse,
} from '../../services/wooden-fish/woodenFishClient';
import type { SelectionStage } from './platformEntryTypes';
import {
buildPuzzleResultProfileId,
buildPuzzleResultWorkId,
buildPuzzleSessionIdFromProfileId,
} from './platformPuzzleIdentityModel';
/** 平台创作恢复 URL 私有 query 的纯模型,调用方只需传入玩法快照。 */
export function normalizeCreationUrlValue(value: string | null | undefined) {
return value?.trim() || null;
}
export function hasCreationUrlStateValue(state: CreationUrlState) {
return Boolean(
normalizeCreationUrlValue(state.sessionId) ||
normalizeCreationUrlValue(state.profileId) ||
normalizeCreationUrlValue(state.draftId) ||
normalizeCreationUrlValue(state.workId),
);
}
export function hasPuzzleRuntimeUrlStateValue(state: PuzzleRuntimeUrlState) {
return Boolean(
normalizeCreationUrlValue(state.runtimeSessionId) ||
normalizeCreationUrlValue(state.runtimeProfileId) ||
normalizeCreationUrlValue(state.runtimeLevelId) ||
normalizeCreationUrlValue(state.publicWorkCode) ||
normalizeCreationUrlValue(state.mode),
);
}
export function buildPuzzleRuntimeUrlStateKey(state: PuzzleRuntimeUrlState) {
return [
normalizeCreationUrlValue(state.mode),
normalizeCreationUrlValue(state.runtimeSessionId),
normalizeCreationUrlValue(state.runtimeProfileId),
normalizeCreationUrlValue(state.runtimeLevelId),
normalizeCreationUrlValue(state.publicWorkCode),
].join('|');
}
export type CreationUrlRestoreTargetKind =
| 'big-fish'
| 'match3d'
| 'square-hole'
| 'puzzle'
| 'visual-novel'
| 'bark-battle'
| 'baby-object-match'
| 'jump-hop'
| 'wooden-fish';
type CreationUrlRestoreTargetBase = {
kind: CreationUrlRestoreTargetKind;
sessionId: string | null;
profileId: string | null;
draftId: string | null;
workId: string | null;
isGeneratingPath: boolean;
};
export type BigFishCreationUrlRestoreTarget = CreationUrlRestoreTargetBase & {
kind: 'big-fish';
bigFishSessionId: string | null;
};
type NonBigFishCreationUrlRestoreTarget = CreationUrlRestoreTargetBase & {
kind: Exclude<CreationUrlRestoreTargetKind, 'big-fish'>;
};
export type CreationUrlRestoreTarget =
| BigFishCreationUrlRestoreTarget
| NonBigFishCreationUrlRestoreTarget;
export type BigFishRestoreWorkIdentity = {
sourceSessionId?: string | null;
workId?: string | null;
};
export type SessionProfileWorkRestoreIdentity = {
sourceSessionId?: string | null;
profileId?: string | null;
workId?: string | null;
};
export type ProfileRestoreWorkIdentity = {
profileId?: string | null;
};
export type BarkBattleRestoreWorkIdentity = {
workId?: string | null;
draftId?: string | null;
};
export type BabyObjectMatchRestoreDraftIdentity = {
profileId?: string | null;
draftId?: string | null;
};
const CREATION_URL_RESTORE_TARGET_ROUTES = [
['/creation/big-fish', 'big-fish'],
['/creation/match3d', 'match3d'],
['/creation/square-hole', 'square-hole'],
['/creation/puzzle', 'puzzle'],
['/creation/visual-novel', 'visual-novel'],
['/creation/bark-battle', 'bark-battle'],
['/creation/baby-object-match', 'baby-object-match'],
['/creation/jump-hop', 'jump-hop'],
['/creation/wooden-fish', 'wooden-fish'],
] as const satisfies readonly (readonly [
string,
CreationUrlRestoreTargetKind,
])[];
export function resolveCreationUrlRestoreTarget(
pathname: string | undefined,
state: CreationUrlState,
): CreationUrlRestoreTarget | null {
const path = pathname?.trim() ?? '';
const route = CREATION_URL_RESTORE_TARGET_ROUTES.find(([prefix]) =>
path === prefix || path.startsWith(`${prefix}/`),
);
if (!route) {
return null;
}
const kind = route[1];
const sessionId = normalizeCreationUrlValue(state.sessionId);
const profileId = normalizeCreationUrlValue(state.profileId);
const draftId = normalizeCreationUrlValue(state.draftId);
const workId = normalizeCreationUrlValue(state.workId);
const base = {
kind,
sessionId,
profileId,
draftId,
workId,
isGeneratingPath: path.includes('/generating'),
};
if (kind === 'big-fish') {
return {
...base,
kind,
bigFishSessionId:
sessionId ?? workId?.replace(/^big-fish-work-/u, '') ?? null,
};
}
return base as NonBigFishCreationUrlRestoreTarget;
}
function matchesRestoreValue(
itemValue: string | null | undefined,
targetValue: string | null,
) {
return Boolean(targetValue && itemValue === targetValue);
}
export function matchesBigFishCreationUrlRestoreTarget(
item: BigFishRestoreWorkIdentity,
target: BigFishCreationUrlRestoreTarget,
) {
return (
matchesRestoreValue(item.sourceSessionId, target.bigFishSessionId) ||
matchesRestoreValue(item.workId, target.workId)
);
}
export function matchesSessionProfileWorkCreationUrlRestoreTarget(
item: SessionProfileWorkRestoreIdentity,
target: Pick<CreationUrlRestoreTarget, 'sessionId' | 'profileId' | 'workId'>,
) {
return (
matchesRestoreValue(item.sourceSessionId, target.sessionId) ||
matchesRestoreValue(item.profileId, target.profileId) ||
matchesRestoreValue(item.workId, target.workId)
);
}
export function matchesVisualNovelCreationUrlRestoreTarget(
item: ProfileRestoreWorkIdentity,
target: Pick<CreationUrlRestoreTarget, 'profileId'>,
) {
return matchesRestoreValue(item.profileId, target.profileId);
}
export function matchesBarkBattleCreationUrlRestoreTarget(
item: BarkBattleRestoreWorkIdentity,
target: Pick<CreationUrlRestoreTarget, 'workId' | 'draftId'>,
) {
return (
matchesRestoreValue(item.workId, target.workId) ||
matchesRestoreValue(item.draftId, target.draftId)
);
}
export function matchesBabyObjectMatchCreationUrlRestoreTarget(
item: BabyObjectMatchRestoreDraftIdentity,
target: Pick<CreationUrlRestoreTarget, 'profileId' | 'draftId' | 'workId'>,
) {
return (
matchesRestoreValue(item.profileId, target.profileId) ||
matchesRestoreValue(item.draftId, target.draftId) ||
matchesRestoreValue(item.profileId, target.workId)
);
}
export function resolveJumpHopCreationUrlRestoreStage(params: {
isGeneratingPath: boolean;
hasRestoredDraft: boolean;
hasRestoredWork: boolean;
}): SelectionStage {
if (params.isGeneratingPath) {
return 'jump-hop-generating';
}
return params.hasRestoredDraft || params.hasRestoredWork
? 'jump-hop-result'
: 'jump-hop-workspace';
}
export function resolveWoodenFishCreationUrlRestoreStage(params: {
isGeneratingPath: boolean;
hasRestoredDraft: boolean;
}): SelectionStage {
if (params.isGeneratingPath) {
return 'wooden-fish-generating';
}
return params.hasRestoredDraft
? 'wooden-fish-result'
: 'wooden-fish-workspace';
}
export type InitialCreationUrlRestoreDecision =
| { type: 'skip' }
| { type: 'mark-handled' }
| { type: 'wait' }
| { type: 'restore' };
export function resolveInitialCreationUrlRestoreDecision(params: {
handled: boolean;
pathname: string | undefined;
state: CreationUrlState;
isLoadingPlatform: boolean;
canReadProtectedData: boolean;
}): InitialCreationUrlRestoreDecision {
if (params.handled) {
return { type: 'skip' };
}
if (
!isCreationRestorePath(params.pathname) ||
!hasCreationUrlStateValue(params.state)
) {
return { type: 'mark-handled' };
}
if (params.isLoadingPlatform || !params.canReadProtectedData) {
return { type: 'wait' };
}
return { type: 'restore' };
}
export function buildBigFishCreationUrlState(
session: BigFishSessionSnapshotResponse | null,
): CreationUrlState {
const sessionId = normalizeCreationUrlValue(session?.sessionId);
return {
sessionId,
workId: sessionId ? `big-fish-work-${sessionId}` : null,
};
}
export function buildMatch3DCreationUrlState(
session: Match3DAgentSessionSnapshot | null,
): CreationUrlState {
const sessionId = normalizeCreationUrlValue(session?.sessionId);
const profileId = normalizeCreationUrlValue(
session?.draft?.profileId ?? session?.publishedProfileId,
);
return {
sessionId,
profileId,
workId: profileId,
};
}
export function buildSquareHoleCreationUrlState(
session: SquareHoleSessionSnapshot | null,
): CreationUrlState {
const sessionId = normalizeCreationUrlValue(session?.sessionId);
const profileId = normalizeCreationUrlValue(
session?.draft?.profileId ?? session?.publishedProfileId,
);
return {
sessionId,
profileId,
workId: profileId,
};
}
export function buildPuzzleCreationUrlState(
session: PuzzleAgentSessionSnapshot | null,
): CreationUrlState {
const sessionId = normalizeCreationUrlValue(session?.sessionId);
const profileId = normalizeCreationUrlValue(
session?.publishedProfileId ?? buildPuzzleResultProfileId(sessionId),
);
return {
sessionId,
profileId,
workId: sessionId ? buildPuzzleResultWorkId(sessionId) : null,
};
}
export function buildPuzzleDraftRuntimeUrlState(
item: PuzzleWorkSummary,
levelId?: string | null,
): PuzzleRuntimeUrlState {
const runtimeSessionId =
normalizeCreationUrlValue(item.sourceSessionId) ??
buildPuzzleSessionIdFromProfileId(item.profileId);
return {
mode: 'draft',
runtimeSessionId,
runtimeProfileId: normalizeCreationUrlValue(item.profileId),
runtimeLevelId: normalizeCreationUrlValue(levelId),
};
}
export function buildPuzzlePublishedRuntimeUrlState(
item: PuzzleWorkSummary,
levelId?: string | null,
): PuzzleRuntimeUrlState {
return {
mode: 'published',
runtimeProfileId: normalizeCreationUrlValue(item.profileId),
runtimeLevelId: normalizeCreationUrlValue(levelId),
publicWorkCode: buildPuzzlePublicWorkCode(item.profileId),
};
}
export function buildVisualNovelCreationUrlState(
session: VisualNovelAgentSessionSnapshot | null,
): CreationUrlState {
const sessionId = normalizeCreationUrlValue(session?.sessionId);
const profileId = normalizeCreationUrlValue(session?.draft?.profileId);
return {
sessionId,
profileId,
workId: profileId ?? sessionId,
};
}
export function buildJumpHopCreationUrlState(params: {
session?: JumpHopSessionSnapshotResponse | null;
work?: JumpHopWorkProfileResponse | null;
}): CreationUrlState {
const sessionId = normalizeCreationUrlValue(params.session?.sessionId);
const profileId = normalizeCreationUrlValue(
params.work?.summary.profileId ?? params.session?.draft?.profileId,
);
return {
sessionId,
profileId,
workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId),
};
}
export function buildWoodenFishCreationUrlState(params: {
session?: WoodenFishSessionSnapshotResponse | null;
work?: WoodenFishWorkProfileResponse | null;
}): CreationUrlState {
const sessionId = normalizeCreationUrlValue(params.session?.sessionId);
const profileId = normalizeCreationUrlValue(
params.work?.summary.profileId ?? params.session?.draft?.profileId,
);
const draftId = profileId ?? sessionId;
return {
sessionId,
profileId,
draftId,
workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId),
};
}
export function buildBarkBattleCreationUrlState(
draft: BarkBattleDraftConfig | null,
): CreationUrlState {
return {
draftId: normalizeCreationUrlValue(draft?.draftId),
workId: normalizeCreationUrlValue(draft?.workId ?? draft?.draftId),
};
}
export function buildBabyObjectMatchCreationUrlState(
draft: BabyObjectMatchDraft | null,
): CreationUrlState {
const profileId = normalizeCreationUrlValue(draft?.profileId);
return {
profileId,
draftId: normalizeCreationUrlValue(draft?.draftId),
workId: profileId,
};
}