Unsaved-work prompt: after answering Stay, the form that asked stays dead until a reload #518
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#518
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, reading how the double-submit guard and the unsaved-work guard instatic/js/app.jsinteract.Related to #258.
Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atstatic/js/app.js:534-559(the submit listener, with the timed release fordata-downloadforms at:554-558),static/js/app.js:561-563(the release onpageshow) andstatic/js/app.js:664-672(beforeunload); the comment on asking beside a dirty review is atapp.js:584-588and Sign out attemplates/base.html:248-253.templates/jobs/capture_review.htmlhas not moved.The double-submit guard marks a submitted form and releases the mark only on
pageshow. The unsaved-work guard can cancel that very submission: when another form on the page is dirty,beforeunloadasks, and a person who answers Stay stays on the page with nopageshowto come. The submitted form keepsdata-submitted, its buttons keeparia-disabledandpointer-events-none, and every later submit of it is cancelled. The button the person just pressed is dead for the rest of the visit.static/js/app.js:495-520(the submit listener setsform.dataset.submittedand greys the buttons; a second submit hitsif (form.dataset.submitted) { event.preventDefault(); … });static/js/app.js:522-524(the only release, onpageshow; the timed release at:515-519applies todata-downloadforms, and no template carries that attribute now);static/js/app.js:625-633(beforeunloadcallspreventDefault()wheneveranythingDirty()). Nothing lifts the mark when the unload is cancelled.templates/jobs/capture_review.html:185-199) someone corrects a field, so the review form is dirty, then pressesdor clicks Discard this capture. Asking here is intended (app.js:545-549, andtests/e2e/test_unsaved_work.pychecks that the question is asked). They choose Stay to keep the correction. Discard this capture and Discard and next now do nothing, whether clicked, pressed with Enter or reached throughd, whosetarget.click()submits a form the guard refuses.templates/base.html:249-254) and chooses Stay. Sign out is inert until the page is reloaded, which asks the question again, or until the letter is saved.beforeunloadhandler, when it asks, set a flag; on the nextpointerdownorkeydownon the page, release everyform[data-submitted]and clear the flag. If the person leaves, no further input reaches the page; if they stay, their next action finds the form usable again. (AsetTimeout(…, 0)scheduled from the handler also works, but can release the mark in the moment between Leave and the new page arriving.)Done when
tests/e2e/test_unsaved_work.pytypes into the review form of a capture, presses Discard this capture, dismisses thebeforeunloaddialog, presses Discard this capture again and accepts, and asserts that the capture is discarded. It fails today and passes after the fix.The maintainer's list of 2026-10-02 asks for a save guard that prompts a dialog when a page is left unsaved. #657 builds on the guard this issue concerns: it adds Postulo's own dialog for links and buttons on the page, and keeps the browser's prompt for closing the tab, reloading and the address bar.
The dialog removes the in-app route to the dead form described here, because its listener runs before the double-submit mark is set. The browser's prompt still takes the other routes, and the mark is still released only on
pageshow(src/postulo/static/js/app.js:776-778atf09b6010b), so this fix is still needed. #657 asks for it first.From the list of improvements of 2026-10-02.