Open a conversation on its first unread message
Selecting a thread put you at the newest message. Anything unread above that sat off the top of the pane with nothing to announce it, and the only way to find out was to scroll up -- by which time the auto-mark-read timer had marked the whole thread read anyway, so scrolling up meant scrolling up to mail already counted as seen (#87). Opening at the bottom is right when there is nothing to catch up on and wrong the moment there is. The pane now opens on the oldest message that was unread when the thread was opened, and falls back to the newest when the thread has already been read. mbunkus's out-of-order case is the one that rules out guessing at a position. A participant whose server could not connect for hours delivers a message long after it was written, and it lands in the middle of a conversation that has already moved past it -- so "second to last", or any other fixed offset from the end, finds nothing. Reading the unread set is the only thing that does. Two cases leave the pane where it is: - a single message, which is already the whole pane - the first unread being the first message, where the top of the pane shows it anyway, together with the subject; scrolling to it would push the subject off for nothing It reads the set captured when the thread was opened rather than live `$seen` state, for the same reason expansion does (#69): the mark-read timer must not change the shape of what you are looking at. That also makes the landing stable, because everything above the first unread message is a collapsed row of fixed height -- nothing up there reflows after the scroll. The mock grows a thread that reproduces it: seven messages with the unread one second, four more behind it. Verified against it. Opening the thread lands the unread message flush against the top of the pane at scrollTop 158; the old scroll to the newest message put it at 445, with 287px of the message -- header, sender and unread bar included -- above the fold. On a thread whose first message is the unread one the pane stays at 0 with the subject in view, where before it would have scrolled 333. Once the thread is read, reopening it goes back to the newest message.
This commit is contained in:
@@ -10,6 +10,7 @@ import { MenuItem, MenuSep, Popover, useMenu } from "@/ui/popover";
|
||||
import { Spinner } from "@/ui/misc";
|
||||
import { client } from "@/jmap/client";
|
||||
import { LabelPicker } from "./LabelPicker";
|
||||
import { threadScrollTarget } from "@/lib/threadScroll";
|
||||
|
||||
interface Props {
|
||||
threadId: Id;
|
||||
@@ -115,11 +116,13 @@ export function ThreadView({ threadId, mailboxId, onBack, actions, onNavigate, h
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [messages.map((m) => m.id + (m.keywords.$seen ? "1" : "0")).join(","), settings.markReadDelay]);
|
||||
|
||||
// Scroll last expanded into view on load
|
||||
// Open on the first unread message rather than the newest one (#87).
|
||||
useEffect(() => {
|
||||
if (!messages.length || !scrollRef.current) return;
|
||||
const el = scrollRef.current.querySelector<HTMLElement>(`[data-msg-id="${CSS.escape(lastId ?? "")}"]`);
|
||||
if (el && messages.length > 1) el.scrollIntoView({ block: "start" });
|
||||
const target = threadScrollTarget(messages, wasUnread);
|
||||
if (!target) return;
|
||||
const el = scrollRef.current.querySelector<HTMLElement>(`[data-msg-id="${CSS.escape(target)}"]`);
|
||||
el?.scrollIntoView({ block: "start" });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [threadId, messages.length > 0]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user