refactor: 收口草稿打开状态规则
This commit is contained in:
@@ -16,6 +16,14 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-04 Draft Generation Shelf 草稿打开 intent 收口
|
||||
|
||||
- 背景:`openPuzzleDraft` / `openMatch3DDraft` 在平台壳内重复判断已发布作品、缺 session、ready 未读、失败 notice、active / background 生成中、持久化 generating 和普通草稿恢复,导致壳层继续理解拼图稳定 ID、抓大鹅 notice key 与生成状态优先级。
|
||||
- 决策:扩展 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,以 `resolvePuzzleDraftOpenIntent(...)` 与 `resolveMatch3DDraftOpenIntent(...)` 返回纯打开计划和 notice keys;壳层只按 intent 执行网络读取、生成态 rebase、试玩启动、错误写入、路由 / stage 和 notice seen 副作用。
|
||||
- 影响范围:创作中心作品架打开拼图 / 抓大鹅草稿、公开码搜索强制打开抓大鹅草稿、生成完成后 ready 未读试玩、失败草稿恢复和后续 pending / persisted generating 判定。
|
||||
- 验证方式:`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、针对 Draft Shelf Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||
- 关联文档:`docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md`。
|
||||
|
||||
## 2026-06-04 Bark Battle Work Cache 草稿状态收口
|
||||
|
||||
- 背景:`PlatformEntryFlowShellImpl.tsx` 仍内联维护 Bark Battle 草稿三图完整性、生成状态归一、作品架摘要恢复草稿配置,以及草稿 / 已发布作品进入 runtime 前的 `BarkBattlePublishedConfig` 字段映射,导致结果页试玩、作品架启动、草稿恢复和公开详情启动都要理解同一份资产字段清单。
|
||||
|
||||
@@ -77,7 +77,7 @@ Bark Battle 草稿三图完整性、生成状态归一、作品架摘要恢复
|
||||
|
||||
RPG Agent 结果页发布门禁展示和预览来源 label 收口到 `src/components/platform-entry/platformRpgAgentResultPreviewModel.ts`,壳层只保留 session/profile 编排和结果页 props 传递,规则见 [【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRpgAgentResultPreviewModel收口计划-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)。
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
- `buildCreationWorkShelfRuntimeState({ item, notices, pendingShelfItems })`:统一输出 `CreationWorkShelfRuntimeState`,处理失败覆盖、拼图空标题 `拼图草稿` 兜底、summary 占位覆盖、生成中遮罩和 ready 未读点。
|
||||
- `collectVisibleDraftNoticeKeys(...)` / `hasUnreadDraftGenerationUpdates(...)`:统一草稿 Tab 顶部未读点规则。
|
||||
- `mergePuzzleWorkSummary(current, updated)` 与 `mergeBigFishWorkSummary(current, updated)`:统一作品详情更新后回填作品架和当前详情的身份匹配规则。
|
||||
- `resolvePuzzleDraftOpenIntent(...)` 与 `resolveMatch3DDraftOpenIntent(...)`:统一拼图 / 抓大鹅草稿打开时的已发布详情、缺 session、ready 未读试玩、失败生成页、active / background 生成页、持久化 generating 恢复和普通草稿恢复优先级。
|
||||
- `buildPuzzleResultWorkId(...)` / `buildPuzzleResultProfileId(...)`、`isPersistedDraftGenerating(...)` / `isPersistedDraftFailed(...)`:把拼图稳定 ID 与持久化状态判断收在同一 **Seam**。
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 仍作为 React state 与副作用 **Adapter**:负责写入 `draftGenerationNotices` / `pendingDraftShelfItems`、启动生成、刷新后端列表、打开结果页和弹窗;它不再内联 pending shelf row shape、notice key 汇总和作品架 runtime state 规则。
|
||||
`PlatformEntryFlowShellImpl.tsx` 仍作为 React state 与副作用 **Adapter**:负责写入 `draftGenerationNotices` / `pendingDraftShelfItems`、读取生成 session、启动 ready 草稿试玩、刷新后端列表、打开结果页和弹窗;它不再内联 pending shelf row shape、notice key 汇总、作品架 runtime state 和拼图 / 抓大鹅草稿打开优先级。
|
||||
|
||||
## 约定
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
- 拼图作品详情更新只以 `profileId` 匹配回填;大鱼吃小鱼作品详情更新只以 `sourceSessionId` 匹配回填。
|
||||
- 失败 notice 优先级高于持久化 generating,且可通过 pending metadata 提供更具体 summary;否则回退玩法默认失败摘要。
|
||||
- 已有封面的拼图草稿即使局部关卡仍在后台生成,也不得被整卡遮罩为不可打开的生成中状态。
|
||||
- 拼图 / 抓大鹅草稿打开 intent 只返回纯计划与 notice keys,不创建失败生成态、不请求详情、不写 stage;这些仍由壳层 Adapter 执行。
|
||||
- 本 **Module** 不做网络请求、路由切换、弹窗副作用或 React state 写入,只保留纯 **Implementation**,以提高 **Depth**、**Leverage** 与 **Locality**。
|
||||
|
||||
## 验证
|
||||
|
||||
@@ -349,8 +349,6 @@ import {
|
||||
buildCreationWorkShelfItems,
|
||||
type CreationWorkShelfItem,
|
||||
isPersistedBarkBattleDraftGenerating,
|
||||
isPersistedPuzzleDraftGenerating,
|
||||
resolvePuzzleWorkCoverImageSrc,
|
||||
} from '../custom-world-home/creationWorkShelf';
|
||||
import {
|
||||
buildPlatformRecommendFeedEntries,
|
||||
@@ -437,7 +435,6 @@ import {
|
||||
import {
|
||||
buildCreationWorkShelfRuntimeState,
|
||||
buildDraftCompletionDialogSource,
|
||||
buildDraftFailedShelfSummary,
|
||||
buildPendingBarkBattleWorks,
|
||||
buildPendingBigFishWorks,
|
||||
buildPendingJumpHopWorks,
|
||||
@@ -451,19 +448,17 @@ import {
|
||||
createPendingDraftShelfState,
|
||||
type DraftGenerationNoticeMap,
|
||||
type DraftGenerationNoticeStatus,
|
||||
getDraftGenerationNotice,
|
||||
getGenerationNoticeShelfKeys,
|
||||
hasDraftGenerationNoticeStatus,
|
||||
hasUnreadDraftGenerationUpdates,
|
||||
hasUnreadReadyDraftGenerationNotice,
|
||||
isPersistedDraftFailed,
|
||||
isPersistedDraftGenerating,
|
||||
mergeBigFishWorkSummary,
|
||||
mergePuzzleWorkSummary,
|
||||
normalizeDraftNoticeId,
|
||||
type PendingDraftShelfKind,
|
||||
type PendingDraftShelfMap,
|
||||
type PendingDraftShelfMetadata,
|
||||
resolveMatch3DDraftOpenIntent,
|
||||
resolvePuzzleDraftOpenIntent,
|
||||
} from './platformDraftGenerationShelfModel';
|
||||
import {
|
||||
canExposePublicWork,
|
||||
@@ -2012,26 +2007,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
activePuzzleGenerationSessionIdRef.current === sessionId
|
||||
);
|
||||
}, []);
|
||||
const isDraftNoticeGenerating = useCallback(
|
||||
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) =>
|
||||
hasDraftGenerationNoticeStatus(
|
||||
draftGenerationNotices,
|
||||
kind,
|
||||
ids,
|
||||
'generating',
|
||||
),
|
||||
[draftGenerationNotices],
|
||||
);
|
||||
const isDraftNoticeFailed = useCallback(
|
||||
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) =>
|
||||
hasDraftGenerationNoticeStatus(draftGenerationNotices, kind, ids, 'failed'),
|
||||
[draftGenerationNotices],
|
||||
);
|
||||
const isDraftNoticeReadyUnread = useCallback(
|
||||
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) =>
|
||||
hasUnreadReadyDraftGenerationNotice(draftGenerationNotices, kind, ids),
|
||||
[draftGenerationNotices],
|
||||
);
|
||||
const ensureEnoughDraftGenerationPointsFromServer = useCallback(
|
||||
async (pointsCost: number) => {
|
||||
try {
|
||||
@@ -10221,83 +10201,67 @@ export function PlatformEntryFlowShellImpl({
|
||||
|
||||
const openPuzzleDraft = useCallback(
|
||||
async (item: PuzzleWorkSummary) => {
|
||||
const noticeKeys = collectDraftNoticeKeys('puzzle', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
]);
|
||||
const failedNotice = getDraftGenerationNotice(
|
||||
draftGenerationNotices,
|
||||
noticeKeys,
|
||||
);
|
||||
const isPersistedFailed = isPersistedDraftFailed(item.generationStatus);
|
||||
const hasGeneratingNotice = isDraftNoticeGenerating('puzzle', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
]);
|
||||
const hasFailedNotice = isDraftNoticeFailed('puzzle', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
]);
|
||||
const noticeErrorMessage =
|
||||
failedNotice?.status === 'failed'
|
||||
? (failedNotice.message ?? buildDraftFailedShelfSummary('puzzle'))
|
||||
: buildDraftFailedShelfSummary('puzzle');
|
||||
const isMarkedGenerating =
|
||||
!hasFailedNotice &&
|
||||
((hasGeneratingNotice && !resolvePuzzleWorkCoverImageSrc(item)) ||
|
||||
isPersistedPuzzleDraftGenerating(item));
|
||||
const sourceSessionId = item.sourceSessionId?.trim() ?? '';
|
||||
const backgroundTask = sourceSessionId
|
||||
? getPuzzleBackgroundCompileTask(sourceSessionId)
|
||||
: null;
|
||||
const activeGenerationState =
|
||||
backgroundTask?.generationState ?? puzzleGenerationViewState;
|
||||
const openIntent = resolvePuzzleDraftOpenIntent({
|
||||
item,
|
||||
notices: draftGenerationNotices,
|
||||
generation: {
|
||||
activeSessionId: puzzleSession?.sessionId,
|
||||
hasActiveGenerationFailure:
|
||||
activeGenerationState?.phase === 'failed',
|
||||
hasActiveGenerationRunning: isMiniGameDraftGenerating(
|
||||
activeGenerationState ?? null,
|
||||
),
|
||||
hasBackgroundGenerationFailure:
|
||||
backgroundTask?.generationState.phase === 'failed',
|
||||
hasBackgroundGenerationRunning: isMiniGameDraftGenerating(
|
||||
backgroundTask?.generationState ?? null,
|
||||
),
|
||||
},
|
||||
});
|
||||
const { noticeKeys } = openIntent;
|
||||
setPuzzleOperation(null);
|
||||
setPuzzleRun(null);
|
||||
setPuzzleRuntimeAuthMode('default');
|
||||
setSelectedPuzzleDetail(null);
|
||||
if (!item.sourceSessionId?.trim()) {
|
||||
if (item.publicationStatus === 'published') {
|
||||
await openPuzzleDetail(item.profileId, { tab: 'create' });
|
||||
return;
|
||||
}
|
||||
|
||||
setPuzzleError('这份拼图草稿缺少会话信息,请重新开始创作。');
|
||||
if (openIntent.type === 'open-published-detail') {
|
||||
await openPuzzleDetail(item.profileId, { tab: 'create' });
|
||||
return;
|
||||
}
|
||||
|
||||
const backgroundTask = getPuzzleBackgroundCompileTask(
|
||||
item.sourceSessionId,
|
||||
);
|
||||
const activeGenerationState =
|
||||
backgroundTask?.generationState ?? puzzleGenerationViewState;
|
||||
const failedGenerationState =
|
||||
backgroundTask?.generationState.phase === 'failed'
|
||||
? backgroundTask.generationState
|
||||
: item.sourceSessionId === puzzleSession?.sessionId &&
|
||||
activeGenerationState?.phase === 'failed'
|
||||
? activeGenerationState
|
||||
: hasFailedNotice || isPersistedFailed
|
||||
? createFailedMiniGameDraftGenerationStateForRestoredDraft(
|
||||
if (openIntent.type === 'missing-session') {
|
||||
setPuzzleError(openIntent.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (openIntent.type === 'failed-generation') {
|
||||
const failedGenerationState =
|
||||
openIntent.source === 'background'
|
||||
? backgroundTask?.generationState
|
||||
: openIntent.source === 'active'
|
||||
? activeGenerationState
|
||||
: createFailedMiniGameDraftGenerationStateForRestoredDraft(
|
||||
'puzzle',
|
||||
item.updatedAt,
|
||||
noticeErrorMessage,
|
||||
openIntent.errorMessage,
|
||||
{ puzzleAiRedraw: true },
|
||||
)
|
||||
: null;
|
||||
|
||||
if ((hasFailedNotice || isPersistedFailed) && failedGenerationState) {
|
||||
);
|
||||
if (!failedGenerationState) {
|
||||
return;
|
||||
}
|
||||
let failedSession = backgroundTask?.session ?? null;
|
||||
let failedPayload = backgroundTask?.payload ?? null;
|
||||
const failedError =
|
||||
backgroundTask?.error ?? failedNotice?.message ?? noticeErrorMessage;
|
||||
const failedError = backgroundTask?.error ?? openIntent.errorMessage;
|
||||
if (!failedSession) {
|
||||
try {
|
||||
const { session: latestSession } = await getPuzzleAgentSession(
|
||||
item.sourceSessionId,
|
||||
sourceSessionId,
|
||||
);
|
||||
failedSession = latestSession;
|
||||
failedPayload = buildPuzzleFormPayloadFromSession(latestSession);
|
||||
@@ -10330,16 +10294,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'puzzle-generating';
|
||||
activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activePuzzleGenerationSessionIdRef.current = sourceSessionId;
|
||||
setPuzzleGenerationState(failedGenerationState);
|
||||
setSelectionStage('puzzle-generating');
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
item.sourceSessionId === puzzleSession?.sessionId &&
|
||||
isMiniGameDraftGenerating(activeGenerationState)
|
||||
) {
|
||||
if (openIntent.type === 'active-generation') {
|
||||
if (!activeGenerationState) {
|
||||
return;
|
||||
}
|
||||
@@ -10347,7 +10308,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
rebaseMiniGameDraftGenerationStateForDisplay(activeGenerationState);
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'puzzle-generating';
|
||||
activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activePuzzleGenerationSessionIdRef.current = sourceSessionId;
|
||||
setPuzzleGenerationState(rebasedGenerationState);
|
||||
if (backgroundTask) {
|
||||
setPuzzleBackgroundCompileTasks((current) => ({
|
||||
@@ -10362,10 +10323,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
backgroundTask &&
|
||||
isMiniGameDraftGenerating(backgroundTask.generationState)
|
||||
) {
|
||||
if (openIntent.type === 'background-generation' && backgroundTask) {
|
||||
const rebasedTask =
|
||||
rebaseMiniGameDraftBackgroundCompileTaskForDisplay(backgroundTask);
|
||||
puzzleFlow.setSession(rebasedTask.session);
|
||||
@@ -10380,15 +10338,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'puzzle-generating';
|
||||
activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activePuzzleGenerationSessionIdRef.current = sourceSessionId;
|
||||
setSelectionStage('puzzle-generating');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMarkedGenerating) {
|
||||
if (openIntent.type === 'restore-generating') {
|
||||
try {
|
||||
const { session: latestSession } = await getPuzzleAgentSession(
|
||||
item.sourceSessionId,
|
||||
sourceSessionId,
|
||||
);
|
||||
const payload = buildPuzzleFormPayloadFromSession(latestSession);
|
||||
const startedAtMs = resolveMiniGameDraftGenerationStartedAtMs(
|
||||
@@ -10424,7 +10382,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
}));
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'puzzle-generating';
|
||||
activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activePuzzleGenerationSessionIdRef.current = sourceSessionId;
|
||||
setSelectionStage('puzzle-generating');
|
||||
return;
|
||||
} catch (error) {
|
||||
@@ -10439,7 +10397,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
markDraftNoticeSeen(noticeKeys);
|
||||
|
||||
const restoredSession = await puzzleFlow.restoreDraft(
|
||||
item.sourceSessionId,
|
||||
sourceSessionId,
|
||||
);
|
||||
if (!restoredSession) {
|
||||
await refreshPuzzleShelf().catch(() => undefined);
|
||||
@@ -10459,8 +10417,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
enterCreateTab,
|
||||
draftGenerationNotices,
|
||||
getPuzzleBackgroundCompileTask,
|
||||
isDraftNoticeFailed,
|
||||
isDraftNoticeGenerating,
|
||||
markDraftNoticeSeen,
|
||||
openPuzzleDetail,
|
||||
puzzleFlow,
|
||||
@@ -10478,78 +10434,52 @@ export function PlatformEntryFlowShellImpl({
|
||||
item: Match3DWorkSummary,
|
||||
options: { forceDraft?: boolean } = {},
|
||||
) => {
|
||||
const noticeKeys = collectDraftNoticeKeys('match3d', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
]);
|
||||
const hasUnreadReadyNotice = isDraftNoticeReadyUnread('match3d', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
]);
|
||||
const sourceSessionId = item.sourceSessionId?.trim() ?? '';
|
||||
const backgroundTask = sourceSessionId
|
||||
? getMatch3DBackgroundCompileTask(sourceSessionId)
|
||||
: null;
|
||||
const activeGenerationState =
|
||||
backgroundTask?.generationState ?? match3dGenerationViewState;
|
||||
const openIntent = resolveMatch3DDraftOpenIntent({
|
||||
item,
|
||||
notices: draftGenerationNotices,
|
||||
forceDraft: options.forceDraft,
|
||||
generation: {
|
||||
activeSessionId: match3dSession?.sessionId,
|
||||
hasActiveGenerationFailure:
|
||||
activeGenerationState?.phase === 'failed',
|
||||
hasActiveGenerationRunning: isMiniGameDraftGenerating(
|
||||
activeGenerationState ?? null,
|
||||
),
|
||||
hasBackgroundGenerationFailure:
|
||||
backgroundTask?.generationState.phase === 'failed',
|
||||
hasBackgroundGenerationRunning: isMiniGameDraftGenerating(
|
||||
backgroundTask?.generationState ?? null,
|
||||
),
|
||||
},
|
||||
});
|
||||
const { noticeKeys } = openIntent;
|
||||
setMatch3DRun(null);
|
||||
setMatch3DError(null);
|
||||
setMatch3DProfile(null);
|
||||
setMatch3DRuntimeProfile(null);
|
||||
|
||||
if (item.publicationStatus === 'published' && !options.forceDraft) {
|
||||
if (openIntent.type === 'open-published-detail') {
|
||||
markDraftNoticeSeen(noticeKeys);
|
||||
openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(item));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.sourceSessionId?.trim()) {
|
||||
if (openIntent.type === 'missing-session') {
|
||||
markDraftNoticeSeen(noticeKeys);
|
||||
setMatch3DError('这份抓大鹅草稿缺少会话信息,请重新开始创作。');
|
||||
setMatch3DError(openIntent.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const failedNotice = getDraftGenerationNotice(
|
||||
draftGenerationNotices,
|
||||
noticeKeys,
|
||||
);
|
||||
const hasFailedNotice = isDraftNoticeFailed('match3d', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
]);
|
||||
const noticeErrorMessage =
|
||||
failedNotice?.status === 'failed'
|
||||
? (failedNotice.message ?? buildDraftFailedShelfSummary('match3d'))
|
||||
: buildDraftFailedShelfSummary('match3d');
|
||||
const isMarkedGenerating =
|
||||
!hasFailedNotice &&
|
||||
(isDraftNoticeGenerating('match3d', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
]) ||
|
||||
isPersistedDraftGenerating(item.generationStatus));
|
||||
|
||||
const backgroundTask = getMatch3DBackgroundCompileTask(
|
||||
item.sourceSessionId,
|
||||
);
|
||||
const activeGenerationState =
|
||||
backgroundTask?.generationState ?? match3dGenerationViewState;
|
||||
const failedGenerationState =
|
||||
backgroundTask?.generationState.phase === 'failed'
|
||||
? backgroundTask.generationState
|
||||
: item.sourceSessionId === match3dSession?.sessionId &&
|
||||
activeGenerationState?.phase === 'failed'
|
||||
? activeGenerationState
|
||||
: hasFailedNotice
|
||||
? createFailedMiniGameDraftGenerationStateForRestoredDraft(
|
||||
'match3d',
|
||||
item.updatedAt,
|
||||
noticeErrorMessage,
|
||||
)
|
||||
: null;
|
||||
|
||||
if (hasUnreadReadyNotice) {
|
||||
if (openIntent.type === 'ready-unread') {
|
||||
try {
|
||||
const { session: latestSession } =
|
||||
await match3dCreationClient.getSession(item.sourceSessionId);
|
||||
await match3dCreationClient.getSession(sourceSessionId);
|
||||
setMatch3DSession(latestSession);
|
||||
setMatch3DFormDraftPayload(null);
|
||||
const profileId = latestSession.draft?.profileId ?? item.profileId;
|
||||
@@ -10577,15 +10507,27 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
}
|
||||
|
||||
if (failedGenerationState) {
|
||||
if (openIntent.type === 'failed-generation') {
|
||||
const failedGenerationState =
|
||||
openIntent.source === 'background'
|
||||
? backgroundTask?.generationState
|
||||
: openIntent.source === 'active'
|
||||
? activeGenerationState
|
||||
: createFailedMiniGameDraftGenerationStateForRestoredDraft(
|
||||
'match3d',
|
||||
item.updatedAt,
|
||||
openIntent.errorMessage,
|
||||
);
|
||||
if (!failedGenerationState) {
|
||||
return;
|
||||
}
|
||||
let failedSession = backgroundTask?.session ?? null;
|
||||
let failedPayload = backgroundTask?.payload ?? null;
|
||||
const failedError =
|
||||
backgroundTask?.error ?? failedNotice?.message ?? noticeErrorMessage;
|
||||
const failedError = backgroundTask?.error ?? openIntent.errorMessage;
|
||||
if (!failedSession) {
|
||||
try {
|
||||
const { session: latestSession } =
|
||||
await match3dCreationClient.getSession(item.sourceSessionId);
|
||||
await match3dCreationClient.getSession(sourceSessionId);
|
||||
failedSession = latestSession;
|
||||
failedPayload = buildMatch3DFormPayloadFromSession(latestSession);
|
||||
} catch {
|
||||
@@ -10617,16 +10559,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'match3d-generating';
|
||||
activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activeMatch3DGenerationSessionIdRef.current = sourceSessionId;
|
||||
setMatch3DGenerationState(failedGenerationState);
|
||||
setSelectionStage('match3d-generating');
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
item.sourceSessionId === match3dSession?.sessionId &&
|
||||
isMiniGameDraftGenerating(activeGenerationState)
|
||||
) {
|
||||
if (openIntent.type === 'active-generation') {
|
||||
if (!activeGenerationState) {
|
||||
return;
|
||||
}
|
||||
@@ -10634,7 +10573,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
rebaseMiniGameDraftGenerationStateForDisplay(activeGenerationState);
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'match3d-generating';
|
||||
activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activeMatch3DGenerationSessionIdRef.current = sourceSessionId;
|
||||
setMatch3DGenerationState(rebasedGenerationState);
|
||||
if (backgroundTask) {
|
||||
setMatch3DBackgroundCompileTasks((current) => ({
|
||||
@@ -10649,10 +10588,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
backgroundTask &&
|
||||
isMiniGameDraftGenerating(backgroundTask.generationState)
|
||||
) {
|
||||
if (openIntent.type === 'background-generation' && backgroundTask) {
|
||||
const rebasedTask =
|
||||
rebaseMiniGameDraftBackgroundCompileTaskForDisplay(backgroundTask);
|
||||
setMatch3DSession(rebasedTask.session);
|
||||
@@ -10667,15 +10603,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'match3d-generating';
|
||||
activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activeMatch3DGenerationSessionIdRef.current = sourceSessionId;
|
||||
setSelectionStage('match3d-generating');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMarkedGenerating) {
|
||||
if (openIntent.type === 'restore-generating') {
|
||||
try {
|
||||
const { session: latestSession } =
|
||||
await match3dCreationClient.getSession(item.sourceSessionId);
|
||||
await match3dCreationClient.getSession(sourceSessionId);
|
||||
setMatch3DSession(latestSession);
|
||||
setMatch3DFormDraftPayload(null);
|
||||
setMatch3DProfile(null);
|
||||
@@ -10690,7 +10626,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
setMatch3DGenerationState(generationState);
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'match3d-generating';
|
||||
activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
|
||||
activeMatch3DGenerationSessionIdRef.current = sourceSessionId;
|
||||
setSelectionStage('match3d-generating');
|
||||
return;
|
||||
} catch (error) {
|
||||
@@ -10705,7 +10641,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
markDraftNoticeSeen(noticeKeys);
|
||||
|
||||
const restoredSession = await match3dFlow.restoreDraft(
|
||||
item.sourceSessionId,
|
||||
sourceSessionId,
|
||||
);
|
||||
if (!restoredSession) {
|
||||
await refreshMatch3DShelf().catch(() => undefined);
|
||||
@@ -10729,9 +10665,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
enterCreateTab,
|
||||
draftGenerationNotices,
|
||||
getMatch3DBackgroundCompileTask,
|
||||
isDraftNoticeFailed,
|
||||
isDraftNoticeGenerating,
|
||||
isDraftNoticeReadyUnread,
|
||||
markDraftNoticeSeen,
|
||||
match3dFlow,
|
||||
match3dGenerationViewState,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||
import { buildCreationWorkShelfItems } from '../custom-world-home/creationWorkShelf';
|
||||
import {
|
||||
@@ -15,9 +16,140 @@ import {
|
||||
hasUnreadDraftGenerationUpdates,
|
||||
mergeBigFishWorkSummary,
|
||||
mergePuzzleWorkSummary,
|
||||
resolveMatch3DDraftOpenIntent,
|
||||
resolvePuzzleDraftOpenIntent,
|
||||
} from './platformDraftGenerationShelfModel';
|
||||
|
||||
describe('platformDraftGenerationShelfModel', () => {
|
||||
test('resolvePuzzleDraftOpenIntent sends published puzzle without session to detail', () => {
|
||||
expect(
|
||||
resolvePuzzleDraftOpenIntent({
|
||||
item: buildPuzzleWork({
|
||||
sourceSessionId: null,
|
||||
publicationStatus: 'published',
|
||||
}),
|
||||
notices: {},
|
||||
generation: emptyGenerationFacts(),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'open-published-detail',
|
||||
});
|
||||
});
|
||||
|
||||
test('resolvePuzzleDraftOpenIntent restores failed puzzle generation with notice copy', () => {
|
||||
expect(
|
||||
resolvePuzzleDraftOpenIntent({
|
||||
item: buildPuzzleWork(),
|
||||
notices: {
|
||||
'puzzle:puzzle-session-base': {
|
||||
status: 'failed',
|
||||
seen: false,
|
||||
message: '首图生成失败。',
|
||||
},
|
||||
},
|
||||
generation: emptyGenerationFacts(),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'failed-generation',
|
||||
source: 'restored',
|
||||
errorMessage: '首图生成失败。',
|
||||
});
|
||||
});
|
||||
|
||||
test('resolvePuzzleDraftOpenIntent prefers active generation before restoring draft', () => {
|
||||
expect(
|
||||
resolvePuzzleDraftOpenIntent({
|
||||
item: buildPuzzleWork(),
|
||||
notices: {},
|
||||
generation: emptyGenerationFacts({
|
||||
activeSessionId: 'puzzle-session-base',
|
||||
hasActiveGenerationRunning: true,
|
||||
}),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'active-generation',
|
||||
});
|
||||
});
|
||||
|
||||
test('resolvePuzzleDraftOpenIntent does not lock a puzzle draft that already has a cover', () => {
|
||||
expect(
|
||||
resolvePuzzleDraftOpenIntent({
|
||||
item: buildPuzzleWork({
|
||||
coverImageSrc: '/media/puzzle-cover.png',
|
||||
}),
|
||||
notices: {
|
||||
'puzzle:puzzle-session-base': {
|
||||
status: 'generating',
|
||||
seen: false,
|
||||
},
|
||||
},
|
||||
generation: emptyGenerationFacts(),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'restore-draft',
|
||||
});
|
||||
});
|
||||
|
||||
test('resolveMatch3DDraftOpenIntent opens published work detail unless forced into draft', () => {
|
||||
const item = buildMatch3DWork({
|
||||
publicationStatus: 'published',
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveMatch3DDraftOpenIntent({
|
||||
item,
|
||||
notices: {},
|
||||
generation: emptyGenerationFacts(),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'open-published-detail',
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveMatch3DDraftOpenIntent({
|
||||
item,
|
||||
notices: {},
|
||||
forceDraft: true,
|
||||
generation: emptyGenerationFacts(),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'restore-draft',
|
||||
});
|
||||
});
|
||||
|
||||
test('resolveMatch3DDraftOpenIntent starts ready unread draft before failure fallback', () => {
|
||||
expect(
|
||||
resolveMatch3DDraftOpenIntent({
|
||||
item: buildMatch3DWork(),
|
||||
notices: {
|
||||
'match3d:match3d-session-base': {
|
||||
status: 'ready',
|
||||
seen: false,
|
||||
},
|
||||
},
|
||||
generation: emptyGenerationFacts({
|
||||
hasBackgroundGenerationFailure: true,
|
||||
}),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'ready-unread',
|
||||
});
|
||||
});
|
||||
|
||||
test('resolveMatch3DDraftOpenIntent restores persisted generating draft', () => {
|
||||
expect(
|
||||
resolveMatch3DDraftOpenIntent({
|
||||
item: buildMatch3DWork({
|
||||
generationStatus: 'generating',
|
||||
}),
|
||||
notices: {},
|
||||
generation: emptyGenerationFacts(),
|
||||
}),
|
||||
).toMatchObject({
|
||||
type: 'restore-generating',
|
||||
});
|
||||
});
|
||||
|
||||
test('buildPendingPuzzleWorks creates failed puzzle placeholder with stable ids and fallback title', () => {
|
||||
const pending = buildPendingPuzzleWorks(
|
||||
{
|
||||
@@ -199,6 +331,19 @@ describe('platformDraftGenerationShelfModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function emptyGenerationFacts(
|
||||
overrides: Partial<Parameters<typeof resolvePuzzleDraftOpenIntent>[0]['generation']> = {},
|
||||
): Parameters<typeof resolvePuzzleDraftOpenIntent>[0]['generation'] {
|
||||
return {
|
||||
activeSessionId: null,
|
||||
hasActiveGenerationFailure: false,
|
||||
hasActiveGenerationRunning: false,
|
||||
hasBackgroundGenerationFailure: false,
|
||||
hasBackgroundGenerationRunning: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPuzzleWork(
|
||||
overrides: Partial<PuzzleWorkSummary> = {},
|
||||
): PuzzleWorkSummary {
|
||||
@@ -227,6 +372,33 @@ function buildPuzzleWork(
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DWork(
|
||||
overrides: Partial<Match3DWorkSummary> = {},
|
||||
): Match3DWorkSummary {
|
||||
return {
|
||||
workId: 'match3d-work-base',
|
||||
profileId: 'match3d-profile-base',
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: 'match3d-session-base',
|
||||
gameName: '潮雾抓大鹅',
|
||||
themeText: '潮雾港口',
|
||||
summary: '潮雾港口抓大鹅。',
|
||||
tags: [],
|
||||
coverImageSrc: null,
|
||||
referenceImageSrc: null,
|
||||
clearCount: 0,
|
||||
difficulty: 1,
|
||||
publicationStatus: 'draft',
|
||||
playCount: 0,
|
||||
updatedAt: '2026-06-03T08:00:00.000Z',
|
||||
publishedAt: null,
|
||||
publishReady: false,
|
||||
generationStatus: 'ready',
|
||||
generatedItemAssets: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildBigFishWork(
|
||||
overrides: Partial<BigFishWorkSummary> = {},
|
||||
): BigFishWorkSummary {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
type CreationWorkShelfItem,
|
||||
type CreationWorkShelfKind,
|
||||
type CreationWorkShelfRuntimeState,
|
||||
isPersistedPuzzleDraftGenerating,
|
||||
resolvePuzzleWorkCoverImageSrc,
|
||||
} from '../custom-world-home/creationWorkShelf';
|
||||
import {
|
||||
@@ -67,6 +68,86 @@ export type PlatformDraftGenerationVisibleShelfSources = {
|
||||
babyObjectMatchItems: readonly BabyObjectMatchDraft[];
|
||||
};
|
||||
|
||||
type DraftOpenGenerationFacts = {
|
||||
activeSessionId?: string | null;
|
||||
hasActiveGenerationFailure: boolean;
|
||||
hasActiveGenerationRunning: boolean;
|
||||
hasBackgroundGenerationFailure: boolean;
|
||||
hasBackgroundGenerationRunning: boolean;
|
||||
};
|
||||
|
||||
type FailedDraftGenerationSource = 'background' | 'active' | 'restored';
|
||||
|
||||
export type PuzzleDraftOpenIntent =
|
||||
| {
|
||||
type: 'open-published-detail';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'missing-session';
|
||||
noticeKeys: string[];
|
||||
errorMessage: string;
|
||||
}
|
||||
| {
|
||||
type: 'failed-generation';
|
||||
noticeKeys: string[];
|
||||
errorMessage: string;
|
||||
source: FailedDraftGenerationSource;
|
||||
}
|
||||
| {
|
||||
type: 'active-generation';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'background-generation';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'restore-generating';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'restore-draft';
|
||||
noticeKeys: string[];
|
||||
};
|
||||
|
||||
export type Match3DDraftOpenIntent =
|
||||
| {
|
||||
type: 'open-published-detail';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'missing-session';
|
||||
noticeKeys: string[];
|
||||
errorMessage: string;
|
||||
}
|
||||
| {
|
||||
type: 'ready-unread';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'failed-generation';
|
||||
noticeKeys: string[];
|
||||
errorMessage: string;
|
||||
source: FailedDraftGenerationSource;
|
||||
}
|
||||
| {
|
||||
type: 'active-generation';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'background-generation';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'restore-generating';
|
||||
noticeKeys: string[];
|
||||
}
|
||||
| {
|
||||
type: 'restore-draft';
|
||||
noticeKeys: string[];
|
||||
};
|
||||
|
||||
export function buildDraftNoticeKey(
|
||||
kind: CreationWorkShelfKind,
|
||||
id: string,
|
||||
@@ -334,6 +415,219 @@ export function hasUnreadReadyDraftGenerationNotice(
|
||||
});
|
||||
}
|
||||
|
||||
export function buildPuzzleDraftOpenNoticeKeys(item: PuzzleWorkSummary) {
|
||||
return collectDraftNoticeKeys('puzzle', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
]);
|
||||
}
|
||||
|
||||
export function buildMatch3DDraftOpenNoticeKeys(item: Match3DWorkSummary) {
|
||||
return collectDraftNoticeKeys('match3d', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
]);
|
||||
}
|
||||
|
||||
export function resolvePuzzleDraftOpenIntent(params: {
|
||||
item: PuzzleWorkSummary;
|
||||
notices: DraftGenerationNoticeMap;
|
||||
generation: DraftOpenGenerationFacts;
|
||||
}): PuzzleDraftOpenIntent {
|
||||
const { item, notices, generation } = params;
|
||||
const noticeKeys = buildPuzzleDraftOpenNoticeKeys(item);
|
||||
const sourceSessionId = normalizeDraftNoticeId(item.sourceSessionId);
|
||||
|
||||
if (!sourceSessionId) {
|
||||
if (item.publicationStatus === 'published') {
|
||||
return { type: 'open-published-detail', noticeKeys };
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'missing-session',
|
||||
noticeKeys,
|
||||
errorMessage: '这份拼图草稿缺少会话信息,请重新开始创作。',
|
||||
};
|
||||
}
|
||||
|
||||
const failedNotice = getDraftGenerationNotice(notices, noticeKeys);
|
||||
const hasFailedNotice = hasDraftGenerationNoticeStatus(
|
||||
notices,
|
||||
'puzzle',
|
||||
[
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
],
|
||||
'failed',
|
||||
);
|
||||
const hasGeneratingNotice = hasDraftGenerationNoticeStatus(
|
||||
notices,
|
||||
'puzzle',
|
||||
[
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
],
|
||||
'generating',
|
||||
);
|
||||
const noticeErrorMessage =
|
||||
failedNotice?.status === 'failed'
|
||||
? (failedNotice.message ?? buildDraftFailedShelfSummary('puzzle'))
|
||||
: buildDraftFailedShelfSummary('puzzle');
|
||||
const isCurrentSession =
|
||||
sourceSessionId === normalizeDraftNoticeId(generation.activeSessionId);
|
||||
|
||||
if (generation.hasBackgroundGenerationFailure) {
|
||||
return {
|
||||
type: 'failed-generation',
|
||||
noticeKeys,
|
||||
errorMessage: noticeErrorMessage,
|
||||
source: 'background',
|
||||
};
|
||||
}
|
||||
|
||||
if (isCurrentSession && generation.hasActiveGenerationFailure) {
|
||||
return {
|
||||
type: 'failed-generation',
|
||||
noticeKeys,
|
||||
errorMessage: noticeErrorMessage,
|
||||
source: 'active',
|
||||
};
|
||||
}
|
||||
|
||||
if (hasFailedNotice || isPersistedDraftFailed(item.generationStatus)) {
|
||||
return {
|
||||
type: 'failed-generation',
|
||||
noticeKeys,
|
||||
errorMessage: noticeErrorMessage,
|
||||
source: 'restored',
|
||||
};
|
||||
}
|
||||
|
||||
if (isCurrentSession && generation.hasActiveGenerationRunning) {
|
||||
return { type: 'active-generation', noticeKeys };
|
||||
}
|
||||
|
||||
if (generation.hasBackgroundGenerationRunning) {
|
||||
return { type: 'background-generation', noticeKeys };
|
||||
}
|
||||
|
||||
const isMarkedGenerating =
|
||||
!hasFailedNotice &&
|
||||
((hasGeneratingNotice && !resolvePuzzleWorkCoverImageSrc(item)) ||
|
||||
isPersistedPuzzleDraftGenerating(item));
|
||||
if (isMarkedGenerating) {
|
||||
return { type: 'restore-generating', noticeKeys };
|
||||
}
|
||||
|
||||
return { type: 'restore-draft', noticeKeys };
|
||||
}
|
||||
|
||||
export function resolveMatch3DDraftOpenIntent(params: {
|
||||
item: Match3DWorkSummary;
|
||||
notices: DraftGenerationNoticeMap;
|
||||
forceDraft?: boolean;
|
||||
generation: DraftOpenGenerationFacts;
|
||||
}): Match3DDraftOpenIntent {
|
||||
const { item, notices, forceDraft = false, generation } = params;
|
||||
const noticeKeys = buildMatch3DDraftOpenNoticeKeys(item);
|
||||
|
||||
if (item.publicationStatus === 'published' && !forceDraft) {
|
||||
return { type: 'open-published-detail', noticeKeys };
|
||||
}
|
||||
|
||||
const sourceSessionId = normalizeDraftNoticeId(item.sourceSessionId);
|
||||
if (!sourceSessionId) {
|
||||
return {
|
||||
type: 'missing-session',
|
||||
noticeKeys,
|
||||
errorMessage: '这份抓大鹅草稿缺少会话信息,请重新开始创作。',
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
hasUnreadReadyDraftGenerationNotice(notices, 'match3d', [
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
])
|
||||
) {
|
||||
return { type: 'ready-unread', noticeKeys };
|
||||
}
|
||||
|
||||
const failedNotice = getDraftGenerationNotice(notices, noticeKeys);
|
||||
const hasFailedNotice = hasDraftGenerationNoticeStatus(
|
||||
notices,
|
||||
'match3d',
|
||||
[item.workId, item.profileId, item.sourceSessionId],
|
||||
'failed',
|
||||
);
|
||||
const noticeErrorMessage =
|
||||
failedNotice?.status === 'failed'
|
||||
? (failedNotice.message ?? buildDraftFailedShelfSummary('match3d'))
|
||||
: buildDraftFailedShelfSummary('match3d');
|
||||
const isCurrentSession =
|
||||
sourceSessionId === normalizeDraftNoticeId(generation.activeSessionId);
|
||||
|
||||
if (generation.hasBackgroundGenerationFailure) {
|
||||
return {
|
||||
type: 'failed-generation',
|
||||
noticeKeys,
|
||||
errorMessage: noticeErrorMessage,
|
||||
source: 'background',
|
||||
};
|
||||
}
|
||||
|
||||
if (isCurrentSession && generation.hasActiveGenerationFailure) {
|
||||
return {
|
||||
type: 'failed-generation',
|
||||
noticeKeys,
|
||||
errorMessage: noticeErrorMessage,
|
||||
source: 'active',
|
||||
};
|
||||
}
|
||||
|
||||
if (hasFailedNotice) {
|
||||
return {
|
||||
type: 'failed-generation',
|
||||
noticeKeys,
|
||||
errorMessage: noticeErrorMessage,
|
||||
source: 'restored',
|
||||
};
|
||||
}
|
||||
|
||||
if (isCurrentSession && generation.hasActiveGenerationRunning) {
|
||||
return { type: 'active-generation', noticeKeys };
|
||||
}
|
||||
|
||||
if (generation.hasBackgroundGenerationRunning) {
|
||||
return { type: 'background-generation', noticeKeys };
|
||||
}
|
||||
|
||||
if (
|
||||
hasDraftGenerationNoticeStatus(
|
||||
notices,
|
||||
'match3d',
|
||||
[item.workId, item.profileId, item.sourceSessionId],
|
||||
'generating',
|
||||
) ||
|
||||
isPersistedDraftGenerating(item.generationStatus)
|
||||
) {
|
||||
return { type: 'restore-generating', noticeKeys };
|
||||
}
|
||||
|
||||
return { type: 'restore-draft', noticeKeys };
|
||||
}
|
||||
|
||||
export function buildCreationWorkShelfRuntimeState(params: {
|
||||
item: CreationWorkShelfItem;
|
||||
notices: DraftGenerationNoticeMap;
|
||||
|
||||
Reference in New Issue
Block a user