MOBILE-3936 reminders: Move calendar reminders to new reminders module
This commit is contained in:
		
							parent
							
								
									3d7b9dfbc5
								
							
						
					
					
						commit
						cefd0248fc
					
				@ -70,7 +70,7 @@ const mainMenuChildrenRoutes: Routes = [
 | 
			
		||||
 | 
			
		||||
                await AddonCalendar.initialize();
 | 
			
		||||
 | 
			
		||||
                AddonCalendar.scheduleAllSitesEventsNotifications();
 | 
			
		||||
                AddonCalendar.updateAllSitesEventReminders();
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -31,9 +31,10 @@ import {
 | 
			
		||||
    AddonCalendarEventType,
 | 
			
		||||
    AddonCalendar,
 | 
			
		||||
    AddonCalendarSubmitCreateUpdateFormDataWSParams,
 | 
			
		||||
    AddonCalendarReminderUnits,
 | 
			
		||||
} from '../../services/calendar';
 | 
			
		||||
import { AddonCalendarOffline } from '../../services/calendar-offline';
 | 
			
		||||
import { AddonCalendarEventReminder, AddonCalendarEventTypeOption, AddonCalendarHelper } from '../../services/calendar-helper';
 | 
			
		||||
import { AddonCalendarEventTypeOption, AddonCalendarHelper } from '../../services/calendar-helper';
 | 
			
		||||
import { AddonCalendarSync, AddonCalendarSyncProvider } from '../../services/calendar-sync';
 | 
			
		||||
import { CoreSite } from '@classes/site';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
@ -156,7 +157,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch the data needed to render the form.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh Whether it's refreshing data.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchData(): Promise<void> {
 | 
			
		||||
@ -273,15 +273,14 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const courseFillterFullname = (course: CoreCourseSearchedData | CoreEnrolledCourseData): Promise<void> =>
 | 
			
		||||
            CoreFilterHelper.getFiltersAndFormatText(course.fullname, 'course', course.id)
 | 
			
		||||
                .then((result) => {
 | 
			
		||||
                    course.fullname = result.text;
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }).catch(() => {
 | 
			
		||||
                    // Ignore errors.
 | 
			
		||||
                });
 | 
			
		||||
        const courseFillFullname = async (course: CoreCourseSearchedData | CoreEnrolledCourseData): Promise<void> => {
 | 
			
		||||
            try {
 | 
			
		||||
                const result = await CoreFilterHelper.getFiltersAndFormatText(course.fullname, 'course', course.id);
 | 
			
		||||
                course.fullname = result.text;
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Ignore errors.
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (this.showAll) {
 | 
			
		||||
            // Remove site home from the list of courses.
 | 
			
		||||
@ -296,9 +295,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
 | 
			
		||||
        // Format the name of the courses.
 | 
			
		||||
        if ('contacts' in courses[0]) {
 | 
			
		||||
            await Promise.all((courses as CoreCourseSearchedData[]).map(courseFillterFullname));
 | 
			
		||||
            await Promise.all((courses as CoreCourseSearchedData[]).map(courseFillFullname));
 | 
			
		||||
        } else {
 | 
			
		||||
            await Promise.all((courses as CoreEnrolledCourseData[]).map(courseFillterFullname));
 | 
			
		||||
            await Promise.all((courses as CoreEnrolledCourseData[]).map(courseFillFullname));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sort courses by name.
 | 
			
		||||
@ -461,17 +460,17 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
        const timeDurationMinutes = parseInt(formData.timedurationminutes || '', 10);
 | 
			
		||||
        let error: string | undefined;
 | 
			
		||||
 | 
			
		||||
        if (formData.eventtype == AddonCalendarEventType.COURSE && !formData.courseid) {
 | 
			
		||||
        if (formData.eventtype === AddonCalendarEventType.COURSE && !formData.courseid) {
 | 
			
		||||
            error = 'core.selectacourse';
 | 
			
		||||
        } else if (formData.eventtype == AddonCalendarEventType.GROUP && !formData.groupcourseid) {
 | 
			
		||||
        } else if (formData.eventtype === AddonCalendarEventType.GROUP && !formData.groupcourseid) {
 | 
			
		||||
            error = 'core.selectacourse';
 | 
			
		||||
        } else if (formData.eventtype == AddonCalendarEventType.GROUP && !formData.groupid) {
 | 
			
		||||
        } else if (formData.eventtype === AddonCalendarEventType.GROUP && !formData.groupid) {
 | 
			
		||||
            error = 'core.selectagroup';
 | 
			
		||||
        } else if (formData.eventtype == AddonCalendarEventType.CATEGORY && !formData.categoryid) {
 | 
			
		||||
        } else if (formData.eventtype === AddonCalendarEventType.CATEGORY && !formData.categoryid) {
 | 
			
		||||
            error = 'core.selectacategory';
 | 
			
		||||
        } else if (formData.duration == 1 && timeStartDate > timeUntilDate) {
 | 
			
		||||
        } else if (formData.duration === 1 && timeStartDate > timeUntilDate) {
 | 
			
		||||
            error = 'addon.calendar.invalidtimedurationuntil';
 | 
			
		||||
        } else if (formData.duration == 2 && (isNaN(timeDurationMinutes) || timeDurationMinutes < 1)) {
 | 
			
		||||
        } else if (formData.duration === 2 && (isNaN(timeDurationMinutes) || timeDurationMinutes < 1)) {
 | 
			
		||||
            error = 'addon.calendar.invalidtimedurationminutes';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -534,8 +533,11 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
 | 
			
		||||
            if (result.sent) {
 | 
			
		||||
                // Event created or edited, invalidate right days & months.
 | 
			
		||||
                const numberOfRepetitions = formData.repeat ? formData.repeats :
 | 
			
		||||
                    (data.repeateditall && this.otherEventsCount ? this.otherEventsCount + 1 : 1);
 | 
			
		||||
                const numberOfRepetitions = formData.repeat
 | 
			
		||||
                    ? formData.repeats
 | 
			
		||||
                    : (data.repeateditall && this.otherEventsCount
 | 
			
		||||
                        ? this.otherEventsCount + 1
 | 
			
		||||
                        : 1);
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    await AddonCalendarHelper.refreshAfterChangeEvent(result.event, numberOfRepetitions);
 | 
			
		||||
@ -651,7 +653,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
 | 
			
		||||
        // Check if default reminders are enabled.
 | 
			
		||||
        const defaultTime = await AddonCalendar.getDefaultNotificationTime(this.currentSite.getId());
 | 
			
		||||
        if (defaultTime === 0) {
 | 
			
		||||
        if (defaultTime === AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -659,7 +661,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
 | 
			
		||||
        // Add default reminder.
 | 
			
		||||
        this.reminders.push({
 | 
			
		||||
            time: null,
 | 
			
		||||
            value: data.value,
 | 
			
		||||
            unit: data.unit,
 | 
			
		||||
            label: AddonCalendar.getUnitValueLabel(data.value, data.unit, true),
 | 
			
		||||
@ -697,7 +698,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
     */
 | 
			
		||||
    removeReminder(reminder: AddonCalendarEventCandidateReminder): void {
 | 
			
		||||
        const index = this.reminders.indexOf(reminder);
 | 
			
		||||
        if (index != -1) {
 | 
			
		||||
        if (index !== -1) {
 | 
			
		||||
            this.reminders.splice(index, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -712,4 +713,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AddonCalendarEventCandidateReminder = Omit<AddonCalendarEventReminder, 'id'|'eventid'>;
 | 
			
		||||
type AddonCalendarEventCandidateReminder =  {
 | 
			
		||||
    time?: number; // Undefined for default reminder.
 | 
			
		||||
    value: number; // Amount of time.
 | 
			
		||||
    unit: AddonCalendarReminderUnits; // Units.
 | 
			
		||||
    label: string; // Label to represent the reminder.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -139,7 +139,7 @@
 | 
			
		||||
                        <p class="item-heading">{{ reminder.label }}</p>
 | 
			
		||||
                        <p *ngIf="reminder.sublabel">{{ reminder.sublabel }}</p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                    <ion-button fill="clear" (click)="cancelNotification(reminder.id, $event)" [attr.aria-label]="'core.delete' | translate"
 | 
			
		||||
                    <ion-button fill="clear" (click)="deleteReminder(reminder.id, $event)" [attr.aria-label]="'core.delete' | translate"
 | 
			
		||||
                        slot="end">
 | 
			
		||||
                        <ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true"></ion-icon>
 | 
			
		||||
                    </ion-button>
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,7 @@ import { AddonCalendarReminderTimeModalComponent } from '@addons/calendar/compon
 | 
			
		||||
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
 | 
			
		||||
import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source';
 | 
			
		||||
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
 | 
			
		||||
import { CoreReminders } from '@features/reminders/services/reminders';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays a single calendar event.
 | 
			
		||||
@ -152,8 +153,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const reminders = await AddonCalendar.getEventReminders(this.eventId, this.currentSiteId);
 | 
			
		||||
        this.reminders = await AddonCalendarHelper.formatReminders(reminders, this.event.timestart, this.currentSiteId);
 | 
			
		||||
        this.reminders = await AddonCalendarHelper.getEventReminders(this.eventId, this.event.timestart, this.currentSiteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -402,12 +402,12 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cancel the selected notification.
 | 
			
		||||
     * Delete the selected reminder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id Reminder ID.
 | 
			
		||||
     * @param e Click event.
 | 
			
		||||
     */
 | 
			
		||||
    async cancelNotification(id: number, e: Event): Promise<void> {
 | 
			
		||||
    async deleteReminder(id: number, e: Event): Promise<void> {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@ -417,7 +417,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
 | 
			
		||||
            const modal = await CoreDomUtils.showModalLoading('core.deleting', true);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await AddonCalendar.deleteEventReminder(id);
 | 
			
		||||
                await CoreReminders.removeReminder(id);
 | 
			
		||||
                await this.loadReminders();
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                CoreDomUtils.showErrorModalDefault(error, 'Error deleting reminder');
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ import {
 | 
			
		||||
    AddonCalendarEventType,
 | 
			
		||||
    AddonCalendarGetEventsEvent,
 | 
			
		||||
    AddonCalendarProvider,
 | 
			
		||||
    AddonCalendarReminderUnits,
 | 
			
		||||
    AddonCalendarWeek,
 | 
			
		||||
    AddonCalendarWeekDay,
 | 
			
		||||
} from './calendar';
 | 
			
		||||
@ -36,8 +35,8 @@ import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonCalendarSyncInvalidateEvent } from './calendar-sync';
 | 
			
		||||
import { AddonCalendarOfflineEventDBRecord } from './database/calendar-offline';
 | 
			
		||||
import { CoreCategoryData } from '@features/courses/services/courses';
 | 
			
		||||
import { AddonCalendarReminderDBRecord } from './database/calendar';
 | 
			
		||||
import { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Context levels enumeration.
 | 
			
		||||
@ -123,7 +122,7 @@ export class AddonCalendarHelperProvider {
 | 
			
		||||
     * Classify events into their respective months and days. If an event duration covers more than one day,
 | 
			
		||||
     * it will be included in all the days it lasts.
 | 
			
		||||
     *
 | 
			
		||||
     * @param events Events to classify.
 | 
			
		||||
     * @param offlineEvents Events to classify.
 | 
			
		||||
     * @return Object with the classified events.
 | 
			
		||||
     */
 | 
			
		||||
    classifyIntoMonths(
 | 
			
		||||
@ -219,7 +218,7 @@ export class AddonCalendarHelperProvider {
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to format some event data to be rendered.
 | 
			
		||||
     *
 | 
			
		||||
     * @param e Event to format.
 | 
			
		||||
     * @param event Event to format.
 | 
			
		||||
     */
 | 
			
		||||
    formatOfflineEventData(event: AddonCalendarOfflineEventDBRecord): AddonCalendarEventToDisplay {
 | 
			
		||||
 | 
			
		||||
@ -295,48 +294,75 @@ export class AddonCalendarHelperProvider {
 | 
			
		||||
     * @param timestart Event timestart.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Formatted reminders.
 | 
			
		||||
     * @deprecated since 4.1 Use AddonCalendarHelper.getEventReminders.
 | 
			
		||||
     */
 | 
			
		||||
    async formatReminders(
 | 
			
		||||
        reminders: AddonCalendarReminderDBRecord[],
 | 
			
		||||
        reminders: { eventid: number }[],
 | 
			
		||||
        timestart: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonCalendarEventReminder[]> {
 | 
			
		||||
        const defaultTime = await AddonCalendar.getDefaultNotificationTime(siteId);
 | 
			
		||||
 | 
			
		||||
        const formattedReminders = <AddonCalendarEventReminder[]> reminders;
 | 
			
		||||
        const eventTimestart = timestart;
 | 
			
		||||
        let defaultTimeValue: number | undefined;
 | 
			
		||||
        let defaultTimeUnit: AddonCalendarReminderUnits | undefined;
 | 
			
		||||
 | 
			
		||||
        if (defaultTime > 0) {
 | 
			
		||||
            const data = AddonCalendarProvider.convertSecondsToValueAndUnit(defaultTime);
 | 
			
		||||
            defaultTimeValue = data.value;
 | 
			
		||||
            defaultTimeUnit = data.unit;
 | 
			
		||||
        if (!reminders.length) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return formattedReminders.map((reminder) => {
 | 
			
		||||
            if (reminder.time === null) {
 | 
			
		||||
        return AddonCalendarHelper.getEventReminders(reminders[0].eventid, timestart, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Format reminders, adding calculated data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param eventId Event Id.
 | 
			
		||||
     * @param eventTimestart Event timestart.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Formatted reminders.
 | 
			
		||||
     */
 | 
			
		||||
    async getEventReminders(
 | 
			
		||||
        eventId: number,
 | 
			
		||||
        eventTimestart: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonCalendarEventReminder[]> {
 | 
			
		||||
        const reminders = await CoreReminders.getReminders(
 | 
			
		||||
            {
 | 
			
		||||
                instanceId: eventId,
 | 
			
		||||
                component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
            },
 | 
			
		||||
            siteId,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!reminders.length) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const defaultTime = await AddonCalendar.getDefaultNotificationTime(siteId);
 | 
			
		||||
        let defaultLabel: string | undefined;
 | 
			
		||||
 | 
			
		||||
        if (defaultTime > AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
 | 
			
		||||
            const data = AddonCalendarProvider.convertSecondsToValueAndUnit(defaultTime);
 | 
			
		||||
            defaultLabel = AddonCalendar.getUnitValueLabel(data.value, data.unit, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return reminders.map((reminder) => {
 | 
			
		||||
            const formatted: AddonCalendarEventReminder = {
 | 
			
		||||
                id: reminder.id,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (reminder.timebefore === CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE) {
 | 
			
		||||
                // Default time. Check if default notifications are disabled.
 | 
			
		||||
                if (defaultTimeValue !== undefined && defaultTimeUnit) {
 | 
			
		||||
                    reminder.value = defaultTimeValue;
 | 
			
		||||
                    reminder.unit = defaultTimeUnit;
 | 
			
		||||
                    reminder.timestamp = eventTimestart - reminder.value * reminder.unit;
 | 
			
		||||
                if (defaultLabel !== undefined) {
 | 
			
		||||
                    formatted.label = defaultLabel;
 | 
			
		||||
                    formatted.timestamp = eventTimestart - defaultTime;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                const data = AddonCalendarProvider.convertSecondsToValueAndUnit(reminder.time);
 | 
			
		||||
                reminder.value = data.value;
 | 
			
		||||
                reminder.unit = data.unit;
 | 
			
		||||
                reminder.timestamp = eventTimestart - reminder.time;
 | 
			
		||||
                const data = AddonCalendarProvider.convertSecondsToValueAndUnit(reminder.timebefore);
 | 
			
		||||
                formatted.label = AddonCalendar.getUnitValueLabel(data.value, data.unit, false);
 | 
			
		||||
                formatted.timestamp = eventTimestart - reminder.timebefore;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (reminder.value && reminder.unit) {
 | 
			
		||||
                reminder.label = AddonCalendar.getUnitValueLabel(reminder.value, reminder.unit, reminder.time === null);
 | 
			
		||||
                if (reminder.timestamp) {
 | 
			
		||||
                    reminder.sublabel = CoreTimeUtils.userDate(reminder.timestamp * 1000, 'core.strftimedatetime');
 | 
			
		||||
                }
 | 
			
		||||
            if (formatted.timestamp) {
 | 
			
		||||
                formatted.sublabel = CoreTimeUtils.userDate(formatted.timestamp * 1000, 'core.strftimedatetime');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return reminder;
 | 
			
		||||
            return formatted;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -381,7 +407,7 @@ export class AddonCalendarHelperProvider {
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the day "id".
 | 
			
		||||
     *
 | 
			
		||||
     * @param day Day moment.
 | 
			
		||||
     * @param moment Day moment.
 | 
			
		||||
     * @return The "id".
 | 
			
		||||
     */
 | 
			
		||||
    getDayId(moment: moment.Moment): string {
 | 
			
		||||
@ -553,18 +579,18 @@ export class AddonCalendarHelperProvider {
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        categoryId?: number,
 | 
			
		||||
    ): boolean {
 | 
			
		||||
        if (event.eventtype == 'user' || event.eventtype == 'site') {
 | 
			
		||||
        if (event.eventtype === 'user' || event.eventtype === 'site') {
 | 
			
		||||
            // User or site event, display it.
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (event.eventtype == 'category' && categories) {
 | 
			
		||||
        if (event.eventtype === 'category' && categories) {
 | 
			
		||||
            if (!event.categoryid || !Object.keys(categories).length || !categoryId) {
 | 
			
		||||
                // We can't tell if the course belongs to the category, display them all.
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (event.categoryid == categoryId) {
 | 
			
		||||
            if (event.categoryid === categoryId) {
 | 
			
		||||
                // The event is in the same category as the course, display it.
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
@ -577,7 +603,7 @@ export class AddonCalendarHelperProvider {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (event.categoryid == category.parent) {
 | 
			
		||||
                if (event.categoryid === category.parent) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                category = categories[category.parent];
 | 
			
		||||
@ -796,9 +822,8 @@ export type AddonCalendarEventTypeOption = {
 | 
			
		||||
/**
 | 
			
		||||
 * Formatted event reminder.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonCalendarEventReminder = AddonCalendarReminderDBRecord & {
 | 
			
		||||
    value?: number; // Amount of time.
 | 
			
		||||
    unit?: AddonCalendarReminderUnits; // Units.
 | 
			
		||||
export type AddonCalendarEventReminder = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    timestamp?: number; // Timestamp (in seconds).
 | 
			
		||||
    label?: string; // Label to represent the reminder.
 | 
			
		||||
    sublabel?: string; // Sub label.
 | 
			
		||||
 | 
			
		||||
@ -23,12 +23,11 @@ import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreGroups } from '@services/groups';
 | 
			
		||||
import { CoreLocalNotifications } from '@services/local-notifications';
 | 
			
		||||
import { CoreConfig } from '@services/config';
 | 
			
		||||
import { ILocalNotification } from '@ionic-native/local-notifications';
 | 
			
		||||
import { AddonCalendarOffline } from './calendar-offline';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreWSExternalWarning, CoreWSDate } from '@services/ws';
 | 
			
		||||
import moment from 'moment-timezone';
 | 
			
		||||
import { AddonCalendarEventDBRecord, AddonCalendarReminderDBRecord, EVENTS_TABLE, REMINDERS_TABLE } from './database/calendar';
 | 
			
		||||
import { AddonCalendarEventDBRecord, EVENTS_TABLE } from './database/calendar';
 | 
			
		||||
import { CoreCourses } from '@features/courses/services/courses';
 | 
			
		||||
import { ContextLevel, CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CoreWSError } from '@classes/errors/wserror';
 | 
			
		||||
@ -42,6 +41,13 @@ import { AddonCalendarSyncEvents, AddonCalendarSyncProvider } from './calendar-s
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreText } from '@singletons/text';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import {
 | 
			
		||||
    CoreReminderData,
 | 
			
		||||
    CoreReminders,
 | 
			
		||||
    CoreRemindersPushNotificationData,
 | 
			
		||||
    CoreRemindersService,
 | 
			
		||||
} from '@features/reminders/services/reminders';
 | 
			
		||||
import { CoreReminderDBRecord } from '@features/reminders/services/database/reminders';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'mmaCalendar:';
 | 
			
		||||
 | 
			
		||||
@ -121,6 +127,8 @@ export class AddonCalendarProvider {
 | 
			
		||||
    static readonly CALENDAR_TF_24 = '%H:%M'; // Calendar time in 24 hours format.
 | 
			
		||||
    static readonly CALENDAR_TF_12 = '%I:%M %p'; // Calendar time in 12 hours format.
 | 
			
		||||
 | 
			
		||||
    static readonly DEFAULT_NOTIFICATION_DISABLED = 0;
 | 
			
		||||
 | 
			
		||||
    protected weekDays: AddonCalendarWeekDaysTranslationKeys[] = [
 | 
			
		||||
        {
 | 
			
		||||
            shortname: 'addon.calendar.sun',
 | 
			
		||||
@ -306,17 +314,12 @@ export class AddonCalendarProvider {
 | 
			
		||||
            EVENTS_TABLE,
 | 
			
		||||
            { id: eventId },
 | 
			
		||||
        ));
 | 
			
		||||
        promises.push(site.getDb().getRecords<AddonCalendarReminderDBRecord>(
 | 
			
		||||
            REMINDERS_TABLE,
 | 
			
		||||
            { eventid: eventId },
 | 
			
		||||
        ).then((reminders) =>
 | 
			
		||||
            Promise.all(reminders.map((reminder) => this.deleteEventReminder(reminder.id, siteId)))));
 | 
			
		||||
        promises.push(CoreReminders.removeReminders({
 | 
			
		||||
            instanceId: eventId,
 | 
			
		||||
            component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
        } , siteId));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
        await CoreUtils.ignoreErrors(Promise.all(promises));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -326,55 +329,56 @@ export class AddonCalendarProvider {
 | 
			
		||||
     */
 | 
			
		||||
    async initialize(): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        CoreLocalNotifications.registerClick<AddonCalendarPushNotificationData>(
 | 
			
		||||
        CoreLocalNotifications.registerClick<CoreRemindersPushNotificationData>(
 | 
			
		||||
            AddonCalendarProvider.COMPONENT,
 | 
			
		||||
            async (notification) => {
 | 
			
		||||
                if (notification.eventId) {
 | 
			
		||||
                    await ApplicationInit.donePromise;
 | 
			
		||||
                await ApplicationInit.donePromise;
 | 
			
		||||
 | 
			
		||||
                    const disabled = await this.isDisabled(notification.siteId);
 | 
			
		||||
                    if (disabled) {
 | 
			
		||||
                        // The calendar is disabled in the site, don't open it.
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonCalendarMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
                        {
 | 
			
		||||
                            siteId: notification.siteId,
 | 
			
		||||
                            preferCurrentTab: false,
 | 
			
		||||
                            nextNavigation: {
 | 
			
		||||
                                path: `calendar/event/${notification.eventId}`,
 | 
			
		||||
                                isSitePath: true,
 | 
			
		||||
                            },
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
                this.notificationClicked(notification);
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        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());
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        if (!CoreLocalNotifications.isAvailable()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, async (data) => {
 | 
			
		||||
            const site = await CoreSites.getSite(data.siteId);
 | 
			
		||||
            const siteId = site.getId();
 | 
			
		||||
 | 
			
		||||
            // Get all the events that have a default reminder.
 | 
			
		||||
            const reminders = await CoreReminders.getRemindersWithDefaultTime(AddonCalendarProvider.COMPONENT, siteId);
 | 
			
		||||
 | 
			
		||||
            // Reschedule all the default reminders.
 | 
			
		||||
            reminders.forEach((reminder) =>
 | 
			
		||||
                CoreReminders.scheduleNotification(reminder, siteId));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notification has been clicked.
 | 
			
		||||
     *
 | 
			
		||||
     * @param notification Calendar notification.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async notificationClicked(notification: CoreRemindersPushNotificationData): Promise<void> {
 | 
			
		||||
        const disabled = await this.isDisabled(notification.siteId);
 | 
			
		||||
        if (disabled) {
 | 
			
		||||
            // The calendar is disabled in the site, don't open it.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreNavigator.navigateToSitePath(
 | 
			
		||||
            AddonCalendarMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
            {
 | 
			
		||||
                siteId: notification.siteId,
 | 
			
		||||
                preferCurrentTab: false,
 | 
			
		||||
                nextNavigation: {
 | 
			
		||||
                    path: `calendar/event/${notification.instanceId}`,
 | 
			
		||||
                    isSitePath: true,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -527,7 +531,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
            await site.read('core_calendar_get_allowed_event_types', params, preSets);
 | 
			
		||||
 | 
			
		||||
        // Convert the array to an object.
 | 
			
		||||
        const result = {};
 | 
			
		||||
        const result: {[name: string]: boolean} = {};
 | 
			
		||||
        if (response.allowedeventtypes) {
 | 
			
		||||
            response.allowedeventtypes.forEach((type) => {
 | 
			
		||||
                result[type] = true;
 | 
			
		||||
@ -699,7 +703,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
                await site.read('core_calendar_get_calendar_event_by_id', params, preSets);
 | 
			
		||||
 | 
			
		||||
            this.storeEventInLocalDb(response.event, { siteId });
 | 
			
		||||
            this.scheduleEventsNotifications([response.event], siteId);
 | 
			
		||||
            this.updateEventsReminders([response.event], site.getId());
 | 
			
		||||
 | 
			
		||||
            return response.event;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
@ -766,27 +770,43 @@ export class AddonCalendarProvider {
 | 
			
		||||
     * Adds an event reminder and schedule a new notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param event Event to set the reminder.
 | 
			
		||||
     * @param time Amount of seconds of the reminder. Undefined for default reminder.
 | 
			
		||||
     * @param timebefore Amount of seconds of the reminder. Undefined for default reminder.
 | 
			
		||||
     * @param siteId ID of the site the event belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the notification is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async addEventReminder(
 | 
			
		||||
        event: { id: number; timestart: number; name: string},
 | 
			
		||||
        time?: number | null,
 | 
			
		||||
        event: AddonCalendarEvent | AddonCalendarEventDBRecord | AddonCalendarEventToDisplay | AddonCalendarOfflineEventDBRecord,
 | 
			
		||||
        timebefore?: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        const reminder: Partial<AddonCalendarReminderDBRecord> = {
 | 
			
		||||
            eventid: event.id,
 | 
			
		||||
            time: time ?? null,
 | 
			
		||||
            timecreated: Date.now(),
 | 
			
		||||
 | 
			
		||||
        timebefore = timebefore ?? CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE;
 | 
			
		||||
 | 
			
		||||
        const previousReminders = await CoreReminders.getReminders({
 | 
			
		||||
            instanceId: event.id,
 | 
			
		||||
            component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
        }, siteId);
 | 
			
		||||
 | 
			
		||||
        if (previousReminders.some((reminder) => reminder.timebefore === timebefore)) {
 | 
			
		||||
            // Already exists.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const url = 'url' in event
 | 
			
		||||
            ? event.url || ''
 | 
			
		||||
            : '';
 | 
			
		||||
 | 
			
		||||
        const reminder: CoreReminderData = {
 | 
			
		||||
            component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
            instanceId: event.id,
 | 
			
		||||
            type: event.eventtype,
 | 
			
		||||
            time: event.timestart,
 | 
			
		||||
            timebefore,
 | 
			
		||||
            title: event.name,
 | 
			
		||||
            url,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const reminderId = await site.getDb().insertRecord(REMINDERS_TABLE, reminder);
 | 
			
		||||
 | 
			
		||||
        const timestamp = time ? event.timestart - time : time;
 | 
			
		||||
 | 
			
		||||
        await this.scheduleEventNotification(event, reminderId, timestamp, site.getId());
 | 
			
		||||
        await CoreReminders.addReminder(reminder, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -810,15 +830,10 @@ export class AddonCalendarProvider {
 | 
			
		||||
     * @param id Reminder ID.
 | 
			
		||||
     * @param siteId ID of the site the event belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the notification is updated.
 | 
			
		||||
     * @deprecated since 4.1. Use CoreReminders.removeReminder instead.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteEventReminder(id: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        if (CoreLocalNotifications.isAvailable()) {
 | 
			
		||||
            CoreLocalNotifications.cancel(id, AddonCalendarProvider.COMPONENT, site.getId());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(REMINDERS_TABLE, { id: id });
 | 
			
		||||
        await CoreReminders.removeReminder(id, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -865,7 +880,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
        }
 | 
			
		||||
        const response: AddonCalendarCalendarDay = await site.read('core_calendar_get_calendar_day_view', params, preSets);
 | 
			
		||||
        this.storeEventsInLocalDB(response.events, { siteId });
 | 
			
		||||
        this.scheduleEventsNotifications(response.events, siteId);
 | 
			
		||||
        this.updateEventsReminders(response.events, site.getId());
 | 
			
		||||
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
@ -909,14 +924,16 @@ export class AddonCalendarProvider {
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a calendar reminders from local Db.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id Event ID.
 | 
			
		||||
     * @param eventId Event ID.
 | 
			
		||||
     * @param siteId ID of the site the event belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the event data is retrieved.
 | 
			
		||||
     * @deprecated since 4.1. Use CoreReminders.getReminders instead.
 | 
			
		||||
     */
 | 
			
		||||
    async getEventReminders(id: number, siteId?: string): Promise<AddonCalendarReminderDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecords(REMINDERS_TABLE, { eventid: id }, 'timecreated ASC, time ASC');
 | 
			
		||||
    async getEventReminders(eventId: number, siteId?: string): Promise<CoreReminderDBRecord[]> {
 | 
			
		||||
        return CoreReminders.getReminders({
 | 
			
		||||
            instanceId: eventId,
 | 
			
		||||
            component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
        }, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1070,7 +1087,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
        response.weeks.forEach((week) => {
 | 
			
		||||
            week.days.forEach((day) => {
 | 
			
		||||
                this.storeEventsInLocalDB(day.events, { siteId });
 | 
			
		||||
                this.scheduleEventsNotifications(day.events, siteId);
 | 
			
		||||
                this.updateEventsReminders(day.events, site.getId());
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -1184,7 +1201,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
 | 
			
		||||
        const response = await site.read<AddonCalendarUpcoming>('core_calendar_get_calendar_upcoming_view', params, preSets);
 | 
			
		||||
        this.storeEventsInLocalDB(response.events, { siteId });
 | 
			
		||||
        this.scheduleEventsNotifications(response.events, siteId);
 | 
			
		||||
        this.updateEventsReminders(response.events, site.getId());
 | 
			
		||||
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
@ -1422,6 +1439,16 @@ export class AddonCalendarProvider {
 | 
			
		||||
        return this.isCalendarDisabledInSite(site);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the next events for all the sites and schedules their notifications.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     * @deprecated since 4.1 Use AddonCalendar.updateAllSitesEventReminders.
 | 
			
		||||
     */
 | 
			
		||||
    async scheduleAllSitesEventsNotifications(): Promise<void> {
 | 
			
		||||
        await AddonCalendar.updateAllSitesEventReminders();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the next events for all the sites and schedules their notifications.
 | 
			
		||||
     * If an event notification time is 0, cancel its scheduled notification (if any).
 | 
			
		||||
@ -1429,95 +1456,40 @@ export class AddonCalendarProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when all the notifications have been scheduled.
 | 
			
		||||
     */
 | 
			
		||||
    async scheduleAllSitesEventsNotifications(): Promise<void> {
 | 
			
		||||
    async updateAllSitesEventReminders(): Promise<void> {
 | 
			
		||||
        await CorePlatform.ready();
 | 
			
		||||
 | 
			
		||||
        const notificationsEnabled = CoreLocalNotifications.isAvailable();
 | 
			
		||||
 | 
			
		||||
        const siteIds = await CoreSites.getSitesIds();
 | 
			
		||||
 | 
			
		||||
        const promises = siteIds.map((siteId: string) => async () => {
 | 
			
		||||
            if (notificationsEnabled) {
 | 
			
		||||
                // Check if calendar is disabled for the site.
 | 
			
		||||
                const disabled = await this.isDisabled(siteId);
 | 
			
		||||
                if (!disabled) {
 | 
			
		||||
                    // Get first events.
 | 
			
		||||
                    const events = await this.getEventsList(undefined, undefined, undefined, siteId);
 | 
			
		||||
                    await this.scheduleEventsNotifications(events, siteId);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Schedules an event notification. If time is 0, cancel scheduled notification if any.
 | 
			
		||||
     * If local notification plugin is not enabled, resolve the promise.
 | 
			
		||||
     *
 | 
			
		||||
     * @param event Event to schedule.
 | 
			
		||||
     * @param reminderId The reminder ID.
 | 
			
		||||
     * @param time Notification timestamp (in seconds). Undefined for default time.
 | 
			
		||||
     * @param siteId Site ID the event belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the notification is scheduled.
 | 
			
		||||
     */
 | 
			
		||||
    protected async scheduleEventNotification(
 | 
			
		||||
        event: { id: number; timestart: number; name: string},
 | 
			
		||||
        reminderId: number,
 | 
			
		||||
        time?: number | null,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        if (!CoreLocalNotifications.isAvailable()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const siteIds = await CoreSites.getSitesIds();
 | 
			
		||||
 | 
			
		||||
        await Promise.all(siteIds.map((siteId: string) => async () => {
 | 
			
		||||
 | 
			
		||||
            // Check if calendar is disabled for the site.
 | 
			
		||||
            const disabled = await this.isDisabled(siteId);
 | 
			
		||||
            if (!disabled) {
 | 
			
		||||
                // Get first events.
 | 
			
		||||
                const events = await this.getEventsList(undefined, undefined, undefined, siteId);
 | 
			
		||||
                await this.updateEventsReminders(events, siteId);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the next events for all the sites and schedules their notifications.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     * @deprecated since 4.1. No replacement for that function.
 | 
			
		||||
     */
 | 
			
		||||
    async scheduleEventsNotifications(
 | 
			
		||||
        events: ({ id: number; timestart: number; timeduration: number; name: string})[],
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        if (time === 0) {
 | 
			
		||||
            // Cancel if it was scheduled.
 | 
			
		||||
            return CoreLocalNotifications.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!time) {
 | 
			
		||||
            // Get event default time to calculate the notification time.
 | 
			
		||||
            time = await this.getDefaultNotificationTime(siteId);
 | 
			
		||||
 | 
			
		||||
            if (time === 0) {
 | 
			
		||||
                // Default notification time is disabled, do not show.
 | 
			
		||||
                return CoreLocalNotifications.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            time = event.timestart - time;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        time = time * 1000;
 | 
			
		||||
 | 
			
		||||
        if (time <= Date.now()) {
 | 
			
		||||
            // This reminder is over, don't schedule. Cancel if it was scheduled.
 | 
			
		||||
            return CoreLocalNotifications.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const notificationData: AddonCalendarPushNotificationData = {
 | 
			
		||||
            eventId: event.id,
 | 
			
		||||
            reminderId: reminderId,
 | 
			
		||||
            siteId: siteId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const notification: ILocalNotification = {
 | 
			
		||||
            id: reminderId,
 | 
			
		||||
            title: event.name,
 | 
			
		||||
            text: CoreTimeUtils.userDate(event.timestart * 1000, 'core.strftimedaydatetime', true),
 | 
			
		||||
            icon: 'file://assets/img/icons/calendar.png',
 | 
			
		||||
            trigger: {
 | 
			
		||||
                at: new Date(time),
 | 
			
		||||
            },
 | 
			
		||||
            data: notificationData,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CoreLocalNotifications.schedule(notification, AddonCalendarProvider.COMPONENT, siteId);
 | 
			
		||||
        await AddonCalendar.updateEventsReminders(events, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1526,38 +1498,43 @@ export class AddonCalendarProvider {
 | 
			
		||||
     * If local notification plugin is not enabled, resolve the promise.
 | 
			
		||||
     *
 | 
			
		||||
     * @param events Events to schedule.
 | 
			
		||||
     * @param siteId ID of the site the events belong to. If not defined, use current site.
 | 
			
		||||
     * @param siteId ID of the site the events belong to.
 | 
			
		||||
     * @return Promise resolved when all the notifications have been scheduled.
 | 
			
		||||
     */
 | 
			
		||||
    async scheduleEventsNotifications(
 | 
			
		||||
        events: ({ id: number; timestart: number; timeduration: number; name: string})[],
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    protected async updateEventsReminders(
 | 
			
		||||
        events: ({ id: number; timestart: number; name: string})[],
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        if (!CoreLocalNotifications.isAvailable()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const promises = events.map(async (event) => {
 | 
			
		||||
        await Promise.all(events.map(async (event) => {
 | 
			
		||||
            if (event.timestart * 1000 <= Date.now()) {
 | 
			
		||||
                // The event has already started, don't schedule it.
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
                // @TODO Decide when to completelly remove expired events.
 | 
			
		||||
                return CoreReminders.cancelReminder(event.id, AddonCalendarProvider.COMPONENT, siteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const reminders = await this.getEventReminders(event.id, siteId);
 | 
			
		||||
            const reminders = await CoreReminders.getReminders({
 | 
			
		||||
                instanceId: event.id,
 | 
			
		||||
                component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
            }, siteId);
 | 
			
		||||
 | 
			
		||||
            const p2 = reminders.map((reminder) => {
 | 
			
		||||
                const time = reminder.time ? event.timestart - reminder.time : reminder.time;
 | 
			
		||||
            await Promise.all(reminders.map(async (reminder) => {
 | 
			
		||||
                if (reminder.time !== event.timestart || reminder.title !== event.name) {
 | 
			
		||||
                    reminder.time = event.timestart;
 | 
			
		||||
                    reminder.title = event.name;
 | 
			
		||||
 | 
			
		||||
                return this.scheduleEventNotification(event, reminder.id, time, siteId);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            await Promise.all(p2);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
                    CoreReminders.updateReminder(
 | 
			
		||||
                        reminder,
 | 
			
		||||
                        siteId,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1587,24 +1564,8 @@ export class AddonCalendarProvider {
 | 
			
		||||
        options: AddonCalendarStoreEventsOptions = {},
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(options.siteId);
 | 
			
		||||
        const siteId = site.getId();
 | 
			
		||||
        const addDefaultReminder = options.addDefaultReminder ?? true;
 | 
			
		||||
 | 
			
		||||
        if (addDefaultReminder) {
 | 
			
		||||
            // Add default reminder if the event isn't stored already and doesn't have any reminder.
 | 
			
		||||
            try {
 | 
			
		||||
                await this.getEventFromLocalDb(event.id, siteId);
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Event does not exist.
 | 
			
		||||
                const reminders = await this.getEventReminders(event.id, siteId);
 | 
			
		||||
 | 
			
		||||
                if (reminders.length === 0) {
 | 
			
		||||
                    // No reminders, create the default one.
 | 
			
		||||
                    this.addEventReminder(event, undefined, siteId);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Don't store data that can be calculated like formattedtime, iscategoryevent, etc.
 | 
			
		||||
        let eventRecord: AddonCalendarEventDBRecord = {
 | 
			
		||||
            id: event.id,
 | 
			
		||||
@ -1659,9 +1620,40 @@ export class AddonCalendarProvider {
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (addDefaultReminder) {
 | 
			
		||||
            this.addDefaultEventReminder(eventRecord, site.getId());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(EVENTS_TABLE, eventRecord);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds the default event reminder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param event Event to add the reminder to.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     */
 | 
			
		||||
    protected async addDefaultEventReminder(event: AddonCalendarEventDBRecord, siteId?: string): Promise<void> {
 | 
			
		||||
        // Add default reminder if the event isn't stored already and doesn't have any reminder.
 | 
			
		||||
        const eventExist = await CoreUtils.promiseWorks(this.getEventFromLocalDb(event.id, siteId));
 | 
			
		||||
        if (eventExist) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const reminders = await CoreReminders.getReminders({
 | 
			
		||||
            instanceId: event.id,
 | 
			
		||||
            component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
        }, siteId);
 | 
			
		||||
 | 
			
		||||
        if (reminders.length > 0) {
 | 
			
		||||
            // It already has reminders.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // No reminders, create the default one.
 | 
			
		||||
        await this.addEventReminder(event, undefined, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Store events in local DB.
 | 
			
		||||
     *
 | 
			
		||||
@ -1681,9 +1673,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @param eventId ID of the event. Negative value to edit offline event. If undefined/null, create a new event.
 | 
			
		||||
     * @param formData Form data.
 | 
			
		||||
     * @param timeCreated The time the event was created. Only if modifying a new offline event.
 | 
			
		||||
     * @param forceOffline True to always save it in offline.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param options Calendar submit event options.
 | 
			
		||||
     * @return Promise resolved with the event and a boolean indicating if data was sent to server or stored in offline.
 | 
			
		||||
     */
 | 
			
		||||
    async submitEvent(
 | 
			
		||||
@ -1701,7 +1691,8 @@ export class AddonCalendarProvider {
 | 
			
		||||
            // Now save the reminders if any.
 | 
			
		||||
            if (options.reminders?.length) {
 | 
			
		||||
                await CoreUtils.ignoreErrors(
 | 
			
		||||
                    Promise.all(options.reminders.map((reminder) => this.addEventReminder(event, reminder.time, siteId))),
 | 
			
		||||
                    Promise.all(options.reminders.map((reminder) =>
 | 
			
		||||
                        this.addEventReminder(event, reminder.time, siteId))),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -1723,7 +1714,8 @@ export class AddonCalendarProvider {
 | 
			
		||||
            // Now save the reminders if any.
 | 
			
		||||
            if (options.reminders?.length) {
 | 
			
		||||
                await CoreUtils.ignoreErrors(
 | 
			
		||||
                    Promise.all(options.reminders.map((reminder) => this.addEventReminder(event, reminder.time, siteId))),
 | 
			
		||||
                    Promise.all(options.reminders.map((reminder) =>
 | 
			
		||||
                        this.addEventReminder(event, reminder.time, siteId))),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -1753,6 +1745,7 @@ export class AddonCalendarProvider {
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonCalendarEvent> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        siteId = site.getId();
 | 
			
		||||
 | 
			
		||||
        // Add data that is "hidden" in web.
 | 
			
		||||
        formData.id = eventId > 0 ? eventId : 0;
 | 
			
		||||
@ -1780,9 +1773,16 @@ export class AddonCalendarProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (eventId < 0) {
 | 
			
		||||
            // Offline event has been sent. Change reminders eventid if any.
 | 
			
		||||
            // Offline event has been sent. Change reminders instanceId if any.
 | 
			
		||||
            await CoreUtils.ignoreErrors(
 | 
			
		||||
                site.getDb().updateRecords(REMINDERS_TABLE, { eventid: result.event.id }, { eventid: eventId }),
 | 
			
		||||
                CoreReminders.updateReminders(
 | 
			
		||||
                    { instanceId: result.event.id },
 | 
			
		||||
                    {
 | 
			
		||||
                        instanceId: eventId,
 | 
			
		||||
                        component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
                    },
 | 
			
		||||
                    siteId,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -2316,15 +2316,6 @@ export type AddonCalendarUpdatedEventEvent = {
 | 
			
		||||
    sent?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Additional data sent in push notifications, with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
type AddonCalendarPushNotificationData = {
 | 
			
		||||
    eventId: number;
 | 
			
		||||
    reminderId: number;
 | 
			
		||||
    siteId: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Value and unit for reminders.
 | 
			
		||||
 */
 | 
			
		||||
@ -2338,7 +2329,7 @@ export type AddonCalendarValueAndUnit = {
 | 
			
		||||
 */
 | 
			
		||||
export type AddonCalendarSubmitEventOptions = {
 | 
			
		||||
    reminders?: {
 | 
			
		||||
        time: number | null;
 | 
			
		||||
        time?: number;
 | 
			
		||||
    }[];
 | 
			
		||||
    forceOffline?: boolean;
 | 
			
		||||
    siteId?: string; // Site ID. If not defined, current site.
 | 
			
		||||
 | 
			
		||||
@ -22,10 +22,9 @@ import { AddonCalendar, AddonCalendarEventType, AddonCalendarProvider } from '..
 | 
			
		||||
 * Database variables for AddonCalendarProvider service.
 | 
			
		||||
 */
 | 
			
		||||
export const EVENTS_TABLE = 'addon_calendar_events_3';
 | 
			
		||||
export const REMINDERS_TABLE = 'addon_calendar_reminders_2';
 | 
			
		||||
export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'AddonCalendarProvider',
 | 
			
		||||
    version: 4,
 | 
			
		||||
    version: 5,
 | 
			
		||||
    canBeCleared: [EVENTS_TABLE],
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
@ -179,47 +178,8 @@ export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: REMINDERS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'id',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                    primaryKey: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'eventid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'time',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timecreated',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            uniqueKeys: [
 | 
			
		||||
                ['eventid', 'time'],
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    async migrate(db: SQLiteDB, oldVersion: number, siteId: string): Promise<void> {
 | 
			
		||||
        if (oldVersion < 3) {
 | 
			
		||||
            // Migrate calendar events. New format @since 3.7.
 | 
			
		||||
            let oldTable = 'addon_calendar_events_2';
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await db.tableExists(oldTable);
 | 
			
		||||
            } catch {
 | 
			
		||||
                // The v2 table doesn't exist, try with v1.
 | 
			
		||||
                oldTable = 'addon_calendar_events';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await db.migrateTable(oldTable, EVENTS_TABLE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (oldVersion < 4) {
 | 
			
		||||
            // Migrate default notification time if it was changed.
 | 
			
		||||
            // Don't use getDefaultNotificationTime to be able to detect if the value was changed or not.
 | 
			
		||||
@ -230,50 +190,6 @@ export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
                // Convert from minutes to seconds.
 | 
			
		||||
                AddonCalendar.setDefaultNotificationTime(defaultTime * 60, siteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Migrate reminders. New format @since 4.0.
 | 
			
		||||
            const oldTable = 'addon_calendar_reminders';
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await db.tableExists(oldTable);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                // Old table does not exist, ignore.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const records = await db.getAllRecords<AddonCalendarReminderDBRecord>(oldTable);
 | 
			
		||||
            const events: Record<number, AddonCalendarEventDBRecord> = {};
 | 
			
		||||
 | 
			
		||||
            await Promise.all(records.map(async (record) => {
 | 
			
		||||
                // Get the event to compare the reminder time with the event time.
 | 
			
		||||
                if (!events[record.eventid]) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        events[record.eventid] = await db.getRecord(EVENTS_TABLE, { id: record.eventid });
 | 
			
		||||
                    } catch {
 | 
			
		||||
                        // Event not found in local DB, shouldn't happen. Ignore the reminder.
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!record.time || record.time === -1) {
 | 
			
		||||
                    // Default reminder. Use null now.
 | 
			
		||||
                    record.time = null;
 | 
			
		||||
                } else if (record.time > events[record.eventid].timestart) {
 | 
			
		||||
                    // Reminder is after the event, ignore it.
 | 
			
		||||
                    return;
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Remove seconds from the old reminder, it could include seconds by mistake.
 | 
			
		||||
                    record.time = events[record.eventid].timestart - Math.floor(record.time / 60) * 60;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return db.insertRecord(REMINDERS_TABLE, record);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await db.dropTable(oldTable);
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                // Error deleting old table, ignore.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
@ -318,10 +234,3 @@ export type AddonCalendarEventDBRecord = {
 | 
			
		||||
    maxdaytimestamp?: number;
 | 
			
		||||
    draggable?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AddonCalendarReminderDBRecord = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    eventid: number;
 | 
			
		||||
    time: number | null; // Number of seconds before the event, null for default time.
 | 
			
		||||
    timecreated?: number | null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -27,9 +27,11 @@ import { CoreGradesModule } from './grades/grades.module';
 | 
			
		||||
import { CoreH5PModule } from './h5p/h5p.module';
 | 
			
		||||
import { CoreLoginModule } from './login/login.module';
 | 
			
		||||
import { CoreMainMenuModule } from './mainmenu/mainmenu.module';
 | 
			
		||||
import { CoreNativeModule } from '@features/native/native.module';
 | 
			
		||||
import { CorePushNotificationsModule } from './pushnotifications/pushnotifications.module';
 | 
			
		||||
import { CoreQuestionModule } from './question/question.module';
 | 
			
		||||
import { CoreRatingModule } from './rating/rating.module';
 | 
			
		||||
import { CoreRemindersModule } from './reminders/reminders.module';
 | 
			
		||||
import { CoreSearchModule } from './search/search.module';
 | 
			
		||||
import { CoreSettingsModule } from './settings/settings.module';
 | 
			
		||||
import { CoreSharedFilesModule } from './sharedfiles/sharedfiles.module';
 | 
			
		||||
@ -37,11 +39,10 @@ import { CoreSiteHomeModule } from './sitehome/sitehome.module';
 | 
			
		||||
import { CoreSitePluginsModule } from './siteplugins/siteplugins.module';
 | 
			
		||||
import { CoreStylesModule } from './styles/styles.module';
 | 
			
		||||
import { CoreTagModule } from './tag/tag.module';
 | 
			
		||||
import { CoreUserToursModule } from './usertours/user-tours.module';
 | 
			
		||||
import { CoreUserModule } from './user/user.module';
 | 
			
		||||
import { CoreUserToursModule } from './usertours/user-tours.module';
 | 
			
		||||
import { CoreViewerModule } from './viewer/viewer.module';
 | 
			
		||||
import { CoreXAPIModule } from './xapi/xapi.module';
 | 
			
		||||
import { CoreNativeModule } from '@features/native/native.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -61,15 +62,16 @@ import { CoreNativeModule } from '@features/native/native.module';
 | 
			
		||||
        CorePushNotificationsModule,
 | 
			
		||||
        CoreQuestionModule,
 | 
			
		||||
        CoreRatingModule,
 | 
			
		||||
        CoreRemindersModule,
 | 
			
		||||
        CoreSearchModule,
 | 
			
		||||
        CoreSettingsModule,
 | 
			
		||||
        CoreSharedFilesModule,
 | 
			
		||||
        CoreSiteHomeModule,
 | 
			
		||||
        CoreSitePluginsModule,
 | 
			
		||||
        CoreTagModule,
 | 
			
		||||
        CoreStylesModule,
 | 
			
		||||
        CoreUserToursModule,
 | 
			
		||||
        CoreTagModule,
 | 
			
		||||
        CoreUserModule,
 | 
			
		||||
        CoreUserToursModule,
 | 
			
		||||
        CoreViewerModule,
 | 
			
		||||
        CoreXAPIModule,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										42
									
								
								src/core/features/reminders/reminders.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/core/features/reminders/reminders.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
import { REMINDERS_SITE_SCHEMA } from './services/database/reminders';
 | 
			
		||||
import { CoreReminders, CoreRemindersService } from './services/reminders';
 | 
			
		||||
 | 
			
		||||
export const CORE_REMINDERS_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    CoreRemindersService,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [REMINDERS_SITE_SCHEMA],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            useValue: async () => {
 | 
			
		||||
                CoreReminders.scheduleAllNotifications();
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreRemindersModule {}
 | 
			
		||||
							
								
								
									
										225
									
								
								src/core/features/reminders/services/database/reminders.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								src/core/features/reminders/services/database/reminders.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,225 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AddonCalendarProvider } from '@addons/calendar/services/calendar';
 | 
			
		||||
import { AddonCalendarEventDBRecord, EVENTS_TABLE } from '@addons/calendar/services/database/calendar';
 | 
			
		||||
import { SQLiteDB } from '@classes/sqlitedb';
 | 
			
		||||
import { CoreSiteSchema } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreReminderData, CoreRemindersService } from '../reminders';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Database variables for CoreRemindersService service.
 | 
			
		||||
 */
 | 
			
		||||
export const REMINDERS_TABLE = 'core_reminders';
 | 
			
		||||
export const REMINDERS_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'CoreRemindersService',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    canBeCleared: [],
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: REMINDERS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'id',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                    primaryKey: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'component',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                    notNull: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'instanceId',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                    notNull: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'type',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                    notNull: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'time',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                    notNull: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timebefore',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                    notNull: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'title',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                    notNull: true,
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'url',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
 | 
			
		||||
            ],
 | 
			
		||||
            uniqueKeys: [
 | 
			
		||||
                ['component', 'instanceId', 'timebefore'],
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    install: async (db: SQLiteDB): Promise<void> => {
 | 
			
		||||
        await migrateFromCalendarRemindersV1(db);
 | 
			
		||||
        await migrateFromCalendarRemindersV2(db);
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const migrateFromCalendarRemindersV1 = async (db: SQLiteDB): Promise<void> => {
 | 
			
		||||
    // Migrate reminders. New format @since 4.0.
 | 
			
		||||
    const oldTable = 'addon_calendar_reminders';
 | 
			
		||||
 | 
			
		||||
    const tableExists = await CoreUtils.promiseWorks(db.tableExists(oldTable));
 | 
			
		||||
    if (!tableExists) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const records = await db.getAllRecords<AddonCalendarReminderDBRecord>(oldTable);
 | 
			
		||||
    const events: Record<number, AddonCalendarEventDBRecord> = {};
 | 
			
		||||
    const uniqueReminder: Record<number, number[]> = {};
 | 
			
		||||
 | 
			
		||||
    await Promise.all(records.map(async (record) => {
 | 
			
		||||
        // Get the event to compare the reminder time with the event time.
 | 
			
		||||
        if (!events[record.eventid]) {
 | 
			
		||||
            try {
 | 
			
		||||
                events[record.eventid] = await db.getRecord(EVENTS_TABLE, { id: record.eventid });
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Event not found in local DB, shouldn't happen. Ignore the reminder.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const event = events[record.eventid];
 | 
			
		||||
 | 
			
		||||
        let reminderTime = record.time;
 | 
			
		||||
 | 
			
		||||
        if (!reminderTime || reminderTime === -1) {
 | 
			
		||||
            // Default reminder.
 | 
			
		||||
            reminderTime = CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE;
 | 
			
		||||
        } else if (reminderTime > event.timestart) {
 | 
			
		||||
            // Reminder is after the event, ignore it.
 | 
			
		||||
            return;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Remove seconds from the old reminder, it could include seconds by mistake.
 | 
			
		||||
            reminderTime = event.timestart - Math.floor(reminderTime / 60) * 60;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (typeof uniqueReminder[record.eventid] === undefined) {
 | 
			
		||||
            uniqueReminder[record.eventid] = [];
 | 
			
		||||
        } else {
 | 
			
		||||
            if (uniqueReminder[record.eventid].includes(reminderTime)) {
 | 
			
		||||
                // Reminder already exists.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await createReminder(db, event, reminderTime);
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        await db.dropTable(oldTable);
 | 
			
		||||
    } catch {
 | 
			
		||||
        // Error deleting old table, ignore.
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const migrateFromCalendarRemindersV2 = async (db: SQLiteDB): Promise<void> => {
 | 
			
		||||
    const oldTable = 'addon_calendar_reminders_2';
 | 
			
		||||
 | 
			
		||||
    const tableExists = await CoreUtils.promiseWorks(db.tableExists(oldTable));
 | 
			
		||||
    if (!tableExists) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const records = await db.getAllRecords<AddonCalendarReminderDBRecord>(oldTable);
 | 
			
		||||
    const events: Record<number, AddonCalendarEventDBRecord> = {};
 | 
			
		||||
    const uniqueReminder: Record<number, number[]> = {};
 | 
			
		||||
 | 
			
		||||
    await Promise.all(records.map(async (record) => {
 | 
			
		||||
        // Get the event to compare the reminder time with the event time.
 | 
			
		||||
        if (!events[record.eventid]) {
 | 
			
		||||
            try {
 | 
			
		||||
                events[record.eventid] = await db.getRecord(EVENTS_TABLE, { id: record.eventid });
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Event not found in local DB, shouldn't happen. Ignore the reminder.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        const event = events[record.eventid];
 | 
			
		||||
 | 
			
		||||
        const reminderTime = record.time || CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE;
 | 
			
		||||
 | 
			
		||||
        if (typeof uniqueReminder[record.eventid] === undefined) {
 | 
			
		||||
            uniqueReminder[record.eventid] = [];
 | 
			
		||||
        } else {
 | 
			
		||||
            if (uniqueReminder[record.eventid].includes(reminderTime)) {
 | 
			
		||||
                // Reminder already exists.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uniqueReminder[record.eventid].push(reminderTime);
 | 
			
		||||
 | 
			
		||||
        await createReminder(db, event, reminderTime);
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        await db.dropTable(oldTable);
 | 
			
		||||
    } catch {
 | 
			
		||||
        // Error deleting old table, ignore.
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const createReminder = async (
 | 
			
		||||
    db: SQLiteDB,
 | 
			
		||||
    event: AddonCalendarEventDBRecord,
 | 
			
		||||
    reminderTime: number,
 | 
			
		||||
): Promise<void> => {
 | 
			
		||||
    const reminder: CoreReminderData = {
 | 
			
		||||
        component: AddonCalendarProvider.COMPONENT,
 | 
			
		||||
        instanceId: event.id,
 | 
			
		||||
        type: event.eventtype,
 | 
			
		||||
        timebefore: reminderTime,
 | 
			
		||||
        url: event.url,
 | 
			
		||||
        title: event.name,
 | 
			
		||||
        time: event.timestart,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    await db.insertRecord(REMINDERS_TABLE, reminder);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type CoreReminderDBRecord = {
 | 
			
		||||
    id: number; // Reminder ID.
 | 
			
		||||
    component: string; // Component where the reminder belongs.
 | 
			
		||||
    instanceId: number; // Instance Id where the reminder belongs.
 | 
			
		||||
    type: string; // Event idenfier type.
 | 
			
		||||
    time: number; // Event time.
 | 
			
		||||
    timebefore: number; // Seconds before the event to remind.
 | 
			
		||||
    title: string; // Notification title.
 | 
			
		||||
    url?: string; // URL where to redirect the user.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type AddonCalendarReminderDBRecord = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    eventid: number;
 | 
			
		||||
    time: number | null; // Number of seconds before the event, null for default time.
 | 
			
		||||
    timecreated?: number | null;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										300
									
								
								src/core/features/reminders/services/reminders.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								src/core/features/reminders/services/reminders.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,300 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AddonCalendar, AddonCalendarProvider } from '@addons/calendar/services/calendar';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreLocalNotifications } from '@services/local-notifications';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreReminderDBRecord, REMINDERS_TABLE } from './database/reminders';
 | 
			
		||||
import { ILocalNotification } from '@ionic-native/local-notifications';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle reminders.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreRemindersService {
 | 
			
		||||
 | 
			
		||||
    static readonly DEFAULT_REMINDER_TIMEBEFORE = -1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns if Reminders are enabled.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True if reminders are enabled and available, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): boolean {
 | 
			
		||||
        return CoreLocalNotifications.isAvailable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Save reminder to Database.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reminder Reminder to set.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Resolved when done. Rejected on failure.
 | 
			
		||||
     */
 | 
			
		||||
    async addReminder(reminder: CoreReminderData, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const reminderId = await site.getDb().insertRecord(REMINDERS_TABLE, reminder);
 | 
			
		||||
 | 
			
		||||
        const reminderRecord: CoreReminderDBRecord = Object.assign(reminder, { id: reminderId });
 | 
			
		||||
 | 
			
		||||
        await this.scheduleNotification(reminderRecord, site.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update a reminder from local Db.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reminder Fields to update.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the reminder data is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async updateReminder(
 | 
			
		||||
        reminder: CoreReminderDBRecord,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.getDb().updateRecords(REMINDERS_TABLE, reminder, { id: reminder.id });
 | 
			
		||||
 | 
			
		||||
        // Reschedule.
 | 
			
		||||
        await this.scheduleNotification(reminder, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update all reminders of a component and instance from local Db.
 | 
			
		||||
     *
 | 
			
		||||
     * @param newFields Fields to update.
 | 
			
		||||
     * @param selector Reminder selector.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the reminder data is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async updateReminders(
 | 
			
		||||
        newFields: Partial<CoreReminderData>,
 | 
			
		||||
        selector: CoreReminderSelector,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const reminders = await this.getReminders(selector, site.getId());
 | 
			
		||||
 | 
			
		||||
        await Promise.all(reminders.map((reminder) => {
 | 
			
		||||
            reminder = Object.assign(reminder, newFields);
 | 
			
		||||
 | 
			
		||||
            return this.updateReminder(reminder, site.getId());
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all reminders from local Db.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the reminder data is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllReminders( siteId?: string): Promise<CoreReminderDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecords(REMINDERS_TABLE, undefined, 'time ASC');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all reminders of a component and instance from local Db.
 | 
			
		||||
     *
 | 
			
		||||
     * @param selector Reminder selector.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the reminder data is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getReminders(selector: CoreReminderSelector, siteId?: string): Promise<CoreReminderDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecords(REMINDERS_TABLE, selector, 'time ASC');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all reminders of a component with default time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param component Component.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the reminder data is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getRemindersWithDefaultTime(component: string, siteId?: string): Promise<CoreReminderDBRecord[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.getDb().getRecords<CoreReminderDBRecord>(
 | 
			
		||||
            REMINDERS_TABLE,
 | 
			
		||||
            { component, timebefore: CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE },
 | 
			
		||||
            'time ASC',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove a reminder and cancel the notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id Reminder ID.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the notification is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async removeReminder(id: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const reminder = await site.getDb().getRecord<CoreReminderDBRecord>(REMINDERS_TABLE, { id });
 | 
			
		||||
 | 
			
		||||
        if (this.isEnabled()) {
 | 
			
		||||
            this.cancelReminder(id, reminder.component, site.getId());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(REMINDERS_TABLE, { id });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove all reminders of the same element.
 | 
			
		||||
     *
 | 
			
		||||
     * @param selector Reminder selector.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the notification is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async removeReminders(selector: CoreReminderSelector, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        siteId = site.getId();
 | 
			
		||||
 | 
			
		||||
        if (this.isEnabled()) {
 | 
			
		||||
            const reminders = await this.getReminders(selector, siteId);
 | 
			
		||||
 | 
			
		||||
            reminders.forEach((reminder) => {
 | 
			
		||||
                this.cancelReminder(reminder.id, reminder.component, siteId);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(REMINDERS_TABLE, selector);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cancel a notification for a reminder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reminderId Reminder Id to cancel.
 | 
			
		||||
     * @param component Reminder component.
 | 
			
		||||
     * @param siteId ID of the site the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @returns
 | 
			
		||||
     */
 | 
			
		||||
    async cancelReminder(reminderId: number, component: string, siteId?: string): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        return CoreLocalNotifications.cancel(reminderId, component, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Schedules a notification. If local notification plugin is not enabled, resolve the promise.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reminder Reminder to schedule.
 | 
			
		||||
     * @param siteId Site ID the reminder belongs to. If not defined, use current site.
 | 
			
		||||
     * @return Promise resolved when the notification is scheduled.
 | 
			
		||||
     */
 | 
			
		||||
    async scheduleNotification(
 | 
			
		||||
        reminder: CoreReminderDBRecord,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        if (!this.isEnabled()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const timebefore = reminder.timebefore === CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE
 | 
			
		||||
            ? await AddonCalendar.getDefaultNotificationTime(siteId)
 | 
			
		||||
            : reminder.timebefore;
 | 
			
		||||
 | 
			
		||||
        if (timebefore === AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
 | 
			
		||||
            // Notification disabled. Cancel.
 | 
			
		||||
            return this.cancelReminder(reminder.id, reminder.component, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const notificationTime = (reminder.time - timebefore) * 1000;
 | 
			
		||||
 | 
			
		||||
        if (notificationTime <= Date.now()) { // @TODO Add a threshold.
 | 
			
		||||
            // This reminder is over, don't schedule. Cancel if it was scheduled.
 | 
			
		||||
            return this.cancelReminder(reminder.id, reminder.component, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const notificationData: CoreRemindersPushNotificationData = {
 | 
			
		||||
            reminderId: reminder.id,
 | 
			
		||||
            instanceId: reminder.instanceId,
 | 
			
		||||
            siteId: siteId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const notification: ILocalNotification = {
 | 
			
		||||
            id: reminder.id,
 | 
			
		||||
            title: reminder.title,
 | 
			
		||||
            text: CoreTimeUtils.userDate(reminder.time * 1000, 'core.strftimedaydatetime', true),
 | 
			
		||||
            icon: 'file://assets/img/icons/calendar.png',
 | 
			
		||||
            trigger: {
 | 
			
		||||
                at: new Date(notificationTime),
 | 
			
		||||
            },
 | 
			
		||||
            data: notificationData,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CoreLocalNotifications.schedule(notification, reminder.component, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the all saved reminders and schedule the notification.
 | 
			
		||||
     * If local notification plugin is not enabled, resolve the promise.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when all the notifications have been scheduled.
 | 
			
		||||
     */
 | 
			
		||||
    async scheduleAllNotifications(): Promise<void> {
 | 
			
		||||
        await CorePlatform.ready();
 | 
			
		||||
 | 
			
		||||
        if (!this.isEnabled()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const siteIds = await CoreSites.getSitesIds();
 | 
			
		||||
 | 
			
		||||
        await Promise.all(siteIds.map((siteId: string) => async () => {
 | 
			
		||||
            const reminders = await this.getAllReminders(siteId);
 | 
			
		||||
 | 
			
		||||
            reminders.forEach((reminder) => {
 | 
			
		||||
                this.scheduleNotification(reminder, siteId);
 | 
			
		||||
            });
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreReminders = makeSingleton(CoreRemindersService);
 | 
			
		||||
 | 
			
		||||
export type CoreReminderData = Omit<CoreReminderDBRecord, 'id'>;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Additional data sent in push notifications, with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreRemindersPushNotificationData = {
 | 
			
		||||
    reminderId: number;
 | 
			
		||||
    instanceId: number;
 | 
			
		||||
    siteId: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type CoreReminderNotificationOptions = {
 | 
			
		||||
    title: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type CoreReminderSelector = {
 | 
			
		||||
    instanceId: number;
 | 
			
		||||
    component: string;
 | 
			
		||||
    type?: string;
 | 
			
		||||
};
 | 
			
		||||
@ -123,7 +123,7 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
    async initializeDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user