Files
calendula/app/src
Jean-Luc Makiola 4fea176e28 fix: drop occurrences via EXDATE on calendars with no _sync_id (#47)
The cancelled-exception fix works on synced calendars but not local ones. A
cancelled exception only attaches to its parent through ORIGINAL_SYNC_ID; a
local event has no _sync_id, so the link never forms and the provider's
expansion of the *parent* collapses — every other occurrence disappears, which
is the original #47 corruption, just on a different calendar type. Verified
on-device both ways: a DAVx5 series survives a single-occurrence delete, the
same series on a LOCAL calendar vanishes entirely.

deleteOccurrence now branches on _sync_id. Synced events keep the (verified)
exception path. Events without one — local calendars, and synced events not yet
pushed — add the occurrence to the master's EXDATE, which needs no parent link
and is the canonical iCalendar way to drop one; a sync adapter carries it
upstream unchanged if the calendar later syncs.

Two provider quirks shape the write (both observed on a Pixel):
- An EXDATE-only update is not treated as a recurrence change: the expanded
  Instances rows are left alone, so the occurrence stays visible. The
  time/recurrence set has to ride along to force re-expansion.
- DTSTART alone is worse — the provider then recomputes lastDate as if the event
  were a single instance and collapses the series to its first occurrence.
  DTSTART + DURATION + RRULE + zone together re-expand it correctly.

This path is reached in normal use: Calendula's own contact special-date
calendars are local and hold all-day yearly series, so deleting one birthday
occurrence went through it. All-day series take the VALUE=DATE EXDATE form.

Adds pure buildOccurrenceExdateValues + JVM tests (timed, append, duplicate
fold, all-day).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:01:11 +02:00
..