refactor: 收口创作作品架Hub接口
This commit is contained in:
@@ -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 与拼图稳定身份互推,导致刷新恢复规则缺少稳定测试面。
|
||||
|
||||
@@ -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)。
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = () => {};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<typeof buildCreationWorkShelfItems>[0];
|
||||
type HubViewProps = Parameters<typeof CustomWorldCreationHubView>[0];
|
||||
|
||||
type LegacyCustomWorldCreationHubProps = Omit<HubViewProps, 'shelfItems'> &
|
||||
Partial<
|
||||
Omit<ShelfBuilderParams, 'rpgItems' | 'bigFishItems' | 'puzzleItems'>
|
||||
> & {
|
||||
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 (
|
||||
<CustomWorldCreationHubView
|
||||
{...props}
|
||||
creationTypes={creationTypes}
|
||||
shelfItems={resolvedShelfItems}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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<CustomWorldProfile>[];
|
||||
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<CustomWorldWorkFilter>('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<WorkMetricSnapshot>(() =>
|
||||
readWorkMetricSnapshot(),
|
||||
);
|
||||
|
||||
@@ -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 ? (
|
||||
<CustomWorldCreationHub
|
||||
mode={mode}
|
||||
items={creationHubItems}
|
||||
shelfItems={creationHubShelfItems}
|
||||
loading={
|
||||
platformBootstrap.isLoadingPlatform ||
|
||||
isBigFishLoadingLibrary ||
|
||||
@@ -14871,144 +15026,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
creationTypes={creationEntryTypes}
|
||||
recentWorkItems={backendRecentCreationShelfItems}
|
||||
onCreateType={handleCreationHubCreateType}
|
||||
getWorkState={getCreationWorkShelfState}
|
||||
onOpenShelfItem={(item) => {
|
||||
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}
|
||||
</Suspense>
|
||||
|
||||
Reference in New Issue
Block a user