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

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2328>
This commit is contained in:
Milan Crha 2022-06-06 17:31:23 +02:00 committed by Marge Bot
parent 66ae4c7ff3
commit a6244e34de
2 changed files with 60 additions and 9 deletions

View File

@ -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;
const 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')) {
const 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');
}

View File

@ -438,20 +438,30 @@ app_process_added_modified_objects (App *app,
GSList *objects) /* ICalComponent * */
{
ECalClient *cal_client;
g_autoptr(GHashTable) covered_uids = NULL;
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)