Files
webmail/lib/list-step.ts
T

40 lines
1.5 KiB
TypeScript

import type { Email } from '@/lib/jmap/types';
import { emailKeyFor } from '@/lib/thread-utils';
/** One row of the message list: its own key and the keys it stands for. */
export interface ListStepRow {
key: string;
members: string[];
}
/**
* The message j / k open: the one after (`direction` 1) or before (-1) the
* message `fromKey` names, staying put at either end. `fromKey` is the open
* message, or the last one opened when the reader was closed, so j after
* Escape goes on from there. Without a place in the list, j starts at the
* top and k at the bottom.
*
* With `rows` (the list's rows in screen order) a conversation counts once,
* and opens at its newest message, as a click on its row does: the reading
* pane shows the rest of it.
*/
export function stepListEmail(
emails: readonly Email[],
fromKey: string | null | undefined,
direction: 1 | -1,
rows?: readonly ListStepRow[] | null,
): Email | null {
const byKey = new Map(emails.map((e) => [emailKeyFor(e), e] as const));
const order: readonly ListStepRow[] = rows && rows.length > 0
? rows.filter((row) => byKey.has(row.key))
: emails.map((e) => ({ key: emailKeyFor(e), members: [] }));
if (order.length === 0) return null;
const at = fromKey
? order.findIndex((row) => row.key === fromKey || row.members.includes(fromKey))
: -1;
const target = at < 0
? (direction === 1 ? 0 : order.length - 1)
: Math.min(order.length - 1, Math.max(0, at + direction));
return byKey.get(order[target].key) ?? null;
}