MOBILE-2820 calendar: Add multiple reminders to calendar events

main
Pau Ferrer Ocaña 2019-02-08 10:32:21 +01:00
parent 9baca2918b
commit 1d6b79f33a
6 changed files with 264 additions and 170 deletions

View File

@ -71,7 +71,7 @@ export class AddonCalendarModule {
newName: AddonCalendarProvider.EVENTS_TABLE, newName: AddonCalendarProvider.EVENTS_TABLE,
filterFields: ['id', 'name', 'description', 'format', 'eventtype', 'courseid', 'timestart', 'timeduration', filterFields: ['id', 'name', 'description', 'format', 'eventtype', 'courseid', 'timestart', 'timeduration',
'categoryid', 'groupid', 'userid', 'instance', 'modulename', 'timemodified', 'repeatid', 'visible', 'uuid', 'categoryid', 'groupid', 'userid', 'instance', 'modulename', 'timemodified', 'repeatid', 'visible', 'uuid',
'sequence', 'subscriptionid', 'notificationtime'] 'sequence', 'subscriptionid']
}); });
// Migrate the component name. // Migrate the component name.

View File

@ -56,10 +56,10 @@
<ion-item> <ion-item>
<h2>{{ 'addon.calendar.reminders' | translate }}</h2> <h2>{{ 'addon.calendar.reminders' | translate }}</h2>
</ion-item> </ion-item>
<ion-item [class.item-dimmed]="notificationTime < currentTime" *ngIf="notificationTime > 0"> <ion-item [class.item-dimmed]="(reminder.time == -1 ? (event.timestart - defaultTime) : reminder.time) <= currentTime" *ngFor="let reminder of reminders">
<p *ngIf="timeToLoad == -1">{{ 'core.defaultvalue' | translate :{$a: ((event.timestart - defaultTime) * 1000) | coreFormatDate } }}</p> <p *ngIf="reminder.time == -1">{{ 'core.defaultvalue' | translate :{$a: ((event.timestart - defaultTime) * 1000) | coreFormatDate } }}</p>
<p *ngIf="timeToLoad > 0">{{ notificationTime * 1000 | coreFormatDate }}</p> <p *ngIf="reminder.time > 0">{{ reminder.time * 1000 | coreFormatDate }}</p>
<button ion-button icon-only clear="true" (click)="cancelNotification($event)" [attr.aria-label]=" 'core.delete' | translate" item-end> <button ion-button icon-only clear="true" (click)="cancelNotification(reminder.id, $event)" [attr.aria-label]=" 'core.delete' | translate" item-end *ngIf="(reminder.time == -1 ? (event.timestart - defaultTime) : reminder.time) > currentTime">
<ion-icon name="trash" color="danger"></ion-icon> <ion-icon name="trash" color="danger"></ion-icon>
</button> </button>
</ion-item> </ion-item>
@ -69,7 +69,7 @@
<ion-datetime [(ngModel)]="notificationTimeText" [placeholder]="'core.choosedots' | translate" [displayFormat]="notificationFormat" [min]="notificationMin" [max]="notificationMax"></ion-datetime> <ion-datetime [(ngModel)]="notificationTimeText" [placeholder]="'core.choosedots' | translate" [displayFormat]="notificationFormat" [min]="notificationMin" [max]="notificationMax"></ion-datetime>
</ion-item> </ion-item>
<ion-item *ngIf="event.timestart + event.timeduration > currentTime"> <ion-item *ngIf="event.timestart + event.timeduration > currentTime">
<button ion-button block color="primary" (click)="addNotificationTime($event)">{{ 'addon.calendar.setnewreminder' | translate }}</button> <button ion-button block color="primary" (click)="addNotificationTime($event)" [disabled]="!notificationTimeText">{{ 'addon.calendar.setnewreminder' | translate }}</button>
</ion-item> </ion-item>
</ion-card> </ion-card>
</core-loading> </core-loading>

