Move focus off a row that has been deleted #80

Closed
opened 2026-08-26 22:15:39 +00:00 by jcoffey-dev · 0 comments
Owner

Closes #71 — both halves, which turn out to be one cause.

Deleting from the keyboard left focusId pointing at a row no longer in the list. afterAction cleared the selection and advanced the open thread, but never touched list focus.

The confirmation "every other message". targetIds() falls back to the focused id, so the second # re-targeted the message the first had just deleted. The optimistic update had already moved that message into Deleted Items — so it read as a permanent delete, and a permanent delete always confirms, whatever "Confirm before deleting" says. The dialog was correct about the message it was asked about; it was asked about the wrong one.

k jumping to the top. moveFocus reads ids.indexOf(focusId), which is -1 for a departed row, and -1 is treated as "before the first row". Add -1 and it clamps to 0.

Both explain the detail that made this diagnosable: "if using the mouse to click on the next message, then no confirmation is requested" — clicking sets focus to a row that exists.

The fix

Focus moves to whatever slid into the deleted row's place, honouring After archiving or deleting (the row below by default, the one above when set to newer), and clears when the folder empties. moveFocus also stops reading a missing row as index 0.

Verified in the browser, not just in arithmetic

Unit tests pin the index maths, but they can't prove the wiring — so this was driven against the running app with confirmDelete: false, the reported condition:

start        focus e4  (index 2)
#            e4 gone,  focus → e5   no dialog
#            e5 gone,  focus → e6   no dialog   ← the reported bug
#            e6 gone,  focus → e9   index still 2
k            focus → e74, index 1               ← up by one, not to the top (e3)

7 new tests covering both directions, the ends of the list, the newer setting, and the empty-folder case. 272 web + 77 server tests, typecheck and build clean.

Merged 2026-08-26 as coffey-labs/ihasmail@6e58c22807

Rebuilt from: git history, session transcript.

Closes #71 — both halves, which turn out to be one cause. Deleting from the keyboard left `focusId` pointing at a row no longer in the list. `afterAction` cleared the selection and advanced the open *thread*, but never touched list focus. **The confirmation "every other message".** `targetIds()` falls back to the focused id, so the second `#` re-targeted the message the first had just deleted. The optimistic update had already moved that message into Deleted Items — so it read as a *permanent* delete, and a permanent delete always confirms, whatever "Confirm before deleting" says. The dialog was correct about the message it was asked about; it was asked about the wrong one. **`k` jumping to the top.** `moveFocus` reads `ids.indexOf(focusId)`, which is `-1` for a departed row, and `-1` is treated as "before the first row". Add `-1` and it clamps to `0`. Both explain the detail that made this diagnosable: *"if using the mouse to click on the next message, then no confirmation is requested"* — clicking sets focus to a row that exists. ## The fix Focus moves to whatever slid into the deleted row's place, honouring **After archiving or deleting** (the row below by default, the one above when set to *newer*), and clears when the folder empties. `moveFocus` also stops reading a missing row as index 0. ## Verified in the browser, not just in arithmetic Unit tests pin the index maths, but they can't prove the wiring — so this was driven against the running app with `confirmDelete: false`, the reported condition: ``` start focus e4 (index 2) # e4 gone, focus → e5 no dialog # e5 gone, focus → e6 no dialog ← the reported bug # e6 gone, focus → e9 index still 2 k focus → e74, index 1 ← up by one, not to the top (e3) ``` 7 new tests covering both directions, the ends of the list, the *newer* setting, and the empty-folder case. 272 web + 77 server tests, typecheck and build clean. **Merged** 2026-08-26 as coffey-labs/ihasmail@6e58c2280758 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.