Fix CalDAV sync overwriting local edits + add immediate push
Details
Two issues fixed:
1. Drag-and-drop event time changes were not setting SyncState to
"pending_push", so the next sync cycle's pull phase would see
content changes and overwrite the local edit with server data.
2. All CalDAV changes (edit, create, delete) waited up to 15 minutes
for the next scheduled sync to push. Now triggers an immediate
background sync for the affected subscription after any local
CalDAV event modification (save, drag-and-drop, delete).
Fix all-day events appearing on wrong/extra days
Details
Two bugs caused CalDAV all-day events to display on two days instead
of one (e.g., a birthday on Feb 24 showing on both Feb 24 and Feb 25):
1. IcsParserService: DATE-only values (DTSTART;VALUE=DATE:20260224)
were parsed with UTC offset (TimeSpan.Zero). When converted to
local time, midnight UTC shifts to the previous day in western
timezones. Now uses local timezone offset so the date is preserved.
2. CalendarViewModel: EventSpansDate and GetEventsForDate only adjusted
exclusive end dates for non-all-day events. Per RFC 5545, all-day
DTEND is exclusive (20260225 means "up to but not including Feb 25").
Now applies the -1 day adjustment for all-day events too.
Fix CalDAV provider UI: add Google toggle, bind provider commands
Details
The provider buttons in AddSubscriptionOverlay were hardcoded with no
command bindings — selecting iCloud or Google had no effect. This adds:
- Three toggle buttons (Generic/Nextcloud, iCloud, Google) bound to
SetCalDavProviderCommand with active-state highlighting
- Google Account picker (ComboBox) visible only when Google is selected
- Server URL field visible only for Generic provider
- Username/Password fields hidden when Google is selected
- xmlns:sdk namespace for ConnectionInfo DataTemplate
Add Google Calendar CalDAV sync via OAuth
Details
Wire the Calendar plugin into the existing shell-level Google OAuth
connection to support CalDAV sync with Google Calendar. Previously
the CalDavClient only supported HTTP Basic auth, which Google has
deprecated.
Changes:
- CalDavProviderType: add Google enum value
- CalDavCredentials: add BearerToken property (takes precedence
over Username/Password when set)
- CalDavClient: rename SetBasicAuth → SetAuth, support Bearer
authorization header for OAuth providers
- CalendarSubscription: add GoogleConnectionId field to persist
which Google OAuth account is linked to the subscription
- CalendarPlugin: implement IConnectionConsumer declaring Google
with calendar scope, pass IConnectionService through to sync
service and ViewModel
- AddSubscriptionViewModel: when provider is Google, load connected
Google accounts, skip username/password fields, use bearer token
for calendar discovery. Store GoogleConnectionId on subscription
- CalendarSyncService: add ResolveCalDavCredentialsAsync that
fetches OAuth bearer tokens from IConnectionService for Google
subscriptions, vault credentials for others
Google CalDAV endpoint: apidata.googleusercontent.com/caldav/v2
Setup: enable CalDAV API and Google Calendar API in Google Cloud
Console, then connect Google account in Settings → Connections.
Fix CalDAV bidirectional sync: delete propagation, metadata preservation, ICS fidelity
Details
Critical fixes:
- Delete propagation: CalDAV events now marked pending_delete instead of being
immediately removed from the store. The sync service handles the server DELETE
and local cleanup on the next cycle.
- Occurrence changes: Deleting or editing a single occurrence of a recurring
CalDAV event now marks the parent as pending_push so ExcludedDates propagate
to the server.
- Editor metadata: Editing a CalDAV event preserves SourceUid, CalDavEtag,
CalDavHref, and ExcludedDates from the original event, preventing silent
metadata wipe and broken ETag concurrency.
- Sync token initialization: After a full-fetch fallback (no token or expired
token), GetSyncTokenAsync PROPFIND acquires the current sync-token so
subsequent syncs use incremental sync-collection instead of full fetch.
- Multi-VEVENT resources: CalDAV resources containing multiple VEVENTs (parent
+ exception overrides) are now all processed, not just the first.
ICS parser/exporter improvements:
- EXDATE: Parse and export excluded dates for recurring events.
- VALARM: Parse TRIGGER durations from VALARM sub-components into
ReminderMinutes. Export reminders as VALARM blocks with proper duration
formatting (e.g., -P1D, -PT1H, -PT30M).
- Week duration: P#W format now correctly parsed as days (P1W = 7 days).
- UNTIL date-only: 8-char UNTIL values (e.g., UNTIL=20250301) now route
through the VALUE=DATE parser branch instead of floating-time.
- RECURRENCE-ID: Parsed into OriginalStart for CalDAV exception events.
Tests: 15 new test cases covering EXDATE, VALARM, week duration, UNTIL
date-only, RECURRENCE-ID, multi-VEVENT, and GetSyncTokenAsync.
Get notified about new releases