View File

@ -42,9 +42,7 @@ export class AddonCalendarEventPage {
notificationFormat: string; notificationFormat: string;
notificationMin: string; notificationMin: string;
notificationMax: string; notificationMax: string;
notificationTime: number;
notificationTimeText: string; notificationTimeText: string;
timeToLoad: number;
event: any = {}; event: any = {};
title: string; title: string;
courseName: string; courseName: string;
@ -54,6 +52,7 @@ export class AddonCalendarEventPage {
categoryPath = ''; categoryPath = '';
currentTime: number; currentTime: number;
defaultTime: number; defaultTime: number;
reminders: any[];
constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams, constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams,
private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider, private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider,
@ -62,16 +61,15 @@ export class AddonCalendarEventPage {
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) { private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) {
this.eventId = navParams.get('id'); this.eventId = navParams.get('id');
this.notificationsEnabled = localNotificationsProvider.isAvailable() || true; this.notificationsEnabled = localNotificationsProvider.isAvailable();
this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId();
if (this.notificationsEnabled) { if (this.notificationsEnabled) {
this.calendarProvider.getEventNotificationTimeOption(this.eventId).then((notificationTime) => { this.calendarProvider.getEventReminders(this.eventId).then((reminders) => {
this.setNotificationTime(notificationTime); this.reminders = reminders;
}); });
this.calendarProvider.getDefaultNotificationTime().then((defaultTime) => { this.calendarProvider.getDefaultNotificationTime().then((defaultTime) => {
this.defaultTime = defaultTime * 60; this.defaultTime = defaultTime * 60;
this.setNotificationTime();
}); });
// Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them. // Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them.
@ -112,11 +110,9 @@ export class AddonCalendarEventPage {
this.event = event; this.event = event;
this.currentTime = this.timeUtils.timestamp(); this.currentTime = this.timeUtils.timestamp();
this.notificationMin = this.timeUtils.userDate(this.currentTime * 1000, 'YYYY-MM-DDTHH:mm:ss', false); this.notificationMin = this.timeUtils.userDate(this.currentTime * 1000, 'YYYY-MM-DDTHH:mm', false);
this.notificationMax = this.timeUtils.userDate((event.timestart + event.timeduration) * 1000, this.notificationMax = this.timeUtils.userDate((event.timestart + event.timeduration) * 1000,
'YYYY-MM-DDTHH:mm:ss', false); 'YYYY-MM-DDTHH:mm', false);
this.setNotificationTime();
// Reset some of the calculated data. // Reset some of the calculated data.
this.categoryPath = ''; this.categoryPath = '';
@ -186,7 +182,7 @@ export class AddonCalendarEventPage {
} }
/** /**
* Add a notification time for this event. * Add a reminder for this event.
* *
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
@ -195,42 +191,33 @@ export class AddonCalendarEventPage {
e.stopPropagation(); e.stopPropagation();
if (this.notificationTimeText && this.event && this.event.id) { if (this.notificationTimeText && this.event && this.event.id) {
this.setNotificationTime(new Date(this.notificationTimeText).getTime() / 1000); const notificationTime = this.timeUtils.convertToTimestamp(this.notificationTimeText);
this.calendarProvider.updateNotificationTime(this.event, this.notificationTime);
this.calendarProvider.addEventReminder(this.event, notificationTime).then(() => {
this.calendarProvider.getEventReminders(this.eventId).then((reminders) => {
this.reminders = reminders;
});
this.notificationTimeText = null;
});
} }
} }
/** /**
* Cancel the current notification. * Cancel the selected notification.
* *
* @param {number} id Reminder ID.
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
cancelNotification(e: Event): void { cancelNotification(id: number, e: Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.calendarProvider.updateNotificationTime(this.event, 0); this.calendarProvider.deleteEventReminder(id).then(() => {
this.notificationTime = 0; this.calendarProvider.getEventReminders(this.eventId).then((reminders) => {
} this.reminders = reminders;
});
/** });
* Loads notification time.
*
* @param {number} [timeToLoad] Time to load. If not set, just recalculate.
*/
setNotificationTime(timeToLoad?: number): void {
this.timeToLoad = typeof timeToLoad == 'undefined' ? this.timeToLoad : timeToLoad;
if (typeof this.timeToLoad != 'undefined') {
if (this.timeToLoad < 0) {
this.notificationTime = this.event.timestart - this.defaultTime * 60;
} else if (this.timeToLoad == 0 || this.timeToLoad > 1440) {
this.notificationTime = this.timeToLoad;
} else {
this.notificationTime = this.event.timestart - this.timeToLoad * 60;
}
this.notificationTimeText = new Date(this.notificationTime * 1000).toString();
}
} }
/** /**

View File

@ -23,6 +23,7 @@ import { CoreConstants } from '@core/constants';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { CoreConfigProvider } from '@providers/config'; import { CoreConfigProvider } from '@providers/config';
import { ILocalNotification } from '@ionic-native/local-notifications'; import { ILocalNotification } from '@ionic-native/local-notifications';
import { SQLiteDB } from '@classes/sqlitedb';
/** /**
* Service to handle calendar events. * Service to handle calendar events.
@ -37,10 +38,11 @@ export class AddonCalendarProvider {
protected ROOT_CACHE_KEY = 'mmaCalendar:'; protected ROOT_CACHE_KEY = 'mmaCalendar:';
// Variables for database. // Variables for database.
static EVENTS_TABLE = 'addon_calendar_events'; static EVENTS_TABLE = 'addon_calendar_events_1';
static REMINDERS_TABLE = 'addon_calendar_reminders';
protected siteSchema: CoreSiteSchema = { protected siteSchema: CoreSiteSchema = {
name: 'AddonCalendarProvider', name: 'AddonCalendarProvider',
version: 1, version: 2,
tables: [ tables: [
{ {
name: AddonCalendarProvider.EVENTS_TABLE, name: AddonCalendarProvider.EVENTS_TABLE,
@ -50,10 +52,6 @@ export class AddonCalendarProvider {
type: 'INTEGER', type: 'INTEGER',
primaryKey: true primaryKey: true
}, },
{
name: 'notificationtime',
type: 'INTEGER'
},
{ {
name: 'name', name: 'name',
type: 'TEXT', type: 'TEXT',
@ -128,8 +126,77 @@ export class AddonCalendarProvider {
type: 'INTEGER' type: 'INTEGER'
} }
] ]
},
{
name: AddonCalendarProvider.REMINDERS_TABLE,
columns: [
{
name: 'id',
type: 'INTEGER',
primaryKey: true
},
{
name: 'eventid',
type: 'INTEGER'
},
{
name: 'time',
type: 'INTEGER'
}
],
uniqueKeys: [
['eventid', 'time']
]
} }
] ],
migrate(db: SQLiteDB, oldVersion: number): Promise<any> | void {
if (oldVersion < 2) {
const newTable = AddonCalendarProvider.EVENTS_TABLE;
const oldTable = 'addon_calendar_events';
return db.tableExists(oldTable).then(() => {
return db.getAllRecords(oldTable).then((events) => {
const now = Math.round(Date.now() / 1000);
return Promise.all(events.map((event) => {
if (event.notificationtime == 0) {
// No reminders.
return Promise.resolve();
}
let time;
if (event.notificationtime == -1) {
time = -1;
} else {
time = event.timestart - event.notificationtime * 60;
if (time < now) {
// Old reminder, just not add this.
return Promise.resolve();
}
}
const reminder = {
eventid: event.id,
time: time
};
return db.insertRecord(AddonCalendarProvider.REMINDERS_TABLE, reminder);
})).then(() => {
// Move the records from the old table.
return db.insertRecordsFrom(newTable, oldTable, undefined, 'id, name, description, format, eventtype,\
courseid, timestart, timeduration, categoryid, groupid, userid, instance, modulename, timemodified,\
repeatid, visible, uuid, sequence, subscriptionid');
}).then(() => {
return db.dropTable(oldTable);
});
});
}).catch(() => {
// Old table does not exist, ignore.
});
}
}
}; };
protected logger; protected logger;
@ -145,29 +212,42 @@ export class AddonCalendarProvider {
* Removes expired events from local DB. * Removes expired events from local DB.
* *
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<void>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
cleanExpiredEvents(siteId?: string): Promise<void> { cleanExpiredEvents(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let promise; return site.getDb().getRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart + timeduration < ?',
[this.timeUtils.timestamp()]).then((events) => {
return Promise.all(events.map((event) => {
return this.deleteEvent(event.id, siteId);
}));
});
});
}
// Cancel expired events notifications first. /**
if (this.localNotificationsProvider.isAvailable()) { * Delete event cancelling all the reminders and notifications.
promise = site.getDb().getRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart < ?', *
[this.timeUtils.timestamp()]).then((events) => { * @param {number} eventId Event ID.
events.forEach((event) => { * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
return this.localNotificationsProvider.cancel(event.id, AddonCalendarProvider.COMPONENT, site.getId()); * @return {Promise<any>} Resolved when done.
}); */
}).catch(() => { protected deleteEvent(eventId: number, siteId?: string): Promise<any> {
// Ignore errors. return this.sitesProvider.getSite(siteId).then((site) => {
}); siteId = site.getId();
} else {
promise = Promise.resolve();
}
return promise.then(() => { const promises = [];
return site.getDb().deleteRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart < ?',
[this.timeUtils.timestamp()]); promises.push(site.getDb().deleteRecords(AddonCalendarProvider.EVENTS_TABLE, {id: eventId}));
promises.push(site.getDb().getRecords(AddonCalendarProvider.REMINDERS_TABLE, {eventid: eventId}).then((reminders) => {
return Promise.all(reminders.map((reminder) => {
return this.deleteEventReminder(reminder.id, siteId);
}));
}));
return Promise.all(promises).catch(() => {
// Ignore errors.
}); });
}); });
} }
@ -282,37 +362,60 @@ export class AddonCalendarProvider {
} }
/** /**
* Get event notification time. Always returns number of minutes (0 if disabled). * Adds an event reminder and schedule a new notification.
* *
* @param {number} id Event ID. * @param {any} event Event to update its notification time.
* @param {number} time New notification setting timestamp.
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<number>} Event notification time in minutes. 0 if disabled. * @return {Promise<any>} Promise resolved when the notification is updated.
*/ */
getEventNotificationTime(id: number, siteId?: string): Promise<number> { addEventReminder(event: any, time: number, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); return this.sitesProvider.getSite(siteId).then((site) => {
siteId = site.getId();
return this.getEventNotificationTimeOption(id, siteId).then((time: number) => { if (!this.sitesProvider.isLoggedIn()) {
if (time == -1) { // Not logged in, we can't get the site DB. User logged out or session expired while an operation was ongoing.
return this.getDefaultNotificationTime(siteId); return Promise.reject(null);
} }
return time; const reminder = {
eventid: event.id,
time: time
};
return site.getDb().insertRecord(AddonCalendarProvider.REMINDERS_TABLE, reminder).then((reminderId) => {
return this.scheduleEventNotification(event, reminderId, time, siteId);
});
}); });
} }
/** /**
* Get event notification time for options. Returns -1 for default time. * Remove an event reminder and cancel the notification.
*
* @param {number} id Reminder ID.
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the notification is updated.
*/
deleteEventReminder(id: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
if (this.localNotificationsProvider.isAvailable()) {
this.localNotificationsProvider.cancel(id, AddonCalendarProvider.COMPONENT, site.getId());
}
return site.getDb().deleteRecords(AddonCalendarProvider.REMINDERS_TABLE, {id: id});
});
}
/**
* Get a calendar reminders from local Db.
* *
* @param {number} id Event ID. * @param {number} id Event ID.
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<number>} Promise with event notification time in minutes. 0 if disabled, -1 if default time. * @return {Promise<any>} Promise resolved when the event data is retrieved.
*/ */
getEventNotificationTimeOption(id: number, siteId?: string): Promise<number> { getEventReminders(id: number, siteId?: string): Promise<any> {
return this.getEventFromLocalDb(id, siteId).then((e) => { return this.sitesProvider.getSite(siteId).then((site) => {
console.error(e.notificationtime); return site.getDb().getRecords(AddonCalendarProvider.REMINDERS_TABLE, {eventid: id}, 'time ASC');
return e.notificationtime || 0;
}).catch(() => {
return -1;
}); });
} }
@ -511,34 +614,35 @@ export class AddonCalendarProvider {
* @param {string} [siteId] Site ID the event belongs to. If not defined, use current site. * @param {string} [siteId] Site ID the event belongs to. If not defined, use current site.
* @return {Promise<void>} Promise resolved when the notification is scheduled. * @return {Promise<void>} Promise resolved when the notification is scheduled.
*/ */
scheduleEventNotification(event: any, time: number, siteId?: string): Promise<void> { protected scheduleEventNotification(event: any, reminderId: number, time: number, siteId?: string): Promise<void> {
if (this.localNotificationsProvider.isAvailable()) { if (this.localNotificationsProvider.isAvailable()) {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (time === 0) { if (time === 0) {
// Cancel if it was scheduled. // Cancel if it was scheduled.
return this.localNotificationsProvider.cancel(event.id, AddonCalendarProvider.COMPONENT, siteId); return this.localNotificationsProvider.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId);
} }
// If time is -1, get event default time. // If time is -1, get event default time.
const promise = time == -1 ? this.getDefaultNotificationTime(siteId) : Promise.resolve(time); const promise = time == -1 ? this.getDefaultNotificationTime(siteId) : Promise.resolve(time);
return promise.then((time) => { return promise.then((time) => {
const timeEnd = (event.timestart + event.timeduration) * 1000;
if (timeEnd <= new Date().getTime()) { if (time * 1000 <= new Date().getTime()) {
// The event has finished already, don't schedule it. // This reminder is over, don't schedule. Cancel if it was scheduled.
return Promise.resolve(); return this.localNotificationsProvider.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId);
} }
const notification: ILocalNotification = { const notification: ILocalNotification = {
id: event.id, id: reminderId,
title: event.name, title: event.name,
text: this.timeUtils.userDate(event.timestart * 1000, 'core.strftimedaydatetime', true), text: this.timeUtils.userDate(event.timestart * 1000, 'core.strftimedaydatetime', true),
trigger: { trigger: {
at: new Date((event.timestart - (time * 60)) * 1000) at: new Date(time * 1000)
}, },
data: { data: {
eventid: event.id, eventid: event.id,
reminderid: reminderId,
siteid: siteId siteid: siteId
} }
}; };
@ -561,18 +665,27 @@ export class AddonCalendarProvider {
* @return {Promise<any[]>} Promise resolved when all the notifications have been scheduled. * @return {Promise<any[]>} Promise resolved when all the notifications have been scheduled.
*/ */
scheduleEventsNotifications(events: any[], siteId?: string): Promise<any[]> { scheduleEventsNotifications(events: any[], siteId?: string): Promise<any[]> {
const promises = [];
if (this.localNotificationsProvider.isAvailable()) { if (this.localNotificationsProvider.isAvailable()) {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
events.forEach((e) => {
promises.push(this.getEventNotificationTime(e.id, siteId).then((time) => { return Promise.all(events.map((event) => {
return this.scheduleEventNotification(e, time, siteId); const timeEnd = (event.timestart + event.timeduration) * 1000;
}));
}); if (timeEnd <= new Date().getTime()) {
// The event has finished already, don't schedule it.
return this.deleteEvent(event.id, siteId);
}
return this.getEventReminders(event.id, siteId).then((reminders) => {
return Promise.all(reminders.map((reminder) => {
return this.scheduleEventNotification(event, reminder.id, reminder.time, siteId);
}));
});
}));
} }
return Promise.all(promises); return Promise.resolve([]);
} }
/** /**
@ -599,7 +712,41 @@ export class AddonCalendarProvider {
*/ */
storeEventInLocalDb(event: any, siteId?: string): Promise<any> { storeEventInLocalDb(event: any, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.getDb().insertRecord(AddonCalendarProvider.EVENTS_TABLE, event); siteId = site.getId();
// If event does not exists on the DB, schedule the reminder.
return this.getEventFromLocalDb(event.id, site.id).catch(() => {
// Event does not exist. Check if any reminder exists first.
return this.getEventReminders(event.id, siteId).then((reminders) => {
if (reminders.length == 0) {
this.addEventReminder(event, -1, siteId);
}
});
}).then(() => {
const eventRecord = {
id: event.id,
name: event.name,
description: event.description,
format: event.format,
eventtype: event.eventtype,
courseid: event.courseid,
timestart: event.timestart,
timeduration: event.timeduration,
categoryid: event.categoryid,
groupid: event.groupid,
userid: event.userid,
instance: event.instance,
modulename: event.modulename,
timemodified: event.timemodified,
repeatid: event.repeatid,
visible: event.visible,
uuid: event.uuid,
sequence: event.sequence,
subscriptionid: event.subscriptionid
};
return site.getDb().insertRecord(AddonCalendarProvider.EVENTS_TABLE, eventRecord);
});
}); });
} }
@ -614,70 +761,10 @@ export class AddonCalendarProvider {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
siteId = site.getId(); siteId = site.getId();
const promises = [], return Promise.all(events.map((event) => {
db = site.getDb(); // If event does not exists on the DB, schedule the reminder.
return this.storeEventInLocalDb(event, siteId);
events.forEach((event) => { }));
// Don't override event notification time if the user configured it.
promises.push(this.getEventFromLocalDb(event.id, siteId).catch(() => {
// Event not stored, return empty object.
return {};
}).then((e) => {
const eventRecord = {
id: event.id,
name: event.name,
description: event.description,
format: event.format,
eventtype: event.eventtype,
courseid: event.courseid,
timestart: event.timestart,
timeduration: event.timeduration,
categoryid: event.categoryid,
groupid: event.groupid,
userid: event.userid,
instance: event.instance,
modulename: event.modulename,
timemodified: event.timemodified,
repeatid: event.repeatid,
visible: event.visible,
uuid: event.uuid,
sequence: event.sequence,
subscriptionid: event.subscriptionid,
notificationtime: e.notificationtime || -1
};
return db.insertRecord(AddonCalendarProvider.EVENTS_TABLE, eventRecord);
}));
});
return Promise.all(promises);
});
}
/**
* Updates an event notification time and schedule a new notification.
*
* @param {any} event Event to update its notification time.
* @param {number} time New notification setting time (in minutes). E.g. 10 means "notificate 10 minutes before start".
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<void>} Promise resolved when the notification is updated.
*/
updateNotificationTime(event: any, time: number, siteId?: string): Promise<void> {
return this.sitesProvider.getSite(siteId).then((site) => {
if (!this.sitesProvider.isLoggedIn()) {
// Not logged in, we can't get the site DB. User logged out or session expired while an operation was ongoing.
return Promise.reject(null);
}
return site.getDb().updateRecords(AddonCalendarProvider.EVENTS_TABLE, {notificationtime: time}, {id: event.id})
.then(() => {
if (time == 0) {
// No notification, cancel it.
return this.localNotificationsProvider.cancel(event.id, AddonCalendarProvider.COMPONENT, site.getId());
} else {
return this.scheduleEventNotification(event, time);
}
});
}); });
} }
} }

