Calendar: CalDAV two-way sync (iCloud/generic) + all-day timegrid fix

CalDAV account connect (Settings → Calendars) with RFC 6764 discovery,
per-calendar vaulted credentials, and full two-way sync verified live
against NextCloud:

- caldav.py: discovery, calendar-query REPORT (pull), PUT/DELETE with
  quoted ETags (If-Match/If-None-Match), iCloud partition-redirect
  handling, ICS build/parse; clear error codes (auth/private-host/
  unreachable) surfaced to the connect UI.
- sync.py: ctag-gated pull + push of local changes; dirty rows protected
  from being clobbered; 15-min cron + immediate enqueue on every
  create/edit/delete so changes reach the server in ~1s.
- Dirty tracking via a BEFORE INSERT/UPDATE trigger (migration 0007),
  suppressed during sync with a txn-local GUC; deletes tombstone then
  DELETE server-side. Writability now driven by server privileges.
- migration 0006: event uniqueness NULLS NOT DISTINCT — fixes duplicate
  events on every full re-pull (NULL recurrence_id never matched ON CONFLICT).
- 5 CalDAV parsing unit tests.

Calendar UI (page.js): all-day events now render in Day/3-days/Week views
(were month-only). They share the timed-event column layout so overlapping
events sit side-by-side, fill the day column vertically with a sticky label,
and are matched by start day (fixes UTC-midnight events spilling into the
next local day).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 10:27:21 +02:00
co-authored by Claude Opus 4.8
parent 719eeef378
commit a27755a4d3
13 changed files with 1260 additions and 22 deletions
+20
View File
@@ -361,6 +361,26 @@ export const en = {
"settings.subscribed": "Subscribed",
"settings.comingSoon": "Coming soon",
"settings.subscribeError": "Couldn't subscribe to that URL.",
"settings.connectTitle": "Connect {provider}",
"settings.connect": "Connect",
"settings.connecting": "Connecting…",
"settings.connectError": "Couldn't connect. Check the details and try again.",
"settings.connectAuthFailed":
"Sign-in was rejected. If you have two-factor auth on, use an app password (not your normal password).",
"settings.connectPrivateHost":
"That server is on a private network, which isn't reachable for security reasons. Use a publicly-accessible HTTPS address.",
"settings.connectUnreachable":
"Couldn't reach that server. Check the URL is correct and uses https://.",
"settings.connectNoCalendars": "Connected, but no calendars were found on that account.",
"settings.connectNeedsUrl": "Enter the CalDAV server URL.",
"settings.davServerUrl": "CalDAV server URL",
"settings.appleId": "Apple ID (email)",
"settings.username": "Username",
"settings.appPassword": "App-specific password",
"settings.password": "Password",
"settings.icloudHint":
"Use an app-specific password from account.apple.com → Sign-In and Security.",
"settings.calendarCount": "{count} calendars",
"settings.removeSubscription": "Remove subscription",
"settings.removeSubscriptionConfirm": "Stop syncing “{name}” and remove its events?",
"settings.deleteCalendar": "Delete calendar",