Confirm irreversible deletes, which sync just made far more consequential
The trash model itself was already right and needed no change: notes soft- delete (`trashed` locally, `deleted_at` server-side), Trash is a real view, restore works, permanent deletion is a separate second step only offered on an already-trashed note, `trash()` shows an Undo toast, and nothing auto- purges — trash persists until someone acts. Sync carries all of it: a trashed note syncs WITH its content, and only `purged_at` deletes a client's copy. What was missing is the guard on the irreversible step. "Delete forever" and label deletion were one click, silent, with no confirmation — and M10.7 has changed what that costs. Before, a mis-click lost a note on one machine. Now it pushes a tombstone that deletes it from every linked device, and the local tombstone survives to make sure it gets there. Both guards live in the STORE, not the call sites: NoteCard and NoteEditor both offer delete-forever, and duplicating the copy is how two prompts drift until one of them stops matching what actually happens. The copy names the real consequence — "deleted from every device you sync with" — because that's the part a user cannot infer from a button in a Trash view. The label prompt also says the notes themselves are kept, since that's what people actually worry about when deleting a label. Labels deliberately get a confirmation but NOT a trash of their own. A label is organization, not content; the reversible middle step notes get would be ceremony around something that costs nothing to recreate. Saved-filter deletion already confirmed (AppShell), so these two were the outliers, not a new convention. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
This commit is contained in:
@@ -47,6 +47,19 @@ export const useLabelsStore = defineStore("labels", () => {
|
||||
}
|
||||
|
||||
async function remove(id: string): Promise<void> {
|
||||
// Labels have no trash of their own — a label is organization, not content, so
|
||||
// the reversible middle step notes get would be ceremony. But deleting one is
|
||||
// still irreversible and now reaches every synced device, so it asks first. The
|
||||
// notes themselves survive; only the membership goes, which is the part people
|
||||
// most need reassuring about.
|
||||
const label = items.value.find((lb) => lb.id === id);
|
||||
const subject = label ? `the label "${label.name}"` : "this label";
|
||||
const confirmed = window.confirm(
|
||||
`Delete ${subject}?\n\n` +
|
||||
"It will be removed from every note that uses it, on every device you sync " +
|
||||
"with. The notes themselves are kept.",
|
||||
);
|
||||
if (!confirmed) return;
|
||||
await repo.labels.remove(id);
|
||||
items.value = items.value.filter((lb) => lb.id !== id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user