- default to Inbox perspective (not INBOX mailbox) after first sync
- selectItemForDetail bridges GTD item selection to detail pane by
creating a synthetic ThreadSummary for email items
- reduce GTD toolbar from 5 to 3 buttons (defer, complete, discard),
someday/project remain accessible via keyboard shortcuts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- add .id(selectedThread?.id) to ThreadDetailView to force SwiftUI
NavigationSplitView detail re-evaluation on selection change
- selectMailbox clears selectedThread/messages to avoid stale state
- remove defer/project buttons from folder toolbar (4 buttons instead of 7),
keyboard shortcuts still work
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- connectAccount() and loadExistingAccount() no longer call syncNow() —
the .task modifier on mailView handles initial sync + periodic sync
- add isSyncing guard to prevent concurrent syncNow() calls that race
on the IMAP connection and cause "Previous command interrupted"
- reload mailboxes after each sync (needed on first sync when DB is empty)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- 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>
- 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>
- 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>
- TaskStore directory changed from `tasks-<id>` to `<accountId>/tasks/` per spec
- TaskStore now created before SyncCoordinator and passed as `taskStore:` arg so deferral resurfacing works
- Use `mailStore.databaseWriter` instead of raw `dbPool` for consistency
- Remove unused `deferSelectedItem` stub (all callers use `deferItem(_:until:)` directly)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- MailViewModel.setup() now creates SMTPClient provider when SMTP config is present,
passes it to ActionQueue so send actions work
- ActionQueue is passed to SyncCoordinator so pending actions flush before sync
- Add ensureBodyLoaded() to fetch message body via IMAP before opening compose
- ThreadDetailView uses callback instead of binding for compose requests,
allowing ContentView to fetch body before presenting sheet
- Add SMTPClient dependency to both app targets in project.yml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>