fix: start another folder, tag or search at the top of its list (bulwarkmail/webmail#1138)

This commit is contained in:
Linus Rath
2026-10-31 04:48:04 +01:00
parent 9067908fef
commit aa25d08dcc
3 changed files with 46 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
import { describe, it, expect } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import { listViewKey } from '../list-view-key';
// The list kept its scroll offset when the folder, tag or search changed, so
// the new list opened somewhere in the middle (webmail #1138).
describe('list scroll position on a view switch (bulwarkmail/webmail#1138)', () => {
it('changes with the folder, the search and the tag, and only with them', () => {
const base = listViewKey('inbox', '', {});
expect(listViewKey('inbox', '', {})).toBe(base);
expect(listViewKey('inbox', ' ', {})).toBe(base);
expect(listViewKey('archive', '', {})).not.toBe(base);
expect(listViewKey('inbox', 'invoice', {})).not.toBe(base);
expect(listViewKey('inbox', '', { keyword: '$label:red' })).not.toBe(base);
});
it('scrolls the list to the top when it changes', () => {
const list = fs.readFileSync(path.resolve(__dirname, '../../screens/EmailListScreen.tsx'), 'utf8');
expect(list).toMatch(/ref=\{listRef\}/);
expect(list).toMatch(/listRef\.current\?\.scrollToOffset\(\{ offset: 0, animated: false \}\);\s*\}, \[viewKey\]\);/);
});
});
+13
View File
@@ -0,0 +1,13 @@
/**
* What the message list shows, as one string: the folder, the search and the
* filters (a tag view is a keyword filter). The list scrolls back to the top
* when it changes, so another folder, tag or search doesn't open at the
* offset the last one was left at (webmail #1138).
*/
export function listViewKey(
currentMailboxId: string | null,
searchQuery: string,
filters: object,
): string {
return JSON.stringify([currentMailboxId, searchQuery.trim(), filters]);
}
+9
View File
@@ -63,6 +63,7 @@ import {
import { isPermanentDelete, confirmPermanentDelete } from '../lib/delete-confirm';
import { confirmMailRemoval } from '../lib/mail-removal-confirm';
import { moveSheetCurrentId } from '../lib/move-targets';
import { listViewKey } from '../lib/list-view-key';
import { draftContextFromEmail, isDraftEmail } from '../lib/draft-context';
import { getFullEmail, emptyMailbox as apiEmptyMailbox } from '../api/email';
import type { RootStackParamList } from '../navigation/types';
@@ -458,6 +459,13 @@ export default function EmailListScreen({
const currentMailboxId = useEmailStore((s) => s.currentMailboxId);
const storeSearchQuery = useEmailStore((s) => s.searchQuery);
const filters = useEmailStore((s) => s.filters);
// Another folder, tag or search starts at the top of its list, not at the
// offset the last one was scrolled to (webmail #1138).
const listRef = React.useRef<FlatList<Email>>(null);
const viewKey = listViewKey(currentMailboxId, storeSearchQuery, filters);
React.useEffect(() => {
listRef.current?.scrollToOffset({ offset: 0, animated: false });
}, [viewKey]);
const accountErrors = useEmailStore((s) => s.accountErrors);
const fetchMailboxes = useEmailStore((s) => s.fetchMailboxes);
const ensureMailboxes = useEmailStore((s) => s.ensureMailboxes);
@@ -1688,6 +1696,7 @@ export default function EmailListScreen({
</View>
) : (
<FlatList
ref={listRef}
data={visibleEmails}
keyExtractor={emailKeyExtractor}
renderItem={renderEmailRow}