51 Commits

Author SHA1 Message Date
367359abe6 filter thread list by selected mailbox, fix sidebar selection
- threadSummariesFromDB accepts optional mailboxId filter, uses EXISTS subquery
  to show only threads containing messages in the selected mailbox
- add selectMailbox() and selectPerspective() to MailViewModel for clean
  state transitions between folder view and GTD perspective view
- sidebar highlights selected mailbox/perspective, clears state on switch
- default to INBOX mailbox after first sync

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:44:41 +01:00
fc90d71021 decode RFC 2047 in To/CC names, convert dates to ISO 8601 for correct sorting
- decode RFC 2047 encoded words in To/CC address display names
- convert IMAP RFC 2822 dates to ISO 8601 before storing in MessageRecord,
  so SQLite ORDER BY text comparison sorts chronologically
- strip parenthesized timezone names from dates (e.g. "+0100 (CET)")

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:34:10 +01:00
a71f609f3b decode RFC 2047 subjects/sender names, parse RFC 2822 dates from IMAP envelopes
- decode RFC 2047 encoded words in subject and fromName via RFC2047Decoder
  before storing in MessageRecord (fixes =?utf-8?Q?...?= raw display)
- add RFC 2822 date formatters to Queries.parseDate (EEE, dd MMM yyyy HH:mm:ss Z)
  so IMAP envelope dates parse correctly instead of falling back to distantPast
- parseDate now tries ISO 8601, RFC 2822 with day-of-week, RFC 2822 without

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:30:34 +01:00
0c31bdc152 fix insertMessages: use COUNT(*) instead of MessageRecord.fetchOne for existence check
MessageRecord.fetchOne decodes all columns but the query only selects id,
causing "column not found: accountId". Use COUNT(*) which returns a plain Int.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:24:32 +01:00
df78397870 fix stale IMAP connection: clean up old connection on reconnect, robust disconnect
- connect() now cleans up any stale connection/event loop group before
  creating a new one, preventing leaked resources after failed syncs
- disconnect() uses try? for both channel close and group shutdown so
  "Already closed" errors don't prevent cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:10:53 +01:00
01605a01ec fix FK constraint: skip duplicate messages before thread reconstruction
- insertMessages checks (mailboxId, uid) existence before INSERT, returns
  only actually inserted records
- syncMailbox only runs ThreadReconstructor on newly inserted messages,
  preventing FK violations from stale UUIDs referencing ignored records
- improve error logging to show full error description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 12:02:55 +01:00
ff91e397e8 fix sync: INSERT OR IGNORE for duplicate messages, fix leaked IMAP continuations
- insertMessages uses INSERT OR IGNORE on (mailboxId, uid) conflict instead of
  crashing on UNIQUE constraint when messages are re-fetched after restart
- IMAPResponseHandler.sendCommand resumes any leaked previous continuation before
  registering a new one, preventing DuplicateCommandTag errors
- add channelInactive handler to resume pending continuations on connection drop
- add error type to sync failure log for better diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:58:29 +01:00
1e39a2bd43 fix offline: remove deadlocking syncShutdownGracefully from deinit, limit flag sync range
- remove deinit from IMAPConnection, SMTPConnection — syncShutdownGracefully
  blocks and deadlocks if called on NIO event loop thread
- limit reconcileFlags to last 500 UIDs to avoid overwhelming the connection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 11:44:45 +01:00
d5eaf35665 add attachmentCount to ThreadSummary, paperclip badges in thread and item lists
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:38:29 +01:00
868d99e60e update SMTPClient.send to use formatMultipart when attachments present
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:27:34 +01:00
1b4556b167 add OutgoingAttachment model, attachments field on OutgoingMessage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:26:03 +01:00
8c33d4d4a6 fix raw MIME body display, re-parse stale cached bodies, fix minor review items
- detect and re-fetch bodies containing unparsed MIME content (boundary markers,
  Content-Transfer-Encoding headers) from pre-MIMEParser code path
- fix MIMEParser section numbering: pass cumulative sectionPrefix in nested multiparts
  instead of resetting to empty string
