commit
cc9ba9204c
|
@ -66,6 +66,8 @@
|
||||||
"addon.calendar.gotoactivity": "calendar",
|
"addon.calendar.gotoactivity": "calendar",
|
||||||
"addon.calendar.noevents": "local_moodlemobileapp",
|
"addon.calendar.noevents": "local_moodlemobileapp",
|
||||||
"addon.calendar.notifications": "local_moodlemobileapp",
|
"addon.calendar.notifications": "local_moodlemobileapp",
|
||||||
|
"addon.calendar.reminders": "local_moodlemobileapp",
|
||||||
|
"addon.calendar.setnewreminder": "local_moodlemobileapp",
|
||||||
"addon.calendar.typecategory": "calendar",
|
"addon.calendar.typecategory": "calendar",
|
||||||
"addon.calendar.typeclose": "calendar",
|
"addon.calendar.typeclose": "calendar",
|
||||||
"addon.calendar.typecourse": "calendar",
|
"addon.calendar.typecourse": "calendar",
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"eventstarttime": "Start time",
|
"eventstarttime": "Start time",
|
||||||
"gotoactivity": "Go to activity",
|
"gotoactivity": "Go to activity",
|
||||||
"noevents": "There are no events",
|
"noevents": "There are no events",
|
||||||
"notifications": "Notifications",
|
"reminders": "Reminders",
|
||||||
|
"setnewreminder": "Set a new reminder",
|
||||||
"typeclose": "Close event",
|
"typeclose": "Close event",
|
||||||
"typecourse": "Course event",
|
"typecourse": "Course event",
|
||||||
"typecategory": "Category event",
|
"typecategory": "Category event",
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
<core-loading [hideUntil]="eventLoaded">
|
<core-loading [hideUntil]="eventLoaded">
|
||||||
<ion-card>
|
<ion-card>
|
||||||
<ion-card-content *ngIf="event">
|
<ion-card-content *ngIf="event">
|
||||||
<ion-card-title text-wrap>
|
<ion-item text-wrap>
|
||||||
<core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
<core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
||||||
<core-format-text [text]="event.name"></core-format-text>
|
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
||||||
</ion-card-title>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<h2>{{ 'addon.calendar.eventstarttime' | translate}}</h2>
|
<h2>{{ 'addon.calendar.eventstarttime' | translate}}</h2>
|
||||||
<p>{{ event.timestart * 1000 | coreFormatDate }}</p>
|
<p>{{ event.timestart * 1000 | coreFormatDate }}</p>
|
||||||
|
@ -52,21 +52,29 @@
|
||||||
</ion-card-content>
|
</ion-card-content>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<ion-card list *ngIf="notificationsEnabled && event.timestart - 600 > currentTime">
|
<ion-card list *ngIf="notificationsEnabled">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label>{{ 'addon.calendar.notifications' | translate }}</ion-label>
|
<h2>{{ 'addon.calendar.reminders' | translate }}</h2>
|
||||||
<ion-select [(ngModel)]="notificationTime" (ionChange)="updateNotificationTime($event)" interface="action-sheet">
|
|
||||||
<ion-option value="-1" *ngIf="event.timestart - defaultTime > currentTime">{{ 'core.defaultvalue' | translate :{$a: defaultTimeReadable} }}</ion-option>
|
|
||||||
<ion-option value="0">{{ 'core.settings.disabled' | translate }}</ion-option>
|
|
||||||
<ion-option value="10">{{ 600 | coreDuration }}</ion-option>
|
|
||||||
<ion-option value="30" *ngIf="event.timestart - 1800 > currentTime">{{ 1800 | coreDuration }}</ion-option>
|
|
||||||
<ion-option value="60" *ngIf="event.timestart - 3600 > currentTime">{{ 3600 | coreDuration }}</ion-option>
|
|
||||||
<ion-option value="120" *ngIf="event.timestart - 7200 > currentTime">{{ 7200 | coreDuration }}</ion-option>
|
|
||||||
<ion-option value="360" *ngIf="event.timestart - 21600 > currentTime">{{ 21600 | coreDuration }}</ion-option>
|
|
||||||
<ion-option value="720" *ngIf="event.timestart - 43200 > currentTime">{{ 43200 | coreDuration }}</ion-option>
|
|
||||||
<ion-option value="1440" *ngIf="event.timestart - 86400 > currentTime">{{ 86400 | coreDuration }}</ion-option>
|
|
||||||
</ion-select>
|
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
<ng-container *ngFor="let reminder of reminders">
|
||||||
|
<ion-item *ngIf="reminder.time > 0 || defaultTime > 0" [class.item-dimmed]="(reminder.time == -1 ? (event.timestart - defaultTime) : reminder.time) <= currentTime" >
|
||||||
|
<p *ngIf="reminder.time == -1">{{ 'core.defaultvalue' | translate :{$a: ((event.timestart - defaultTime) * 1000) | coreFormatDate } }}</p>
|
||||||
|
<p *ngIf="reminder.time > 0">{{ reminder.time * 1000 | coreFormatDate }}</p>
|
||||||
|
<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>
|
||||||
|
</button>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="event.timestart + event.timeduration > currentTime">
|
||||||
|
<ion-item>
|
||||||
|
<ion-label stacked>{{ 'addon.calendar.setnewreminder' | translate }}</ion-label>
|
||||||
|
<ion-datetime [(ngModel)]="notificationTimeText" [placeholder]="'core.choosedots' | translate" [displayFormat]="notificationFormat" [min]="notificationMin" [max]="notificationMax"></ion-datetime>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item>
|
||||||
|
<button ion-button block color="primary" (click)="addNotificationTime($event)" [disabled]="!notificationTimeText">{{ 'addon.calendar.setnewreminder' | translate }}</button>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -39,8 +39,10 @@ export class AddonCalendarEventPage {
|
||||||
protected eventId;
|
protected eventId;
|
||||||
protected siteHomeId: number;
|
protected siteHomeId: number;
|
||||||
eventLoaded: boolean;
|
eventLoaded: boolean;
|
||||||
notificationTime: number;
|
notificationFormat: string;
|
||||||
defaultTimeReadable: string;
|
notificationMin: string;
|
||||||
|
notificationMax: string;
|
||||||
|
notificationTimeText: string;
|
||||||
event: any = {};
|
event: any = {};
|
||||||
title: string;
|
title: string;
|
||||||
courseName: string;
|
courseName: string;
|
||||||
|
@ -50,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,
|
||||||
|
@ -61,21 +64,17 @@ export class AddonCalendarEventPage {
|
||||||
this.notificationsEnabled = localNotificationsProvider.isAvailable();
|
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.notificationTime = notificationTime;
|
this.reminders = reminders;
|
||||||
this.loadNotificationTime();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.calendarProvider.getDefaultNotificationTime().then((defaultTime) => {
|
this.calendarProvider.getDefaultNotificationTime().then((defaultTime) => {
|
||||||
this.defaultTime = defaultTime * 60;
|
this.defaultTime = defaultTime * 60;
|
||||||
this.loadNotificationTime();
|
|
||||||
if (defaultTime === 0) {
|
|
||||||
// Disabled by default.
|
|
||||||
this.defaultTimeReadable = this.translate.instant('core.settings.disabled');
|
|
||||||
} else {
|
|
||||||
this.defaultTimeReadable = timeUtils.formatTime(defaultTime * 60);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them.
|
||||||
|
this.notificationFormat = this.timeUtils.convertPHPToMoment(this.translate.instant('core.strftimedatetimeshort'))
|
||||||
|
.replace(/[\[\]]/g, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,12 +87,6 @@ export class AddonCalendarEventPage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNotificationTime(): void {
|
|
||||||
if (!isNaN(this.notificationTime) && this.event && this.event.id) {
|
|
||||||
this.calendarProvider.updateNotificationTime(this.event, this.notificationTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the event and updates the view.
|
* Fetches the event and updates the view.
|
||||||
*
|
*
|
||||||
|
@ -117,7 +110,9 @@ export class AddonCalendarEventPage {
|
||||||
this.event = event;
|
this.event = event;
|
||||||
|
|
||||||
this.currentTime = this.timeUtils.timestamp();
|
this.currentTime = this.timeUtils.timestamp();
|
||||||
this.loadNotificationTime();
|
this.notificationMin = this.timeUtils.userDate(this.currentTime * 1000, 'YYYY-MM-DDTHH:mm', false);
|
||||||
|
this.notificationMax = this.timeUtils.userDate((event.timestart + event.timeduration) * 1000,
|
||||||
|
'YYYY-MM-DDTHH:mm', false);
|
||||||
|
|
||||||
// Reset some of the calculated data.
|
// Reset some of the calculated data.
|
||||||
this.categoryPath = '';
|
this.categoryPath = '';
|
||||||
|
@ -187,16 +182,50 @@ export class AddonCalendarEventPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads notification time by discarding options not in the list.
|
* Add a reminder for this event.
|
||||||
|
*
|
||||||
|
* @param {Event} e Click event.
|
||||||
*/
|
*/
|
||||||
loadNotificationTime(): void {
|
addNotificationTime(e: Event): void {
|
||||||
if (typeof this.notificationTime != 'undefined') {
|
e.preventDefault();
|
||||||
if (this.notificationTime > 0 && this.event.timestart - this.notificationTime * 60 < this.currentTime) {
|
e.stopPropagation();
|
||||||
this.notificationTime = 0;
|
|
||||||
} else if (this.notificationTime < 0 && this.event.timestart - this.defaultTime < this.currentTime) {
|
if (this.notificationTimeText && this.event && this.event.id) {
|
||||||
this.notificationTime = 0;
|
let notificationTime = this.timeUtils.convertToTimestamp(this.notificationTimeText);
|
||||||
|
|
||||||
|
const currentTime = this.timeUtils.timestamp(),
|
||||||
|
minute = Math.floor(currentTime / 60) * 60;
|
||||||
|
|
||||||
|
// Check if the notification time is in the same minute as we are, so the notification is triggered.
|
||||||
|
if (notificationTime >= minute && notificationTime < minute + 60) {
|
||||||
|
notificationTime = currentTime + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.calendarProvider.addEventReminder(this.event, notificationTime).then(() => {
|
||||||
|
this.calendarProvider.getEventReminders(this.eventId).then((reminders) => {
|
||||||
|
this.reminders = reminders;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.notificationTimeText = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the selected notification.
|
||||||
|
*
|
||||||
|
* @param {number} id Reminder ID.
|
||||||
|
* @param {Event} e Click event.
|
||||||
|
*/
|
||||||
|
cancelNotification(id: number, e: Event): void {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.calendarProvider.deleteEventReminder(id).then(() => {
|
||||||
|
this.calendarProvider.getEventReminders(this.eventId).then((reminders) => {
|
||||||
|
this.reminders = reminders;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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_2';
|
||||||
|
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,80 @@ 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, siteId: string): 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
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cancel old notification.
|
||||||
|
this.localNotificationsProvider.cancel(event.id, AddonCalendarProvider.COMPONENT, siteId);
|
||||||
|
|
||||||
|
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 +215,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 < ?',
|
||||||
|
|
||||||
// Cancel expired events notifications first.
|
|
||||||
if (this.localNotificationsProvider.isAvailable()) {
|
|
||||||
promise = site.getDb().getRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart < ?',
|
|
||||||
[this.timeUtils.timestamp()]).then((events) => {
|
[this.timeUtils.timestamp()]).then((events) => {
|
||||||
events.forEach((event) => {
|
return Promise.all(events.map((event) => {
|
||||||
return this.localNotificationsProvider.cancel(event.id, AddonCalendarProvider.COMPONENT, site.getId());
|
return this.deleteEvent(event.id, siteId);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}).catch(() => {
|
|
||||||
// Ignore errors.
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
promise = Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
/**
|
||||||
return site.getDb().deleteRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart < ?',
|
* Delete event cancelling all the reminders and notifications.
|
||||||
[this.timeUtils.timestamp()]);
|
*
|
||||||
|
* @param {number} eventId Event ID.
|
||||||
|
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
protected deleteEvent(eventId: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
siteId = site.getId();
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
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,36 +365,53 @@ 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) => {
|
||||||
|
const reminder = {
|
||||||
|
eventid: event.id,
|
||||||
|
time: time
|
||||||
|
};
|
||||||
|
|
||||||
return this.getEventNotificationTimeOption(id, siteId).then((time: number) => {
|
return site.getDb().insertRecord(AddonCalendarProvider.REMINDERS_TABLE, reminder).then((reminderId) => {
|
||||||
if (time == -1) {
|
return this.scheduleEventNotification(event, reminderId, time, site.getId());
|
||||||
return this.getDefaultNotificationTime(siteId);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return time;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 wvent 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) => {
|
||||||
return e.notificationtime || -1;
|
return site.getDb().getRecords(AddonCalendarProvider.REMINDERS_TABLE, {eventid: id}, 'time ASC');
|
||||||
}).catch(() => {
|
|
||||||
return -1;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,34 +610,48 @@ 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.
|
let promise;
|
||||||
const promise = time == -1 ? this.getDefaultNotificationTime(siteId) : Promise.resolve(time);
|
if (time == -1) {
|
||||||
|
// If time is -1, get event default time to calculate the notification time.
|
||||||
|
promise = this.getDefaultNotificationTime(siteId).then((time) => {
|
||||||
|
if (time == 0) {
|
||||||
|
// Default notification time is disabled, do not show.
|
||||||
|
return this.localNotificationsProvider.cancel(reminderId, AddonCalendarProvider.COMPONENT, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.timestart - (time * 60);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve(time);
|
||||||
|
}
|
||||||
|
|
||||||
return promise.then((time) => {
|
return promise.then((time) => {
|
||||||
const timeEnd = (event.timestart + event.timeduration) * 1000;
|
time = time * 1000;
|
||||||
if (timeEnd <= new Date().getTime()) {
|
|
||||||
// The event has finished already, don't schedule it.
|
if (time <= new Date().getTime()) {
|
||||||
return Promise.resolve();
|
// This reminder is over, don't schedule. Cancel if it was scheduled.
|
||||||
|
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)
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
eventid: event.id,
|
eventid: event.id,
|
||||||
|
reminderid: reminderId,
|
||||||
siteid: siteId
|
siteid: siteId
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -560,18 +674,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 Promise.all(promises);
|
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.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -597,31 +720,18 @@ export class AddonCalendarProvider {
|
||||||
* @return {Promise<any>} Promise resolved when stored.
|
* @return {Promise<any>} Promise resolved when stored.
|
||||||
*/
|
*/
|
||||||
storeEventInLocalDb(event: any, siteId?: string): Promise<any> {
|
storeEventInLocalDb(event: any, siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
|
||||||
return site.getDb().insertRecord(AddonCalendarProvider.EVENTS_TABLE, event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store events in local DB.
|
|
||||||
*
|
|
||||||
* @param {any[]} events Events to store.
|
|
||||||
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
|
|
||||||
* @return {Promise<any[]>} Promise resolved when the events are stored.
|
|
||||||
*/
|
|
||||||
protected storeEventsInLocalDB(events: any[], siteId?: string): Promise<any[]> {
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
siteId = site.getId();
|
siteId = site.getId();
|
||||||
|
|
||||||
const promises = [],
|
// If event does not exist on the DB, schedule the reminder.
|
||||||
db = site.getDb();
|
return this.getEventFromLocalDb(event.id, site.id).catch(() => {
|
||||||
|
// Event does not exist. Check if any reminder exists first.
|
||||||
events.forEach((event) => {
|
return this.getEventReminders(event.id, siteId).then((reminders) => {
|
||||||
// Don't override event notification time if the user configured it.
|
if (reminders.length == 0) {
|
||||||
promises.push(this.getEventFromLocalDb(event.id, siteId).catch(() => {
|
this.addEventReminder(event, -1, siteId);
|
||||||
// Event not stored, return empty object.
|
}
|
||||||
return {};
|
});
|
||||||
}).then((e) => {
|
}).then(() => {
|
||||||
const eventRecord = {
|
const eventRecord = {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
name: event.name,
|
name: event.name,
|
||||||
|
@ -641,37 +751,29 @@ export class AddonCalendarProvider {
|
||||||
visible: event.visible,
|
visible: event.visible,
|
||||||
uuid: event.uuid,
|
uuid: event.uuid,
|
||||||
sequence: event.sequence,
|
sequence: event.sequence,
|
||||||
subscriptionid: event.subscriptionid,
|
subscriptionid: event.subscriptionid
|
||||||
notificationtime: e.notificationtime || -1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return db.insertRecord(AddonCalendarProvider.EVENTS_TABLE, eventRecord);
|
return site.getDb().insertRecord(AddonCalendarProvider.EVENTS_TABLE, eventRecord);
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an event notification time and schedule a new notification.
|
* Store events in local DB.
|
||||||
*
|
*
|
||||||
* @param {any} event Event to update its notification time.
|
* @param {any[]} events Events to store.
|
||||||
* @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.
|
* @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.
|
* @return {Promise<any[]>} Promise resolved when the events are stored.
|
||||||
*/
|
*/
|
||||||
updateNotificationTime(event: any, time: number, siteId?: string): Promise<void> {
|
protected storeEventsInLocalDB(events: any[], siteId?: string): Promise<any[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
if (!this.sitesProvider.isLoggedIn()) {
|
siteId = site.getId();
|
||||||
// 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})
|
return Promise.all(events.map((event) => {
|
||||||
.then(() => {
|
// If event does not exist on the DB, schedule the reminder.
|
||||||
return this.scheduleEventNotification(event, time);
|
return this.storeEventInLocalDb(event, siteId);
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ export class AddonModDataOfflineProvider {
|
||||||
primaryKeys: ['dataid', 'entryid', 'action']
|
primaryKeys: ['dataid', 'entryid', 'action']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
migrate(db: SQLiteDB, oldVersion: number): Promise<any> | void {
|
migrate(db: SQLiteDB, oldVersion: number, siteId: string): Promise<any> | void {
|
||||||
if (oldVersion == 0) {
|
if (oldVersion == 0) {
|
||||||
// Move the records from the old table.
|
// Move the records from the old table.
|
||||||
const newTable = AddonModDataOfflineProvider.DATA_ENTRY_TABLE;
|
const newTable = AddonModDataOfflineProvider.DATA_ENTRY_TABLE;
|
||||||
|
|
|
@ -65,7 +65,8 @@
|
||||||
"addon.calendar.eventstarttime": "Start time",
|
"addon.calendar.eventstarttime": "Start time",
|
||||||
"addon.calendar.gotoactivity": "Go to activity",
|
"addon.calendar.gotoactivity": "Go to activity",
|
||||||
"addon.calendar.noevents": "There are no events",
|
"addon.calendar.noevents": "There are no events",
|
||||||
"addon.calendar.notifications": "Notifications",
|
"addon.calendar.reminders": "Reminders",
|
||||||
|
"addon.calendar.setnewreminder": "Set a new reminder",
|
||||||
"addon.calendar.typecategory": "Category event",
|
"addon.calendar.typecategory": "Category event",
|
||||||
"addon.calendar.typeclose": "Close event",
|
"addon.calendar.typeclose": "Close event",
|
||||||
"addon.calendar.typecourse": "Course event",
|
"addon.calendar.typecourse": "Course event",
|
||||||
|
|
|
@ -157,9 +157,10 @@ export interface CoreSiteSchema {
|
||||||
*
|
*
|
||||||
* @param {SQLiteDB} db Site database.
|
* @param {SQLiteDB} db Site database.
|
||||||
* @param {number} oldVersion Old version of the schema or 0 if not installed.
|
* @param {number} oldVersion Old version of the schema or 0 if not installed.
|
||||||
|
* @param {string} siteId Site Id to migrate.
|
||||||
* @return {Promise<any> | void} Promise resolved when done.
|
* @return {Promise<any> | void} Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
migrate?(db: SQLiteDB, oldVersion: number): Promise<any> | void;
|
migrate?(db: SQLiteDB, oldVersion: number, siteId: string): Promise<any> | void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1444,7 +1445,7 @@ export class CoreSitesProvider {
|
||||||
promise = promise.then(() => db.createTablesFromSchema(schema.tables));
|
promise = promise.then(() => db.createTablesFromSchema(schema.tables));
|
||||||
}
|
}
|
||||||
if (schema.migrate) {
|
if (schema.migrate) {
|
||||||
promise = promise.then(() => schema.migrate(db, oldVersion));
|
promise = promise.then(() => schema.migrate(db, oldVersion, site.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set installed version.
|
// Set installed version.
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue