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
36 lines
1.1 KiB
Python
36 lines
1.1 KiB
Python
"""One-off: recover delivery_id for messages whose stored Message-ID was
|
|
synthesized (§7.1 cross-account duplicate). The real Message-ID is still in the
|
|
raw blob, so re-parse it. Non-synthetic rows were already backfilled by migration
|
|
0011; here we only touch rows left with delivery_id IS NULL that have a real
|
|
Message-ID in their raw bytes."""
|
|
|
|
import asyncio
|
|
|
|
from sqlalchemy import select
|
|
|
|
from app.db import session_scope
|
|
from app.indexer import blobstore
|
|
from app.indexer.mime import parse_message
|
|
from app.models import Message
|
|
|
|
|
|
async def main() -> None:
|
|
async with session_scope() as session:
|
|
rows = list(
|
|
await session.scalars(select(Message).where(Message.delivery_id.is_(None)))
|
|
)
|
|
fixed = 0
|
|
for m in rows:
|
|
try:
|
|
raw = blobstore.load(m.user_id, m.blob_sha256)
|
|
except FileNotFoundError:
|
|
continue
|
|
parsed = parse_message(raw)
|
|
if parsed.message_id:
|
|
m.delivery_id = parsed.message_id
|
|
fixed += 1
|
|
print(f"scanned {len(rows)} null rows, set delivery_id on {fixed}")
|
|
|
|
|
|
asyncio.run(main())
|