From 91e926396555e51b8cca8d068f84d677ff0e4371 Mon Sep 17 00:00:00 2001 From: Salakar Date: Thu, 30 Mar 2017 16:25:27 +0100 Subject: [PATCH] [js][ios][android][messaging] full implementation of upstream send including RemoteMessage builder --- .../messaging/RNFirebaseMessaging.java | 39 ++++----- ios/RNFirebase/RNFirebaseMessaging.m | 27 ++----- lib/firebase.js | 16 +--- lib/modules/messaging/RemoteMessage.js | 80 +++++++++++++++++++ lib/modules/messaging/index.js | 37 +++++---- 5 files changed, 129 insertions(+), 70 deletions(-) create mode 100644 lib/modules/messaging/RemoteMessage.js diff --git a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java index 148b0e7d..89df96f0 100644 --- a/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java +++ b/android/src/main/java/io/invertase/firebase/messaging/RNFirebaseMessaging.java @@ -156,35 +156,30 @@ public class RNFirebaseMessaging extends ReactContextBaseJavaModule implements L } @ReactMethod - public void send(String senderId, ReadableMap payload) throws Exception { + public void send(ReadableMap remoteMessage) { FirebaseMessaging fm = FirebaseMessaging.getInstance(); - RemoteMessage.Builder message = new RemoteMessage.Builder(senderId + "@gcm.googleapis.com") - .setMessageId(UUID.randomUUID().toString()); + RemoteMessage.Builder message = new RemoteMessage.Builder(remoteMessage.getString("sender")); - ReadableMapKeySetIterator iterator = payload.keySetIterator(); + message.setTtl(remoteMessage.getInt("ttl")); + message.setMessageId(remoteMessage.getString("id")); + message.setMessageType(remoteMessage.getString("type")); + + if (remoteMessage.hasKey("collapseKey")) { + message.setCollapseKey(remoteMessage.getString("collapseKey")); + } + + // get data keys and values and add to builder + // js side ensures all data values are strings + // so no need to check types + ReadableMap data = remoteMessage.getMap("data"); + ReadableMapKeySetIterator iterator = data.keySetIterator(); while (iterator.hasNextKey()) { String key = iterator.nextKey(); - String value = getStringFromReadableMap(payload, key); + String value = data.getString(key); message.addData(key, value); } - fm.send(message.build()); - } - private String getStringFromReadableMap(ReadableMap map, String key) throws Exception { - switch (map.getType(key)) { - case String: - return map.getString(key); - case Number: - try { - return String.valueOf(map.getInt(key)); - } catch (Exception e) { - return String.valueOf(map.getDouble(key)); - } - case Boolean: - return String.valueOf(map.getBoolean(key)); - default: - throw new Exception("Unknown data type: " + map.getType(key).name() + " for message key " + key); - } + fm.send(message.build()); } private void registerMessageHandler() { diff --git a/ios/RNFirebase/RNFirebaseMessaging.m b/ios/RNFirebase/RNFirebaseMessaging.m index 4ed6cca5..852273c8 100644 --- a/ios/RNFirebase/RNFirebaseMessaging.m +++ b/ios/RNFirebase/RNFirebaseMessaging.m @@ -278,12 +278,12 @@ RCT_EXPORT_METHOD(requestPermissions:(RCTPromiseResolveBlock)resolve rejecter:(R [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { - resolve(@{@"granted":@(granted)}); + resolve(@{@"granted":@(granted)}); } ]; #endif } - + [[UIApplication sharedApplication] registerForRemoteNotifications]; } @@ -408,23 +408,12 @@ RCT_EXPORT_METHOD(getBadgeNumber: (RCTPromiseResolveBlock)resolve rejecter:(RCTP resolve(@([RCTSharedApplication() applicationIconBadgeNumber])); } -RCT_EXPORT_METHOD(send:(NSString*)senderId withPayload:(NSDictionary *)message) -{ - NSMutableDictionary * mMessage = [message mutableCopy]; - NSMutableDictionary * upstreamMessage = [[NSMutableDictionary alloc] init]; - for (NSString* key in mMessage) { - upstreamMessage[key] = [NSString stringWithFormat:@"%@", [mMessage valueForKey:key]]; - } - - NSDictionary *imMessage = [NSDictionary dictionaryWithDictionary:upstreamMessage]; - - int64_t ttl = 3600; - NSString * receiver = [NSString stringWithFormat:@"%@@gcm.googleapis.com", senderId]; - - NSUUID *uuid = [NSUUID UUID]; - NSString * messageID = [uuid UUIDString]; - - [[FIRMessaging messaging]sendMessage:imMessage to:receiver withMessageID:messageID timeToLive:ttl]; +RCT_EXPORT_METHOD(send:(NSDictionary *)remoteMessage) { + int64_t ttl = @([[remoteMessage valueForKey:@"ttl"] intValue]).doubleValue; + NSString * mId = [[remoteMessage valueForKey:@"id"] stringValue]; + NSString * receiver = [[remoteMessage valueForKey:@"senderId"] stringValue]; + NSDictionary * data = [[remoteMessage valueForKey:@"data"] dictionaryRepresentation]; + [[FIRMessaging messaging]sendMessage:data to:receiver withMessageID:mId timeToLive:ttl]; } RCT_EXPORT_METHOD(finishRemoteNotification: (NSString *)completionHandlerId fetchResult:(UIBackgroundFetchResult)result){ diff --git a/lib/firebase.js b/lib/firebase.js index 567df61f..65685af2 100644 --- a/lib/firebase.js +++ b/lib/firebase.js @@ -11,7 +11,7 @@ import { isObject } from './utils'; import Auth, { statics as AuthStatics } from './modules/auth'; import Storage, { statics as StorageStatics } from './modules/storage'; import Database, { statics as DatabaseStatics } from './modules/database'; -import Messaging from './modules/messaging'; +import Messaging, { statics as MessagingStatics } from './modules/messaging'; import Analytics from './modules/analytics'; import Crash from './modules/crash'; @@ -49,16 +49,16 @@ export default class Firebase { this._log = new Log('firebase'); - this._auth = new Auth(this, this.options); if (this.options.errorOnMissingPlayServices && !this.googleApiAvailability.isAvailable) { throw new Error(`Google Play Services is required to run this application but no valid installation was found (Code ${this.googleApiAvailability.status}).`); } - this.auth = this._staticsOrInstance('auth', StorageStatics, Auth); + this.auth = this._staticsOrInstance('auth', AuthStatics, Auth); this.storage = this._staticsOrInstance('storage', StorageStatics, Storage); this.database = this._staticsOrInstance('database', DatabaseStatics, Database); + this.messaging = this._staticsOrInstance('messaging', MessagingStatics, Messaging); - // init auth to stat listeners + // init auth to start listeners this.auth(); } @@ -92,13 +92,6 @@ export default class Firebase { return this._analytics; } - messaging() { - if (!this._messaging) { - this._messaging = new Messaging(this); - } - return this._messaging; - } - crash() { if (!this._crash) { this._crash = new Crash(this); @@ -146,7 +139,6 @@ export default class Firebase { }; Object.assign(getInstance, statics || {}); - return getInstance; } } diff --git a/lib/modules/messaging/RemoteMessage.js b/lib/modules/messaging/RemoteMessage.js new file mode 100644 index 00000000..ab5c2a66 --- /dev/null +++ b/lib/modules/messaging/RemoteMessage.js @@ -0,0 +1,80 @@ +import { isObject, generatePushID } from './../../utils'; + +export default class RemoteMessage { + constructor(sender: String) { + this.properties = { + id: generatePushID(), + ttl: 3600, + // add the googleapis sender id part if not already added. + sender: sender.includes('@') ? sender : `${sender}@gcm.googleapis.com`, + type: 'remote', + data: {}, + }; + } + + /** + * + * @param ttl + * @returns {RemoteMessage} + */ + setTtl(ttl: Number): RemoteMessage { + this.properties.ttl = ttl; + return this; + } + + /** + * + * @param id + */ + setId(id: string): RemoteMessage { + this.properties.id = id; + return this; + } + + /** + * + * @param type + * @returns {RemoteMessage} + */ + setType(type: string): RemoteMessage { + this.properties.type = type; + return this; + } + + /** + * + * @param key + * @returns {RemoteMessage} + */ + setCollapseKey(key: string): RemoteMessage { + this.properties.collapseKey = key; + return this; + } + + + /** + * + * @param data + * @returns {RemoteMessage} + */ + setData(data: Object = {}) { + if (!isObject(data)) { + throw new Error(`RemoteMessage:setData expects an object as the first parameter but got type '${typeof data}'.`); + } + + const props = Object.keys(data); + + // coerce all property values to strings as + // remote message data only supports strings + for (let i = 0, len = props.length; i < len; i++) { + const prop = props[i]; + this.properties.data[prop] = `${data[prop]}`; + } + + return this; + } + + toJSON() { + return Object.assign({}, this.properties); + } +} diff --git a/lib/modules/messaging/index.js b/lib/modules/messaging/index.js index 776d58fe..79a261c0 100644 --- a/lib/modules/messaging/index.js +++ b/lib/modules/messaging/index.js @@ -1,40 +1,32 @@ import { NativeModules, DeviceEventEmitter, Platform } from 'react-native'; import { Base } from './../base'; +import RemoteMessage from './RemoteMessage'; const FirebaseMessaging = NativeModules.RNFirebaseMessaging; -export const EVENT_TYPE = { +const EVENT_TYPE = { RefreshToken: 'FCMTokenRefreshed', Notification: 'FCMNotificationReceived', }; -export const NOTIFICATION_TYPE = { +const NOTIFICATION_TYPE = { Remote: 'remote_notification', NotificationResponse: 'notification_response', WillPresent: 'will_present_notification', Local: 'local_notification', }; -export const REMOTE_NOTIFICATION_RESULT = { +const REMOTE_NOTIFICATION_RESULT = { NewData: 'UIBackgroundFetchResultNewData', NoData: 'UIBackgroundFetchResultNoData', ResultFailed: 'UIBackgroundFetchResultFailed', }; -export const WILL_PRESENT_RESULT = { +const WILL_PRESENT_RESULT = { All: 'UNNotificationPresentationOptionAll', None: 'UNNotificationPresentationOptionNone', }; -type RemoteMessage = { - id: string, - type: string, - ttl?: number, - sender: string, - collapseKey?: string, - data: Object, -}; - /** * IOS only finish function * @param data @@ -246,10 +238,21 @@ export default class Messaging extends Base { /** * Send an upstream message - * @param senderId - * @param payload + * @param remoteMessage */ - send(senderId, payload: RemoteMessage) { - return FirebaseMessaging.send(senderId, payload); + send(remoteMessage: RemoteMessage) { + if (!(remoteMessage instanceof RemoteMessage)) { + throw new Error('messaging().send requires an instance of RemoteMessage as the first argument.'); + } + + return FirebaseMessaging.send(remoteMessage.toJSON()); } } + +export const statics = { + EVENT_TYPE, + NOTIFICATION_TYPE, + REMOTE_NOTIFICATION_RESULT, + WILL_PRESENT_RESULT, + RemoteMessage, +};