Merge pull request #3043 from dpalou/MOBILE-3951

MOBILE-3951 calendar: Schedule notifications from service
main
Pau Ferrer Ocaña 2022-01-11 12:42:51 +01:00 committed by GitHub
commit af9e1ce486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 79 deletions

View File

@ -18,9 +18,9 @@ jobs:
node-version: '${{ steps.nvmrc.outputs.node_version }}' node-version: '${{ steps.nvmrc.outputs.node_version }}'
- name: Additional checkouts - name: Additional checkouts
run: | run: |
git clone --branch master --depth 1 git://github.com/moodle/moodle $GITHUB_WORKSPACE/moodle git clone --branch master --depth 1 https://github.com/moodle/moodle $GITHUB_WORKSPACE/moodle
git clone --branch integration --depth 1 git://github.com/moodlehq/moodle-local_moodlemobileapp $GITHUB_WORKSPACE/moodle/local/moodlemobileapp git clone --branch integration --depth 1 https://github.com/moodlehq/moodle-local_moodlemobileapp $GITHUB_WORKSPACE/moodle/local/moodlemobileapp
git clone --branch master --depth 1 git://github.com/moodlehq/moodle-docker $GITHUB_WORKSPACE/moodle-docker git clone --branch master --depth 1 https://github.com/moodlehq/moodle-docker $GITHUB_WORKSPACE/moodle-docker
- name: Install npm packages - name: Install npm packages
run: npm ci run: npm ci
- name: Generate Behat tests plugin - name: Generate Behat tests plugin

View File