View File

@ -21,6 +21,7 @@ import { CoreLocalNotificationsProvider } from './local-notifications';
import { CoreLoggerProvider } from './logger'; import { CoreLoggerProvider } from './logger';
import { CoreSitesProvider } from './sites'; import { CoreSitesProvider } from './sites';
import { CoreUtilsProvider } from './utils/utils'; import { CoreUtilsProvider } from './utils/utils';
import { CoreTimeUtilsProvider } from './utils/time';
import { CoreConfigConstants } from '../configconstants'; import { CoreConfigConstants } from '../configconstants';
import { AddonCalendarProvider } from '@addon/calendar/providers/calendar'; import { AddonCalendarProvider } from '@addon/calendar/providers/calendar';
import { SQLiteDB } from '@classes/sqlitedb'; import { SQLiteDB } from '@classes/sqlitedb';
@ -321,7 +322,7 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
constructor(logger: CoreLoggerProvider, private configProvider: CoreConfigProvider, private sitesProvider: CoreSitesProvider, constructor(logger: CoreLoggerProvider, private configProvider: CoreConfigProvider, private sitesProvider: CoreSitesProvider,
private filepoolProvider: CoreFilepoolProvider, private notifProvider: CoreLocalNotificationsProvider, private filepoolProvider: CoreFilepoolProvider, private notifProvider: CoreLocalNotificationsProvider,
private utils: CoreUtilsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private appProvider: CoreAppProvider, private timeUtils: CoreTimeUtilsProvider,
private calendarProvider: AddonCalendarProvider) { private calendarProvider: AddonCalendarProvider) {
this.logger = logger.getInstance('CoreUpdateManagerProvider'); this.logger = logger.getInstance('CoreUpdateManagerProvider');
} }
@ -606,6 +607,8 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
return Promise.resolve(); return Promise.resolve();
} }
const now = this.timeUtils.timestamp();
return this.sitesProvider.getSitesIds().then((siteIds) => { return this.sitesProvider.getSitesIds().then((siteIds) => {
const promises = []; const promises = [];
@ -615,9 +618,16 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
const eventPromises = []; const eventPromises = [];
events.forEach((event) => { events.forEach((event) => {
if (event.notificationtime == AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME) { if (event.notificationtime && event.notificationtime == AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME) {
event.notificationtime = -1; eventPromises.push(this.calendarProvider.addEventReminder(event, -1, siteId));
eventPromises.push(this.calendarProvider.storeEventInLocalDb(event, siteId)); } else if (event.notificationtime && event.notificationtime > 0) {
const time = event.timestart - event.notificationtime * 60;
if (time < now) {
// Old reminder, just not add this.
return;
}
eventPromises.push(this.calendarProvider.addEventReminder(event, time, siteId));
} }
}); });

View File

@ -276,6 +276,16 @@ export class CoreTimeUtilsProvider {
return moment(timestamp).format(format); return moment(timestamp).format(format);
} }
/**
* Convert a text into user timezone timestamp.
*
* @param {number} date To convert to timestamp.
* @return {number} Converted timestamp.
*/
convertToTimestamp(date: string): number {
return moment(date).unix() - (moment().utcOffset() * 60);
}
/** /**
* Return the localized ISO format (i.e DDMMYY) from the localized moment format. Useful for translations. * Return the localized ISO format (i.e DDMMYY) from the localized moment format. Useful for translations.
* DO NOT USE this function for ion-datetime format. Moment escapes characters with [], but ion-datetime doesn't support it. * DO NOT USE this function for ion-datetime format. Moment escapes characters with [], but ion-datetime doesn't support it.