MOBILE-2328 notifications: PR fixes and emulator missing code

main
Albert Gasset 2018-04-09 11:44:12 +02:00
parent 133866598e
commit ee7808643b
17 changed files with 572 additions and 51 deletions

View File

@ -28,7 +28,6 @@ import { AddonMessageOutputAirnotifierProvider } from '../../providers/airnotifi
})
export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
title = '';
devices = [];
devicesLoaded = false;
@ -108,8 +107,8 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
/**
* Enable or disable a certain device.
*
* @param {any} device
* @param {boolean} enable
* @param {any} device The device object.
* @param {boolean} enable True to enable the device, false to disable it.
*/
enableDevice(device: any, enable: boolean): void {
device.updating = true;

View File

@ -70,7 +70,7 @@ export class AddonMessageOutputAirnotifierProvider {
/**
* Get user devices.
*
* @param {string} [siteid] Site ID. If not defined, use current site.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any>} Promise resolved with the devices.
*/
getUserDevices(siteId?: string): Promise<any> {

View File

@ -24,6 +24,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { AddonPushNotificationsProvider } from '@addon/pushnotifications/providers/pushnotifications';
import { AddonPushNotificationsDelegate } from '@addon/pushnotifications/providers/delegate';
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
/**
* Handler to inject an option into main menu.
@ -46,7 +47,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider,
private localNotificationsProvider: CoreLocalNotificationsProvider, private textUtils: CoreTextUtilsProvider,
private pushNotificationsProvider: AddonPushNotificationsProvider, utils: CoreUtilsProvider,
pushNotificationsDelegate: AddonPushNotificationsDelegate) {
pushNotificationsDelegate: AddonPushNotificationsDelegate, private emulatorHelper: CoreEmulatorHelperProvider) {
eventsProvider.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => {
this.updateBadge(data.siteId);
@ -132,9 +133,9 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
}
if (this.appProvider.isDesktop() && this.localNotificationsProvider.isAvailable()) {
// @todo
/*$mmEmulatorHelper.checkNewNotifications(
AddonMessagesProvider.PUSH_SIMULATION_COMPONENT, this.fetchMessages, this.getTitleAndText, siteId);*/
this.emulatorHelper.checkNewNotifications(
AddonMessagesProvider.PUSH_SIMULATION_COMPONENT,
this.fetchMessages.bind(this), this.getTitleAndText.bind(this), siteId);
}
return Promise.resolve();

View File

@ -20,6 +20,7 @@ import { CoreUserProvider } from '@core/user/providers/user';
import { AddonMessagesOfflineProvider } from './messages-offline';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
/**
* Service to handle messages.
@ -40,7 +41,8 @@ export class AddonMessagesProvider {
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider,
private userProvider: CoreUserProvider, private messagesOffline: AddonMessagesOfflineProvider,
private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider) {
private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
private emulatorHelper: CoreEmulatorHelperProvider) {
this.logger = logger.getInstance('AddonMessagesProvider');
}
@ -1088,7 +1090,6 @@ export class AddonMessagesProvider {
/**
* Store the last received message if it's newer than the last stored.
* @todo
*
* @param {number} userIdFrom ID of the useridfrom retrieved, 0 for all users.
* @param {any} message Last message received.
@ -1096,10 +1097,10 @@ export class AddonMessagesProvider {
* @return {Promise<any>} Promise resolved when done.
*/
protected storeLastReceivedMessageIfNeeded(userIdFrom: number, message: any, siteId?: string): Promise<any> {
/*let component = mmaMessagesPushSimulationComponent;
const component = AddonMessagesProvider.PUSH_SIMULATION_COMPONENT;
// Get the last received message.
return $mmEmulatorHelper.getLastReceivedNotification(component, siteId).then((lastMessage) => {
return this.emulatorHelper.getLastReceivedNotification(component, siteId).then((lastMessage) => {
if (userIdFrom > 0 && (!message || !lastMessage)) {
// Seeing a single discussion. No received message or cannot know if it really is the last received message. Stop.
return;
@ -1110,9 +1111,8 @@ export class AddonMessagesProvider {
return;
}
return $mmEmulatorHelper.storeLastReceivedNotification(component, message, siteId);
});*/
return Promise.resolve();
return this.emulatorHelper.storeLastReceivedNotification(component, message, siteId);
});
}
/**

View File

@ -32,8 +32,5 @@ import { AddonNotificationsActionsComponent } from './actions/actions';
exports: [
AddonNotificationsActionsComponent
],
entryComponents: [
AddonNotificationsActionsComponent
]
})
export class AddonNotificationsComponentsModule {}

View File

@ -14,11 +14,11 @@
<img [src]="notification.profileimageurlfrom || 'assets/img/user-avatar.png'" core-external-content [alt]="'core.pictureof' | translate:{$a: notification.userfromfullname}" role="presentation">
</ion-avatar>
<h2>{{notification.userfromfullname}}</h2>
<div item-end *ngIf="!notification.timeread"><ion-icon name="record" color=""></ion-icon></div>
<div item-end *ngIf="!notification.timeread"><ion-icon name="record" color="primary"></ion-icon></div>
<p>{{notification.timecreated | coreDateDayOrTime}}</p>
</ion-item>
<ion-item text-wrap>
<p><core-format-text [text]="formatText(notification.mobiletext | coreCreateLinks)"></core-format-text></p>
<p><core-format-text [text]="notification.mobiletext | coreCreateLinks"></core-format-text></p>
</ion-item>
<addon-notifications-actions [contextUrl]="notification.contexturl" [courseId]="notification.courseid"></addon-notifications-actions>
</ion-card>

View File

@ -70,8 +70,8 @@ export class AddonNotificationsListPage {
/**
* Convenience function to get notifications. Gets unread notifications first.
*
* @param {boolean} refreh
* @return {Primise<any>} Resolved when done.
* @param {boolean} refreh Whether we're refreshing data.
* @return {Promise<any>} Resolved when done.
*/
protected fetchNotifications(refresh?: boolean): Promise<any> {
if (refresh) {
@ -83,6 +83,9 @@ export class AddonNotificationsListPage {
return this.notificationsProvider.getUnreadNotifications(this.unreadCount, limit).then((unread) => {
let promise;
unread.forEach(this.formatText.bind(this));
/* Don't add the unread notifications to this.notifications yet. If there are no unread notifications
that causes that the "There are no notifications" message is shown in pull to refresh. */
this.unreadCount += unread.length;
@ -91,6 +94,7 @@ export class AddonNotificationsListPage {
// Limit not reached. Get read notifications until reach the limit.
const readLimit = limit - unread.length;
promise = this.notificationsProvider.getReadNotifications(this.readCount, readLimit).then((read) => {
read.forEach(this.formatText.bind(this));
this.readCount += read.length;
if (refresh) {
this.notifications = unread.concat(read);
@ -127,7 +131,7 @@ export class AddonNotificationsListPage {
/**
* Mark notifications as read.
*
* @param {any[]} notifications
* @param {any[]} notifications Array of notification objects.
*/
protected markNotificationsAsRead(notifications: any[]): void {
if (notifications.length > 0) {
@ -172,14 +176,12 @@ export class AddonNotificationsListPage {
/**
* Formats the text of a notification.
* @param {string} text
* @return {string} Formatted text.
*
* @param {any} notification The notification object.
*/
formatText(text: string): string {
text = text.replace(/-{4,}/ig, '');
text = this.textUtils.replaceNewLines(text, '<br>');
return text;
protected formatText(notification: any): void {
const text = notification.mobiletext.replace(/-{4,}/ig, '');
notification.mobiletext = this.textUtils.replaceNewLines(text, '<br>');
}
/**

View File

@ -0,0 +1,10 @@
page-addon-notifications-settings {
.list-header {
margin-bottom: 0;
border-top: 0;
}
.toggle {
display: inline-block;
}
}

View File

@ -115,7 +115,7 @@ export class AddonNotificationsSettingsPage implements OnDestroy {
/**
* Load a processor.
*
* @param {any} processor
* @param {any} processor Processor object.
*/
protected loadProcessor(processor: any): void {
if (!processor) {
@ -242,7 +242,7 @@ export class AddonNotificationsSettingsPage implements OnDestroy {
/**
* Change the notification sound setting.
*
* @param {enabled} enabled
* @param {enabled} enabled True to enable the notification sound, false to disable it.
*/
changeNotificationSound(enabled: boolean): void {
this.configProvider.set(CoreConstants.SETTINGS_NOTIFICATION_SOUND, enabled).finally(() => {

View File

@ -18,6 +18,8 @@ import { CoreCronHandler } from '@providers/cron';
import { CoreEventsProvider } from '@providers/events';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { CoreSitesProvider } from '@providers/sites';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
import { AddonNotificationsProvider } from './notifications';
/**
@ -29,7 +31,8 @@ export class AddonNotificationsCronHandler implements CoreCronHandler {
constructor(private appProvider: CoreAppProvider, private eventsProvider: CoreEventsProvider,
private sitesProvider: CoreSitesProvider, private localNotifications: CoreLocalNotificationsProvider,
private notificationsProvider: AddonNotificationsProvider) {}
private notificationsProvider: AddonNotificationsProvider, private textUtils: CoreTextUtilsProvider,
private emulatorHelper: CoreEmulatorHelperProvider) {}
/**
* Get the time between consecutive executions.
@ -73,12 +76,37 @@ export class AddonNotificationsCronHandler implements CoreCronHandler {
}
if (this.appProvider.isDesktop() && this.localNotifications.isAvailable()) {
/* @todo
$mmEmulatorHelper.checkNewNotifications(
mmaNotificationsPushSimulationComponent, fetchNotifications, getTitleAndText, siteId);
*/
this.emulatorHelper.checkNewNotifications(
AddonNotificationsProvider.PUSH_SIMULATION_COMPONENT,
this.fetchNotifications.bind(this), this.getTitleAndText.bind(this), siteId);
}
return Promise.resolve(null);
}
/**
* Get the latest unread notifications from a site.
*
* @param {string} siteId Site ID.
* @return {Promise<any[]>} Promise resolved with the notifications.
*/
protected fetchNotifications(siteId: string): Promise<any[]> {
return this.notificationsProvider.getUnreadNotifications(0, undefined, true, false, true, siteId);
}
/**
* Given a notification, return the title and the text for the notification.
*
* @param {any} notification Notification.
* @return {Promise<any>} Promise resvoled with an object with title and text.
*/
protected getTitleAndText(notification: any): Promise<any> {
const data = {
title: notification.userfromfullname,
text: notification.mobiletext.replace(/-{4,}/ig, '')
};
data.text = this.textUtils.replaceNewLines(data.text, '<br>');
return Promise.resolve(data);
}
}

View File

@ -95,7 +95,7 @@ export class AddonNotificationsMainMenuHandler implements CoreMainMenuHandler {
/**
* Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled.
*
* @param {string} siteId Site ID or current Site if undefined.
* @param {string} [siteId] Site ID or current Site if undefined.
*/
updateBadge(siteId?: string): void {
siteId = siteId || this.sitesProvider.getCurrentSiteId();

View File

@ -18,6 +18,7 @@ import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreUserProvider } from '@core/user/providers/user';
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
/**
* Service to handle notifications.
@ -27,14 +28,15 @@ export class AddonNotificationsProvider {
static READ_CHANGED_EVENT = 'addon_notifications_read_changed_event';
static READ_CRON_EVENT = 'addon_notifications_read_cron_event';
static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation';
static PUSH_SIMULATION_COMPONENT = 'AddonNotificationsPushSimulation';
static LIST_LIMIT = 20;
protected ROOT_CACHE_KEY = 'mmaNotifications:';
protected logger;
constructor(logger: CoreLoggerProvider, private appProvider: CoreAppProvider, private sitesProvider: CoreSitesProvider,
private timeUtils: CoreTimeUtilsProvider, private userProvider: CoreUserProvider) {
private timeUtils: CoreTimeUtilsProvider, private userProvider: CoreUserProvider,
private emulatorHelper: CoreEmulatorHelperProvider) {
this.logger = logger.getInstance('AddonNotificationsProvider');
}
@ -75,7 +77,7 @@ export class AddonNotificationsProvider {
/**
* Get notification preferences.
*
* @param {string} [siteid] Site ID. If not defined, use current site.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any>} Promise resolved with the notification preferences.
*/
getNotificationPreferences(siteId?: string): Promise<any> {
@ -141,11 +143,9 @@ export class AddonNotificationsProvider {
const notifications = response.messages;
this.formatNotificationsData(notifications);
if (this.appProvider.isDesktop() && toDisplay && !read && limitFrom === 0) {
/* @todo
// Store the last received notification. Don't block the user for this.
$mmEmulatorHelper.storeLastReceivedNotification(
mmaNotificationsPushSimulationComponent, notifications[0], siteId);
*/
this.emulatorHelper.storeLastReceivedNotification(
AddonNotificationsProvider.PUSH_SIMULATION_COMPONENT, notifications[0], siteId);
}
return notifications;

View File

@ -25,6 +25,7 @@ import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreConfigProvider } from '@providers/config';
import { CoreConstants } from '@core/constants';
import { CoreConfigConstants } from '../../../configconstants';
/**
@ -89,8 +90,7 @@ export class AddonPushNotificationsProvider {
* @return {Promise<PushOptions>} Promise with the push options resolved when done.
*/
protected getOptions(): Promise<PushOptions> {
// @todo: CoreSettingsProvider.NOTIFICATION_SOUND
return this.configProvider.get('CoreSettingsProvider.NOTIFICATION_SOUND', true).then((soundEnabled) => {
return this.configProvider.get(CoreConstants.SETTINGS_NOTIFICATION_SOUND, true).then((soundEnabled) => {
return {
android: {
senderID: CoreConfigConstants.gcmpn,

View File

@ -35,6 +35,7 @@ import { SQLite } from '@ionic-native/sqlite';
import { Zip } from '@ionic-native/zip';
// Services that Mock Ionic Native in browser an desktop.
import { BadgeMock } from './providers/badge';
import { CameraMock } from './providers/camera';
import { ClipboardMock } from './providers/clipboard';
import { FileMock } from './providers/file';
@ -44,6 +45,7 @@ import { InAppBrowserMock } from './providers/inappbrowser';
import { LocalNotificationsMock } from './providers/local-notifications';
import { MediaCaptureMock } from './providers/media-capture';
import { NetworkMock } from './providers/network';
import { PushMock } from './providers/push';
import { ZipMock } from './providers/zip';
import { CoreEmulatorHelperProvider } from './providers/helper';
@ -89,7 +91,14 @@ export const IONIC_NATIVE_PROVIDERS = [
imports: [
],
providers: [
Badge, // @todo: Mock
{
provide: Badge,
deps: [CoreAppProvider],
useFactory: (appProvider: CoreAppProvider): Badge => {
// Use platform instead of CoreAppProvider to prevent circular dependencies.
return appProvider.isMobile() ? new Badge() : new BadgeMock(appProvider);
}
},
CoreEmulatorHelperProvider,
CoreEmulatorCaptureHelperProvider,
{
@ -162,7 +171,14 @@ export const IONIC_NATIVE_PROVIDERS = [
return platform.is('cordova') ? new Network() : new NetworkMock();
}
},
Push, // @todo: Mock
{
provide: Push,
deps: [CoreAppProvider],
useFactory: (appProvider: CoreAppProvider): Push => {
// Use platform instead of CoreAppProvider to prevent circular dependencies.
return appProvider.isMobile() ? new Push() : new PushMock(appProvider);
}
},
SplashScreen,
StatusBar,
SQLite,

View File

@ -0,0 +1,123 @@
// (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 { Badge } from '@ionic-native/badge';
import { CoreAppProvider } from '@providers/app';
/**
* Emulates the Cordova Push plugin in desktop apps and in browser.
*/
@Injectable()
export class BadgeMock implements Badge {
constructor(private appProvider: CoreAppProvider) {}
/**
* Clear the badge of the app icon.
*
* @returns {Promise<boolean>}
*/
clear(): Promise<boolean> {
return Promise.reject('clear is only supported in mobile devices');
}
/**
* Set the badge of the app icon.
* @param {number} badgeNumber The new badge number.
* @returns {Promise<any>}
*/
set(badgeNumber: number): Promise<any> {
if (!this.appProvider.isDesktop()) {
return Promise.reject('set is not supported in browser');
}
try {
const app = require('electron').remote.app;
if (app.setBadgeCount(badgeNumber)) {
return Promise.resolve();
} else {
return Promise.reject(null);
}
} catch (ex) {
return Promise.reject(ex);
}
}
/**
* Get the badge of the app icon.
*
* @returns {Promise<any>}
*/
get(): Promise<any> {
if (!this.appProvider.isDesktop()) {
return Promise.reject('get is not supported in browser');
}
try {
const app = require('electron').remote.app;
return Promise.resolve(app.getBadgeCount());
} catch (ex) {
return Promise.reject(ex);
}
}
/**
* Increase the badge number.
*
* @param {number} increaseBy Count to add to the current badge number
* @returns {Promise<any>}
*/
increase(increaseBy: number): Promise<any> {
return Promise.reject('increase is only supported in mobile devices');
}
/**
* Decrease the badge number.
*
* @param {number} decreaseBy Count to subtract from the current badge number
* @returns {Promise<any>}
*/
decrease(decreaseBy: number): Promise<any> {
return Promise.reject('decrease is only supported in mobile devices');
}
/**
* Check support to show badges.
*
* @returns {Promise<any>}
*/
isSupported(): Promise<any> {
return Promise.reject('isSupported is only supported in mobile devices');
}
/**
* Determine if the app has permission to show badges.
*
* @returns {Promise<any>}
*/
hasPermission(): Promise<any> {
return Promise.reject('hasPermission is only supported in mobile devices');
}
/**
* Register permission to set badge notifications
*
* @returns {Promise<any>}
*/
requestPermission(): Promise<any> {
return Promise.reject('requestPermission is only supported in mobile devices');
}
}

View File

@ -17,9 +17,15 @@ import { CoreFileProvider } from '@providers/file';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { File } from '@ionic-native/file';
import { LocalNotifications } from '@ionic-native/local-notifications';
import { CoreAppProvider } from '@providers/app';
import { CoreInitDelegate, CoreInitHandler } from '@providers/init';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { FileTransferErrorMock } from './file-transfer';
import { CoreEmulatorCaptureHelperProvider } from './capture-helper';
import { CoreConstants } from '../../constants';
/**
* Helper service for the emulator feature. It also acts as an init handler.
@ -30,9 +36,38 @@ export class CoreEmulatorHelperProvider implements CoreInitHandler {
priority = CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 500;
blocking = true;
protected logger;
// Variables for database.
protected LAST_RECEIVED_NOTIFICATION_TABLE = 'core_emulator_last_received_notification';
protected tablesSchema = [
{
name: this.LAST_RECEIVED_NOTIFICATION_TABLE,
columns: [
{
name: 'component',
type: 'TEXT'
},
{
name: 'id',
type: 'INTEGER',
},
{
name: 'timecreated',
type: 'INTEGER',
},
],
primaryKeys: ['component']
}
];
constructor(private file: File, private fileProvider: CoreFileProvider, private utils: CoreUtilsProvider,
initDelegate: CoreInitDelegate, private localNotif: LocalNotifications,
private captureHelper: CoreEmulatorCaptureHelperProvider) { }
logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private localNotif: LocalNotifications,
private captureHelper: CoreEmulatorCaptureHelperProvider, private timeUtils: CoreTimeUtilsProvider,
private appProvider: CoreAppProvider, private localNotifProvider: CoreLocalNotificationsProvider) {
this.logger = logger.getInstance('CoreEmulatorHelper');
sitesProvider.createTablesFromSchema(this.tablesSchema);
}
/**
* Load the Mocks that need it.
@ -52,4 +87,130 @@ export class CoreEmulatorHelperProvider implements CoreInitHandler {
return this.utils.allPromises(promises);
}
/**
* Check if there are new notifications, triggering a local notification if found.
* Only for desktop apps since they don't support push notifications.
*
* @param {string} component Component to check.
* @param {Function} fetchFn Function that receives a site ID and returns a Promise resolved with an array of notifications.
* @param {Function} getDataFn Function that receives a notification and returns a promise resolved with the title and text.
* @param {string} [siteId] Site ID to check. If not defined, check all sites.
* @return {Promise<any>} Promise resolved when done.
*/
checkNewNotifications(component: string, fetchFn: Function, getDataFn: Function, siteId?: string): Promise<any> {
if (!this.appProvider.isDesktop() || !this.localNotifProvider.isAvailable()) {
return Promise.resolve(null);
}
if (!this.appProvider.isOnline()) {
this.logger.debug('Cannot check push notifications because device is offline.');
return Promise.reject(null);
}
let promise: Promise<string[]>;
if (!siteId) {
// No site ID defined, check all sites.
promise = this.sitesProvider.getSitesIds();
} else {
promise = Promise.resolve([siteId]);
}
return promise.then((siteIds) => {
const sitePromises = siteIds.map((siteId) => {
// Check new notifications for each site.
return this.checkNewNotificationsForSite(component, fetchFn, getDataFn, siteId);
});
return Promise.all(sitePromises);
});
}
/**
* Check if there are new notifications for a certain site, triggering a local notification if found.
*
* @param {string} component Component to check.
* @param {Function} fetchFn Function that receives a site ID and returns a Promise resolved with an array of notifications.
* @param {Function} getDataFn Function that receives a notification and returns a promise resolved with the title and text.
* @param {string} siteId Site ID to check.
* @return {Promise<any>} Promise resolved when done.
*/
protected checkNewNotificationsForSite(component: string, fetchFn: Function, getDataFn: Function, siteId: string)
: Promise<any> {
// Get the last received notification in the app.
return this.getLastReceivedNotification(component, siteId).then((lastNotification) => {
// Now fetch the latest notifications from the server.
return fetchFn(siteId).then((notifications) => {
if (!lastNotification || !notifications.length) {
// No last notification stored (first call) or no new notifications. Stop.
return;
}
const notification = notifications[0];
if (notification.id == lastNotification.id || notification.timecreated <= lastNotification.timecreated ||
this.timeUtils.timestamp() - notification.timecreated > CoreConstants.SECONDS_DAY) {
// There are no new notifications or the newest one happened more than a day ago, stop.
return;
}
// There is a new notification, show it.
return getDataFn(notification).then((titleAndText) => {
const localNotif = {
id: 1,
at: new Date(),
title: titleAndText.title,
text: titleAndText.text,
data: {
notif: notification,
site: siteId
}
};
return this.localNotifProvider.schedule(localNotif, component, siteId);
});
});
});
}
/**
* Get the last notification received in a certain site for a certain component.
*
* @param {string} component Component of the notification to get.
* @param {string} siteId Site ID of the notification.
* @return {Promise<any>} Promise resolved with the notification or false if not found.
*/
getLastReceivedNotification(component: string, siteId: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
return site.getDb().getRecord(this.LAST_RECEIVED_NOTIFICATION_TABLE, {component: component});
}).catch(() => {
return false;
});
}
/**
* Store the last notification received in a certain site.
*
* @param {string} component Component of the notification to store.
* @param {any} notification Notification to store.
* @param {string} siteId Site ID of the notification.
* @return {Promise<any>} Promise resolved when done.
*/
storeLastReceivedNotification(component: string, notification: any, siteId: string): Promise<any> {
if (!notification) {
// No notification, store a fake one.
notification = {id: -1, timecreated: 0};
}
return this.sitesProvider.getSite(siteId).then((site) => {
const entry = {
component: component,
id: notification.id,
timecreated: notification.timecreated,
};
return site.getDb().insertRecord(this.LAST_RECEIVED_NOTIFICATION_TABLE, entry);
});
}
}

View File

@ -0,0 +1,184 @@
// (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 { Observable } from 'rxjs/Observable';
import { Channel, EventResponse, Push, PushEvent, PushObject, PushOptions } from '@ionic-native/push';
import { CoreAppProvider } from '@providers/app';
/**
* Emulates the Cordova Push plugin in desktop apps and in browser.
*/
@Injectable()
export class PushMock implements Push {
constructor(private appProvider: CoreAppProvider) {
}
/**
* Init push notifications
*
* @param {PushOptions} options
* @return {PushObject}
*/
init(options: PushOptions): PushObject {
return new PushObjectMock(this.appProvider);
}
/**
* Check whether the push notification permission has been granted.
*
* @return {Promise<{isEnabled: boolean}>} Returns a Promise that resolves with an object with one property: isEnabled, a
* boolean that indicates if permission has been granted.
*/
hasPermission(): Promise<{isEnabled: boolean}> {
return Promise.reject('hasPermission is only supported in mobile devices');
}
/**
* Create a new notification channel for Android O and above.
*
* @param {Channel} channel
*/
createChannel(channel?: Channel): Promise<any> {
return Promise.reject('createChannel is only supported in mobile devices');
}
/**
* Delete a notification channel for Android O and above.
*
* @param {string} id
*/
deleteChannel(id?: string): Promise<any> {
return Promise.reject('deleteChannel is only supported in mobile devices');
}
/**
* Returns a list of currently configured channels.
*
* @return {Promise<Channel[]>}
*/
listChannels(): Promise<Channel[]> {
return Promise.reject('listChannels is only supported in mobile devices');
}
}
/**
* Emulates the PushObject class in desktop apps and in browser.
*/
export class PushObjectMock extends PushObject {
constructor(private appProvider: CoreAppProvider) {
super({});
}
/**
* Adds an event listener
* @param event {string}
* @return {Observable<EventResponse>}
*/
on(event: PushEvent): Observable<EventResponse> {
return Observable.empty();
}
/**
* The unregister method is used when the application no longer wants to receive push notifications.
* Beware that this cleans up all event handlers previously registered,
* so you will need to re-register them if you want them to function again without an application reload.
*/
unregister(): Promise<any> {
return Promise.reject('unregister is only supported in mobile devices');
}
/**
* Set the badge count visible when the app is not running
*
* The count is an integer indicating what number should show up in the badge.
* Passing 0 will clear the badge.
* Each notification event contains a data.count value which can be used to set the badge to correct number.
*
* @param count
*/
setApplicationIconBadgeNumber(count?: number): Promise<any> {
if (!this.appProvider.isDesktop()) {
return Promise.reject('setApplicationIconBadgeNumber is not supported in browser');
}
try {
const app = require('electron').remote.app;
if (app.setBadgeCount(count)) {
return Promise.resolve();
} else {
return Promise.reject(null);
}
} catch (ex) {
return Promise.reject(ex);
}
}
/**
* Get the current badge count visible when the app is not running
* successHandler gets called with an integer which is the current badge count
*/
getApplicationIconBadgeNumber(): Promise<number> {
if (!this.appProvider.isDesktop()) {
return Promise.reject('getApplicationIconBadgeNumber is not supported in browser');
}
try {
const app = require('electron').remote.app;
return Promise.resolve(app.getBadgeCount());
} catch (ex) {
return Promise.reject(ex);
}
}
/**
* iOS only
* Tells the OS that you are done processing a background push notification.
* successHandler gets called when background push processing is successfully completed.
* @param [id]
*/
finish(id?: string): Promise<any> {
return Promise.reject('finish is only supported in mobile devices');
}
/**
* Tells the OS to clear all notifications from the Notification Center
*/
clearAllNotifications(): Promise<any> {
return Promise.reject('clearAllNotifications is only supported in mobile devices');
}
/**
* The subscribe method is used when the application wants to subscribe a new topic to receive push notifications.
* @param topic {string} Topic to subscribe to.
* @return {Promise<any>}
*/
subscribe(topic: string): Promise<any> {
return Promise.reject('subscribe is only supported in mobile devices');
}
/**
* The unsubscribe method is used when the application no longer wants to receive push notifications from a specific topic but
* continue to receive other push messages.
*
* @param topic {string} Topic to unsubscribe from.
* @return {Promise<any>}
*/
unsubscribe(topic: string): Promise<any> {
return Promise.reject('unsubscribe is only supported in mobile devices');
}
}