diff --git a/src/addon/pushnotifications/providers/pushnotifications.ts b/src/addon/pushnotifications/providers/pushnotifications.ts index adb48e419..df36c3bed 100644 --- a/src/addon/pushnotifications/providers/pushnotifications.ts +++ b/src/addon/pushnotifications/providers/pushnotifications.ts @@ -22,6 +22,7 @@ import { CoreAppProvider } from '@providers/app'; import { CoreInitDelegate } from '@providers/init'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; +import { CoreSitesFactoryProvider } from '@providers/sites-factory'; import { AddonPushNotificationsDelegate } from './delegate'; import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -30,7 +31,7 @@ import { CoreConfigProvider } from '@providers/config'; import { CoreConstants } from '@core/constants'; import { CoreConfigConstants } from '../../../configconstants'; import { ILocalNotification } from '@ionic-native/local-notifications'; -import { SQLiteDBTableSchema } from '@classes/sqlitedb'; +import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; import { CoreSite } from '@classes/site'; /** @@ -87,11 +88,12 @@ export interface AddonPushNotificationsRegisterData { export class AddonPushNotificationsProvider { protected logger; protected pushID: string; - protected appDB: any; + protected appDB: SQLiteDB; static COMPONENT = 'AddonPushNotificationsProvider'; // Variables for database. static BADGE_TABLE = 'addon_pushnotifications_badge'; + static PENDING_UNREGISTER_TABLE = 'addon_pushnotifications_pending_unregister'; static REGISTERED_DEVICES_TABLE = 'addon_pushnotifications_registered_devices'; protected appTablesSchema: SQLiteDBTableSchema[] = [ { @@ -111,6 +113,28 @@ export class AddonPushNotificationsProvider { } ], primaryKeys: ['siteid', 'addon'] + }, + { + name: AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, + columns: [ + { + name: 'siteid', + type: 'TEXT', + primaryKey: true + }, + { + name: 'siteurl', + type: 'TEXT' + }, + { + name: 'token', + type: 'TEXT' + }, + { + name: 'info', + type: 'TEXT' + } + ] } ]; protected siteSchema: CoreSiteSchema = { @@ -159,7 +183,7 @@ export class AddonPushNotificationsProvider { private badge: Badge, private localNotificationsProvider: CoreLocalNotificationsProvider, private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, private push: Push, private configProvider: CoreConfigProvider, private device: Device, private zone: NgZone, - private translate: TranslateService, private platform: Platform) { + private translate: TranslateService, private platform: Platform, private sitesFactory: CoreSitesFactoryProvider) { this.logger = logger.getInstance('AddonPushNotificationsProvider'); this.appDB = appProvider.getDB(); this.appDB.createTablesFromSchema(this.appTablesSchema); @@ -354,11 +378,33 @@ export class AddonPushNotificationsProvider { return Promise.reject(null); } + const promises = []; + // Remove the device from the local DB. - return site.getDb().deleteRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, this.getRegisterData()) - .catch(() => { + promises.push(site.getDb().deleteRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, + this.getRegisterData())); + + // Remove pending unregisters for this site. + promises.push(this.appDB.deleteRecords(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: site.id})); + + return Promise.all(promises).catch(() => { // Ignore errors. }); + }).catch((error) => { + if (this.utils.isWebServiceError(error)) { + // It's a WebService error, can't unregister. + return Promise.reject(error); + } + + // Store the pending unregister so it's retried again later. + return this.appDB.insertRecord(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, { + siteid: site.id, + siteurl: site.getURL(), + token: site.getToken(), + info: JSON.stringify(site.getInfo()) + }).then(() => { + return Promise.reject(error); + }); }); } @@ -547,8 +593,12 @@ export class AddonPushNotificationsProvider { }); }); } + }).finally(() => { + // Remove pending unregisters for this site. + this.appDB.deleteRecords(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: site.id}).catch(() => { + // Ignore errors. + }); }); - } /** @@ -566,6 +616,38 @@ export class AddonPushNotificationsProvider { }); } + /** + * Retry pending unregisters. + * + * @param {string} [siteId] If defined, retry only for that site if needed. Otherwise, retry all pending unregisters. + * @return {Promise} Promise resolved when done. + */ + retryUnregisters(siteId?: string): Promise { + let promise; + + if (siteId) { + // Check if the site has a pending unregister. + promise = this.appDB.getRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, {siteid: siteId}); + } else { + // Get all pending unregisters. + promise = this.appDB.getAllRecords(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE); + } + + return promise.then((results) => { + const promises = []; + + results.forEach((result) => { + // Create a temporary site to unregister. + const tmpSite = this.sitesFactory.makeSite(result.siteid, result.siteurl, result.token, + this.textUtils.parseJSON(result.info, {})); + + promises.push(this.unregisterDeviceOnMoodle(tmpSite)); + }); + + return Promise.all(promises); + }); + } + /** * Save the addon/site badgecounter on the database. * diff --git a/src/addon/pushnotifications/providers/unregister-cron-handler.ts b/src/addon/pushnotifications/providers/unregister-cron-handler.ts new file mode 100644 index 000000000..548d44bd0 --- /dev/null +++ b/src/addon/pushnotifications/providers/unregister-cron-handler.ts @@ -0,0 +1,47 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injectable } from '@angular/core'; +import { CoreCronHandler } from '@providers/cron'; +import { AddonPushNotificationsProvider } from './pushnotifications'; + +/** + * Cron handler to retry pending unregisters. + */ +@Injectable() +export class AddonPushNotificationsUnregisterCronHandler implements CoreCronHandler { + name = 'AddonPushNotificationsUnregisterCronHandler'; + + constructor(private pushNotificationsProvider: AddonPushNotificationsProvider) {} + + /** + * Execute the process. + * Receives the ID of the site affected, undefined for all sites. + * + * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @return {Promise} Promise resolved when done, rejected if failure. + */ + execute(siteId?: string): Promise { + return this.pushNotificationsProvider.retryUnregisters(siteId); + } + + /** + * Get the time between consecutive executions. + * + * @return {number} Time between consecutive executions (in ms). + */ + getInterval(): number { + return 300000; + } +} diff --git a/src/addon/pushnotifications/pushnotifications.module.ts b/src/addon/pushnotifications/pushnotifications.module.ts index 9ae4bde6a..d36a0ce43 100644 --- a/src/addon/pushnotifications/pushnotifications.module.ts +++ b/src/addon/pushnotifications/pushnotifications.module.ts @@ -17,6 +17,7 @@ import { Platform } from 'ionic-angular'; import { AddonPushNotificationsProvider } from './providers/pushnotifications'; import { AddonPushNotificationsDelegate } from './providers/delegate'; import { AddonPushNotificationsRegisterCronHandler } from './providers/register-cron-handler'; +import { AddonPushNotificationsUnregisterCronHandler } from './providers/unregister-cron-handler'; import { CoreCronDelegate } from '@providers/cron'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; @@ -37,19 +38,22 @@ export const ADDON_PUSHNOTIFICATIONS_PROVIDERS: any[] = [ providers: [ AddonPushNotificationsProvider, AddonPushNotificationsDelegate, - AddonPushNotificationsRegisterCronHandler + AddonPushNotificationsRegisterCronHandler, + AddonPushNotificationsUnregisterCronHandler ] }) export class AddonPushNotificationsModule { constructor(platform: Platform, pushNotificationsProvider: AddonPushNotificationsProvider, eventsProvider: CoreEventsProvider, localNotificationsProvider: CoreLocalNotificationsProvider, loggerProvider: CoreLoggerProvider, updateManager: CoreUpdateManagerProvider, cronDelegate: CoreCronDelegate, - registerCronHandler: AddonPushNotificationsRegisterCronHandler) { + registerCronHandler: AddonPushNotificationsRegisterCronHandler, + unregisterCronHandler: AddonPushNotificationsUnregisterCronHandler) { const logger = loggerProvider.getInstance('AddonPushNotificationsModule'); // Register the handlers. cronDelegate.register(registerCronHandler); + cronDelegate.register(unregisterCronHandler); // Register device on GCM or APNS server. platform.ready().then(() => {