@ -41,7 +41,6 @@ import { AddonCalendarFilter, AddonCalendarHelper } from '../../services/calenda
import { AddonCalendarOffline } from '../../services/calendar-offline'; import { AddonCalendarOffline } from '../../services/calendar-offline';
import { CoreCategoryData, CoreCourses } from '@features/courses/services/courses'; import { CoreCategoryData, CoreCourses } from '@features/courses/services/courses';
import { CoreApp } from '@services/app'; import { CoreApp } from '@services/app';
import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreSwipeSlidesComponent } from '@components/swipe-slides/swipe-slides'; import { CoreSwipeSlidesComponent } from '@components/swipe-slides/swipe-slides';
import { import {
CoreSwipeSlidesDynamicItem, CoreSwipeSlidesDynamicItem,
@ -78,7 +77,6 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
protected differ: KeyValueDiffer<unknown, unknown>; // To detect changes in the data input. protected differ: KeyValueDiffer<unknown, unknown>; // To detect changes in the data input.
// Observers and listeners. // Observers and listeners.
protected undeleteEventObserver: CoreEventObserver; protected undeleteEventObserver: CoreEventObserver;
protected obsDefaultTimeChange?: CoreEventObserver;
protected managerUnsubscribe?: () => void; protected managerUnsubscribe?: () => void;
constructor( constructor(
@ -86,23 +84,6 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
) { ) {
this.currentSiteId = CoreSites.getCurrentSiteId(); this.currentSiteId = CoreSites.getCurrentSiteId();
if (CoreLocalNotifications.isAvailable()) {
// Re-schedule events if default time changes.
this.obsDefaultTimeChange = CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => {
this.manager?.getSource().getItems()?.forEach((month) => {
if (!month.loaded) {
return;
}
month.weeks?.forEach((week) => {
week.days.forEach((day) => {
AddonCalendar.scheduleEventsNotifications(day.eventsFormated || []);
});
});
});
}, this.currentSiteId);
}
// Listen for events "undeleted" (offline). // Listen for events "undeleted" (offline).
this.undeleteEventObserver = CoreEvents.on( this.undeleteEventObserver = CoreEvents.on(
AddonCalendarProvider.UNDELETED_EVENT_EVENT, AddonCalendarProvider.UNDELETED_EVENT_EVENT,
@ -329,7 +310,6 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.undeleteEventObserver?.off(); this.undeleteEventObserver?.off();
this.obsDefaultTimeChange?.off();
this.managerUnsubscribe && this.managerUnsubscribe(); this.managerUnsubscribe && this.managerUnsubscribe();
} }
@ -577,13 +557,6 @@ class AddonCalendarMonthSlidesItemsManagerSource extends CoreSwipeSlidesDynamicI
weeks.forEach((week) => { weeks.forEach((week) => {
week.days.forEach((day) => { week.days.forEach((day) => {
// Schedule notifications for the events retrieved (only future events will be scheduled).
AddonCalendar.scheduleEventsNotifications(day.eventsFormated || []);
if (monthOfflineEvents || this.deletedEvents.length) {
// There is offline data, merge it.
if (this.deletedEvents.length) { if (this.deletedEvents.length) {
// Mark as deleted the events that were deleted in offline. // Mark as deleted the events that were deleted in offline.
day.eventsFormated?.forEach((event) => { day.eventsFormated?.forEach((event) => {
@ -601,7 +574,6 @@ class AddonCalendarMonthSlidesItemsManagerSource extends CoreSwipeSlidesDynamicI
day.eventsFormated = day.eventsFormated =
AddonCalendarHelper.sortEvents(day.eventsFormated.concat(monthOfflineEvents[day.mday])); AddonCalendarHelper.sortEvents(day.eventsFormated.concat(monthOfflineEvents[day.mday]));
} }
}
}); });
}); });
} }

View File

@ -25,7 +25,6 @@ import { AddonCalendarHelper, AddonCalendarFilter } from '../../services/calenda
import { AddonCalendarOffline } from '../../services/calendar-offline'; import { AddonCalendarOffline } from '../../services/calendar-offline';
import { CoreCategoryData, CoreCourses } from '@features/courses/services/courses'; import { CoreCategoryData, CoreCourses } from '@features/courses/services/courses';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { CoreLocalNotifications } from '@services/local-notifications';
/** /**
* Component that displays upcoming events. * Component that displays upcoming events.
@ -58,19 +57,12 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, On
// Observers. // Observers.
protected undeleteEventObserver: CoreEventObserver; protected undeleteEventObserver: CoreEventObserver;
protected obsDefaultTimeChange?: CoreEventObserver;
constructor( constructor(
differs: KeyValueDiffers, differs: KeyValueDiffers,
) { ) {
this.currentSiteId = CoreSites.getCurrentSiteId(); this.currentSiteId = CoreSites.getCurrentSiteId();
if (CoreLocalNotifications.isAvailable()) { // Re-schedule events if default time changes.
this.obsDefaultTimeChange = CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => {
AddonCalendar.scheduleEventsNotifications(this.onlineEvents);
}, this.currentSiteId);
}
// Listen for events "undeleted" (offline). // Listen for events "undeleted" (offline).
this.undeleteEventObserver = CoreEvents.on( this.undeleteEventObserver = CoreEvents.on(
AddonCalendarProvider.UNDELETED_EVENT_EVENT, AddonCalendarProvider.UNDELETED_EVENT_EVENT,
@ -174,8 +166,6 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, On
// Don't pass courseId and categoryId, we'll filter them locally. // Don't pass courseId and categoryId, we'll filter them locally.
const result = await AddonCalendar.getUpcomingEvents(); const result = await AddonCalendar.getUpcomingEvents();
this.onlineEvents = await Promise.all(result.events.map((event) => AddonCalendarHelper.formatEventData(event))); this.onlineEvents = await Promise.all(result.events.map((event) => AddonCalendarHelper.formatEventData(event)));
// Schedule notifications for the events retrieved.
AddonCalendar.scheduleEventsNotifications(this.onlineEvents);
// Merge the online events with offline data. // Merge the online events with offline data.
this.events = this.mergeEvents(); this.events = this.mergeEvents();
// Filter events by course. // Filter events by course.
@ -313,7 +303,6 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, On
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.undeleteEventObserver?.off(); this.undeleteEventObserver?.off();
this.obsDefaultTimeChange?.off();
} }
} }

View File

@ -16,7 +16,6 @@ import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { IonRefresher } from '@ionic/angular'; import { IonRefresher } from '@ionic/angular';
import { CoreApp } from '@services/app'; import { CoreApp } from '@services/app';
import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreTimeUtils } from '@services/utils/time'; import { CoreTimeUtils } from '@services/utils/time';
@ -70,7 +69,6 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
protected syncObserver: CoreEventObserver; protected syncObserver: CoreEventObserver;
protected manualSyncObserver: CoreEventObserver; protected manualSyncObserver: CoreEventObserver;
protected onlineObserver: Subscription; protected onlineObserver: Subscription;
protected obsDefaultTimeChange?: CoreEventObserver;
protected filterChangedObserver: CoreEventObserver; protected filterChangedObserver: CoreEventObserver;
protected managerUnsubscribe?: () => void; protected managerUnsubscribe?: () => void;
@ -93,15 +91,6 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
constructor() { constructor() {
this.currentSiteId = CoreSites.getCurrentSiteId(); this.currentSiteId = CoreSites.getCurrentSiteId();
if (CoreLocalNotifications.isAvailable()) {
// Re-schedule events if default time changes.
this.obsDefaultTimeChange = CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => {
this.manager?.getSource().getItems()?.forEach(day => {
AddonCalendar.scheduleEventsNotifications(day.onlineEvents || []);
});
}, this.currentSiteId);
}
// Listen for events added. When an event is added, reload the data. // Listen for events added. When an event is added, reload the data.
this.newEventObserver = CoreEvents.on( this.newEventObserver = CoreEvents.on(
AddonCalendarProvider.NEW_EVENT_EVENT, AddonCalendarProvider.NEW_EVENT_EVENT,
@ -464,7 +453,6 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
this.manualSyncObserver?.off(); this.manualSyncObserver?.off();
this.onlineObserver?.unsubscribe(); this.onlineObserver?.unsubscribe();
this.filterChangedObserver?.off(); this.filterChangedObserver?.off();
this.obsDefaultTimeChange?.off();
this.managerUnsubscribe && this.managerUnsubscribe(); this.managerUnsubscribe && this.managerUnsubscribe();
} }
@ -684,9 +672,6 @@ class AddonCalendarDaySlidesItemsManagerSource extends CoreSwipeSlidesDynamicIte
} }
} }
// Schedule notifications for the events retrieved (only future events will be scheduled).
AddonCalendar.scheduleEventsNotifications(preloadedDay.onlineEvents || []);
// Merge the online events with offline data. // Merge the online events with offline data.
preloadedDay.events = this.mergeEvents(preloadedDay); preloadedDay.events = this.mergeEvents(preloadedDay);

View File

@ -129,10 +129,6 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
// Reload reminders if default notification time changes. // Reload reminders if default notification time changes.
this.defaultTimeChangedObserver = CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { this.defaultTimeChangedObserver = CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => {
this.loadReminders(); this.loadReminders();
if (this.event) {
AddonCalendar.scheduleEventsNotifications([this.event]);
}
}, this.currentSiteId); }, this.currentSiteId);
// Set and update current time. Use a 5 seconds error margin. // Set and update current time. Use a 5 seconds error margin.
@ -225,9 +221,8 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
return; // At this point we should always have the event, adding this check to avoid TS errors. return; // At this point we should always have the event, adding this check to avoid TS errors.
} }
// Load reminders, and re-schedule them if needed (maybe the event time has changed). // Load reminders.
this.loadReminders(); this.loadReminders();
AddonCalendar.scheduleEventsNotifications([this.event]);
// Reset some of the calculated data. // Reset some of the calculated data.
this.categoryPath = ''; this.categoryPath = '';

View File

@ -39,6 +39,7 @@ import { SafeUrl } from '@angular/platform-browser';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { AddonCalendarFilter } from './calendar-helper'; import { AddonCalendarFilter } from './calendar-helper';
import { AddonCalendarSyncEvents, AddonCalendarSyncProvider } from './calendar-sync'; import { AddonCalendarSyncEvents, AddonCalendarSyncProvider } from './calendar-sync';
import { CoreEvents } from '@singletons/events';
const ROOT_CACHE_KEY = 'mmaCalendar:'; const ROOT_CACHE_KEY = 'mmaCalendar:';
@ -351,6 +352,28 @@ export class AddonCalendarProvider {
}, },
); );
if (CoreLocalNotifications.isAvailable()) {
CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, async (data) => {
const site = await CoreSites.getSite(data.siteId);
// Get all the events that have a default reminder.
const query = 'SELECT events.*, reminders.id AS reminderid ' +
'FROM ' + EVENTS_TABLE + ' events ' +
'INNER JOIN ' + REMINDERS_TABLE + ' reminders ON events.id = reminders.eventid ' +
'WHERE reminders.time IS NULL';
const result = await site.getDb().execute(query);
// Reschedule all the default reminders.
for (let i = 0; i < result.rows.length; i++) {
const event = result.rows.item(i) as AddonCalendarEventDBRecord & {
reminderid: number;
};
this.scheduleEventNotification(event, event.reminderid, null, site.getId());
}
});
}
} }
/** /**
@ -674,6 +697,9 @@ export class AddonCalendarProvider {
const response: AddonCalendarGetCalendarEventByIdWSResponse = const response: AddonCalendarGetCalendarEventByIdWSResponse =
await site.read('core_calendar_get_calendar_event_by_id', params, preSets); await site.read('core_calendar_get_calendar_event_by_id', params, preSets);
this.storeEventInLocalDb(response.event, { siteId });
this.scheduleEventsNotifications([response.event], siteId);
return response.event; return response.event;
} catch (error) { } catch (error) {
try { try {
@ -837,6 +863,7 @@ export class AddonCalendarProvider {
} }
const response: AddonCalendarCalendarDay = await site.read('core_calendar_get_calendar_day_view', params, preSets); const response: AddonCalendarCalendarDay = await site.read('core_calendar_get_calendar_day_view', params, preSets);
this.storeEventsInLocalDB(response.events, { siteId }); this.storeEventsInLocalDB(response.events, { siteId });
this.scheduleEventsNotifications(response.events, siteId);
return response; return response;
} }
@ -1040,7 +1067,8 @@ export class AddonCalendarProvider {
const response = await site.read<AddonCalendarMonth>('core_calendar_get_calendar_monthly_view', params, preSets); const response = await site.read<AddonCalendarMonth>('core_calendar_get_calendar_monthly_view', params, preSets);
response.weeks.forEach((week) => { response.weeks.forEach((week) => {
week.days.forEach((day) => { week.days.forEach((day) => {
this.storeEventsInLocalDB(day.events as AddonCalendarCalendarEvent[], { siteId }); this.storeEventsInLocalDB(day.events, { siteId });
this.scheduleEventsNotifications(day.events, siteId);
}); });
}); });
@ -1154,6 +1182,7 @@ export class AddonCalendarProvider {
const response = await site.read<AddonCalendarUpcoming>('core_calendar_get_calendar_upcoming_view', params, preSets); const response = await site.read<AddonCalendarUpcoming>('core_calendar_get_calendar_upcoming_view', params, preSets);
this.storeEventsInLocalDB(response.events, { siteId }); this.storeEventsInLocalDB(response.events, { siteId });
this.scheduleEventsNotifications(response.events, siteId);
return response; return response;
} }
@ -1512,7 +1541,7 @@ export class AddonCalendarProvider {
const promises = events.map(async (event) => { const promises = events.map(async (event) => {
if (event.timestart * 1000 <= Date.now()) { if (event.timestart * 1000 <= Date.now()) {
// The event has already started, don't schedule it. // The event has already started, don't schedule it.
return this.deleteLocalEvent(event.id, siteId); return;
} }
const reminders = await this.getEventReminders(event.id, siteId); const reminders = await this.getEventReminders(event.id, siteId);