email: group messages into conversations (Gmail-style threading)

Hybrid grouping via a new thread_id column: new mail threads exactly on
References/In-Reply-To (id is sha1(Message-Id), so a referenced id hashes to
the ancestor's own id); already-synced mail is backfilled with a
normalized-subject + counterpart key. Folder views collapse to one row per
thread with a count badge; the reader shows the thread as a collapsible stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 08:21:40 +00:00
co-authored by Claude Opus 4.8
parent 18d69616d2
commit 58373c9d59
6 changed files with 321 additions and 103 deletions
+10
View File
@@ -9,6 +9,9 @@ export type EmailSummary = {
read?: boolean;
fromDomain?: string;
labels?: string[];
// Conversation grouping: total messages in the thread, and how many are unread (folder view only).
threadCount?: number;
threadUnread?: number;
};
export type EmailMessage = EmailSummary & {
@@ -17,3 +20,10 @@ export type EmailMessage = EmailSummary & {
text?: string;
attachments: Array<{ filename: string; size: number; contentType: string }>;
};
// A full conversation: every non-deleted message in the thread, oldest first.
export type EmailThread = {
id: string; // the representative message id used to open the thread
subject: string;
messages: EmailMessage[];
};
+1 -1
View File
@@ -2,7 +2,7 @@ export type {} from './globals';
export * from 'officerdb/types';
export type { Job, JobStep, JobStatus, JobStepStatus, JobProgress } from './queue';
export type { EmailSummary, EmailMessage } from './email';
export type { EmailSummary, EmailMessage, EmailThread } from './email';
export type SelectOption = { value: number | string; label?: string; href?: string };