diff --git a/android/build.gradle b/android/build.gradle index 4009637..a06d876 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,13 +20,13 @@ android { dependencies { compile 'com.facebook.react:react-native:0.20.+' - compile 'com.google.android.gms:play-services-base:9.6.1' + compile 'com.google.android.gms:play-services-base:9.8.0' - compile 'com.google.firebase:firebase-core:9.6.0' - compile 'com.google.firebase:firebase-auth:9.6.0' - compile 'com.google.firebase:firebase-analytics:9.6.0' - compile 'com.google.firebase:firebase-database:9.6.0' - compile 'com.google.firebase:firebase-storage:9.6.0' - compile 'com.google.firebase:firebase-messaging:9.6.0' + compile 'com.google.firebase:firebase-core:9.8.0' + compile 'com.google.firebase:firebase-auth:9.8.0' + compile 'com.google.firebase:firebase-analytics:9.8.0' + compile 'com.google.firebase:firebase-database:9.8.0' + compile 'com.google.firebase:firebase-storage:9.8.0' + compile 'com.google.firebase:firebase-messaging:9.8.0' } diff --git a/android/src/main/java/io/fullstack/firestack/FirestackAuth.java b/android/src/main/java/io/fullstack/firestack/FirestackAuth.java index 29a4efb..7c0c2bb 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 (user != null) { + if (FirestackAuthModule.this.user != null) { WritableMap userMap = getUserMap(); msgMap.putBoolean("authenticated", true); @@ -107,8 +107,8 @@ public void createUserWithEmail(final String email, final String password, final @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - user = task.getResult().getUser(); - userCallback(user, onComplete); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, onComplete); }else{ userErrorCallback(task, onComplete); } @@ -125,8 +125,8 @@ public void signInWithEmail(final String email, final String password, final Cal @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - user = task.getResult().getUser(); - userCallback(user, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); } else { userErrorCallback(task, callback); } @@ -156,8 +156,8 @@ public void onComplete(@NonNull Task task) { Log.d(TAG, "signInAnonymously:onComplete:" + task.isSuccessful()); if (task.isSuccessful()) { - user = task.getResult().getUser(); - anonymousUserCallback(user, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + anonymousUserCallback(FirestackAuthModule.this.user, callback); }else{ userErrorCallback(task, callback); } @@ -176,8 +176,8 @@ public void signInWithCustomToken(final String customToken, final Callback callb public void onComplete(@NonNull Task task) { Log.d(TAG, "signInWithCustomToken:onComplete:" + task.isSuccessful()); if (task.isSuccessful()) { - user = task.getResult().getUser(); - userCallback(user, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); } else { userErrorCallback(task, callback); } @@ -356,7 +356,7 @@ public void onComplete(@NonNull Task task) { @ReactMethod public void signOut(final Callback callback) { FirebaseAuth.getInstance().signOut(); - user = null; + this.user = null; WritableMap resp = Arguments.createMap(); resp.putString("status", "complete"); @@ -368,11 +368,11 @@ public void signOut(final Callback callback) { public void getCurrentUser(final Callback callback) { mAuth = FirebaseAuth.getInstance(); - user = mAuth.getCurrentUser(); - if(user == null){ + this.user = mAuth.getCurrentUser(); + if(this.user == null){ noUserCallback(callback); }else{ - userCallback(user, callback); + userCallback(this.user, callback); } } @@ -387,8 +387,8 @@ public void googleLogin(String IdToken, final Callback callback) { @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - user = task.getResult().getUser(); - userCallback(user, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); }else{ userErrorCallback(task, callback); } @@ -406,8 +406,8 @@ public void facebookLogin(String Token, final Callback callback) { @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - user = task.getResult().getUser(); - userCallback(user, callback); + FirestackAuthModule.this.user = task.getResult().getUser(); + userCallback(FirestackAuthModule.this.user, callback); }else{ userErrorCallback(task, callback); } @@ -417,26 +417,24 @@ public void onComplete(@NonNull Task task) { // Internal helpers public void userCallback(FirebaseUser passedUser, final Callback onComplete) { - WritableMap userMap = getUserMap(); if (passedUser == null) { mAuth = FirebaseAuth.getInstance(); - final FirebaseUser user = mAuth.getCurrentUser(); + this.user = mAuth.getCurrentUser(); } else { - final FirebaseUser user = passedUser; + this.user = passedUser; } - user.getToken(true).addOnCompleteListener(new OnCompleteListener() { + this.user.getToken(true).addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { - WritableMap msgMap = Arguments.createMap(); - WritableMap userMap = Arguments.createMap(); - - if (user != null) { - final String token = task.getResult().getToken(); + WritableMap msgMap = Arguments.createMap(); + WritableMap userMap = getUserMap(); + if (FirestackAuthModule.this.user != null) { + final String token = task.getResult().getToken(); - userMap.putString("token", token); - userMap.putBoolean("anonymous", false); + userMap.putString("token", token); + userMap.putBoolean("anonymous", false); } msgMap.putMap("user", userMap); @@ -448,26 +446,25 @@ public void onComplete(@NonNull Task task) { // TODO: Reduce to one method public void anonymousUserCallback(FirebaseUser passedUser, final Callback onComplete) { - WritableMap userMap = getUserMap(); if (passedUser == null) { mAuth = FirebaseAuth.getInstance(); - final FirebaseUser user = mAuth.getCurrentUser(); + this.user = mAuth.getCurrentUser(); } else { - final FirebaseUser user = passedUser; + this.user = passedUser; } - user.getToken(true).addOnCompleteListener(new OnCompleteListener() { + this.user.getToken(true).addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { - WritableMap msgMap = Arguments.createMap(); - WritableMap userMap = Arguments.createMap(); + WritableMap msgMap = Arguments.createMap(); + WritableMap userMap = getUserMap(); - if (user != null) { - final String token = task.getResult().getToken(); + if (FirestackAuthModule.this.user != null) { + final String token = task.getResult().getToken(); - userMap.putString("token", token); - userMap.putBoolean("anonymous", true); + userMap.putString("token", token); + userMap.putBoolean("anonymous", true); } msgMap.putMap("user", userMap); diff --git a/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java b/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java index 78f45c7..61cda5d 100644 --- a/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java +++ b/android/src/main/java/io/fullstack/firestack/FirestackDatabase.java @@ -3,16 +3,11 @@ import android.content.Context; import android.util.Log; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import java.util.ArrayList; import java.util.Map; import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; @@ -24,11 +19,6 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReactContext; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.OnFailureListener; -import com.google.android.gms.tasks.Task; -import com.google.firebase.FirebaseApp; - import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.ChildEventListener; @@ -62,33 +52,35 @@ public void setModifiers(final ReadableArray modifiers) { public void addChildEventListener(final String name, final ReadableArray modifiers) { final FirestackDBReference self = this; - mEventListener = new ChildEventListener() { - @Override - public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { - self.handleDatabaseEvent(name, mPath, dataSnapshot); - } + if (mEventListener == null) { + mEventListener = new ChildEventListener() { + @Override + public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { + self.handleDatabaseEvent("child_added", mPath, dataSnapshot); + } - @Override - public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) { - self.handleDatabaseEvent(name, mPath, dataSnapshot); - } + @Override + public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) { + self.handleDatabaseEvent("child_changed", mPath, dataSnapshot); + } - @Override - public void onChildRemoved(DataSnapshot dataSnapshot) { - self.handleDatabaseEvent(name, mPath, dataSnapshot); - } + @Override + public void onChildRemoved(DataSnapshot dataSnapshot) { + self.handleDatabaseEvent("child_removed", mPath, dataSnapshot); + } - @Override - public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) { - self.handleDatabaseEvent(name, mPath, dataSnapshot); - } + @Override + public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) { + self.handleDatabaseEvent("child_moved", mPath, dataSnapshot); + } + + @Override + public void onCancelled(DatabaseError error) { + self.handleDatabaseError(name, mPath, error); + } + }; + } - @Override - public void onCancelled(DatabaseError error) { - self.handleDatabaseError(name, mPath, error); - } - }; - Query ref = this.getDatabaseQueryAtPathAndModifiers(modifiers); ref.addChildEventListener(mEventListener); this.setListeningTo(mPath, name); @@ -144,6 +136,9 @@ public Boolean isListeningTo(final String path, final String evtName) { return mListeners.containsKey(key); } + /** + * Note: these path/eventType listeners only get removed when javascript calls .off() and cleanup is run on the entire path + */ public void setListeningTo(final String path, final String evtName) { String key = this.pathListeningKey(path, evtName); mListeners.put(key, true); @@ -177,17 +172,24 @@ public void removeChildEventListener() { } public void removeValueEventListener() { + DatabaseReference ref = this.getDatabaseRef(); if (mValueListener != null) { - DatabaseReference ref = this.getDatabaseRef(); ref.removeEventListener(mValueListener); this.notListeningTo(mPath, "value"); mValueListener = null; } + if (mOnceValueListener != null) { + ref.removeEventListener(mOnceValueListener); + mOnceValueListener = null; + } } private void handleDatabaseEvent(final String name, final String path, final DataSnapshot dataSnapshot) { + if (!FirestackDBReference.this.isListeningTo(path, name)) { + return; + } WritableMap data = FirestackUtils.dataSnapshotToMap(name, path, dataSnapshot); - WritableMap evt = Arguments.createMap(); + WritableMap evt = Arguments.createMap(); evt.putString("eventName", name); evt.putString("path", path); evt.putMap("body", data); @@ -430,7 +432,7 @@ public void on(final String path, final ReadableArray modifiers, final String name, final Callback callback) { - FirestackDBReference ref = this.getDBHandle(path, name); + FirestackDBReference ref = this.getDBHandle(path); WritableMap resp = Arguments.createMap(); @@ -440,7 +442,7 @@ public void on(final String path, ref.addChildEventListener(name, modifiers); } - this.saveDBHandle(path, name, ref); + this.saveDBHandle(path, ref); resp.putString("result", "success"); Log.d(TAG, "Added listener " + name + " for " + ref); @@ -454,23 +456,20 @@ public void onOnce(final String path, final String name, final Callback callback) { Log.d(TAG, "Setting one-time listener on event: " + name + " for path " + path); - FirestackDBReference ref = this.getDBHandle(path, "once"); + FirestackDBReference ref = this.getDBHandle(path); ref.addOnceValueEventListener(modifiers, callback); } + /** + * At the time of this writing, off() only gets called when there are no more subscribers to a given path. + * `mListeners` might therefore be out of sync (though javascript isnt listening for those eventTypes, so + * it doesn't really matter- just polluting the RN bridge a little more than necessary. + * off() should therefore clean *everything* up + */ @ReactMethod - public void off(final String path, final String name, final Callback callback) { - // TODO - FirestackDBReference ref = this.getDBHandle(path, name); - - if (name.equals("value")) { - ref.removeValueEventListener(); - } else { - ref.removeChildEventListener(); - } - - this.removeDBHandle(path, name); - Log.d(TAG, "Removed listener " + name); + public void off(final String path, @Deprecated final String name, final Callback callback) { + this.removeDBHandle(path); + Log.d(TAG, "Removed listener " + path); WritableMap resp = Arguments.createMap(); resp.putString("handle", path); resp.putString("result", "success"); @@ -570,29 +569,24 @@ private void handleCallback( } } - private FirestackDBReference getDBHandle(final String path, final String eventName) { - String key = this.keyPath(path, eventName); - if (!mDBListeners.containsKey(key)) { + private FirestackDBReference getDBHandle(final String path) { + if (!mDBListeners.containsKey(path)) { ReactContext ctx = getReactApplicationContext(); - mDBListeners.put(key, new FirestackDBReference(ctx, path)); + mDBListeners.put(path, new FirestackDBReference(ctx, path)); } - return mDBListeners.get(key); + return mDBListeners.get(path); } - private void saveDBHandle(final String path, - final String eventName, - final FirestackDBReference dbRef) { - String key = this.keyPath(path, eventName); - this.removeDBHandle(key, eventName); - mDBListeners.put(key, dbRef); + private void saveDBHandle(final String path, final FirestackDBReference dbRef) { + mDBListeners.put(path, dbRef); } - private void removeDBHandle(final String path, final String eventName) { - String key = this.keyPath(path, eventName); - if (mDBListeners.containsKey(key)) { - FirestackDBReference r = mDBListeners.get(key); + private void removeDBHandle(final String path) { + if (mDBListeners.containsKey(path)) { + FirestackDBReference r = mDBListeners.get(path); r.cleanup(); + mDBListeners.remove(path); } }