refactor: 收口公开详情启动意图
This commit is contained in:
@@ -19,11 +19,12 @@
|
||||
## 2026-06-03 平台入口公开作品详情 Strategy 收口
|
||||
|
||||
- 背景:平台壳层直接判断公开作品详情入口的玩法类型、是否需要补读完整详情,以及自有作品按钮显示“编辑”还是“改造”,导致统一作品详情的纯决策散落在巨型 Implementation 内。
|
||||
- 决策:新增 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`,以 `getPlatformPublicWorkDetailKind`、`resolvePlatformPublicWorkDetailOpenStrategy`、`resolvePlatformPublicWorkActionMode`、`resolvePlatformPublicWorkDetailOpenDecision` 和 `resolveActivePlatformPublicWorkAuthorEntry` 收口公开作品详情 Strategy。`PlatformEntryFlowShellImpl.tsx` 只按 Strategy 调用现有详情读取 / 直接展示 Adapter,并保留作者请求竞态控制;启动、点赞、remix 和编辑副作用暂不抽走。
|
||||
- 决策:新增 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`,以 `getPlatformPublicWorkDetailKind`、`resolvePlatformPublicWorkDetailOpenStrategy`、`resolvePlatformPublicWorkActionMode`、`resolvePlatformPublicWorkDetailOpenDecision` 和 `resolveActivePlatformPublicWorkAuthorEntry` 收口公开作品详情 Strategy。`PlatformEntryFlowShellImpl.tsx` 只按 Strategy 调用现有详情读取 / 直接展示 Adapter,并保留作者请求竞态控制;启动、点赞、remix 和编辑副作用不搬入 Module。
|
||||
- 追加决策:公开详情 entry 映射与公开详情反推玩法 work 摘要也归入 `platformPublicWorkDetailFlow.ts`,包括 RPG、拼图、大鱼吃小鱼、方洞挑战、视觉小说、跳一跳、敲木鱼和汪汪声浪的通用映射。抓大鹅 `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 做素材归一和背景资产提升,避免把 Match3D 运行态规则复制到公开详情 Flow Module。
|
||||
- 追加决策:拼图公开详情封面解锁数由 `resolveVisiblePuzzleDetailCoverCount(entry, run)` 收口;非拼图、无当前 run 或 run 不匹配当前公开详情时只展示首图,匹配当前公开详情时按 `clearedLevelCount + 1` 解锁且至少为 1。`PlatformWorkDetailView` 只接收 `visibleCoverCount` 展示,不读取 run。
|
||||
- 追加决策:公开详情点赞能力矩阵由 `resolvePlatformPublicWorkLikeIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、API 调用、缓存同步、错误展示和 busy 状态。
|
||||
- 追加决策:公开详情改造能力矩阵由 `resolvePlatformPublicWorkRemixIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、remix API、session / 缓存写入、stage 切换、错误展示和 busy 状态。
|
||||
- 追加决策:公开详情启动分流由 `resolvePlatformPublicWorkStartIntent(entry, deps)` 收口;Module 只返回大鱼吃小鱼、拼图、跳一跳、敲木鱼、抓大鹅、方洞挑战、视觉小说、汪汪声浪、宝贝识物或旧 RPG gallery 记录游玩的 intent。壳层仍执行登录保护、运行态启动、RPG 游玩记录、详情更新、busy 状态和错误展示;抓大鹅 public detail -> work mapper 作为 Adapter 注入,继续由 Match3D Runtime Profile Module 维护素材归一与背景资产提升。
|
||||
- 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。
|
||||
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicWorkDetailFlow.test.ts`、`npm run test -- src/components/platform-entry/platformMatch3DRuntimeProfile.test.ts`、公开详情壳层交互回归、`npm run typecheck`、`npm run check:encoding`。
|
||||
- 关联文档:`docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md`。
|
||||
|
||||
@@ -41,7 +41,7 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
|
||||
|
||||
平台入口公开作品身份、跨玩法去重、推荐运行态 kind 判定和最新排序收口到 `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`;抓大鹅公开详情映射的素材归一仍归 `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)。
|
||||
|
||||
创作中心作品架打开动作由 `CreationWorkShelfItem.actions.open` 统一承载,生产 Hub 只接收 `CreationWorkShelfItem[]` 与 UI 状态,不再接收各玩法 raw items 和回调列阵,规则见 [【前端架构】WorkShelfModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91WorkShelfModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
- `resolvePlatformPublicWorkActionMode(entry, viewerUserId)`
|
||||
- `resolvePlatformPublicWorkLikeIntent(entry)`
|
||||
- `resolvePlatformPublicWorkRemixIntent(entry)`
|
||||
- `resolvePlatformPublicWorkStartIntent(entry, deps)`
|
||||
- `resolvePlatformPublicWorkDetailOpenDecision(entry, deps)`
|
||||
- `resolveActivePlatformPublicWorkAuthorEntry(args)`
|
||||
- `map*WorkToPublicWorkDetail(...)`
|
||||
@@ -24,7 +25,7 @@
|
||||
- `PlatformEntryFlowShellImpl.tsx` 继续作为 Adapter:根据 open strategy 调用 `openPublicWorkDetail`、`openPuzzlePublicWorkDetail`、`openJumpHopPublicWorkDetail`、`openWoodenFishPublicWorkDetail`、`openVisualNovelPublicWorkDetail` 或 `openRpgPublicWorkDetail`。
|
||||
- 公开详情 entry 映射与公开详情反推玩法 work 摘要也收口到 Module。壳层只在运行态启动、编辑、改造、推荐缓存和详情展示时调用映射 Interface,不再在壳层顶部持有每个玩法的 DTO 拼装 Implementation。
|
||||
- `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 处理素材归一和背景资产提升;`platformPublicWorkDetailFlow.ts` 不复制 Match3D 运行态素材规则。
|
||||
- 本次不抽 `startSelectedPublicWork`、`likePublicWork`、`remixPublicWork`、`editOwnedPublicWork`。这些函数牵涉运行态启动、计数写入、草稿恢复、作品架缓存和多路错误 setter;若直接搬进一个 Hook,会形成浅 Interface。
|
||||
- 公开详情启动、点赞和改造只抽“意图” Interface,不把整个 callback 搬进 Module。壳层继续作为 Adapter 执行鉴权、API 调用、运行态启动、busy 状态、缓存同步、stage 切换和错误 setter,避免形成伪 Seam。
|
||||
|
||||
## Interface 约束
|
||||
|
||||
@@ -36,6 +37,8 @@
|
||||
- `resolvePlatformPublicWorkActionMode` 只比较 `entry.ownerUserId` 与当前 viewer user id;当前用户拥有该公开作品时返回 `edit`,否则返回 `remix`。
|
||||
- `resolvePlatformPublicWorkLikeIntent` 只表达公开作品点赞意图:大鱼吃小鱼、拼图和旧 RPG gallery fallback 返回可执行 intent;宝贝识物、汪汪声浪、方洞挑战和视觉小说返回不可用文案。壳层只按 intent 调用 API、写缓存和展示错误,不再持有这组能力矩阵。
|
||||
- `resolvePlatformPublicWorkRemixIntent` 只表达公开作品改造意图:大鱼吃小鱼和拼图返回可执行 intent 与成功后目标 stage,旧 RPG gallery fallback 返回可执行 intent,其它玩法返回原未开放文案。壳层只按 intent 调用 remix API、写 session / 缓存、切 stage 和展示错误。
|
||||
- `resolvePlatformPublicWorkStartIntent` 只表达公开作品“开始游玩”意图:大鱼吃小鱼、拼图、跳一跳、敲木鱼、抓大鹅、方洞挑战、视觉小说、汪汪声浪和宝贝识物返回对应启动目标;旧 RPG gallery fallback 只在完整 RPG 详情已补读且 profile 匹配时返回记录游玩 intent,否则返回原阻断文案。壳层仍执行登录保护、运行态启动、RPG 游玩记录、详情更新、busy 状态和错误展示。
|
||||
- `resolvePlatformPublicWorkStartIntent` 的 `deps` 只接启动决策所需的当前拼图详情、当前 RPG 详情、汪汪声浪作品缓存,以及抓大鹅 public detail -> work 的 Adapter。抓大鹅 Adapter 必须来自 Match3D Runtime Profile Module,以保留 `generatedItemAssets` 归一化与背景资产提升的 Locality。
|
||||
- `resolvePlatformPublicWorkDetailOpenDecision` 只表达直接展示公开详情的打开 / 阻断结果、错误文案、目标 stage 与可写入历史的路径;真正执行 setter、push history 的副作用仍由壳层 Adapter 执行。
|
||||
- `resolveActivePlatformPublicWorkAuthorEntry` 只在 `work-detail` 阶段选择统一公开详情 entry,在 RPG `detail` 阶段只选择非 draft 的 RPG 详情 entry;作者请求、竞态 request key 和缓存仍留壳层。
|
||||
- `map*WorkToPublicWorkDetail` 只把各玩法已存在的 work / gallery summary 映射为统一详情 entry;公开码、封面、统计与标题字段继续复用 `rpgEntryWorldPresentation.ts` 的平台公开卡片映射。
|
||||
@@ -45,7 +48,7 @@
|
||||
|
||||
## Depth / Leverage / Locality
|
||||
|
||||
- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id 或当前拼图 run,即可得到详情打开策略、动作模式、点赞 / 改造意图、统一详情映射和封面可见数;玩法判定、能力矩阵与 DTO 默认值藏在 Module Implementation 内。
|
||||
- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id、当前拼图 run 或少量启动 deps,即可得到详情打开策略、动作模式、点赞 / 改造 / 启动意图、统一详情映射和封面可见数;玩法判定、能力矩阵与 DTO 默认值藏在 Module Implementation 内。
|
||||
- **Leverage**:新增玩法公开详情时先补 Strategy / Mapping 单测,再接壳层 Adapter,不必在多个 JSX / callback 位置重复 sourceType 判断或 DTO 回填。
|
||||
- **Locality**:公开作品详情入口的纯策略与通用映射集中到一个小 Module;Match3D 素材归一仍在 Match3D Module;启动运行态、点赞、改造、编辑等副作用仍留在壳层,避免伪 Seam。
|
||||
|
||||
|
||||
@@ -534,6 +534,7 @@ import {
|
||||
resolvePlatformPublicWorkDetailOpenStrategy,
|
||||
resolvePlatformPublicWorkLikeIntent,
|
||||
resolvePlatformPublicWorkRemixIntent,
|
||||
resolvePlatformPublicWorkStartIntent,
|
||||
resolveVisiblePuzzleDetailCoverCount,
|
||||
} from './platformPublicWorkDetailFlow';
|
||||
import {
|
||||
@@ -12611,134 +12612,93 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
if (isBigFishGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work = mapPublicWorkDetailToBigFishWork(selectedPublicWorkDetail);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError('当前作品缺少会话信息,暂时无法进入玩法。');
|
||||
return;
|
||||
}
|
||||
startBigFishRunFromWork(work);
|
||||
return;
|
||||
}
|
||||
const intent = resolvePlatformPublicWorkStartIntent(
|
||||
selectedPublicWorkDetail,
|
||||
{
|
||||
selectedPuzzleDetail,
|
||||
selectedRpgDetailEntry: selectedDetailEntry,
|
||||
barkBattleGalleryEntries,
|
||||
barkBattleWorks,
|
||||
mapMatch3DWork: mapPublicWorkDetailToMatch3DWork,
|
||||
},
|
||||
);
|
||||
|
||||
if (isPuzzleGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work =
|
||||
selectedPuzzleDetail?.profileId === selectedPublicWorkDetail.profileId
|
||||
? selectedPuzzleDetail
|
||||
: mapPublicWorkDetailToPuzzleWork(selectedPublicWorkDetail);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError(
|
||||
'当前拼图作品信息不完整,暂时无法进入玩法。',
|
||||
switch (intent.type) {
|
||||
case 'blocked':
|
||||
setPublicWorkDetailError(intent.errorMessage);
|
||||
return;
|
||||
case 'start-big-fish':
|
||||
startBigFishRunFromWork(intent.work, intent.returnStage);
|
||||
return;
|
||||
case 'start-puzzle':
|
||||
setPublicWorkDetailError(null);
|
||||
void startPuzzleRunFromProfile(
|
||||
intent.work.profileId,
|
||||
intent.returnStage,
|
||||
intent.work,
|
||||
true,
|
||||
null,
|
||||
{ authMode: intent.authMode },
|
||||
);
|
||||
return;
|
||||
}
|
||||
setPublicWorkDetailError(null);
|
||||
void startPuzzleRunFromProfile(
|
||||
work.profileId,
|
||||
'work-detail',
|
||||
work,
|
||||
true,
|
||||
null,
|
||||
{ authMode: 'isolated' },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJumpHopGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startJumpHopRunFromProfile(selectedPublicWorkDetail.profileId, {
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWoodenFishGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startWoodenFishRunFromProfile(selectedPublicWorkDetail.profileId, {
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work = mapPublicWorkDetailToMatch3DWork(selectedPublicWorkDetail);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError(
|
||||
'当前抓大鹅作品信息不完整,暂时无法进入玩法。',
|
||||
case 'start-jump-hop':
|
||||
setPublicWorkDetailError(null);
|
||||
void startJumpHopRunFromProfile(intent.profileId, {
|
||||
returnStage: intent.returnStage,
|
||||
});
|
||||
return;
|
||||
case 'start-wooden-fish':
|
||||
setPublicWorkDetailError(null);
|
||||
void startWoodenFishRunFromProfile(intent.profileId, {
|
||||
returnStage: intent.returnStage,
|
||||
});
|
||||
return;
|
||||
case 'start-match3d':
|
||||
setPublicWorkDetailError(null);
|
||||
void startMatch3DRunFromProfile(
|
||||
intent.work,
|
||||
intent.returnStage,
|
||||
true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
setPublicWorkDetailError(null);
|
||||
void startMatch3DRunFromProfile(work, 'work-detail', true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSquareHoleGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work = mapPublicWorkDetailToSquareHoleWork(
|
||||
selectedPublicWorkDetail,
|
||||
);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError(
|
||||
'当前方洞挑战作品信息不完整,暂时无法进入玩法。',
|
||||
case 'start-square-hole':
|
||||
setPublicWorkDetailError(null);
|
||||
void startSquareHoleRunFromProfile(
|
||||
intent.work,
|
||||
intent.returnStage,
|
||||
true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
setPublicWorkDetailError(null);
|
||||
void startSquareHoleRunFromProfile(work, 'work-detail', true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isVisualNovelGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startVisualNovelRunFromProfile(
|
||||
selectedPublicWorkDetail.profileId,
|
||||
'work-detail',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBarkBattleGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work =
|
||||
barkBattleGalleryEntries.find(
|
||||
(item) => item.workId === selectedPublicWorkDetail.workId,
|
||||
) ??
|
||||
barkBattleWorks.find(
|
||||
(item) => item.workId === selectedPublicWorkDetail.workId,
|
||||
) ??
|
||||
mapBarkBattlePublicDetailToWorkSummary(selectedPublicWorkDetail);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError(
|
||||
'当前汪汪声浪作品信息不完整,暂时无法进入玩法。',
|
||||
case 'start-visual-novel':
|
||||
setPublicWorkDetailError(null);
|
||||
void startVisualNovelRunFromProfile(
|
||||
intent.profileId,
|
||||
intent.returnStage,
|
||||
);
|
||||
return;
|
||||
case 'start-bark-battle':
|
||||
setPublicWorkDetailError(null);
|
||||
startBarkBattleRunFromWork(intent.work, intent.returnStage);
|
||||
return;
|
||||
case 'start-edutainment':
|
||||
setPublicWorkDetailError(null);
|
||||
void startBabyObjectMatchRuntimeFromEntry(
|
||||
intent.entry,
|
||||
intent.returnStage,
|
||||
);
|
||||
return;
|
||||
case 'record-rpg-gallery-play':
|
||||
break;
|
||||
default: {
|
||||
const exhaustive: never = intent;
|
||||
return exhaustive;
|
||||
}
|
||||
setPublicWorkDetailError(null);
|
||||
startBarkBattleRunFromWork(work, 'work-detail');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEdutainmentGalleryEntry(selectedPublicWorkDetail)) {
|
||||
setPublicWorkDetailError(null);
|
||||
void startBabyObjectMatchRuntimeFromEntry(
|
||||
selectedPublicWorkDetail,
|
||||
'work-detail',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const launchEntry =
|
||||
selectedDetailEntry?.profileId === selectedPublicWorkDetail.profileId
|
||||
? selectedDetailEntry
|
||||
: null;
|
||||
if (!launchEntry) {
|
||||
setPublicWorkDetailError('作品详情尚未读取完成。');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPublicWorkDetailBusy(true);
|
||||
void recordRpgEntryWorldGalleryPlay(
|
||||
launchEntry.ownerUserId,
|
||||
launchEntry.profileId,
|
||||
intent.entry.ownerUserId,
|
||||
intent.entry.profileId,
|
||||
)
|
||||
.then((updatedEntry) => {
|
||||
setSelectedDetailEntry(updatedEntry);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { expect, test } from 'vitest';
|
||||
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
||||
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||
import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/contracts/jumpHop';
|
||||
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
|
||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
||||
@@ -30,12 +31,14 @@ import {
|
||||
mapWoodenFishWorkToPublicWorkDetail,
|
||||
type PlatformPublicWorkDetailKind,
|
||||
type PlatformPublicWorkDetailOpenStrategy,
|
||||
type PlatformPublicWorkStartIntentDeps,
|
||||
resolveActivePlatformPublicWorkAuthorEntry,
|
||||
resolvePlatformPublicWorkActionMode,
|
||||
resolvePlatformPublicWorkDetailOpenDecision,
|
||||
resolvePlatformPublicWorkDetailOpenStrategy,
|
||||
resolvePlatformPublicWorkLikeIntent,
|
||||
resolvePlatformPublicWorkRemixIntent,
|
||||
resolvePlatformPublicWorkStartIntent,
|
||||
resolveVisiblePuzzleDetailCoverCount,
|
||||
} from './platformPublicWorkDetailFlow';
|
||||
|
||||
@@ -56,7 +59,10 @@ type TypedPlatformPublicGalleryCardOverrides<
|
||||
function narrowTypedEntry<TSourceType extends PlatformGallerySourceType>(
|
||||
entry: TypedPlatformPublicGalleryCard,
|
||||
): Extract<TypedPlatformPublicGalleryCard, { sourceType: TSourceType }> {
|
||||
return entry as Extract<TypedPlatformPublicGalleryCard, { sourceType: TSourceType }>;
|
||||
return entry as Extract<
|
||||
TypedPlatformPublicGalleryCard,
|
||||
{ sourceType: TSourceType }
|
||||
>;
|
||||
}
|
||||
|
||||
function buildRpgEntry(
|
||||
@@ -104,19 +110,47 @@ function buildTypedEntry<TSourceType extends PlatformGallerySourceType>(
|
||||
|
||||
switch (sourceType) {
|
||||
case 'puzzle':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'big-fish':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'match3d':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'square-hole':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'visual-novel':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'jump-hop':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'wooden-fish':
|
||||
return narrowTypedEntry<TSourceType>({ ...common, ...overrides, sourceType });
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
...overrides,
|
||||
sourceType,
|
||||
});
|
||||
case 'edutainment':
|
||||
return narrowTypedEntry<TSourceType>({
|
||||
...common,
|
||||
@@ -337,6 +371,45 @@ function buildBarkBattleWork(
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DWork(
|
||||
overrides: Partial<Match3DWorkSummary> = {},
|
||||
): Match3DWorkSummary {
|
||||
return {
|
||||
workId: 'match3d-work',
|
||||
profileId: 'match3d-profile',
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: 'match3d-session',
|
||||
gameName: '抓大鹅作品',
|
||||
themeText: '经典消除',
|
||||
summary: '抓大鹅摘要',
|
||||
tags: ['抓大鹅'],
|
||||
coverImageSrc: '/match3d-cover.png',
|
||||
referenceImageSrc: null,
|
||||
clearCount: 12,
|
||||
difficulty: 4,
|
||||
publicationStatus: 'published',
|
||||
playCount: 10,
|
||||
updatedAt: '2026-06-01T01:00:00.000Z',
|
||||
publishedAt: '2026-06-01T00:00:00.000Z',
|
||||
publishReady: true,
|
||||
generatedItemAssets: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildStartIntentDeps(
|
||||
overrides: Partial<PlatformPublicWorkStartIntentDeps> = {},
|
||||
): PlatformPublicWorkStartIntentDeps {
|
||||
return {
|
||||
selectedPuzzleDetail: null,
|
||||
selectedRpgDetailEntry: null,
|
||||
barkBattleGalleryEntries: [],
|
||||
barkBattleWorks: [],
|
||||
mapMatch3DWork: () => buildMatch3DWork(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('platform public work detail flow resolves detail kind for every play kind', () => {
|
||||
const cases: Array<
|
||||
[sourceType: PlatformGallerySourceType, kind: PlatformPublicWorkDetailKind]
|
||||
@@ -579,18 +652,16 @@ test('platform public work detail flow maps detail entries back to work summarie
|
||||
});
|
||||
|
||||
expect(
|
||||
mapBarkBattlePublicDetailToWorkSummary(
|
||||
{
|
||||
...buildTypedEntry('bark-battle', {
|
||||
themeTags: ['森林', '小狗', '对手'],
|
||||
coverImageSrc: '/bark-bg.png',
|
||||
coverCharacterImageSrcs: ['/player.png', '/opponent.png'],
|
||||
playCount: 11,
|
||||
recentPlayCount7d: 5,
|
||||
}),
|
||||
sourceSessionId: 'bark-draft',
|
||||
},
|
||||
),
|
||||
mapBarkBattlePublicDetailToWorkSummary({
|
||||
...buildTypedEntry('bark-battle', {
|
||||
themeTags: ['森林', '小狗', '对手'],
|
||||
coverImageSrc: '/bark-bg.png',
|
||||
coverCharacterImageSrcs: ['/player.png', '/opponent.png'],
|
||||
playCount: 11,
|
||||
recentPlayCount7d: 5,
|
||||
}),
|
||||
sourceSessionId: 'bark-draft',
|
||||
}),
|
||||
).toMatchObject({
|
||||
workId: 'bark-battle-work',
|
||||
draftId: 'bark-draft',
|
||||
@@ -605,8 +676,12 @@ test('platform public work detail flow maps detail entries back to work summarie
|
||||
recentPlayCount7d: 5,
|
||||
});
|
||||
|
||||
expect(mapPublicWorkDetailToPuzzleWork(buildTypedEntry('big-fish'))).toBeNull();
|
||||
expect(mapPublicWorkDetailToBigFishWork(buildTypedEntry('puzzle'))).toBeNull();
|
||||
expect(
|
||||
mapPublicWorkDetailToPuzzleWork(buildTypedEntry('big-fish')),
|
||||
).toBeNull();
|
||||
expect(
|
||||
mapPublicWorkDetailToBigFishWork(buildTypedEntry('puzzle')),
|
||||
).toBeNull();
|
||||
expect(
|
||||
mapPublicWorkDetailToSquareHoleWork(buildTypedEntry('puzzle')),
|
||||
).toBeNull();
|
||||
@@ -654,13 +729,15 @@ test('platform public work detail flow resolves edit mode only for owned works',
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves like intent', () => {
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('big-fish'))).toEqual(
|
||||
{
|
||||
type: 'like-big-fish',
|
||||
profileId: 'big-fish-profile',
|
||||
},
|
||||
);
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('puzzle'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('big-fish')),
|
||||
).toEqual({
|
||||
type: 'like-big-fish',
|
||||
profileId: 'big-fish-profile',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('puzzle')),
|
||||
).toEqual({
|
||||
type: 'like-puzzle',
|
||||
profileId: 'puzzle-profile',
|
||||
});
|
||||
@@ -669,42 +746,50 @@ test('platform public work detail flow resolves like intent', () => {
|
||||
ownerUserId: 'user-1',
|
||||
profileId: 'rpg-profile',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('match3d'))).toEqual(
|
||||
{
|
||||
type: 'like-rpg-gallery',
|
||||
ownerUserId: 'user-1',
|
||||
profileId: 'match3d-profile',
|
||||
},
|
||||
);
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('edutainment'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('match3d')),
|
||||
).toEqual({
|
||||
type: 'like-rpg-gallery',
|
||||
ownerUserId: 'user-1',
|
||||
profileId: 'match3d-profile',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('edutainment')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '宝贝识物点赞将在后续版本开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('bark-battle'))).toEqual(
|
||||
{
|
||||
type: 'unsupported',
|
||||
errorMessage: '汪汪声浪点赞将在后续版本开放。',
|
||||
},
|
||||
);
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('square-hole'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('bark-battle')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '汪汪声浪点赞将在后续版本开放。',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('square-hole')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '方洞挑战点赞将在后续版本开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkLikeIntent(buildTypedEntry('visual-novel'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkLikeIntent(buildTypedEntry('visual-novel')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '视觉小说点赞将在后续版本开放。',
|
||||
});
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves remix intent', () => {
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('big-fish'))).toEqual(
|
||||
{
|
||||
type: 'remix-big-fish',
|
||||
profileId: 'big-fish-profile',
|
||||
selectionStage: 'big-fish-result',
|
||||
},
|
||||
);
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('puzzle'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('big-fish')),
|
||||
).toEqual({
|
||||
type: 'remix-big-fish',
|
||||
profileId: 'big-fish-profile',
|
||||
selectionStage: 'big-fish-result',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('puzzle')),
|
||||
).toEqual({
|
||||
type: 'remix-puzzle',
|
||||
profileId: 'puzzle-profile',
|
||||
selectionStage: 'puzzle-result',
|
||||
@@ -714,38 +799,236 @@ test('platform public work detail flow resolves remix intent', () => {
|
||||
ownerUserId: 'user-1',
|
||||
profileId: 'rpg-profile',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('match3d'))).toEqual(
|
||||
{
|
||||
type: 'unsupported',
|
||||
errorMessage: '抓大鹅作品改造将在后续版本开放。',
|
||||
},
|
||||
);
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('square-hole'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('match3d')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '抓大鹅作品改造将在后续版本开放。',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('square-hole')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '方洞挑战作品改造将在后续版本开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('jump-hop'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('jump-hop')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '跳一跳作品改造将在后续版本开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('wooden-fish'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('wooden-fish')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '敲木鱼作品改造将在后续版本开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('visual-novel'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('visual-novel')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '视觉小说作品改造将在后续版本开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('edutainment'))).toEqual({
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('edutainment')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '宝贝识物作品改造将在创作链路接入后开放。',
|
||||
});
|
||||
expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('bark-battle'))).toEqual(
|
||||
{
|
||||
type: 'unsupported',
|
||||
errorMessage: '汪汪声浪作品改造将在后续版本开放。',
|
||||
},
|
||||
);
|
||||
expect(
|
||||
resolvePlatformPublicWorkRemixIntent(buildTypedEntry('bark-battle')),
|
||||
).toEqual({
|
||||
type: 'unsupported',
|
||||
errorMessage: '汪汪声浪作品改造将在后续版本开放。',
|
||||
});
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves start intent for direct launches', () => {
|
||||
const bigFishEntry = buildTypedEntry('big-fish');
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(bigFishEntry, buildStartIntentDeps()),
|
||||
).toEqual({
|
||||
type: 'start-big-fish',
|
||||
work: mapPublicWorkDetailToBigFishWork(bigFishEntry),
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
|
||||
const selectedPuzzleDetail = buildPuzzleWork({
|
||||
profileId: 'puzzle-profile',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
buildTypedEntry('puzzle'),
|
||||
buildStartIntentDeps({ selectedPuzzleDetail }),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-puzzle',
|
||||
work: selectedPuzzleDetail,
|
||||
returnStage: 'work-detail',
|
||||
authMode: 'isolated',
|
||||
});
|
||||
|
||||
const puzzleEntry = buildTypedEntry('puzzle', {
|
||||
profileId: 'fallback-puzzle-profile',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
puzzleEntry,
|
||||
buildStartIntentDeps({
|
||||
selectedPuzzleDetail: buildPuzzleWork({ profileId: 'stale-profile' }),
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-puzzle',
|
||||
work: mapPublicWorkDetailToPuzzleWork(puzzleEntry),
|
||||
returnStage: 'work-detail',
|
||||
authMode: 'isolated',
|
||||
});
|
||||
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
buildTypedEntry('jump-hop'),
|
||||
buildStartIntentDeps(),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-jump-hop',
|
||||
profileId: 'jump-hop-profile',
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
buildTypedEntry('wooden-fish'),
|
||||
buildStartIntentDeps(),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-wooden-fish',
|
||||
profileId: 'wooden-fish-profile',
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
buildTypedEntry('visual-novel'),
|
||||
buildStartIntentDeps(),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-visual-novel',
|
||||
profileId: 'visual-novel-profile',
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
buildTypedEntry('edutainment'),
|
||||
buildStartIntentDeps(),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-edutainment',
|
||||
entry: buildTypedEntry('edutainment'),
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves start intent for mapper-backed launches', () => {
|
||||
const match3DEntry = buildTypedEntry('match3d');
|
||||
const match3DWork = buildMatch3DWork({ workId: 'mapped-match3d-work' });
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
match3DEntry,
|
||||
buildStartIntentDeps({
|
||||
mapMatch3DWork: (entry) =>
|
||||
entry === match3DEntry ? match3DWork : null,
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-match3d',
|
||||
work: match3DWork,
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
match3DEntry,
|
||||
buildStartIntentDeps({ mapMatch3DWork: () => null }),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'blocked',
|
||||
errorMessage: '当前抓大鹅作品信息不完整,暂时无法进入玩法。',
|
||||
});
|
||||
|
||||
const squareHoleEntry = buildTypedEntry('square-hole');
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
squareHoleEntry,
|
||||
buildStartIntentDeps(),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-square-hole',
|
||||
work: mapPublicWorkDetailToSquareHoleWork(squareHoleEntry),
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves bark battle start work priority', () => {
|
||||
const entry = buildTypedEntry('bark-battle');
|
||||
const galleryWork = buildBarkBattleWork({
|
||||
workId: 'bark-battle-work',
|
||||
title: '作品架缓存',
|
||||
});
|
||||
const loadedWork = buildBarkBattleWork({
|
||||
workId: 'bark-battle-work',
|
||||
title: '完整作品列表',
|
||||
});
|
||||
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
entry,
|
||||
buildStartIntentDeps({
|
||||
barkBattleGalleryEntries: [galleryWork],
|
||||
barkBattleWorks: [loadedWork],
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-bark-battle',
|
||||
work: galleryWork,
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
entry,
|
||||
buildStartIntentDeps({
|
||||
barkBattleWorks: [loadedWork],
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'start-bark-battle',
|
||||
work: loadedWork,
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(entry, buildStartIntentDeps()),
|
||||
).toEqual({
|
||||
type: 'start-bark-battle',
|
||||
work: mapBarkBattlePublicDetailToWorkSummary(entry),
|
||||
returnStage: 'work-detail',
|
||||
});
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves rpg start intent from loaded detail', () => {
|
||||
const rpgEntry = buildRpgEntry();
|
||||
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(
|
||||
rpgEntry,
|
||||
buildStartIntentDeps({ selectedRpgDetailEntry: rpgEntry }),
|
||||
),
|
||||
).toEqual({
|
||||
type: 'record-rpg-gallery-play',
|
||||
entry: rpgEntry,
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkStartIntent(rpgEntry, buildStartIntentDeps()),
|
||||
).toEqual({
|
||||
type: 'blocked',
|
||||
errorMessage: '作品详情尚未读取完成。',
|
||||
});
|
||||
});
|
||||
|
||||
test('platform public work detail flow resolves direct open decision', () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
JumpHopGalleryCardResponse,
|
||||
JumpHopWorkProfileResponse,
|
||||
} from '../../../packages/shared/src/contracts/jumpHop';
|
||||
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
|
||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
||||
@@ -121,6 +122,72 @@ export type PlatformPublicWorkRemixIntent =
|
||||
errorMessage: string;
|
||||
};
|
||||
|
||||
export type PlatformPublicWorkStartIntent =
|
||||
| {
|
||||
type: 'blocked';
|
||||
errorMessage: string;
|
||||
}
|
||||
| {
|
||||
type: 'start-big-fish';
|
||||
work: BigFishWorkSummary;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-puzzle';
|
||||
work: PuzzleWorkSummary;
|
||||
returnStage: 'work-detail';
|
||||
authMode: 'isolated';
|
||||
}
|
||||
| {
|
||||
type: 'start-jump-hop';
|
||||
profileId: string;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-wooden-fish';
|
||||
profileId: string;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-match3d';
|
||||
work: Match3DWorkSummary;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-square-hole';
|
||||
work: SquareHoleWorkSummary;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-visual-novel';
|
||||
profileId: string;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-bark-battle';
|
||||
work: BarkBattleWorkSummary;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'start-edutainment';
|
||||
entry: PlatformPublicGalleryCard;
|
||||
returnStage: 'work-detail';
|
||||
}
|
||||
| {
|
||||
type: 'record-rpg-gallery-play';
|
||||
entry: CustomWorldGalleryCard;
|
||||
};
|
||||
|
||||
export type PlatformPublicWorkStartIntentDeps = {
|
||||
selectedPuzzleDetail?: PuzzleWorkSummary | null;
|
||||
selectedRpgDetailEntry?: CustomWorldGalleryCard | null;
|
||||
barkBattleGalleryEntries?: readonly BarkBattleWorkSummary[];
|
||||
barkBattleWorks?: readonly BarkBattleWorkSummary[];
|
||||
mapMatch3DWork: (
|
||||
entry: PlatformPublicGalleryCard,
|
||||
) => Match3DWorkSummary | null;
|
||||
};
|
||||
|
||||
export type PlatformPublicWorkDetailOpenDecision =
|
||||
| {
|
||||
type: 'blocked';
|
||||
@@ -622,6 +689,149 @@ export function resolvePlatformPublicWorkRemixIntent(
|
||||
};
|
||||
}
|
||||
|
||||
export function resolvePlatformPublicWorkStartIntent(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
deps: PlatformPublicWorkStartIntentDeps,
|
||||
): PlatformPublicWorkStartIntent {
|
||||
if (isBigFishGalleryEntry(entry)) {
|
||||
const work = mapPublicWorkDetailToBigFishWork(entry);
|
||||
if (!work) {
|
||||
return {
|
||||
type: 'blocked',
|
||||
errorMessage: '当前作品缺少会话信息,暂时无法进入玩法。',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'start-big-fish',
|
||||
work,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isPuzzleGalleryEntry(entry)) {
|
||||
const work =
|
||||
deps.selectedPuzzleDetail?.profileId === entry.profileId
|
||||
? deps.selectedPuzzleDetail
|
||||
: mapPublicWorkDetailToPuzzleWork(entry);
|
||||
if (!work) {
|
||||
return {
|
||||
type: 'blocked',
|
||||
errorMessage: '当前拼图作品信息不完整,暂时无法进入玩法。',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'start-puzzle',
|
||||
work,
|
||||
returnStage: 'work-detail',
|
||||
authMode: 'isolated',
|
||||
};
|
||||
}
|
||||
|
||||
if (isJumpHopGalleryEntry(entry)) {
|
||||
return {
|
||||
type: 'start-jump-hop',
|
||||
profileId: entry.profileId,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isWoodenFishGalleryEntry(entry)) {
|
||||
return {
|
||||
type: 'start-wooden-fish',
|
||||
profileId: entry.profileId,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
// 中文注释:抓大鹅运行态素材归一仍归 Match3D Module,公开详情 Flow 只接其 Adapter。
|
||||
const work = deps.mapMatch3DWork(entry);
|
||||
if (!work) {
|
||||
return {
|
||||
type: 'blocked',
|
||||
errorMessage: '当前抓大鹅作品信息不完整,暂时无法进入玩法。',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'start-match3d',
|
||||
work,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isSquareHoleGalleryEntry(entry)) {
|
||||
const work = mapPublicWorkDetailToSquareHoleWork(entry);
|
||||
if (!work) {
|
||||
return {
|
||||
type: 'blocked',
|
||||
errorMessage: '当前方洞挑战作品信息不完整,暂时无法进入玩法。',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'start-square-hole',
|
||||
work,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isVisualNovelGalleryEntry(entry)) {
|
||||
return {
|
||||
type: 'start-visual-novel',
|
||||
profileId: entry.profileId,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isBarkBattleGalleryEntry(entry)) {
|
||||
const work =
|
||||
deps.barkBattleGalleryEntries?.find(
|
||||
(item) => item.workId === entry.workId,
|
||||
) ??
|
||||
deps.barkBattleWorks?.find((item) => item.workId === entry.workId) ??
|
||||
mapBarkBattlePublicDetailToWorkSummary(entry);
|
||||
if (!work) {
|
||||
return {
|
||||
type: 'blocked',
|
||||
errorMessage: '当前汪汪声浪作品信息不完整,暂时无法进入玩法。',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'start-bark-battle',
|
||||
work,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
if (isEdutainmentGalleryEntry(entry)) {
|
||||
return {
|
||||
type: 'start-edutainment',
|
||||
entry,
|
||||
returnStage: 'work-detail',
|
||||
};
|
||||
}
|
||||
|
||||
const launchEntry =
|
||||
deps.selectedRpgDetailEntry?.profileId === entry.profileId
|
||||
? deps.selectedRpgDetailEntry
|
||||
: null;
|
||||
if (!launchEntry) {
|
||||
return {
|
||||
type: 'blocked',
|
||||
errorMessage: '作品详情尚未读取完成。',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'record-rpg-gallery-play',
|
||||
entry: launchEntry,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolvePlatformPublicWorkDetailOpenDecision(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
deps: PlatformPublicWorkDetailOpenDecisionDeps = {},
|
||||
|
||||
Reference in New Issue
Block a user