Files
webmail-client/backend/app/api/schemas.py
T
lukasandClaude Opus 4.8 b4e07f11aa collapse cross-account dupes, Lato UI font, mail-list selection & density tuning
Backend:
- Collapse identical cross-account deliveries in a thread: new messages.delivery_id
  (migration 0011) preserves the real Message-ID even when message_id_hdr is
  synthesized (§7.1); get_thread groups by it and reports also_in_account_ids; the
  list count counts distinct deliveries. Backfill script for existing rows.

Frontend:
- Bundle Lato (SIL OFL) as the UI font; unread mail-list rows use Black weight.
- Mail-list selection: hover-avatar checkbox, tri-state "select all", bulk
  Archive/Pin/Delete (Restore/Delete in Archive/Trash/Spam) with a count in the
  reading pane; unread blue dot; "also delivered to" note in the thread.
- Spacing/size tuning: sender-column width, icon gaps, avatar size, bar heights,
  and a round of -1px font sizes across the sidebar, mail list, and reading pane.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U72FZiF1W1pb8XxyWBTREA
2026-07-22 21:49:48 +02:00

97 lines
1.9 KiB
Python

"""Shared API response models (SPEC §9.1)."""
from __future__ import annotations
import datetime as dt
import uuid
from pydantic import BaseModel
class Address(BaseModel):
name: str = ""
email: str = ""
class ThreadSummary(BaseModel):
id: uuid.UUID
subject: str
snippet: str
participants: list[Address]
account_ids: list[uuid.UUID]
message_count: int
unread_count: int
has_attachments: bool
pinned: bool
last_message_at: dt.datetime
class ThreadListOut(BaseModel):
threads: list[ThreadSummary]
cursor: str | None = None
class AttachmentOut(BaseModel):
id: uuid.UUID
filename: str
mime_type: str
size_bytes: int
class MessageEnvelope(BaseModel):
id: uuid.UUID
account_id: uuid.UUID
mailbox_id: uuid.UUID
thread_id: uuid.UUID | None
subject: str
from_addr: list[Address]
to_addr: list[Address]
cc_addr: list[Address]
bcc_addr: list[Address]
date: dt.datetime
flags: list[str]
is_unread: bool
is_starred: bool
has_attachments: bool
has_remote_images: bool
size_bytes: int
snippet: str
# Other accounts this same delivery also landed in (collapsed duplicates).
also_in_account_ids: list[uuid.UUID] = []
class ThreadDetail(BaseModel):
id: uuid.UUID
subject: str
order: str # "asc" | "desc"
pinned: bool
messages: list[MessageEnvelope] # always ASC (SPEC §9.4)
class FlagChangeRequest(BaseModel):
add: list[str] = []
remove: list[str] = []
class MoveRequest(BaseModel):
mailbox_id: uuid.UUID
class AccountOut(BaseModel):
id: uuid.UUID
email: str
display_name: str
color: str
state: str
signature: str = ""
class SearchThreadHit(BaseModel):
thread: ThreadSummary
highlights: dict[str, str]
class SearchOut(BaseModel):
threads: list[SearchThreadHit]
cursor: str | None = None