[fcm] Android instanceid and core fcm support; iOS instance and basic fcm support

This commit is contained in:
Chris Bianca
2018-02-02 08:40:48 +00:00
parent d429db7e86
commit 850f04914f
28 changed files with 824 additions and 1411 deletions

View File

@@ -18,11 +18,12 @@ import Crashlytics, {
} from '../fabric/crashlytics';
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
import InstanceId, { NAMESPACE as InstanceIdNamespace } from '../instanceid';
import Links, { NAMESPACE as LinksNamespace } from '../links';
import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging';
import NewMessaging, {
NAMESPACE as NewMessagingNamespace,
} from '../messaging/messagingIndex';
import Notifications, {
NAMESPACE as NotificationsNamespace,
} from '../notifications';
import Performance, { NAMESPACE as PerfNamespace } from '../perf';
import Storage, { NAMESPACE as StorageNamespace } from '../storage';
import Utils, { NAMESPACE as UtilsNamespace } from '../utils';
@@ -47,9 +48,10 @@ export default class App {
crashlytics: () => Crashlytics,
};
firestore: () => Firestore;
instanceid: () => InstanceId;
links: () => Links;
messaging: () => Messaging;
newmessaging: () => NewMessaging;
notifications: () => Notifications;
perf: () => Performance;
storage: () => Storage;
utils: () => Utils;
@@ -87,12 +89,13 @@ export default class App {
crashlytics: APPS.appModule(this, CrashlyticsNamespace, Crashlytics),
};
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
this.instanceid = APPS.appModule(this, InstanceIdNamespace, InstanceId);
this.links = APPS.appModule(this, LinksNamespace, Links);
this.messaging = APPS.appModule(this, MessagingNamespace, Messaging);
this.newmessaging = APPS.appModule(
this.notifications = APPS.appModule(
this,
NewMessagingNamespace,
NewMessaging
NotificationsNamespace,
Notifications
);
this.perf = APPS.appModule(this, PerfNamespace, Performance);
this.storage = APPS.appModule(this, StorageNamespace, Storage);

View File

@@ -39,6 +39,10 @@ import {
statics as FirestoreStatics,
MODULE_NAME as FirestoreModuleName,
} from '../firestore';
import {
statics as InstanceIdStatics,
MODULE_NAME as InstanceIdModuleName,
} from '../instanceid';
import {
statics as LinksStatics,
MODULE_NAME as LinksModuleName,
@@ -48,9 +52,9 @@ import {
MODULE_NAME as MessagingModuleName,
} from '../messaging';
import {
statics as NewMessagingStatics,
MODULE_NAME as NewMessagingModuleName,
} from '../messaging/messagingIndex';
statics as NotificationsStatics,
MODULE_NAME as NotificationsModuleName,
} from '../notifications';
import {
statics as PerformanceStatics,
MODULE_NAME as PerfModuleName,
@@ -74,9 +78,10 @@ import type {
FabricModule,
FirebaseOptions,
FirestoreModule,
InstanceIdModule,
LinksModule,
MessagingModule,
NewMessagingModule,
NotificationsModule,
PerformanceModule,
StorageModule,
UtilsModule,
@@ -93,9 +98,10 @@ class Firebase {
database: DatabaseModule;
fabric: FabricModule;
firestore: FirestoreModule;
instanceid: InstanceIdModule;
links: LinksModule;
messaging: MessagingModule;
newmessaging: NewMessagingModule;
notifications: NotificationsModule;
perf: PerformanceModule;
storage: StorageModule;
utils: UtilsModule;
@@ -137,16 +143,21 @@ class Firebase {
FirestoreStatics,
FirestoreModuleName
);
this.instanceid = APPS.moduleAndStatics(
'instanceid',
InstanceIdStatics,
InstanceIdModuleName
);
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
this.messaging = APPS.moduleAndStatics(
'messaging',
MessagingStatics,
MessagingModuleName
);
this.newmessaging = APPS.moduleAndStatics(
'newmessaging',
NewMessagingStatics,
NewMessagingModuleName
this.notifications = APPS.moduleAndStatics(
'notifications',
NotificationsStatics,
NotificationsModuleName
);
this.perf = APPS.moduleAndStatics(
'perf',

View File

@@ -0,0 +1,31 @@
/**
* @flow
* Instance ID representation wrapper
*/
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/firebase-app';
export const MODULE_NAME = 'RNFirebaseInstanceId';
export const NAMESPACE = 'instanceid';
export default class InstanceId extends ModuleBase {
constructor(app: App) {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
}
delete(): Promise<void> {
return getNativeModule(this).delete();
}
get(): Promise<string> {
return getNativeModule(this).get();
}
}
export const statics = {};

View File

@@ -1,94 +1,69 @@
/**
* @flow
* Messaging representation wrapper
* Messaging (FCM) representation wrapper
*/
import { Platform, NativeModules } from 'react-native';
import { SharedEventEmitter } from '../../utils/events';
import INTERNALS from '../../utils/internals';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import RemoteMessage from './RemoteMessage';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
import type App from '../core/firebase-app';
const EVENT_TYPE = {
RefreshToken: 'messaging_token_refreshed',
Notification: 'messaging_notification_received',
type Notification = {
body: string,
bodyLocalizationArgs: string[],
bodyLocalizationKey: string,
clickAction: string,
color: string,
icon: string,
link: string,
sound: string,
tag: string,
title: string,
titleLocalizationArgs: string[],
titleLocalizationKey: string,
};
const NOTIFICATION_TYPE = {
Remote: 'remote_notification',
NotificationResponse: 'notification_response',
WillPresent: 'will_present_notification',
Local: 'local_notification',
type Message = {
collapseKey: string,
data: { [string]: string },
from: string,
messageId: string,
messageType?: string,
openedFromTray: boolean,
notification?: Notification,
sentTime: number,
to?: string,
ttl?: number,
};
const REMOTE_NOTIFICATION_RESULT = {
NewData: 'UIBackgroundFetchResultNewData',
NoData: 'UIBackgroundFetchResultNoData',
ResultFailed: 'UIBackgroundFetchResultFailed',
type OnMessage = Message => any;
type OnMessageObserver = {
next: OnMessage,
};
const WILL_PRESENT_RESULT = {
All: 'UNNotificationPresentationOptionAll',
None: 'UNNotificationPresentationOptionNone',
type OnTokenRefresh = String => any;
type OnTokenRefreshObserver = {
next: OnTokenRefresh,
};
const NATIVE_EVENTS = [EVENT_TYPE.RefreshToken, EVENT_TYPE.Notification];
type RemoteMessage = {
collapseKey?: string,
data: { [string]: string },
messageId?: string,
messageType?: string,
to: string,
ttl: number,
};
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
/**
* IOS only finish function
* @param data
*/
function finish(data) {
if (Platform.OS !== 'ios') {
return;
}
if (!this._finishCalled && this._completionHandlerId) {
let result = data;
this._finishCalled = true;
switch (this._notificationType) {
case NOTIFICATION_TYPE.Remote:
result = result || REMOTE_NOTIFICATION_RESULT.NoData;
if (!Object.values(REMOTE_NOTIFICATION_RESULT).includes(result)) {
throw new Error(
'Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT'
);
}
FirebaseMessaging.finishRemoteNotification(
this._completionHandlerId,
result
);
return;
case NOTIFICATION_TYPE.NotificationResponse:
FirebaseMessaging.finishNotificationResponse(this._completionHandlerId);
return;
case NOTIFICATION_TYPE.WillPresent:
result =
result ||
(this.show_in_foreground
? WILL_PRESENT_RESULT.All
: WILL_PRESENT_RESULT.None);
if (!Object.values(WILL_PRESENT_RESULT).includes(result)) {
throw new Error(
'Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT'
);
}
FirebaseMessaging.finishWillPresentNotification(
this._completionHandlerId,
result
);
break;
default:
}
}
}
const NATIVE_EVENTS = [
'messaging_message_received',
'messaging_token_refreshed',
];
export const MODULE_NAME = 'RNFirebaseMessaging';
export const NAMESPACE = 'messaging';
@@ -104,204 +79,139 @@ export default class Messaging extends ModuleBase {
multiApp: false,
namespace: NAMESPACE,
});
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onMessage
'messaging_message_received',
(message: Message) => {
SharedEventEmitter.emit('onMessage', message);
}
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onMessage
'messaging_token_refreshed',
(token: string) => {
SharedEventEmitter.emit('onTokenRefresh', token);
}
);
}
get EVENT_TYPE(): Object {
return EVENT_TYPE;
}
get NOTIFICATION_TYPE(): Object {
return NOTIFICATION_TYPE;
}
get REMOTE_NOTIFICATION_RESULT(): Object {
return REMOTE_NOTIFICATION_RESULT;
}
get WILL_PRESENT_RESULT(): Object {
return WILL_PRESENT_RESULT;
}
/**
* Returns the notification that triggered application open
* @returns {*}
*/
getInitialNotification(): Promise<Object> {
return getNativeModule(this).getInitialNotification();
}
/**
* Returns the fcm token for the current device
* @returns {*|Promise.<String>}
*/
getToken(): Promise<string> {
return getNativeModule(this).getToken();
}
/**
* Reset Instance ID and revokes all tokens.
* @returns {*|Promise.<*>}
*/
deleteInstanceId(): Promise<void> {
return getNativeModule(this).deleteInstanceId();
}
/**
* Create and display a local notification
* @param notification
* @returns {*}
*/
createLocalNotification(notification: Object): Promise<void> {
const _notification = Object.assign({}, notification);
_notification.id = _notification.id || new Date().getTime().toString();
_notification.local_notification = true;
return getNativeModule(this).createLocalNotification(_notification);
}
/**
*
* @param notification
* @returns {*}
*/
scheduleLocalNotification(notification: Object): Promise<void> {
const _notification = Object.assign({}, notification);
if (!notification.id)
return Promise.reject(
new Error('An id is required to schedule a local notification.')
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
let listener;
if (isFunction(nextOrObserver)) {
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
throw new Error(
'Messaging.onMessage failed: First argument must be a function or observer object with a `next` function.'
);
_notification.local_notification = true;
return getNativeModule(this).scheduleLocalNotification(_notification);
}
// TODO: iOS finish
getLogger(this).info('Creating onMessage listener');
SharedEventEmitter.addListener('onMessage', listener);
return () => {
getLogger(this).info('Removing onMessage listener');
SharedEventEmitter.removeListener('onMessage', listener);
};
}
onTokenRefresh(
nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver
): () => any {
let listener;
if (isFunction(nextOrObserver)) {
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
throw new Error(
'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.'
);
}
getLogger(this).info('Creating onTokenRefresh listener');
SharedEventEmitter.addListener('onTokenRefresh', listener);
return () => {
getLogger(this).info('Removing onTokenRefresh listener');
SharedEventEmitter.removeListener('onTokenRefresh', listener);
};
}
// TODO: Permission structure?
requestPermission(): Promise<void> {
return getNativeModule(this).requestPermission();
}
/**
* Returns an array of all scheduled notifications
* @returns {Promise.<Array>}
* NON WEB-SDK METHODS
*/
getScheduledLocalNotifications(): Promise<Object[]> {
return getNativeModule(this).getScheduledLocalNotifications();
getBadge(): Promise<number> {
return getNativeModule(this).getBadge();
}
/**
* Cancel a local notification by id - using '*' will cancel
* all local notifications.
* @param id
* @returns {*}
*/
cancelLocalNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return getNativeModule(this).cancelAllLocalNotifications();
return getNativeModule(this).cancelLocalNotification(id);
getInitialMessage(): Promise<?Message> {
return getNativeModule(this).getInitialMessage();
}
/**
* Remove a delivered notification - using '*' will remove
* all delivered notifications.
* @param id
* @returns {*}
*/
removeDeliveredNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*')
return getNativeModule(this).removeAllDeliveredNotifications();
return getNativeModule(this).removeDeliveredNotification(id);
sendMessage(remoteMessage: RemoteMessage): Promise<void> {
return getNativeModule(this).send(remoteMessage);
}
/**
* Request notification permission
* @platforms ios
* @returns {*|Promise.<*>}
*/
requestPermissions(): Promise<void> {
return getNativeModule(this).requestPermissions();
setBadge(badge: number): void {
getNativeModule(this).setBadge(badge);
}
/**
* Set notification count badge number
* @param n
*/
setBadgeNumber(n: number): void {
getNativeModule(this).setBadgeNumber(n);
}
/**
* set notification count badge number
* @returns {Promise.<Number>}
*/
getBadgeNumber(): Promise<number> {
return getNativeModule(this).getBadgeNumber();
}
/**
* Subscribe to messages / notifications
* @param listener
* @returns {*}
*/
onMessage(listener: Object => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Notification,
async event => {
const data = {
...event,
finish,
};
await listener(data);
if (!data._finishCalled) {
data.finish();
}
}
);
return () => rnListener.remove();
}
/**
* Subscribe to token refresh events
* @param listener
* @returns {*}
*/
onTokenRefresh(listener: string => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.RefreshToken,
listener
);
return () => rnListener.remove();
}
/**
* Subscribe to a topic
* @param topic
*/
subscribeToTopic(topic: string): void {
getNativeModule(this).subscribeToTopic(topic);
}
/**
* Unsubscribe from a topic
* @param topic
*/
unsubscribeFromTopic(topic: string): void {
getNativeModule(this).unsubscribeFromTopic(topic);
}
/**
* Send an upstream message
* @param remoteMessage
* KNOWN UNSUPPORTED METHODS
*/
send(remoteMessage: RemoteMessage): Promise<void> {
if (!(remoteMessage instanceof RemoteMessage)) {
throw new Error(
'messaging().send requires an instance of RemoteMessage as the first argument.'
);
}
return getNativeModule(this).send(remoteMessage.toJSON());
deleteToken() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'messaging',
'deleteToken'
)
);
}
setBackgroundMessageHandler() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'messaging',
'setBackgroundMessageHandler'
)
);
}
useServiceWorker() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'messaging',
'useServiceWorker'
)
);
}
}
export const statics = {
EVENT_TYPE,
NOTIFICATION_TYPE,
REMOTE_NOTIFICATION_RESULT,
WILL_PRESENT_RESULT,
RemoteMessage,
// RemoteMessage,
};

