refactor: 收口创作直达恢复目标

This commit is contained in:
2026-06-04 01:57:13 +08:00
parent dbc00be2cc
commit a504da1e32
7 changed files with 213 additions and 27 deletions

View File

@@ -409,6 +409,7 @@ import {
buildWoodenFishCreationUrlState,
hasPuzzleRuntimeUrlStateValue,
normalizeCreationUrlValue,
resolveCreationUrlRestoreTarget,
resolveInitialCreationUrlRestoreDecision,
} from './platformCreationUrlStateModel';
import { resolvePlatformCreationWorkDeleteConfirmationModel } from './platformCreationWorkDeleteFlow';
@@ -12142,21 +12143,17 @@ export function PlatformEntryFlowShellImpl({
handledInitialCreationUrlStateRef.current = true;
const restoreCreationUrlState = async () => {
const path = window.location.pathname;
const sessionId = normalizeCreationUrlValue(
initialCreationUrlState.sessionId,
const target = resolveCreationUrlRestoreTarget(
window.location.pathname,
initialCreationUrlState,
);
const profileId = normalizeCreationUrlValue(
initialCreationUrlState.profileId,
);
const draftId = normalizeCreationUrlValue(
initialCreationUrlState.draftId,
);
const workId = normalizeCreationUrlValue(initialCreationUrlState.workId);
if (!target) {
return;
}
const { sessionId, profileId, draftId, workId } = target;
if (path.startsWith('/creation/big-fish')) {
const targetSessionId =
sessionId ?? workId?.replace(/^big-fish-work-/u, '');
if (target.kind === 'big-fish') {
const targetSessionId = target.bigFishSessionId;
if (targetSessionId) {
const matchedWork =
(bigFishWorks.length > 0
@@ -12176,7 +12173,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/match3d')) {
if (target.kind === 'match3d') {
const matchedWork =
(match3dWorks.length > 0
? match3dWorks
@@ -12199,7 +12196,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/square-hole')) {
if (target.kind === 'square-hole') {
const matchedWork =
(squareHoleWorks.length > 0
? squareHoleWorks
@@ -12220,7 +12217,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/puzzle')) {
if (target.kind === 'puzzle') {
const matchedWork =
(puzzleWorks.length > 0
? puzzleWorks
@@ -12241,7 +12238,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/visual-novel')) {
if (target.kind === 'visual-novel') {
const matchedWork =
(visualNovelWorks.length > 0
? visualNovelWorks
@@ -12257,7 +12254,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/bark-battle')) {
if (target.kind === 'bark-battle') {
const matchedWork =
(barkBattleWorks.length > 0
? barkBattleWorks
@@ -12271,7 +12268,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/baby-object-match')) {
if (target.kind === 'baby-object-match') {
const matchedDraft =
(babyObjectMatchDrafts.length > 0
? babyObjectMatchDrafts
@@ -12288,7 +12285,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/jump-hop')) {
if (target.kind === 'jump-hop') {
let session: JumpHopSessionSnapshotResponse | null = null;
let work: JumpHopWorkProfileResponse | null = null;
try {
@@ -12316,7 +12313,7 @@ export function PlatformEntryFlowShellImpl({
);
enterCreateTab();
setSelectionStage(
path.includes('/generating')
target.isGeneratingPath
? 'jump-hop-generating'
: session?.draft || work
? 'jump-hop-result'
@@ -12330,7 +12327,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (path.startsWith('/creation/wooden-fish')) {
if (target.kind === 'wooden-fish') {
if (!sessionId) {
return;
}
@@ -12347,7 +12344,7 @@ export function PlatformEntryFlowShellImpl({
);
enterCreateTab();
setSelectionStage(
path.includes('/generating')
target.isGeneratingPath
? 'wooden-fish-generating'
: session.draft
? 'wooden-fish-result'

View File

@@ -32,6 +32,7 @@ import {
hasCreationUrlStateValue,
hasPuzzleRuntimeUrlStateValue,
normalizeCreationUrlValue,
resolveCreationUrlRestoreTarget,
resolveInitialCreationUrlRestoreDecision,
} from './platformCreationUrlStateModel';
@@ -94,6 +95,104 @@ describe('platformCreationUrlStateModel', () => {
});
});
test('resolves supported creation url restore targets from paths', () => {
const state = {
sessionId: ' session-1 ',
profileId: ' profile-1 ',
draftId: ' draft-1 ',
workId: ' work-1 ',
};
const cases = [
['/creation/big-fish/result', 'big-fish'],
['/creation/match3d/result', 'match3d'],
['/creation/square-hole/result', 'square-hole'],
['/creation/puzzle/result', 'puzzle'],
['/creation/visual-novel/result', 'visual-novel'],
['/creation/bark-battle/result', 'bark-battle'],
['/creation/baby-object-match/result', 'baby-object-match'],
['/creation/jump-hop/result', 'jump-hop'],
['/creation/wooden-fish/result', 'wooden-fish'],
] as const;
cases.forEach(([pathname, kind]) => {
expect(resolveCreationUrlRestoreTarget(pathname, state)).toMatchObject({
kind,
sessionId: 'session-1',
profileId: 'profile-1',
draftId: 'draft-1',
workId: 'work-1',
isGeneratingPath: false,
});
});
});
test('normalizes creation url restore target values and generating paths', () => {
expect(
resolveCreationUrlRestoreTarget('/creation/jump-hop/generating', {
sessionId: ' ',
profileId: ' jump-profile-1 ',
draftId: undefined,
workId: null,
}),
).toEqual({
kind: 'jump-hop',
sessionId: null,
profileId: 'jump-profile-1',
draftId: null,
workId: null,
isGeneratingPath: true,
});
});
test('derives big fish restore session from work id when needed', () => {
expect(
resolveCreationUrlRestoreTarget('/creation/big-fish/result', {
workId: 'big-fish-work-river',
}),
).toEqual({
kind: 'big-fish',
sessionId: null,
profileId: null,
draftId: null,
workId: 'big-fish-work-river',
isGeneratingPath: false,
bigFishSessionId: 'river',
});
expect(
resolveCreationUrlRestoreTarget('/creation/big-fish/result', {
sessionId: 'big-fish-session-carp',
workId: 'big-fish-work-river',
}),
).toMatchObject({
kind: 'big-fish',
bigFishSessionId: 'big-fish-session-carp',
});
});
test('keeps unsupported creation paths without a concrete restore target', () => {
expect(
resolveCreationUrlRestoreTarget('/creation/rpg/result', {
sessionId: 'rpg-session-1',
}),
).toBeNull();
expect(
resolveCreationUrlRestoreTarget('/creation/unknown/result', {
sessionId: 'unknown-session-1',
}),
).toBeNull();
expect(
resolveCreationUrlRestoreTarget('/creation/big-fishery/result', {
sessionId: 'big-fish-session-1',
}),
).toBeNull();
expect(
resolveCreationUrlRestoreTarget('/works/detail', {
workId: 'work-1',
}),
).toBeNull();
});
test('builds creation restore state for core session based plays', () => {
expect(
buildBigFishCreationUrlState({

View File

@@ -60,6 +60,93 @@ export function buildPuzzleRuntimeUrlStateKey(state: PuzzleRuntimeUrlState) {
].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 CreationUrlRestoreTarget =
| (CreationUrlRestoreTargetBase & {
kind: 'big-fish';
bigFishSessionId: string | null;
})
| (CreationUrlRestoreTargetBase & {
kind: Exclude<CreationUrlRestoreTargetKind, 'big-fish'>;
});
type NonBigFishCreationUrlRestoreTarget = Extract<
CreationUrlRestoreTarget,
{ kind: Exclude<CreationUrlRestoreTargetKind, 'big-fish'> }
>;
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;
}
export type InitialCreationUrlRestoreDecision =
| { type: 'skip' }
| { type: 'mark-handled' }