Keep the full copy of an email the server says changed
The reading pane emptied and refilled when a thread was marked read. On an HTML message that is a flash to the app's own background and out again, which is what remained of #100 once the message view stopped rebuilding its body. `applyChanges` dropped `fullIds` for every email the server reported as updated, so the next read would fetch it again. But the reading pane renders only the emails it holds in full. Dropping one took the message out of the open thread until the refetch at the end of the same function put it back -- and marking as read causes exactly that, because the server echoes our own change back as an update. The gap is a round trip, which is why it is plainly visible against a real server. Nothing is lost by keeping the copy. RFC 8621 makes every property of an Email immutable except `keywords` and `mailboxIds` -- the id is derived from the content, so a body cannot change beneath one -- and both are in LIST_PROPS, which the refresh immediately below merges over the cached copy. The eviction only ever cost the message its place in the thread. On the evidence, since I got this wrong once already by trusting a reproduction that did not exist. This is reasoned from the code and matched against the reported symptom -- "the pane empties and comes back", which is precisely what removing an email from the thread and refetching it looks like. It is not backed by a local reproduction: the mock never ran this path at all, because `Email/set` announced nothing and `Email/changes` always answered empty. That is being fixed separately, and it is why every check made here has been against a server that never reported the change being made.
This commit is contained in:
+21
-4
@@ -833,10 +833,27 @@ export const useMail = create<MailState>((set, get) => ({
|
||||
delete next[id];
|
||||
delete nextFull[id];
|
||||
}
|
||||
// Drop cached versions of updated emails so they're refetched lazily.
|
||||
for (const id of updated) {
|
||||
if (next[id] && nextFull[id]) delete nextFull[id];
|
||||
}
|
||||
/*
|
||||
* The full copy of an updated email is deliberately kept.
|
||||
*
|
||||
* This used to drop it so the next read would fetch it again. But
|
||||
* the reading pane renders only the emails it holds in full, so
|
||||
* dropping one took the message out of the open thread until the
|
||||
* refetch at the end of this function put it back. The pane emptied
|
||||
* and refilled -- on an HTML message, a flash to the app's own
|
||||
* background and out again, which is what was left of #100 after
|
||||
* the message view stopped rebuilding its body.
|
||||
*
|
||||
* Marking as read causes exactly this: the server echoes our own
|
||||
* change back as an update.
|
||||
*
|
||||
* Nothing is lost by keeping it. RFC 8621 makes every property of
|
||||
* an Email immutable except `keywords` and `mailboxIds` -- the id
|
||||
* is derived from the content, so a body cannot change beneath one
|
||||
* -- and both are in LIST_PROPS, which the refresh immediately
|
||||
* below merges over the cached copy. The eviction only ever cost
|
||||
* the message its place in the thread.
|
||||
*/
|
||||
return { emails: next, fullIds: nextFull, emailState: since };
|
||||
});
|
||||
// Refresh the list-level props of updated/cached emails.
|
||||
|
||||
Reference in New Issue
Block a user