feat: 平台错误与完成弹窗收口

This commit is contained in:
kdletters
2026-05-26 15:57:39 +08:00
parent fbda614156
commit abea7cec1d
6 changed files with 347 additions and 11 deletions

View File

@@ -429,6 +429,10 @@ import {
PlatformErrorDialog,
type PlatformErrorDialogPayload,
} from './PlatformErrorDialog';
import {
PlatformTaskCompletionDialog,
type PlatformTaskCompletionDialogPayload,
} from './PlatformTaskCompletionDialog';
import { PlatformFeedbackView } from './PlatformFeedbackView';
import { PlatformWorkDetailView } from './PlatformWorkDetailView';
import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController';
@@ -444,6 +448,7 @@ type DraftGenerationNoticeStatus = 'generating' | 'ready';
type DraftGenerationNotice = {
status: DraftGenerationNoticeStatus;
seen: boolean;
completedAtMs?: number;
};
type DraftGenerationNoticeMap = Record<string, DraftGenerationNotice>;
type CreationWorkShelfKind = CreationWorkShelfItem['kind'];
@@ -2027,12 +2032,74 @@ function formatPlatformErrorSource(label: string, id?: string | null) {
return normalizedId ? `${label} ${normalizedId}` : label;
}
function formatPlatformTaskCompletionSource(label: string, id?: string | null) {
const normalizedId = id?.trim();
return normalizedId ? `${label} ${normalizedId}` : label;
}
function buildPlatformErrorDialogDismissKey(
error: (PlatformErrorDialogPayload & { key: string }) | null,
) {
return error ? `${error.key}:${error.source}:${error.message}` : null;
}
function buildPlatformTaskCompletionDialogDismissKey(
completion:
| (PlatformTaskCompletionDialogPayload & {
key: string;
completedAtMs: number | null;
})
| null,
) {
return completion
? `${completion.key}:${completion.source}:${completion.message}:${completion.completedAtMs ?? 0}`
: null;
}
function pickDraftCompletionDialogSourceId(
ids: Array<string | null | undefined>,
) {
const normalizedIds = ids
.map((id) => id?.trim() ?? '')
.filter((id) => Boolean(id));
return (
normalizedIds.find((id) => /session/i.test(id)) ??
normalizedIds.find((id) => /work/i.test(id)) ??
normalizedIds.find((id) => /draft/i.test(id)) ??
normalizedIds.find((id) => /run/i.test(id)) ??
normalizedIds.find((id) => /profile/i.test(id)) ??
normalizedIds[0] ??
null
);
}
function buildDraftCompletionDialogSource(
kind: CreationWorkShelfKind,
ids: Array<string | null | undefined>,
) {
const sourceId = pickDraftCompletionDialogSourceId(ids);
switch (kind) {
case 'rpg':
return formatPlatformTaskCompletionSource('RPG 草稿', sourceId);
case 'big-fish':
return formatPlatformTaskCompletionSource('大鱼吃小鱼草稿', sourceId);
case 'match3d':
return formatPlatformTaskCompletionSource('抓大鹅草稿', sourceId);
case 'square-hole':
return formatPlatformTaskCompletionSource('方洞挑战草稿', sourceId);
case 'jump-hop':
return formatPlatformTaskCompletionSource('跳一跳草稿', sourceId);
case 'puzzle':
return formatPlatformTaskCompletionSource('拼图草稿', sourceId);
case 'visual-novel':
return formatPlatformTaskCompletionSource('视觉小说草稿', sourceId);
case 'bark-battle':
return formatPlatformTaskCompletionSource('汪汪声浪草稿', sourceId);
case 'baby-object-match':
return formatPlatformTaskCompletionSource('宝贝识物草稿', sourceId);
}
}
function createMiniGameDraftGenerationStateForRestoredDraft(
kind: MiniGameDraftGenerationKind,
metadata?: MiniGameDraftGenerationState['metadata'],
@@ -3327,6 +3394,16 @@ export function PlatformEntryFlowShellImpl({
useState<DraftGenerationNoticeMap>({});
const [pendingDraftShelfItems, setPendingDraftShelfItems] =
useState<PendingDraftShelfMap>({});
const [
pendingPlatformTaskCompletionDialog,
setPendingPlatformTaskCompletionDialog,
] = useState<
| (PlatformTaskCompletionDialogPayload & {
key: string;
completedAtMs: number | null;
})
| null
>(null);
const [initialCreationUrlState] = useState(() => readCreationUrlState());
const handledInitialCreationUrlStateRef = useRef(false);
const [initialPuzzleRuntimeUrlState] = useState(() =>
@@ -3404,10 +3481,14 @@ export function PlatformEntryFlowShellImpl({
return;
}
const completedAtMs = status === 'ready' ? Date.now() : undefined;
setDraftGenerationNotices((current) => {
const next = { ...current };
for (const key of uniqueKeys) {
next[key] = { status, seen };
next[key] =
completedAtMs === undefined
? { status, seen }
: { status, seen, completedAtMs };
}
return next;
});
@@ -3449,12 +3530,13 @@ export function PlatformEntryFlowShellImpl({
);
const markDraftGenerating = useCallback(
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
setPendingPlatformTaskCompletionDialog(null);
updateDraftGenerationNotices(
collectDraftNoticeKeys(kind, ids),
'generating',
);
},
[updateDraftGenerationNotices],
[setPendingPlatformTaskCompletionDialog, updateDraftGenerationNotices],
);
const markDraftReady = useCallback(
(
@@ -3467,17 +3549,27 @@ export function PlatformEntryFlowShellImpl({
'ready',
viewedImmediately,
);
if (!viewedImmediately) {
const completedAtMs = Date.now();
setPendingPlatformTaskCompletionDialog({
key: `${kind}:${collectDraftNoticeKeys(kind, ids).join('|')}:${completedAtMs}`,
source: buildDraftCompletionDialogSource(kind, ids),
message: '生成任务已完成,可以继续查看草稿。',
completedAtMs,
});
}
},
[updateDraftGenerationNotices],
[setPendingPlatformTaskCompletionDialog, updateDraftGenerationNotices],
);
const markPendingDraftGenerating = useCallback(
(
kind: Exclude<CreationWorkShelfKind, 'rpg'>,
id: string | null | undefined,
) => {
setPendingPlatformTaskCompletionDialog(null);
updatePendingDraftShelfItem(kind, id, 'generating');
},
[updatePendingDraftShelfItem],
[setPendingPlatformTaskCompletionDialog, updatePendingDraftShelfItem],
);
const markPendingDraftReady = useCallback(
(
@@ -5790,6 +5882,10 @@ export function PlatformEntryFlowShellImpl({
);
const [dismissedPlatformErrorDialogKey, setDismissedPlatformErrorDialogKey] =
useState<string | null>(null);
const [
dismissedPlatformTaskCompletionDialogKey,
setDismissedPlatformTaskCompletionDialogKey,
] = useState<string | null>(null);
const currentPlatformErrorDialog = useMemo<
(PlatformErrorDialogPayload & { key: string }) | null
>(() => {
@@ -6013,6 +6109,25 @@ export function PlatformEntryFlowShellImpl({
woodenFishRun?.runId,
woodenFishSession?.sessionId,
]);
const currentPlatformTaskCompletionDialog = useMemo<
| (PlatformTaskCompletionDialogPayload & {
key: string;
completedAtMs: number | null;
})
| null
>(() => pendingPlatformTaskCompletionDialog, [
pendingPlatformTaskCompletionDialog,
]);
const activePlatformTaskCompletionDialogDismissKey =
buildPlatformTaskCompletionDialogDismissKey(
currentPlatformTaskCompletionDialog,
);
const activePlatformTaskCompletionDialog =
activePlatformTaskCompletionDialogDismissKey &&
activePlatformTaskCompletionDialogDismissKey ===
dismissedPlatformTaskCompletionDialogKey
? null
: currentPlatformTaskCompletionDialog;
const activePlatformErrorDialogDismissKey =
buildPlatformErrorDialogDismissKey(currentPlatformErrorDialog);
const activePlatformErrorDialog =
@@ -6118,6 +6233,19 @@ export function PlatformEntryFlowShellImpl({
setSquareHoleError,
setVisualNovelError,
]);
const closePlatformTaskCompletionDialog = useCallback(() => {
if (!currentPlatformTaskCompletionDialog) {
return;
}
const dismissKey = buildPlatformTaskCompletionDialogDismissKey(
currentPlatformTaskCompletionDialog,
);
if (dismissKey) {
setDismissedPlatformTaskCompletionDialogKey(dismissKey);
}
setPendingPlatformTaskCompletionDialog(null);
}, [currentPlatformTaskCompletionDialog]);
const shouldPollPuzzleGenerationSession =
selectionStage === 'puzzle-generating' &&
activePuzzleGenerationSessionId != null &&
@@ -7116,6 +7244,7 @@ export function PlatformEntryFlowShellImpl({
setIsProfilePlayStatsOpen(false);
setDraftGenerationNotices({});
setPendingDraftShelfItems({});
setPendingPlatformTaskCompletionDialog(null);
resetRpgSessionViewState();
setRpgGeneratedCustomWorldProfile(null);
setRpgCustomWorldError(null);
@@ -16871,6 +17000,12 @@ export function PlatformEntryFlowShellImpl({
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
panelClassName="platform-remap-surface rounded-[1.5rem]"
/>
<PlatformTaskCompletionDialog
completion={activePlatformTaskCompletionDialog}
onClose={closePlatformTaskCompletionDialog}
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
panelClassName="platform-remap-surface rounded-[1.5rem]"
/>
<UnifiedModal
open={Boolean(pendingDeleteCreationWork)}
title="删除作品"