fix: address review feedback on auto-save

- Snapshot fields at start of performSave to avoid race with user edits
  during async save (fields could change between await and lastSaved update)
- Only update lastSaved tracking on successful save, so failed saves
  are retried on the next debounce cycle
- Stop restoring title after Untitled fallback in autoSave(), which was
  re-triggering the auto-save effect
- Capture note count before opening editor in empty-note e2e test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-03-24 23:49:08 +01:00
committed by Fred Cabassut
co-authored by Claude Opus 4.6
parent 07b27c01e7
commit 9d1c898b84
2 changed files with 27 additions and 16 deletions
+21 -10
View File
@@ -83,24 +83,37 @@
async function performSave() {
if (isSaving) return;
if (!title.trim() && !content.trim()) return;
if (!hasUnsavedChanges()) return;
// Snapshot current fields to avoid races with user edits during await
const snap = { title, content, color, checklistMode };
if (!snap.title.trim() && !snap.content.trim()) return;
if (
snap.title === lastSavedTitle &&
snap.content === lastSavedContent &&
snap.color === lastSavedColor &&
snap.checklistMode === lastSavedChecklistMode
) return;
isSaving = true;
try {
if (currentlyNew) {
const created = await createNote({ title, content, color, checklistMode });
const created = await createNote(snap);
if (created) {
noteId = created.id;
currentlyNew = false;
lastSavedTitle = snap.title;
lastSavedContent = snap.content;
lastSavedColor = snap.color;
lastSavedChecklistMode = snap.checklistMode;
}
} else if (noteId) {
await updateNote(noteId, { title, content, color, checklistMode });
const updated = await updateNote(noteId, snap);
if (updated) {
lastSavedTitle = snap.title;
lastSavedContent = snap.content;
lastSavedColor = snap.color;
lastSavedChecklistMode = snap.checklistMode;
}
}
lastSavedTitle = title;
lastSavedContent = content;
lastSavedColor = color;
lastSavedChecklistMode = checklistMode;
} finally {
isSaving = false;
}
@@ -335,10 +348,8 @@
clearTimeout(autoSaveTimer);
if (savingPromise) await savingPromise;
// For feature-triggered saves (image upload, share), use 'Untitled' fallback
const prevTitle = title;
if (!title.trim()) title = 'Untitled';
await performSave();
if (!prevTitle.trim()) title = prevTitle;
return noteId;
}
+6 -6
View File
@@ -41,20 +41,20 @@ test.describe('Auto-save', () => {
});
test('Scenario: Empty note is not auto-saved', async ({ authenticatedPage: page }) => {
// Given the current number of notes
const noteCards = page.locator('[data-testid="note-card"]');
const countBefore = await noteCards.count();
// When the user opens a new note but leaves it empty
await page.getByTestId('new-note-btn').click();
// And waits longer than the auto-save delay
await page.waitForTimeout(3000);
// Then no note card appears in the list
const noteCards = page.locator('[data-testid="note-card"]');
const countBefore = await noteCards.count();
// Close the empty editor
// And closes the empty editor
await page.getByTestId('close-editor-btn').click();
// And no new note was created
// Then no new note was created
const countAfter = await noteCards.count();
expect(countAfter).toBe(countBefore);
});