Stop the reading view rearranging itself as you read #81

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

Closes #69.

Opening a conversation with several unread messages showed them all expanded, each with its unread bar. The moment the auto-mark-read timer fired, every one collapsed except the last, and the bars vanished — so the messages you'd just been handed were taken away again, and the only record of which ones they were went with them.

Both symptoms came from one place: expansion and the bar were derived from $seen, live.

return !e.keywords.$seen || e.id === lastId || messages.length === 1;

Marking read on the server changed what the view thought it was looking at.

What's fixed, and what deliberately isn't

Marking read isn't the problem — opening a thread is the signal that you're reading it, and mbunkus was explicit that switching the setting off isn't the answer he wants. What was wrong was letting a change this view caused alter its own shape underneath the reader.

The thread now remembers which messages were unread when it was opened, and uses that for expansion and for the bar. The set only grows while a thread is open — a message arriving unread joins it — and is discarded on the way to another thread. The server is still marked read on the timer, and the message list still updates. That's his proposed 3.1, 3.2, and a 3.3 that leaves the pane alone.

It's accumulated during render rather than in an effect: it's derived purely from the messages already in hand, adding an id twice does nothing, and an effect would repaint a frame later — which is the flicker this exists to remove.

Verified in the browser

Arithmetic can't demonstrate a timing bug, so this was driven against the running app with markReadDelay: 0 — the harshest setting, where the timer fires immediately.

Six seconds after opening a three-message thread:

was unread at open   e1, e2, e3
server now says      all seen        ← marked read, correctly
view still shows     all expanded, all bars present

Before the fix, two of the three would have collapsed in the first instant.

279 web + 77 server tests, typecheck and build clean.

Merged 2026-08-26 as coffey-labs/ihasmail@72c409f0f5

Rebuilt from: git history, session transcript.

Closes #69. Opening a conversation with several unread messages showed them all expanded, each with its unread bar. The moment the auto-mark-read timer fired, **every one collapsed except the last**, and the bars vanished — so the messages you'd just been handed were taken away again, and the only record of which ones they were went with them. Both symptoms came from one place: expansion and the bar were derived from `$seen`, live. ```js return !e.keywords.$seen || e.id === lastId || messages.length === 1; ``` Marking read on the server changed what the view thought it was looking at. ## What's fixed, and what deliberately isn't Marking read isn't the problem — opening a thread *is* the signal that you're reading it, and mbunkus was explicit that switching the setting off isn't the answer he wants. What was wrong was letting a change **this view caused** alter its own shape underneath the reader. The thread now remembers which messages were unread when it was opened, and uses that for expansion and for the bar. The set only grows while a thread is open — a message arriving unread joins it — and is discarded on the way to another thread. The server is still marked read on the timer, and the message list still updates. That's his proposed 3.1, 3.2, and a 3.3 that leaves the pane alone. It's accumulated during render rather than in an effect: it's derived purely from the messages already in hand, adding an id twice does nothing, and an effect would repaint a frame later — which is the flicker this exists to remove. ## Verified in the browser Arithmetic can't demonstrate a timing bug, so this was driven against the running app with `markReadDelay: 0` — the harshest setting, where the timer fires immediately. Six seconds after opening a three-message thread: ``` was unread at open e1, e2, e3 server now says all seen ← marked read, correctly view still shows all expanded, all bars present ``` Before the fix, two of the three would have collapsed in the first instant. 279 web + 77 server tests, typecheck and build clean. **Merged** 2026-08-26 as coffey-labs/ihasmail@72c409f0f53c <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.