baaa5658dee6
fix data loss in +add migration: source migration patch from saved entry value not draft; only commit drafts that are non-empty and changed (v2.33.4)
- time
- 2026-05-07 01:10 UTC · 5 months ago
- machine
- koh2
- parent
- 4c8f4b2cdb09
↓ peach-browser-baaa5658.tar.gzatlas at this save
files changed3 files
manifest.firefox.json+1 −1manifest.json+1 −1src/popup/views/Entry.tsx+27 −19manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach",4- "version": "2.33.3",4+ "version": "2.33.4",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach",4- "version": "2.33.3",4+ "version": "2.33.4",55 "description": "Zero-knowledge password manager",66 "action": {77 "default_popup": "src/popup/index.html",src/popup/views/Entry.tsx+27 −19
@@ -267,58 +267,66 @@267267 setShowGenerator(false);268268 }269269 270- // Switch from one identity slot to the other while editing.271- // If we're in the username slot with an email-shaped draft and no separate272- // email yet, migrate that value into entry.email so the now-empty username273- // slot can hold a plain username — this is the legacy-data fix-up path.270+ // Switch from one identity slot to the other.271+ //272+ // Migration source is the SAVED entry value, never the live draft — using273+ // the draft caused data loss when the user typed or cleared the input274+ // before clicking +add (the migration would skip and a later Save would275+ // overwrite the original email-shape value with whatever was in the input).276+ //277+ // Drafts only get committed when they are non-empty AND actually changed,278+ // so an empty input never accidentally clears a saved field.274279 async function addOtherIdentity(target: "username" | "email") {275280 const entry = props.entry;276281 if (!entry) return;277282 const current = editingField();278- const value = draft().trim();279- if (current === "username" && target === "email") {280- // Commit current username draft (if changed), then open email editor.281- if (value !== (entry.username ?? "")) {283+ const draftValue = draft().trim();284+ const savedUsername = entry.username ?? "";285+ const savedEmail = entry.email ?? "";286+287+ if (current === "username" && target === "username") {288+ // Legacy fix-up: saved username is email-shaped and no separate email289+ // exists yet. Move it to entry.email and free the username slot.290+ if (savedUsername.includes("@") && !entry.email) {282291 setSaving(true);283292 try {284293 await sendMessage({285294 type: "UPDATE_ENTRY",286- payload: { id: entry.id, patch: { username: value } },295+ payload: { id: entry.id, patch: { email: savedUsername, username: "" } },287296 });288297 await props.onFieldSaved();289298 } finally {290299 setSaving(false);291300 }292301 }293- startEdit("email", "");302+ startEdit("username", "");294303 return;295304 }296- if (current === "username" && target === "username") {297- // Already editing username — only meaningful when the saved username298- // is email-shaped. Migrate it to entry.email and reset the slot.299- if (value.includes("@") && !entry.email) {305+306+ if (current === "username" && target === "email") {307+ if (draftValue && draftValue !== savedUsername) {300308 setSaving(true);301309 try {302310 await sendMessage({303311 type: "UPDATE_ENTRY",304- payload: { id: entry.id, patch: { email: value, username: "" } },312+ payload: { id: entry.id, patch: { username: draftValue } },305313 });306314 await props.onFieldSaved();307315 } finally {308316 setSaving(false);309317 }310318 }311- startEdit("username", "");319+ startEdit("email", "");312320 return;313321 }322+314323 if (current === "email" && target === "username") {315- // Commit current email draft (if changed), then open username editor.316- if (value !== (entry.email ?? "")) {324+ if (draftValue && draftValue !== savedEmail) {317325 setSaving(true);318326 try {319327 await sendMessage({320328 type: "UPDATE_ENTRY",321- payload: { id: entry.id, patch: { email: value } },329+ payload: { id: entry.id, patch: { email: draftValue } },322330 });323331 await props.onFieldSaved();324332 } finally {