frontend: redesign the thread reading panel
Rework the conversation view to match the new dark-theme mockups: - Subject bar gains a print button and an expand/collapse-all toggle (default action expands all; collapsing leaves only the newest message open). - Collapsed messages are compact single-line cards (40px avatar, colored sender name, snippet, relative date) that overlap into a stack; alignment follows the sort order (top edge for oldest-first, bottom edge for newest-first) and the always-open newest card overlaps its neighbour in both orders. - Expanded messages show sender+address (whole header row toggles collapse), a detail disclosure (From/To/Cc/Subject/Date/Size), the message location (Inbox/Sent), a read/unread circle (per-message flag toggle), a quick Reply/Reply-all, and an Actions menu (reply/forward/print/delete). The Reply/Reply all/Forward bar now sits inside the newest card above a divider. - Message body: the quoted original is hidden by default with a "•••" toggle injected after the "On … wrote:" attribution; the iframe now collapses-then- measures so it shrinks back when the quote is hidden. - Avatars use the sender's IMAP-account colour when the address matches one of our accounts, else a stable per-sender hue; two initials for multi-word names. - Brighter dark-theme border token; new print/expand-all/collapse-all icons. Backend: add size_bytes to the message envelope (shown in message details). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ function msg(id: string, minutes: number, unread = false): MessageEnvelope {
|
||||
is_starred: false,
|
||||
has_attachments: false,
|
||||
has_remote_images: false,
|
||||
size_bytes: 1024,
|
||||
snippet: `snippet ${id}`,
|
||||
};
|
||||
}
|
||||
@@ -89,8 +90,8 @@ describe("ThreadView direction-agnostic layout (SPEC §9.4)", () => {
|
||||
const messages = [msg("a", 0), msg("b", 1, true), msg("c", 2)];
|
||||
const { container } = render(wrap(<ThreadView subject="Subject" messages={messages} order="asc" />));
|
||||
// An expanded message renders a MessageBody (iframe/loading/error); collapsed does not.
|
||||
const aNode = screen.getByText("snippet a").closest('[data-testid="message"]') as HTMLElement;
|
||||
const bNode = screen.getByText("snippet b").closest('[data-testid="message"]') as HTMLElement;
|
||||
const aNode = container.querySelector('[data-message-id="a"]') as HTMLElement;
|
||||
const bNode = container.querySelector('[data-message-id="b"]') as HTMLElement;
|
||||
const hasBody = (el: HTMLElement) =>
|
||||
el.querySelector("iframe") !== null ||
|
||||
/Loading thread|Couldn't load/i.test(el.textContent ?? "");
|
||||
|
||||
Reference in New Issue
Block a user