Redesign setup wizard — auth first, cloud workspace selection (v1.35.0)
Details
Reorder the first-run setup wizard so authentication (License step)
happens before workspace creation. This enables users who already have
a cloud workspace from another device (e.g., Android) to connect to it
during initial setup instead of always creating a new empty workspace.
New flow: Welcome → License → Workspace → DataDirectory → [CloudWorkspaces] → Password → EmergencyKit → Complete
Key changes:
SetupStep enum reordered: License now comes after Welcome (position 2)
instead of after DataDirectory (position 4). CloudWorkspaces is a new
conditional step shown when the user selects PrivStack Cloud and has
existing cloud workspaces.
TrialResponse DTO extended with access_token, refresh_token, user_id,
and cloud_config fields to receive auth tokens from the server's trial
verification endpoint.
ListCloudWorkspacesAsync added to PrivStackApiClient — a direct HTTP
GET /api/cloud/workspaces call that works before FFI initialization,
needing only an access token.
Both auth flows (OAuth sign-in and trial verification) now capture
cloud sync tokens (CloudSyncAccessToken, CloudSyncRefreshToken,
CloudSyncConfigJson, CloudSyncUserId) and set IsCloudSyncAvailable
when cloud_config is present.
DataDirectory step gains a PrivStack Cloud option (visible when
authenticated). When selected, the wizard fetches cloud workspaces
via the API client and either shows the CloudWorkspaces picker step
(if existing workspaces are found) or falls through to create a new
cloud workspace automatically.
InitializeServiceAndContinue updated: after runtime initialization,
if PrivStack Cloud is selected, it calls Configure + AuthenticateWithTokens
on the ICloudSyncService, then either connects to the selected existing
cloud workspace or registers a new one, and persists the CloudWorkspaceId
+ SyncTier on the workspace record.
ICloudSyncService injected into the SetupWizardViewModel constructor.
AXAML rewritten to match the new step order with the License step
content block before Workspace, a new PrivStack Cloud card in the
DataDirectory options, and a new CloudWorkspaces step with workspace
list + "Create New Workspace" option.
Add IMAP IDLE and inline image support (v1.21.0)
Details
Adds real-time new message notifications via IMAP IDLE for Inbox folders.
ImapIdleService maintains persistent connections per account, re-IDLEs every
25 minutes per IMAP spec, with exponential backoff reconnect. Falls back to
polling when IDLE is not supported. EmailSyncManager integrates IDLE alongside
existing polling — IDLE triggers immediate incremental sync on CountChanged.
Adds inline image support for HTML emails. FetchBodyAsync now extracts inline
MimePart resources with ContentId and converts them to base64 data URIs.
HtmlSanitizer replaces cid: references in img src attributes with the
corresponding data URIs. InlineImages dictionary flows through the body cache
(EmailCachedBody) for offline access and is bound through HtmlMessageRenderer
to the reading pane view.
Email v1.20.0: Conversation threading with MessageId/InReplyTo chains
Details
Phase 5 of full-featured email client implementation:
- New EmailThread model: ThreadId, Messages (sorted newest-first),
LatestMessage, Count, IsExpanded, HasUnread, Participants, Subject
properties for thread header display
- New EmailThreadingService: groups messages into conversation threads
using a 5-phase algorithm: (1) build MessageId lookup, (2) follow
InReplyTo chains to find thread roots with cycle detection, (3) group
by root, (4) merge subject-based groups with MessageId-based groups,
(5) deduplicate and sort. Fallback to normalized subject matching
(strips Re:/Fwd:/Fw: prefixes) for messages without threading headers
- New EmailViewModel.Threading.cs: IsThreadView toggle, Threads collection,
SelectedThread, ExpandThread/SelectThreadMessage commands, RebuildThreads
called after filter changes when thread view is enabled
- InReplyTo promoted from transient to persisted field on EmailMessage with
JsonPropertyName("in_reply_to") and new indexed field in entity schema
for efficient thread lookups
- ImapSyncService.MapToEmailMessage now captures Envelope.InReplyTo during
header sync so threading data is available from first sync
- Thread view toggle added to gear menu settings
- Threading integrates with existing filter pipeline: ApplyFilters()
automatically rebuilds threads when IsThreadView is true
Email v1.19.0: Save sent to Sent folder, draft auto-save persistence
Details
Phase 4 of full-featured email client implementation:
- New ImapSyncService.Append.cs: IMAP APPEND operation for saving MimeMessages
to server folders (Sent, Drafts) with configurable flags, returns server-
assigned UID for draft replacement tracking
- Made SmtpSendService.BuildMimeMessage public so ComposeViewModel can reuse
it for building MimeMessages to save to Sent/Drafts without duplication
- ComposeViewModel rewritten with draft persistence: 30-second auto-save timer
saves to Drafts folder via IMAP APPEND with Draft+Seen flags, tracks UID to
replace old draft on re-save, deletes server draft after successful send
- Save-to-Sent: after SMTP send completes, fire-and-forget APPEND to Sent
folder with Seen flag so sent messages appear on the server immediately
- ComposeMessage model extended with DraftUid and DraftFolderPath fields
for tracking the server-side draft across save cycles
- New OpenDraft command: when a message in the Drafts folder is selected,
opens compose overlay pre-filled with the draft content and UID tracking
so subsequent saves replace the same server draft
Email v1.18.0: Drag-and-drop to folders, server-side IMAP search
Details
Phase 3 of full-featured email client implementation:
- New ImapSyncService.Search.cs: server-side search using MailKit SearchQuery
combinators (SubjectContains, BodyContains, FromContains, ToContains,
DeliveredAfter/Before, NotSeen) with results capped at configurable max
and fetched as headers sorted newest-first
- New EmailSearchCriteria model: Text, From, To, After, Before,
HasAttachments, IsUnread, MaxResults fields for structured search
- New EmailViewModel.Search.cs: IsServerSearch toggle, 300ms debounced
ExecuteServerSearchAsync that calls IMAP search when online, falls back
to client-side ApplyFilters on error or when offline/empty query
- Updated OnSearchQueryChanged to route through server search when enabled
- Drag-and-drop: message list items can be dragged to sidebar folder buttons
(both system folders and labels), using custom "privstack-email-uids"
data format with 8px minimum drag distance to prevent accidental drags
- Drop handler validates target folder (prevents same-folder and cross-account
drops), executes BulkMoveAsync + local entity cleanup via HandleDropMoveAsync
- DragDrop.AllowDrop="True" on both system folder and label sidebar buttons
with Tag="{Binding}" for folder identification during drop
Get notified about new releases