Skip to content

Commit 63f2a0f

Browse files
imnasnainaecclaude
andcommitted
Send error notification on all createAndPersistProject failure paths
Matches the openProject convention: the catch block now sends the same notification as the validation-failure path, so transport-level rejections (where the backend may not have reached its own notification send) also surface visible feedback to the user. Expanded the JSDoc to document why the pre-round-trip newDraft() call is safe: dirty=false means all draft data is committed to the active project (or the draft was empty), and dirty=true means the user has already confirmed discarding via DiscardDraftConfirm. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d52a966 commit 63f2a0f

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

src/__tests__/components/modals/ProjectModals.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,10 @@ describe('ProjectModals', () => {
543543
'New',
544544
'Desc',
545545
);
546+
expect(papi.notifications.send).toHaveBeenCalledWith({
547+
message: '%interlinearizer_error_create_project_failed%',
548+
severity: 'error',
549+
});
546550
expect(resetActiveProject).toHaveBeenCalledTimes(1);
547551
});
548552

src/components/modals/ProjectModals.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,17 @@ export default function ProjectModals({
212212
/**
213213
* Creates a new project in storage with the given config and an empty analysis, then seeds the
214214
* draft from it so Save targets the newly created project. This is the "New" flow: the project is
215-
* persisted immediately so it shows up in "Select Interlinear Project" right away. On failure the
216-
* backend has already sent an error notification; here we only log and clear the active project.
215+
* persisted immediately so it shows up in "Select Interlinear Project" right away.
216+
*
217+
* `newDraft` is called synchronously before the backend round-trip so the editor is ready
218+
* immediately regardless of whether persistence succeeds. This is safe: when dirty is `false` (no
219+
* discard confirmation shown), any data in the draft is either already committed to the active
220+
* project or the draft was empty — nothing is lost. When dirty is `true` the
221+
* {@link DiscardDraftConfirm} dialog has already obtained explicit user consent to discard.
222+
*
223+
* Sends an error notification on any failure path (matching the convention in {@link openProject})
224+
* so the user always gets feedback even when the backend error notification does not reach the
225+
* frontend (e.g. a transport-level rejection).
217226
*
218227
* @param config - The configuration collected by the New dialog.
219228
* @returns A promise that resolves once the project is created (or the failure is handled).
@@ -245,6 +254,9 @@ export default function ProjectModals({
245254
}
246255
} catch (e) {
247256
logger.error('Interlinearizer: failed to create project from New dialog', e);
257+
await papi.notifications
258+
.send({ message: '%interlinearizer_error_create_project_failed%', severity: 'error' })
259+
.catch(() => {});
248260
}
249261
if (created) {
250262
setActiveProject(created);

0 commit comments

Comments
 (0)