Mail list: a new page starts at the top

Paging while scrolled down kept the old scroll position, so clicking
next dropped you into the middle of the following fifty with nothing to
signal you had moved.

The list is virtualized, so there is no natural anchor to reset — the
scroll container has to be told. MessageList now takes the page number
and scrolls back to the top when it changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
This commit is contained in:
2026-09-21 21:32:15 +02:00
co-authored by Claude Opus 5
parent c5742f19da
commit ea137c32c3
2 changed files with 11 additions and 1 deletions
+10 -1
View File
@@ -1,6 +1,6 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useVirtualizer } from "@tanstack/react-virtual"; import { useVirtualizer } from "@tanstack/react-virtual";
import { useRef } from "react"; import { useEffect, useRef } from "react";
import { useT } from "../../i18n"; import { useT } from "../../i18n";
import { api } from "../../lib/api"; import { api } from "../../lib/api";
@@ -56,6 +56,7 @@ function Paperclip() {
// Virtualized thread list (SPEC §2 list/MessageList.tsx). // Virtualized thread list (SPEC §2 list/MessageList.tsx).
export function MessageList({ export function MessageList({
threads, threads,
page,
selectedId, selectedId,
folder, folder,
onSelect, onSelect,
@@ -69,6 +70,8 @@ export function MessageList({
onToggleCheck, onToggleCheck,
}: { }: {
threads: ThreadSummary[]; threads: ThreadSummary[];
/** Which page of the list this is. Changing it scrolls back to the top. */
page: number;
selectedId?: string; selectedId?: string;
folder: string; folder: string;
onSelect: (id: string) => void; onSelect: (id: string) => void;
@@ -106,6 +109,12 @@ export function MessageList({
overscan: 8, overscan: 8,
}); });
// A new page starts at the top. Paging while scrolled down would otherwise
// drop you into the middle of the next fifty with no sense of having moved.
useEffect(() => {
parentRef.current?.scrollTo({ top: 0 });
}, [page]);
if (threads.length === 0) { if (threads.length === 0) {
return <div className="p-6 text-[15px] text-app-faint">{t("mail.noConversations")}</div>; return <div className="p-6 text-[15px] text-app-faint">{t("mail.noConversations")}</div>;
} }
+1
View File
@@ -842,6 +842,7 @@ export function MailLayout() {
) : ( ) : (
<MessageList <MessageList
threads={pageThreads} threads={pageThreads}
page={page}
selectedId={threadId} selectedId={threadId}
folder={currentFolder} folder={currentFolder}
onSelect={(id) => navigate(`/mail/t/${id}`)} onSelect={(id) => navigate(`/mail/t/${id}`)}