MOBILE-3936 reminders: Move calendar reminders to new reminders module
parent
3d7b9dfbc5
commit
cefd0248fc
|
@ -70,7 +70,7 @@ const mainMenuChildrenRoutes: Routes = [
|
||||||
|
|
||||||
await AddonCalendar.initialize();
|
await AddonCalendar.initialize();
|
||||||
|
|
||||||
AddonCalendar.scheduleAllSitesEventsNotifications();
|
AddonCalendar.updateAllSitesEventReminders();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -31,9 +31,10 @@ import {
|
||||||
AddonCalendarEventType,
|
AddonCalendarEventType,
|
||||||
AddonCalendar,
|
AddonCalendar,
|
||||||
AddonCalendarSubmitCreateUpdateFormDataWSParams,
|
AddonCalendarSubmitCreateUpdateFormDataWSParams,
|
||||||
|
AddonCalendarReminderUnits,
|
||||||
} from '../../services/calendar';
|
} from '../../services/calendar';
|
||||||
import { AddonCalendarOffline } from '../../services/calendar-offline';
|
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 { AddonCalendarSync, AddonCalendarSyncProvider } from '../../services/calendar-sync';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
|
@ -156,7 +157,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
/**
|
/**
|
||||||
* Fetch the data needed to render the form.
|
* Fetch the data needed to render the form.
|
||||||
*
|
*
|
||||||
* @param refresh Whether it's refreshing data.
|
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async fetchData(): Promise<void> {
|
protected async fetchData(): Promise<void> {
|
||||||
|
@ -273,15 +273,14 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const courseFillterFullname = (course: CoreCourseSearchedData | CoreEnrolledCourseData): Promise<void> =>
|
const courseFillFullname = async (course: CoreCourseSearchedData | CoreEnrolledCourseData): Promise<void> => {
|
||||||
CoreFilterHelper.getFiltersAndFormatText(course.fullname, 'course', course.id)
|
try {
|
||||||
.then((result) => {
|
const result = await CoreFilterHelper.getFiltersAndFormatText(course.fullname, 'course', course.id);
|
||||||
course.fullname = result.text;
|
course.fullname = result.text;
|
||||||
|
} catch {
|
||||||
return;
|
// Ignore errors.
|
||||||
}).catch(() => {
|
}
|
||||||
// Ignore errors.
|
};
|
||||||
});
|
|
||||||
|
|
||||||
if (this.showAll) {
|
if (this.showAll) {
|
||||||
// Remove site home from the list of courses.
|
// 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.
|
// Format the name of the courses.
|
||||||
if ('contacts' in courses[0]) {
|
if ('contacts' in courses[0]) {
|
||||||
await Promise.all((courses as CoreCourseSearchedData[]).map(courseFillterFullname));
|
await Promise.all((courses as CoreCourseSearchedData[]).map(courseFillFullname));
|
||||||
} else {
|
} else {
|
||||||
await Promise.all((courses as CoreEnrolledCourseData[]).map(courseFillterFullname));
|
await Promise.all((courses as CoreEnrolledCourseData[]).map(courseFillFullname));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort courses by name.
|
// Sort courses by name.
|
||||||
|
@ -461,17 +460,17 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
const timeDurationMinutes = parseInt(formData.timedurationminutes || '', 10);
|
const timeDurationMinutes = parseInt(formData.timedurationminutes || '', 10);
|
||||||
let error: string | undefined;
|
let error: string | undefined;
|
||||||
|
|
||||||
if (formData.eventtype == AddonCalendarEventType.COURSE && !formData.courseid) {
|
if (formData.eventtype === AddonCalendarEventType.COURSE && !formData.courseid) {
|
||||||
error = 'core.selectacourse';
|
error = 'core.selectacourse';
|
||||||
} else if (formData.eventtype == AddonCalendarEventType.GROUP && !formData.groupcourseid) {
|
} else if (formData.eventtype === AddonCalendarEventType.GROUP && !formData.groupcourseid) {
|
||||||
error = 'core.selectacourse';
|
error = 'core.selectacourse';
|
||||||
} else if (formData.eventtype == AddonCalendarEventType.GROUP && !formData.groupid) {
|
} else if (formData.eventtype === AddonCalendarEventType.GROUP && !formData.groupid) {
|
||||||
error = 'core.selectagroup';
|
error = 'core.selectagroup';
|
||||||
} else if (formData.eventtype == AddonCalendarEventType.CATEGORY && !formData.categoryid) {
|
} else if (formData.eventtype === AddonCalendarEventType.CATEGORY && !formData.categoryid) {
|
||||||
error = 'core.selectacategory';
|
error = 'core.selectacategory';
|
||||||
} else if (formData.duration == 1 && timeStartDate > timeUntilDate) {
|
} else if (formData.duration === 1 && timeStartDate > timeUntilDate) {
|
||||||
error = 'addon.calendar.invalidtimedurationuntil';
|
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';
|
error = 'addon.calendar.invalidtimedurationminutes';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,8 +533,11 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
|
|
||||||
if (result.sent) {
|
if (result.sent) {
|
||||||
// Event created or edited, invalidate right days & months.
|
// Event created or edited, invalidate right days & months.
|
||||||
const numberOfRepetitions = formData.repeat ? formData.repeats :
|
const numberOfRepetitions = formData.repeat
|
||||||
(data.repeateditall && this.otherEventsCount ? this.otherEventsCount + 1 : 1);
|
? formData.repeats
|
||||||
|
: (data.repeateditall && this.otherEventsCount
|
||||||
|
? this.otherEventsCount + 1
|
||||||
|
: 1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AddonCalendarHelper.refreshAfterChangeEvent(result.event, numberOfRepetitions);
|
await AddonCalendarHelper.refreshAfterChangeEvent(result.event, numberOfRepetitions);
|
||||||
|
@ -651,7 +653,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
|
|
||||||
// Check if default reminders are enabled.
|
// Check if default reminders are enabled.
|
||||||
const defaultTime = await AddonCalendar.getDefaultNotificationTime(this.currentSite.getId());
|
const defaultTime = await AddonCalendar.getDefaultNotificationTime(this.currentSite.getId());
|
||||||
if (defaultTime === 0) {
|
if (defaultTime === AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,7 +661,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
|
|
||||||
// Add default reminder.
|
// Add default reminder.
|
||||||
this.reminders.push({
|
this.reminders.push({
|
||||||
time: null,
|
|
||||||
value: data.value,
|
value: data.value,
|
||||||
unit: data.unit,
|
unit: data.unit,
|
||||||
label: AddonCalendar.getUnitValueLabel(data.value, data.unit, true),
|
label: AddonCalendar.getUnitValueLabel(data.value, data.unit, true),
|
||||||
|
@ -697,7 +698,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
*/
|
*/
|
||||||
removeReminder(reminder: AddonCalendarEventCandidateReminder): void {
|
removeReminder(reminder: AddonCalendarEventCandidateReminder): void {
|
||||||
const index = this.reminders.indexOf(reminder);
|
const index = this.reminders.indexOf(reminder);
|
||||||
if (index != -1) {
|
if (index !== -1) {
|
||||||
this.reminders.splice(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 class="item-heading">{{ reminder.label }}</p>
|
||||||
<p *ngIf="reminder.sublabel">{{ reminder.sublabel }}</p>
|
<p *ngIf="reminder.sublabel">{{ reminder.sublabel }}</p>
|
||||||
</ion-label>
|
</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">
|
slot="end">
|
||||||
<ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true"></ion-icon>
|
<ion-icon name="fas-trash" color="danger" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
|
|
@ -42,6 +42,7 @@ import { AddonCalendarReminderTimeModalComponent } from '@addons/calendar/compon
|
||||||
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
|
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
|
||||||
import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source';
|
import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source';
|
||||||
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
|
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
|
||||||
|
import { CoreReminders } from '@features/reminders/services/reminders';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays a single calendar event.
|
* Page that displays a single calendar event.
|
||||||
|
@ -152,8 +153,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reminders = await AddonCalendar.getEventReminders(this.eventId, this.currentSiteId);
|
this.reminders = await AddonCalendarHelper.getEventReminders(this.eventId, this.event.timestart, this.currentSiteId);
|
||||||
this.reminders = await AddonCalendarHelper.formatReminders(reminders, 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 id Reminder ID.
|
||||||
* @param e Click event.
|
* @param e Click event.
|
||||||
*/
|
*/
|
||||||
async cancelNotification(id: number, e: Event): Promise<void> {
|
async deleteReminder(id: number, e: Event): Promise<void> {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -417,7 +417,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
||||||
const modal = await CoreDomUtils.showModalLoading('core.deleting', true);
|
const modal = await CoreDomUtils.showModalLoading('core.deleting', true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AddonCalendar.deleteEventReminder(id);
|
await CoreReminders.removeReminder(id);
|
||||||
await this.loadReminders();
|
await this.loadReminders();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'Error deleting reminder');
|
CoreDomUtils.showErrorModalDefault(error, 'Error deleting reminder');
|
||||||
|
|
|
@ -23,7 +23,6 @@ import {
|
||||||
AddonCalendarEventType,
|
AddonCalendarEventType,
|
||||||
AddonCalendarGetEventsEvent,
|
AddonCalendarGetEventsEvent,
|
||||||
AddonCalendarProvider,
|
AddonCalendarProvider,
|
||||||
AddonCalendarReminderUnits,
|
|
||||||
AddonCalendarWeek,
|
AddonCalendarWeek,
|
||||||
AddonCalendarWeekDay,
|
AddonCalendarWeekDay,
|
||||||
} from './calendar';
|
} from './calendar';
|
||||||
|
@ -36,8 +35,8 @@ import { makeSingleton } from '@singletons';
|
||||||
import { AddonCalendarSyncInvalidateEvent } from './calendar-sync';
|
import { AddonCalendarSyncInvalidateEvent } from './calendar-sync';
|
||||||
import { AddonCalendarOfflineEventDBRecord } from './database/calendar-offline';
|
import { AddonCalendarOfflineEventDBRecord } from './database/calendar-offline';
|
||||||
import { CoreCategoryData } from '@features/courses/services/courses';
|
import { CoreCategoryData } from '@features/courses/services/courses';
|
||||||
import { AddonCalendarReminderDBRecord } from './database/calendar';
|
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
|
import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context levels enumeration.
|
* 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,
|
* 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.
|
* 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.
|
* @return Object with the classified events.
|
||||||
*/
|
*/
|
||||||
classifyIntoMonths(
|
classifyIntoMonths(
|
||||||
|
@ -219,7 +218,7 @@ export class AddonCalendarHelperProvider {
|
||||||
/**
|
/**
|
||||||
* Convenience function to format some event data to be rendered.
|
* Convenience function to format some event data to be rendered.
|
||||||
*
|
*
|
||||||
* @param e Event to format.
|
* @param event Event to format.
|
||||||
*/
|
*/
|
||||||
formatOfflineEventData(event: AddonCalendarOfflineEventDBRecord): AddonCalendarEventToDisplay {
|
formatOfflineEventData(event: AddonCalendarOfflineEventDBRecord): AddonCalendarEventToDisplay {
|
||||||
|
|
||||||
|
@ -295,48 +294,75 @@ export class AddonCalendarHelperProvider {
|
||||||
* @param timestart Event timestart.
|
* @param timestart Event timestart.
|
||||||
* @param siteId Site ID.
|
* @param siteId Site ID.
|
||||||
* @return Formatted reminders.
|
* @return Formatted reminders.
|
||||||
|
* @deprecated since 4.1 Use AddonCalendarHelper.getEventReminders.
|
||||||
*/
|
*/
|
||||||
async formatReminders(
|
async formatReminders(
|
||||||
reminders: AddonCalendarReminderDBRecord[],
|
reminders: { eventid: number }[],
|
||||||
timestart: number,
|
timestart: number,
|
||||||
siteId?: string,
|
siteId?: string,
|
||||||
): Promise<AddonCalendarEventReminder[]> {
|
): Promise<AddonCalendarEventReminder[]> {
|
||||||
const defaultTime = await AddonCalendar.getDefaultNotificationTime(siteId);
|
if (!reminders.length) {
|
||||||
|
return [];
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return formattedReminders.map((reminder) => {
|
return AddonCalendarHelper.getEventReminders(reminders[0].eventid, timestart, siteId);
|
||||||
if (reminder.time === null) {
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
// Default time. Check if default notifications are disabled.
|
||||||
if (defaultTimeValue !== undefined && defaultTimeUnit) {
|
if (defaultLabel !== undefined) {
|
||||||
reminder.value = defaultTimeValue;
|
formatted.label = defaultLabel;
|
||||||
reminder.unit = defaultTimeUnit;
|
formatted.timestamp = eventTimestart - defaultTime;
|
||||||
reminder.timestamp = eventTimestart - reminder.value * reminder.unit;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const data = AddonCalendarProvider.convertSecondsToValueAndUnit(reminder.time);
|
const data = AddonCalendarProvider.convertSecondsToValueAndUnit(reminder.timebefore);
|
||||||
reminder.value = data.value;
|
formatted.label = AddonCalendar.getUnitValueLabel(data.value, data.unit, false);
|
||||||
reminder.unit = data.unit;
|
formatted.timestamp = eventTimestart - reminder.timebefore;
|
||||||
reminder.timestamp = eventTimestart - reminder.time;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reminder.value && reminder.unit) {
|
if (formatted.timestamp) {
|
||||||
reminder.label = AddonCalendar.getUnitValueLabel(reminder.value, reminder.unit, reminder.time === null);
|
formatted.sublabel = CoreTimeUtils.userDate(formatted.timestamp * 1000, 'core.strftimedatetime');
|
||||||
if (reminder.timestamp) {
|
|
||||||
reminder.sublabel = CoreTimeUtils.userDate(reminder.timestamp * 1000, 'core.strftimedatetime');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return reminder;
|
return formatted;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +407,7 @@ export class AddonCalendarHelperProvider {
|
||||||
/**
|
/**
|
||||||
* Get the day "id".
|
* Get the day "id".
|
||||||
*
|
*
|
||||||
* @param day Day moment.
|
* @param moment Day moment.
|
||||||
* @return The "id".
|
* @return The "id".
|
||||||
*/
|
*/
|
||||||
getDayId(moment: moment.Moment): string {
|
getDayId(moment: moment.Moment): string {
|
||||||
|
@ -553,18 +579,18 @@ export class AddonCalendarHelperProvider {
|
||||||
courseId: number,
|
courseId: number,
|
||||||
categoryId?: number,
|
categoryId?: number,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (event.eventtype == 'user' || event.eventtype == 'site') {
|
if (event.eventtype === 'user' || event.eventtype === 'site') {
|
||||||
// User or site event, display it.
|
// User or site event, display it.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.eventtype == 'category' && categories) {
|
if (event.eventtype === 'category' && categories) {
|
||||||
if (!event.categoryid || !Object.keys(categories).length || !categoryId) {
|
if (!event.categoryid || !Object.keys(categories).length || !categoryId) {
|
||||||
// We can't tell if the course belongs to the category, display them all.
|
// We can't tell if the course belongs to the category, display them all.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.categoryid == categoryId) {
|
if (event.categoryid === categoryId) {
|
||||||
// The event is in the same category as the course, display it.
|
// The event is in the same category as the course, display it.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -577,7 +603,7 @@ export class AddonCalendarHelperProvider {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.categoryid == category.parent) {
|
if (event.categoryid === category.parent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
category = categories[category.parent];
|
category = categories[category.parent];
|
||||||
|
@ -796,9 +822,8 @@ export type AddonCalendarEventTypeOption = {
|
||||||
/**
|
/**
|
||||||
* Formatted event reminder.
|
* Formatted event reminder.
|
||||||
*/
|
*/
|
||||||
export type AddonCalendarEventReminder = AddonCalendarReminderDBRecord & {
|
export type AddonCalendarEventReminder = {
|
||||||
value?: number; // Amount of time.
|
id: number;
|
||||||
unit?: AddonCalendarReminderUnits; // Units.
|
|
||||||
timestamp?: number; // Timestamp (in seconds).
|
timestamp?: number; // Timestamp (in seconds).
|
||||||
label?: string; // Label to represent the reminder.
|
label?: string; // Label to represent the reminder.
|
||||||
sublabel?: string; // Sub label.
|
sublabel?: string; // Sub label.
|
||||||
|
|
|
@ -23,12 +23,11 @@ import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreGroups } from '@services/groups';
|
import { CoreGroups } from '@services/groups';
|
||||||
import { CoreLocalNotifications } from '@services/local-notifications';
|
import { CoreLocalNotifications } from '@services/local-notifications';
|
||||||
import { CoreConfig } from '@services/config';
|
import { CoreConfig } from '@services/config';
|
||||||
import { ILocalNotification } from '@ionic-native/local-notifications';
|
|
||||||
import { AddonCalendarOffline } from './calendar-offline';
|
import { AddonCalendarOffline } from './calendar-offline';
|
||||||
import { CoreUser } from '@features/user/services/user';
|
import { CoreUser } from '@features/user/services/user';
|
||||||
import { CoreWSExternalWarning, CoreWSDate } from '@services/ws';
|
import { CoreWSExternalWarning, CoreWSDate } from '@services/ws';
|
||||||
import moment from 'moment-timezone';
|
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 { CoreCourses } from '@features/courses/services/courses';
|
||||||
import { ContextLevel, CoreConstants } from '@/core/constants';
|
import { ContextLevel, CoreConstants } from '@/core/constants';
|
||||||
import { CoreWSError } from '@classes/errors/wserror';
|
import { CoreWSError } from '@classes/errors/wserror';
|
||||||
|
@ -42,6 +41,13 @@ import { AddonCalendarSyncEvents, AddonCalendarSyncProvider } from './calendar-s
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { CoreText } from '@singletons/text';
|
import { CoreText } from '@singletons/text';
|
||||||
import { CorePlatform } from '@services/platform';
|
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:';
|
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_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 CALENDAR_TF_12 = '%I:%M %p'; // Calendar time in 12 hours format.
|
||||||
|
|
||||||
|
static readonly DEFAULT_NOTIFICATION_DISABLED = 0;
|
||||||
|
|
||||||
protected weekDays: AddonCalendarWeekDaysTranslationKeys[] = [
|
protected weekDays: AddonCalendarWeekDaysTranslationKeys[] = [
|
||||||
{
|
{
|
||||||
shortname: 'addon.calendar.sun',
|
shortname: 'addon.calendar.sun',
|
||||||
|
@ -306,17 +314,12 @@ export class AddonCalendarProvider {
|
||||||
EVENTS_TABLE,
|
EVENTS_TABLE,
|
||||||
{ id: eventId },
|
{ id: eventId },
|
||||||
));
|
));
|
||||||
promises.push(site.getDb().getRecords<AddonCalendarReminderDBRecord>(
|
promises.push(CoreReminders.removeReminders({
|
||||||
REMINDERS_TABLE,
|
instanceId: eventId,
|
||||||
{ eventid: eventId },
|
component: AddonCalendarProvider.COMPONENT,
|
||||||
).then((reminders) =>
|
} , siteId));
|
||||||
Promise.all(reminders.map((reminder) => this.deleteEventReminder(reminder.id, siteId)))));
|
|
||||||
|
|
||||||
try {
|
await CoreUtils.ignoreErrors(Promise.all(promises));
|
||||||
await Promise.all(promises);
|
|
||||||
} catch {
|
|
||||||
// Ignore errors.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -326,55 +329,56 @@ export class AddonCalendarProvider {
|
||||||
*/
|
*/
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
|
|
||||||
CoreLocalNotifications.registerClick<AddonCalendarPushNotificationData>(
|
CoreLocalNotifications.registerClick<CoreRemindersPushNotificationData>(
|
||||||
AddonCalendarProvider.COMPONENT,
|
AddonCalendarProvider.COMPONENT,
|
||||||
async (notification) => {
|
async (notification) => {
|
||||||
if (notification.eventId) {
|
await ApplicationInit.donePromise;
|
||||||
await ApplicationInit.donePromise;
|
|
||||||
|
|
||||||
const disabled = await this.isDisabled(notification.siteId);
|
this.notificationClicked(notification);
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (CoreLocalNotifications.isAvailable()) {
|
if (!CoreLocalNotifications.isAvailable()) {
|
||||||
CoreEvents.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, async (data) => {
|
return;
|
||||||
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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
await site.read('core_calendar_get_allowed_event_types', params, preSets);
|
||||||
|
|
||||||
// Convert the array to an object.
|
// Convert the array to an object.
|
||||||
const result = {};
|
const result: {[name: string]: boolean} = {};
|
||||||
if (response.allowedeventtypes) {
|
if (response.allowedeventtypes) {
|
||||||
response.allowedeventtypes.forEach((type) => {
|
response.allowedeventtypes.forEach((type) => {
|
||||||
result[type] = true;
|
result[type] = true;
|
||||||
|
@ -699,7 +703,7 @@ export class AddonCalendarProvider {
|
||||||
await site.read('core_calendar_get_calendar_event_by_id', params, preSets);
|
await site.read('core_calendar_get_calendar_event_by_id', params, preSets);
|
||||||
|
|
||||||
this.storeEventInLocalDb(response.event, { siteId });
|
this.storeEventInLocalDb(response.event, { siteId });
|
||||||
this.scheduleEventsNotifications([response.event], siteId);
|
this.updateEventsReminders([response.event], site.getId());
|
||||||
|
|
||||||
return response.event;
|
return response.event;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -766,27 +770,43 @@ export class AddonCalendarProvider {
|
||||||
* Adds an event reminder and schedule a new notification.
|
* Adds an event reminder and schedule a new notification.
|
||||||
*
|
*
|
||||||
* @param event Event to set the reminder.
|
* @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.
|
* @param siteId ID of the site the event belongs to. If not defined, use current site.
|
||||||
* @return Promise resolved when the notification is updated.
|
* @return Promise resolved when the notification is updated.
|
||||||
*/
|
*/
|
||||||
async addEventReminder(
|
async addEventReminder(
|
||||||
event: { id: number; timestart: number; name: string},
|
event: AddonCalendarEvent | AddonCalendarEventDBRecord | AddonCalendarEventToDisplay | AddonCalendarOfflineEventDBRecord,
|
||||||
time?: number | null,
|
timebefore?: number,
|
||||||
siteId?: string,
|
siteId?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
|
||||||
const reminder: Partial<AddonCalendarReminderDBRecord> = {
|
timebefore = timebefore ?? CoreRemindersService.DEFAULT_REMINDER_TIMEBEFORE;
|
||||||
eventid: event.id,
|
|
||||||
time: time ?? null,
|
const previousReminders = await CoreReminders.getReminders({
|
||||||
timecreated: Date.now(),
|
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);
|
await CoreReminders.addReminder(reminder, siteId);
|
||||||
|
|
||||||
const timestamp = time ? event.timestart - time : time;
|
|
||||||
|
|
||||||
await this.scheduleEventNotification(event, reminderId, timestamp, site.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -810,15 +830,10 @@ export class AddonCalendarProvider {
|
||||||
* @param id Reminder ID.
|
* @param id Reminder ID.
|
||||||
* @param siteId ID of the site the event belongs to. If not defined, use current site.
|
* @param siteId ID of the site the event belongs to. If not defined, use current site.
|
||||||
* @return Promise resolved when the notification is updated.
|
* @return Promise resolved when the notification is updated.
|
||||||
|
* @deprecated since 4.1. Use CoreReminders.removeReminder instead.
|
||||||
*/
|
*/
|
||||||
async deleteEventReminder(id: number, siteId?: string): Promise<void> {
|
async deleteEventReminder(id: number, siteId?: string): Promise<void> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
await CoreReminders.removeReminder(id, siteId);
|
||||||
|
|
||||||
if (CoreLocalNotifications.isAvailable()) {
|
|
||||||
CoreLocalNotifications.cancel(id, AddonCalendarProvider.COMPONENT, site.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
await site.getDb().deleteRecords(REMINDERS_TABLE, { id: id });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -865,7 +880,7 @@ export class AddonCalendarProvider {
|
||||||
}
|
}
|
||||||
const response: AddonCalendarCalendarDay = await site.read('core_calendar_get_calendar_day_view', params, preSets);
|
const response: AddonCalendarCalendarDay = await site.read('core_calendar_get_calendar_day_view', params, preSets);
|
||||||
this.storeEventsInLocalDB(response.events, { siteId });
|
this.storeEventsInLocalDB(response.events, { siteId });
|
||||||
this.scheduleEventsNotifications(response.events, siteId);
|
this.updateEventsReminders(response.events, site.getId());
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -909,14 +924,16 @@ export class AddonCalendarProvider {
|
||||||
/**
|
/**
|
||||||
* Get a calendar reminders from local Db.
|
* 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.
|
* @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.
|
* @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[]> {
|
async getEventReminders(eventId: number, siteId?: string): Promise<CoreReminderDBRecord[]> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
return CoreReminders.getReminders({
|
||||||
|
instanceId: eventId,
|
||||||
return site.getDb().getRecords(REMINDERS_TABLE, { eventid: id }, 'timecreated ASC, time ASC');
|
component: AddonCalendarProvider.COMPONENT,
|
||||||
|
}, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1070,7 +1087,7 @@ export class AddonCalendarProvider {
|
||||||
response.weeks.forEach((week) => {
|
response.weeks.forEach((week) => {
|
||||||
week.days.forEach((day) => {
|
week.days.forEach((day) => {
|
||||||
this.storeEventsInLocalDB(day.events, { siteId });
|
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);
|
const response = await site.read<AddonCalendarUpcoming>('core_calendar_get_calendar_upcoming_view', params, preSets);
|
||||||
this.storeEventsInLocalDB(response.events, { siteId });
|
this.storeEventsInLocalDB(response.events, { siteId });
|
||||||
this.scheduleEventsNotifications(response.events, siteId);
|
this.updateEventsReminders(response.events, site.getId());
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -1422,6 +1439,16 @@ export class AddonCalendarProvider {
|
||||||
return this.isCalendarDisabledInSite(site);
|
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.
|
* 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).
|
* 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.
|
* @return Promise resolved when all the notifications have been scheduled.
|
||||||
*/
|
*/
|
||||||
async scheduleAllSitesEventsNotifications(): Promise<void> {
|
async updateAllSitesEventReminders(): Promise<void> {
|
||||||
await CorePlatform.ready();
|
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()) {
|
if (!CoreLocalNotifications.isAvailable()) {
|
||||||
return;
|
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();
|
siteId = siteId || CoreSites.getCurrentSiteId();
|
||||||
|
|
||||||
if (time === 0) {
|
await AddonCalendar.updateEventsReminders(events, siteId);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1526,38 +1498,43 @@ export class AddonCalendarProvider {
|
||||||
* If local notification plugin is not enabled, resolve the promise.
|
* If local notification plugin is not enabled, resolve the promise.
|
||||||
*
|
*
|
||||||
* @param events Events to schedule.
|
* @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.
|
* @return Promise resolved when all the notifications have been scheduled.
|
||||||
*/
|
*/
|
||||||
async scheduleEventsNotifications(
|
protected async updateEventsReminders(
|
||||||
events: ({ id: number; timestart: number; timeduration: number; name: string})[],
|
events: ({ id: number; timestart: number; name: string})[],
|
||||||
siteId?: string,
|
siteId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
||||||
if (!CoreLocalNotifications.isAvailable()) {
|
if (!CoreLocalNotifications.isAvailable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
siteId = siteId || CoreSites.getCurrentSiteId();
|
await Promise.all(events.map(async (event) => {
|
||||||
|
|
||||||
const promises = events.map(async (event) => {
|
|
||||||
if (event.timestart * 1000 <= Date.now()) {
|
if (event.timestart * 1000 <= Date.now()) {
|
||||||
// The event has already started, don't schedule it.
|
// The event has already started, don't schedule it.
|
||||||
return;
|
|
||||||
|
// @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) => {
|
await Promise.all(reminders.map(async (reminder) => {
|
||||||
const time = reminder.time ? event.timestart - reminder.time : reminder.time;
|
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);
|
CoreReminders.updateReminder(
|
||||||
});
|
reminder,
|
||||||
|
siteId,
|
||||||
await Promise.all(p2);
|
);
|
||||||
});
|
}
|
||||||
|
}));
|
||||||
await Promise.all(promises);
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1587,24 +1564,8 @@ export class AddonCalendarProvider {
|
||||||
options: AddonCalendarStoreEventsOptions = {},
|
options: AddonCalendarStoreEventsOptions = {},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const site = await CoreSites.getSite(options.siteId);
|
const site = await CoreSites.getSite(options.siteId);
|
||||||
const siteId = site.getId();
|
|
||||||
const addDefaultReminder = options.addDefaultReminder ?? true;
|
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.
|
// Don't store data that can be calculated like formattedtime, iscategoryevent, etc.
|
||||||
let eventRecord: AddonCalendarEventDBRecord = {
|
let eventRecord: AddonCalendarEventDBRecord = {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
|
@ -1659,9 +1620,40 @@ export class AddonCalendarProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addDefaultReminder) {
|
||||||
|
this.addDefaultEventReminder(eventRecord, site.getId());
|
||||||
|
}
|
||||||
|
|
||||||
await site.getDb().insertRecord(EVENTS_TABLE, eventRecord);
|
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.
|
* 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 eventId ID of the event. Negative value to edit offline event. If undefined/null, create a new event.
|
||||||
* @param formData Form data.
|
* @param formData Form data.
|
||||||
* @param timeCreated The time the event was created. Only if modifying a new offline event.
|
* @param options Calendar submit event options.
|
||||||
* @param forceOffline True to always save it in offline.
|
|
||||||
* @param siteId Site ID. If not defined, current site.
|
|
||||||
* @return Promise resolved with the event and a boolean indicating if data was sent to server or stored in offline.
|
* @return Promise resolved with the event and a boolean indicating if data was sent to server or stored in offline.
|
||||||
*/
|
*/
|
||||||
async submitEvent(
|
async submitEvent(
|
||||||
|
@ -1701,7 +1691,8 @@ export class AddonCalendarProvider {
|
||||||
// Now save the reminders if any.
|
// Now save the reminders if any.
|
||||||
if (options.reminders?.length) {
|
if (options.reminders?.length) {
|
||||||
await CoreUtils.ignoreErrors(
|
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.
|
// Now save the reminders if any.
|
||||||
if (options.reminders?.length) {
|
if (options.reminders?.length) {
|
||||||
await CoreUtils.ignoreErrors(
|
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,
|
siteId?: string,
|
||||||
): Promise<AddonCalendarEvent> {
|
): Promise<AddonCalendarEvent> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
siteId = site.getId();
|
||||||
|
|
||||||
// Add data that is "hidden" in web.
|
// Add data that is "hidden" in web.
|
||||||
formData.id = eventId > 0 ? eventId : 0;
|
formData.id = eventId > 0 ? eventId : 0;
|
||||||
|
@ -1780,9 +1773,16 @@ export class AddonCalendarProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventId < 0) {
|
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(
|
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;
|
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.
|
* Value and unit for reminders.
|
||||||
*/
|
*/
|
||||||
|
@ -2338,7 +2329,7 @@ export type AddonCalendarValueAndUnit = {
|
||||||
*/
|
*/
|
||||||
export type AddonCalendarSubmitEventOptions = {
|
export type AddonCalendarSubmitEventOptions = {
|
||||||
reminders?: {
|
reminders?: {
|
||||||
time: number | null;
|
time?: number;
|
||||||
}[];
|
}[];
|
||||||
forceOffline?: boolean;
|
forceOffline?: boolean;
|
||||||
siteId?: string; // Site ID. If not defined, current site.
|
siteId?: string; // Site ID. If not defined, current site.
|
||||||
|
|
|
@ -22,10 +22,9 @@ import { AddonCalendar, AddonCalendarEventType, AddonCalendarProvider } from '..
|
||||||
* Database variables for AddonCalendarProvider service.
|
* Database variables for AddonCalendarProvider service.
|
||||||
*/
|
*/
|
||||||
export const EVENTS_TABLE = 'addon_calendar_events_3';
|
export const EVENTS_TABLE = 'addon_calendar_events_3';
|
||||||
export const REMINDERS_TABLE = 'addon_calendar_reminders_2';
|
|
||||||
export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = {
|
export const CALENDAR_SITE_SCHEMA: CoreSiteSchema = {
|
||||||
name: 'AddonCalendarProvider',
|
name: 'AddonCalendarProvider',
|
||||||
version: 4,
|
version: 5,
|
||||||
canBeCleared: [EVENTS_TABLE],
|
canBeCleared: [EVENTS_TABLE],
|
||||||
tables: [
|
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> {
|
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) {
|
if (oldVersion < 4) {
|
||||||
// Migrate default notification time if it was changed.
|
// Migrate default notification time if it was changed.
|
||||||
// Don't use getDefaultNotificationTime to be able to detect if the value was changed or not.
|
// 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.
|
// Convert from minutes to seconds.
|
||||||
AddonCalendar.setDefaultNotificationTime(defaultTime * 60, siteId);
|
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;
|
maxdaytimestamp?: number;
|
||||||
draggable?: 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 { CoreH5PModule } from './h5p/h5p.module';
|
||||||
import { CoreLoginModule } from './login/login.module';
|
import { CoreLoginModule } from './login/login.module';
|
||||||
import { CoreMainMenuModule } from './mainmenu/mainmenu.module';
|
import { CoreMainMenuModule } from './mainmenu/mainmenu.module';
|
||||||
|
import { CoreNativeModule } from '@features/native/native.module';
|
||||||
import { CorePushNotificationsModule } from './pushnotifications/pushnotifications.module';
|
import { CorePushNotificationsModule } from './pushnotifications/pushnotifications.module';
|
||||||
import { CoreQuestionModule } from './question/question.module';
|
import { CoreQuestionModule } from './question/question.module';
|
||||||
import { CoreRatingModule } from './rating/rating.module';
|
import { CoreRatingModule } from './rating/rating.module';
|
||||||
|
import { CoreRemindersModule } from './reminders/reminders.module';
|
||||||
import { CoreSearchModule } from './search/search.module';
|
import { CoreSearchModule } from './search/search.module';
|
||||||
import { CoreSettingsModule } from './settings/settings.module';
|
import { CoreSettingsModule } from './settings/settings.module';
|
||||||
import { CoreSharedFilesModule } from './sharedfiles/sharedfiles.module';
|
import { CoreSharedFilesModule } from './sharedfiles/sharedfiles.module';
|
||||||
|
@ -37,11 +39,10 @@ import { CoreSiteHomeModule } from './sitehome/sitehome.module';
|
||||||
import { CoreSitePluginsModule } from './siteplugins/siteplugins.module';
|
import { CoreSitePluginsModule } from './siteplugins/siteplugins.module';
|
||||||
import { CoreStylesModule } from './styles/styles.module';
|
import { CoreStylesModule } from './styles/styles.module';
|
||||||
import { CoreTagModule } from './tag/tag.module';
|
import { CoreTagModule } from './tag/tag.module';
|
||||||
import { CoreUserToursModule } from './usertours/user-tours.module';
|
|
||||||
import { CoreUserModule } from './user/user.module';
|
import { CoreUserModule } from './user/user.module';
|
||||||
|
import { CoreUserToursModule } from './usertours/user-tours.module';
|
||||||
import { CoreViewerModule } from './viewer/viewer.module';
|
import { CoreViewerModule } from './viewer/viewer.module';
|
||||||
import { CoreXAPIModule } from './xapi/xapi.module';
|
import { CoreXAPIModule } from './xapi/xapi.module';
|
||||||
import { CoreNativeModule } from '@features/native/native.module';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -61,15 +62,16 @@ import { CoreNativeModule } from '@features/native/native.module';
|
||||||
CorePushNotificationsModule,
|
CorePushNotificationsModule,
|
||||||
CoreQuestionModule,
|
CoreQuestionModule,
|
||||||
CoreRatingModule,
|
CoreRatingModule,
|
||||||
|
CoreRemindersModule,
|
||||||
CoreSearchModule,
|
CoreSearchModule,
|
||||||
CoreSettingsModule,
|
CoreSettingsModule,
|
||||||
CoreSharedFilesModule,
|
CoreSharedFilesModule,
|
||||||
CoreSiteHomeModule,
|
CoreSiteHomeModule,
|
||||||
CoreSitePluginsModule,
|
CoreSitePluginsModule,
|
||||||
CoreTagModule,
|
|
||||||
CoreStylesModule,
|
CoreStylesModule,
|
||||||
CoreUserToursModule,
|
CoreTagModule,
|
||||||
CoreUserModule,
|
CoreUserModule,
|
||||||
|
CoreUserToursModule,
|
||||||
CoreViewerModule,
|
CoreViewerModule,
|
||||||
CoreXAPIModule,
|
CoreXAPIModule,
|
||||||
|
|
||||||
|
|
|
@ -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 {}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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> {
|
async initializeDatabase(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await CoreApp.createTablesFromSchema(APP_SCHEMA);
|
await CoreApp.createTablesFromSchema(APP_SCHEMA);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue