refactor: 收口推荐运行态就绪判定
This commit is contained in:
@@ -49,7 +49,7 @@
|
|||||||
## 2026-06-03 平台入口公开作品流身份规则收口
|
## 2026-06-03 平台入口公开作品流身份规则收口
|
||||||
|
|
||||||
- 背景:平台入口公开作品推荐流需要同时处理 RPG、拼图、抓大鹅、跳一跳、敲木鱼、视觉小说、Bark Battle、宝贝识物等卡片,公开作品身份、跨玩法去重、排序和推荐运行态 kind 判定曾放在 `PlatformEntryFlowShellImpl.tsx` 巨型实现里。
|
- 背景:平台入口公开作品推荐流需要同时处理 RPG、拼图、抓大鹅、跳一跳、敲木鱼、视觉小说、Bark Battle、宝贝识物等卡片,公开作品身份、跨玩法去重、排序和推荐运行态 kind 判定曾放在 `PlatformEntryFlowShellImpl.tsx` 巨型实现里。
|
||||||
- 决策:公开作品身份、排序规则和推荐 runtime 启动意图统一收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`;入口壳层只调用该 Module 的 `getPlatformPublicGalleryEntryKey`、`getPlatformRecommendRuntimeKind`、`resolvePlatformRecommendRuntimeStartIntent`、`isSamePlatformPublicGalleryEntry` 和 `mergePlatformPublicGalleryEntries`。`edutainment` key 必须带 `templateId`,RPG 卡片回退为 `rpg`。推荐 runtime 启动 intent 只返回启动目标、`embedded` / `returnStage` 参数、阻断文案和错误落点;壳层仍执行 request key、运行态 API、错误 setter 与 UI 状态。
|
- 决策:公开作品身份、排序规则、推荐 runtime 启动意图和 ready 判定统一收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`;入口壳层只调用该 Module 的 `getPlatformPublicGalleryEntryKey`、`getPlatformRecommendRuntimeKind`、`resolvePlatformRecommendRuntimeStartIntent`、`isPlatformRecommendRuntimeReadyForEntry`、`isSamePlatformPublicGalleryEntry` 和 `mergePlatformPublicGalleryEntries`。`edutainment` key 必须带 `templateId`,RPG 卡片回退为 `rpg`。推荐 runtime 启动 intent 只返回启动目标、`embedded` / `returnStage` 参数、阻断文案和错误落点;ready 判定只接布尔值与拼图 profile id,避免把各玩法 run snapshot 类型拖入 Module。壳层仍执行 request key、运行态 API、错误 setter 与 UI 状态。
|
||||||
- 影响范围:平台入口推荐流、公开作品详情、推荐 runtime 启动、跨玩法公开作品合并,以及后续新增玩法的入口接入。
|
- 影响范围:平台入口推荐流、公开作品详情、推荐 runtime 启动、跨玩法公开作品合并,以及后续新增玩法的入口接入。
|
||||||
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
|
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts`、`npm run typecheck`、`npm run check:encoding`、相关文件 ESLint 通过。
|
||||||
- 关联文档:`docs/technical/【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md`。
|
- 关联文档:`docs/technical/【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md`。
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
|
|||||||
|
|
||||||
前端 Server-Sent Events 客户端传输层收口到 `src/services/sseStream.ts`,事件边界、UTF-8 flush、JSON 解析跳过和提前取消约定见 [【前端架构】SSE客户端传输层收口约定-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91SSE%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BC%A0%E8%BE%93%E5%B1%82%E6%94%B6%E5%8F%A3%E7%BA%A6%E5%AE%9A-2026-06-03.md)。
|
前端 Server-Sent Events 客户端传输层收口到 `src/services/sseStream.ts`,事件边界、UTF-8 flush、JSON 解析跳过和提前取消约定见 [【前端架构】SSE客户端传输层收口约定-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91SSE%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BC%A0%E8%BE%93%E5%B1%82%E6%94%B6%E5%8F%A3%E7%BA%A6%E5%AE%9A-2026-06-03.md)。
|
||||||
|
|
||||||
平台入口公开作品身份、跨玩法去重、推荐运行态 kind 判定、推荐 runtime 启动意图和最新排序收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91%E5%B9%B3%E5%8F%B0%E5%85%A5%E5%8F%A3PublicGalleryFlowModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
平台入口公开作品身份、跨玩法去重、推荐运行态 kind 判定、推荐 runtime 启动意图、ready 判定和最新排序收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91%E5%B9%B3%E5%8F%B0%E5%85%A5%E5%8F%A3PublicGalleryFlowModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||||
|
|
||||||
统一作品详情页的玩法 kind、详情打开策略、自有作品动作模式、编辑 / 点赞 / 改造 / 启动意图和公开详情映射收口到 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`;抓大鹅公开详情映射与启动 / 编辑 Adapter 的素材归一仍归 `platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md](./technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md)。
|
统一作品详情页的玩法 kind、详情打开策略、自有作品动作模式、编辑 / 点赞 / 改造 / 启动意图和公开详情映射收口到 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`;抓大鹅公开详情映射与启动 / 编辑 Adapter 的素材归一仍归 `platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md](./technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md)。
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,11 @@
|
|||||||
- `getPlatformPublicGalleryEntryKey(entry)`:按玩法类型、作者和 `profileId` 生成公开作品身份。
|
- `getPlatformPublicGalleryEntryKey(entry)`:按玩法类型、作者和 `profileId` 生成公开作品身份。
|
||||||
- `getPlatformRecommendRuntimeKind(entry)`:把公开作品卡映射为推荐运行态 kind。
|
- `getPlatformRecommendRuntimeKind(entry)`:把公开作品卡映射为推荐运行态 kind。
|
||||||
- `resolvePlatformRecommendRuntimeStartIntent(entry, deps)`:把公开作品卡映射为推荐 runtime 启动意图、错误落点和 embedded / returnStage 参数。
|
- `resolvePlatformRecommendRuntimeStartIntent(entry, deps)`:把公开作品卡映射为推荐 runtime 启动意图、错误落点和 embedded / returnStage 参数。
|
||||||
|
- `isPlatformRecommendRuntimeReadyForEntry(entry, state)`:用标量 ready state 判定当前推荐 runtime 是否已能承接该公开作品。
|
||||||
- `isSamePlatformPublicGalleryEntry(left, right)`:按公开作品身份比较。
|
- `isSamePlatformPublicGalleryEntry(left, right)`:按公开作品身份比较。
|
||||||
- `mergePlatformPublicGalleryEntries(rpgEntries, puzzleEntries)`:统一完成 RPG 与各玩法公开作品去重、覆盖和倒序排序。
|
- `mergePlatformPublicGalleryEntries(rpgEntries, puzzleEntries)`:统一完成 RPG 与各玩法公开作品去重、覆盖和倒序排序。
|
||||||
|
|
||||||
入口壳层只调用这些函数,不再在 `PlatformEntryFlowShellImpl.tsx` 内手写公开作品身份、排序规则和推荐 runtime 启动能力矩阵。`isRecommendRuntimeReadyForEntry` 暂留入口壳层,因为它依赖各运行态 run 状态,直接抽出会把更多 runtime 状态类型拖入这个 Module,降低本次改造的 locality。
|
入口壳层只调用这些函数,不再在 `PlatformEntryFlowShellImpl.tsx` 内手写公开作品身份、排序规则、推荐 runtime 启动能力矩阵和 ready 判定。ready 判定只接布尔值与拼图 profile id,不把各玩法 run snapshot 类型拖入 Module。
|
||||||
|
|
||||||
## 玩法身份规则
|
## 玩法身份规则
|
||||||
|
|
||||||
@@ -33,9 +34,17 @@
|
|||||||
- 抓大鹅 public detail -> work mapper 必须作为 Adapter 注入,继续由 Match3D Runtime Profile Module 维护 `generatedItemAssets` 归一化与背景资产提升。推荐 runtime 固定沿用旧参数 `returnStage = 'work-detail'` 与 `embedded = true`。
|
- 抓大鹅 public detail -> work mapper 必须作为 Adapter 注入,继续由 Match3D Runtime Profile Module 维护 `generatedItemAssets` 归一化与背景资产提升。推荐 runtime 固定沿用旧参数 `returnStage = 'work-detail'` 与 `embedded = true`。
|
||||||
- 汪汪声浪优先使用推荐流已持有的 `barkBattleGalleryEntries`,再回退公开卡映射;不额外读取作品架列表。
|
- 汪汪声浪优先使用推荐流已持有的 `barkBattleGalleryEntries`,再回退公开卡映射;不额外读取作品架列表。
|
||||||
|
|
||||||
|
## 推荐 runtime ready 判定
|
||||||
|
|
||||||
|
- `isPlatformRecommendRuntimeReadyForEntry` 先要求 `state.activeKind` 与当前公开作品的 `getPlatformRecommendRuntimeKind(entry)` 相同,否则返回 `false`。
|
||||||
|
- 大鱼吃小鱼、跳一跳、敲木鱼、抓大鹅、方洞挑战和视觉小说只看对应 `has*Run` 布尔值,保持旧行为,不在本 Module 内解析 run snapshot。
|
||||||
|
- 拼图只看 `puzzleRunEntryProfileId` 或 `puzzleRunCurrentLevelProfileId` 是否等于当前公开作品 `profileId`。
|
||||||
|
- 汪汪声浪和 RPG 在 kind 匹配时沿用旧 `ready = true` 行为;宝贝识物只看 `hasBabyObjectMatchDraft`。
|
||||||
|
- 若未来要修正同玩法旧 run 误判或 RPG 无嵌入 runtime 的旧行为,应另立行为变更任务;本 Module 先只收口现有规则。
|
||||||
|
|
||||||
## 后续深化
|
## 后续深化
|
||||||
|
|
||||||
下一步可继续把平台入口的作品架刷新、删除确认和直达恢复逻辑收口成更深的 Work Shelf **Module**。当前 `platformPublicGalleryFlow` 先提供一个稳定 seam,使公开作品 identity、runtime kind 与推荐 runtime 启动意图的修改集中在一处。
|
下一步可继续把平台入口的作品架刷新、删除确认和直达恢复逻辑收口成更深的 Work Shelf **Module**。当前 `platformPublicGalleryFlow` 先提供一个稳定 seam,使公开作品 identity、runtime kind、推荐 runtime 启动意图与 ready 判定的修改集中在一处。
|
||||||
|
|
||||||
## 验证
|
## 验证
|
||||||
|
|
||||||
|
|||||||
@@ -503,6 +503,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
getPlatformPublicGalleryEntryKey,
|
getPlatformPublicGalleryEntryKey,
|
||||||
getPlatformRecommendRuntimeKind,
|
getPlatformRecommendRuntimeKind,
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry,
|
||||||
isSamePlatformPublicGalleryEntry,
|
isSamePlatformPublicGalleryEntry,
|
||||||
mergePlatformPublicGalleryEntries,
|
mergePlatformPublicGalleryEntries,
|
||||||
type RecommendRuntimeKind,
|
type RecommendRuntimeKind,
|
||||||
@@ -600,18 +601,6 @@ type WoodenFishRuntimeReturnStage =
|
|||||||
type VisualNovelEntryGenerationPhase = 'generating' | 'ready' | 'failed';
|
type VisualNovelEntryGenerationPhase = 'generating' | 'ready' | 'failed';
|
||||||
type BabyObjectMatchGenerationPhase = 'generating' | 'ready' | 'failed';
|
type BabyObjectMatchGenerationPhase = 'generating' | 'ready' | 'failed';
|
||||||
|
|
||||||
type RecommendRuntimeState = {
|
|
||||||
activeKind: RecommendRuntimeKind | null;
|
|
||||||
babyObjectMatchDraft: BabyObjectMatchDraft | null;
|
|
||||||
bigFishRun: BigFishRuntimeSnapshotResponse | null;
|
|
||||||
jumpHopRun: JumpHopRunResponse['run'] | null;
|
|
||||||
match3dRun: Match3DRunSnapshot | null;
|
|
||||||
puzzleRun: PuzzleRunSnapshot | null;
|
|
||||||
squareHoleRun: SquareHoleRunSnapshot | null;
|
|
||||||
visualNovelRun: VisualNovelRunSnapshot | null;
|
|
||||||
woodenFishRun: WoodenFishRunResponse['run'] | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PuzzleSaveArchiveState = {
|
type PuzzleSaveArchiveState = {
|
||||||
runtimeKind?: unknown;
|
runtimeKind?: unknown;
|
||||||
entryProfileId?: unknown;
|
entryProfileId?: unknown;
|
||||||
@@ -682,49 +671,6 @@ 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;
|
||||||
|
|
||||||
function isRecommendRuntimeReadyForEntry(
|
|
||||||
entry: PlatformPublicGalleryCard,
|
|
||||||
state: RecommendRuntimeState,
|
|
||||||
) {
|
|
||||||
const expectedKind = getPlatformRecommendRuntimeKind(entry);
|
|
||||||
if (state.activeKind !== expectedKind) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectedKind === 'big-fish') {
|
|
||||||
return Boolean(state.bigFishRun);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'jump-hop') {
|
|
||||||
return Boolean(state.jumpHopRun);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'wooden-fish') {
|
|
||||||
return Boolean(state.woodenFishRun);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'match3d') {
|
|
||||||
return Boolean(state.match3dRun);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'puzzle') {
|
|
||||||
return (
|
|
||||||
state.puzzleRun?.entryProfileId === entry.profileId ||
|
|
||||||
state.puzzleRun?.currentLevel?.profileId === entry.profileId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'square-hole') {
|
|
||||||
return Boolean(state.squareHoleRun);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'visual-novel') {
|
|
||||||
return Boolean(state.visualNovelRun);
|
|
||||||
}
|
|
||||||
if (expectedKind === 'bark-battle') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (expectedKind === 'edutainment') {
|
|
||||||
return Boolean(state.babyObjectMatchDraft);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBarkBattleWorkToPublishedConfig(
|
function mapBarkBattleWorkToPublishedConfig(
|
||||||
work: BarkBattleWorkSummary,
|
work: BarkBattleWorkSummary,
|
||||||
): BarkBattlePublishedConfig {
|
): BarkBattlePublishedConfig {
|
||||||
@@ -13359,16 +13305,18 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
: null;
|
: null;
|
||||||
const isActiveRecommendRuntimeReady =
|
const isActiveRecommendRuntimeReady =
|
||||||
activeRecommendEntry !== null &&
|
activeRecommendEntry !== null &&
|
||||||
isRecommendRuntimeReadyForEntry(activeRecommendEntry, {
|
isPlatformRecommendRuntimeReadyForEntry(activeRecommendEntry, {
|
||||||
activeKind: activeRecommendRuntimeKind,
|
activeKind: activeRecommendRuntimeKind,
|
||||||
babyObjectMatchDraft,
|
hasBabyObjectMatchDraft: Boolean(babyObjectMatchDraft),
|
||||||
bigFishRun,
|
hasBigFishRun: Boolean(bigFishRun),
|
||||||
jumpHopRun,
|
hasJumpHopRun: Boolean(jumpHopRun),
|
||||||
match3dRun,
|
hasMatch3DRun: Boolean(match3dRun),
|
||||||
puzzleRun,
|
hasSquareHoleRun: Boolean(squareHoleRun),
|
||||||
squareHoleRun,
|
hasVisualNovelRun: Boolean(visualNovelRun),
|
||||||
visualNovelRun,
|
hasWoodenFishRun: Boolean(woodenFishRun),
|
||||||
woodenFishRun,
|
puzzleRunEntryProfileId: puzzleRun?.entryProfileId ?? null,
|
||||||
|
puzzleRunCurrentLevelProfileId:
|
||||||
|
puzzleRun?.currentLevel?.profileId ?? null,
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
(activeRecommendEntry !== null && isActiveRecommendRuntimeReady) ||
|
(activeRecommendEntry !== null && isActiveRecommendRuntimeReady) ||
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
getPlatformPublicGalleryEntryKey,
|
getPlatformPublicGalleryEntryKey,
|
||||||
getPlatformPublicGalleryEntryTime,
|
getPlatformPublicGalleryEntryTime,
|
||||||
getPlatformRecommendRuntimeKind,
|
getPlatformRecommendRuntimeKind,
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry,
|
||||||
isSamePlatformPublicGalleryEntry,
|
isSamePlatformPublicGalleryEntry,
|
||||||
mergePlatformPublicGalleryEntries,
|
mergePlatformPublicGalleryEntries,
|
||||||
type PlatformRecommendRuntimeStartIntentDeps,
|
type PlatformRecommendRuntimeStartIntentDeps,
|
||||||
@@ -439,6 +440,94 @@ test('platform public gallery flow resolves recommend runtime bark battle priori
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('platform public gallery flow resolves recommend runtime readiness', () => {
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('big-fish'), {
|
||||||
|
activeKind: 'puzzle',
|
||||||
|
hasBigFishRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('big-fish'), {
|
||||||
|
activeKind: 'big-fish',
|
||||||
|
hasBigFishRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('jump-hop'), {
|
||||||
|
activeKind: 'jump-hop',
|
||||||
|
hasJumpHopRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('wooden-fish'), {
|
||||||
|
activeKind: 'wooden-fish',
|
||||||
|
hasWoodenFishRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('match3d'), {
|
||||||
|
activeKind: 'match3d',
|
||||||
|
hasMatch3DRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('square-hole'), {
|
||||||
|
activeKind: 'square-hole',
|
||||||
|
hasSquareHoleRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('visual-novel'), {
|
||||||
|
activeKind: 'visual-novel',
|
||||||
|
hasVisualNovelRun: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('bark-battle'), {
|
||||||
|
activeKind: 'bark-battle',
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildRpgEntry(), {
|
||||||
|
activeKind: 'rpg',
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('platform public gallery flow resolves puzzle and edutainment readiness details', () => {
|
||||||
|
const puzzleEntry = buildTypedEntry('puzzle', {
|
||||||
|
profileId: 'puzzle-profile',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(puzzleEntry, {
|
||||||
|
activeKind: 'puzzle',
|
||||||
|
puzzleRunEntryProfileId: 'other-profile',
|
||||||
|
puzzleRunCurrentLevelProfileId: 'puzzle-profile',
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(puzzleEntry, {
|
||||||
|
activeKind: 'puzzle',
|
||||||
|
puzzleRunEntryProfileId: 'other-profile',
|
||||||
|
puzzleRunCurrentLevelProfileId: 'another-profile',
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('edutainment'), {
|
||||||
|
activeKind: 'edutainment',
|
||||||
|
hasBabyObjectMatchDraft: true,
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('edutainment'), {
|
||||||
|
activeKind: 'edutainment',
|
||||||
|
hasBabyObjectMatchDraft: false,
|
||||||
|
}),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
test('platform public gallery flow merges duplicate identities and sorts newest first', () => {
|
test('platform public gallery flow merges duplicate identities and sorts newest first', () => {
|
||||||
const staleRpgEntry = buildRpgEntry({
|
const staleRpgEntry = buildRpgEntry({
|
||||||
profileId: 'shared-rpg',
|
profileId: 'shared-rpg',
|
||||||
|
|||||||
@@ -114,6 +114,19 @@ export type PlatformRecommendRuntimeStartIntentDeps = {
|
|||||||
) => Match3DWorkSummary | null;
|
) => Match3DWorkSummary | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PlatformRecommendRuntimeReadyState = {
|
||||||
|
activeKind: RecommendRuntimeKind | null;
|
||||||
|
hasBabyObjectMatchDraft?: boolean;
|
||||||
|
hasBigFishRun?: boolean;
|
||||||
|
hasJumpHopRun?: boolean;
|
||||||
|
hasMatch3DRun?: boolean;
|
||||||
|
hasSquareHoleRun?: boolean;
|
||||||
|
hasVisualNovelRun?: boolean;
|
||||||
|
hasWoodenFishRun?: boolean;
|
||||||
|
puzzleRunEntryProfileId?: string | null;
|
||||||
|
puzzleRunCurrentLevelProfileId?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
export function getPlatformPublicGalleryEntryTime(
|
export function getPlatformPublicGalleryEntryTime(
|
||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
) {
|
) {
|
||||||
@@ -332,6 +345,49 @@ export function resolvePlatformRecommendRuntimeStartIntent(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isPlatformRecommendRuntimeReadyForEntry(
|
||||||
|
entry: PlatformPublicGalleryCard,
|
||||||
|
state: PlatformRecommendRuntimeReadyState,
|
||||||
|
) {
|
||||||
|
const expectedKind = getPlatformRecommendRuntimeKind(entry);
|
||||||
|
if (state.activeKind !== expectedKind) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedKind === 'big-fish') {
|
||||||
|
return Boolean(state.hasBigFishRun);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'jump-hop') {
|
||||||
|
return Boolean(state.hasJumpHopRun);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'wooden-fish') {
|
||||||
|
return Boolean(state.hasWoodenFishRun);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'match3d') {
|
||||||
|
return Boolean(state.hasMatch3DRun);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'puzzle') {
|
||||||
|
return (
|
||||||
|
state.puzzleRunEntryProfileId === entry.profileId ||
|
||||||
|
state.puzzleRunCurrentLevelProfileId === entry.profileId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'square-hole') {
|
||||||
|
return Boolean(state.hasSquareHoleRun);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'visual-novel') {
|
||||||
|
return Boolean(state.hasVisualNovelRun);
|
||||||
|
}
|
||||||
|
if (expectedKind === 'bark-battle') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (expectedKind === 'edutainment') {
|
||||||
|
return Boolean(state.hasBabyObjectMatchDraft);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export function isSamePlatformPublicGalleryEntry(
|
export function isSamePlatformPublicGalleryEntry(
|
||||||
left: PlatformPublicGalleryCard,
|
left: PlatformPublicGalleryCard,
|
||||||
right: PlatformPublicGalleryCard,
|
right: PlatformPublicGalleryCard,
|
||||||
|
|||||||
Reference in New Issue
Block a user