修正画布无效项目跳转

显式项目读取失败时返回项目页

403 和 404 项目访问错误不再停留在画布

补充无效项目 ID 回项目页测试
This commit is contained in:
2026-06-25 23:44:36 +08:00
parent 00b2720237
commit 0876361557
2 changed files with 57 additions and 1 deletions
@@ -4,6 +4,7 @@ import { act, render, screen, waitFor } from '@testing-library/react';
import { useCallback, useMemo, useRef, useState } from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ApiClientError } from '../../services/apiClient';
import type { EditorProjectSnapshot } from '../../services/image-editor/editorProjectClient';
import type {
CanvasGenerationDialogState,
@@ -1243,4 +1244,34 @@ describe('useImageCanvasProjectPersistence', () => {
expect(loadEditorProjectMock).not.toHaveBeenCalled();
expect(screen.getByTestId('project-id').textContent).toBe('-');
});
it.each([
[404, '项目不存在'],
[403, '无权访问项目'],
])(
'returns to the project page when an explicit project fails with %i',
async (status, message) => {
window.history.replaceState(
null,
'',
'/editor/canvas?projectid=missing-project',
);
loadEditorProjectMock.mockRejectedValueOnce(
new ApiClientError({
message,
status,
code: status === 404 ? 'not_found' : 'forbidden',
}),
);
render(<ProjectPersistenceHarness />);
await waitFor(() => {
expect(window.location.pathname).toBe('/project');
});
expect(window.location.search).toBe('');
expect(loadEditorProjectMock).toHaveBeenCalledWith('missing-project');
expect(loadOrCreateRecentEditorProjectMock).not.toHaveBeenCalled();
},
);
});
@@ -7,6 +7,7 @@ import {
} from 'react';
import { ApiClientError } from '../../services/apiClient';
import { replaceAppHistoryPath } from '../../routing/appPageRoutes';
import { uploadEditorMediaAssetFile } from '../../services/image-editor/editorMediaAssetUploadClient';
import { putEditorProjectCoverCache } from '../../services/image-editor/editorProjectCoverCache';
import {
@@ -80,7 +81,14 @@ type ImageCanvasProjectPersistenceSetters = {
function isEditorAuthError(error: unknown) {
return (
error instanceof ApiClientError &&
(error.status === 401 || error.status === 403)
error.status === 401
);
}
function isEditorProjectAccessError(error: unknown) {
return (
error instanceof ApiClientError &&
(error.status === 403 || error.status === 404)
);
}
@@ -166,6 +174,18 @@ function readEditorProjectSessionCache(projectId: string | null) {
}
}
function removeEditorProjectSessionCache(projectId: string) {
const storage = getEditorProjectSessionStorage();
if (!storage) {
return;
}
try {
storage.removeItem(editorProjectSessionCacheKey(projectId));
} catch {
// ignore storage cleanup errors
}
}
function writeEditorProjectSessionCache(project: EditorProjectSnapshot) {
if (projectSnapshotContainsInlineMedia(project)) {
return;
@@ -673,6 +693,11 @@ export function useImageCanvasProjectPersistence({
openEditorLoginModal(() => {
window.location.reload();
});
return;
}
if (projectIdFromQuery && isEditorProjectAccessError(error)) {
removeEditorProjectSessionCache(projectIdFromQuery);
replaceAppHistoryPath('/project');
}
});