View File

@@ -1,186 +0,0 @@
/**
* @flow
* Messaging (FCM) representation wrapper
*/
import { SharedEventEmitter } from '../../utils/events';
import INTERNALS from '../../utils/internals';
import { getLogger } from '../../utils/log';
import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import { isFunction, isObject } from '../../utils';
import type App from '../core/firebase-app';
type Message = {
// TODO
};
type OnMessage = Message => any;
type OnMessageObserver = {
next: OnMessage,
};
type OnTokenRefresh = String => any;
type OnTokenRefreshObserver = {
next: OnTokenRefresh,
};
type RemoteMessage = {
// TODO
};
const NATIVE_EVENTS = [
'messaging_message_received',
'messaging_token_refreshed',
];
export const MODULE_NAME = 'NewRNFirebaseMessaging';
export const NAMESPACE = 'newmessaging';
/**
* @class Messaging
*/
export default class Messaging extends ModuleBase {
constructor(app: App) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
namespace: NAMESPACE,
});
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onMessage
'messaging_message_received',
(message: Message) => {
SharedEventEmitter.emit('onMessage', message);
}
);
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
// public event name: onMessage
'messaging_token_refreshed',
(token: string) => {
SharedEventEmitter.emit('onTokenRefresh', token);
}
);
}
deleteToken(token: string): Promise<void> {
return getNativeModule(this).deleteToken(token);
}
getToken(): Promise<string> {
return getNativeModule(this).getToken();
}
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
let listener;
if (isFunction(nextOrObserver)) {
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
throw new Error(
'Messaging.onMessage failed: First argument must be a function or observer object with a `next` function.'
);
}
// TODO: iOS finish
getLogger(this).info('Creating onMessage listener');
SharedEventEmitter.addListener('onMessage', listener);
return () => {
getLogger(this).info('Removing onMessage listener');
SharedEventEmitter.removeListener('onMessage', listener);
};
}
onTokenRefresh(
nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver
): () => any {
let listener;
if (isFunction(nextOrObserver)) {
listener = nextOrObserver;
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
listener = nextOrObserver.next;
} else {
throw new Error(
'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.'
);
}
getLogger(this).info('Creating onTokenRefresh listener');
SharedEventEmitter.addListener('onTokenRefresh', listener);
return () => {
getLogger(this).info('Removing onTokenRefresh listener');
SharedEventEmitter.removeListener('onTokenRefresh', listener);
};
}
requestPermission(): Promise<void> {
return getNativeModule(this).requestPermission();
}
/**
* NON WEB-SDK METHODS
*/
deleteInstanceId(): Promise<void> {
return getNativeModule(this).deleteInstanceId();
}
getBadgeNumber(): Promise<number> {
return getNativeModule(this).getBadgeNumber();
}
getInitialMessage(): Promise<Message> {
return getNativeModule(this).getInitialMessage();
}
sendMessage(remoteMessage: RemoteMessage): Promise<void> {
return getNativeModule(this).send(remoteMessage);
}
setBadgeNumber(badge: number): void {
getNativeModule(this).setBadgeNumber(badge);
}
subscribeToTopic(topic: string): void {
getNativeModule(this).subscribeToTopic(topic);
}
unsubscribeFromTopic(topic: string): void {
getNativeModule(this).unsubscribeFromTopic(topic);
}
/**
* KNOWN UNSUPPORTED METHODS
*/
setBackgroundMessageHandler() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'messaging',
'setBackgroundMessageHandler'
)
);
}
useServiceWorker() {
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'messaging',
'useServiceWorker'
)
);
}
}
export const statics = {
// RemoteMessage,
};

View File

@@ -108,19 +108,24 @@ export default class Notifications extends ModuleBase {
}
/**
* Remove a delivered notification - using '*' will remove
* all delivered notifications.
* Remove a delivered notification.
* @param id
* @returns {*}
*/
removeDeliveredNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') {
return getNativeModule(this).removeAllDeliveredNotifications();
}
return getNativeModule(this).removeDeliveredNotification(id);
}
/**
* Remove all delivered notifications.
* @param id
* @returns {*}
*/
removeDeliveredNotifications(): Promise<void> {
return getNativeModule(this).removeDeliveredNotifications();
}
/**
*
* @param notification
@@ -136,3 +141,5 @@ export default class Notifications extends ModuleBase {
return getNativeModule(this).scheduleLocalNotification(_notification);
}
}
export const statics = {};