fix: reconcile architecture adjustment merge

This commit is contained in:
2026-06-07 00:57:23 +08:00
parent ce930ee5c3
commit 48ef19d518
20 changed files with 431 additions and 33 deletions

View File

@@ -219,6 +219,7 @@ import {
buildSquareHoleGenerationAnchorEntries, buildSquareHoleGenerationAnchorEntries,
buildWoodenFishGenerationAnchorEntries, buildWoodenFishGenerationAnchorEntries,
createMiniGameDraftGenerationState, createMiniGameDraftGenerationState,
type MiniGameDraftGenerationKind,
type MiniGameDraftGenerationState, type MiniGameDraftGenerationState,
resolveMiniGameDraftGenerationStartedAtMs, resolveMiniGameDraftGenerationStartedAtMs,
} from '../../services/miniGameDraftGenerationProgress'; } from '../../services/miniGameDraftGenerationProgress';
@@ -234,6 +235,7 @@ import {
buildSquareHolePublicWorkCode, buildSquareHolePublicWorkCode,
buildVisualNovelPublicWorkCode, buildVisualNovelPublicWorkCode,
buildWoodenFishPublicWorkCode, buildWoodenFishPublicWorkCode,
isSamePuzzleClearPublicWorkCode,
isSamePuzzlePublicWorkCode, isSamePuzzlePublicWorkCode,
} from '../../services/publicWorkCode'; } from '../../services/publicWorkCode';
import { import {
@@ -373,7 +375,12 @@ import {
selectAdjacentPlatformRecommendEntry, selectAdjacentPlatformRecommendEntry,
} from '../rpg-entry/rpgEntryPublicGalleryViewModel'; } from '../rpg-entry/rpgEntryPublicGalleryViewModel';
import { import {
isBigFishGalleryEntry,
isEdutainmentGalleryEntry, isEdutainmentGalleryEntry,
isJumpHopGalleryEntry,
isPuzzleGalleryEntry,
isPuzzleClearGalleryEntry,
mapPuzzleClearWorkToPlatformGalleryCard,
mapPuzzleWorkToPlatformGalleryCard, mapPuzzleWorkToPlatformGalleryCard,
type PlatformPublicGalleryCard, type PlatformPublicGalleryCard,
resolvePlatformPublicWorkCode, resolvePlatformPublicWorkCode,
@@ -415,6 +422,7 @@ import {
buildBigFishCreationUrlState, buildBigFishCreationUrlState,
buildJumpHopCreationUrlState, buildJumpHopCreationUrlState,
buildMatch3DCreationUrlState, buildMatch3DCreationUrlState,
buildPuzzleClearCreationUrlState,
buildPuzzleCreationUrlState, buildPuzzleCreationUrlState,
buildPuzzleDraftRuntimeUrlState, buildPuzzleDraftRuntimeUrlState,
buildPuzzlePublishedRuntimeUrlState, buildPuzzlePublishedRuntimeUrlState,
@@ -456,6 +464,7 @@ import {
buildPendingBigFishWorks, buildPendingBigFishWorks,
buildPendingJumpHopWorks, buildPendingJumpHopWorks,
buildPendingMatch3DWorks, buildPendingMatch3DWorks,
buildPendingPuzzleClearWorks,
buildPendingPuzzleWorks, buildPendingPuzzleWorks,
buildPendingSquareHoleWorks, buildPendingSquareHoleWorks,
buildPendingVisualNovelWorks, buildPendingVisualNovelWorks,
@@ -557,6 +566,8 @@ import {
} from './platformMiniGameDraftPayloadModel'; } from './platformMiniGameDraftPayloadModel';
import { import {
buildJumpHopPendingSession, buildJumpHopPendingSession,
buildPuzzleClearPendingSession,
buildPuzzleClearSessionFromWorkDetail,
buildPuzzleRuntimeWorkFromSession, buildPuzzleRuntimeWorkFromSession,
buildSquareHoleProfileFromSession, buildSquareHoleProfileFromSession,
buildVisualNovelSessionFromWorkDetail, buildVisualNovelSessionFromWorkDetail,
@@ -752,7 +763,7 @@ async function resumePuzzleProfileSaveArchiveRaw(worldKey: string) {
); );
} }
const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS = const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions =
BACKGROUND_AUTH_REQUEST_OPTIONS; BACKGROUND_AUTH_REQUEST_OPTIONS;
const RECOMMEND_PUZZLE_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions = const RECOMMEND_PUZZLE_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions =
RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS; RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
@@ -778,6 +789,16 @@ function resolveCurrentRecommendRuntimeAuthPlan(
hasStoredAccessToken: Boolean(getStoredAccessToken()), hasStoredAccessToken: Boolean(getStoredAccessToken()),
}); });
} }
function shouldUseRecommendRuntimeGuestAuth(authUi: RecommendRuntimeAuthUi) {
return (
resolveCurrentRecommendRuntimeAuthPlan(authUi, {
embedded: true,
allowRuntimeGuestAuth: true,
}).requestKind === 'runtime-guest'
);
}
async function buildRecommendRuntimeOptionsFromAuthPlan( async function buildRecommendRuntimeOptionsFromAuthPlan(
plan: ReturnType<typeof resolvePlatformRecommendRuntimeAuthPlan>, plan: ReturnType<typeof resolvePlatformRecommendRuntimeAuthPlan>,
) { ) {
@@ -797,6 +818,28 @@ async function buildRecommendRuntimeAuthOptions(
resolveCurrentRecommendRuntimeAuthPlan(authUi, { embedded }), resolveCurrentRecommendRuntimeAuthPlan(authUi, { embedded }),
); );
} }
function resolveRecommendEntryShareStage(
entry: PlatformPublicGalleryCard,
): PublishShareModalPayload['stage'] {
if (isBigFishGalleryEntry(entry)) {
return 'big-fish-runtime';
}
if (isPuzzleGalleryEntry(entry)) {
return 'puzzle-gallery-detail';
}
return 'work-detail';
}
function pushPuzzleResultHistoryEntry(
session: PuzzleAgentSessionSnapshot | null,
) {
pushAppHistoryPath('/creation/puzzle/result');
writeCreationUrlState(buildPuzzleCreationUrlState(session));
}
const PUZZLE_DRAFT_GENERATION_POINT_COST = 2; const PUZZLE_DRAFT_GENERATION_POINT_COST = 2;
const MATCH3D_DRAFT_GENERATION_POINT_COST = 10; const MATCH3D_DRAFT_GENERATION_POINT_COST = 10;
const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3; const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3;
@@ -2795,6 +2838,7 @@ export function PlatformEntryFlowShellImpl({
bigFishEntries: bigFishGalleryEntries, bigFishEntries: bigFishGalleryEntries,
match3dEntries: match3dGalleryEntries, match3dEntries: match3dGalleryEntries,
puzzleEntries: puzzleGalleryEntries, puzzleEntries: puzzleGalleryEntries,
puzzleClearEntries: puzzleClearGalleryEntries,
barkBattleGalleryEntries, barkBattleGalleryEntries,
barkBattleWorks, barkBattleWorks,
jumpHopEntries: jumpHopGalleryEntries, jumpHopEntries: jumpHopGalleryEntries,
@@ -12358,6 +12402,12 @@ export function PlatformEntryFlowShellImpl({
{ authMode: intent.authMode }, { authMode: intent.authMode },
); );
return; return;
case 'start-puzzle-clear':
setPublicWorkDetailError(null);
void startPuzzleClearRunFromProfile(intent.profileId, {
returnStage: intent.returnStage,
});
return;
case 'start-jump-hop': case 'start-jump-hop':
setPublicWorkDetailError(null); setPublicWorkDetailError(null);
void startJumpHopRunFromProfile(intent.profileId, { void startJumpHopRunFromProfile(intent.profileId, {
@@ -12491,6 +12541,8 @@ export function PlatformEntryFlowShellImpl({
setBigFishError(intent.errorMessage); setBigFishError(intent.errorMessage);
} else if (intent.errorTarget === 'puzzle') { } else if (intent.errorTarget === 'puzzle') {
setPuzzleError(intent.errorMessage); setPuzzleError(intent.errorMessage);
} else if (intent.errorTarget === 'puzzle-clear') {
setPuzzleClearError(intent.errorMessage);
} else if (intent.errorTarget === 'match3d') { } else if (intent.errorTarget === 'match3d') {
setMatch3DError(intent.errorMessage); setMatch3DError(intent.errorMessage);
} else if (intent.errorTarget === 'square-hole') { } else if (intent.errorTarget === 'square-hole') {
@@ -12514,6 +12566,12 @@ export function PlatformEntryFlowShellImpl({
{ embedded: intent.embedded }, { embedded: intent.embedded },
); );
break; break;
case 'start-puzzle-clear':
started = await startPuzzleClearRunFromProfile(intent.profileId, {
embedded: intent.embedded,
returnStage: intent.returnStage,
});
break;
case 'start-jump-hop': case 'start-jump-hop':
started = await startJumpHopRunFromProfile(intent.profileId, { started = await startJumpHopRunFromProfile(intent.profileId, {
embedded: intent.embedded, embedded: intent.embedded,
@@ -13092,6 +13150,7 @@ export function PlatformEntryFlowShellImpl({
hasBigFishRun: Boolean(bigFishRun), hasBigFishRun: Boolean(bigFishRun),
hasJumpHopRun: Boolean(jumpHopRun), hasJumpHopRun: Boolean(jumpHopRun),
hasMatch3DRun: Boolean(match3dRun), hasMatch3DRun: Boolean(match3dRun),
hasPuzzleClearRun: Boolean(puzzleClearRun),
hasSquareHoleRun: Boolean(squareHoleRun), hasSquareHoleRun: Boolean(squareHoleRun),
hasVisualNovelRun: Boolean(visualNovelRun), hasVisualNovelRun: Boolean(visualNovelRun),
hasWoodenFishRun: Boolean(woodenFishRun), hasWoodenFishRun: Boolean(woodenFishRun),
@@ -13114,6 +13173,7 @@ export function PlatformEntryFlowShellImpl({
hasBigFishRun: Boolean(bigFishRun), hasBigFishRun: Boolean(bigFishRun),
hasJumpHopRun: Boolean(jumpHopRun), hasJumpHopRun: Boolean(jumpHopRun),
hasMatch3DRun: Boolean(match3dRun), hasMatch3DRun: Boolean(match3dRun),
hasPuzzleClearRun: Boolean(puzzleClearRun),
hasSquareHoleRun: Boolean(squareHoleRun), hasSquareHoleRun: Boolean(squareHoleRun),
hasVisualNovelRun: Boolean(visualNovelRun), hasVisualNovelRun: Boolean(visualNovelRun),
hasWoodenFishRun: Boolean(woodenFishRun), hasWoodenFishRun: Boolean(woodenFishRun),

View File

@@ -3,6 +3,10 @@ import type { BigFishSessionSnapshotResponse } from '../../../packages/shared/sr
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import type { Match3DAgentSessionSnapshot } from '../../../packages/shared/src/contracts/match3dAgent'; import type { Match3DAgentSessionSnapshot } from '../../../packages/shared/src/contracts/match3dAgent';
import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession'; import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession';
import type {
PuzzleClearSessionSnapshotResponse,
PuzzleClearWorkProfileResponse,
} from '../../../packages/shared/src/contracts/puzzleClear';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { SquareHoleSessionSnapshot } from '../../../packages/shared/src/contracts/squareHoleAgent'; import type { SquareHoleSessionSnapshot } from '../../../packages/shared/src/contracts/squareHoleAgent';
import type { VisualNovelAgentSessionSnapshot } from '../../../packages/shared/src/contracts/visualNovel'; import type { VisualNovelAgentSessionSnapshot } from '../../../packages/shared/src/contracts/visualNovel';
@@ -393,6 +397,21 @@ export function buildJumpHopCreationUrlState(params: {
}; };
} }
export function buildPuzzleClearCreationUrlState(params: {
session?: PuzzleClearSessionSnapshotResponse | null;
work?: PuzzleClearWorkProfileResponse | 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: { export function buildWoodenFishCreationUrlState(params: {
session?: WoodenFishSessionSnapshotResponse | null; session?: WoodenFishSessionSnapshotResponse | null;
work?: WoodenFishWorkProfileResponse | null; work?: WoodenFishWorkProfileResponse | null;

View File

@@ -751,6 +751,7 @@ function buildJumpHopWork(
profileId: 'jump-hop-profile-base', profileId: 'jump-hop-profile-base',
ownerUserId: 'user-1', ownerUserId: 'user-1',
sourceSessionId: 'jump-hop-session-base', sourceSessionId: 'jump-hop-session-base',
themeText: '潮雾港口',
workTitle: '潮雾跳一跳', workTitle: '潮雾跳一跳',
workDescription: '潮雾港口跳一跳。', workDescription: '潮雾港口跳一跳。',
themeTags: [], themeTags: [],

View File

@@ -4,6 +4,7 @@ import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contra
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import type { JumpHopWorkSummaryResponse } from '../../../packages/shared/src/contracts/jumpHop'; import type { JumpHopWorkSummaryResponse } from '../../../packages/shared/src/contracts/jumpHop';
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks'; import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
import type { PuzzleClearWorkSummaryResponse } from '../../../packages/shared/src/contracts/puzzleClear';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks'; import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel'; import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel';
@@ -1191,6 +1192,7 @@ export function buildPendingJumpHopWorks(
profileId: `jump-hop-profile-${sessionId}`, profileId: `jump-hop-profile-${sessionId}`,
ownerUserId: '', ownerUserId: '',
sourceSessionId: sessionId, sourceSessionId: sessionId,
themeText: state.title ?? '跳一跳草稿',
workTitle: '跳一跳草稿', workTitle: '跳一跳草稿',
workDescription: workDescription:
state.status === 'failed' state.status === 'failed'
@@ -1210,6 +1212,48 @@ export function buildPendingJumpHopWorks(
}); });
} }
export function buildPendingPuzzleClearWorks(
pending: Record<string, PendingDraftShelfState> | undefined,
existingItems: readonly PuzzleClearWorkSummaryResponse[],
): PuzzleClearWorkSummaryResponse[] {
if (!pending) {
return [];
}
return Object.entries(pending)
.filter(([sessionId]) =>
existingItems.every((item) => item.sourceSessionId !== sessionId),
)
.map(([sessionId, state]) => {
const generationStatus =
state.status === 'failed'
? 'failed'
: state.status === 'generating'
? 'generating'
: 'ready';
return {
runtimeKind: 'puzzle-clear',
workId: `puzzle-clear-work-${sessionId}`,
profileId: sessionId,
ownerUserId: '',
sourceSessionId: sessionId,
workTitle: '拼消消草稿',
workDescription:
state.status === 'failed'
? '拼消消草稿生成失败,可重新打开处理。'
: '正在生成拼消消草稿。',
themePrompt: '',
coverImageSrc: null,
publicationStatus: 'draft',
playCount: 0,
updatedAt: state.updatedAt,
publishedAt: null,
publishReady: false,
generationStatus,
};
});
}
export function buildPendingWoodenFishWorks( export function buildPendingWoodenFishWorks(
pending: Record<string, PendingDraftShelfState> | undefined, pending: Record<string, PendingDraftShelfState> | undefined,
existingItems: readonly WoodenFishWorkSummaryResponse[], existingItems: readonly WoodenFishWorkSummaryResponse[],

View File

@@ -213,6 +213,7 @@ function buildJumpHopDraft(
templateId: 'jump-hop', templateId: 'jump-hop',
templateName: '跳一跳', templateName: '跳一跳',
profileId: 'jump-hop-profile-1', profileId: 'jump-hop-profile-1',
themeText: '草稿主题',
workTitle: '草稿跳一跳', workTitle: '草稿跳一跳',
workDescription: '从草稿恢复。', workDescription: '从草稿恢复。',
themeTags: ['草稿'], themeTags: ['草稿'],
@@ -236,6 +237,7 @@ function buildJumpHopPayload(
): JumpHopWorkspaceCreateRequest { ): JumpHopWorkspaceCreateRequest {
return { return {
templateId: 'jump-hop', templateId: 'jump-hop',
themeText: '表单主题',
workTitle: '表单跳一跳', workTitle: '表单跳一跳',
workDescription: '从表单提交。', workDescription: '从表单提交。',
themeTags: ['表单'], themeTags: ['表单'],

View File

@@ -76,7 +76,7 @@ export function buildPuzzleWorkUpdatePayloadFromDraft(
} }
export function buildJumpHopDraftActionPayload( export function buildJumpHopDraftActionPayload(
actionType: 'compile-draft' | 'regenerate-character' | 'regenerate-tiles', actionType: 'compile-draft' | 'regenerate-tiles',
input: { input: {
payload?: JumpHopWorkspaceCreateRequest | null; payload?: JumpHopWorkspaceCreateRequest | null;
draft?: JumpHopSessionSnapshotResponse['draft'] | null; draft?: JumpHopSessionSnapshotResponse['draft'] | null;

View File

@@ -120,6 +120,7 @@ function buildJumpHopSummary(
profileId: 'jump-hop-profile-1', profileId: 'jump-hop-profile-1',
ownerUserId: 'user-1', ownerUserId: 'user-1',
sourceSessionId: ' jump-hop-session-1 ', sourceSessionId: ' jump-hop-session-1 ',
themeText: '云阶机关',
workTitle: '云阶跳跃', workTitle: '云阶跳跃',
workDescription: '越过云阶。', workDescription: '越过云阶。',
themeTags: ['云阶'], themeTags: ['云阶'],
@@ -522,6 +523,7 @@ describe('platformMiniGameSessionMappingModel', () => {
templateId: 'jump-hop', templateId: 'jump-hop',
templateName: '跳一跳', templateName: '跳一跳',
profileId: 'jump-hop-profile-1', profileId: 'jump-hop-profile-1',
themeText: '云阶机关',
workTitle: '云阶跳跃', workTitle: '云阶跳跃',
workDescription: '越过云阶。', workDescription: '越过云阶。',
themeTags: ['云阶'], themeTags: ['云阶'],

View File

@@ -1,4 +1,12 @@
import type { JumpHopSessionSnapshotResponse, JumpHopWorkSummaryResponse } from '../../../packages/shared/src/contracts/jumpHop'; import type {
JumpHopSessionSnapshotResponse,
JumpHopWorkSummaryResponse,
} from '../../../packages/shared/src/contracts/jumpHop';
import type {
PuzzleClearSessionSnapshotResponse,
PuzzleClearWorkProfileResponse,
PuzzleClearWorkSummaryResponse,
} from '../../../packages/shared/src/contracts/puzzleClear';
import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession'; import type { PuzzleAgentSessionSnapshot } from '../../../packages/shared/src/contracts/puzzleAgentSession';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { SquareHoleSessionSnapshot } from '../../../packages/shared/src/contracts/squareHoleAgent'; import type { SquareHoleSessionSnapshot } from '../../../packages/shared/src/contracts/squareHoleAgent';
@@ -59,6 +67,54 @@ export function buildPuzzleRuntimeWorkFromSession(
}; };
} }
export function buildPuzzleClearSessionFromWorkDetail(
work: PuzzleClearWorkProfileResponse,
fallbackItem?: PuzzleClearWorkSummaryResponse | null,
): PuzzleClearSessionSnapshotResponse {
const sessionId =
normalizeCreationUrlValue(work.summary.sourceSessionId) ??
normalizeCreationUrlValue(fallbackItem?.sourceSessionId) ??
work.summary.profileId;
return {
sessionId,
ownerUserId: work.summary.ownerUserId,
status: work.summary.generationStatus,
draft: work.draft,
createdAt: work.summary.updatedAt,
updatedAt: work.summary.updatedAt,
};
}
export function buildPuzzleClearPendingSession(
item: PuzzleClearWorkSummaryResponse,
): PuzzleClearSessionSnapshotResponse {
const sessionId =
normalizeCreationUrlValue(item.sourceSessionId) ?? item.profileId;
return {
sessionId,
ownerUserId: item.ownerUserId,
status: item.generationStatus,
draft: {
templateId: 'puzzle-clear',
templateName: '拼消消',
profileId: item.profileId,
workTitle: item.workTitle,
workDescription: item.workDescription,
themePrompt: item.themePrompt,
boardBackgroundPrompt: item.themePrompt,
generateBoardBackground: true,
boardBackgroundAsset: null,
cardBackImageSrc: null,
atlasAsset: null,
patternGroups: [],
cardAssets: [],
generationStatus: item.generationStatus,
},
createdAt: item.updatedAt,
updatedAt: item.updatedAt,
};
}
export function buildSquareHoleProfileFromSession( export function buildSquareHoleProfileFromSession(
session: SquareHoleSessionSnapshot | null, session: SquareHoleSessionSnapshot | null,
): SquareHoleWorkProfile | null { ): SquareHoleWorkProfile | null {
@@ -122,6 +178,7 @@ export function buildJumpHopPendingSession(
templateId: 'jump-hop', templateId: 'jump-hop',
templateName: '跳一跳', templateName: '跳一跳',
profileId: item.profileId, profileId: item.profileId,
themeText: item.themeText,
workTitle: item.workTitle, workTitle: item.workTitle,
workDescription: item.workDescription, workDescription: item.workDescription,
themeTags: item.themeTags, themeTags: item.themeTags,

View File

@@ -303,6 +303,7 @@ function buildJumpHopCard(
profileId, profileId,
ownerUserId: 'user-1', ownerUserId: 'user-1',
authorDisplayName: '测试作者', authorDisplayName: '测试作者',
themeText: '潮雾港',
workTitle: '潮雾跳一跳', workTitle: '潮雾跳一跳',
workDescription: '潮雾跳一跳说明。', workDescription: '潮雾跳一跳说明。',
coverImageSrc: null, coverImageSrc: null,

View File

@@ -128,6 +128,7 @@ function buildJumpHopEntry(
profileId: 'jump-hop-profile', profileId: 'jump-hop-profile',
ownerUserId: 'user-1', ownerUserId: 'user-1',
authorDisplayName: '玩家', authorDisplayName: '玩家',
themeText: '一路向前',
workTitle: '跳一跳', workTitle: '跳一跳',
workDescription: '一路向前。', workDescription: '一路向前。',
coverImageSrc: '/jump-hop-cover.png', coverImageSrc: '/jump-hop-cover.png',
@@ -166,6 +167,13 @@ function buildTypedEntry(
switch (sourceType) { switch (sourceType) {
case 'puzzle': case 'puzzle':
return { ...common, ...overrides, sourceType }; return { ...common, ...overrides, sourceType };
case 'puzzle-clear':
return {
...common,
...overrides,
sourceType,
themePrompt: '拼消消主题',
};
case 'big-fish': case 'big-fish':
return { ...common, ...overrides, sourceType }; return { ...common, ...overrides, sourceType };
case 'match3d': case 'match3d':
@@ -769,6 +777,7 @@ test('platform public gallery flow builds feeds with visibility gates and bark b
bigFishEntries: [hiddenBigFish], bigFishEntries: [hiddenBigFish],
match3dEntries: [], match3dEntries: [],
puzzleEntries: [], puzzleEntries: [],
puzzleClearEntries: [],
barkBattleGalleryEntries: [], barkBattleGalleryEntries: [],
barkBattleWorks: [draftBarkFallback, publishedBarkFallback], barkBattleWorks: [draftBarkFallback, publishedBarkFallback],
jumpHopEntries: [], jumpHopEntries: [],
@@ -793,6 +802,7 @@ test('platform public gallery flow builds feeds with visibility gates and bark b
bigFishEntries: [hiddenBigFish], bigFishEntries: [hiddenBigFish],
match3dEntries: [], match3dEntries: [],
puzzleEntries: [], puzzleEntries: [],
puzzleClearEntries: [],
barkBattleGalleryEntries: [ barkBattleGalleryEntries: [
buildBarkBattleWork({ buildBarkBattleWork({
workId: 'gallery-bark', workId: 'gallery-bark',
@@ -828,6 +838,7 @@ test('platform public gallery flow preserves feed tie order and featured slice',
bigFishEntries: [], bigFishEntries: [],
match3dEntries: [], match3dEntries: [],
puzzleEntries: [], puzzleEntries: [],
puzzleClearEntries: [],
barkBattleGalleryEntries: [], barkBattleGalleryEntries: [],
barkBattleWorks: [ barkBattleWorks: [
buildBarkBattleWork({ buildBarkBattleWork({
@@ -868,6 +879,7 @@ test('platform public gallery flow preserves feed tie order and featured slice',
bigFishEntries: [], bigFishEntries: [],
match3dEntries: [], match3dEntries: [],
puzzleEntries: [], puzzleEntries: [],
puzzleClearEntries: [],
barkBattleGalleryEntries: [], barkBattleGalleryEntries: [],
barkBattleWorks: [], barkBattleWorks: [],
jumpHopEntries: [], jumpHopEntries: [],

View File

@@ -8,12 +8,14 @@ import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contra
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks'; import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel'; import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel';
import type { WoodenFishGalleryCardResponse } from '../../../packages/shared/src/contracts/woodenFish'; import type { WoodenFishGalleryCardResponse } from '../../../packages/shared/src/contracts/woodenFish';
import type { PuzzleClearGalleryCardResponse } from '../../services/puzzle-clear/puzzleClearClient';
import { import {
isBarkBattleGalleryEntry, isBarkBattleGalleryEntry,
isBigFishGalleryEntry, isBigFishGalleryEntry,
isEdutainmentGalleryEntry, isEdutainmentGalleryEntry,
isJumpHopGalleryEntry, isJumpHopGalleryEntry,
isMatch3DGalleryEntry, isMatch3DGalleryEntry,
isPuzzleClearGalleryEntry,
isPuzzleGalleryEntry, isPuzzleGalleryEntry,
isSquareHoleGalleryEntry, isSquareHoleGalleryEntry,
isVisualNovelGalleryEntry, isVisualNovelGalleryEntry,
@@ -22,6 +24,7 @@ import {
mapBarkBattleWorkToPlatformGalleryCard, mapBarkBattleWorkToPlatformGalleryCard,
mapBigFishWorkToPlatformGalleryCard, mapBigFishWorkToPlatformGalleryCard,
mapJumpHopWorkToPlatformGalleryCard, mapJumpHopWorkToPlatformGalleryCard,
mapPuzzleClearWorkToPlatformGalleryCard,
mapPuzzleWorkToPlatformGalleryCard, mapPuzzleWorkToPlatformGalleryCard,
mapSquareHoleWorkToPlatformGalleryCard, mapSquareHoleWorkToPlatformGalleryCard,
mapVisualNovelWorkToPlatformGalleryCard, mapVisualNovelWorkToPlatformGalleryCard,
@@ -43,6 +46,7 @@ export type RecommendRuntimeKind =
| 'jump-hop' | 'jump-hop'
| 'match3d' | 'match3d'
| 'puzzle' | 'puzzle'
| 'puzzle-clear'
| 'square-hole' | 'square-hole'
| 'wooden-fish' | 'wooden-fish'
| 'visual-novel' | 'visual-novel'
@@ -53,6 +57,7 @@ export type PlatformRecommendRuntimeStartErrorTarget =
| 'big-fish' | 'big-fish'
| 'match3d' | 'match3d'
| 'puzzle' | 'puzzle'
| 'puzzle-clear'
| 'square-hole'; | 'square-hole';
export type PlatformRecommendRuntimeStartIntent = export type PlatformRecommendRuntimeStartIntent =
@@ -73,6 +78,12 @@ export type PlatformRecommendRuntimeStartIntent =
returnStage: 'platform'; returnStage: 'platform';
embedded: true; embedded: true;
} }
| {
type: 'start-puzzle-clear';
profileId: string;
returnStage: 'platform';
embedded: true;
}
| { | {
type: 'start-jump-hop'; type: 'start-jump-hop';
profileId: string; profileId: string;
@@ -133,6 +144,7 @@ export type PlatformRecommendRuntimeReadyState = {
hasBigFishRun?: boolean; hasBigFishRun?: boolean;
hasJumpHopRun?: boolean; hasJumpHopRun?: boolean;
hasMatch3DRun?: boolean; hasMatch3DRun?: boolean;
hasPuzzleClearRun?: boolean;
hasSquareHoleRun?: boolean; hasSquareHoleRun?: boolean;
hasVisualNovelRun?: boolean; hasVisualNovelRun?: boolean;
hasWoodenFishRun?: boolean; hasWoodenFishRun?: boolean;
@@ -161,6 +173,7 @@ export type PlatformPublicGalleryFeedsInput = {
bigFishEntries: readonly BigFishWorkSummary[]; bigFishEntries: readonly BigFishWorkSummary[];
match3dEntries: readonly Match3DWorkSummary[]; match3dEntries: readonly Match3DWorkSummary[];
puzzleEntries: readonly PuzzleWorkSummary[]; puzzleEntries: readonly PuzzleWorkSummary[];
puzzleClearEntries: readonly PuzzleClearGalleryCardResponse[];
barkBattleGalleryEntries: readonly BarkBattleWorkSummary[]; barkBattleGalleryEntries: readonly BarkBattleWorkSummary[];
barkBattleWorks: readonly BarkBattleWorkSummary[]; barkBattleWorks: readonly BarkBattleWorkSummary[];
jumpHopEntries: readonly JumpHopGalleryCardResponse[]; jumpHopEntries: readonly JumpHopGalleryCardResponse[];
@@ -194,6 +207,8 @@ export function getPlatformPublicGalleryEntryKey(
? 'big-fish' ? 'big-fish'
: isPuzzleGalleryEntry(entry) : isPuzzleGalleryEntry(entry)
? 'puzzle' ? 'puzzle'
: isPuzzleClearGalleryEntry(entry)
? 'puzzle-clear'
: isJumpHopGalleryEntry(entry) : isJumpHopGalleryEntry(entry)
? 'jump-hop' ? 'jump-hop'
: isWoodenFishGalleryEntry(entry) : isWoodenFishGalleryEntry(entry)
@@ -223,6 +238,10 @@ export function getPlatformRecommendRuntimeKind(
return 'puzzle'; return 'puzzle';
} }
if (isPuzzleClearGalleryEntry(entry)) {
return 'puzzle-clear';
}
if (isJumpHopGalleryEntry(entry)) { if (isJumpHopGalleryEntry(entry)) {
return 'jump-hop'; return 'jump-hop';
} }
@@ -297,6 +316,15 @@ export function resolvePlatformRecommendRuntimeStartIntent(
}; };
} }
if (isPuzzleClearGalleryEntry(entry)) {
return {
type: 'start-puzzle-clear',
profileId: entry.profileId,
returnStage: 'platform',
embedded: true,
};
}
if (isJumpHopGalleryEntry(entry)) { if (isJumpHopGalleryEntry(entry)) {
return { return {
type: 'start-jump-hop', type: 'start-jump-hop',
@@ -423,6 +451,9 @@ export function isPlatformRecommendRuntimeReadyForEntry(
state.puzzleRunCurrentLevelProfileId === entry.profileId state.puzzleRunCurrentLevelProfileId === entry.profileId
); );
} }
if (expectedKind === 'puzzle-clear') {
return Boolean(state.hasPuzzleClearRun);
}
if (expectedKind === 'square-hole') { if (expectedKind === 'square-hole') {
return Boolean(state.hasSquareHoleRun); return Boolean(state.hasSquareHoleRun);
} }
@@ -527,6 +558,7 @@ export function buildPlatformPublicGalleryFeeds(
...bigFishEntries, ...bigFishEntries,
...input.match3dEntries.map(mapMatch3DWorkToPublicWorkDetail), ...input.match3dEntries.map(mapMatch3DWorkToPublicWorkDetail),
...input.puzzleEntries.map(mapPuzzleWorkToPlatformGalleryCard), ...input.puzzleEntries.map(mapPuzzleWorkToPlatformGalleryCard),
...input.puzzleClearEntries.map(mapPuzzleClearWorkToPlatformGalleryCard),
...barkBattleGalleryEntries, ...barkBattleGalleryEntries,
...input.jumpHopEntries.map(mapJumpHopWorkToPlatformGalleryCard), ...input.jumpHopEntries.map(mapJumpHopWorkToPlatformGalleryCard),
...barkBattleFallbackEntries, ...barkBattleFallbackEntries,
@@ -539,6 +571,7 @@ export function buildPlatformPublicGalleryFeeds(
...bigFishEntries, ...bigFishEntries,
...input.match3dEntries.map(mapMatch3DWorkToPublicWorkDetail), ...input.match3dEntries.map(mapMatch3DWorkToPublicWorkDetail),
...input.puzzleEntries.map(mapPuzzleWorkToPlatformGalleryCard), ...input.puzzleEntries.map(mapPuzzleWorkToPlatformGalleryCard),
...input.puzzleClearEntries.map(mapPuzzleClearWorkToPlatformGalleryCard),
...(barkBattleGalleryEntries.length > 0 ...(barkBattleGalleryEntries.length > 0
? barkBattleGalleryEntries ? barkBattleGalleryEntries
: barkBattleFallbackEntries), : barkBattleFallbackEntries),

View File

@@ -6,6 +6,7 @@ import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/co
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks'; import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession'; import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { PuzzleClearGalleryCardResponse } from '../../../packages/shared/src/contracts/puzzleClear';
import type { import type {
CustomWorldGalleryCard, CustomWorldGalleryCard,
CustomWorldLibraryEntry, CustomWorldLibraryEntry,
@@ -25,6 +26,7 @@ import {
mapBarkBattleWorkToPublicWorkDetail, mapBarkBattleWorkToPublicWorkDetail,
mapBigFishWorkToPublicWorkDetail, mapBigFishWorkToPublicWorkDetail,
mapJumpHopWorkToPublicWorkDetail, mapJumpHopWorkToPublicWorkDetail,
mapPuzzleClearWorkToPublicWorkDetail,
mapPublicWorkDetailToBigFishWork, mapPublicWorkDetailToBigFishWork,
mapPublicWorkDetailToPuzzleWork, mapPublicWorkDetailToPuzzleWork,
mapPublicWorkDetailToSquareHoleWork, mapPublicWorkDetailToSquareHoleWork,
@@ -133,6 +135,13 @@ function buildTypedEntry<TSourceType extends PlatformGallerySourceType>(
...overrides, ...overrides,
sourceType, sourceType,
}); });
case 'puzzle-clear':
return narrowTypedEntry<TSourceType>({
...common,
...overrides,
sourceType,
themePrompt: '拼消消主题',
});
case 'big-fish': case 'big-fish':
return narrowTypedEntry<TSourceType>({ return narrowTypedEntry<TSourceType>({
...common, ...common,
@@ -324,6 +333,7 @@ function buildJumpHopGalleryCard(
profileId: 'jump-hop-profile', profileId: 'jump-hop-profile',
ownerUserId: 'user-1', ownerUserId: 'user-1',
authorDisplayName: '玩家', authorDisplayName: '玩家',
themeText: '跳一跳',
workTitle: '跳一跳作品', workTitle: '跳一跳作品',
workDescription: '跳一跳摘要', workDescription: '跳一跳摘要',
coverImageSrc: '/jump-hop-cover.png', coverImageSrc: '/jump-hop-cover.png',
@@ -339,6 +349,31 @@ function buildJumpHopGalleryCard(
}; };
} }
function buildPuzzleClearGalleryCard(
overrides: Partial<PuzzleClearGalleryCardResponse> = {},
): PuzzleClearGalleryCardResponse {
return {
runtimeKind: 'puzzle-clear',
publicWorkCode: 'PCLR-0001',
workId: 'puzzle-clear-work',
profileId: 'puzzle-clear-profile',
ownerUserId: 'user-1',
sourceSessionId: 'puzzle-clear-session',
authorDisplayName: '玩家',
workTitle: '拼消消作品',
workDescription: '拼消消摘要',
themePrompt: '水果',
coverImageSrc: '/puzzle-clear-cover.png',
publicationStatus: 'published',
playCount: 6,
updatedAt: '2026-06-01T01:00:00.000Z',
publishedAt: '2026-06-01T00:00:00.000Z',
publishReady: true,
generationStatus: 'ready',
...overrides,
};
}
function buildWoodenFishGalleryCard( function buildWoodenFishGalleryCard(
overrides: Partial<WoodenFishGalleryCardResponse> = {}, overrides: Partial<WoodenFishGalleryCardResponse> = {},
): WoodenFishGalleryCardResponse { ): WoodenFishGalleryCardResponse {
@@ -448,6 +483,7 @@ test('platform public work detail flow resolves detail kind for every play kind'
> = [ > = [
['big-fish', 'big-fish'], ['big-fish', 'big-fish'],
['puzzle', 'puzzle'], ['puzzle', 'puzzle'],
['puzzle-clear', 'puzzle-clear'],
['jump-hop', 'jump-hop'], ['jump-hop', 'jump-hop'],
['wooden-fish', 'wooden-fish'], ['wooden-fish', 'wooden-fish'],
['match3d', 'match3d'], ['match3d', 'match3d'],
@@ -509,6 +545,13 @@ test('platform public work detail flow resolves open strategy', () => {
kind: 'edutainment', kind: 'edutainment',
}, },
], ],
[
buildTypedEntry('puzzle-clear'),
{
type: 'use-entry',
kind: 'puzzle-clear',
},
],
[ [
buildTypedEntry('puzzle'), buildTypedEntry('puzzle'),
{ {
@@ -595,6 +638,14 @@ test('platform public work detail flow maps work summaries to detail entries', (
profileId: 'jump-hop-profile', profileId: 'jump-hop-profile',
publicWorkCode: 'JH-0001', publicWorkCode: 'JH-0001',
}); });
expect(
mapPuzzleClearWorkToPublicWorkDetail(buildPuzzleClearGalleryCard()),
).toMatchObject({
sourceType: 'puzzle-clear',
workId: 'puzzle-clear-work',
profileId: 'puzzle-clear-profile',
publicWorkCode: 'PCLR-0001',
});
expect( expect(
mapWoodenFishWorkToPublicWorkDetail(buildWoodenFishGalleryCard()), mapWoodenFishWorkToPublicWorkDetail(buildWoodenFishGalleryCard()),
).toMatchObject({ ).toMatchObject({
@@ -773,6 +824,12 @@ test('platform public work detail flow resolves like intent', () => {
type: 'like-puzzle', type: 'like-puzzle',
profileId: 'puzzle-profile', profileId: 'puzzle-profile',
}); });
expect(
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('puzzle-clear')),
).toEqual({
type: 'unsupported',
errorMessage: '拼消消点赞将在后续版本开放。',
});
expect(resolvePlatformPublicWorkLikeIntent(buildRpgEntry())).toEqual({ expect(resolvePlatformPublicWorkLikeIntent(buildRpgEntry())).toEqual({
type: 'like-rpg-gallery', type: 'like-rpg-gallery',
ownerUserId: 'user-1', ownerUserId: 'user-1',
@@ -826,6 +883,12 @@ test('platform public work detail flow resolves remix intent', () => {
profileId: 'puzzle-profile', profileId: 'puzzle-profile',
selectionStage: 'puzzle-result', selectionStage: 'puzzle-result',
}); });
expect(
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('puzzle-clear')),
).toEqual({
type: 'unsupported',
errorMessage: '拼消消作品改造将在后续版本开放。',
});
expect(resolvePlatformPublicWorkRemixIntent(buildRpgEntry())).toEqual({ expect(resolvePlatformPublicWorkRemixIntent(buildRpgEntry())).toEqual({
type: 'remix-rpg-gallery', type: 'remix-rpg-gallery',
ownerUserId: 'user-1', ownerUserId: 'user-1',
@@ -1038,6 +1101,15 @@ test('platform public work detail flow resolves edit intent for unsupported and
type: 'blocked', type: 'blocked',
errorMessage: '这份跳一跳作品暂时请从作品架编辑。', errorMessage: '这份跳一跳作品暂时请从作品架编辑。',
}); });
expect(
resolvePlatformPublicWorkEditIntent(
buildTypedEntry('puzzle-clear'),
buildEditIntentDeps(),
),
).toEqual({
type: 'blocked',
errorMessage: '这份拼消消作品暂时请从作品架编辑。',
});
expect( expect(
resolvePlatformPublicWorkEditIntent( resolvePlatformPublicWorkEditIntent(
buildTypedEntry('wooden-fish'), buildTypedEntry('wooden-fish'),
@@ -1126,6 +1198,16 @@ test('platform public work detail flow resolves start intent for direct launches
profileId: 'jump-hop-profile', profileId: 'jump-hop-profile',
returnStage: 'work-detail', returnStage: 'work-detail',
}); });
expect(
resolvePlatformPublicWorkStartIntent(
buildTypedEntry('puzzle-clear'),
buildStartIntentDeps(),
),
).toEqual({
type: 'start-puzzle-clear',
profileId: 'puzzle-clear-profile',
returnStage: 'work-detail',
});
expect( expect(
resolvePlatformPublicWorkStartIntent( resolvePlatformPublicWorkStartIntent(
buildTypedEntry('wooden-fish'), buildTypedEntry('wooden-fish'),

View File

@@ -7,6 +7,10 @@ import type {
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks'; import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession'; import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type {
PuzzleClearGalleryCardResponse,
PuzzleClearWorkProfileResponse,
} from '../../../packages/shared/src/contracts/puzzleClear';
import type { import type {
CustomWorldGalleryCard, CustomWorldGalleryCard,
CustomWorldLibraryEntry, CustomWorldLibraryEntry,
@@ -25,6 +29,7 @@ import {
isEdutainmentGalleryEntry, isEdutainmentGalleryEntry,
isJumpHopGalleryEntry, isJumpHopGalleryEntry,
isMatch3DGalleryEntry, isMatch3DGalleryEntry,
isPuzzleClearGalleryEntry,
isPuzzleGalleryEntry, isPuzzleGalleryEntry,
isSquareHoleGalleryEntry, isSquareHoleGalleryEntry,
isVisualNovelGalleryEntry, isVisualNovelGalleryEntry,
@@ -32,6 +37,7 @@ import {
mapBarkBattleWorkToPlatformGalleryCard, mapBarkBattleWorkToPlatformGalleryCard,
mapBigFishWorkToPlatformGalleryCard, mapBigFishWorkToPlatformGalleryCard,
mapJumpHopWorkToPlatformGalleryCard, mapJumpHopWorkToPlatformGalleryCard,
mapPuzzleClearWorkToPlatformGalleryCard,
mapPuzzleWorkToPlatformGalleryCard, mapPuzzleWorkToPlatformGalleryCard,
mapSquareHoleWorkToPlatformGalleryCard, mapSquareHoleWorkToPlatformGalleryCard,
mapVisualNovelWorkToPlatformGalleryCard, mapVisualNovelWorkToPlatformGalleryCard,
@@ -50,6 +56,7 @@ export type PlatformPublicWorkDetailKind =
| 'jump-hop' | 'jump-hop'
| 'match3d' | 'match3d'
| 'puzzle' | 'puzzle'
| 'puzzle-clear'
| 'rpg' | 'rpg'
| 'square-hole' | 'square-hole'
| 'visual-novel' | 'visual-novel'
@@ -199,6 +206,11 @@ export type PlatformPublicWorkStartIntent =
returnStage: 'work-detail'; returnStage: 'work-detail';
authMode: 'isolated'; authMode: 'isolated';
} }
| {
type: 'start-puzzle-clear';
profileId: string;
returnStage: 'work-detail';
}
| { | {
type: 'start-jump-hop'; type: 'start-jump-hop';
profileId: string; profileId: string;
@@ -325,6 +337,12 @@ export function mapJumpHopWorkToPublicWorkDetail(
return mapJumpHopWorkToPlatformGalleryCard(item); return mapJumpHopWorkToPlatformGalleryCard(item);
} }
export function mapPuzzleClearWorkToPublicWorkDetail(
item: PuzzleClearGalleryCardResponse | PuzzleClearWorkProfileResponse,
): PlatformPublicGalleryCard {
return mapPuzzleClearWorkToPlatformGalleryCard(item);
}
export function mapBarkBattleWorkToPublicWorkDetail( export function mapBarkBattleWorkToPublicWorkDetail(
item: BarkBattleWorkSummary, item: BarkBattleWorkSummary,
): PlatformPublicGalleryCard { ): PlatformPublicGalleryCard {
@@ -512,6 +530,10 @@ export function getPlatformPublicWorkDetailKind(
return 'puzzle'; return 'puzzle';
} }
if (isPuzzleClearGalleryEntry(entry)) {
return 'puzzle-clear';
}
if (isJumpHopGalleryEntry(entry)) { if (isJumpHopGalleryEntry(entry)) {
return 'jump-hop'; return 'jump-hop';
} }
@@ -560,6 +582,13 @@ export function resolvePlatformPublicWorkDetailOpenStrategy(
}; };
} }
if (isPuzzleClearGalleryEntry(entry)) {
return {
type: 'use-entry',
kind: 'puzzle-clear',
};
}
if (isJumpHopGalleryEntry(entry)) { if (isJumpHopGalleryEntry(entry)) {
return { return {
type: 'load-jump-hop-detail', type: 'load-jump-hop-detail',
@@ -653,6 +682,13 @@ export function resolvePlatformPublicWorkLikeIntent(
}; };
} }
if (isPuzzleClearGalleryEntry(entry)) {
return {
type: 'unsupported',
errorMessage: '拼消消点赞将在后续版本开放。',
};
}
if (isBarkBattleGalleryEntry(entry)) { if (isBarkBattleGalleryEntry(entry)) {
return { return {
type: 'unsupported', type: 'unsupported',
@@ -700,6 +736,13 @@ export function resolvePlatformPublicWorkRemixIntent(
}; };
} }
if (isPuzzleClearGalleryEntry(entry)) {
return {
type: 'unsupported',
errorMessage: '拼消消作品改造将在后续版本开放。',
};
}
if (isMatch3DGalleryEntry(entry)) { if (isMatch3DGalleryEntry(entry)) {
return { return {
type: 'unsupported', type: 'unsupported',
@@ -833,6 +876,13 @@ export function resolvePlatformPublicWorkEditIntent(
}; };
} }
if (isPuzzleClearGalleryEntry(entry)) {
return {
type: 'blocked',
errorMessage: '这份拼消消作品暂时请从作品架编辑。',
};
}
if (isWoodenFishGalleryEntry(entry)) { if (isWoodenFishGalleryEntry(entry)) {
return { return {
type: 'blocked', type: 'blocked',
@@ -944,6 +994,14 @@ export function resolvePlatformPublicWorkStartIntent(
}; };
} }
if (isPuzzleClearGalleryEntry(entry)) {
return {
type: 'start-puzzle-clear',
profileId: entry.profileId,
returnStage: 'work-detail',
};
}
if (isJumpHopGalleryEntry(entry)) { if (isJumpHopGalleryEntry(entry)) {
return { return {
type: 'start-jump-hop', type: 'start-jump-hop',

View File

@@ -48,6 +48,10 @@ const PROTECTED_DATA_LOSS_STABLE_STAGE_BY_STAGE = {
'puzzle-result': false, 'puzzle-result': false,
'puzzle-gallery-detail': true, 'puzzle-gallery-detail': true,
'puzzle-runtime': false, 'puzzle-runtime': false,
'puzzle-clear-workspace': true,
'puzzle-clear-generating': false,
'puzzle-clear-result': false,
'puzzle-clear-runtime': false,
'custom-world-generating': false, 'custom-world-generating': false,
'custom-world-result': false, 'custom-world-result': false,
} as const satisfies Record<SelectionStage, boolean>; } as const satisfies Record<SelectionStage, boolean>;

View File

@@ -358,6 +358,7 @@ const {
})); }));
vi.mock('../../services/apiClient', () => ({ vi.mock('../../services/apiClient', () => ({
BACKGROUND_AUTH_REQUEST_OPTIONS: {},
refreshStoredAccessToken: mockRefreshStoredAccessToken, refreshStoredAccessToken: mockRefreshStoredAccessToken,
})); }));
@@ -2670,7 +2671,7 @@ test('profile total play time card always uses hours', async () => {
}); });
const playTimeCard = screen.getByRole('button', { const playTimeCard = screen.getByRole('button', {
name: //u, name: //u,
}); });
expect(within(playTimeCard).getByText('1.5小时')).toBeTruthy(); expect(within(playTimeCard).getByText('1.5小时')).toBeTruthy();
@@ -2684,11 +2685,11 @@ test('profile played works card shows count unit', async () => {
}); });
const playedCard = screen.getByRole('button', { const playedCard = screen.getByRole('button', {
name: /\s*1/u, name: /\s*1/u,
}); });
expect(within(playedCard).getByText('1个')).toBeTruthy(); expect(within(playedCard).getByText('1个')).toBeTruthy();
expect(within(playedCard).queryByText('已玩游戏数量')).toBeNull(); expect(within(playedCard).getByText('已玩游戏数量')).toBeTruthy();
await screen.findByText('1 / 1'); await screen.findByText('1 / 1');
}); });
@@ -2700,8 +2701,12 @@ test('profile stats cards are centered without update timestamp', async () => {
const walletCard = screen.getByRole('button', { const walletCard = screen.getByRole('button', {
name: /\s*0/u, name: /\s*0/u,
}); });
const playTimeCard = screen.getByRole('button', { name: /\s*0/u }); const playTimeCard = screen.getByRole('button', {
const playedCard = screen.getByRole('button', { name: /\s*0/u }); name: /\s*0/u,
});
const playedCard = screen.getByRole('button', {
name: /\s*0/u,
});
for (const card of [walletCard, playTimeCard, playedCard]) { for (const card of [walletCard, playTimeCard, playedCard]) {
expect(card.className).toContain('platform-profile-stat-card'); expect(card.className).toContain('platform-profile-stat-card');
@@ -2753,8 +2758,8 @@ test('mobile profile page matches the reference layout sections', async () => {
expect(statPanel.className).toContain('platform-profile-stats-panel'); expect(statPanel.className).toContain('platform-profile-stats-panel');
expect(statPanel.querySelector('.platform-profile-stats-grid')).toBeTruthy(); expect(statPanel.querySelector('.platform-profile-stats-grid')).toBeTruthy();
expect(within(statPanel).getByRole('button', { name: /\s*70/u })).toBeTruthy(); expect(within(statPanel).getByRole('button', { name: /\s*70/u })).toBeTruthy();
expect(within(statPanel).getByRole('button', { name: /\s*0/u })).toBeTruthy(); expect(within(statPanel).getByRole('button', { name: /\s*0/u })).toBeTruthy();
expect(within(statPanel).getByRole('button', { name: /\s*0/u })).toBeTruthy(); expect(within(statPanel).getByRole('button', { name: /\s*0/u })).toBeTruthy();
expect( expect(
within(statPanel).getByRole('button', { name: /\s*70/u }).className, within(statPanel).getByRole('button', { name: /\s*70/u }).className,
).toContain('platform-profile-stat-card'); ).toContain('platform-profile-stat-card');

View File

@@ -326,6 +326,15 @@ const WECHAT_PAY_CONFIRM_RETRY_DELAYS_MS = [800, 1600, 3000] as const;
const WECHAT_NATIVE_PAY_QR_IMAGE_SIZE = 180; const WECHAT_NATIVE_PAY_QR_IMAGE_SIZE = 180;
const PROFILE_QR_SCAN_INTERVAL_MS = 360; const PROFILE_QR_SCAN_INTERVAL_MS = 360;
function getDelayUntilNextProfileTaskReset(nowMs = Date.now()) {
const shiftedNow = nowMs + PROFILE_TASK_BEIJING_OFFSET_MS;
const nextDayStart =
Math.floor(shiftedNow / PROFILE_TASK_DAY_MS) * PROFILE_TASK_DAY_MS +
PROFILE_TASK_DAY_MS;
const nextResetAt = nextDayStart - PROFILE_TASK_BEIJING_OFFSET_MS;
return Math.max(PROFILE_TASK_MIN_RESET_DELAY_MS, nextResetAt - nowMs);
}
type ProfileReferralPanel = 'invite' | 'redeem' | 'community'; type ProfileReferralPanel = 'invite' | 'redeem' | 'community';
type ProfilePopupPanel = ProfileReferralPanel | 'saveArchives'; type ProfilePopupPanel = ProfileReferralPanel | 'saveArchives';
type BarcodeDetectorLike = { type BarcodeDetectorLike = {

View File

@@ -1018,6 +1018,9 @@ export function describePlatformPublicWorkKind(
if (isPuzzleGalleryEntry(entry)) { if (isPuzzleGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('拼图'); return formatPlatformWorkDisplayTag('拼图');
} }
if (isPuzzleClearGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('拼消消');
}
if (isMatch3DGalleryEntry(entry)) { if (isMatch3DGalleryEntry(entry)) {
return formatPlatformWorkDisplayTag('抓大鹅'); return formatPlatformWorkDisplayTag('抓大鹅');
} }

View File

@@ -56,7 +56,7 @@ describe('jumpHopClient runtime requests', () => {
it('submits jump input with a generated client event id', async () => { it('submits jump input with a generated client event id', async () => {
await submitJumpHopJump( await submitJumpHopJump(
'run/1', 'run/1',
{ chargeMs: 320 }, { dragDistance: 320 },
{ runtimeGuestToken: 'runtime-guest-token' }, { runtimeGuestToken: 'runtime-guest-token' },
); );
@@ -69,7 +69,7 @@ describe('jumpHopClient runtime requests', () => {
Authorization: 'Bearer runtime-guest-token', Authorization: 'Bearer runtime-guest-token',
}, },
body: JSON.stringify({ body: JSON.stringify({
chargeMs: 320, dragDistance: 320,
clientEventId: 'jump-run/1-1780000000000', clientEventId: 'jump-run/1-1780000000000',
}), }),
}), }),

View File

@@ -5,6 +5,7 @@ import type {
JumpHopGalleryCardResponse, JumpHopGalleryCardResponse,
JumpHopGalleryDetailResponse, JumpHopGalleryDetailResponse,
JumpHopGalleryResponse, JumpHopGalleryResponse,
JumpHopJumpRequest,
JumpHopLeaderboardResponse, JumpHopLeaderboardResponse,
JumpHopRunResponse, JumpHopRunResponse,
JumpHopRuntimeRunSnapshotResponse, JumpHopRuntimeRunSnapshotResponse,
@@ -22,7 +23,11 @@ import {
requestJson, requestJson,
} from '../apiClient'; } from '../apiClient';
import { createCreationAgentClient } from '../creation-agent'; import { createCreationAgentClient } from '../creation-agent';
import { type RuntimeGuestRequestOptions } from '../runtimeGuestAuth'; import {
buildRuntimeGuestAuthOptions,
buildRuntimeGuestHeaders,
type RuntimeGuestRequestOptions,
} from '../runtimeGuestAuth';
import { buildRuntimeApiPath, requestRuntimeJson } from '../runtimeRequest'; import { buildRuntimeApiPath, requestRuntimeJson } from '../runtimeRequest';
const JUMP_HOP_API_BASE = '/api/creation/jump-hop/sessions'; const JUMP_HOP_API_BASE = '/api/creation/jump-hop/sessions';
@@ -40,11 +45,10 @@ type JumpHopRuntimeMode = 'draft' | 'published';
type JumpHopStartRunOptions = JumpHopRuntimeRequestOptions & { type JumpHopStartRunOptions = JumpHopRuntimeRequestOptions & {
runtimeMode?: JumpHopRuntimeMode; runtimeMode?: JumpHopRuntimeMode;
}; };
type JumpHopJumpPayload = { type JumpHopJumpPayload = Pick<
dragDistance: number; JumpHopJumpRequest,
dragVectorX?: number; 'dragDistance' | 'dragVectorX' | 'dragVectorY'
dragVectorY?: number; >;
};
export type { export type {
JumpHopActionRequest, JumpHopActionRequest,

View File

@@ -4,10 +4,12 @@ import {
buildCustomWorldPublicWorkCode, buildCustomWorldPublicWorkCode,
buildJumpHopPublicWorkCode, buildJumpHopPublicWorkCode,
buildMatch3DPublicWorkCode, buildMatch3DPublicWorkCode,
buildPuzzleClearPublicWorkCode,
buildWoodenFishPublicWorkCode, buildWoodenFishPublicWorkCode,
isSameCustomWorldPublicWorkCode, isSameCustomWorldPublicWorkCode,
isSameJumpHopPublicWorkCode, isSameJumpHopPublicWorkCode,
isSameMatch3DPublicWorkCode, isSameMatch3DPublicWorkCode,
isSamePuzzleClearPublicWorkCode,
isSameWoodenFishPublicWorkCode, isSameWoodenFishPublicWorkCode,
} from './publicWorkCode'; } from './publicWorkCode';