refactor: 收口作品架删除确认模型
This commit is contained in:
@@ -16,6 +16,14 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-06-04 Creation Work Delete Flow 收口
|
||||||
|
|
||||||
|
- 背景:平台入口作品架删除入口在 RPG、拼图、抓大鹅、方洞挑战、大鱼吃小鱼、视觉小说和宝贝识物 handler 内重复计算确认标题、删除说明、草稿 notice key 与拼图派生稳定 ID,导致删除确认规则散在巨型壳层。
|
||||||
|
- 决策:新增 `src/components/platform-entry/platformCreationWorkDeleteFlow.ts`,以 `resolvePlatformCreationWorkDeleteConfirmationModel(input)` 收口作品架删除确认纯模型;输出 `id/title/detail/noticeKeys`。`PlatformEntryFlowShellImpl.tsx` 仍作为副作用 Adapter,保留删除 API、刷新作品架 / 公开广场、错误状态、`markDraftNoticeSeen` 和页面跳转。
|
||||||
|
- 影响范围:创作中心作品架删除确认弹窗、删除后生成 notice 清理、拼图稳定 result ID 清理、宝贝识物已发布删除说明,以及后续新增玩法作品架删除接入。
|
||||||
|
- 验证方式:`npm run test -- src/components/platform-entry/platformCreationWorkDeleteFlow.test.ts`、`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对新 Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
|
- 关联文档:`docs/technical/【前端架构】CreationWorkDeleteFlow收口计划-2026-06-04.md`。
|
||||||
|
|
||||||
## 2026-06-03 平台入口公开作品详情 Strategy 收口
|
## 2026-06-03 平台入口公开作品详情 Strategy 收口
|
||||||
|
|
||||||
- 背景:平台壳层直接判断公开作品详情入口的玩法类型、是否需要补读完整详情,以及自有作品按钮显示“编辑”还是“改造”,导致统一作品详情的纯决策散落在巨型 Implementation 内。
|
- 背景:平台壳层直接判断公开作品详情入口的玩法类型、是否需要补读完整详情,以及自有作品按钮显示“编辑”还是“改造”,导致统一作品详情的纯决策散落在巨型 Implementation 内。
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
|
|||||||
|
|
||||||
创作中心作品架打开动作由 `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)。
|
||||||
|
|
||||||
|
作品架删除确认的标题、删除说明、草稿 notice key 和拼图派生稳定 ID 收口到 `src/components/platform-entry/platformCreationWorkDeleteFlow.ts`,平台壳只保留删除 API、刷新、错误和页面跳转副作用,规则见 [【前端架构】CreationWorkDeleteFlow收口计划-2026-06-04.md](./technical/【前端架构】CreationWorkDeleteFlow收口计划-2026-06-04.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)。
|
平台入口创作生成通知、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)。
|
||||||
|
|
||||||
平台入口创作恢复 URL 私有 query、拼图 runtime query 与拼图稳定身份互推收口到 `src/components/platform-entry/platformCreationUrlStateModel.ts` 和 `src/components/platform-entry/platformPuzzleIdentityModel.ts`,规则见 [【前端架构】CreationUrlStateModel收口计划-2026-06-03.md](./technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md)。
|
平台入口创作恢复 URL 私有 query、拼图 runtime query 与拼图稳定身份互推收口到 `src/components/platform-entry/platformCreationUrlStateModel.ts` 和 `src/components/platform-entry/platformPuzzleIdentityModel.ts`,规则见 [【前端架构】CreationUrlStateModel收口计划-2026-06-03.md](./technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md)。
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 【前端架构】Creation Work Delete Flow 收口计划
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
平台入口作品架的删除入口覆盖 RPG、拼图、抓大鹅、方洞挑战、大鱼吃小鱼、视觉小说和宝贝识物。此前 `PlatformEntryFlowShellImpl.tsx` 在每个删除 handler 内重复计算确认框标题、删除说明、草稿 notice key 和拼图派生稳定 ID。壳层既要理解每种玩法的作品身份,又要承接异步删除、刷新列表、错误状态和页面跳转,导致删除确认规则缺少稳定测试面。
|
||||||
|
|
||||||
|
该 **Interface** 过浅:页面只想展示“删除哪个作品、会从哪里移除、删除成功后清哪些生成 notice”,却必须知道 `workId` / `profileId` / `sourceSessionId` / `draftId`、`status` / `publicationStatus` / `publishStatus` 和宝贝识物特殊公开去向。
|
||||||
|
|
||||||
|
## 决策
|
||||||
|
|
||||||
|
新增 `src/components/platform-entry/platformCreationWorkDeleteFlow.ts` 作为 Creation Work Delete Flow **Module**。其唯一公开 **Interface** 是 `resolvePlatformCreationWorkDeleteConfirmationModel(input)`,输入为带 `kind` 的 union,输出:
|
||||||
|
|
||||||
|
- `id`:确认框和删除 busy 使用的稳定作品 ID。
|
||||||
|
- `title`:确认框标题,含拼图、视觉小说和宝贝识物标题兜底。
|
||||||
|
- `detail`:草稿 / 已发布删除说明,宝贝识物已发布使用“寓教于乐板块”文案。
|
||||||
|
- `noticeKeys`:删除成功后应标记已读的草稿生成 notice keys,拼图包含 `buildPuzzleResultWorkId` / `buildPuzzleResultProfileId` 派生 key。
|
||||||
|
|
||||||
|
`PlatformEntryFlowShellImpl.tsx` 仍作为副作用 **Adapter**:负责鉴权保护、确认框 state、调用各玩法删除 API、清错误、刷新作品架 / 公开广场、`markDraftNoticeSeen` 和必要的页面跳转。`run` 不进入纯 **Module**,避免把网络副作用和 React state 写入藏入模型层。
|
||||||
|
|
||||||
|
## 约定
|
||||||
|
|
||||||
|
- 新玩法接入作品架删除时,先补齐后端删除链路、作品架 action 和本 **Module** 的确认模型,再开放删除按钮。
|
||||||
|
- Jump Hop、Wooden Fish 和 Bark Battle 当前仅有作品架 action 预留,平台壳不传删除 handler;不得因本 Module 存在而默认开放删除。
|
||||||
|
- 删除确认文案不得散回平台壳;若公开去向不是公开广场,应在本 **Module** 明确分支。
|
||||||
|
- 草稿 notice key 的身份扩展必须复用 `collectDraftNoticeKeys`,保持 trim、去空和去重语义一致。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- `npm run test -- src/components/platform-entry/platformCreationWorkDeleteFlow.test.ts`
|
||||||
|
- `npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`
|
||||||
|
- `npx eslint src/components/platform-entry/platformCreationWorkDeleteFlow.ts src/components/platform-entry/platformCreationWorkDeleteFlow.test.ts src/components/platform-entry/PlatformEntryFlowShellImpl.tsx --quiet`
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run check:encoding`
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
9. 私有 generated 图片必须通过 `ResolvedAssetImage` / `/api/assets/read-url` 换签读取。
|
9. 私有 generated 图片必须通过 `ResolvedAssetImage` / `/api/assets/read-url` 换签读取。
|
||||||
10. 敲木鱼作品架读取当前用户作品列表时走 `GET /api/creation/wooden-fish/works`;发布成功后平台壳必须同时刷新作品架与公开广场,避免作品刚发布时仍停留在旧列表。
|
10. 敲木鱼作品架读取当前用户作品列表时走 `GET /api/creation/wooden-fish/works`;发布成功后平台壳必须同时刷新作品架与公开广场,避免作品刚发布时仍停留在旧列表。
|
||||||
11. 移动端草稿页整体禁止长按选择文字,避免误触系统选区;输入框、文本域和可编辑区域仍必须保留文本选择能力。
|
11. 移动端草稿页整体禁止长按选择文字,避免误触系统选区;输入框、文本域和可编辑区域仍必须保留文本选择能力。
|
||||||
|
12. 作品架删除确认的纯规则统一由 `platformCreationWorkDeleteFlow.ts` 解析,输出确认框 `id/title/detail` 与删除成功后清理的草稿 notice keys;平台壳只接回该模型执行删除 API、刷新列表、清错误和跳转。Jump Hop、Wooden Fish、Bark Battle 虽在作品架 action 层有预留删除入口,但未补齐删除 API 前不得传入删除 handler 或开放按钮。
|
||||||
|
|
||||||
发现页 / 推荐页公开作品卡的作者行只显示可读公开昵称;不得把手机号掩码、`SY-*` 陶泥号或作品号拼接进卡片作者名。陶泥号搜索、作品号复制和完整作品身份只在搜索、详情页或明确的复制入口展示,避免卡片列表暴露账号标识。
|
发现页 / 推荐页公开作品卡的作者行只显示可读公开昵称;不得把手机号掩码、`SY-*` 陶泥号或作品号拼接进卡片作者名。陶泥号搜索、作品号复制和完整作品身份只在搜索、详情页或明确的复制入口展示,避免卡片列表暴露账号标识。
|
||||||
|
|
||||||
|
|||||||
@@ -412,6 +412,7 @@ import {
|
|||||||
hasPuzzleRuntimeUrlStateValue,
|
hasPuzzleRuntimeUrlStateValue,
|
||||||
normalizeCreationUrlValue,
|
normalizeCreationUrlValue,
|
||||||
} from './platformCreationUrlStateModel';
|
} from './platformCreationUrlStateModel';
|
||||||
|
import { resolvePlatformCreationWorkDeleteConfirmationModel } from './platformCreationWorkDeleteFlow';
|
||||||
import {
|
import {
|
||||||
buildPlatformErrorDialogDismissKey,
|
buildPlatformErrorDialogDismissKey,
|
||||||
buildPlatformTaskCompletionDialogDismissKey,
|
buildPlatformTaskCompletionDialogDismissKey,
|
||||||
@@ -10212,12 +10213,16 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'rpg-library',
|
||||||
|
entry,
|
||||||
|
});
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: entry.profileId,
|
id: deleteModel.id,
|
||||||
title: entry.worldName,
|
title: deleteModel.title,
|
||||||
detail: '删除后会从你的作品列表和公开广场中移除。',
|
detail: deleteModel.detail,
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(entry.profileId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
platformBootstrap.setPlatformError(null);
|
platformBootstrap.setPlatformError(null);
|
||||||
|
|
||||||
void deleteRpgEntryWorldProfile(entry.profileId)
|
void deleteRpgEntryWorldProfile(entry.profileId)
|
||||||
@@ -10245,21 +10250,17 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('rpg', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.workId,
|
kind: 'rpg',
|
||||||
work.sessionId,
|
work,
|
||||||
work.profileId,
|
});
|
||||||
]);
|
|
||||||
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.workId,
|
id: deleteModel.id,
|
||||||
title: work.title,
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.status === 'published'
|
|
||||||
? '删除后会从你的作品列表和公开广场中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.workId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
platformBootstrap.setPlatformError(null);
|
platformBootstrap.setPlatformError(null);
|
||||||
|
|
||||||
const deleteTask =
|
const deleteTask =
|
||||||
@@ -10282,7 +10283,7 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
|
|
||||||
void deleteTask
|
void deleteTask
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
await platformBootstrap.refreshPublishedGallery().catch(() => []);
|
await platformBootstrap.refreshPublishedGallery().catch(() => []);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -10309,25 +10310,22 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('big-fish', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.workId,
|
kind: 'big-fish',
|
||||||
work.sourceSessionId,
|
work,
|
||||||
]);
|
});
|
||||||
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.workId,
|
id: deleteModel.id,
|
||||||
title: work.title,
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.status === 'published'
|
|
||||||
? '删除后会从你的作品列表和公开广场中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.workId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
setBigFishError(null);
|
setBigFishError(null);
|
||||||
|
|
||||||
void deleteBigFishWork(work.sourceSessionId)
|
void deleteBigFishWork(work.sourceSessionId)
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
setBigFishWorks(response.items);
|
setBigFishWorks(response.items);
|
||||||
await refreshBigFishGallery().catch(() => []);
|
await refreshBigFishGallery().catch(() => []);
|
||||||
})
|
})
|
||||||
@@ -10357,31 +10355,22 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('puzzle', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.workId,
|
kind: 'puzzle',
|
||||||
work.profileId,
|
work,
|
||||||
work.sourceSessionId,
|
});
|
||||||
buildPuzzleResultWorkId(work.sourceSessionId),
|
|
||||||
buildPuzzleResultProfileId(work.sourceSessionId),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const displayName =
|
|
||||||
work.workTitle?.trim() || work.levelName.trim() || '未命名拼图';
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.workId,
|
id: deleteModel.id,
|
||||||
title: displayName,
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.publicationStatus === 'published'
|
|
||||||
? '删除后会从你的作品列表和公开广场中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.workId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
setPuzzleFormDraftPayload(null);
|
setPuzzleFormDraftPayload(null);
|
||||||
setPuzzleError(null);
|
setPuzzleError(null);
|
||||||
|
|
||||||
void deletePuzzleWork(work.profileId)
|
void deletePuzzleWork(work.profileId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
setPuzzleWorks(response.items);
|
setPuzzleWorks(response.items);
|
||||||
void refreshPuzzleGallery();
|
void refreshPuzzleGallery();
|
||||||
})
|
})
|
||||||
@@ -10411,27 +10400,23 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('match3d', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.workId,
|
kind: 'match3d',
|
||||||
work.profileId,
|
work,
|
||||||
work.sourceSessionId,
|
});
|
||||||
]);
|
|
||||||
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.workId,
|
id: deleteModel.id,
|
||||||
title: work.gameName,
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.publicationStatus === 'published'
|
|
||||||
? '删除后会从你的作品列表和公开广场中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.workId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
setMatch3DFormDraftPayload(null);
|
setMatch3DFormDraftPayload(null);
|
||||||
setMatch3DError(null);
|
setMatch3DError(null);
|
||||||
|
|
||||||
void deleteMatch3DWork(work.profileId)
|
void deleteMatch3DWork(work.profileId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
setMatch3DWorks(mapMatch3DWorksForRuntimeUi(response.items));
|
setMatch3DWorks(mapMatch3DWorksForRuntimeUi(response.items));
|
||||||
void refreshMatch3DGallery();
|
void refreshMatch3DGallery();
|
||||||
})
|
})
|
||||||
@@ -10462,26 +10447,22 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('square-hole', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.workId,
|
kind: 'square-hole',
|
||||||
work.profileId,
|
work,
|
||||||
work.sourceSessionId,
|
});
|
||||||
]);
|
|
||||||
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.workId,
|
id: deleteModel.id,
|
||||||
title: work.gameName,
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.publicationStatus === 'published'
|
|
||||||
? '删除后会从你的作品列表和公开广场中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.workId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
setSquareHoleError(null);
|
setSquareHoleError(null);
|
||||||
|
|
||||||
void deleteSquareHoleWork(work.profileId)
|
void deleteSquareHoleWork(work.profileId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
setSquareHoleWorks(response.items);
|
setSquareHoleWorks(response.items);
|
||||||
void refreshSquareHoleGallery();
|
void refreshSquareHoleGallery();
|
||||||
})
|
})
|
||||||
@@ -10511,24 +10492,22 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('visual-novel', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.profileId,
|
kind: 'visual-novel',
|
||||||
]);
|
work,
|
||||||
|
});
|
||||||
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.profileId,
|
id: deleteModel.id,
|
||||||
title: work.title || '未命名视觉小说',
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.publishStatus === 'published'
|
|
||||||
? '删除后会从你的作品列表和公开广场中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.profileId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
setVisualNovelError(null);
|
setVisualNovelError(null);
|
||||||
|
|
||||||
void deleteVisualNovelWork(work.profileId)
|
void deleteVisualNovelWork(work.profileId)
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
setVisualNovelWorks(response.works);
|
setVisualNovelWorks(response.works);
|
||||||
await refreshVisualNovelGallery();
|
await refreshVisualNovelGallery();
|
||||||
})
|
})
|
||||||
@@ -10558,26 +10537,22 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
if (deletingCreationWorkId) {
|
if (deletingCreationWorkId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const noticeKeys = collectDraftNoticeKeys('baby-object-match', [
|
const deleteModel = resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
work.profileId,
|
kind: 'baby-object-match',
|
||||||
work.draftId,
|
work,
|
||||||
]);
|
});
|
||||||
const displayName = work.workTitle.trim() || work.templateName;
|
|
||||||
|
|
||||||
requestDeleteCreationWork({
|
requestDeleteCreationWork({
|
||||||
id: work.profileId,
|
id: deleteModel.id,
|
||||||
title: displayName,
|
title: deleteModel.title,
|
||||||
detail:
|
detail: deleteModel.detail,
|
||||||
work.publicationStatus === 'published'
|
|
||||||
? '删除后会从你的作品列表和寓教于乐板块中移除。'
|
|
||||||
: '删除后会从你的作品列表中移除。',
|
|
||||||
run: () => {
|
run: () => {
|
||||||
setDeletingCreationWorkId(work.profileId);
|
setDeletingCreationWorkId(deleteModel.id);
|
||||||
setBabyObjectMatchError(null);
|
setBabyObjectMatchError(null);
|
||||||
|
|
||||||
void deleteLocalBabyObjectMatchDraft(work.profileId)
|
void deleteLocalBabyObjectMatchDraft(work.profileId)
|
||||||
.then((nextDrafts) => {
|
.then((nextDrafts) => {
|
||||||
markDraftNoticeSeen(noticeKeys);
|
markDraftNoticeSeen(deleteModel.noticeKeys);
|
||||||
setBabyObjectMatchDrafts(nextDrafts);
|
setBabyObjectMatchDrafts(nextDrafts);
|
||||||
setBabyObjectMatchDraft((current) =>
|
setBabyObjectMatchDraft((current) =>
|
||||||
current?.profileId === work.profileId ? null : current,
|
current?.profileId === work.profileId ? null : current,
|
||||||
|
|||||||
@@ -0,0 +1,334 @@
|
|||||||
|
import { describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
|
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||||
|
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldWorkSummary';
|
||||||
|
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||||
|
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||||
|
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||||
|
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
|
||||||
|
import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel';
|
||||||
|
import { resolvePlatformCreationWorkDeleteConfirmationModel } from './platformCreationWorkDeleteFlow';
|
||||||
|
|
||||||
|
describe('platformCreationWorkDeleteFlow', () => {
|
||||||
|
test('resolves RPG library delete confirmation without draft notice keys', () => {
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'rpg-library',
|
||||||
|
entry: {
|
||||||
|
profileId: 'rpg-profile',
|
||||||
|
worldName: '潮雾列岛',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: 'rpg-profile',
|
||||||
|
title: '潮雾列岛',
|
||||||
|
detail: '删除后会从你的作品列表和公开广场中移除。',
|
||||||
|
noticeKeys: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves RPG work delete detail and notice keys by work status', () => {
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'rpg',
|
||||||
|
work: buildRpgWork(),
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: 'rpg-work',
|
||||||
|
title: 'RPG 草稿',
|
||||||
|
detail: '删除后会从你的作品列表中移除。',
|
||||||
|
noticeKeys: ['rpg:rpg-work', 'rpg:rpg-session', 'rpg:rpg-profile'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'rpg',
|
||||||
|
work: buildRpgWork({ status: 'published' }),
|
||||||
|
}).detail,
|
||||||
|
).toBe('删除后会从你的作品列表和公开广场中移除。');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves mini game delete models with shared public and private detail copy', () => {
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'big-fish',
|
||||||
|
work: buildBigFishWork({ status: 'published' }),
|
||||||
|
}),
|
||||||
|
).toMatchObject({
|
||||||
|
id: 'big-fish-work',
|
||||||
|
title: '大鱼作品',
|
||||||
|
detail: '删除后会从你的作品列表和公开广场中移除。',
|
||||||
|
noticeKeys: ['big-fish:big-fish-work', 'big-fish:big-fish-session'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'match3d',
|
||||||
|
work: buildMatch3DWork(),
|
||||||
|
}).detail,
|
||||||
|
).toBe('删除后会从你的作品列表中移除。');
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'square-hole',
|
||||||
|
work: buildSquareHoleWork({ publicationStatus: 'published' }),
|
||||||
|
}).noticeKeys,
|
||||||
|
).toEqual([
|
||||||
|
'square-hole:square-hole-work',
|
||||||
|
'square-hole:square-hole-profile',
|
||||||
|
'square-hole:square-hole-session',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves puzzle title fallback and stable result notice keys', () => {
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'puzzle',
|
||||||
|
work: buildPuzzleWork({
|
||||||
|
workTitle: ' ',
|
||||||
|
levelName: ' 雾港第一关 ',
|
||||||
|
sourceSessionId: 'puzzle-session-ocean',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: 'puzzle-work',
|
||||||
|
title: '雾港第一关',
|
||||||
|
detail: '删除后会从你的作品列表中移除。',
|
||||||
|
noticeKeys: [
|
||||||
|
'puzzle:puzzle-work',
|
||||||
|
'puzzle:puzzle-profile',
|
||||||
|
'puzzle:puzzle-session-ocean',
|
||||||
|
'puzzle:puzzle-work-ocean',
|
||||||
|
'puzzle:puzzle-profile-ocean',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'puzzle',
|
||||||
|
work: buildPuzzleWork({ workTitle: '', levelName: ' ' }),
|
||||||
|
}).title,
|
||||||
|
).toBe('未命名拼图');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves visual novel and baby object match special delete copy', () => {
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'visual-novel',
|
||||||
|
work: buildVisualNovelWork({ title: '', publishStatus: 'published' }),
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: 'visual-novel-profile',
|
||||||
|
title: '未命名视觉小说',
|
||||||
|
detail: '删除后会从你的作品列表和公开广场中移除。',
|
||||||
|
noticeKeys: ['visual-novel:visual-novel-profile'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformCreationWorkDeleteConfirmationModel({
|
||||||
|
kind: 'baby-object-match',
|
||||||
|
work: buildBabyObjectMatchDraft({
|
||||||
|
workTitle: ' ',
|
||||||
|
publicationStatus: 'published',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: 'baby-profile',
|
||||||
|
title: '宝贝识物',
|
||||||
|
detail: '删除后会从你的作品列表和寓教于乐板块中移除。',
|
||||||
|
noticeKeys: [
|
||||||
|
'baby-object-match:baby-profile',
|
||||||
|
'baby-object-match:baby-draft',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildRpgWork(
|
||||||
|
overrides: Partial<CustomWorldWorkSummary> = {},
|
||||||
|
): CustomWorldWorkSummary {
|
||||||
|
return {
|
||||||
|
workId: 'rpg-work',
|
||||||
|
sourceType: 'agent_session',
|
||||||
|
status: 'draft',
|
||||||
|
title: 'RPG 草稿',
|
||||||
|
subtitle: '待完善',
|
||||||
|
summary: 'RPG 摘要。',
|
||||||
|
coverImageSrc: null,
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
stage: 'draft',
|
||||||
|
stageLabel: '草稿',
|
||||||
|
playableNpcCount: 1,
|
||||||
|
landmarkCount: 1,
|
||||||
|
sessionId: 'rpg-session',
|
||||||
|
profileId: 'rpg-profile',
|
||||||
|
canResume: true,
|
||||||
|
canEnterWorld: false,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBigFishWork(
|
||||||
|
overrides: Partial<BigFishWorkSummary> = {},
|
||||||
|
): BigFishWorkSummary {
|
||||||
|
return {
|
||||||
|
workId: 'big-fish-work',
|
||||||
|
sourceSessionId: 'big-fish-session',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
authorDisplayName: '玩家',
|
||||||
|
title: '大鱼作品',
|
||||||
|
subtitle: '大鱼吃小鱼',
|
||||||
|
summary: '大鱼摘要。',
|
||||||
|
coverImageSrc: null,
|
||||||
|
status: 'draft',
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
publishReady: false,
|
||||||
|
levelCount: 1,
|
||||||
|
levelMainImageReadyCount: 0,
|
||||||
|
levelMotionReadyCount: 0,
|
||||||
|
backgroundReady: true,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPuzzleWork(
|
||||||
|
overrides: Partial<PuzzleWorkSummary> = {},
|
||||||
|
): PuzzleWorkSummary {
|
||||||
|
return {
|
||||||
|
workId: 'puzzle-work',
|
||||||
|
profileId: 'puzzle-profile',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
sourceSessionId: 'puzzle-session',
|
||||||
|
authorDisplayName: '玩家',
|
||||||
|
workTitle: '拼图作品',
|
||||||
|
workDescription: '拼图摘要。',
|
||||||
|
levelName: '拼图第一关',
|
||||||
|
summary: '拼图摘要。',
|
||||||
|
themeTags: [],
|
||||||
|
coverImageSrc: null,
|
||||||
|
coverAssetId: null,
|
||||||
|
publicationStatus: 'draft',
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
playCount: 0,
|
||||||
|
remixCount: 0,
|
||||||
|
likeCount: 0,
|
||||||
|
publishReady: false,
|
||||||
|
levels: [],
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMatch3DWork(
|
||||||
|
overrides: Partial<Match3DWorkSummary> = {},
|
||||||
|
): Match3DWorkSummary {
|
||||||
|
return {
|
||||||
|
workId: 'match3d-work',
|
||||||
|
profileId: 'match3d-profile',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
sourceSessionId: 'match3d-session',
|
||||||
|
gameName: '抓大鹅作品',
|
||||||
|
themeText: '糖果厨房',
|
||||||
|
summary: '抓大鹅摘要。',
|
||||||
|
tags: [],
|
||||||
|
coverImageSrc: null,
|
||||||
|
referenceImageSrc: null,
|
||||||
|
clearCount: 12,
|
||||||
|
difficulty: 4,
|
||||||
|
publicationStatus: 'draft',
|
||||||
|
playCount: 0,
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
publishReady: false,
|
||||||
|
generatedItemAssets: [],
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSquareHoleWork(
|
||||||
|
overrides: Partial<SquareHoleWorkSummary> = {},
|
||||||
|
): SquareHoleWorkSummary {
|
||||||
|
return {
|
||||||
|
workId: 'square-hole-work',
|
||||||
|
profileId: 'square-hole-profile',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
sourceSessionId: 'square-hole-session',
|
||||||
|
gameName: '方洞作品',
|
||||||
|
themeText: '图形',
|
||||||
|
twistRule: '反直觉',
|
||||||
|
summary: '方洞摘要。',
|
||||||
|
tags: [],
|
||||||
|
coverImageSrc: null,
|
||||||
|
backgroundPrompt: '背景',
|
||||||
|
backgroundImageSrc: null,
|
||||||
|
shapeOptions: [],
|
||||||
|
holeOptions: [],
|
||||||
|
shapeCount: 8,
|
||||||
|
difficulty: 4,
|
||||||
|
publicationStatus: 'draft',
|
||||||
|
playCount: 0,
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
publishReady: false,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildVisualNovelWork(
|
||||||
|
overrides: Partial<VisualNovelWorkSummary> = {},
|
||||||
|
): VisualNovelWorkSummary {
|
||||||
|
return {
|
||||||
|
runtimeKind: 'visual-novel',
|
||||||
|
profileId: 'visual-novel-profile',
|
||||||
|
ownerUserId: 'user-1',
|
||||||
|
title: '视觉小说作品',
|
||||||
|
description: '视觉小说摘要。',
|
||||||
|
coverImageSrc: null,
|
||||||
|
tags: [],
|
||||||
|
publishStatus: 'draft',
|
||||||
|
publishReady: false,
|
||||||
|
playCount: 0,
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBabyObjectMatchDraft(
|
||||||
|
overrides: Partial<BabyObjectMatchDraft> = {},
|
||||||
|
): BabyObjectMatchDraft {
|
||||||
|
return {
|
||||||
|
draftId: 'baby-draft',
|
||||||
|
profileId: 'baby-profile',
|
||||||
|
templateId: 'baby-object-match',
|
||||||
|
templateName: '宝贝识物',
|
||||||
|
workTitle: '宝贝识物作品',
|
||||||
|
workDescription: '宝贝识物摘要。',
|
||||||
|
itemNames: ['苹果', '香蕉'],
|
||||||
|
itemAssets: [
|
||||||
|
{
|
||||||
|
itemId: 'apple',
|
||||||
|
itemName: '苹果',
|
||||||
|
imageSrc: '/apple.png',
|
||||||
|
assetObjectId: null,
|
||||||
|
generationProvider: 'placeholder',
|
||||||
|
prompt: '苹果',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemId: 'banana',
|
||||||
|
itemName: '香蕉',
|
||||||
|
imageSrc: '/banana.png',
|
||||||
|
assetObjectId: null,
|
||||||
|
generationProvider: 'placeholder',
|
||||||
|
prompt: '香蕉',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
themeTags: [],
|
||||||
|
publicationStatus: 'draft',
|
||||||
|
createdAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
updatedAt: '2026-06-04T00:00:00.000Z',
|
||||||
|
publishedAt: null,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
288
src/components/platform-entry/platformCreationWorkDeleteFlow.ts
Normal file
288
src/components/platform-entry/platformCreationWorkDeleteFlow.ts
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||||
|
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldWorkSummary';
|
||||||
|
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||||
|
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 {
|
||||||
|
buildPuzzleResultProfileId,
|
||||||
|
buildPuzzleResultWorkId,
|
||||||
|
collectDraftNoticeKeys,
|
||||||
|
} from './platformDraftGenerationShelfModel';
|
||||||
|
|
||||||
|
const PRIVATE_WORK_DELETE_DETAIL = '删除后会从你的作品列表中移除。';
|
||||||
|
const PUBLIC_GALLERY_DELETE_DETAIL = '删除后会从你的作品列表和公开广场中移除。';
|
||||||
|
const EDUTAINMENT_PUBLIC_DELETE_DETAIL =
|
||||||
|
'删除后会从你的作品列表和寓教于乐板块中移除。';
|
||||||
|
|
||||||
|
export type PlatformCreationWorkDeleteConfirmationModel = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
detail: string;
|
||||||
|
noticeKeys: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlatformCreationWorkDeleteInput =
|
||||||
|
| {
|
||||||
|
kind: 'rpg-library';
|
||||||
|
entry: Pick<CustomWorldLibraryEntry<unknown>, 'profileId' | 'worldName'>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'rpg';
|
||||||
|
work: Pick<
|
||||||
|
CustomWorldWorkSummary,
|
||||||
|
'workId' | 'title' | 'status' | 'sessionId' | 'profileId'
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'big-fish';
|
||||||
|
work: Pick<
|
||||||
|
BigFishWorkSummary,
|
||||||
|
'workId' | 'title' | 'status' | 'sourceSessionId'
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'puzzle';
|
||||||
|
work: Pick<
|
||||||
|
PuzzleWorkSummary,
|
||||||
|
| 'workId'
|
||||||
|
| 'profileId'
|
||||||
|
| 'sourceSessionId'
|
||||||
|
| 'workTitle'
|
||||||
|
| 'levelName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'match3d';
|
||||||
|
work: Pick<
|
||||||
|
Match3DWorkSummary,
|
||||||
|
| 'workId'
|
||||||
|
| 'profileId'
|
||||||
|
| 'sourceSessionId'
|
||||||
|
| 'gameName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'square-hole';
|
||||||
|
work: Pick<
|
||||||
|
SquareHoleWorkSummary,
|
||||||
|
| 'workId'
|
||||||
|
| 'profileId'
|
||||||
|
| 'sourceSessionId'
|
||||||
|
| 'gameName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'visual-novel';
|
||||||
|
work: Pick<
|
||||||
|
VisualNovelWorkSummary,
|
||||||
|
'profileId' | 'title' | 'publishStatus'
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: 'baby-object-match';
|
||||||
|
work: Pick<
|
||||||
|
BabyObjectMatchDraft,
|
||||||
|
| 'profileId'
|
||||||
|
| 'draftId'
|
||||||
|
| 'workTitle'
|
||||||
|
| 'templateName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function resolvePlatformCreationWorkDeleteConfirmationModel(
|
||||||
|
input: PlatformCreationWorkDeleteInput,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
switch (input.kind) {
|
||||||
|
case 'rpg-library':
|
||||||
|
return resolveRpgLibraryDeleteConfirmationModel(input.entry);
|
||||||
|
case 'rpg':
|
||||||
|
return resolveRpgWorkDeleteConfirmationModel(input.work);
|
||||||
|
case 'big-fish':
|
||||||
|
return resolveBigFishWorkDeleteConfirmationModel(input.work);
|
||||||
|
case 'puzzle':
|
||||||
|
return resolvePuzzleWorkDeleteConfirmationModel(input.work);
|
||||||
|
case 'match3d':
|
||||||
|
return resolveMatch3DWorkDeleteConfirmationModel(input.work);
|
||||||
|
case 'square-hole':
|
||||||
|
return resolveSquareHoleWorkDeleteConfirmationModel(input.work);
|
||||||
|
case 'visual-novel':
|
||||||
|
return resolveVisualNovelWorkDeleteConfirmationModel(input.work);
|
||||||
|
case 'baby-object-match':
|
||||||
|
return resolveBabyObjectMatchDeleteConfirmationModel(input.work);
|
||||||
|
default: {
|
||||||
|
const exhaustive: never = input;
|
||||||
|
return exhaustive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveStatusDeleteDetail(
|
||||||
|
status: string,
|
||||||
|
publishedDetail = PUBLIC_GALLERY_DELETE_DETAIL,
|
||||||
|
) {
|
||||||
|
return status === 'published' ? publishedDetail : PRIVATE_WORK_DELETE_DETAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTrimmedTitle(
|
||||||
|
value: string | null | undefined,
|
||||||
|
fallback: string,
|
||||||
|
) {
|
||||||
|
const trimmedValue = value?.trim();
|
||||||
|
return trimmedValue || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRpgLibraryDeleteConfirmationModel(
|
||||||
|
entry: Pick<CustomWorldLibraryEntry<unknown>, 'profileId' | 'worldName'>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: entry.profileId,
|
||||||
|
title: entry.worldName,
|
||||||
|
detail: PUBLIC_GALLERY_DELETE_DETAIL,
|
||||||
|
noticeKeys: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRpgWorkDeleteConfirmationModel(
|
||||||
|
work: Pick<
|
||||||
|
CustomWorldWorkSummary,
|
||||||
|
'workId' | 'title' | 'status' | 'sessionId' | 'profileId'
|
||||||
|
>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.workId,
|
||||||
|
title: work.title,
|
||||||
|
detail: resolveStatusDeleteDetail(work.status),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('rpg', [
|
||||||
|
work.workId,
|
||||||
|
work.sessionId,
|
||||||
|
work.profileId,
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveBigFishWorkDeleteConfirmationModel(
|
||||||
|
work: Pick<
|
||||||
|
BigFishWorkSummary,
|
||||||
|
'workId' | 'title' | 'status' | 'sourceSessionId'
|
||||||
|
>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.workId,
|
||||||
|
title: work.title,
|
||||||
|
detail: resolveStatusDeleteDetail(work.status),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('big-fish', [
|
||||||
|
work.workId,
|
||||||
|
work.sourceSessionId,
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePuzzleWorkDeleteConfirmationModel(
|
||||||
|
work: Pick<
|
||||||
|
PuzzleWorkSummary,
|
||||||
|
| 'workId'
|
||||||
|
| 'profileId'
|
||||||
|
| 'sourceSessionId'
|
||||||
|
| 'workTitle'
|
||||||
|
| 'levelName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.workId,
|
||||||
|
title: resolveTrimmedTitle(
|
||||||
|
work.workTitle,
|
||||||
|
resolveTrimmedTitle(work.levelName, '未命名拼图'),
|
||||||
|
),
|
||||||
|
detail: resolveStatusDeleteDetail(work.publicationStatus),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('puzzle', [
|
||||||
|
work.workId,
|
||||||
|
work.profileId,
|
||||||
|
work.sourceSessionId,
|
||||||
|
buildPuzzleResultWorkId(work.sourceSessionId),
|
||||||
|
buildPuzzleResultProfileId(work.sourceSessionId),
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveMatch3DWorkDeleteConfirmationModel(
|
||||||
|
work: Pick<
|
||||||
|
Match3DWorkSummary,
|
||||||
|
| 'workId'
|
||||||
|
| 'profileId'
|
||||||
|
| 'sourceSessionId'
|
||||||
|
| 'gameName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.workId,
|
||||||
|
title: work.gameName,
|
||||||
|
detail: resolveStatusDeleteDetail(work.publicationStatus),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('match3d', [
|
||||||
|
work.workId,
|
||||||
|
work.profileId,
|
||||||
|
work.sourceSessionId,
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSquareHoleWorkDeleteConfirmationModel(
|
||||||
|
work: Pick<
|
||||||
|
SquareHoleWorkSummary,
|
||||||
|
| 'workId'
|
||||||
|
| 'profileId'
|
||||||
|
| 'sourceSessionId'
|
||||||
|
| 'gameName'
|
||||||
|
| 'publicationStatus'
|
||||||
|
>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.workId,
|
||||||
|
title: work.gameName,
|
||||||
|
detail: resolveStatusDeleteDetail(work.publicationStatus),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('square-hole', [
|
||||||
|
work.workId,
|
||||||
|
work.profileId,
|
||||||
|
work.sourceSessionId,
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveVisualNovelWorkDeleteConfirmationModel(
|
||||||
|
work: Pick<VisualNovelWorkSummary, 'profileId' | 'title' | 'publishStatus'>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.profileId,
|
||||||
|
title: work.title || '未命名视觉小说',
|
||||||
|
detail: resolveStatusDeleteDetail(work.publishStatus),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('visual-novel', [work.profileId]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveBabyObjectMatchDeleteConfirmationModel(
|
||||||
|
work: Pick<
|
||||||
|
BabyObjectMatchDraft,
|
||||||
|
'profileId' | 'draftId' | 'workTitle' | 'templateName' | 'publicationStatus'
|
||||||
|
>,
|
||||||
|
): PlatformCreationWorkDeleteConfirmationModel {
|
||||||
|
return {
|
||||||
|
id: work.profileId,
|
||||||
|
title: resolveTrimmedTitle(work.workTitle, work.templateName),
|
||||||
|
detail: resolveStatusDeleteDetail(
|
||||||
|
work.publicationStatus,
|
||||||
|
EDUTAINMENT_PUBLIC_DELETE_DETAIL,
|
||||||
|
),
|
||||||
|
noticeKeys: collectDraftNoticeKeys('baby-object-match', [
|
||||||
|
work.profileId,
|
||||||
|
work.draftId,
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user