forked from mirror/crumbs-original
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:
committed by
Fred Cabassut
co-authored by
Claude Opus 4.6
parent
5e260a7d56
commit
28ec9baf11
@@ -83,24 +83,37 @@
|
|||||||
|
|
||||||
async function performSave() {
|
async function performSave() {
|
||||||
if (isSaving) return;
|
if (isSaving) return;
|
||||||
if (!title.trim() && !content.trim()) return;
|
// Snapshot current fields to avoid races with user edits during await
|
||||||
if (!hasUnsavedChanges()) return;
|
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;
|
isSaving = true;
|
||||||
try {
|
try {
|
||||||
if (currentlyNew) {
|
if (currentlyNew) {
|
||||||
const created = await createNote({ title, content, color, checklistMode });
|
const created = await createNote(snap);
|
||||||
if (created) {
|
if (created) {
|
||||||
noteId = created.id;
|
noteId = created.id;
|
||||||
currentlyNew = false;
|
currentlyNew = false;
|
||||||
|
lastSavedTitle = snap.title;
|
||||||
|
lastSavedContent = snap.content;
|
||||||
|
lastSavedColor = snap.color;
|
||||||
|
lastSavedChecklistMode = snap.checklistMode;
|
||||||
}
|
}
|
||||||
} else if (noteId) {
|
} 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 {
|
} finally {
|
||||||
isSaving = false;
|
isSaving = false;
|
||||||
}
|
}
|
||||||
@@ -335,10 +348,8 @@
|
|||||||
clearTimeout(autoSaveTimer);
|
clearTimeout(autoSaveTimer);
|
||||||
if (savingPromise) await savingPromise;
|
if (savingPromise) await savingPromise;
|
||||||
// For feature-triggered saves (image upload, share), use 'Untitled' fallback
|
// For feature-triggered saves (image upload, share), use 'Untitled' fallback
|
||||||
const prevTitle = title;
|
|
||||||
if (!title.trim()) title = 'Untitled';
|
if (!title.trim()) title = 'Untitled';
|
||||||
await performSave();
|
await performSave();
|
||||||
if (!prevTitle.trim()) title = prevTitle;
|
|
||||||
return noteId;
|
return noteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,20 +41,20 @@ test.describe('Auto-save', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Scenario: Empty note is not auto-saved', async ({ authenticatedPage: page }) => {
|
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
|
// When the user opens a new note but leaves it empty
|
||||||
await page.getByTestId('new-note-btn').click();
|
await page.getByTestId('new-note-btn').click();
|
||||||
|
|
||||||
// And waits longer than the auto-save delay
|
// And waits longer than the auto-save delay
|
||||||
await page.waitForTimeout(3000);
|
await page.waitForTimeout(3000);
|
||||||
|
|
||||||
// Then no note card appears in the list
|
// And closes the empty editor
|
||||||
const noteCards = page.locator('[data-testid="note-card"]');
|
|
||||||
const countBefore = await noteCards.count();
|
|
||||||
|
|
||||||
// Close the empty editor
|
|
||||||
await page.getByTestId('close-editor-btn').click();
|
await page.getByTestId('close-editor-btn').click();
|
||||||
|
|
||||||
// And no new note was created
|
// Then no new note was created
|
||||||
const countAfter = await noteCards.count();
|
const countAfter = await noteCards.count();
|
||||||
expect(countAfter).toBe(countBefore);
|
expect(countAfter).toBe(countBefore);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user