From b66991f777fcae821a7ecae435fb70658989d37d Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 19 Aug 2019 15:59:48 +0200 Subject: [PATCH] MOBILE-3090 calendar: Improve invalidate data after sync --- .../calendar/pages/edit-event/edit-event.ts | 23 ++-- src/addon/calendar/pages/event/event.ts | 17 ++- .../calendar/providers/calendar-offline.ts | 2 +- src/addon/calendar/providers/calendar-sync.ts | 33 +++-- src/addon/calendar/providers/helper.ts | 117 +++++++++++------- 5 files changed, 126 insertions(+), 66 deletions(-) diff --git a/src/addon/calendar/pages/edit-event/edit-event.ts b/src/addon/calendar/pages/edit-event/edit-event.ts index 2daaf90cf..91f8a505a 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.ts +++ b/src/addon/calendar/pages/edit-event/edit-event.ts @@ -479,7 +479,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { } if (formData.repeat) { - data.repeats = formData.repeats; + data.repeats = Number(formData.repeats); } if (this.event && this.event.repeatid) { @@ -489,15 +489,22 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { // Send the data. const modal = this.domUtils.showModalLoading('core.sending', true); + let event; this.calendarProvider.submitEvent(this.eventId, data).then((result) => { - const numberOfRepetitions = formData.repeat ? formData.repeats : - (data.repeateditall && this.event.othereventscount ? this.event.othereventscount + 1 : 1); - this.calendarHelper.invalidateRepeatedEventsOnCalendar(result.event, numberOfRepetitions).catch(() => { - // Ignore errors. - }).then(() => { - this.returnToList(result.event); - }); + event = result.event; + + if (result.sent) { + // Event created or edited, invalidate right days & months. + const numberOfRepetitions = formData.repeat ? formData.repeats : + (data.repeateditall && this.event.othereventscount ? this.event.othereventscount + 1 : 1); + + this.calendarHelper.invalidateRepeatedEventsOnCalendarForEvent(result.event, numberOfRepetitions).catch(() => { + // Ignore errors. + }); + } + }).then(() => { + this.returnToList(event); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'Error sending data.'); }).finally(() => { diff --git a/src/addon/calendar/pages/event/event.ts b/src/addon/calendar/pages/event/event.ts index 2d4811227..d23579df0 100644 --- a/src/addon/calendar/pages/event/event.ts +++ b/src/addon/calendar/pages/event/event.ts @@ -445,10 +445,19 @@ export class AddonCalendarEventPage implements OnDestroy { const modal = this.domUtils.showModalLoading('core.sending', true); this.calendarProvider.deleteEvent(this.event.id, this.event.name, deleteAll).then((sent) => { - this.calendarHelper.invalidateRepeatedEventsOnCalendar(this.event, deleteAll ? this.event.eventcount : 1) - .catch(() => { - // Ignore errors. - }).then(() => { + let promise; + + if (sent) { + // Event deleted, invalidate right days & months. + promise = this.calendarHelper.invalidateRepeatedEventsOnCalendarForEvent(this.event, + deleteAll ? this.event.eventcount : 1).catch(() => { + // Ignore errors. + }); + } else { + promise = Promise.resolve(); + } + + return promise.then(() => { // Trigger an event. this.eventsProvider.trigger(AddonCalendarProvider.DELETED_EVENT_EVENT, { eventId: this.eventId, diff --git a/src/addon/calendar/providers/calendar-offline.ts b/src/addon/calendar/providers/calendar-offline.ts index ee06f75c9..5b0f02436 100644 --- a/src/addon/calendar/providers/calendar-offline.ts +++ b/src/addon/calendar/providers/calendar-offline.ts @@ -95,7 +95,7 @@ export class AddonCalendarOfflineProvider { }, { name: 'repeats', - type: 'TEXT', + type: 'INTEGER', }, { name: 'repeatid', diff --git a/src/addon/calendar/providers/calendar-sync.ts b/src/addon/calendar/providers/calendar-sync.ts index 457c25bb3..b395ae6af 100644 --- a/src/addon/calendar/providers/calendar-sync.ts +++ b/src/addon/calendar/providers/calendar-sync.ts @@ -26,6 +26,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { AddonCalendarProvider } from './calendar'; import { AddonCalendarOfflineProvider } from './calendar-offline'; +import { AddonCalendarHelperProvider } from './helper'; /** * Service to sync calendar. @@ -48,7 +49,8 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { timeUtils: CoreTimeUtilsProvider, private utils: CoreUtilsProvider, private calendarProvider: AddonCalendarProvider, - private calendarOffline: AddonCalendarOfflineProvider) { + private calendarOffline: AddonCalendarOfflineProvider, + private calendarHelper: AddonCalendarHelperProvider) { super('AddonCalendarSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils); @@ -124,6 +126,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { warnings: [], events: [], deleted: [], + toinvalidate: [], updated: false }; let offlineEventIds: number[]; @@ -152,18 +155,13 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { return this.utils.allPromises(promises); }).then(() => { if (result.updated) { + // Data has been sent to server. Now invalidate the WS calls. const promises = [ this.calendarProvider.invalidateEventsList(siteId), + this.calendarHelper.invalidateRepeatedEventsOnCalendar(result.toinvalidate, siteId) ]; - offlineEventIds.forEach((eventId) => { - if (eventId > 0) { - // An event was edited, invalidate its data too. - promises.push(this.calendarProvider.invalidateEvent(eventId, siteId)); - } - }); - return Promise.all(promises).catch(() => { // Ignore errors. }); @@ -214,6 +212,16 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { // Ignore errors, maybe there was no edit data. })); + // We need the event data to invalidate it. Get it from local DB. + promises.push(this.calendarProvider.getEventFromLocalDb(eventId, siteId).then((event) => { + result.toinvalidate.push({ + event: event, + repeated: data.repeat ? event.eventcount : 1 + }); + }).catch(() => { + // Ignore errors. + })); + return Promise.all(promises); }).catch((error) => { @@ -257,6 +265,15 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { result.updated = true; result.events.push(newEvent); + // Add data to invalidate. + const numberOfRepetitions = data.repeat ? data.repeats : + (data.repeateditall && newEvent.repeatid ? newEvent.eventcount : 1); + + result.toinvalidate.push({ + event: newEvent, + repeated: numberOfRepetitions + }); + // Event sent, delete the offline data. return this.calendarOffline.deleteEvent(event.id, siteId); }).catch((error) => { diff --git a/src/addon/calendar/providers/helper.ts b/src/addon/calendar/providers/helper.ts index c3b24c956..36d1f6518 100644 --- a/src/addon/calendar/providers/helper.ts +++ b/src/addon/calendar/providers/helper.ts @@ -344,73 +344,100 @@ export class AddonCalendarHelperProvider { /** * Invalidate all calls from calendar WS calls. * - * @param {any} event Event that has been touched. - * @param {number} repeated Number of times the event is repeated. + * @param {{event: any, repeated: number}[]} events Events that have been touched and number of times each event is repeated. * @param {string} [siteId] Site ID. If not defined, current site. - * @return {Promise} REsolved when done. + * @return {Promise} Resolved when done. */ - invalidateRepeatedEventsOnCalendar(event: any, repeated: number, siteId?: string): Promise { + invalidateRepeatedEventsOnCalendar(events: {event: any, repeated: number}[], siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { - let invalidatePromise; const timestarts = []; - if (repeated > 1) { - if (event.repeatid) { - // Being edited or deleted. - invalidatePromise = this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(event.repeatid, site.id) - .then((events) => { - return this.utils.allPromises(events.map((event) => { - timestarts.push(event.timestart); + // Invalidate the events and get the timestarts so we can invalidate months & days. + return this.utils.allPromises(events.map((eventData) => { - return this.calendarProvider.invalidateEvent(event.id); - })); - }); - } else { - // Being added. - let time = event.timestart; - while (repeated > 0) { - timestarts.push(time); - time += CoreConstants.SECONDS_DAY * 7; - repeated--; + if (eventData.repeated > 1) { + if (eventData.event.repeatid) { + // Being edited or deleted. + // We need to calculate the days to invalidate because the event date could have changed. + // We don't know if the repeated events are before or after this one, invalidate them all. + timestarts.push(eventData.event.timestart); + + for (let i = 1; i < eventData.repeated; i++) { + timestarts.push(eventData.event.timestart + CoreConstants.SECONDS_DAY * 7 * i); + timestarts.push(eventData.event.timestart - CoreConstants.SECONDS_DAY * 7 * i); + } + + // Get the repeated events to invalidate them. + return this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(eventData.event.repeatid, site.id) + .then((events) => { + + return this.utils.allPromises(events.map((event) => { + return this.calendarProvider.invalidateEvent(event.id); + })); + }); + } else { + // Being added. + let time = eventData.event.timestart; + while (eventData.repeated > 0) { + timestarts.push(time); + time += CoreConstants.SECONDS_DAY * 7; + eventData.repeated--; + } + + return Promise.resolve(); } + } else { + // Not repeated. + timestarts.push(eventData.event.timestart); - invalidatePromise = Promise.resolve(); + return this.calendarProvider.invalidateEvent(eventData.event.id); } - } else { - // Not repeated. - timestarts.push(event.timestart); - invalidatePromise = this.calendarProvider.invalidateEvent(event.id); - } - return invalidatePromise.finally(() => { - let lastMonth, lastYear; + })).finally(() => { + const invalidatedMonths = {}, + invalidatedDays = {}; return this.utils.allPromises([ this.calendarProvider.invalidateAllUpcomingEvents(), - // Invalidate months. + // Invalidate months and days. this.utils.allPromises(timestarts.map((time) => { - const day = moment(new Date(time * 1000)); + const promises = [], + day = moment(new Date(time * 1000)), + monthId = this.getMonthId(day.year(), day.month() + 1), + dayId = monthId + '#' + day.date(); - if (lastMonth && (lastMonth == day.month() + 1 && lastYear == day.year())) { - return Promise.resolve(); + if (!invalidatedMonths[monthId]) { + // Month not invalidated already, do it now. + invalidatedMonths[monthId] = monthId; + + promises.push(this.calendarProvider.invalidateMonthlyEvents(day.year(), day.month() + 1, site.id)); } - // Invalidate once. - lastMonth = day.month() + 1; - lastYear = day.year(); + if (!invalidatedDays[dayId]) { + // Day not invalidated already, do it now. + invalidatedDays[dayId] = dayId; - return this.calendarProvider.invalidateMonthlyEvents(lastYear, lastMonth, site.id); - })), + promises.push(this.calendarProvider.invalidateDayEvents(day.year(), day.month() + 1, day.date(), + site.id)); + } - // Invalidate days. - this.utils.allPromises(timestarts.map((time) => { - const day = moment(new Date(time * 1000)); - - return this.calendarProvider.invalidateDayEvents(day.year(), day.month() + 1, day.date(), site.id); - })), + return this.utils.allPromises(promises); + })) ]); }); }); } + + /** + * Invalidate all calls from calendar WS calls. + * + * @param {any} event Event that has been touched. + * @param {number} repeated Number of times the event is repeated. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Resolved when done. + */ + invalidateRepeatedEventsOnCalendarForEvent(event: any, repeated: number, siteId?: string): Promise { + return this.invalidateRepeatedEventsOnCalendar([{event: event, repeated: repeated}], siteId); + } }