From ed409a2c7a04a8cfc832bf05ffb3b4912ce53dcd Mon Sep 17 00:00:00 2001 From: kphayen Date: Mon, 7 Nov 2016 17:52:49 -0700 Subject: [PATCH 01/23] Implement reauthenticateWithCredentialForProvider for Android --- .../io/fullstack/firestack/FirestackAuth.java | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index 7c0c2bb..d171c90 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java @@ -187,10 +187,41 @@ public void onComplete(@NonNull Task task) { @ReactMethod public void reauthenticateWithCredentialForProvider(final String provider, final String authToken, final String authSecret, final Callback callback) { - // TODO: - FirestackUtils.todoNote(TAG, "reauthenticateWithCredentialForProvider", callback); - // AuthCredential credential; - // Log.d(TAG, "reauthenticateWithCredentialForProvider called with: " + provider); + AuthCredential credential; + + if (provider.equals("facebook")) { + credential = FacebookAuthProvider.getCredential(authToken); + } else if (provider.equals("google")) { + credential = GoogleAuthProvider.getCredential(authToken, null); + } else { + // TODO: + FirestackUtils.todoNote(TAG, "reauthenticateWithCredentialForProvider", callback); + // AuthCredential credential; + // Log.d(TAG, "reauthenticateWithCredentialForProvider called with: " + provider); + return; + } + + FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); + if (user != null) { + user.reauthenticate(credential) + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful()) { + Log.d(TAG, "User re-authenticated with " + provider); + FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); + userCallback(u, callback); + } else { + userErrorCallback(task, callback); + } + } + }); + } else { + WritableMap err = Arguments.createMap(); + err.putInt("errorCode", NO_CURRENT_USER); + err.putString("errorMessage", "No current user"); + callback.invoke(err); + } } @ReactMethod From 972b16d27d83791db5ff66f043bab883d9270762 Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Tue, 8 Nov 2016 19:24:04 +0530 Subject: [PATCH 02/23] Catch exception thrown by setPersistenceEnabled which was causing the rn app to fail --- android/.idea/gradle.xml | 1 + .../main/java/io/fullstack/firestack/FirestackDatabase.java | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml index f4f39e8..76a4349 100644 --- a/android/.idea/gradle.xml +++ b/android/.idea/gradle.xml @@ -5,6 +5,7 @@ diff --git a/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java b/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java index 61cda5d..37aeca3 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java @@ -304,8 +304,12 @@ public String getName() { public void enablePersistence( final Boolean enable, final Callback callback) { - FirebaseDatabase.getInstance() + try { + FirebaseDatabase.getInstance() .setPersistenceEnabled(enable); + } catch (Throwable t) { + Log.e(TAG, "FirebaseDatabase setPersistenceEnabled exception", t); + } WritableMap res = Arguments.createMap(); res.putString("status", "success"); From a4a115f16d55fd9052b5f68d72936d067c67f193 Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Tue, 8 Nov 2016 19:25:36 +0530 Subject: [PATCH 03/23] Parity in response format between ios and android --- .../java/io/fullstack/firestack/FirestackAuth.java | 4 ++-- ios/Firestack/FirestackAuth.m | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index 7c0c2bb..7d3f889 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java @@ -432,7 +432,6 @@ public void onComplete(@NonNull Task task) { WritableMap userMap = getUserMap(); if (FirestackAuthModule.this.user != null) { final String token = task.getResult().getToken(); - userMap.putString("token", token); userMap.putBoolean("anonymous", false); } @@ -509,9 +508,10 @@ private WritableMap getUserMap() { userMap.putString("email", email); userMap.putString("uid", uid); userMap.putString("providerId", provider); + userMap.putBoolean("emailVerified", user.isEmailVerified()); if (name != null) { - userMap.putString("name", name); + userMap.putString("displayName", name); } if (photoUrl != null) { diff --git a/ios/Firestack/FirestackAuth.m b/ios/Firestack/FirestackAuth.m index 0eba61c..287c52b 100644 --- a/ios/Firestack/FirestackAuth.m +++ b/ios/Firestack/FirestackAuth.m @@ -25,7 +25,7 @@ @implementation FirestackAuth if (!user) { NSDictionary *evt = @{ @"eventName": AUTH_ANONYMOUS_ERROR_EVENT, - @"msg": [error localizedDescription] + @"errorMessage": [error localizedDescription] }; @@ -41,7 +41,7 @@ @implementation FirestackAuth } @catch(NSException *ex) { NSDictionary *eventError = @{ @"eventName": AUTH_ANONYMOUS_ERROR_EVENT, - @"msg": ex.reason + @"errorMessage": ex.reason }; [self sendJSEvent:AUTH_ERROR_EVENT @@ -144,14 +144,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 }]; } @@ -164,7 +165,7 @@ @implementation FirestackAuth [self sendJSEvent:AUTH_CHANGED_EVENT props:@{ @"eventName": @"no_user", - @"authenticated": @(false), + @"authenticated": @((BOOL)false), @"error": err }]; } From 483e15fd4b7a8256bec99771220c4cc9721e198a Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Tue, 8 Nov 2016 20:19:53 +0530 Subject: [PATCH 04/23] Fixed a typo that was causing an error --- lib/modules/database.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index f7f91fe..0cf1382 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -232,7 +232,7 @@ class DatabaseRef extends ReferenceBase { const path = this.dbPath(); return this.db.off(path, evt, origCB) .then(({callback, subscriptions}) => { - if (dbSubscriptions[path] && dbSubscriptions[path][evt].length > 0) { + if (subscriptions[path] && subscriptions[path][evt].length > 0) { return subscriptions; } @@ -514,4 +514,4 @@ export class Database extends Base { } } -export default Database \ No newline at end of file +export default Database From a85f5facacc4ecb6af0924e88c222f6d4d4c3747 Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Wed, 9 Nov 2016 14:50:11 +0530 Subject: [PATCH 05/23] Parity in response format between ios and android --- ios/Firestack/FirestackAuth.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/Firestack/FirestackAuth.m b/ios/Firestack/FirestackAuth.m index 287c52b..bcd0919 100644 --- a/ios/Firestack/FirestackAuth.m +++ b/ios/Firestack/FirestackAuth.m @@ -186,7 +186,8 @@ @implementation FirestackAuth FIRUser *user = [FIRAuth auth].currentUser; if (user != nil) { - NSDictionary *userProps = [self userPropsFromFIRUser:user]; + NSMutableDictionary *userProps = [self userPropsFromFIRUser:user]; + [userProps setValue: @((BOOL)true) forKey: @"authenticated"]; callback(@[[NSNull null], userProps]); } else { // No user is signed in. From e34988b436d027ca9ec1f036d07d79c10748e1ec Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Wed, 9 Nov 2016 19:22:55 +0530 Subject: [PATCH 06/23] Reverted changes since it was an error from my end --- lib/modules/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index 0cf1382..6affe14 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -232,7 +232,7 @@ class DatabaseRef extends ReferenceBase { const path = this.dbPath(); return this.db.off(path, evt, origCB) .then(({callback, subscriptions}) => { - if (subscriptions[path] && subscriptions[path][evt].length > 0) { + if (dbSubscriptions[path] && dbSubscriptions[path][evt].length > 0) { return subscriptions; } From 68396e07542cedd66fd61fad48554f5cdebd8430 Mon Sep 17 00:00:00 2001 From: Pedro Ribeiro Date: Wed, 9 Nov 2016 14:53:22 +0000 Subject: [PATCH 07/23] fixing filter value creation --- lib/modules/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index f7f91fe..b87d429 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -113,7 +113,7 @@ class DatabaseQuery { .forEach(key => { const filter = this.filters[key]; if (filter) { - const filterArgs = [key, filter].join(argsSeparator) + const filterArgs = ([key].concat(filter)).join(argsSeparator) modifiers.push(filterArgs); } }) From c3129fbb16663c4813b5c4b2fa58265e33c41a31 Mon Sep 17 00:00:00 2001 From: ghuh Date: Wed, 9 Nov 2016 19:04:54 -0700 Subject: [PATCH 08/23] Fix Android listenForAuth method to correctly check for user --- android/src/main/java/io/fullstack/firestack/FirestackAuth.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index e9ec2bf..9f1372a 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java @@ -68,7 +68,7 @@ public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { WritableMap msgMap = Arguments.createMap(); msgMap.putString("eventName", "listenForAuth"); - if (FirestackAuthModule.this.user != null) { + if (firebaseAuth.getCurrentUser() != null) { WritableMap userMap = getUserMap(); msgMap.putBoolean("authenticated", true); From 28644a6560e12576b7243732f46ae1a7990decc1 Mon Sep 17 00:00:00 2001 From: Ari Lerner Date: Wed, 9 Nov 2016 20:26:11 -0800 Subject: [PATCH 09/23] WIP: FirebaseAuth --- .../io/fullstack/firestack/FirestackAuth.java | 193 +++++++++++++----- lib/utils/window-or-global.js | 2 +- 2 files changed, 138 insertions(+), 57 deletions(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index e9ec2bf..050768e 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java @@ -1,3 +1,4 @@ + package io.fullstack.firestack; import android.content.Context; @@ -99,7 +100,7 @@ public void unlistenForAuth(final Callback callback) { } @ReactMethod - public void createUserWithEmail(final String email, final String password, final Callback onComplete) { + public void createUserWithEmail(final String email, final String password, final Callback callback) { mAuth = FirebaseAuth.getInstance(); mAuth.createUserWithEmailAndPassword(email, password) @@ -107,13 +108,18 @@ public void createUserWithEmail(final String email, final String password, final @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - FirestackAuthModule.this.user = task.getResult().getUser(); - userCallback(FirestackAuthModule.this.user, onComplete); - }else{ - userErrorCallback(task, onComplete); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); + } else { + // userErrorCallback(task, callback); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -125,13 +131,19 @@ public void signInWithEmail(final String email, final String password, final Cal @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - FirestackAuthModule.this.user = task.getResult().getUser(); - userCallback(FirestackAuthModule.this.user, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); } else { - userErrorCallback(task, callback); + // userErrorCallback(task, callback); } + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + Log.e(TAG, "An exception occurred: " + ex.getMessage()); + userExceptionCallback(ex, callback); } - }); + }); } @ReactMethod @@ -156,14 +168,18 @@ public void onComplete(@NonNull Task task) { Log.d(TAG, "signInAnonymously:onComplete:" + task.isSuccessful()); if (task.isSuccessful()) { - FirestackAuthModule.this.user = task.getResult().getUser(); - anonymousUserCallback(FirestackAuthModule.this.user, callback); - }else{ - userErrorCallback(task, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + anonymousUserCallback(FirestackAuthModule.this.user, callback); + } else { + // userErrorCallback(task, callback); } - } - }); - + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -175,14 +191,19 @@ public void signInWithCustomToken(final String customToken, final Callback callb @Override public void onComplete(@NonNull Task task) { Log.d(TAG, "signInWithCustomToken:onComplete:" + task.isSuccessful()); - if (task.isSuccessful()) { - FirestackAuthModule.this.user = task.getResult().getUser(); + if (task.isSuccessful()) { + FirestackAuthModule.this.user = task.getResult().getUser(); userCallback(FirestackAuthModule.this.user, callback); - } else { - userErrorCallback(task, callback); - } + } else { + // userErrorCallback(task, callback); + } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -212,7 +233,7 @@ public void onComplete(@NonNull Task task) { FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { - userErrorCallback(task, callback); + // userErrorCallback(task, callback); } } }); @@ -238,10 +259,15 @@ public void onComplete(@NonNull Task task) { FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { - userErrorCallback(task, callback); + // userErrorCallback(task, callback); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); @@ -265,10 +291,15 @@ public void onComplete(@NonNull Task task) { FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { - userErrorCallback(task, callback); + // userErrorCallback(task, callback); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); @@ -289,11 +320,16 @@ public void onComplete(@NonNull Task task) { WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); callback.invoke(null, resp); - }else{ + } else { callback.invoke(task.getException().toString()); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -312,10 +348,15 @@ public void onComplete(@NonNull Task task) { resp.putString("msg", "User account deleted"); callback.invoke(null, resp); } else { - userErrorCallback(task, callback); + // userErrorCallback(task, callback); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } else { WritableMap err = Arguments.createMap(); err.putInt("errorCode", NO_CURRENT_USER); @@ -345,7 +386,12 @@ public void onComplete(@NonNull Task task) { callback.invoke(err); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -378,10 +424,15 @@ public void onComplete(@NonNull Task task) { FirebaseUser u = FirebaseAuth.getInstance().getCurrentUser(); userCallback(u, callback); } else { - userErrorCallback(task, callback); + // userErrorCallback(task, callback); } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -417,14 +468,19 @@ public void googleLogin(String IdToken, final Callback callback) { .addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { - if (task.isSuccessful()) { - FirestackAuthModule.this.user = task.getResult().getUser(); - userCallback(FirestackAuthModule.this.user, callback); - }else{ - userErrorCallback(task, callback); - } + if (task.isSuccessful()) { + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); + }else{ + // userErrorCallback(task, callback); + } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @ReactMethod @@ -436,18 +492,23 @@ public void facebookLogin(String Token, final Callback callback) { .addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { - if (task.isSuccessful()) { - FirestackAuthModule.this.user = task.getResult().getUser(); - userCallback(FirestackAuthModule.this.user, callback); - }else{ - userErrorCallback(task, callback); - } + if (task.isSuccessful()) { + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); + }else{ + // userErrorCallback(task, callback); + } } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } // Internal helpers - public void userCallback(FirebaseUser passedUser, final Callback onComplete) { + public void userCallback(FirebaseUser passedUser, final Callback callback) { if (passedUser == null) { mAuth = FirebaseAuth.getInstance(); @@ -469,13 +530,18 @@ public void onComplete(@NonNull Task task) { msgMap.putMap("user", userMap); - onComplete.invoke(null, msgMap); + callback.invoke(null, msgMap); } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } // TODO: Reduce to one method - public void anonymousUserCallback(FirebaseUser passedUser, final Callback onComplete) { + public void anonymousUserCallback(FirebaseUser passedUser, final Callback callback) { if (passedUser == null) { mAuth = FirebaseAuth.getInstance(); @@ -484,7 +550,8 @@ public void anonymousUserCallback(FirebaseUser passedUser, final Callback onComp this.user = passedUser; } - this.user.getToken(true).addOnCompleteListener(new OnCompleteListener() { + this.user.getToken(true) + .addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { WritableMap msgMap = Arguments.createMap(); @@ -499,9 +566,14 @@ public void onComplete(@NonNull Task task) { msgMap.putMap("user", userMap); - onComplete.invoke(null, msgMap); + callback.invoke(null, msgMap); } - }); + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception ex) { + userExceptionCallback(ex, callback); + } + }); } @@ -524,6 +596,15 @@ public void userErrorCallback(Task task, final Callback onFail) { onFail.invoke(error); } + public void userExceptionCallback(Exception ex, final Callback onFail) { + WritableMap error = Arguments.createMap(); + error.putInt("errorCode", ex.hashCode()); + error.putString("errorMessage", ex.getMessage()); + error.putString("allErrorMessage", ex.toString()); + + onFail.invoke(error); + } + private WritableMap getUserMap() { WritableMap userMap = Arguments.createMap(); 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 From 1111617f2b07e38b0ad1849c4eb919723da2ffc4 Mon Sep 17 00:00:00 2001 From: Ari Lerner Date: Thu, 10 Nov 2016 02:55:43 -0800 Subject: [PATCH 10/23] Added initial handling of android error messages --- .../io/fullstack/firestack/FirestackAuth.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index 050768e..d9d4101 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java @@ -33,6 +33,7 @@ import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.GetTokenResult; import com.google.firebase.auth.GoogleAuthProvider; +import com.google.firebase.auth.FirebaseAuthException; class FirestackAuthModule extends ReactContextBaseJavaModule { private final int NO_CURRENT_USER = 100; @@ -588,19 +589,21 @@ public void noUserCallback(final Callback callback) { } public void userErrorCallback(Task task, final Callback onFail) { - WritableMap error = Arguments.createMap(); - error.putInt("errorCode", task.getException().hashCode()); - error.putString("errorMessage", task.getException().getMessage()); - error.putString("allErrorMessage", task.getException().toString()); - - onFail.invoke(error); + userExceptionCallback(task.getException(), onFail); } - public void userExceptionCallback(Exception ex, final Callback onFail) { + public void userExceptionCallback(Exception exp, final Callback onFail) { WritableMap error = Arguments.createMap(); - error.putInt("errorCode", ex.hashCode()); - error.putString("errorMessage", ex.getMessage()); - error.putString("allErrorMessage", ex.toString()); + error.putString("errorMessage", exp.getMessage()); + error.putString("allErrorMessage", exp.toString()); + + try { + throw exp; + } catch (FirebaseAuthException ex) { + error.putString("errorCode", ex.getErrorCode()); + } catch (Exception ex) { + Log.e(TAG, ex.getMessage()); + } onFail.invoke(error); } @@ -636,3 +639,6 @@ private WritableMap getUserMap() { return userMap; } } +n userMap; + } +} From 7d09ab4a7aba582290e1bebf6692dadaba3e38e7 Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Fri, 11 Nov 2016 20:30:56 +0530 Subject: [PATCH 11/23] Parity of response format between ios and android. Added authenticated flag to response from getCurrentUser --- android/src/main/java/io/fullstack/firestack/FirestackAuth.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index a155612..36dd584 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java @@ -530,7 +530,7 @@ public void onComplete(@NonNull Task task) { } msgMap.putMap("user", userMap); - + msgMap.putBoolean("authenticated", true); callback.invoke(null, msgMap); } }).addOnFailureListener(new OnFailureListener() { From e5255698ae07a84e7e946c1666e1f430616581a1 Mon Sep 17 00:00:00 2001 From: Ari Lerner Date: Mon, 14 Nov 2016 08:22:34 -0800 Subject: [PATCH 12/23] Remove RCTConvert to work with react-native-fcm --- ios/Firestack/FirestackCloudMessaging.m | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/ios/Firestack/FirestackCloudMessaging.m b/ios/Firestack/FirestackCloudMessaging.m index 9cfa6b3..0a71a64 100644 --- a/ios/Firestack/FirestackCloudMessaging.m +++ b/ios/Firestack/FirestackCloudMessaging.m @@ -13,28 +13,7 @@ #endif #import "FirestackCloudMessaging.h" #import "FirestackEvents.h" -#import "RCTConvert.h" - -// 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; -} - -@end +// #import "RCTConvert.h" @implementation FirestackCloudMessaging From 8306c45f3be5be063b3eebc362ea7d3ca1ab2505 Mon Sep 17 00:00:00 2001 From: Samer Date: Wed, 16 Nov 2016 14:38:18 +0100 Subject: [PATCH 13/23] Filter out undefined filters --- lib/modules/database.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index 5c4b27e..6ebdecc 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -111,9 +111,10 @@ class DatabaseQuery { } Object.keys(this.filters) .forEach(key => { - const filter = this.filters[key]; + let filter = this.filters[key]; if (filter) { - const filterArgs = ([key].concat(filter)).join(argsSeparator) + const cleanFilters = filter.filter((f) => typeof f !== "undefined"); + const filterArgs = ([key].concat(cleanFilters)).join(argsSeparator); modifiers.push(filterArgs); } }) From 487ed30886c1cba943d34cde67f565a0a92c4881 Mon Sep 17 00:00:00 2001 From: Pedro Ribeiro Date: Wed, 16 Nov 2016 18:23:19 +0000 Subject: [PATCH 14/23] removing trailing comma --- lib/modules/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index 5c4b27e..9956f39 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -96,7 +96,7 @@ class DatabaseQuery { } setFilter(name, ...args) { - this.filters[name] = args; + this.filters[name] = args.filter(n => n != undefined); return this.ref; } From 6ca78a19ea7d611605ddf82f151c13c6534ac098 Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Thu, 17 Nov 2016 11:46:52 +0530 Subject: [PATCH 15/23] forEach and map method on the DataSnapshot does not retain key --- lib/modules/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index 5c4b27e..ff59c74 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -35,7 +35,7 @@ class DataSnapshot { forEach(fn) { (this.childKeys || []) - .forEach(key => fn(this.value[key])) + .forEach(key => fn({key: key, value: this.value[key]})) } map(fn) { From 80133de8316729a851610ee6d326c73a642dde32 Mon Sep 17 00:00:00 2001 From: Chaitanya Bhagvan Date: Thu, 17 Nov 2016 12:30:32 +0530 Subject: [PATCH 16/23] Fixed error calling length of undefined --- lib/modules/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/database.js b/lib/modules/database.js index ff59c74..1112919 100644 --- a/lib/modules/database.js +++ b/lib/modules/database.js @@ -232,7 +232,7 @@ class DatabaseRef extends ReferenceBase { const path = this.dbPath(); return this.db.off(path, evt, origCB) .then(({callback, subscriptions}) => { - if (dbSubscriptions[path] && dbSubscriptions[path][evt].length > 0) { + if (dbSubscriptions[path] && dbSubscriptions[path][evt] && dbSubscriptions[path][evt].length > 0) { return subscriptions; } From d005ab2f966cca23e955ca79f109efbefced8772 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 17 Nov 2016 20:05:09 +0100 Subject: [PATCH 17/23] Use storage url from .plist file if not setStorageUrl() hasn't been called. - Fix weird error that NSError is not KVC compliant when storage is not configured JS - Fixed code seems to exist on several more locations but has not been fixed, since there are no tests to ensure I don't break anything. --- ios/Firestack/FirestackStorage.m | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ios/Firestack/FirestackStorage.m b/ios/Firestack/FirestackStorage.m index d88a19a..ff96646 100644 --- a/ios/Firestack/FirestackStorage.m +++ b/ios/Firestack/FirestackStorage.m @@ -52,14 +52,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]; From dfc0bd1eb86706a421341fdf9b6a6f27e3931d97 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 17 Nov 2016 20:51:23 +0100 Subject: [PATCH 18/23] Fix donwloadUrl to use storageUrl from .plist. - Use storageUrl from default config if not set otherwise in JS - Similar code exists on several other places that are not fixed since no tests prevent breaking stuff. --- ios/Firestack/FirestackStorage.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ios/Firestack/FirestackStorage.m b/ios/Firestack/FirestackStorage.m index ff96646..b87d394 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) { From f4bcfea81abb1c0d8fb37f02e912700ede340a4e Mon Sep 17 00:00:00 2001 From: Ari Lerner Date: Thu, 17 Nov 2016 20:24:38 -0500 Subject: [PATCH 19/23] WIP --- ios/Firestack/FirestackCloudMessaging.m | 55 ++++++++++++++----------- ios/Firestack/FirestackStorage.m | 1 + lib/modules/cloudmessaging.js | 24 ++++++++++- 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/ios/Firestack/FirestackCloudMessaging.m b/ios/Firestack/FirestackCloudMessaging.m index 0a71a64..ea8a346 100644 --- a/ios/Firestack/FirestackCloudMessaging.m +++ b/ios/Firestack/FirestackCloudMessaging.m @@ -68,34 +68,39 @@ + (void) setup:(UIApplication *) application selector:@selector(handleTokenRefresh) name:kFIRInstanceIDTokenRefreshNotification object: nil]; +} +#pragma mark Request permissions +- (void) requestPermissions(NSDictionary *)requestedPermissions + callback:(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) - [[FIRMessaging messaging] setRemoteMessageDelegate:self]; - #endif -} + 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 + } -[[UIApplication sharedApplication] registerForRemoteNotifications]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; } #pragma mark callbacks diff --git a/ios/Firestack/FirestackStorage.m b/ios/Firestack/FirestackStorage.m index d88a19a..3e4511a 100644 --- a/ios/Firestack/FirestackStorage.m +++ b/ios/Firestack/FirestackStorage.m @@ -157,6 +157,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 index a03a2ed..73b390b 100644 --- a/lib/modules/cloudmessaging.js +++ b/lib/modules/cloudmessaging.js @@ -1,12 +1,20 @@ -import {NativeModules, NativeEventEmitter} from 'react-native'; +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' @@ -16,6 +24,20 @@ export class CloudMessaging extends Base { 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); From 98d873f26b1cbae4af9bed5c207d0adf385d9dc4 Mon Sep 17 00:00:00 2001 From: Matt Jeanes Date: Mon, 21 Nov 2016 17:04:44 -0500 Subject: [PATCH 20/23] Implement firestack.storage.download() for android --- .../fullstack/firestack/FirestackStorage.java | 156 ++++++++++++++++-- 1 file changed, 144 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackStorage.java b/android/src/main/java/io/fullstack/firestack/FirestackStorage.java index b41301d..a6b4094 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); @@ -129,6 +249,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 +323,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); } } }) From f3233ade9f35ad12ef5224f400f159f6eefb9d02 Mon Sep 17 00:00:00 2001 From: Matt Jeanes Date: Mon, 21 Nov 2016 17:04:58 -0500 Subject: [PATCH 21/23] Factor out errorCodes for future constants --- .../io/fullstack/firestack/FirestackStorage.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/io/fullstack/firestack/FirestackStorage.java b/android/src/main/java/io/fullstack/firestack/FirestackStorage.java index a6b4094..21ed217 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackStorage.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackStorage.java @@ -229,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)); } }); @@ -336,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)); } } @@ -353,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)); } } From d50e9766ee4f8089110ca7e0c7f3cf03325a7a6c Mon Sep 17 00:00:00 2001 From: Matt Jeanes Date: Mon, 21 Nov 2016 18:19:04 -0500 Subject: [PATCH 22/23] Remove unused/old database.js Database.js was factored out into the database/ folder. This stuck around for some reason and the require('modules/database') preferred the old version --- lib/modules/database.js | 518 ---------------------------------------- 1 file changed, 518 deletions(-) delete mode 100644 lib/modules/database.js diff --git a/lib/modules/database.js b/lib/modules/database.js deleted file mode 100644 index cbf3bc4..0000000 --- a/lib/modules/database.js +++ /dev/null @@ -1,518 +0,0 @@ -/** - * Database representation wrapper - */ -import {NativeModules, NativeEventEmitter} from 'react-native'; -const FirestackDatabase = NativeModules.FirestackDatabase; -const FirestackDatabaseEvt = new NativeEventEmitter(FirestackDatabase); - -import promisify from '../utils/promisify' -import { Base, ReferenceBase } from './base' - -let dbSubscriptions = {}; - -class DataSnapshot { - static key:String; - static value:Object; - static exists:boolean; - static hasChildren:boolean; - static childrenCount:Number; - static childKeys:String[]; - - constructor(ref, snapshot) { - this.ref = ref; - this.key = snapshot.key; - this.value = snapshot.value; - this.exists = snapshot.exists || true; - this.priority = snapshot.priority; - this.hasChildren = snapshot.hasChildren || false; - this.childrenCount = snapshot.childrenCount || 0; - this.childKeys = snapshot.childKeys || []; - } - - val() { - return this.value; - } - - forEach(fn) { - (this.childKeys || []) - .forEach(key => fn({key: key, value: this.value[key]})) - } - - map(fn) { - let arr = []; - this.forEach(item => arr.push(fn(item))) - return arr; - } - - reverseMap(fn) { - return this.map(fn).reverse(); - } -} - -class DatabaseOnDisconnect { - constructor(ref) { - this.ref = ref; - } - - setValue(val) { - const path = this.ref.dbPath(); - if (typeof val == 'string') { - return promisify('onDisconnectSetString', FirestackDatabase)(path, val); - } else if (typeof val == 'object') { - return promisify('onDisconnectSetObject', FirestackDatabase)(path, val); - } - } - - remove() { - const path = this.ref.dbPath(); - return promisify('onDisconnectRemove', FirestackDatabase)(path); - } - - cancel() { - const path = this.ref.dbPath(); - return promisify('onDisconnectCancel', FirestackDatabase)(path); - } -} - -class DatabaseQuery { - static ref: DatabaseRef; - static orderBy: String[]; - static limit: String[]; - static filters: Object; - - constructor(ref) { - this.ref = ref; - this.reset(); - } - - setOrderBy(name, ...args) { - this.orderBy = [name].concat(args); - return this.ref; - } - - setLimit(name, ...args) { - this.limit = [name].concat(args); - return this.ref; - } - - setFilter(name, ...args) { - this.filters[name] = args.filter(n => n != undefined); - return this.ref; - } - - build() { - const argsSeparator = ':' - let modifiers = []; - if (this.orderBy) { - modifiers.push(this.orderBy.join(argsSeparator)); - } - if (this.limit) { - modifiers.push(this.limit.join(argsSeparator)); - } - Object.keys(this.filters) - .forEach(key => { - let filter = this.filters[key]; - if (filter) { - const cleanFilters = filter.filter((f) => typeof f !== "undefined"); - const filterArgs = ([key].concat(cleanFilters)).join(argsSeparator); - modifiers.push(filterArgs); - } - }) - return modifiers; - } - - reset() { - this.orderBy = null; - this.limit = null; - this.filters = {}; - ['startAt', 'endAt', 'equalTo'] - .forEach(key => this.filters[key] = null); - return this.ref; - } -} - -// https://firebase.google.com/docs/reference/js/firebase.database.Reference -const separator = '/'; -class DatabaseRef extends ReferenceBase { - constructor(db, path) { - super(db.firestack, path); - - this.db = db; - this.query = new DatabaseQuery(this); - this.listeners = {}; - - // Aliases - this.get = this.getAt; - this.set = this.setAt; - this.update = this.updateAt; - this.remove = this.removeAt; - - this.log.debug('Created new DatabaseRef', this.dbPath()); - } - - // Parent roots - parent() { - const parentPaths = this.path.slice(0, -1); - return new DatabaseRef(this.db, parentPaths); - } - - root() { - return new DatabaseRef(this.db, []); - } - - child(...paths) { - return new DatabaseRef(this.db, this.path.concat(paths)); - } - - keepSynced(bool) { - const path = this.dbPath(); - return promisify('keepSynced', FirestackDatabase)(path, bool); - } - - // Get the value of a ref either with a key - getAt() { - const path = this.dbPath(); - const modifiers = this.dbModifiers(); - return promisify('onOnce', FirestackDatabase)(path, modifiers, 'value'); - } - - setAt(val) { - const path = this.dbPath(); - const value = this._serializeValue(val); - return promisify('set', FirestackDatabase)(path, value) - } - - updateAt(val) { - const path = this.dbPath(); - const value = this._serializeValue(val); - return promisify('update', FirestackDatabase)(path, value) - } - - removeAt(key) { - const path = this.dbPath(); - return promisify('remove', FirestackDatabase)(path) - } - - push(val={}) { - const path = this.dbPath(); - const value = this._serializeValue(val); - return promisify('push', FirestackDatabase)(path, value) - .then(({ref}) => { - return new DatabaseRef(this.db, ref.split(separator)) - }) - } - - on(evt, cb) { - const path = this.dbPath(); - const modifiers = this.dbModifiers(); - return this.db.on(path, evt, cb) - .then(({callback, subscriptions}) => { - return promisify('on', FirestackDatabase)(path, modifiers, evt) - .then(() => { - this.listeners[evt] = subscriptions; - callback(this); - return subscriptions; - }) - }); - } - - once(evt='once', cb) { - const path = this.dbPath(); - const modifiers = this.dbModifiers(); - return promisify('onOnce', FirestackDatabase)(path, modifiers, evt) - .then(({snapshot}) => new DataSnapshot(this, snapshot)) - .then(snapshot => { - if (cb && typeof cb === 'function') { - cb(snapshot); - } - return snapshot; - }) - } - - off(evt='', origCB) { - const path = this.dbPath(); - return this.db.off(path, evt, origCB) - .then(({callback, subscriptions}) => { - if (dbSubscriptions[path] && dbSubscriptions[path][evt] && dbSubscriptions[path][evt].length > 0) { - return subscriptions; - } - - return promisify('off', FirestackDatabase)(path, evt) - .then(() => { - // subscriptions.forEach(sub => sub.remove()); - delete this.listeners[evt]; - callback(this); - return subscriptions; - }) - }) - .catch(err => { - console.error('Never get here', err); - }) - } - - cleanup() { - let promises = Object.keys(this.listeners) - .map(key => this.off(key)) - return Promise.all(promises); - } - - // Sanitize value - // As Firebase cannot store date objects. - _serializeValue(obj={}) { - return Object.keys(obj).reduce((sum, key) => { - let val = obj[key]; - if (val instanceof Date) { - val = val.toISOString(); - } - return { - ...sum, - [key]: val - } - }, {}); - } - - _deserializeValue(obj={}) { - return Object.keys(obj).reduce((sum, key) => { - let val = obj[key]; - if (val instanceof Date) { - val = val.getTime(); - } - return { - ...sum, - [key]: val - } - }, {}); - } - - // Modifiers - orderByKey() { - return this.query.setOrderBy('orderByKey'); - } - - orderByPriority() { - return this.query.setOrderBy('orderByPriority'); - } - - orderByValue() { - return this.query.setOrderBy('orderByValue'); - } - - orderByChild(key) { - return this.query.setOrderBy('orderByChild', key); - } - - // Limits - limitToLast(limit) { - return this.query.setLimit('limitToLast', limit); - } - - limitToFirst(limit) { - return this.query.setLimit('limitToFirst', limit); - } - - // Filters - equalTo(value, key) { - return this.query.setFilter('equalTo', value, key); - } - - endAt(value, key) { - return this.query.setFilter('endAt', value, key); - } - - startAt(value, key) { - return this.query.setFilter('startAt', value, key); - } - - presence(path) { - const presence = this.firestack.presence; - const ref = path ? this.child(path) : this; - return presence.ref(ref, this.dbPath()); - } - - // onDisconnect - onDisconnect() { - return new DatabaseOnDisconnect(this); - } - - // attributes - get fullPath() { - return this.dbPath(); - } - - get name() { - return this.path.splice(-1); - } - - dbPath() { - let path = this.path; - let pathStr = (path.length > 0 ? path.join('/') : '/'); - if (pathStr[0] != '/') { - pathStr = `/${pathStr}` - } - return pathStr; - } - - dbModifiers() { - const modifiers = this.query.build(); - this.query.reset(); // should we reset this - return modifiers; - } - - get namespace() { - return `firestack:dbRef` - } -} - -export class Database extends Base { - - constructor(firestack, options={}) { - super(firestack, options); - this.log.debug('Created new Database instance', this.options); - - this.persistenceEnabled = false; - this.successListener = null; - this.errorListener = null; - this.refs = {}; - } - - ref(...path) { - const key = this._pathKey(path); - if (!this.refs[key]) { - const ref = new DatabaseRef(this, path); - this.refs[key] = ref; - } - return this.refs[key]; - } - - setPersistence(enable=true) { - let promise; - if (this.persistenceEnabled !== enable) { - this.log.debug(`${enable ? 'Enabling' : 'Disabling'} persistence`); - promise = this.whenReady(promisify('enablePersistence', FirestackDatabase)(enable)); - this.persistenceEnabled = enable; - } else { - promise = this.whenReady(Promise.resolve({status: "Already enabled"})) - } - - return promise; - } - - handleDatabaseEvent(evt) { - const body = evt.body; - const path = body.path; - const evtName = body.eventName; - - const subscriptions = dbSubscriptions[path]; - - if (subscriptions) { - const cbs = subscriptions[evtName]; - cbs.forEach(cb => { - if (cb && typeof(cb) === 'function') { - const snap = new DataSnapshot(this, body.snapshot); - this.log.debug('database_event received', path, evtName); - cb(snap, body); - } - }); - } - } - - handleDatabaseError(evt) { - this.log.debug('handleDatabaseError ->', evt); - } - - on(path, evt, cb) { - const key = this._pathKey(path); - - if (!dbSubscriptions[key]) { - dbSubscriptions[key] = {}; - } - - if (!dbSubscriptions[key][evt]) { - dbSubscriptions[key][evt] = []; - } - dbSubscriptions[key][evt].push(cb); - - if (!this.successListener) { - this.successListener = FirestackDatabaseEvt - .addListener( - 'database_event', - this.handleDatabaseEvent.bind(this)); - } - - if (!this.errorListener) { - this.errorListener = FirestackDatabaseEvt - .addListener( - 'database_error', - this.handleDatabaseError.bind(this)); - } - - const callback = (ref) => { - const key = this._pathKey(ref.path); - this.refs[key] = ref; - } - const subscriptions = [this.successListener, this.errorListener]; - return Promise.resolve({callback, subscriptions}); - } - - off(path, evt, origCB) { - const key = this._pathKey(path); - // Remove subscription - if (dbSubscriptions[key]) { - if (!evt || evt === "") { - dbSubscriptions[key] = {}; - } else if (dbSubscriptions[key][evt]) { - if (origCB) { - dbSubscriptions[key][evt].splice(dbSubscriptions[key][evt].indexOf(origCB), 1); - } else { - delete dbSubscriptions[key][evt]; - } - } - - if (Object.keys(dbSubscriptions[key]).length <= 0) { - // there are no more subscriptions - // so we can unwatch - delete dbSubscriptions[key] - } - if (Object.keys(dbSubscriptions).length == 0) { - if (this.successListener) { - this.successListener.remove(); - this.successListener = null; - } - if (this.errorListener) { - this.errorListener.remove(); - this.errorListener = null; - } - } - } - const callback = (ref) => { - const key = this._pathKey(ref.path); - delete this.refs[key]; - } - const subscriptions = [this.successListener, this.errorListener]; - return Promise.resolve({callback, subscriptions}); - } - - cleanup() { - let promises = Object.keys(this.refs) - .map(key => this.refs[key]) - .map(ref => ref.cleanup()) - return Promise.all(promises); - } - - release(...path) { - const key = this._pathKey(path); - if (this.refs[key]) { - delete this.refs[key]; - } - } - - _pathKey(...path) { - return path.join('-'); - } - - get namespace() { - return 'firestack:database' - } -} - -export default Database From 4bb951c01b0191003da00152ab3bab3114ac59c8 Mon Sep 17 00:00:00 2001 From: tretelny Date: Fri, 25 Nov 2016 23:36:35 -0800 Subject: [PATCH 23/23] FCM implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the FCM implementation, local notifications could be cleaner… --- docs/api/cloud-messaging.md | 56 +++ ios/Firestack/Firestack.m | 4 +- ios/Firestack/FirestackCloudMessaging.h | 3 +- ios/Firestack/FirestackCloudMessaging.m | 434 ++++++++++++++---------- lib/modules/messaging.js | 44 ++- 5 files changed, 362 insertions(+), 179 deletions(-) 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/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 ea8a346..3d9f97a 100644 --- a/ios/Firestack/FirestackCloudMessaging.m +++ b/ios/Firestack/FirestackCloudMessaging.m @@ -13,153 +13,190 @@ #endif #import "FirestackCloudMessaging.h" #import "FirestackEvents.h" -// #import "RCTConvert.h" +#import "RCTConvert.h" + +// 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"]]; + 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 // 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]; + [[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]; } + #pragma mark Request permissions -- (void) requestPermissions(NSDictionary *)requestedPermissions - callback:(RCTResponseSenderBlock) callback +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 = + 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 - } - - [[UIApplication sharedApplication] registerForRemoteNotifications]; + [[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); @@ -167,68 +204,121 @@ - (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) {} @@ -237,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) @@ -289,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 @@ -302,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/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 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