diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 0000000..76a4349 --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 760a0a0..4b6e0e5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -44,7 +44,7 @@ allprojects { dependencies { compile 'com.facebook.react:react-native:0.20.+' - compile 'com.google.android.gms:play-services-base:9.8.0' + compile 'com.google.android.gms:play-services-base:+' compile 'com.google.firebase:firebase-core:9.8.0' compile 'com.google.firebase:firebase-config:9.8.0' compile 'com.google.firebase:firebase-auth:9.8.0' diff --git a/android/src/main/java/io/fullstack/firestack/FirestackStorage.java b/android/src/main/java/io/fullstack/firestack/FirestackStorage.java index b41301d..21ed217 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackStorage.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackStorage.java @@ -5,6 +5,9 @@ import android.content.Context; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.Map; import java.util.HashMap; @@ -26,6 +29,8 @@ import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; +import com.google.firebase.storage.StorageException; +import com.google.firebase.storage.StreamDownloadTask; import com.google.firebase.storage.UploadTask; import com.google.firebase.storage.FirebaseStorage; import com.google.firebase.storage.StorageMetadata; @@ -49,6 +54,18 @@ class FirestackStorageModule extends ReactContextBaseJavaModule { private static final String FileTypeRegular = "FILETYPE_REGULAR"; private static final String FileTypeDirectory = "FILETYPE_DIRECTORY"; + private static final String STORAGE_UPLOAD_PROGRESS = "upload_progress"; + private static final String STORAGE_UPLOAD_PAUSED = "upload_paused"; + private static final String STORAGE_UPLOAD_RESUMED = "upload_resumed"; + + private static final String STORAGE_DOWNLOAD_PROGRESS = "download_progress"; + private static final String STORAGE_DOWNLOAD_PAUSED = "download_paused"; + private static final String STORAGE_DOWNLOAD_RESUMED = "download_resumed"; + private static final String STORAGE_DOWNLOAD_SUCCESS = "download_success"; + private static final String STORAGE_DOWNLOAD_FAILURE = "download_failure"; + + private ReactContext mReactContext; + public FirestackStorageModule(ReactApplicationContext reactContext) { super(reactContext); @@ -60,6 +77,118 @@ public String getName() { return TAG; } + + public boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + return false; + } + + @ReactMethod + public void downloadFile(final String urlStr, + final String fbPath, + final String localFile, + final Callback callback) { + Log.d(TAG, "downloadFile: "+urlStr+", "+localFile); + if (!isExternalStorageWritable()) { + Log.w(TAG, "downloadFile failed: external storage not writable"); + WritableMap error = Arguments.createMap(); + final int errorCode = 1; + error.putDouble("code", errorCode); + error.putString("description", "downloadFile failed: external storage not writable"); + callback.invoke(error); + return; + } + FirebaseStorage storage = FirebaseStorage.getInstance(); + String storageBucket = storage.getApp().getOptions().getStorageBucket(); + String storageUrl = "gs://" + storageBucket; + Log.d(TAG, "Storage url " + storageUrl + fbPath); + + StorageReference storageRef = storage.getReferenceFromUrl(storageUrl); + StorageReference fileRef = storageRef.child(fbPath); + + fileRef.getStream(new StreamDownloadTask.StreamProcessor() { + @Override + public void doInBackground(StreamDownloadTask.TaskSnapshot taskSnapshot, InputStream inputStream) throws IOException { + int indexOfLastSlash = localFile.lastIndexOf("/"); + String pathMinusFileName = localFile.substring(0, indexOfLastSlash) + "/"; + String filename = localFile.substring(indexOfLastSlash+1); + File fileWithJustPath = new File(pathMinusFileName); + if (!fileWithJustPath.mkdirs()) { + Log.e(TAG, "Directory not created"); + WritableMap error = Arguments.createMap(); + error.putString("message", "Directory not created"); + callback.invoke(error); + return; + } + File fileWithFullPath = new File(pathMinusFileName, filename); + FileOutputStream output = new FileOutputStream(fileWithFullPath); + int bufferSize = 1024; + byte[] buffer = new byte[bufferSize]; + int len = 0; + while ((len = inputStream.read(buffer)) != -1) { + output.write(buffer, 0, len); + } + output.close(); + } + }).addOnProgressListener(new OnProgressListener() { + @Override + public void onProgress(StreamDownloadTask.TaskSnapshot taskSnapshot) { + WritableMap data = Arguments.createMap(); + data.putString("ref", taskSnapshot.getStorage().getBucket()); + double percentComplete = taskSnapshot.getTotalByteCount() == 0 ? 0.0f : 100.0f * (taskSnapshot.getBytesTransferred()) / (taskSnapshot.getTotalByteCount()); + data.putDouble("progress", percentComplete); + FirestackUtils.sendEvent(mReactContext, STORAGE_DOWNLOAD_PROGRESS, data); + } + }).addOnPausedListener(new OnPausedListener() { + @Override + public void onPaused(StreamDownloadTask.TaskSnapshot taskSnapshot) { + WritableMap data = Arguments.createMap(); + data.putString("ref", taskSnapshot.getStorage().getBucket()); + FirestackUtils.sendEvent(mReactContext, STORAGE_DOWNLOAD_PAUSED, data); + } + }).addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) { + final WritableMap data = Arguments.createMap(); + StorageReference ref = taskSnapshot.getStorage(); + data.putString("fullPath", ref.getPath()); + data.putString("bucket", ref.getBucket()); + data.putString("name", ref.getName()); + ref.getMetadata().addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(final StorageMetadata storageMetadata) { + data.putMap("metadata", getMetadataAsMap(storageMetadata)); + callback.invoke(null, data); + } + }) + .addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception exception) { + final int errorCode = 1; + WritableMap data = Arguments.createMap(); + StorageException storageException = StorageException.fromException(exception); + data.putString("description", storageException.getMessage()); + data.putInt("code", errorCode); + callback.invoke(makeErrorPayload(errorCode, exception)); + } + }); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception exception) { + final int errorCode = 1; + WritableMap data = Arguments.createMap(); + StorageException storageException = StorageException.fromException(exception); + data.putString("description", storageException.getMessage()); + data.putInt("code", errorCode); + callback.invoke(makeErrorPayload(errorCode, exception)); + } + }); + } + @ReactMethod public void downloadUrl(final String javascriptStorageBucket, final String path, @@ -90,16 +219,7 @@ public void onSuccess(Uri uri) { public void onSuccess(final StorageMetadata storageMetadata) { Log.d(TAG, "getMetadata success " + storageMetadata); - WritableMap metadata = Arguments.createMap(); - metadata.putString("getBucket", storageMetadata.getBucket()); - metadata.putString("getName", storageMetadata.getName()); - metadata.putDouble("sizeBytes", storageMetadata.getSizeBytes()); - metadata.putDouble("created_at", storageMetadata.getCreationTimeMillis()); - metadata.putDouble("updated_at", storageMetadata.getUpdatedTimeMillis()); - metadata.putString("md5hash", storageMetadata.getMd5Hash()); - metadata.putString("encoding", storageMetadata.getContentEncoding()); - - res.putMap("metadata", metadata); + res.putMap("metadata", getMetadataAsMap(storageMetadata)); res.putString("name", storageMetadata.getName()); res.putString("url", storageMetadata.getDownloadUrl().toString()); callback.invoke(null, res); @@ -109,7 +229,8 @@ public void onSuccess(final StorageMetadata storageMetadata) { @Override public void onFailure(@NonNull Exception exception) { Log.e(TAG, "Failure in download " + exception); - callback.invoke(makeErrorPayload(1, exception)); + final int errorCode = 1; + callback.invoke(makeErrorPayload(errorCode, exception)); } }); @@ -129,6 +250,18 @@ public void onFailure(@NonNull Exception exception) { }); } + private WritableMap getMetadataAsMap(StorageMetadata storageMetadata) { + WritableMap metadata = Arguments.createMap(); + metadata.putString("getBucket", storageMetadata.getBucket()); + metadata.putString("getName", storageMetadata.getName()); + metadata.putDouble("sizeBytes", storageMetadata.getSizeBytes()); + metadata.putDouble("created_at", storageMetadata.getCreationTimeMillis()); + metadata.putDouble("updated_at", storageMetadata.getUpdatedTimeMillis()); + metadata.putString("md5hash", storageMetadata.getMd5Hash()); + metadata.putString("encoding", storageMetadata.getContentEncoding()); + return metadata; + } + // STORAGE @ReactMethod public void uploadFile(final String urlStr, final String name, final String filepath, final ReadableMap metadata, final Callback callback) { @@ -191,9 +324,9 @@ public void onProgress(UploadTask.TaskSnapshot taskSnapshot) { if (progress >= 0) { WritableMap data = Arguments.createMap(); - data.putString("eventName", "upload_progress"); + data.putString("eventName", STORAGE_UPLOAD_PROGRESS); data.putDouble("progress", progress); - FirestackUtils.sendEvent(getReactApplicationContext(), "upload_progress", data); + FirestackUtils.sendEvent(getReactApplicationContext(), STORAGE_UPLOAD_PROGRESS, data); } } }) @@ -204,13 +337,14 @@ public void onPaused(UploadTask.TaskSnapshot taskSnapshot) { StorageMetadata d = taskSnapshot.getMetadata(); String bucket = d.getBucket(); WritableMap data = Arguments.createMap(); - data.putString("eventName", "upload_paused"); + data.putString("eventName", STORAGE_UPLOAD_PAUSED); data.putString("ref", bucket); - FirestackUtils.sendEvent(getReactApplicationContext(), "upload_paused", data); + FirestackUtils.sendEvent(getReactApplicationContext(), STORAGE_UPLOAD_PAUSED, data); } }); } catch (Exception ex) { - callback.invoke(makeErrorPayload(2, ex)); + final int errorCode = 2; + callback.invoke(makeErrorPayload(errorCode, ex)); } } @@ -221,7 +355,8 @@ public void getRealPathFromURI(final String uri, final Callback callback) { callback.invoke(null, path); } catch (Exception ex) { ex.printStackTrace(); - callback.invoke(makeErrorPayload(1, ex)); + final int errorCode = 1; + callback.invoke(makeErrorPayload(errorCode, ex)); } } diff --git a/docs/api/cloud-messaging.md b/docs/api/cloud-messaging.md index 8b13789..f53b217 100644 --- a/docs/api/cloud-messaging.md +++ b/docs/api/cloud-messaging.md @@ -1 +1,57 @@ +#cloud messaging +Make this prettier at some point but can't forget these things +setup certificates, enable push settings in app +Add things to app delegate +appdelegate.h -> +@import UserNotifications; +@interface AppDelegate : UIResponder + +appdelegate.m +Appdidfinishwithlaunching blah blah + UILocalNotification *localNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; + NSDictionary *userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; + + if (localNotification) { + [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_MESSAGE_RECEIVED_LOCAL object:localNotification]; + NSLog(@"fresh launch from local notificaiton"); + } + + if(userInfo){ + [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_MESSAGE_RECEIVED_REMOTE object:self userInfo:userInfo]; + NSLog(@"fresh launch from remote"); + } + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { + // If you are receiving a notification message while your app is in the background, + // this callback will not be fired till the user taps on the notification launching the application. + // TODO: Handle data of notification + + // Print full message. + NSLog(@"%@", userInfo); + + [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_MESSAGE_RECEIVED_REMOTE object:self userInfo:userInfo]; + +} + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{ + NSLog(@"%@", userInfo); + if ( application.applicationState == UIApplicationStateActive ){ + //user had the app in the foreground + } + else { + //app went from background to foreground + } + [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_MESSAGE_RECEIVED_REMOTE object:self userInfo:userInfo]; + completionHandler(UIBackgroundFetchResultNoData); +} + +- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification +{ + NSLog(@"%@", notification); + [[NSNotificationCenter defaultCenter] postNotificationName:MESSAGING_MESSAGE_RECEIVED_LOCAL object:notification]; +} + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{ + NSLog(@"Notification Registration Error %@", [error description]); +} diff --git a/ios/Firestack/Firestack.m b/ios/Firestack/Firestack.m index 284f38b..7073fe8 100644 --- a/ios/Firestack/Firestack.m +++ b/ios/Firestack/Firestack.m @@ -8,7 +8,7 @@ #import "FirestackErrors.h" #import "FirestackEvents.h" // #import "FirestackAnalytics.h" -// #import "FirestackCloudMessaging.h" +#import "FirestackCloudMessaging.h" static Firestack *_sharedInstance = nil; static dispatch_once_t onceToken; @@ -239,6 +239,8 @@ - (FIRApp *) firebaseApp // if (!self.configured) { + //If you want this method could replace all of the above for the option setting for firebase + //FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:plistPath]; if ([FIRApp defaultApp] == NULL) { [FIRApp configureWithOptions:finalOptions]; } diff --git a/ios/Firestack/FirestackAuth.m b/ios/Firestack/FirestackAuth.m index a7ac803..939933b 100644 --- a/ios/Firestack/FirestackAuth.m +++ b/ios/Firestack/FirestackAuth.m @@ -40,7 +40,7 @@ @implementation FirestackAuth } @catch(NSException *ex) { NSDictionary *eventError = @{ @"eventName": AUTH_ANONYMOUS_ERROR_EVENT, - @"msg": ex.reason + @"errorMessage": ex.reason }; [self sendJSEvent:AUTH_ERROR_EVENT @@ -137,14 +137,15 @@ @implementation FirestackAuth sendJSEvent:AUTH_CHANGED_EVENT props: @{ @"eventName": @"userTokenError", - @"msg": [error localizedFailureReason] + @"authenticated": @((BOOL)true), + @"errorMessage": [error localizedFailureReason] }]; } else { [self sendJSEvent:AUTH_CHANGED_EVENT props: @{ @"eventName": @"user", - @"authenticated": @(true), + @"authenticated": @((BOOL)true), @"user": userProps }]; } @@ -157,7 +158,7 @@ @implementation FirestackAuth [self sendJSEvent:AUTH_CHANGED_EVENT props:@{ @"eventName": @"no_user", - @"authenticated": @(false), + @"authenticated": @((BOOL)false), @"error": err }]; } diff --git a/ios/Firestack/FirestackCloudMessaging.h b/ios/Firestack/FirestackCloudMessaging.h index 3e7c98b..52cbb7e 100644 --- a/ios/Firestack/FirestackCloudMessaging.h +++ b/ios/Firestack/FirestackCloudMessaging.h @@ -14,6 +14,7 @@ #import "RCTBridgeModule.h" #import "RCTUtils.h" + @interface FirestackCloudMessaging : RCTEventEmitter { } @@ -22,4 +23,4 @@ @end -#endif \ No newline at end of file +#endif diff --git a/ios/Firestack/FirestackCloudMessaging.m b/ios/Firestack/FirestackCloudMessaging.m index 9cfa6b3..3d9f97a 100644 --- a/ios/Firestack/FirestackCloudMessaging.m +++ b/ios/Firestack/FirestackCloudMessaging.m @@ -17,23 +17,22 @@ // https://github.com/facebook/react-native/blob/master/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @implementation RCTConvert (UILocalNotification) - + (UILocalNotification *)UILocalNotification:(id)json { - NSDictionary *details = [self NSDictionary:json]; - UILocalNotification *notification = [UILocalNotification new]; - notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date]; - notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; - notification.alertAction = [RCTConvert NSString:details[@"alertAction"]]; - notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; - notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]]; - notification.category = [RCTConvert NSString:details[@"category"]]; - if (details[@"applicationIconBadgeNumber"]) { - notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"applicationIconBadgeNumber"]]; - } - return notification; + NSDictionary *details = [self NSDictionary:json]; + + UILocalNotification* notification = [UILocalNotification new]; + notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]]; + notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; + notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]]; + notification.alertAction = [RCTConvert NSString:details[@"alertAction"]]; + notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; + notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{}; + notification.category = [RCTConvert NSString:details[@"category"]]; + + + return notification; } - @end @implementation FirestackCloudMessaging @@ -41,141 +40,163 @@ @implementation FirestackCloudMessaging // https://github.com/facebook/react-native/blob/master/Libraries/PushNotificationIOS/RCTPushNotificationManager.m static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) { - NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary]; - if (notification.fireDate) { - NSDateFormatter *formatter = [NSDateFormatter new]; - [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; - NSString *fireDateString = [formatter stringFromDate:notification.fireDate]; - formattedLocalNotification[@"fireDate"] = fireDateString; - } - formattedLocalNotification[@"alertAction"] = RCTNullIfNil(notification.alertAction); - formattedLocalNotification[@"alertBody"] = RCTNullIfNil(notification.alertBody); - formattedLocalNotification[@"applicationIconBadgeNumber"] = @(notification.applicationIconBadgeNumber); - formattedLocalNotification[@"category"] = RCTNullIfNil(notification.category); - formattedLocalNotification[@"soundName"] = RCTNullIfNil(notification.soundName); - formattedLocalNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo)); - formattedLocalNotification[@"remote"] = @NO; - return formattedLocalNotification; + NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary]; + if (notification.fireDate) { + NSDateFormatter *formatter = [NSDateFormatter new]; + [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; + NSString *fireDateString = [formatter stringFromDate:notification.fireDate]; + formattedLocalNotification[@"fireDate"] = fireDateString; + } + formattedLocalNotification[@"alertAction"] = RCTNullIfNil(notification.alertAction); + formattedLocalNotification[@"alertBody"] = RCTNullIfNil(notification.alertBody); + formattedLocalNotification[@"applicationIconBadgeNumber"] = @(notification.applicationIconBadgeNumber); + formattedLocalNotification[@"category"] = RCTNullIfNil(notification.category); + formattedLocalNotification[@"soundName"] = RCTNullIfNil(notification.soundName); + formattedLocalNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo)); + formattedLocalNotification[@"remote"] = @NO; + return formattedLocalNotification; } + - (void) dealloc { - [[NSNotificationCenter defaultCenter] removeObserver: self]; + [[NSNotificationCenter defaultCenter] removeObserver: self]; } -+ (void) setup:(UIApplication *) application +@synthesize bridge = _bridge; + +-(void) setBridge:(RCTBridge *)bridge { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(connectToFirebase) - name: UIApplicationDidEnterBackgroundNotification - object: nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(disconnectFromFirebase) - name: UIApplicationDidBecomeActiveNotification - object: nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleRemoteNotificationReceived:) - name:MESSAGING_MESSAGE_RECEIVED_REMOTE - object: nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleLocalNotificationReceived:) - name:MESSAGING_MESSAGE_RECEIVED_LOCAL - object: nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleTokenRefresh) - name:kFIRInstanceIDTokenRefreshNotification - object: nil]; - - if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"9.0")) { - UIUserNotificationType allNotificationTypes = - (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); - UIUserNotificationSettings *settings = - [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; -} else { - // iOS 10 or later - #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - UNAuthorizationOptions authOptions = - UNAuthorizationOptionAlert - | UNAuthorizationOptionSound - | UNAuthorizationOptionBadge; - [[UNUserNotificationCenter currentNotificationCenter] - requestAuthorizationWithOptions:authOptions - completionHandler:^(BOOL granted, NSError * _Nullable error) { - } - ]; - - // For iOS 10 display notification (sent via APNS) - [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; - // For iOS 10 data message (sent via FCM) - [[FIRMessaging messaging] setRemoteMessageDelegate:self]; - #endif + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(connectToFirebase) + name: UIApplicationDidBecomeActiveNotification + object: nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(disconnectFromFirebase) + name: UIApplicationDidEnterBackgroundNotification + object: nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleLocalNotificationReceived:) + name:MESSAGING_MESSAGE_RECEIVED_REMOTE + object: nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleLocalNotificationReceived:) + name:MESSAGING_MESSAGE_RECEIVED_LOCAL + object: nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleTokenRefresh) + name:kFIRInstanceIDTokenRefreshNotification + object: nil]; + + [[NSNotificationCenter defaultCenter]addObserver:self + selector:@selector(sendDataMessageFailure:) + name:FIRMessagingSendErrorNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(sendDataMessageSuccess:) + name:FIRMessagingSendSuccessNotification + object:nil]; } -[[UIApplication sharedApplication] registerForRemoteNotifications]; + +#pragma mark Request permissions +RCT_EXPORT_METHOD(requestPermissions:(RCTResponseSenderBlock) callback) +{ + if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"9.0")) { + UIUserNotificationType allNotificationTypes = + (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + } else { + // iOS 10 or later +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNAuthorizationOptions authOptions = + UNAuthorizationOptionAlert + | UNAuthorizationOptionSound + | UNAuthorizationOptionBadge; + [[UNUserNotificationCenter currentNotificationCenter] + requestAuthorizationWithOptions:authOptions + completionHandler:^(BOOL granted, NSError * _Nullable error) { + } + ]; + + // For iOS 10 display notification (sent via APNS) + [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; + // For iOS 10 data message (sent via FCM) + //This is hacky + dispatch_async(dispatch_get_main_queue(), ^{ + [[FIRMessaging messaging] setRemoteMessageDelegate:self]; + }); +#endif + } + + [[UIApplication sharedApplication] registerForRemoteNotifications]; } #pragma mark callbacks - (void) connectToFirebase { - [[FIRMessaging messaging] connectWithCompletion:^(NSError *error) { - NSDictionary *evt; - NSString *evtName; - if (error != nil) { - NSLog(@"Error connecting: %@", [error debugDescription]); - evtName = MESSAGING_SUBSYSTEM_ERROR; - evt = @{ - @"eventName": MESSAGING_SUBSYSTEM_ERROR, - @"msg": [error debugDescription] - }; - } else { - NSLog(@"Connected to Firebase messaging"); - evtName = MESSAGING_SUBSYSTEM_EVENT; - evt = @{ - @"result": @"connected" - }; - [self - sendJSEvent:evtName - props: evt]; - - } - }]; + [[FIRMessaging messaging] connectWithCompletion:^(NSError *error) { + NSDictionary *evt; + NSString *evtName; + if (error != nil) { + NSLog(@"Error connecting: %@", [error debugDescription]); + evtName = MESSAGING_SUBSYSTEM_ERROR; + evt = @{ + @"eventName": MESSAGING_SUBSYSTEM_ERROR, + @"msg": [error debugDescription] + }; + } else { + NSLog(@"Connected to Firebase messaging"); + evtName = MESSAGING_SUBSYSTEM_EVENT; + evt = @{ + @"result": @"connected" + }; + [self + sendJSEvent:evtName + props: evt]; + + } + }]; } - (void) disconnectFromFirebase { - [[FIRMessaging messaging] disconnect]; - NSLog(@"Disconnect from Firebase"); - [self + [[FIRMessaging messaging] disconnect]; + NSLog(@"Disconnect from Firebase"); + [self sendJSEvent:MESSAGING_SUBSYSTEM_EVENT props: @{ - @"status": @"disconnected" - }]; + @"status": @"disconnected" + }]; } - (void) handleRemoteNotificationReceived:(NSNotification *) n { - NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithDictionary: n.userInfo]; - [self sendJSEvent:MESSAGING_MESSAGE_RECEIVED_REMOTE props: props]; + NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithDictionary: n.userInfo]; + [self sendJSEvent:MESSAGING_MESSAGE_RECEIVED_REMOTE props: n]; } - (void) handleLocalNotificationReceived:(NSNotification *) n { - NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithDictionary: n.userInfo]; - [self sendJSEvent:MESSAGING_MESSAGE_RECEIVED_LOCAL props: props]; + NSMutableDictionary *props = [[NSMutableDictionary alloc] initWithDictionary: n.userInfo]; + [self sendJSEvent:MESSAGING_MESSAGE_RECEIVED_LOCAL props: props]; } + - (void) handleTokenRefresh { - NSDictionary *props = @{ - @"status": @"token_refreshed", - @"token": [[FIRInstanceID instanceID] token] - }; - [self sendJSEvent:MESSAGING_TOKEN_REFRESH props: props]; + NSDictionary *props = @{ + @"status": @"token_refreshed", + @"token": [[FIRInstanceID instanceID] token] + }; + [self sendJSEvent:MESSAGING_TOKEN_REFRESH props: props]; } RCT_EXPORT_MODULE(FirestackCloudMessaging); @@ -183,67 +204,120 @@ - (void) handleTokenRefresh RCT_EXPORT_METHOD(getToken:(RCTResponseSenderBlock)callback) { - NSString *token = [[FIRInstanceID instanceID] token]; - callback(@[[NSNull null], @{ - @"status": @"success", - @"token": token - }]); + NSString *token = [[FIRInstanceID instanceID] token]; + callback(@[[NSNull null], @{ + @"status": @"success", + @"token": token + }]); } +RCT_EXPORT_METHOD(getInitialNotification:(RCTResponseSenderBlock)callback){ + NSDictionary *localUserInfo = _bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; + if(localUserInfo){ + callback(@[[NSNull null], @{ + @"status": @"success", + @"launchedFrom": localUserInfo + }]); + } +} +//WORK ON THESE SOME MORE THIS IS UGLY -RCT_EXPORT_METHOD(sendLocal:(UILocalNotification *)notification - callback:(RCTResponseSenderBlock) callback) +RCT_EXPORT_METHOD(sendLocal:(NSDictionary *) notification + callback:(RCTResponseSenderBlock) callback) { - NSLog(@"sendLocal called with notification: %@", notification); - [RCTSharedApplication() presentLocalNotificationNow:notification]; + UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification]; + NSLog(@"sendLocal called with notification: %@", notification); + [RCTSharedApplication() presentLocalNotificationNow:localNotification]; + } -RCT_EXPORT_METHOD(scheduleLocal:(UILocalNotification *)notification - callback:(RCTResponseSenderBlock) callback) +RCT_EXPORT_METHOD(scheduleLocal:(NSDictionary *) notification + callback:(RCTResponseSenderBlock) callback) { - [RCTSharedApplication() scheduleLocalNotification:notification]; + UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification]; + NSLog(@"scheduleLocal called with notification: %@", notification); + [RCTSharedApplication() scheduleLocalNotification:localNotification]; } RCT_EXPORT_METHOD(cancelAllLocalNotifications) { - [RCTSharedApplication() cancelAllLocalNotifications]; + [RCTSharedApplication() cancelAllLocalNotifications]; } RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary *)userInfo) { - for (UILocalNotification *notification in [UIApplication sharedApplication].scheduledLocalNotifications) { - __block BOOL matchesAll = YES; - NSDictionary *notificationInfo = notification.userInfo; - // Note: we do this with a loop instead of just `isEqualToDictionary:` - // because we only require that all specified userInfo values match the - // notificationInfo values - notificationInfo may contain additional values - // which we don't care about. - [userInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { - if (![notificationInfo[key] isEqual:obj]) { - matchesAll = NO; - *stop = YES; - } - }]; - if (matchesAll) { - [[UIApplication sharedApplication] cancelLocalNotification:notification]; + for (UILocalNotification *notification in [UIApplication sharedApplication].scheduledLocalNotifications) { + __block BOOL matchesAll = YES; + NSDictionary *notificationInfo = notification.userInfo; + // Note: we do this with a loop instead of just `isEqualToDictionary:` + // because we only require that all specified userInfo values match the + // notificationInfo values - notificationInfo may contain additional values + // which we don't care about. + [userInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + if (![notificationInfo[key] isEqual:obj]) { + matchesAll = NO; + *stop = YES; + } + }]; + if (matchesAll) { + [[UIApplication sharedApplication] cancelLocalNotification:notification]; + } } - } } -RCT_EXPORT_METHOD(sendRemote:(UILocalNotification *)notification - callback:(RCTResponseSenderBlock) callback) +//RCT_EXPORT_METHOD(sendRemote:(UILocalNotification *)notification +// callback:(RCTResponseSenderBlock) callback) +//{ +// +//} + + +//for upstream it makes sense, we could probably use this as a general function for all local,upstream etc. Lets revisit +RCT_EXPORT_METHOD(send:(NSString *) messageId + msg: (NSDictionary *) msg + ttl: (int) ttl + callback:(RCTResponseSenderBlock)callback) { - + + int64_t ttl64 = (int64_t) ttl; + //This is hacky + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* senderId = [[[FIRApp defaultApp] options] GCMSenderID]; + + senderId = [NSString stringWithFormat:@"%@@gcm.googleapis.com", senderId]; + [[FIRMessaging messaging] sendMessage:msg + to:senderId + withMessageID:messageId + timeToLive:ttl]; + }); } - -RCT_EXPORT_METHOD(send:(NSString *) senderId - messageId:(NSString *) messageId - messageType:(NSString *) messageType - msg: (NSString *) msg - callback:(RCTResponseSenderBlock)callback) +RCT_EXPORT_METHOD(sendMessage:(NSDictionary *) details + type: (NSString *) type + callback:(RCTResponseSenderBlock)callback) { + +// int64_t ttl64 = (int64_t) ttl; +// //This is hacky +// dispatch_async(dispatch_get_main_queue(), ^{ +// NSString* senderId = [[[FIRApp defaultApp] options] GCMSenderID]; +// +// senderId = [NSString stringWithFormat:@"%@@gcm.googleapis.com", senderId]; +// [[FIRMessaging messaging] sendMessage:msg +// to:senderId +// withMessageID:messageId +// timeToLive:ttl]; +// }); +} +//send upstream always returns sendDataMessageFailure - pretty sure have to setup the server side component of this to actually see it work +- (void)sendDataMessageFailure:(NSNotification *)notification { + NSString *messageID = (NSString *)notification.userInfo[@"messageId"]; + NSDictionary *userInfo = notification.userInfo; // contains error info etc + +} +- (void)sendDataMessageSuccess:(NSNotification *)notification { + NSString *messageID = (NSString *)notification.userInfo[@"messageId"]; } RCT_EXPORT_METHOD(listenForTokenRefresh:(RCTResponseSenderBlock)callback) @@ -253,41 +327,41 @@ - (void) handleTokenRefresh {} RCT_EXPORT_METHOD(subscribeToTopic:(NSString *) topic - callback:(RCTResponseSenderBlock)callback) + callback:(RCTResponseSenderBlock)callback) { - [[FIRMessaging messaging] subscribeToTopic:topic]; - callback(@[[NSNull null], @{ - @"result": @"success", - @"topic": topic - }]); + [[FIRMessaging messaging] subscribeToTopic:topic]; + callback(@[[NSNull null], @{ + @"result": @"success", + @"topic": topic + }]); } RCT_EXPORT_METHOD(unsubscribeFromTopic:(NSString *) topic - callback: (RCTResponseSenderBlock)callback) + callback: (RCTResponseSenderBlock)callback) { - [[FIRMessaging messaging] unsubscribeFromTopic:topic]; - callback(@[[NSNull null], @{ - @"result": @"success", - @"topic": topic - }]); + [[FIRMessaging messaging] unsubscribeFromTopic:topic]; + callback(@[[NSNull null], @{ + @"result": @"success", + @"topic": topic + }]); } RCT_EXPORT_METHOD(setBadge:(NSInteger) number - callback:(RCTResponseSenderBlock) callback) + callback:(RCTResponseSenderBlock) callback) { - RCTSharedApplication().applicationIconBadgeNumber = number; - callback(@[[NSNull null], @{ - @"result": @"success", - @"number": @(number) - }]); + RCTSharedApplication().applicationIconBadgeNumber = number; + callback(@[[NSNull null], @{ + @"result": @"success", + @"number": @(number) + }]); } RCT_EXPORT_METHOD(getBadge:(RCTResponseSenderBlock) callback) { - callback(@[[NSNull null], @{ - @"result": @"success", - @"number": @(RCTSharedApplication().applicationIconBadgeNumber) - }]); + callback(@[[NSNull null], @{ + @"result": @"success", + @"number": @(RCTSharedApplication().applicationIconBadgeNumber) + }]); } RCT_EXPORT_METHOD(listenForReceiveNotification:(RCTResponseSenderBlock)callback) @@ -305,11 +379,11 @@ - (void) handleTokenRefresh // Not sure how to get away from this... yet - (NSArray *)supportedEvents { return @[ - MESSAGING_SUBSYSTEM_EVENT, - MESSAGING_SUBSYSTEM_ERROR, - MESSAGING_TOKEN_REFRESH, - MESSAGING_MESSAGE_RECEIVED_LOCAL, - MESSAGING_MESSAGE_RECEIVED_REMOTE]; + MESSAGING_SUBSYSTEM_EVENT, + MESSAGING_SUBSYSTEM_ERROR, + MESSAGING_TOKEN_REFRESH, + MESSAGING_MESSAGE_RECEIVED_LOCAL, + MESSAGING_MESSAGE_RECEIVED_REMOTE]; } - (void) sendJSEvent:(NSString *)title @@ -318,9 +392,9 @@ - (void) sendJSEvent:(NSString *)title @try { [self sendEventWithName:title body:@{ - @"eventName": title, - @"body": props - }]; + @"eventName": title, + @"body": props + }]; } @catch (NSException *err) { NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]); diff --git a/ios/Firestack/FirestackStorage.m b/ios/Firestack/FirestackStorage.m index d88a19a..2da5377 100644 --- a/ios/Firestack/FirestackStorage.m +++ b/ios/Firestack/FirestackStorage.m @@ -25,7 +25,12 @@ - (dispatch_queue_t)methodQueue path:(NSString *) path callback:(RCTResponseSenderBlock) callback) { - FIRStorageReference *storageRef = [[FIRStorage storage] referenceForURL:storageUrl]; + FIRStorageReference *storageRef; + if (storageUrl == nil ) { + storageRef = [[FIRStorage storage] reference]; + } else { + storageRef = [[FIRStorage storage] referenceForURL:storageUrl]; + } FIRStorageReference *fileRef = [storageRef child:path]; [fileRef downloadURLWithCompletion:^(NSURL * _Nullable URL, NSError * _Nullable error) { if (error != nil) { @@ -52,14 +57,13 @@ - (dispatch_queue_t)methodQueue metadata:(NSDictionary *)metadata callback:(RCTResponseSenderBlock) callback) { + FIRStorageReference *storageRef; if (urlStr == nil) { - NSError *err = [[NSError alloc] init]; - [err setValue:@"Storage configuration error" forKey:@"name"]; - [err setValue:@"Call setStorageUrl() first" forKey:@"description"]; - return callback(@[err]); + storageRef = [[FIRStorage storage] reference]; + } else { + storageRef = [[FIRStorage storage] referenceForURL:urlStr]; } - FIRStorageReference *storageRef = [[FIRStorage storage] referenceForURL:urlStr]; FIRStorageReference *uploadRef = [storageRef child:name]; FIRStorageMetadata *firmetadata = [[FIRStorageMetadata alloc] initWithDictionary:metadata]; @@ -157,6 +161,7 @@ - (void) addUploadObservers:(FIRStorageUploadTask *) uploadTask case FIRStorageErrorCodeUnknown: // Unknown error occurred, inspect the server response [errProps setValue:@"Unknown error" forKey:@"description"]; + NSLog(@"Unknown error: %@", snapshot.error); break; } diff --git a/lib/modules/cloudmessaging.js b/lib/modules/cloudmessaging.js new file mode 100644 index 0000000..73b390b --- /dev/null +++ b/lib/modules/cloudmessaging.js @@ -0,0 +1,108 @@ +import {Platform, NativeModules, NativeEventEmitter} from 'react-native'; +const FirestackCloudMessaging = NativeModules.FirestackCloudMessaging; +const FirestackCloudMessagingEvt = new NativeEventEmitter(FirestackCloudMessaging); + +import promisify from '../utils/promisify' +import { Base, ReferenceBase } from './base' + +const defaultPermissions = { + 'badge': 1, + 'sound': 2, + 'alert': 3 +} +export class CloudMessaging extends Base { + constructor(firestack, options = {}) { + super(firestack, options); + + this.requestedPermissions = Object.assign({}, defaultPermissions, options.permissions); + } + get namespace() { + return 'firestack:cloudMessaging' + } + getToken() { + this.log.info('getToken for cloudMessaging'); + return promisify('getToken', FirestackCloudMessaging)(); + } + + // Request FCM permissions + requestPermissions(requestedPermissions = {}) { + if (Platform.OS === 'ios') { + const mergedRequestedPermissions = Object.assign({}, + this.requestedPermissions, + requestedPermissions); + return promisify('requestPermissions', FirestackCloudMessaging)(mergedRequestedPermissions) + .then(perms => { + + return perms; + }); + } + } + + sendMessage(details:Object = {}, type:string='local') { + const methodName = `send${type == 'local' ? 'Local' : 'Remote'}` + this.log.info('sendMessage', methodName, details); + return promisify(methodName, FirestackCloudMessaging)(details); + } + scheduleMessage(details:Object = {}, type:string='local') { + const methodName = `schedule${type == 'local' ? 'Local' : 'Remote'}` + return promisify(methodName, FirestackCloudMessaging)(details); + } + // OLD + send(senderId, messageId, messageType, msg){ + return promisify('send', FirestackCloudMessaging)(senderId, messageId, messageType, msg); + } + // + listenForTokenRefresh(callback) { + this.log.info('Setting up listenForTokenRefresh callback'); + const sub = this._on('FirestackRefreshToken', callback, FirestackCloudMessagingEvt); + return promisify(() => sub, FirestackCloudMessaging)(sub); + } + unlistenForTokenRefresh() { + this.log.info('Unlistening for TokenRefresh'); + this._off('FirestackRefreshToken'); + } + subscribeToTopic(topic) { + this.log.info('subscribeToTopic ' + topic); + const finalTopic = `/topics/${topic}` + return promisify('subscribeToTopic', FirestackCloudMessaging)(finalTopic); + } + unsubscribeFromTopic(topic) { + this.log.info('unsubscribeFromTopic ' + topic); + const finalTopic = `/topics/${topic}` + return promisify('unsubscribeFromTopic', FirestackCloudMessaging)(finalTopic); + } + // New api + onRemoteMessage(callback) { + this.log.info('On remote message callback'); + const sub = this._on('messaging_remote_event_received', callback, FirestackCloudMessagingEvt); + return promisify(() => sub, FirestackCloudMessaging)(sub); + } + + onLocalMessage(callback) { + this.log.info('on local callback'); + const sub = this._on('messaging_local_event_received', callback, FirestackCloudMessagingEvt); + return promisify(() => sub, FirestackCloudMessaging)(sub); + } + + // Original API + listenForReceiveNotification(callback) { + this.log.info('Setting up listenForReceiveNotification callback'); + const sub = this._on('FirestackReceiveNotification', callback, FirestackCloudMessagingEvt); + return promisify(() => sub, FirestackCloudMessaging)(sub); + } + unlistenForReceiveNotification() { + this.log.info('Unlistening for ReceiveNotification'); + this._off('FirestackRefreshToken'); + } + listenForReceiveUpstreamSend(callback) { + this.log.info('Setting up send callback'); + const sub = this._on('FirestackUpstreamSend', callback, FirestackCloudMessagingEvt); + return promisify(() => sub, FirestackCloudMessaging)(sub); + } + unlistenForReceiveUpstreamSend() { + this.log.info('Unlistening for send'); + this._off('FirestackUpstreamSend'); + } +} + +export default CloudMessaging \ No newline at end of file diff --git a/lib/modules/messaging.js b/lib/modules/messaging.js index 877cf29..696e5e4 100644 --- a/lib/modules/messaging.js +++ b/lib/modules/messaging.js @@ -5,14 +5,42 @@ import promisify from '../utils/promisify'; const FirestackCloudMessaging = NativeModules.FirestackCloudMessaging; const FirestackCloudMessagingEvt = new NativeEventEmitter(FirestackCloudMessaging); +const defaultPermissions = { + 'badge': 1, + 'sound': 2, + 'alert': 3 +} + /** * @class Messaging */ export default class Messaging extends Base { constructor(firestack, options = {}) { super(firestack, options); - } - + //this.requestedPermissions = Object.assign({}, defaultPermissions, options.permissions); + + } + + // Request FCM permissions + // requestPermissions(requestedPermissions = {}) { + // // if (Platform.OS === 'ios') { + // const mergedRequestedPermissions = Object.assign({}, + // this.requestedPermissions, + // requestedPermissions); + // return promisify('requestPermissions', FirestackCloudMessaging)(mergedRequestedPermissions) + // .then(perms => { + + // return perms; + // }); + // // } + // } + + // Request FCM permissions + requestPermissions() { + this.log.info('requesting permissions'); + return promisify('requestPermissions', FirestackCloudMessaging)(); + // } + } /* * WEB API */ @@ -48,8 +76,14 @@ export default class Messaging extends Base { return promisify('getToken', FirestackCloudMessaging)(); } + getInitialNotification(){ + this.log.info('user launched app from notification'); + return promisify('getInitialNotification',FirestackCloudMessaging)(); + } + sendMessage(details: Object = {}, type: string = 'local') { const methodName = `send${type == 'local' ? 'Local' : 'Remote'}`; + this.log.info(methodName); this.log.info('sendMessage', methodName, details); return promisify(methodName, FirestackCloudMessaging)(details); } @@ -60,8 +94,8 @@ export default class Messaging extends Base { } // OLD - send(senderId, messageId, messageType, msg) { - return promisify('send', FirestackCloudMessaging)(senderId, messageId, messageType, msg); + send(messageId, msg,ttl) { + return promisify('send', FirestackCloudMessaging)(messageId, msg,ttl); } // @@ -111,7 +145,7 @@ export default class Messaging extends Base { console.warn('Firestack: listenForReceiveNotification is now deprecated, please use onMessage'); return this.onMessage(...args); } - + /** * @deprecated * @param args diff --git a/lib/utils/window-or-global.js b/lib/utils/window-or-global.js index 3228c06..7b64020 100644 --- a/lib/utils/window-or-global.js +++ b/lib/utils/window-or-global.js @@ -2,4 +2,4 @@ // https://github.com/purposeindustries/window-or-global module.exports = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || - this \ No newline at end of file + {} \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy