diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md index 28337fc3..c3aaa42e 100644 --- a/.hermes/shared-memory/decision-log.md +++ b/.hermes/shared-memory/decision-log.md @@ -1289,6 +1289,14 @@ - 验证方式:`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、`npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts -t "generation state|failure notice|failed puzzle"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating puzzle draft|persisted generating match3d draft|completed baby object match draft"`、针对新 Module 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。 - 关联文档:`docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md`。 +## 2026-06-03 Creation Hub Shelf Items Interface 收口 + +- 背景:`creationWorkShelf.ts` 已把各玩法作品映射为 `CreationWorkShelfItem.actions`,但 `CustomWorldCreationHub.tsx` 的生产 Interface 仍接收 raw items 与 open/delete/claim 回调列阵,新增玩法时 Hub props 继续膨胀。 +- 决策:`CustomWorldCreationHub.tsx` 生产 Interface 收敛为 `shelfItems: CreationWorkShelfItem[]` 与少量 UI 状态;`PlatformEntryFlowShellImpl.tsx` 在外层作为 Adapter 调用 `buildCreationWorkShelfItems` 注入完整 actions;Hub 测试改经 `CustomWorldCreationHub.testAdapter.tsx` 把旧 fixture 转成 shelf items,不让测试继续依赖旧浅 Interface。 +- 影响范围:创作 Tab / 草稿 Tab 作品架、RPG / 拼图 / 抓大鹅 / 方洞 / 跳一跳 / 敲木鱼 / 视觉小说 / Bark Battle / 宝贝识物作品打开、删除、生成态与拼图奖励领取。 +- 验证方式:`npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts`、`npm run test -- src/components/custom-world-home/CustomWorldCreationHub.test.tsx`、`npm run test -- src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx`、相关 FlowShell creation hub 交互片段、针对变更文件执行 ESLint、`npm run typecheck`、`npm run check:encoding`。 +- 关联文档:`docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md`。 + ## 2026-06-03 Creation URL State Model 收口 - 背景:平台壳内散落各玩法创作恢复 URL 的 `sessionId` / `profileId` / `draftId` / `workId` 组装、空值归一化、拼图 runtime query key 与拼图稳定身份互推,导致刷新恢复规则缺少稳定测试面。 diff --git a/docs/README.md b/docs/README.md index af451bc9..ab1166ee 100644 --- a/docs/README.md +++ b/docs/README.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)。 -创作中心作品架打开动作由 `CreationWorkShelfItem.actions.open` 统一承载,Hub 不再按玩法 `kind` 分发,规则见 [【前端架构】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)。 平台入口创作生成通知、pending 作品架占位、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。 diff --git a/docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md b/docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md index b6e954b9..5b1cfd6c 100644 --- a/docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md +++ b/docs/technical/【前端架构】WorkShelfModule收口计划-2026-06-03.md @@ -2,7 +2,7 @@ ## 背景 -创作中心作品架需要同时展示 RPG、拼图、抓大鹅、方洞、跳一跳、敲木鱼、视觉小说、Bark Battle 和宝贝识物等作品。`creationWorkShelf.ts` 已经统一了卡片标题、摘要、封面、发布码、分享路径、指标、生成态和动作 Adapter,但 `CustomWorldCreationHub.tsx` 仍在点击作品卡时按玩法 `kind` 再写一遍打开逻辑,导致调用方仍须理解每种玩法。 +创作中心作品架需要同时展示 RPG、拼图、抓大鹅、方洞、跳一跳、敲木鱼、视觉小说、Bark Battle 和宝贝识物等作品。`creationWorkShelf.ts` 已经统一了卡片标题、摘要、封面、发布码、分享路径、指标、生成态和动作 Adapter。后续深化前,`CustomWorldCreationHub.tsx` 虽已不再按玩法 `kind` 分发点击,但生产调用仍向 Hub 传入多玩法 raw items 与 open/delete/claim 回调列阵,Hub Interface 仍偏 shallow。 ## 决策 @@ -10,20 +10,25 @@ `buildCreationWorkShelfItemsFromSources` 是作品架 source registry 的正式 **Interface**。每个玩法提供一个 `CreationWorkShelfSourceAdapter`,Adapter 负责把玩法数据、删除权限、打开动作和特殊动作映射为 `CreationWorkShelfItem[]`。registry 统一执行 flatten、运行态覆盖、持久化生成态兜底和更新时间排序。 +`CustomWorldCreationHub.tsx` 的生产 **Interface** 收敛为 `shelfItems: CreationWorkShelfItem[]` 加 `loading/error/onRetry/mode/recentWorkItems/onOpenShelfItem/deletingWorkId/claimingPuzzleProfileId` 等 UI 状态。平台壳 `PlatformEntryFlowShellImpl.tsx` 在外层作为 Adapter 调用 `buildCreationWorkShelfItems` 注入完整 open/delete/claim actions 后再传给 Hub;Hub 不再接触各玩法 raw items、删除权限布尔值或玩法专属打开回调。 + +测试文件通过 `CustomWorldCreationHub.testAdapter.tsx` 把旧 fixture 转成 `shelfItems`,避免测试继续强化生产 Hub 的旧浅 Interface。 + 此决策让 `creationWorkShelf.ts` 的 **Module** 更 deep: - **Implementation**:玩法差异、草稿 / 已发布分支、profileId 进入方式和回调绑定都留在 Work Shelf Adapter 内。 -- **Interface**:Hub 只需要 `CreationWorkShelfItem`;后续调用方也可只传 `CreationWorkShelfSourceAdapter[]`,不需要知道每种玩法的打开规则、状态覆盖和排序规则。 +- **Interface**:Hub 只需要 `CreationWorkShelfItem[]`;后续调用方也可只传 `CreationWorkShelfSourceAdapter[]`,不需要知道每种玩法的打开规则、状态覆盖和排序规则。 - **Leverage**:新增玩法时只补 shelf item 映射与 Adapter,Hub 不再新增 switch 分支。 - **Locality**:作品架点击行为、source flatten、运行态覆盖和排序错误集中在 `creationWorkShelf.ts` 与其测试里定位。 ## 后续深化 -`buildCreationWorkShelfItems` 仍保留旧长参数兼容入口,但其 **Implementation** 已改为组装 `CreationWorkShelfSourceAdapter[]` 后复用 `buildCreationWorkShelfItemsFromSources`。下一步可让 Hub / 平台壳逐步直接传入 source adapters,从而减少按玩法平铺的参数数量。删除、刷新和直达恢复也可沿同一 seam 收口。 +`buildCreationWorkShelfItems` 仍保留旧长参数兼容入口,但其 **Implementation** 已改为组装 `CreationWorkShelfSourceAdapter[]` 后复用 `buildCreationWorkShelfItemsFromSources`。下一步可让平台壳直接传入 source adapters,从而继续减少按玩法平铺的参数数量。`deletingWorkId` 与 `claimingPuzzleProfileId` 仍是 Hub UI 状态,可后续下沉到 shelf item/action busy state。 ## 验证 -- `npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx` +- `npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts src/components/custom-world-home/CustomWorldCreationHub.test.tsx src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx` +- `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "creation hub published work can open detail view before deleting from detail page|creation hub published work enters existing detail view|creation hub published work card reveals delete action after card action reveal"` - `npm run typecheck` - `npm run check:encoding` - 针对变更文件执行 ESLint diff --git a/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx b/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx index 61e0c605..20ac5d68 100644 --- a/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx +++ b/src/components/custom-world-home/CustomWorldCreationHub.interaction.test.tsx @@ -10,7 +10,7 @@ import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contract import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks'; import type { CreationEntryConfig } from '../../services/creationEntryConfigService'; import { derivePlatformCreationTypes } from '../platform-entry/platformEntryCreationTypes'; -import { CustomWorldCreationHub } from './CustomWorldCreationHub'; +import { CustomWorldCreationHub } from './CustomWorldCreationHub.testAdapter'; const noopCreateType = () => {}; diff --git a/src/components/custom-world-home/CustomWorldCreationHub.test.tsx b/src/components/custom-world-home/CustomWorldCreationHub.test.tsx index 43d0ebea..0701a005 100644 --- a/src/components/custom-world-home/CustomWorldCreationHub.test.tsx +++ b/src/components/custom-world-home/CustomWorldCreationHub.test.tsx @@ -4,7 +4,7 @@ import { expect, test } from 'vitest'; import type { CreationEntryConfig } from '../../services/creationEntryConfigService'; import { derivePlatformCreationTypes } from '../platform-entry/platformEntryCreationTypes'; import { buildCreationWorkShelfItems } from './creationWorkShelf'; -import { CustomWorldCreationHub } from './CustomWorldCreationHub'; +import { CustomWorldCreationHub } from './CustomWorldCreationHub.testAdapter'; const noopCreateType = () => {}; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/src/components/custom-world-home/CustomWorldCreationHub.testAdapter.tsx b/src/components/custom-world-home/CustomWorldCreationHub.testAdapter.tsx new file mode 100644 index 00000000..b87d3732 --- /dev/null +++ b/src/components/custom-world-home/CustomWorldCreationHub.testAdapter.tsx @@ -0,0 +1,128 @@ +import { isPlatformCreationTypeVisible } from '../platform-entry/platformEntryCreationTypes'; +import { + buildCreationWorkShelfItems, + type CreationWorkShelfItem, +} from './creationWorkShelf'; +import { + CustomWorldCreationHub as CustomWorldCreationHubView, +} from './CustomWorldCreationHub'; + +type ShelfBuilderParams = Parameters[0]; +type HubViewProps = Parameters[0]; + +type LegacyCustomWorldCreationHubProps = Omit & + Partial< + Omit + > & { + shelfItems?: CreationWorkShelfItem[]; + items?: ShelfBuilderParams['rpgItems']; + bigFishItems?: ShelfBuilderParams['bigFishItems']; + puzzleItems?: ShelfBuilderParams['puzzleItems']; + onOpenDraft?: ShelfBuilderParams['onOpenRpgDraft']; + onEnterPublished?: ShelfBuilderParams['onEnterRpgPublished']; + onDeletePublished?: ShelfBuilderParams['onDeleteRpg'] | null; + getWorkState?: ShelfBuilderParams['getItemState']; + }; + +/** 测试用 Adapter:旧 fixture 先转成 shelfItems,生产 Hub Interface 保持窄面。 */ +export function CustomWorldCreationHub({ + shelfItems, + items = [], + rpgLibraryEntries = [], + bigFishItems = [], + match3dItems = [], + squareHoleItems = [], + jumpHopItems = [], + woodenFishItems = [], + puzzleItems = [], + babyObjectMatchItems = [], + barkBattleItems = [], + visualNovelItems = [], + onOpenDraft, + onEnterPublished, + onDeletePublished = null, + onOpenBigFishDetail, + onDeleteBigFish, + onOpenMatch3DDetail, + onDeleteMatch3D, + onOpenSquareHoleDetail, + onDeleteSquareHole, + onOpenJumpHopDetail, + onDeleteJumpHop, + onOpenWoodenFishDetail, + onDeleteWoodenFish, + onOpenPuzzleDetail, + onDeletePuzzle, + onClaimPuzzlePointIncentive, + onOpenBabyObjectMatchDetail, + onDeleteBabyObjectMatch, + onOpenBarkBattleDetail, + onDeleteBarkBattle, + onOpenVisualNovelDetail, + onDeleteVisualNovel, + getItemState, + getWorkState, + creationTypes, + ...props +}: LegacyCustomWorldCreationHubProps) { + const isSquareHoleCreationVisible = isPlatformCreationTypeVisible( + creationTypes, + 'square-hole', + ); + const resolvedShelfItems = + shelfItems ?? + buildCreationWorkShelfItems({ + rpgItems: items, + rpgLibraryEntries, + bigFishItems, + match3dItems, + squareHoleItems: isSquareHoleCreationVisible ? squareHoleItems : [], + jumpHopItems, + woodenFishItems, + puzzleItems, + babyObjectMatchItems, + barkBattleItems, + visualNovelItems, + canDeleteRpg: Boolean(onDeletePublished), + canDeleteBigFish: Boolean(onDeleteBigFish), + canDeleteMatch3D: Boolean(onDeleteMatch3D), + canDeleteSquareHole: Boolean(onDeleteSquareHole), + canDeleteJumpHop: Boolean(onDeleteJumpHop), + canDeleteWoodenFish: Boolean(onDeleteWoodenFish), + canDeletePuzzle: Boolean(onDeletePuzzle), + canDeleteBabyObjectMatch: Boolean(onDeleteBabyObjectMatch), + canDeleteBarkBattle: Boolean(onDeleteBarkBattle), + canDeleteVisualNovel: Boolean(onDeleteVisualNovel), + onOpenRpgDraft: onOpenDraft, + onEnterRpgPublished: onEnterPublished, + onDeleteRpg: onDeletePublished ?? undefined, + onOpenBigFishDetail, + onDeleteBigFish, + onOpenMatch3DDetail, + onDeleteMatch3D, + onOpenSquareHoleDetail, + onDeleteSquareHole, + onOpenJumpHopDetail, + onDeleteJumpHop, + onOpenWoodenFishDetail, + onDeleteWoodenFish, + onOpenPuzzleDetail, + onDeletePuzzle, + onClaimPuzzlePointIncentive, + onOpenBabyObjectMatchDetail, + onDeleteBabyObjectMatch, + onOpenBarkBattleDetail, + onDeleteBarkBattle, + onOpenVisualNovelDetail, + onDeleteVisualNovel, + getItemState: getItemState ?? getWorkState, + }); + + return ( + + ); +} diff --git a/src/components/custom-world-home/CustomWorldCreationHub.tsx b/src/components/custom-world-home/CustomWorldCreationHub.tsx index e4be3bd6..5243c00b 100644 --- a/src/components/custom-world-home/CustomWorldCreationHub.tsx +++ b/src/components/custom-world-home/CustomWorldCreationHub.tsx @@ -1,28 +1,13 @@ import { useEffect, useMemo, useState } from 'react'; -import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle'; -import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; -import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldAgent'; -import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; -import type { JumpHopWorkSummaryResponse } from '../../../packages/shared/src/contracts/jumpHop'; -import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks'; -import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; -import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime'; -import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks'; -import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel'; -import type { WoodenFishWorkSummaryResponse } from '../../../packages/shared/src/contracts/woodenFish'; import type { CreationEntryConfig } from '../../services/creationEntryConfigService'; -import type { CustomWorldProfile } from '../../types'; import type { PlatformCreationTypeCard, PlatformCreationTypeId, } from '../platform-entry/platformEntryCreationTypes'; -import { isPlatformCreationTypeVisible } from '../platform-entry/platformEntryCreationTypes'; import { - buildCreationWorkShelfItems, type CreationWorkShelfItem, type CreationWorkShelfMetricId, - type CreationWorkShelfRuntimeState, getCreationWorkShelfItemTime, } from './creationWorkShelf'; import { @@ -47,7 +32,7 @@ type WorkMetricSnapshot = Record< >; type CustomWorldCreationHubProps = { - items: CustomWorldWorkSummary[]; + shelfItems: CreationWorkShelfItem[]; loading: boolean; error: string | null; onRetry: () => void; @@ -55,45 +40,8 @@ type CustomWorldCreationHubProps = { entryConfig: CreationEntryConfig; creationTypes: readonly PlatformCreationTypeCard[]; onCreateType: (type: PlatformCreationTypeId) => void; - onOpenDraft: (item: CustomWorldWorkSummary) => void; - onEnterPublished: (profileId: string) => void; - onDeletePublished?: ((item: CustomWorldWorkSummary) => void) | null; deletingWorkId?: string | null; - rpgLibraryEntries?: CustomWorldLibraryEntry[]; - bigFishItems?: BigFishWorkSummary[]; - onOpenBigFishDetail?: (item: BigFishWorkSummary) => void; - onDeleteBigFish?: ((item: BigFishWorkSummary) => void) | null; - match3dItems?: Match3DWorkSummary[]; - onOpenMatch3DDetail?: (item: Match3DWorkSummary) => void; - onDeleteMatch3D?: ((item: Match3DWorkSummary) => void) | null; - squareHoleItems?: SquareHoleWorkSummary[]; - onOpenSquareHoleDetail?: (item: SquareHoleWorkSummary) => void; - onDeleteSquareHole?: ((item: SquareHoleWorkSummary) => void) | null; - jumpHopItems?: JumpHopWorkSummaryResponse[]; - onOpenJumpHopDetail?: (item: JumpHopWorkSummaryResponse) => void; - onDeleteJumpHop?: ((item: JumpHopWorkSummaryResponse) => void) | null; - woodenFishItems?: WoodenFishWorkSummaryResponse[]; - onOpenWoodenFishDetail?: - | ((item: WoodenFishWorkSummaryResponse) => void) - | null; - onDeleteWoodenFish?: ((item: WoodenFishWorkSummaryResponse) => void) | null; - puzzleItems?: PuzzleWorkSummary[]; - onOpenPuzzleDetail?: (item: PuzzleWorkSummary) => void; - onDeletePuzzle?: ((item: PuzzleWorkSummary) => void) | null; - onClaimPuzzlePointIncentive?: ((item: PuzzleWorkSummary) => void) | null; claimingPuzzleProfileId?: string | null; - babyObjectMatchItems?: BabyObjectMatchDraft[]; - onOpenBabyObjectMatchDetail?: ((item: BabyObjectMatchDraft) => void) | null; - onDeleteBabyObjectMatch?: ((item: BabyObjectMatchDraft) => void) | null; - barkBattleItems?: BarkBattleWorkSummary[]; - onOpenBarkBattleDetail?: ((item: BarkBattleWorkSummary) => void) | null; - onDeleteBarkBattle?: ((item: BarkBattleWorkSummary) => void) | null; - visualNovelItems?: VisualNovelWorkSummary[]; - onOpenVisualNovelDetail?: ((item: VisualNovelWorkSummary) => void) | null; - onDeleteVisualNovel?: ((item: VisualNovelWorkSummary) => void) | null; - getWorkState?: ( - item: CreationWorkShelfItem, - ) => CreationWorkShelfRuntimeState | null; onOpenShelfItem?: (item: CreationWorkShelfItem) => void; // 中文注释:底部加号入口可传入后端作品架摘要,用于推导最近使用过的模板。 recentWorkItems?: CreationWorkShelfItem[]; @@ -165,7 +113,7 @@ function writeWorkMetricSnapshot(items: CreationWorkShelfItem[]) { /** 渲染底部加号创作入口页与草稿作品架,最近创作复用最近使用过的模板入口。 */ export function CustomWorldCreationHub({ - items, + shelfItems, loading, error, onRetry, @@ -173,138 +121,14 @@ export function CustomWorldCreationHub({ entryConfig, creationTypes, onCreateType, - onOpenDraft, - onEnterPublished, - onDeletePublished = null, deletingWorkId = null, - rpgLibraryEntries = [], - bigFishItems = [], - onOpenBigFishDetail, - onDeleteBigFish = null, - match3dItems = [], - onOpenMatch3DDetail, - onDeleteMatch3D = null, - squareHoleItems = [], - onOpenSquareHoleDetail, - onDeleteSquareHole = null, - jumpHopItems = [], - onOpenJumpHopDetail, - onDeleteJumpHop = null, - woodenFishItems = [], - onOpenWoodenFishDetail = null, - onDeleteWoodenFish = null, - puzzleItems = [], - onOpenPuzzleDetail, - onDeletePuzzle = null, - onClaimPuzzlePointIncentive = null, claimingPuzzleProfileId = null, - babyObjectMatchItems = [], - onOpenBabyObjectMatchDetail = null, - onDeleteBabyObjectMatch = null, - barkBattleItems = [], - onOpenBarkBattleDetail = null, - onDeleteBarkBattle = null, - visualNovelItems = [], - onOpenVisualNovelDetail = null, - onDeleteVisualNovel = null, - getWorkState, onOpenShelfItem, recentWorkItems: recentWorkSourceItems, mode = 'full', }: CustomWorldCreationHubProps) { const [activeFilter, setActiveFilter] = useState('all'); - const isSquareHoleCreationVisible = isPlatformCreationTypeVisible( - creationTypes, - 'square-hole', - ); - const shelfItems = useMemo( - () => - buildCreationWorkShelfItems({ - rpgItems: items, - rpgLibraryEntries, - bigFishItems, - match3dItems, - squareHoleItems: isSquareHoleCreationVisible ? squareHoleItems : [], - jumpHopItems, - woodenFishItems, - puzzleItems, - babyObjectMatchItems, - barkBattleItems, - visualNovelItems, - canDeleteRpg: Boolean(onDeletePublished), - canDeleteBigFish: Boolean(onDeleteBigFish), - canDeleteMatch3D: Boolean(onDeleteMatch3D), - canDeleteSquareHole: - isSquareHoleCreationVisible && Boolean(onDeleteSquareHole), - canDeleteJumpHop: Boolean(onDeleteJumpHop), - canDeleteWoodenFish: Boolean(onDeleteWoodenFish), - canDeletePuzzle: Boolean(onDeletePuzzle), - canDeleteBabyObjectMatch: Boolean(onDeleteBabyObjectMatch), - canDeleteBarkBattle: Boolean(onDeleteBarkBattle), - canDeleteVisualNovel: Boolean(onDeleteVisualNovel), - onOpenRpgDraft: onOpenDraft, - onEnterRpgPublished: onEnterPublished, - onDeleteRpg: onDeletePublished ?? undefined, - onOpenBigFishDetail, - onDeleteBigFish: onDeleteBigFish ?? undefined, - onOpenMatch3DDetail, - onDeleteMatch3D: onDeleteMatch3D ?? undefined, - onOpenSquareHoleDetail, - onDeleteSquareHole: onDeleteSquareHole ?? undefined, - onOpenJumpHopDetail: onOpenJumpHopDetail ?? undefined, - onDeleteJumpHop: onDeleteJumpHop ?? undefined, - onOpenWoodenFishDetail: onOpenWoodenFishDetail ?? undefined, - onDeleteWoodenFish: onDeleteWoodenFish ?? undefined, - onOpenPuzzleDetail, - onDeletePuzzle: onDeletePuzzle ?? undefined, - onClaimPuzzlePointIncentive: onClaimPuzzlePointIncentive ?? undefined, - onOpenBabyObjectMatchDetail: onOpenBabyObjectMatchDetail ?? undefined, - onDeleteBabyObjectMatch: onDeleteBabyObjectMatch ?? undefined, - onOpenBarkBattleDetail: onOpenBarkBattleDetail ?? undefined, - onDeleteBarkBattle: onDeleteBarkBattle ?? undefined, - onOpenVisualNovelDetail: onOpenVisualNovelDetail ?? undefined, - onDeleteVisualNovel: onDeleteVisualNovel ?? undefined, - getItemState: getWorkState, - }), - [ - bigFishItems, - isSquareHoleCreationVisible, - babyObjectMatchItems, - barkBattleItems, - items, - match3dItems, - squareHoleItems, - onDeleteBigFish, - onDeleteMatch3D, - onDeleteSquareHole, - onDeletePublished, - onDeletePuzzle, - onDeleteBabyObjectMatch, - onDeleteBarkBattle, - onDeleteVisualNovel, - onDeleteJumpHop, - onDeleteWoodenFish, - onClaimPuzzlePointIncentive, - onOpenBigFishDetail, - onOpenDraft, - onOpenMatch3DDetail, - onOpenBabyObjectMatchDetail, - onOpenBarkBattleDetail, - onOpenPuzzleDetail, - onOpenSquareHoleDetail, - onOpenVisualNovelDetail, - onOpenWoodenFishDetail, - onEnterPublished, - getWorkState, - puzzleItems, - rpgLibraryEntries, - onOpenJumpHopDetail, - jumpHopItems, - woodenFishItems, - visualNovelItems, - ], - ); const [metricSnapshot] = useState(() => readWorkMetricSnapshot(), ); diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 6a27ebf7..7d15d87d 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -14733,6 +14733,161 @@ export function PlatformEntryFlowShellImpl({ selectionStage, ]); + const creationHubShelfItems = useMemo( + () => + buildCreationWorkShelfItems({ + rpgItems: creationHubItems, + rpgLibraryEntries: platformBootstrap.savedCustomWorldEntries, + bigFishItems: isBigFishCreationVisible ? bigFishShelfItems : [], + jumpHopItems: isJumpHopCreationVisible ? jumpHopShelfItems : [], + woodenFishItems: woodenFishShelfItems, + match3dItems: match3dShelfItems, + squareHoleItems: isSquareHoleCreationVisible ? squareHoleShelfItems : [], + puzzleItems: puzzleShelfItems, + babyObjectMatchItems: isBabyObjectMatchVisible + ? babyObjectMatchDrafts + : [], + barkBattleItems: barkBattleShelfItems, + visualNovelItems: visualNovelShelfItems, + canDeleteRpg: true, + canDeleteBigFish: isBigFishCreationVisible, + canDeleteMatch3D: true, + canDeleteSquareHole: isSquareHoleCreationVisible, + canDeletePuzzle: true, + canDeleteBabyObjectMatch: isBabyObjectMatchVisible, + canDeleteVisualNovel: true, + onOpenRpgDraft: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void detailNavigation.handleOpenCreationWork(item); + }); + }, + onEnterRpgPublished: (profileId) => { + runProtectedAction(() => { + const matchedWork = creationHubItems.find( + (entry) => entry.profileId === profileId, + ); + if (!matchedWork) { + return; + } + markCreationFlowReturnToDraftShelf(); + void detailNavigation.handleOpenCreationWork(matchedWork); + }); + }, + onDeleteRpg: handleDeletePublishedWork, + onOpenBigFishDetail: isBigFishCreationVisible + ? (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openBigFishDraft(item); + }); + } + : undefined, + onDeleteBigFish: isBigFishCreationVisible + ? handleDeleteBigFishWork + : undefined, + onOpenJumpHopDetail: isJumpHopCreationVisible + ? (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openJumpHopDraft(item); + }); + } + : undefined, + onOpenWoodenFishDetail: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openWoodenFishDraft(item); + }); + }, + onOpenMatch3DDetail: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openMatch3DDraft(item); + }); + }, + onDeleteMatch3D: handleDeleteMatch3DWork, + onOpenSquareHoleDetail: isSquareHoleCreationVisible + ? (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openSquareHoleDraft(item); + }); + } + : undefined, + onDeleteSquareHole: isSquareHoleCreationVisible + ? handleDeleteSquareHoleWork + : undefined, + onOpenPuzzleDetail: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openPuzzleDraft(item); + }); + }, + onDeletePuzzle: handleDeletePuzzleWork, + onClaimPuzzlePointIncentive: handleClaimPuzzlePointIncentive, + onOpenBabyObjectMatchDetail: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + openBabyObjectMatchDraft(item); + }); + }, + onDeleteBabyObjectMatch: handleDeleteBabyObjectMatchWork, + onOpenBarkBattleDetail: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + openBarkBattleDraft(item); + }); + }, + onOpenVisualNovelDetail: (item) => { + runProtectedAction(() => { + markCreationFlowReturnToDraftShelf(); + void openVisualNovelDraft(item); + }); + }, + onDeleteVisualNovel: handleDeleteVisualNovelWork, + getItemState: getCreationWorkShelfState, + }), + [ + barkBattleShelfItems, + babyObjectMatchDrafts, + bigFishShelfItems, + creationHubItems, + detailNavigation, + getCreationWorkShelfState, + handleClaimPuzzlePointIncentive, + handleDeleteBabyObjectMatchWork, + handleDeleteBigFishWork, + handleDeleteMatch3DWork, + handleDeletePublishedWork, + handleDeletePuzzleWork, + handleDeleteSquareHoleWork, + handleDeleteVisualNovelWork, + isBabyObjectMatchVisible, + isBigFishCreationVisible, + isJumpHopCreationVisible, + isSquareHoleCreationVisible, + jumpHopShelfItems, + markCreationFlowReturnToDraftShelf, + match3dShelfItems, + openBabyObjectMatchDraft, + openBarkBattleDraft, + openBigFishDraft, + openJumpHopDraft, + openMatch3DDraft, + openPuzzleDraft, + openSquareHoleDraft, + openVisualNovelDraft, + openWoodenFishDraft, + platformBootstrap.savedCustomWorldEntries, + puzzleShelfItems, + runProtectedAction, + squareHoleShelfItems, + visualNovelShelfItems, + woodenFishShelfItems, + ], + ); + // 中文注释:最近创作必须由真实作品架/后端草稿摘要决定,不能混入本地生成中占位。 const backendRecentCreationShelfItems = useMemo( () => @@ -14781,7 +14936,7 @@ export function PlatformEntryFlowShellImpl({ {creationEntryConfig ? ( { markDraftNoticeSeen(getGenerationNoticeShelfKeys(item)); }} - onOpenDraft={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void detailNavigation.handleOpenCreationWork(item); - }); - }} - onEnterPublished={(profileId) => { - runProtectedAction(() => { - const matchedWork = creationHubItems.find( - (entry) => entry.profileId === profileId, - ); - if (!matchedWork) { - return; - } - markCreationFlowReturnToDraftShelf(); - void detailNavigation.handleOpenCreationWork(matchedWork); - }); - }} - onDeletePublished={(item) => { - handleDeletePublishedWork(item); - }} deletingWorkId={deletingCreationWorkId} - rpgLibraryEntries={platformBootstrap.savedCustomWorldEntries} - bigFishItems={isBigFishCreationVisible ? bigFishShelfItems : []} - jumpHopItems={isJumpHopCreationVisible ? jumpHopShelfItems : []} - woodenFishItems={woodenFishShelfItems} - onOpenBigFishDetail={ - isBigFishCreationVisible - ? (item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openBigFishDraft(item); - }); - } - : undefined - } - onOpenJumpHopDetail={ - isJumpHopCreationVisible - ? (item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openJumpHopDraft(item); - }); - } - : undefined - } - onDeleteBigFish={ - isBigFishCreationVisible - ? (item) => { - handleDeleteBigFishWork(item); - } - : null - } - onDeleteJumpHop={null} - onOpenWoodenFishDetail={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openWoodenFishDraft(item); - }); - }} - onDeleteWoodenFish={null} - match3dItems={match3dShelfItems} - onOpenMatch3DDetail={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openMatch3DDraft(item); - }); - }} - onDeleteMatch3D={(item) => { - handleDeleteMatch3DWork(item); - }} - squareHoleItems={ - isSquareHoleCreationVisible ? squareHoleShelfItems : [] - } - onOpenSquareHoleDetail={ - isSquareHoleCreationVisible - ? (item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openSquareHoleDraft(item); - }); - } - : undefined - } - onDeleteSquareHole={ - isSquareHoleCreationVisible - ? (item) => { - handleDeleteSquareHoleWork(item); - } - : null - } - puzzleItems={puzzleShelfItems} - onOpenPuzzleDetail={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openPuzzleDraft(item); - }); - }} - onDeletePuzzle={(item) => { - handleDeletePuzzleWork(item); - }} - onClaimPuzzlePointIncentive={(item) => { - handleClaimPuzzlePointIncentive(item); - }} claimingPuzzleProfileId={claimingPuzzlePointIncentiveProfileId} - babyObjectMatchItems={ - isBabyObjectMatchVisible ? babyObjectMatchDrafts : [] - } - onOpenBabyObjectMatchDetail={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - openBabyObjectMatchDraft(item); - }); - }} - onDeleteBabyObjectMatch={(item) => { - handleDeleteBabyObjectMatchWork(item); - }} - barkBattleItems={barkBattleShelfItems} - onOpenBarkBattleDetail={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - openBarkBattleDraft(item); - }); - }} - visualNovelItems={visualNovelShelfItems} - onOpenVisualNovelDetail={(item) => { - runProtectedAction(() => { - markCreationFlowReturnToDraftShelf(); - void openVisualNovelDraft(item); - }); - }} - onDeleteVisualNovel={(item) => { - handleDeleteVisualNovelWork(item); - }} /> ) : null}