From a2f281a3a227a05ea2898818ee7181a6db5bd193 Mon Sep 17 00:00:00 2001 From: Rajat Saxena Date: Tue, 3 Nov 2026 13:52:08 +0530 Subject: [PATCH 1/2] fix(web): stop the editor asking to save after a test send saved the email The 'Send test' dialog saves the document through the editor's actions prop, which called onSave directly. That left the unsaved-changes flag set, so leaving the editor showed 'Save your changes?' for an email that was already saved. Every save now goes through one persist helper that clears the flag. Co-Authored-By: Claude Opus 5.5 --- .../dashboard/email-editor-screen.test.tsx | 27 +++++++++++++++++++ .../dashboard/email-editor-screen.tsx | 19 ++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/apps/web/components/dashboard/email-editor-screen.test.tsx b/apps/web/components/dashboard/email-editor-screen.test.tsx index b3f3e12..3e1befc 100644 --- a/apps/web/components/dashboard/email-editor-screen.test.tsx +++ b/apps/web/components/dashboard/email-editor-screen.test.tsx @@ -135,6 +135,33 @@ describe("EmailEditorScreen", () => { ).toBe(false); }); + it("doesn't ask to save again after a save made from an action", async () => { + Object.defineProperty(window.history, "length", { configurable: true, value: 2 }); + const onSave = mock().mockResolvedValue(undefined); + render( + ( + + )} + />, + ); + + fireEvent.click(screen.getByText("Change document")); + fireEvent.click(screen.getByRole("button", { name: "Send test" })); + await waitFor(() => expect(onSave).toHaveBeenCalledWith(mocks.changedContent)); + + fireEvent.click(screen.getByRole("button", { name: "Exit editor" })); + + expect(screen.queryByText("Save your changes?")).toBeNull(); + expect(mocks.back).toHaveBeenCalled(); + }); + it("uses the fallback route for a deep-linked editor", () => { Object.defineProperty(window.history, "length", { configurable: true, diff --git a/apps/web/components/dashboard/email-editor-screen.tsx b/apps/web/components/dashboard/email-editor-screen.tsx index 6b30604..e5cdc91 100644 --- a/apps/web/components/dashboard/email-editor-screen.tsx +++ b/apps/web/components/dashboard/email-editor-screen.tsx @@ -303,19 +303,30 @@ export function EmailEditorScreen({ } } + /** Persists the current document, throwing on failure. Every save goes + * through here, so a save from an action (such as a test send) also counts + * as saved for the unsaved-changes check. */ + async function persist() { + contentChanged.current = false; + try { + await onSave(contentRef.current); + } catch (err) { + contentChanged.current = true; + throw err; + } + } + /** Saves, returning whether it worked. */ async function save(): Promise { if (saving) return false; setSaving(true); setError(null); try { - contentChanged.current = false; - await onSave(contentRef.current); + await persist(); setSaved(true); setTimeout(() => setSaved(false), 2000); return true; } catch (err) { - contentChanged.current = true; setError(err instanceof ApiError ? err.message : "Failed to save email"); return false; } finally { @@ -337,7 +348,7 @@ export function EmailEditorScreen({
- {actions?.(() => onSave(contentRef.current))} + {actions?.(persist)}