(file-panel): ask before quit, close or reload drops unsaved file edits (#373) - #401
Conversation
Unsaved edits in the file panel were lost silently when the app quit, the window closed or the page reloaded. Main now holds the window close and a blocked unload until the renderer answers, with a bounded wait so a hung renderer cannot prevent quitting. The renderer lists the dirty tabs (shown, kept aside, any session) in a Save / Discard / Cancel dialog and vetoes beforeunload while a tab is dirty. Closes #373
|
Reviewing |
|
Adversarial review at |
…ter quit is confirmed The renderer now acknowledges an unsaved-edits check on receipt; the bound covers only send to ack, so a slow user, a slow save or a stale-disk confirm no longer counts as yes. The check moves into before-quit, ahead of the PTY, MCP and watcher cleanup, so Cancel leaves the app intact. An approved reload allows exactly one unload, so a timeout yes cannot loop. Refs #373
|
Reviewing |
|
Re-review at |
… question at a time electron-updater starts the installer before it quits, so the unsaved-edits check now runs before quitAndInstall and a Cancel leaves the installer unstarted. A Windows session end approves the quit so logoff does not wait on a dialog. A window close, a quit and an install share one open question. Refs #373
|
Re-review at |
# Conflicts: # CHANGELOG.md
Closes #373
What changed
Quitting, closing the window or reloading while a file tab of the file panel has unsaved edits now asks first. This covers the shown tab, tabs kept aside by #369, and tabs of other sessions.
unsaved-guard.js): the window'scloseevent (every app quit goes through it) is held while the renderer is asked overunsaved-check/unsaved-check-result. Awill-prevent-unload(the renderer'sbeforeunloadveto, which a reload hits) asks the same way and reloads on a yes. No answer within 2.5 s, a crashed or destroyed renderer, or a failed send all answer yes, so a hung renderer cannot keep the app from quitting.file-panel.js): an in-app dialog built on the add-project dialog's classes lists the dirty files with Save / Discard / Cancel. Save writes the shown tab through the viewer's own save (stale-disk confirm included) and the tabs kept aside throughsaveFileForPanelwith their agreed base; a refused save keeps the dialog open with the reason.beforeunloadvetoes an unload while a tab is dirty, unless the user just approved one.ViewerPanel.saveNow()exposes the existing save.Conventional default taken: warn, never persist drafts silently.
Not covered / decisions
destroySessionleavesfilePanelStatealone), so nothing is lost there; the quit check sees those tabs. Deleting a session leaves its state in memory too, so its edits are asked about at quit. No change on those paths.Tests
test/unsaved-guard.test.js(8, main side, fake window and injected timers): hold and approve, no keeps open, no duplicate question, timeout, late answer ignored, crashed renderer, blocked unload reload, approved close lets unload through. Written first; red withCannot find module '../unsaved-guard'.test/dom-file-panel-unsaved-guard.test.js(7, jsdom with the realViewerPanelandfile-panel.js): clean = immediate yes; Cancel; Discard +beforeunload; Save; refused save keeps the dialog; other session plus kept-aside tab;beforeunloadwhile dirty. Written first; red withctx.calls.check is not a function.beforeunloadapproval flag ignored; Discard not setting the approval; the approved-close shortcut removed; Save reporting success on refusal; held tabs not collected.close/will-prevent-unloadsemantics follow the Electron docs and are exercised only against fakes.