432 lines
13 KiB
TypeScript
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,
|
|
};
|
|
}
|