fix: keep the mail list where it was when coming back to the Pro mail tab

This commit is contained in:
Linus Rath
2026-11-03 08:46:12 +01:00
parent a3027fc301
commit 7bbd148ff5
3 changed files with 46 additions and 2 deletions
+2 -2
View File
@@ -41,6 +41,7 @@ import { toast } from "@/stores/toast-store";
import { confirmMailRemoval } from "@/lib/mail-removal-confirm";
import { markSelectionReadWithUndo, runBatchEmailAction, setSelectionTagWithUndo, toastWithUndo, undoLabels, undoMoveAction } from "@/lib/email-action-toast";
import { useVirtualizer } from "@tanstack/react-virtual";
import { measureVisibleRow } from "@/lib/virtual-row-measure";
import { TagDisplayContext, useMeasuredTagDisplay } from "@/hooks/use-tag-display";
import { SearchChips } from "@/components/search/search-chips";
import { isFilterEmpty, DEFAULT_SEARCH_FILTERS } from "@/lib/jmap/search-utils";
@@ -493,8 +494,7 @@ export function EmailList({
// Keep sub-pixel row heights. The default measurer rounds them, so a
// row could start up to half a pixel inside the one above it and paint
// over that row's divider (at 125% or 150% display scaling).
measureElement: (element, entry) =>
entry?.borderBoxSize?.[0]?.blockSize ?? element.getBoundingClientRect().height,
measureElement: measureVisibleRow,
});
+26
View File
@@ -0,0 +1,26 @@
import { describe, expect, it } from 'vitest';
import type { Virtualizer } from '@tanstack/react-virtual';
import { measureVisibleRow } from '@/lib/virtual-row-measure';
const instance = {
measurementsCache: [{ size: 120 }, { size: 80 }],
indexFromElement: () => 1,
} as unknown as Virtualizer<HTMLDivElement, HTMLElement>;
const entry = (blockSize: number) => ({ borderBoxSize: [{ blockSize, inlineSize: 300 }] }) as unknown as ResizeObserverEntry;
const row = document.createElement('div');
describe('measureVisibleRow', () => {
it('takes the measured height, sub-pixels included', () => {
expect(measureVisibleRow(row, entry(96.5), instance)).toBe(96.5);
});
it('keeps the known height of a row in a hidden list', () => {
expect(measureVisibleRow(row, entry(0), instance)).toBe(80);
});
it('reports 0 for a hidden row it knows nothing about', () => {
const empty = { measurementsCache: [], indexFromElement: () => 0 } as unknown as Virtualizer<HTMLDivElement, HTMLElement>;
expect(measureVisibleRow(row, entry(0), empty)).toBe(0);
});
});
+18
View File
@@ -0,0 +1,18 @@
import type { Virtualizer } from '@tanstack/react-virtual';
/**
* A virtualizer's measureElement with sub-pixel row heights that ignores a row
* measuring 0 because its list is hidden. A Pro tab in the background is
* display:none and its rows report no height: taken at face value the list
* collapses, and coming back it grows row by row and scrolls away from where
* it was. No visible row is 0 high, so the known size stands then.
*/
export function measureVisibleRow<TItem extends Element>(
element: TItem,
entry: ResizeObserverEntry | undefined,
instance: Virtualizer<HTMLDivElement, TItem>,
): number {
const size = entry?.borderBoxSize?.[0]?.blockSize ?? element.getBoundingClientRect().height;
if (size > 0) return size;
return instance.measurementsCache[instance.indexFromElement(element)]?.size ?? size;
}