refactor: 收口平台弹窗状态模型

This commit is contained in:
2026-06-03 22:00:36 +08:00
parent 3efbb6882c
commit caac418e0e
6 changed files with 501 additions and 311 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,113 @@
import { describe, expect, test } from 'vitest';
import {
buildPlatformErrorDialogDismissKey,
buildPlatformTaskCompletionDialogDismissKey,
formatPlatformDialogSource,
isBackgroundGenerationStillRunningMessage,
normalizePlatformDialogMessage,
PLATFORM_TASK_COMPLETION_MESSAGE,
resolveActivePlatformDialog,
resolvePlatformErrorDialog,
} from './platformDialogStateModel';
describe('platformDialogStateModel', () => {
test('normalizes platform dialog messages', () => {
expect(normalizePlatformDialogMessage(' 图片失败 ')).toBe('图片失败');
expect(normalizePlatformDialogMessage(' ')).toBeNull();
expect(normalizePlatformDialogMessage(null)).toBeNull();
});
test('formats dialog source with optional identity', () => {
expect(formatPlatformDialogSource('拼图草稿', ' puzzle-session-1 ')).toBe(
'拼图草稿 puzzle-session-1',
);
expect(formatPlatformDialogSource('拼图草稿', ' ')).toBe('拼图草稿');
});
test('detects background generation still running messages', () => {
expect(
isBackgroundGenerationStillRunningMessage('后台仍在处理,请稍后查看。'),
).toBe(true);
expect(isBackgroundGenerationStillRunningMessage('素材生成失败。')).toBe(
false,
);
});
test('resolves the first non-empty error candidate', () => {
expect(
resolvePlatformErrorDialog([
{
key: 'empty',
source: '空来源',
message: ' ',
},
{
key: 'puzzle',
source: '拼图草稿 puzzle-session-1',
message: ' 素材生成失败。 ',
},
]),
).toEqual({
key: 'puzzle',
source: '拼图草稿 puzzle-session-1',
message: '素材生成失败。',
});
expect(
resolvePlatformErrorDialog([
{
key: 'empty',
source: '空来源',
message: null,
},
]),
).toBeNull();
});
test('builds stable dismiss keys for error and completion dialogs', () => {
expect(
buildPlatformErrorDialogDismissKey({
key: 'puzzle',
source: '拼图草稿 puzzle-session-1',
message: '素材生成失败。',
}),
).toBe('puzzle:拼图草稿 puzzle-session-1:素材生成失败。');
expect(buildPlatformErrorDialogDismissKey(null)).toBeNull();
expect(
buildPlatformTaskCompletionDialogDismissKey({
key: 'match3d',
source: '抓大鹅草稿 match3d-session-1',
message: PLATFORM_TASK_COMPLETION_MESSAGE,
completedAtMs: null,
}),
).toBe(
`match3d:抓大鹅草稿 match3d-session-1:${PLATFORM_TASK_COMPLETION_MESSAGE}:0`,
);
});
test('hides active dialog when the dismiss key has already been recorded', () => {
const dialog = {
key: 'puzzle',
source: '拼图草稿 puzzle-session-1',
message: '素材生成失败。',
};
const dismissKey = buildPlatformErrorDialogDismissKey(dialog);
expect(
resolveActivePlatformDialog(
dialog,
dismissKey,
buildPlatformErrorDialogDismissKey,
),
).toBeNull();
expect(
resolveActivePlatformDialog(
dialog,
'other-dismiss-key',
buildPlatformErrorDialogDismissKey,
),
).toBe(dialog);
});
});
@@ -0,0 +1,85 @@
import type { PlatformErrorDialogPayload } from './PlatformErrorDialog';
import type { PlatformTaskCompletionDialogPayload } from './PlatformTaskCompletionDialog';
export type PlatformErrorDialogState = PlatformErrorDialogPayload & {
key: string;
};
export type PlatformTaskFailureDialogState = PlatformErrorDialogState & {
failedAtMs: number;
};
export type PlatformTaskCompletionDialogState =
PlatformTaskCompletionDialogPayload & {
key: string;
completedAtMs: number | null;
};
export type PlatformDialogCandidate = {
key: string;
source: string;
message: string | null | undefined;
};
export const PLATFORM_TASK_COMPLETION_MESSAGE =
'生成任务已完成,可以继续查看草稿。';
/** 收口平台弹窗候选的纯状态规则,壳层只负责副作用清理。 */
export function normalizePlatformDialogMessage(
message: string | null | undefined,
) {
const normalized = message?.trim();
return normalized ? normalized : null;
}
export function formatPlatformDialogSource(label: string, id?: string | null) {
const normalizedId = id?.trim();
return normalizedId ? `${label} ${normalizedId}` : label;
}
export function isBackgroundGenerationStillRunningMessage(message: string) {
return /|||/u.test(message);
}
export function resolvePlatformErrorDialog(
candidates: readonly PlatformDialogCandidate[],
): PlatformErrorDialogState | null {
for (const candidate of candidates) {
const message = normalizePlatformDialogMessage(candidate.message);
if (message) {
return {
key: candidate.key,
source: candidate.source,
message,
};
}
}
return null;
}
export function buildPlatformErrorDialogDismissKey(
error: PlatformErrorDialogState | null,
) {
return error ? `${error.key}:${error.source}:${error.message}` : null;
}
export function buildPlatformTaskCompletionDialogDismissKey(
completion: PlatformTaskCompletionDialogState | null,
) {
return completion
? `${completion.key}:${completion.source}:${completion.message}:${completion.completedAtMs ?? 0}`
: null;
}
export function resolveActivePlatformDialog<TDialog>(
currentDialog: TDialog | null,
dismissedDialogKey: string | null,
buildDismissKey: (dialog: TDialog | null) => string | null,
): TDialog | null {
const currentDialogDismissKey = buildDismissKey(currentDialog);
return currentDialogDismissKey &&
currentDialogDismissKey === dismissedDialogKey
? null
: currentDialog;
}