aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-30 15:12:12 -0400
committerCraig Jennings <c@cjennings.net>2026-06-30 15:12:12 -0400
commitd5e9451022e9254655cc67e015f9d331414d2d76 (patch)
treef19e4572fb65ea698ad6c44005e4f905c214f2dd /modules
parent11e45bee9f101e998131d2938a778c5f52887131 (diff)
downloaddotemacs-d5e9451022e9254655cc67e015f9d331414d2d76.tar.gz
dotemacs-d5e9451022e9254655cc67e015f9d331414d2d76.zip
fix(calendar-sync): drop singly-declined recurring occurrences
Declining one occurrence of a recurring meeting left it on the agenda. Google emits that decline as a RECURRENCE-ID override carrying the user's PARTSTAT=DECLINED. But calendar-sync--parse-exception-event never read the override's attendee block, so the occurrence kept the series' inherited "accepted" status and the declined filter never dropped it. The apply side already re-derives status from an override's attendees. The parse side just wasn't supplying them. The fix parses the override's ATTENDEE lines into :attendees, the same way parse-event does. A unit test covers the extraction. An integration test runs the full parse/apply/filter chain on a declined week.
Diffstat (limited to 'modules')
-rw-r--r--modules/calendar-sync-recurrence.el20
1 files changed, 17 insertions, 3 deletions
diff --git a/modules/calendar-sync-recurrence.el b/modules/calendar-sync-recurrence.el
index d4f70b7d..72576a6f 100644
--- a/modules/calendar-sync-recurrence.el
+++ b/modules/calendar-sync-recurrence.el
@@ -51,7 +51,15 @@ Returns nil if not found."
"Parse a RECURRENCE-ID override EVENT-STR into an exception plist, or nil.
Returns nil when EVENT-STR carries no RECURRENCE-ID, or its recurrence-id /
start time fail to parse. The plist holds :recurrence-id (localized),
-:recurrence-id-raw, :start, :end, :summary, :description, :location."
+:recurrence-id-raw, :start, :end, :summary, :description, :location,
+:attendees.
+
+:attendees is carried so `calendar-sync--apply-single-exception' can
+re-derive the user's status when a single occurrence is declined: a
+RECURRENCE-ID override is exactly how a calendar marks one occurrence of a
+recurring series declined, and without the attendee block here the override
+inherits the series' \"accepted\" status and the declined occurrence is never
+dropped by `calendar-sync--filter-declined'."
(let ((recurrence-id (calendar-sync--get-recurrence-id event-str)))
(when recurrence-id
(let* ((recurrence-id-line (calendar-sync--get-recurrence-id-line event-str))
@@ -72,7 +80,12 @@ start time fail to parse. The plist holds :recurrence-id (localized),
(description (calendar-sync--clean-text
(calendar-sync--get-property event-str "DESCRIPTION")))
(location (calendar-sync--clean-text
- (calendar-sync--get-property event-str "LOCATION"))))
+ (calendar-sync--get-property event-str "LOCATION")))
+ ;; Carry the override's attendee block so a singly-declined
+ ;; occurrence can re-derive the user's status downstream.
+ (attendee-lines (calendar-sync--get-all-property-lines event-str "ATTENDEE"))
+ (attendees (delq nil (mapcar #'calendar-sync--parse-attendee-line
+ attendee-lines))))
(when (and recurrence-id-parsed start-parsed)
(list :recurrence-id (calendar-sync--localize-parsed-datetime
recurrence-id-parsed recurrence-id-is-utc recurrence-id-tzid)
@@ -81,7 +94,8 @@ start time fail to parse. The plist holds :recurrence-id (localized),
:end end-parsed
:summary summary
:description description
- :location location))))))
+ :location location
+ :attendees attendees))))))
(defun calendar-sync--collect-recurrence-exceptions (ics-content)
"Collect all RECURRENCE-ID events from ICS-CONTENT.