- generate snippet from parsed body text when envelope snippet is missing
- add pendingAction(id:) direct lookup to MailStore, avoid re-fetching all actions
- add updateSnippet method to MailStore
- fix IMAPIdleClient.selectInbox: use incrementing tag counter instead of hardcoded tag
- use static nonisolated(unsafe) ISO8601DateFormatter in ActionQueue (avoid repeated alloc)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 10:33:08 +01:00
31ab18cb2a fix remaining code review issues, add HTML email rendering, charset-aware MIME decoding
- add charset-aware string decoding in MIMEParser (supports UTF-8, Latin-1, Windows-1252, etc.)
- fix prefetchBodies: remove broken ISO8601 date filter that prevented body fetching
- fix ensureBodyLoaded to use fetchFullMessage + MIMEParser instead of broken fetchBody
- add N+1 query fix: inboxMessagesExcludingDeferred uses SQL LEFT JOIN instead of per-message deferral check
- add inboxMessageCountExcludingDeferred for efficient perspective counts
- add unreadMessageCount, totalMessageCount queries to MailStore
- wire mailbox unread/total counts in loadMailboxes (were hardcoded to 0)
- add flag sync: reconcileFlags fetches flags for existing UIDs, updates local read/flagged state
- move account config from UserDefaults to Application Support file, auto-migrate existing config
- render HTML emails by default (toggle to plain text), render plain text as HTML for proper Unicode/emoji
- replace print() with os_log Logger in SyncCoordinator

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 10:05:19 +01:00
10b7cb2fd2 fix code review issues: deferral date format, storeFlags SELECT, event loop leaks, GTD selection tracking
- fix deferral resurfacing using VTODOParser.formatDateOnly instead of ISO8601 (date format mismatch)
- add SELECT mailbox before UID STORE in storeFlags (IMAP protocol requirement)
- pass credentials to SyncCoordinator so IDLE monitoring activates
- add selectedItem tracking to MailViewModel, wire List selection in GTD views
- fix startPeriodicSync to sleep-first, preventing duplicate sync on launch
- add deinit cleanup for EventLoopGroup in IMAPConnection, SMTPConnection
- use separate IMAP client for attachment downloads, avoid shared connection interference
- remove [weak self] from IMAPIdleClient actor Task to prevent orphaned connections
- fix isGTDPerspective to check selectedMailbox instead of items.isEmpty
- fix fetchBody to use complete RFC822 fetch instead of BODY[TEXT]
- reuse single IMAP connection per ActionQueue.flush() batch
- add requiresIMAP to ActionPayload for connection batching
- load task categories from label store instead of hardcoded empty array
- suppress NIOSSLHandler Sendable warnings via Package.swift unsafeFlags
- fix unused variable warnings across codebase

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:13:46 +01:00
3b82e6cd95 fix IMAPIdleHandler thread safety: pass idleTag via init, clean up event loop group on reconnect 2026-03-14 14:20:53 +01:00
55ec465677 bump periodic sync to 15min (IDLE is fast path), add MIMEParser to app targets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:44:41 +01:00
961f87359a add 25 MB per-file attachment size guard to MessageFormatter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:44:09 +01:00
0ba309c525 add attachment download service: fetch section from IMAP, cache to disk
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:43:11 +01:00
004d1ad027 remove inline body fetch from fetchEnvelopes, bodies now come from MIME-aware prefetchBodies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:41:33 +01:00
9a42ae9574 add IDLE monitoring to SyncCoordinator: start/stop with capability check
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:40:47 +01:00
1cbe09c443 add IMAPIdleClient actor with IDLE loop, reconnect backoff, re-IDLE timer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:38:39 +01:00
3ea88f6402 add IMAPIdleHandler: NIO channel handler for IDLE event streaming
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:37:16 +01:00
858cdf5284 replace prefetchBodies with MIME-aware parsing, store attachments during sync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 13:35:14 +01:00
0b9bbe1255 add multipart/mixed formatting, base64 line-wrapped encoding to MessageFormatter 2026-03-14 13:32:23 +01:00
968dd91f80 add fetchFullMessage, fetchSection to IMAPClient for MIME attachment retrieval 2026-03-14 13:31:38 +01:00
0a564a05fd add attachment CRUD to MailStore, wire hasAttachments in Queries 2026-03-14 13:29:58 +01:00
17d47cfddd add v4_attachment migration: sectionPath on attachment, hasAttachments on message 2026-03-14 13:29:29 +01:00
aac7c7c0af add MIMEParser: multipart parsing, content decoding, boundary generation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:09:10 +01:00
cd7d39de9e add RFC 2047 encoded word decoder for MIME filenames
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:07:17 +01:00
a3a99e668f add MIME types: MIMEMessage, MIMEPart, MIMEAttachment, TransferEncoding
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:06:32 +01:00
8095000a56 add MIMEParser module target to Package.swift
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:06:10 +01:00
f28b44d445 move v0.1 artifacts to DELETE/, fix xcode build, bump calver to 2026.03.14
- move backend/, clients/, scripts/ to DELETE/ (v0.1 era, replaced by on-device arch)
- delete feature/v0.1-backend-and-macos branch
- add TaskStore dependency to project.yml
- fix ComposeViewModel deinit concurrency, make toMessageSummary public
- regenerate Xcode project, verify macOS build succeeds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:40:41 +01:00
ac824cd72e add deferral resurfacing to SyncCoordinator (task 7)
Import TaskStore, add optional taskStore property, add resurfaceDeferrals()
that clears expired email deferrals and deferred task deferUntil on each sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
39c0fd40bf add VTODO parser, formatter, TaskStore file I/O with cache rebuild
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
a4f0761f25 add v0.4 models, schema, query extensions for GTD tasks, labels, deferrals
New model types: TaskStatus, TaskSummary, LabelInfo, ItemSummary.
GRDB records: TaskRecord, LabelRecord, ItemLabelRecord, DeferralRecord.
Database migrations v3_task, v3_label, v3_deferral with indexes.
MailStore query extensions for labels, item-labels, deferrals, task cache.
TaskStore module wrapping MailStore. Tests for all v3 tables and records.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 10:31:00 +01:00
cf2b463fd3 add ActionQueue: two-phase enqueue, FIFO flush, retry with max 5 attempts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 05:29:56 +01:00
c38c31bf4d add dot-stuffing for SMTP DATA command (RFC 5321 §4.5.2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 05:25:55 +01:00
54ce92e280 add SMTPClient module: connection layer, message formatter, public API, tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 05:25:03 +01:00
427f197bb3 add IMAP write operations, special folder role detection
Extend IMAPClientProtocol with storeFlags, moveMessage, copyMessage,
expunge, appendMessage, capabilities methods. Implement all six in
IMAPClient actor using NIOIMAPCore typed commands. Add multi-part
command support to IMAPConnection/IMAPCommandRunner for APPEND.
MockIMAPClient tracks all write calls for testing. SyncCoordinator
detects mailbox roles from LIST attributes with name-based fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 05:13:39 +01:00
d659ed67de add v0.3 schema: smtp fields, mailbox roles, drafts, pending actions, mailstore queries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 05:01:17 +01:00
a75738a79d add background body prefetch for recent messages (last 30 days)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:34:38 +01:00
21227b3c78 add real IMAPClient actor with NIO connection, command pipeline, envelope parsing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:12:39 +01:00
ec820b2785 add nio connection layer: tls bootstrap, response handler, command runner
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:05:44 +01:00
300b583695 add sync coordinator: imap → mailstore pipeline with delta sync
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:32:38 +01:00
ce5c985272 add imap client protocol, types, mock for testing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:27:20 +01:00
4670612e75 add FTS5 search, thread summaries, reactive observation streams
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:25:18 +01:00
ca3817c683 add thread reconstruction: simplified JWZ with merge support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:21:15 +01:00
abee491cec add mailstore CRUD: accounts, mailboxes, messages, threads, flags, body
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:18:59 +01:00
0927d9827d add mailstore schema: accounts, mailboxes, messages, threads, FTS5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:16:23 +01:00
f37b287f5e add models module: shared types for accounts, messages, threads, sync
fix Package.swift: remove NIOIMAPCore product reference (only NIOIMAP is exported by swift-nio-imap)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:11:24 +01:00