继续收敛活动回合读取错误
保留 Direct 活动回合快照 IPC 失败正文 补充窗口标题栏显示和回归测试
This commit is contained in:
@@ -178,6 +178,7 @@ export function WindowChrome({ children }: WindowChromeProps) {
|
||||
activeTurns={activeProjectRuns.activeTurns}
|
||||
currentProjectPath={activeProjectRuns.currentProjectPath}
|
||||
readFailed={activeProjectRuns.readFailed}
|
||||
readError={activeProjectRuns.readError}
|
||||
onOpenProject={activeProjectRuns.onOpenProject}
|
||||
placement="titlebar"
|
||||
/>
|
||||
|
||||
@@ -19,6 +19,7 @@ export type WindowChromeActiveProjectRuns = {
|
||||
activeTurns: GameCreatorDirectActiveTurn[];
|
||||
currentProjectPath?: string | null;
|
||||
readFailed?: boolean;
|
||||
readError?: string;
|
||||
onOpenProject?: (projectPath: string) => void;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { GameCreatorDirectActiveTurn, TauriInvoke } from '../../app/types';
|
||||
import { visibleClientErrorMessage } from '../../services/errorReporting';
|
||||
import {
|
||||
canSubscribeTauriEvents,
|
||||
subscribeTauriEvent,
|
||||
@@ -35,6 +36,7 @@ export function useDirectActiveTurns({
|
||||
[],
|
||||
);
|
||||
const [snapshotReadFailed, setSnapshotReadFailed] = useState(false);
|
||||
const [snapshotReadError, setSnapshotReadError] = useState('');
|
||||
const mountedRef = useRef(true);
|
||||
const inFlightRef = useRef<Promise<void> | null>(null);
|
||||
/**
|
||||
@@ -93,10 +95,12 @@ export function useDirectActiveTurns({
|
||||
setActiveTurns(nextTurns);
|
||||
}
|
||||
setSnapshotReadFailed(false);
|
||||
setSnapshotReadError('');
|
||||
inFlightRef.current = null;
|
||||
return;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (!isCurrent()) return;
|
||||
setSnapshotReadError(visibleClientErrorMessage(error));
|
||||
if (attempt < DIRECT_ACTIVE_TURNS_READ_ATTEMPTS) {
|
||||
await new Promise<void>((resolve) => {
|
||||
retryTimerRef.current = window.setTimeout(() => {
|
||||
@@ -132,6 +136,7 @@ export function useDirectActiveTurns({
|
||||
// 空态也要保持引用稳定:已经空了就不要再换一个新数组。
|
||||
setActiveTurns((current) => (current.length === 0 ? current : []));
|
||||
setSnapshotReadFailed((current) => (current ? false : current));
|
||||
setSnapshotReadError((current) => (current ? '' : current));
|
||||
return;
|
||||
}
|
||||
let disposed = false;
|
||||
@@ -172,5 +177,10 @@ export function useDirectActiveTurns({
|
||||
};
|
||||
}, [enabled, invoke, refreshActiveTurns]);
|
||||
|
||||
return { activeTurns, refreshActiveTurns, snapshotReadFailed };
|
||||
return {
|
||||
activeTurns,
|
||||
refreshActiveTurns,
|
||||
snapshotReadFailed,
|
||||
snapshotReadError,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export type ActiveProjectRunsPanelProps = {
|
||||
activeTurns: GameCreatorDirectActiveTurn[];
|
||||
currentProjectPath?: string | null;
|
||||
readFailed?: boolean;
|
||||
readError?: string;
|
||||
onOpenProject?: (projectPath: string) => void;
|
||||
placement?: 'panel' | 'titlebar';
|
||||
};
|
||||
@@ -59,6 +60,7 @@ export function ActiveProjectRunsPanel({
|
||||
activeTurns,
|
||||
currentProjectPath = null,
|
||||
readFailed = false,
|
||||
readError = '',
|
||||
onOpenProject,
|
||||
placement = 'panel',
|
||||
}: ActiveProjectRunsPanelProps) {
|
||||
@@ -97,7 +99,7 @@ export function ActiveProjectRunsPanel({
|
||||
className="launcher-runs-titlebar launcher-runs-titlebar--error"
|
||||
role="status"
|
||||
>
|
||||
正在运行的项目读取失败
|
||||
{readError || '正在运行的项目读取失败'}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -105,7 +107,7 @@ export function ActiveProjectRunsPanel({
|
||||
return (
|
||||
<aside className="launcher-runs-panel" aria-label="正在运行的项目">
|
||||
<span className="launcher-runs-panel-note" role="status">
|
||||
未能读取正在运行的项目
|
||||
{readError || '未能读取正在运行的项目'}
|
||||
</span>
|
||||
</aside>
|
||||
);
|
||||
@@ -166,10 +168,10 @@ export function ActiveProjectRunsPanel({
|
||||
const elapsed = formatActiveTurnElapsed(turn.startedAt, now);
|
||||
const isCurrent = Boolean(
|
||||
currentProjectPath &&
|
||||
projectPathsMatchForInvalidation(
|
||||
turn.projectPath,
|
||||
currentProjectPath,
|
||||
),
|
||||
projectPathsMatchForInvalidation(
|
||||
turn.projectPath,
|
||||
currentProjectPath,
|
||||
),
|
||||
);
|
||||
return (
|
||||
<li key={`${turn.projectPath}:${turn.turnId}`}>
|
||||
@@ -215,10 +217,10 @@ export function ActiveProjectRunsPanel({
|
||||
const elapsed = formatActiveTurnElapsed(turn.startedAt, now);
|
||||
const isCurrent = Boolean(
|
||||
currentProjectPath &&
|
||||
projectPathsMatchForInvalidation(
|
||||
turn.projectPath,
|
||||
currentProjectPath,
|
||||
),
|
||||
projectPathsMatchForInvalidation(
|
||||
turn.projectPath,
|
||||
currentProjectPath,
|
||||
),
|
||||
);
|
||||
return (
|
||||
<li key={`${turn.projectPath}:${turn.turnId}`}>
|
||||
|
||||
@@ -174,10 +174,11 @@ export function WorkspaceLauncherShell({
|
||||
: null,
|
||||
);
|
||||
const directInvoke = resolveTauriInvoke();
|
||||
const { activeTurns, snapshotReadFailed } = useDirectActiveTurns({
|
||||
invoke: directInvoke,
|
||||
enabled: true,
|
||||
});
|
||||
const { activeTurns, snapshotReadFailed, snapshotReadError } =
|
||||
useDirectActiveTurns({
|
||||
invoke: directInvoke,
|
||||
enabled: true,
|
||||
});
|
||||
const switchedToGameRuntime =
|
||||
gameRuntimeSwitch !== null &&
|
||||
currentProjectContext !== null &&
|
||||
@@ -304,6 +305,7 @@ export function WorkspaceLauncherShell({
|
||||
activeTurns,
|
||||
currentProjectPath: currentProjectContext?.projectPath ?? null,
|
||||
readFailed: snapshotReadFailed,
|
||||
readError: snapshotReadError,
|
||||
onOpenProject: (projectPath: string) =>
|
||||
openActiveProjectRef.current(projectPath),
|
||||
});
|
||||
@@ -312,6 +314,7 @@ export function WorkspaceLauncherShell({
|
||||
currentProjectContext?.projectPath,
|
||||
setActiveProjectRuns,
|
||||
snapshotReadFailed,
|
||||
snapshotReadError,
|
||||
]);
|
||||
useEffect(() => () => setActiveProjectRuns(null), [setActiveProjectRuns]);
|
||||
|
||||
|
||||
@@ -152,6 +152,9 @@ describe('useDirectActiveTurns', () => {
|
||||
});
|
||||
expect(result.current.activeTurns).toBe(firstSnapshot);
|
||||
expect(result.current.snapshotReadFailed).toBe(true);
|
||||
expect(result.current.snapshotReadError).toContain(
|
||||
'snapshot unavailable',
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user