Mail list: show who wrote, not everyone involved

The first column listed a thread's `participants`, which the threader builds from
from + to + cc — so every ordinary email read "Sender, You", and the +N badge
beside the avatar counted the user themselves.

`participants` cannot simply be narrowed: the subject-fallback join matches an
incoming message's recipients against it (threader._by_subject), so dropping
recipients would change how mail is threaded. Hence a separate `threads.senders`,
holding only from addresses, newest writer first — which is also the right thing
for a conversation where several people replied.

Migration 0019 backfills it, so existing threads do not wait to be touched again.
The list falls back to `participants` if a row somehow has no senders, rather
than rendering an empty column.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
This commit is contained in:
2026-09-18 18:27:42 +02:00
co-authored by Claude Opus 5
parent e58a2f0ef4
commit 550b8cf503
7 changed files with 86 additions and 4 deletions
+6 -4
View File
@@ -119,10 +119,12 @@ export function MessageList({
const selected = selectedId === thread.id;
const checked = checkedIds.has(thread.id);
const dimmed = dimmedIds?.has(thread.id) ?? false;
const primary = thread.participants[0];
const names =
thread.participants.map((p) => p.name || p.email).join(", ") || "(no one)";
const extra = Math.max(0, thread.participants.length - 1);
// Senders, not participants: the latter includes recipients, so every
// ordinary email read "Sender, You" and the +N badge counted the user.
const people = thread.senders.length ? thread.senders : thread.participants;
const primary = people[0];
const names = people.map((p) => p.name || p.email).join(", ") || "(no one)";
const extra = Math.max(0, people.length - 1);
const avatarBg = `hsl(${hue(primary?.email || primary?.name || "?")} 45% 45%)`;
const stop = (fn: () => void) => (e: React.MouseEvent) => {
e.stopPropagation();
+3
View File
@@ -31,6 +31,9 @@ export interface ThreadSummary {
id: string;
subject: string;
snippet: string;
/** Who wrote in the thread, newest first. The list shows these. */
senders: Address[];
/** Everyone involved, recipients included — used for threading, not display. */
participants: Address[];
account_ids: string[];
message_count: number;