[gnome-shell/4592-calendar-correct-handling-of-recurring-events: 276/276] calendar: Correct handling of recurring events




commit c2a5ee676a77a8659a30eebb92a3ba003cbec2e6
Author: Milan Crha <mcrha redhat com>
Date:   Mon Jun 6 17:31:23 2022 +0200

    calendar: Correct handling of recurring events
    
    When a recurring event has deleted a single instance, it's received
    as an event modification, thus make sure all of the old instances
    are removed before adding the event to the list of events.
    
    Closes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4592

 js/ui/calendar.js                                 | 28 ++++++++++-----
 src/calendar-server/gnome-shell-calendar-server.c | 42 ++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 9 deletions(-)
---
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index 90ef9cc959..9656f87f77 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -273,6 +273,15 @@ class DBusEventSource extends EventSourceBase {
         this._lastRequestEnd = null;
     }
 
+    _removeMatching(uidPrefix) {
+        let changed = false;
+        for (const id of this._events.keys()) {
+            if (id.startsWith(uidPrefix))
+                changed = this._events.delete(id) || changed;
+        }
+        return changed;
+    }
+
     _onNameAppeared() {
         this._initialized = true;
         this._resetCache();
@@ -287,12 +296,21 @@ class DBusEventSource extends EventSourceBase {
     _onEventsAddedOrUpdated(dbusProxy, nameOwner, argArray) {
         const [appointments = []] = argArray;
         let changed = false;
+        let handledRemovals = new Set();
 
         for (let n = 0; n < appointments.length; n++) {
             const [id, summary, startTime, endTime] = appointments[n];
             const date = new Date(startTime * 1000);
             const end = new Date(endTime * 1000);
             let event = new CalendarEvent(id, date, end, summary);
+            /* It's a recurring event */
+            if (!id.endsWith('\n')) {
+                let parentId = id.substr(0, id.lastIndexOf('\n') + 1);
+                if (!handledRemovals.has(parentId)) {
+                    handledRemovals.add(parentId);
+                    this._removeMatching(parentId);
+                }
+            }
             this._events.set(event.id, event);
 
             changed = true;
@@ -307,7 +325,7 @@ class DBusEventSource extends EventSourceBase {
 
         let changed = false;
         for (const id of ids)
-            changed ||= this._events.delete(id);
+            changed = this._removeMatching(id) || changed;
 
         if (changed)
             this.emit('changed');
@@ -317,13 +335,7 @@ class DBusEventSource extends EventSourceBase {
         let [sourceUid = ''] = argArray;
         sourceUid += '\n';
 
-        let changed = false;
-        for (const id of this._events.keys()) {
-            if (id.startsWith(sourceUid))
-                changed ||= this._events.delete(id);
-        }
-
-        if (changed)
+        if (this._removeMatching(sourceUid))
             this.emit('changed');
     }
 
diff --git a/src/calendar-server/gnome-shell-calendar-server.c 
b/src/calendar-server/gnome-shell-calendar-server.c
index f1980c633c..f8b54bda94 100644
--- a/src/calendar-server/gnome-shell-calendar-server.c
+++ b/src/calendar-server/gnome-shell-calendar-server.c
@@ -438,20 +438,30 @@ app_process_added_modified_objects (App *app,
                                     GSList *objects) /* ICalComponent * */
 {
   ECalClient *cal_client;
+  GHashTable *covered_uids;
   GSList *link;
   gboolean expand_recurrences;
 
   cal_client = e_cal_client_view_ref_client (view);
+  covered_uids = g_hash_table_new (g_str_hash, g_str_equal);
   expand_recurrences = e_cal_client_get_source_type (cal_client) == E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
 
   for (link = objects; link; link = g_slist_next (link))
     {
       ECalComponent *comp;
       ICalComponent *icomp = link->data;
+      const gchar *uid;
+      gboolean fallback = FALSE;
 
-      if (!icomp || !i_cal_component_get_uid (icomp))
+      if (!icomp)
         continue;
 
+      uid = i_cal_component_get_uid (icomp);
+      if (!uid || g_hash_table_contains (covered_uids, uid))
+        continue;
+
+      g_hash_table_add (covered_uids, (gpointer) uid);
+
       if (expand_recurrences &&
           !e_cal_util_component_is_instance (icomp) &&
           e_cal_util_component_has_recurrences (icomp))
@@ -464,7 +474,36 @@ app_process_added_modified_objects (App *app,
           e_cal_client_generate_instances_for_object_sync (cal_client, icomp, app->since, app->until, NULL,
                                                            generate_instances_cb, &data);
         }
+      else if (expand_recurrences &&
+               e_cal_util_component_is_instance (icomp))
+        {
+          ICalComponent *main_comp = NULL;
+
+          /* Always pass whole series of the recurring events, because
+           * the calendar removes events with the same UID first. */
+          if (e_cal_client_get_object_sync (cal_client, uid, NULL, &main_comp, NULL, NULL))
+            {
+              CollectAppointmentsData data;
+
+              data.client = cal_client;
+              data.pappointments = &app->notify_appointments;
+
+              e_cal_client_generate_instances_for_object_sync (cal_client, main_comp, app->since, 
app->until, NULL,
+                                                               generate_instances_cb, &data);
+
+              g_clear_object (&main_comp);
+            }
+          else
+            {
+               fallback = TRUE;
+            }
+        }
       else
+        {
+         fallback = TRUE;
+        }
+
+      if (fallback)
         {
           comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
           if (!comp)
@@ -476,6 +515,7 @@ app_process_added_modified_objects (App *app,
         }
     }
 
+  g_clear_pointer (&covered_uids, g_hash_table_unref);
   g_clear_object (&cal_client);
 
   if (app->notify_appointments)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]