refactor: 收口平台弹窗状态模型
This commit is contained in:
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;
|
||||
}
|
||||
Reference in New Issue
Block a user