refactor: 收口公开详情编辑意图
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
- 追加决策:公开详情点赞能力矩阵由 `resolvePlatformPublicWorkLikeIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、API 调用、缓存同步、错误展示和 busy 状态。
|
- 追加决策:公开详情点赞能力矩阵由 `resolvePlatformPublicWorkLikeIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、API 调用、缓存同步、错误展示和 busy 状态。
|
||||||
- 追加决策:公开详情改造能力矩阵由 `resolvePlatformPublicWorkRemixIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、remix API、session / 缓存写入、stage 切换、错误展示和 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 维护素材归一与背景资产提升。
|
- 追加决策:公开详情启动分流由 `resolvePlatformPublicWorkStartIntent(entry, deps)` 收口;Module 只返回大鱼吃小鱼、拼图、跳一跳、敲木鱼、抓大鹅、方洞挑战、视觉小说、汪汪声浪、宝贝识物或旧 RPG gallery 记录游玩的 intent。壳层仍执行登录保护、运行态启动、RPG 游玩记录、详情更新、busy 状态和错误展示;抓大鹅 public detail -> work mapper 作为 Adapter 注入,继续由 Match3D Runtime Profile Module 维护素材归一与背景资产提升。
|
||||||
|
- 追加决策:自有公开作品编辑分流由 `resolvePlatformPublicWorkEditIntent(entry, deps)` 收口;Module 只返回可编辑草稿目标、需解析宝贝识物本地草稿 intent、旧 RPG gallery 编辑 intent 或原阻断文案。壳层仍执行登录保护、草稿恢复、宝贝识物异步草稿解析、RPG 编辑导航和错误展示;抓大鹅 public detail -> work mapper 仍作为 Adapter 注入,不复制 Match3D 素材归一规则。
|
||||||
- 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。
|
- 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。
|
||||||
- 验证方式:`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`。
|
- 验证方式:`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`。
|
- 关联文档:`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/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)。
|
||||||
|
|
||||||
创作中心作品架打开动作由 `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)。
|
创作中心作品架打开动作由 `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)。
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
- `getPlatformPublicWorkDetailKind(entry)`
|
- `getPlatformPublicWorkDetailKind(entry)`
|
||||||
- `resolvePlatformPublicWorkDetailOpenStrategy(entry)`
|
- `resolvePlatformPublicWorkDetailOpenStrategy(entry)`
|
||||||
- `resolvePlatformPublicWorkActionMode(entry, viewerUserId)`
|
- `resolvePlatformPublicWorkActionMode(entry, viewerUserId)`
|
||||||
|
- `resolvePlatformPublicWorkEditIntent(entry, deps)`
|
||||||
- `resolvePlatformPublicWorkLikeIntent(entry)`
|
- `resolvePlatformPublicWorkLikeIntent(entry)`
|
||||||
- `resolvePlatformPublicWorkRemixIntent(entry)`
|
- `resolvePlatformPublicWorkRemixIntent(entry)`
|
||||||
- `resolvePlatformPublicWorkStartIntent(entry, deps)`
|
- `resolvePlatformPublicWorkStartIntent(entry, deps)`
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
- `PlatformEntryFlowShellImpl.tsx` 继续作为 Adapter:根据 open strategy 调用 `openPublicWorkDetail`、`openPuzzlePublicWorkDetail`、`openJumpHopPublicWorkDetail`、`openWoodenFishPublicWorkDetail`、`openVisualNovelPublicWorkDetail` 或 `openRpgPublicWorkDetail`。
|
- `PlatformEntryFlowShellImpl.tsx` 继续作为 Adapter:根据 open strategy 调用 `openPublicWorkDetail`、`openPuzzlePublicWorkDetail`、`openJumpHopPublicWorkDetail`、`openWoodenFishPublicWorkDetail`、`openVisualNovelPublicWorkDetail` 或 `openRpgPublicWorkDetail`。
|
||||||
- 公开详情 entry 映射与公开详情反推玩法 work 摘要也收口到 Module。壳层只在运行态启动、编辑、改造、推荐缓存和详情展示时调用映射 Interface,不再在壳层顶部持有每个玩法的 DTO 拼装 Implementation。
|
- 公开详情 entry 映射与公开详情反推玩法 work 摘要也收口到 Module。壳层只在运行态启动、编辑、改造、推荐缓存和详情展示时调用映射 Interface,不再在壳层顶部持有每个玩法的 DTO 拼装 Implementation。
|
||||||
- `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 处理素材归一和背景资产提升;`platformPublicWorkDetailFlow.ts` 不复制 Match3D 运行态素材规则。
|
- `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 处理素材归一和背景资产提升;`platformPublicWorkDetailFlow.ts` 不复制 Match3D 运行态素材规则。
|
||||||
- 公开详情启动、点赞和改造只抽“意图” Interface,不把整个 callback 搬进 Module。壳层继续作为 Adapter 执行鉴权、API 调用、运行态启动、busy 状态、缓存同步、stage 切换和错误 setter,避免形成伪 Seam。
|
- 公开详情启动、编辑、点赞和改造只抽“意图” Interface,不把整个 callback 搬进 Module。壳层继续作为 Adapter 执行鉴权、API 调用、运行态启动、草稿恢复、busy 状态、缓存同步、stage 切换和错误 setter,避免形成伪 Seam。
|
||||||
|
|
||||||
## Interface 约束
|
## Interface 约束
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@
|
|||||||
- 大鱼吃小鱼、抓大鹅、方洞挑战、汪汪声浪、宝贝识物和其它可直接展示的公开 entry 返回 `use-entry` strategy。
|
- 大鱼吃小鱼、抓大鹅、方洞挑战、汪汪声浪、宝贝识物和其它可直接展示的公开 entry 返回 `use-entry` strategy。
|
||||||
- RPG 返回 `load-rpg-detail` strategy,由壳层 Adapter 继续调用 RPG 详情读取流程。
|
- RPG 返回 `load-rpg-detail` strategy,由壳层 Adapter 继续调用 RPG 详情读取流程。
|
||||||
- `resolvePlatformPublicWorkActionMode` 只比较 `entry.ownerUserId` 与当前 viewer user id;当前用户拥有该公开作品时返回 `edit`,否则返回 `remix`。
|
- `resolvePlatformPublicWorkActionMode` 只比较 `entry.ownerUserId` 与当前 viewer user id;当前用户拥有该公开作品时返回 `edit`,否则返回 `remix`。
|
||||||
|
- `resolvePlatformPublicWorkEditIntent` 只表达自有公开作品编辑意图:大鱼吃小鱼、拼图、抓大鹅、方洞挑战、视觉小说和汪汪声浪在能定位原草稿时返回对应 draft open 目标;跳一跳、敲木鱼和缺草稿作品返回原阻断文案;宝贝识物只返回需解析本地草稿的 intent;旧 RPG gallery fallback 只在完整 RPG 详情已补读且 profile 匹配时返回编辑 intent。壳层仍执行草稿恢复、宝贝识物异步草稿解析、RPG 编辑导航和错误展示。
|
||||||
|
- `resolvePlatformPublicWorkEditIntent` 的 `deps` 只接编辑决策所需的当前拼图详情、当前 RPG 详情、视觉小说作品缓存、汪汪声浪作品缓存,以及抓大鹅 public detail -> work 的 Adapter。抓大鹅 Adapter 必须来自 Match3D Runtime Profile Module,以保留 `generatedItemAssets` 归一化与背景资产提升的 Locality。
|
||||||
- `resolvePlatformPublicWorkLikeIntent` 只表达公开作品点赞意图:大鱼吃小鱼、拼图和旧 RPG gallery fallback 返回可执行 intent;宝贝识物、汪汪声浪、方洞挑战和视觉小说返回不可用文案。壳层只按 intent 调用 API、写缓存和展示错误,不再持有这组能力矩阵。
|
- `resolvePlatformPublicWorkLikeIntent` 只表达公开作品点赞意图:大鱼吃小鱼、拼图和旧 RPG gallery fallback 返回可执行 intent;宝贝识物、汪汪声浪、方洞挑战和视觉小说返回不可用文案。壳层只按 intent 调用 API、写缓存和展示错误,不再持有这组能力矩阵。
|
||||||
- `resolvePlatformPublicWorkRemixIntent` 只表达公开作品改造意图:大鱼吃小鱼和拼图返回可执行 intent 与成功后目标 stage,旧 RPG gallery fallback 返回可执行 intent,其它玩法返回原未开放文案。壳层只按 intent 调用 remix API、写 session / 缓存、切 stage 和展示错误。
|
- `resolvePlatformPublicWorkRemixIntent` 只表达公开作品改造意图:大鱼吃小鱼和拼图返回可执行 intent 与成功后目标 stage,旧 RPG gallery fallback 返回可执行 intent,其它玩法返回原未开放文案。壳层只按 intent 调用 remix API、写 session / 缓存、切 stage 和展示错误。
|
||||||
- `resolvePlatformPublicWorkStartIntent` 只表达公开作品“开始游玩”意图:大鱼吃小鱼、拼图、跳一跳、敲木鱼、抓大鹅、方洞挑战、视觉小说、汪汪声浪和宝贝识物返回对应启动目标;旧 RPG gallery fallback 只在完整 RPG 详情已补读且 profile 匹配时返回记录游玩 intent,否则返回原阻断文案。壳层仍执行登录保护、运行态启动、RPG 游玩记录、详情更新、busy 状态和错误展示。
|
- `resolvePlatformPublicWorkStartIntent` 只表达公开作品“开始游玩”意图:大鱼吃小鱼、拼图、跳一跳、敲木鱼、抓大鹅、方洞挑战、视觉小说、汪汪声浪和宝贝识物返回对应启动目标;旧 RPG gallery fallback 只在完整 RPG 详情已补读且 profile 匹配时返回记录游玩 intent,否则返回原阻断文案。壳层仍执行登录保护、运行态启动、RPG 游玩记录、详情更新、busy 状态和错误展示。
|
||||||
@@ -48,7 +51,7 @@
|
|||||||
|
|
||||||
## Depth / Leverage / Locality
|
## Depth / Leverage / Locality
|
||||||
|
|
||||||
- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id、当前拼图 run 或少量启动 deps,即可得到详情打开策略、动作模式、点赞 / 改造 / 启动意图、统一详情映射和封面可见数;玩法判定、能力矩阵与 DTO 默认值藏在 Module Implementation 内。
|
- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id、当前拼图 run 或少量启动 / 编辑 deps,即可得到详情打开策略、动作模式、编辑 / 点赞 / 改造 / 启动意图、统一详情映射和封面可见数;玩法判定、能力矩阵与 DTO 默认值藏在 Module Implementation 内。
|
||||||
- **Leverage**:新增玩法公开详情时先补 Strategy / Mapping 单测,再接壳层 Adapter,不必在多个 JSX / callback 位置重复 sourceType 判断或 DTO 回填。
|
- **Leverage**:新增玩法公开详情时先补 Strategy / Mapping 单测,再接壳层 Adapter,不必在多个 JSX / callback 位置重复 sourceType 判断或 DTO 回填。
|
||||||
- **Locality**:公开作品详情入口的纯策略与通用映射集中到一个小 Module;Match3D 素材归一仍在 Match3D Module;启动运行态、点赞、改造、编辑等副作用仍留在壳层,避免伪 Seam。
|
- **Locality**:公开作品详情入口的纯策略与通用映射集中到一个小 Module;Match3D 素材归一仍在 Match3D Module;启动运行态、点赞、改造、编辑等副作用仍留在壳层,避免伪 Seam。
|
||||||
|
|
||||||
|
|||||||
@@ -532,6 +532,7 @@ import {
|
|||||||
resolvePlatformPublicWorkActionMode,
|
resolvePlatformPublicWorkActionMode,
|
||||||
resolvePlatformPublicWorkDetailOpenDecision,
|
resolvePlatformPublicWorkDetailOpenDecision,
|
||||||
resolvePlatformPublicWorkDetailOpenStrategy,
|
resolvePlatformPublicWorkDetailOpenStrategy,
|
||||||
|
resolvePlatformPublicWorkEditIntent,
|
||||||
resolvePlatformPublicWorkLikeIntent,
|
resolvePlatformPublicWorkLikeIntent,
|
||||||
resolvePlatformPublicWorkRemixIntent,
|
resolvePlatformPublicWorkRemixIntent,
|
||||||
resolvePlatformPublicWorkStartIntent,
|
resolvePlatformPublicWorkStartIntent,
|
||||||
@@ -13513,117 +13514,65 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
runProtectedAction(async () => {
|
runProtectedAction(async () => {
|
||||||
setPublicWorkDetailError(null);
|
setPublicWorkDetailError(null);
|
||||||
|
|
||||||
// 中文注释:自有公开作品必须恢复原草稿,不能复用 remix 复制链路。
|
const intent = resolvePlatformPublicWorkEditIntent(entry, {
|
||||||
if (isBigFishGalleryEntry(entry)) {
|
selectedPuzzleDetail,
|
||||||
const work = mapPublicWorkDetailToBigFishWork(entry);
|
selectedRpgDetailEntry: selectedDetailEntry,
|
||||||
if (!work?.sourceSessionId?.trim()) {
|
visualNovelWorks,
|
||||||
setPublicWorkDetailError(
|
barkBattleGalleryEntries,
|
||||||
'这份大鱼吃小鱼作品缺少原草稿会话,暂时无法编辑。',
|
barkBattleWorks,
|
||||||
|
mapMatch3DWork: mapPublicWorkDetailToMatch3DWork,
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (intent.type) {
|
||||||
|
case 'blocked':
|
||||||
|
setPublicWorkDetailError(intent.errorMessage);
|
||||||
|
return;
|
||||||
|
case 'edit-big-fish':
|
||||||
|
void openBigFishDraft(intent.work);
|
||||||
|
return;
|
||||||
|
case 'edit-puzzle':
|
||||||
|
void openPuzzleDraft(intent.work);
|
||||||
|
return;
|
||||||
|
case 'edit-match3d':
|
||||||
|
void openMatch3DDraft(intent.work, {
|
||||||
|
forceDraft: intent.forceDraft,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case 'edit-square-hole':
|
||||||
|
void openSquareHoleDraft(intent.work, {
|
||||||
|
forceDraft: intent.forceDraft,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case 'edit-visual-novel':
|
||||||
|
void openVisualNovelDraft(intent.work, {
|
||||||
|
forceDraft: intent.forceDraft,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case 'resolve-edutainment-draft': {
|
||||||
|
const matchedDraft = await resolveBabyObjectMatchRuntimeDraft(
|
||||||
|
intent.entry,
|
||||||
);
|
);
|
||||||
|
if (!matchedDraft) {
|
||||||
|
setPublicWorkDetailError('这份宝贝识物缺少可编辑草稿。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openBabyObjectMatchDraft(matchedDraft);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void openBigFishDraft(work);
|
case 'edit-bark-battle':
|
||||||
return;
|
openBarkBattleDraft(intent.work, {
|
||||||
}
|
forceDraft: intent.forceDraft,
|
||||||
|
});
|
||||||
if (isPuzzleGalleryEntry(entry)) {
|
|
||||||
const work =
|
|
||||||
selectedPuzzleDetail?.profileId === entry.profileId
|
|
||||||
? selectedPuzzleDetail
|
|
||||||
: mapPublicWorkDetailToPuzzleWork(entry);
|
|
||||||
if (!work?.sourceSessionId?.trim()) {
|
|
||||||
setPublicWorkDetailError(
|
|
||||||
'这份拼图作品缺少原草稿会话,暂时无法编辑。',
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
case 'edit-rpg-gallery':
|
||||||
void openPuzzleDraft(work);
|
void detailNavigation.openSavedCustomWorldEditor(intent.entry);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMatch3DGalleryEntry(entry)) {
|
|
||||||
const work = mapPublicWorkDetailToMatch3DWork(entry);
|
|
||||||
if (!work?.sourceSessionId?.trim()) {
|
|
||||||
setPublicWorkDetailError(
|
|
||||||
'这份抓大鹅作品缺少原草稿会话,暂时无法编辑。',
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
|
default: {
|
||||||
|
const exhaustive: never = intent;
|
||||||
|
return exhaustive;
|
||||||
}
|
}
|
||||||
void openMatch3DDraft(work, { forceDraft: true });
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSquareHoleGalleryEntry(entry)) {
|
|
||||||
const work = mapPublicWorkDetailToSquareHoleWork(entry);
|
|
||||||
if (!work?.sourceSessionId?.trim()) {
|
|
||||||
setPublicWorkDetailError(
|
|
||||||
'这份方洞挑战作品缺少原草稿会话,暂时无法编辑。',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void openSquareHoleDraft(work, { forceDraft: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isJumpHopGalleryEntry(entry)) {
|
|
||||||
setPublicWorkDetailError('这份跳一跳作品暂时请从作品架编辑。');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWoodenFishGalleryEntry(entry)) {
|
|
||||||
setPublicWorkDetailError('这份敲木鱼作品暂时请从作品架编辑。');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVisualNovelGalleryEntry(entry)) {
|
|
||||||
const matchedWork = visualNovelWorks.find(
|
|
||||||
(work) => work.profileId === entry.profileId,
|
|
||||||
);
|
|
||||||
if (!matchedWork) {
|
|
||||||
setPublicWorkDetailError('这份视觉小说缺少可编辑草稿。');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void openVisualNovelDraft(matchedWork, { forceDraft: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEdutainmentGalleryEntry(entry)) {
|
|
||||||
const matchedDraft = await resolveBabyObjectMatchRuntimeDraft(entry);
|
|
||||||
if (!matchedDraft) {
|
|
||||||
setPublicWorkDetailError('这份宝贝识物缺少可编辑草稿。');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
openBabyObjectMatchDraft(matchedDraft);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBarkBattleGalleryEntry(entry)) {
|
|
||||||
const matchedWork =
|
|
||||||
barkBattleWorks.find((work) => work.workId === entry.workId) ??
|
|
||||||
barkBattleGalleryEntries.find(
|
|
||||||
(work) => work.workId === entry.workId,
|
|
||||||
) ??
|
|
||||||
mapBarkBattlePublicDetailToWorkSummary(entry);
|
|
||||||
if (!matchedWork?.draftId?.trim()) {
|
|
||||||
setPublicWorkDetailError('这份汪汪声浪缺少可编辑草稿。');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
openBarkBattleDraft(matchedWork, { forceDraft: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const editEntry =
|
|
||||||
selectedDetailEntry?.profileId === entry.profileId
|
|
||||||
? selectedDetailEntry
|
|
||||||
: null;
|
|
||||||
if (!editEntry) {
|
|
||||||
setPublicWorkDetailError('作品详情尚未读取完成。');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void detailNavigation.openSavedCustomWorldEditor(editEntry);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ 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 { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
import type {
|
||||||
|
CustomWorldGalleryCard,
|
||||||
|
CustomWorldLibraryEntry,
|
||||||
|
} from '../../../packages/shared/src/contracts/runtime';
|
||||||
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 { CustomWorldProfile } from '../../types';
|
||||||
import {
|
import {
|
||||||
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
|
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
|
||||||
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
|
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
|
||||||
@@ -31,11 +35,13 @@ import {
|
|||||||
mapWoodenFishWorkToPublicWorkDetail,
|
mapWoodenFishWorkToPublicWorkDetail,
|
||||||
type PlatformPublicWorkDetailKind,
|
type PlatformPublicWorkDetailKind,
|
||||||
type PlatformPublicWorkDetailOpenStrategy,
|
type PlatformPublicWorkDetailOpenStrategy,
|
||||||
|
type PlatformPublicWorkEditIntentDeps,
|
||||||
type PlatformPublicWorkStartIntentDeps,
|
type PlatformPublicWorkStartIntentDeps,
|
||||||
resolveActivePlatformPublicWorkAuthorEntry,
|
resolveActivePlatformPublicWorkAuthorEntry,
|
||||||
resolvePlatformPublicWorkActionMode,
|
resolvePlatformPublicWorkActionMode,
|
||||||
resolvePlatformPublicWorkDetailOpenDecision,
|
resolvePlatformPublicWorkDetailOpenDecision,
|
||||||
resolvePlatformPublicWorkDetailOpenStrategy,
|
resolvePlatformPublicWorkDetailOpenStrategy,
|
||||||
|
resolvePlatformPublicWorkEditIntent,
|
||||||
resolvePlatformPublicWorkLikeIntent,
|
resolvePlatformPublicWorkLikeIntent,
|
||||||
resolvePlatformPublicWorkRemixIntent,
|
resolvePlatformPublicWorkRemixIntent,
|
||||||
resolvePlatformPublicWorkStartIntent,
|
resolvePlatformPublicWorkStartIntent,
|
||||||
@@ -88,6 +94,18 @@ function buildRpgEntry(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildRpgLibraryEntry(
|
||||||
|
overrides: Partial<CustomWorldLibraryEntry<CustomWorldProfile>> = {},
|
||||||
|
): CustomWorldLibraryEntry<CustomWorldProfile> {
|
||||||
|
return {
|
||||||
|
...buildRpgEntry(overrides),
|
||||||
|
profile: {
|
||||||
|
id: overrides.profileId ?? 'rpg-profile',
|
||||||
|
} as unknown as CustomWorldProfile,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildTypedEntry<TSourceType extends PlatformGallerySourceType>(
|
function buildTypedEntry<TSourceType extends PlatformGallerySourceType>(
|
||||||
sourceType: TSourceType,
|
sourceType: TSourceType,
|
||||||
overrides: TypedPlatformPublicGalleryCardOverrides<TSourceType> = {},
|
overrides: TypedPlatformPublicGalleryCardOverrides<TSourceType> = {},
|
||||||
@@ -410,6 +428,20 @@ function buildStartIntentDeps(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildEditIntentDeps(
|
||||||
|
overrides: Partial<PlatformPublicWorkEditIntentDeps> = {},
|
||||||
|
): PlatformPublicWorkEditIntentDeps {
|
||||||
|
return {
|
||||||
|
selectedPuzzleDetail: null,
|
||||||
|
selectedRpgDetailEntry: null,
|
||||||
|
visualNovelWorks: [],
|
||||||
|
barkBattleGalleryEntries: [],
|
||||||
|
barkBattleWorks: [],
|
||||||
|
mapMatch3DWork: () => buildMatch3DWork(),
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
test('platform public work detail flow resolves detail kind for every play kind', () => {
|
test('platform public work detail flow resolves detail kind for every play kind', () => {
|
||||||
const cases: Array<
|
const cases: Array<
|
||||||
[sourceType: PlatformGallerySourceType, kind: PlatformPublicWorkDetailKind]
|
[sourceType: PlatformGallerySourceType, kind: PlatformPublicWorkDetailKind]
|
||||||
@@ -435,7 +467,7 @@ test('platform public work detail flow resolves detail kind for every play kind'
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('platform public work detail flow resolves open strategy', () => {
|
test('platform public work detail flow resolves open strategy', () => {
|
||||||
const rpgEntry = buildRpgEntry();
|
const rpgEntry = buildRpgLibraryEntry();
|
||||||
const cases: Array<
|
const cases: Array<
|
||||||
[
|
[
|
||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
@@ -522,7 +554,7 @@ test('platform public work detail flow resolves open strategy', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('platform public work detail flow maps work summaries to detail entries', () => {
|
test('platform public work detail flow maps work summaries to detail entries', () => {
|
||||||
const rpgEntry = buildRpgEntry();
|
const rpgEntry = buildRpgLibraryEntry();
|
||||||
|
|
||||||
expect(mapRpgGalleryCardToPublicWorkDetail(rpgEntry)).toBe(rpgEntry);
|
expect(mapRpgGalleryCardToPublicWorkDetail(rpgEntry)).toBe(rpgEntry);
|
||||||
expect(mapPuzzleWorkToPublicWorkDetail(buildPuzzleWork())).toMatchObject({
|
expect(mapPuzzleWorkToPublicWorkDetail(buildPuzzleWork())).toMatchObject({
|
||||||
@@ -843,6 +875,205 @@ test('platform public work detail flow resolves remix intent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('platform public work detail flow resolves edit intent for draft-backed works', () => {
|
||||||
|
const bigFishEntry = buildTypedEntry('big-fish');
|
||||||
|
expect(resolvePlatformPublicWorkEditIntent(bigFishEntry, buildEditIntentDeps()))
|
||||||
|
.toEqual({
|
||||||
|
type: 'edit-big-fish',
|
||||||
|
work: mapPublicWorkDetailToBigFishWork(bigFishEntry),
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedPuzzleDetail = buildPuzzleWork({
|
||||||
|
profileId: 'puzzle-profile',
|
||||||
|
sourceSessionId: 'selected-puzzle-session',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('puzzle'),
|
||||||
|
buildEditIntentDeps({ selectedPuzzleDetail }),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-puzzle',
|
||||||
|
work: selectedPuzzleDetail,
|
||||||
|
});
|
||||||
|
|
||||||
|
const puzzleEntry = buildTypedEntry('puzzle', {
|
||||||
|
profileId: 'fallback-puzzle-profile',
|
||||||
|
sourceSessionId: 'fallback-puzzle-session',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
puzzleEntry,
|
||||||
|
buildEditIntentDeps({
|
||||||
|
selectedPuzzleDetail: buildPuzzleWork({ profileId: 'stale-profile' }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-puzzle',
|
||||||
|
work: mapPublicWorkDetailToPuzzleWork(puzzleEntry),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('puzzle', { sourceSessionId: null }),
|
||||||
|
buildEditIntentDeps(),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份拼图作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('platform public work detail flow resolves edit intent for mapper-backed works', () => {
|
||||||
|
const match3DEntry = buildTypedEntry('match3d');
|
||||||
|
const match3DWork = buildMatch3DWork({ workId: 'editable-match3d-work' });
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
match3DEntry,
|
||||||
|
buildEditIntentDeps({
|
||||||
|
mapMatch3DWork: (entry) =>
|
||||||
|
entry === match3DEntry ? match3DWork : null,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-match3d',
|
||||||
|
work: match3DWork,
|
||||||
|
forceDraft: true,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
match3DEntry,
|
||||||
|
buildEditIntentDeps({
|
||||||
|
mapMatch3DWork: () => buildMatch3DWork({ sourceSessionId: ' ' }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份抓大鹅作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
});
|
||||||
|
|
||||||
|
const squareHoleEntry = buildTypedEntry('square-hole', {
|
||||||
|
sourceSessionId: 'square-hole-session',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(squareHoleEntry, buildEditIntentDeps()),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-square-hole',
|
||||||
|
work: mapPublicWorkDetailToSquareHoleWork(squareHoleEntry),
|
||||||
|
forceDraft: true,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('square-hole', { sourceSessionId: null }),
|
||||||
|
buildEditIntentDeps(),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份方洞挑战作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('platform public work detail flow resolves edit intent for cached work lookups', () => {
|
||||||
|
const visualNovelWork = buildVisualNovelWork();
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('visual-novel'),
|
||||||
|
buildEditIntentDeps({ visualNovelWorks: [visualNovelWork] }),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-visual-novel',
|
||||||
|
work: visualNovelWork,
|
||||||
|
forceDraft: true,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('visual-novel'),
|
||||||
|
buildEditIntentDeps(),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份视觉小说缺少可编辑草稿。',
|
||||||
|
});
|
||||||
|
|
||||||
|
const entry = buildTypedEntry('bark-battle');
|
||||||
|
const galleryWork = buildBarkBattleWork({
|
||||||
|
workId: 'bark-battle-work',
|
||||||
|
draftId: 'gallery-draft',
|
||||||
|
});
|
||||||
|
const loadedWork = buildBarkBattleWork({
|
||||||
|
workId: 'bark-battle-work',
|
||||||
|
draftId: 'loaded-draft',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
entry,
|
||||||
|
buildEditIntentDeps({
|
||||||
|
barkBattleGalleryEntries: [galleryWork],
|
||||||
|
barkBattleWorks: [loadedWork],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-bark-battle',
|
||||||
|
work: loadedWork,
|
||||||
|
forceDraft: true,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('bark-battle', { sourceSessionId: null }),
|
||||||
|
buildEditIntentDeps(),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份汪汪声浪缺少可编辑草稿。',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('platform public work detail flow resolves edit intent for unsupported and deferred works', () => {
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('jump-hop'),
|
||||||
|
buildEditIntentDeps(),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份跳一跳作品暂时请从作品架编辑。',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
buildTypedEntry('wooden-fish'),
|
||||||
|
buildEditIntentDeps(),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份敲木鱼作品暂时请从作品架编辑。',
|
||||||
|
});
|
||||||
|
|
||||||
|
const edutainmentEntry = buildTypedEntry('edutainment');
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(edutainmentEntry, buildEditIntentDeps()),
|
||||||
|
).toEqual({
|
||||||
|
type: 'resolve-edutainment-draft',
|
||||||
|
entry: edutainmentEntry,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rpgEntry = buildRpgLibraryEntry();
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(
|
||||||
|
rpgEntry,
|
||||||
|
buildEditIntentDeps({ selectedRpgDetailEntry: rpgEntry }),
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
type: 'edit-rpg-gallery',
|
||||||
|
entry: rpgEntry,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformPublicWorkEditIntent(rpgEntry, buildEditIntentDeps()),
|
||||||
|
).toEqual({
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '作品详情尚未读取完成。',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('platform public work detail flow resolves start intent for direct launches', () => {
|
test('platform public work detail flow resolves start intent for direct launches', () => {
|
||||||
const bigFishEntry = buildTypedEntry('big-fish');
|
const bigFishEntry = buildTypedEntry('big-fish');
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -7,7 +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 { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
import type {
|
||||||
|
CustomWorldGalleryCard,
|
||||||
|
CustomWorldLibraryEntry,
|
||||||
|
} from '../../../packages/shared/src/contracts/runtime';
|
||||||
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 {
|
import type {
|
||||||
@@ -15,6 +18,7 @@ import type {
|
|||||||
WoodenFishWorkProfileResponse,
|
WoodenFishWorkProfileResponse,
|
||||||
} from '../../../packages/shared/src/contracts/woodenFish';
|
} from '../../../packages/shared/src/contracts/woodenFish';
|
||||||
import { buildPublicWorkStagePath } from '../../routing/appPageRoutes';
|
import { buildPublicWorkStagePath } from '../../routing/appPageRoutes';
|
||||||
|
import type { CustomWorldProfile } from '../../types';
|
||||||
import {
|
import {
|
||||||
isBarkBattleGalleryEntry,
|
isBarkBattleGalleryEntry,
|
||||||
isBigFishGalleryEntry,
|
isBigFishGalleryEntry,
|
||||||
@@ -51,6 +55,10 @@ export type PlatformPublicWorkDetailKind =
|
|||||||
| 'visual-novel'
|
| 'visual-novel'
|
||||||
| 'wooden-fish';
|
| 'wooden-fish';
|
||||||
|
|
||||||
|
export type PlatformRpgPublicWorkDetailEntry =
|
||||||
|
| CustomWorldGalleryCard
|
||||||
|
| CustomWorldLibraryEntry<CustomWorldProfile>;
|
||||||
|
|
||||||
export type PlatformPublicWorkDetailOpenStrategy =
|
export type PlatformPublicWorkDetailOpenStrategy =
|
||||||
| {
|
| {
|
||||||
type: 'use-entry';
|
type: 'use-entry';
|
||||||
@@ -77,7 +85,7 @@ export type PlatformPublicWorkDetailOpenStrategy =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'load-rpg-detail';
|
type: 'load-rpg-detail';
|
||||||
entry: CustomWorldGalleryCard;
|
entry: PlatformRpgPublicWorkDetailEntry;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PlatformPublicWorkActionMode = 'edit' | 'remix';
|
export type PlatformPublicWorkActionMode = 'edit' | 'remix';
|
||||||
@@ -122,6 +130,59 @@ export type PlatformPublicWorkRemixIntent =
|
|||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PlatformPublicWorkEditIntent =
|
||||||
|
| {
|
||||||
|
type: 'blocked';
|
||||||
|
errorMessage: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-big-fish';
|
||||||
|
work: BigFishWorkSummary;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-puzzle';
|
||||||
|
work: PuzzleWorkSummary;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-match3d';
|
||||||
|
work: Match3DWorkSummary;
|
||||||
|
forceDraft: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-square-hole';
|
||||||
|
work: SquareHoleWorkSummary;
|
||||||
|
forceDraft: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-visual-novel';
|
||||||
|
work: VisualNovelWorkSummary;
|
||||||
|
forceDraft: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'resolve-edutainment-draft';
|
||||||
|
entry: PlatformPublicGalleryCard;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-bark-battle';
|
||||||
|
work: BarkBattleWorkSummary;
|
||||||
|
forceDraft: true;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'edit-rpg-gallery';
|
||||||
|
entry: CustomWorldLibraryEntry<CustomWorldProfile>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlatformPublicWorkEditIntentDeps = {
|
||||||
|
selectedPuzzleDetail?: PuzzleWorkSummary | null;
|
||||||
|
selectedRpgDetailEntry?: PlatformRpgPublicWorkDetailEntry | null;
|
||||||
|
visualNovelWorks?: readonly VisualNovelWorkSummary[];
|
||||||
|
barkBattleGalleryEntries?: readonly BarkBattleWorkSummary[];
|
||||||
|
barkBattleWorks?: readonly BarkBattleWorkSummary[];
|
||||||
|
mapMatch3DWork: (
|
||||||
|
entry: PlatformPublicGalleryCard,
|
||||||
|
) => Match3DWorkSummary | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type PlatformPublicWorkStartIntent =
|
export type PlatformPublicWorkStartIntent =
|
||||||
| {
|
| {
|
||||||
type: 'blocked';
|
type: 'blocked';
|
||||||
@@ -175,12 +236,12 @@ export type PlatformPublicWorkStartIntent =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'record-rpg-gallery-play';
|
type: 'record-rpg-gallery-play';
|
||||||
entry: CustomWorldGalleryCard;
|
entry: PlatformRpgPublicWorkDetailEntry;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PlatformPublicWorkStartIntentDeps = {
|
export type PlatformPublicWorkStartIntentDeps = {
|
||||||
selectedPuzzleDetail?: PuzzleWorkSummary | null;
|
selectedPuzzleDetail?: PuzzleWorkSummary | null;
|
||||||
selectedRpgDetailEntry?: CustomWorldGalleryCard | null;
|
selectedRpgDetailEntry?: PlatformRpgPublicWorkDetailEntry | null;
|
||||||
barkBattleGalleryEntries?: readonly BarkBattleWorkSummary[];
|
barkBattleGalleryEntries?: readonly BarkBattleWorkSummary[];
|
||||||
barkBattleWorks?: readonly BarkBattleWorkSummary[];
|
barkBattleWorks?: readonly BarkBattleWorkSummary[];
|
||||||
mapMatch3DWork: (
|
mapMatch3DWork: (
|
||||||
@@ -213,21 +274,27 @@ export type PlatformPublicWorkDetailOpenDecisionDeps = {
|
|||||||
export type ActivePlatformPublicWorkAuthorEntryInput = {
|
export type ActivePlatformPublicWorkAuthorEntryInput = {
|
||||||
selectionStage: string;
|
selectionStage: string;
|
||||||
selectedPublicWorkDetail: PlatformPublicGalleryCard | null;
|
selectedPublicWorkDetail: PlatformPublicGalleryCard | null;
|
||||||
selectedRpgDetailEntry: CustomWorldGalleryCard | null;
|
selectedRpgDetailEntry: PlatformRpgPublicWorkDetailEntry | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isRpgPublicWorkDetailEntry(
|
export function isRpgPublicWorkDetailEntry(
|
||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
): entry is CustomWorldGalleryCard {
|
): entry is PlatformRpgPublicWorkDetailEntry {
|
||||||
return !('sourceType' in entry);
|
return !('sourceType' in entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapRpgGalleryCardToPublicWorkDetail(
|
export function mapRpgGalleryCardToPublicWorkDetail(
|
||||||
entry: CustomWorldGalleryCard,
|
entry: PlatformRpgPublicWorkDetailEntry,
|
||||||
): PlatformPublicGalleryCard {
|
): PlatformPublicGalleryCard {
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRpgPublicWorkLibraryEntry(
|
||||||
|
entry: PlatformRpgPublicWorkDetailEntry | null | undefined,
|
||||||
|
): entry is CustomWorldLibraryEntry<CustomWorldProfile> {
|
||||||
|
return Boolean(entry && 'profile' in entry);
|
||||||
|
}
|
||||||
|
|
||||||
export function mapPuzzleWorkToPublicWorkDetail(
|
export function mapPuzzleWorkToPublicWorkDetail(
|
||||||
item: PuzzleWorkSummary,
|
item: PuzzleWorkSummary,
|
||||||
): PlatformPublicGalleryCard {
|
): PlatformPublicGalleryCard {
|
||||||
@@ -689,6 +756,154 @@ export function resolvePlatformPublicWorkRemixIntent(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolvePlatformPublicWorkEditIntent(
|
||||||
|
entry: PlatformPublicGalleryCard,
|
||||||
|
deps: PlatformPublicWorkEditIntentDeps,
|
||||||
|
): PlatformPublicWorkEditIntent {
|
||||||
|
if (isBigFishGalleryEntry(entry)) {
|
||||||
|
const work = mapPublicWorkDetailToBigFishWork(entry);
|
||||||
|
if (!work?.sourceSessionId?.trim()) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份大鱼吃小鱼作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-big-fish',
|
||||||
|
work,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPuzzleGalleryEntry(entry)) {
|
||||||
|
const work =
|
||||||
|
deps.selectedPuzzleDetail?.profileId === entry.profileId
|
||||||
|
? deps.selectedPuzzleDetail
|
||||||
|
: mapPublicWorkDetailToPuzzleWork(entry);
|
||||||
|
if (!work?.sourceSessionId?.trim()) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份拼图作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-puzzle',
|
||||||
|
work,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMatch3DGalleryEntry(entry)) {
|
||||||
|
// 中文注释:抓大鹅草稿恢复仍复用 Match3D Module 的 public detail -> work Adapter。
|
||||||
|
const work = deps.mapMatch3DWork(entry);
|
||||||
|
if (!work?.sourceSessionId?.trim()) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份抓大鹅作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-match3d',
|
||||||
|
work,
|
||||||
|
forceDraft: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSquareHoleGalleryEntry(entry)) {
|
||||||
|
const work = mapPublicWorkDetailToSquareHoleWork(entry);
|
||||||
|
if (!work?.sourceSessionId?.trim()) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份方洞挑战作品缺少原草稿会话,暂时无法编辑。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-square-hole',
|
||||||
|
work,
|
||||||
|
forceDraft: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJumpHopGalleryEntry(entry)) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份跳一跳作品暂时请从作品架编辑。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWoodenFishGalleryEntry(entry)) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份敲木鱼作品暂时请从作品架编辑。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVisualNovelGalleryEntry(entry)) {
|
||||||
|
const work =
|
||||||
|
deps.visualNovelWorks?.find((item) => item.profileId === entry.profileId) ??
|
||||||
|
null;
|
||||||
|
if (!work) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份视觉小说缺少可编辑草稿。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-visual-novel',
|
||||||
|
work,
|
||||||
|
forceDraft: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEdutainmentGalleryEntry(entry)) {
|
||||||
|
return {
|
||||||
|
type: 'resolve-edutainment-draft',
|
||||||
|
entry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBarkBattleGalleryEntry(entry)) {
|
||||||
|
const work =
|
||||||
|
deps.barkBattleWorks?.find((item) => item.workId === entry.workId) ??
|
||||||
|
deps.barkBattleGalleryEntries?.find(
|
||||||
|
(item) => item.workId === entry.workId,
|
||||||
|
) ??
|
||||||
|
mapBarkBattlePublicDetailToWorkSummary(entry);
|
||||||
|
if (!work?.draftId?.trim()) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '这份汪汪声浪缺少可编辑草稿。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-bark-battle',
|
||||||
|
work,
|
||||||
|
forceDraft: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const editEntry =
|
||||||
|
deps.selectedRpgDetailEntry?.profileId === entry.profileId &&
|
||||||
|
isRpgPublicWorkLibraryEntry(deps.selectedRpgDetailEntry)
|
||||||
|
? deps.selectedRpgDetailEntry
|
||||||
|
: null;
|
||||||
|
if (!editEntry) {
|
||||||
|
return {
|
||||||
|
type: 'blocked',
|
||||||
|
errorMessage: '作品详情尚未读取完成。',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'edit-rpg-gallery',
|
||||||
|
entry: editEntry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function resolvePlatformPublicWorkStartIntent(
|
export function resolvePlatformPublicWorkStartIntent(
|
||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
deps: PlatformPublicWorkStartIntentDeps,
|
deps: PlatformPublicWorkStartIntentDeps,
|
||||||
|
|||||||
Reference in New Issue
Block a user