mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-23 20:10:05 +08:00
[database][wip] refactor & improvements to add support for multiple apps
This commit is contained in:
@@ -77,15 +77,14 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param refId
|
||||
* @param listenerId
|
||||
* @param path
|
||||
* @param dataSnapshot
|
||||
* @param refId
|
||||
* @param listenerId
|
||||
* @return
|
||||
*/
|
||||
public static WritableMap snapshotToMap(String name, int refId, Integer listenerId, String path, DataSnapshot dataSnapshot, @Nullable String previousChildName) {
|
||||
public static WritableMap snapshotToMap(String name, String path, DataSnapshot dataSnapshot, @Nullable String previousChildName, int refId, int listenerId) {
|
||||
WritableMap snapshot = Arguments.createMap();
|
||||
WritableMap eventMap = Arguments.createMap();
|
||||
|
||||
@@ -109,19 +108,16 @@ public class Utils {
|
||||
mapPutValue("priority", dataSnapshot.getPriority(), snapshot);
|
||||
|
||||
eventMap.putInt("refId", refId);
|
||||
if (listenerId != null) {
|
||||
eventMap.putInt("listenerId", listenerId);
|
||||
}
|
||||
eventMap.putString("path", path);
|
||||
eventMap.putMap("snapshot", snapshot);
|
||||
eventMap.putString("eventName", name);
|
||||
eventMap.putInt("listenerId", listenerId);
|
||||
eventMap.putString("previousChildName", previousChildName);
|
||||
|
||||
return eventMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dataSnapshot
|
||||
* @return
|
||||
*/
|
||||
@@ -151,7 +147,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param snapshot
|
||||
* @param <Any>
|
||||
* @return
|
||||
@@ -182,7 +177,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mutableData
|
||||
* @param <Any>
|
||||
* @return
|
||||
@@ -216,7 +210,7 @@ public class Utils {
|
||||
* Data should be treated as an array if:
|
||||
* 1) All the keys are integers
|
||||
* 2) More than half the keys between 0 and the maximum key in the object have non-empty values
|
||||
*
|
||||
* <p>
|
||||
* Definition from: https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html
|
||||
*
|
||||
* @param snapshot
|
||||
@@ -244,7 +238,7 @@ public class Utils {
|
||||
* Data should be treated as an array if:
|
||||
* 1) All the keys are integers
|
||||
* 2) More than half the keys between 0 and the maximum key in the object have non-empty values
|
||||
*
|
||||
* <p>
|
||||
* Definition from: https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html
|
||||
*
|
||||
* @param mutableData
|
||||
@@ -269,7 +263,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param snapshot
|
||||
* @param <Any>
|
||||
* @return
|
||||
@@ -316,7 +309,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mutableData
|
||||
* @param <Any>
|
||||
* @return
|
||||
@@ -363,7 +355,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param snapshot
|
||||
* @param <Any>
|
||||
* @return
|
||||
@@ -401,7 +392,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mutableData
|
||||
* @param <Any>
|
||||
* @return
|
||||
@@ -439,7 +429,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param snapshot
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -1,252 +1,124 @@
|
||||
package io.invertase.firebase.database;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
|
||||
import com.facebook.react.bridge.WritableNativeArray;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.database.MutableData;
|
||||
import com.google.firebase.database.ServerValue;
|
||||
import com.google.firebase.database.OnDisconnect;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.google.firebase.database.DatabaseException;
|
||||
import com.google.firebase.database.ServerValue;
|
||||
import com.google.firebase.database.Transaction;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
|
||||
public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "RNFirebaseDatabase";
|
||||
private HashMap<Integer, RNFirebaseDatabaseReference> mReferences = new HashMap<>();
|
||||
private HashMap<String, RNFirebaseTransactionHandler> mTransactionHandlers = new HashMap<>();
|
||||
private FirebaseDatabase mFirebaseDatabase;
|
||||
private SparseArray<RNFirebaseDatabaseReference> references = new SparseArray<>();
|
||||
private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
|
||||
|
||||
public RNFirebaseDatabase(ReactApplicationContext reactContext) {
|
||||
RNFirebaseDatabase(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
mFirebaseDatabase = FirebaseDatabase.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TAG;
|
||||
}
|
||||
/*
|
||||
* REACT NATIVE METHODS
|
||||
*/
|
||||
|
||||
// Persistence
|
||||
/**
|
||||
* @param appName
|
||||
*/
|
||||
@ReactMethod
|
||||
public void enablePersistence(
|
||||
final Boolean enable,
|
||||
final Callback callback) {
|
||||
try {
|
||||
mFirebaseDatabase.setPersistenceEnabled(enable);
|
||||
} catch (DatabaseException t) {
|
||||
|
||||
}
|
||||
|
||||
WritableMap res = Arguments.createMap();
|
||||
res.putString("status", "success");
|
||||
callback.invoke(null, res);
|
||||
public void goOnline(String appName) {
|
||||
getDatabaseForApp(appName).goOnline();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
*/
|
||||
@ReactMethod
|
||||
public void keepSynced(
|
||||
final String path,
|
||||
final Boolean enable,
|
||||
final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
ref.keepSynced(enable);
|
||||
|
||||
WritableMap res = Arguments.createMap();
|
||||
res.putString("status", "success");
|
||||
res.putString("path", path);
|
||||
callback.invoke(null, res);
|
||||
public void goOffline(String appName) {
|
||||
getDatabaseForApp(appName).goOffline();
|
||||
}
|
||||
|
||||
// RNFirebaseDatabase
|
||||
/**
|
||||
* @param appName
|
||||
* @param state
|
||||
*/
|
||||
@ReactMethod
|
||||
public void set(
|
||||
final String path,
|
||||
final ReadableMap props,
|
||||
final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(props);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("set", callback, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.setValue(m.get("value"), listener);
|
||||
public void setPersistence(String appName, Boolean state) {
|
||||
getDatabaseForApp(appName).setPersistenceEnabled(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param state
|
||||
*/
|
||||
@ReactMethod
|
||||
public void priority(
|
||||
final String path,
|
||||
final ReadableMap priority,
|
||||
final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
Map<String, Object> priorityMap = Utils.recursivelyDeconstructReadableMap(priority);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("priority", callback, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.setPriority(priorityMap.get("value"), listener);
|
||||
public void keepSynced(String appName, String path, Boolean state) {
|
||||
getReferenceForAppPath(appName, path).keepSynced(state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TRANSACTIONS
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param transactionId
|
||||
* @param updates
|
||||
*/
|
||||
@ReactMethod
|
||||
public void withPriority(
|
||||
final String path,
|
||||
final ReadableMap data,
|
||||
final ReadableMap priority,
|
||||
final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
Map<String, Object> dataMap = Utils.recursivelyDeconstructReadableMap(data);
|
||||
Map<String, Object> priorityMap = Utils.recursivelyDeconstructReadableMap(priority);
|
||||
public void transactionTryCommit(String appName, int transactionId, ReadableMap updates) {
|
||||
RNFirebaseTransactionHandler handler = transactionHandlers.get(transactionId);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("withPriority", callback, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.setValue(dataMap.get("value"), priorityMap.get("value"), listener);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void update(final String path,
|
||||
final ReadableMap props,
|
||||
final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(props);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("update", callback, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.updateChildren(m, listener);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void remove(final String path,
|
||||
final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("remove", callback, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.removeValue(listener);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void push(final String path,
|
||||
final ReadableMap props,
|
||||
final Callback callback) {
|
||||
|
||||
Log.d(TAG, "Called push with " + path);
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
DatabaseReference newRef = ref.push();
|
||||
|
||||
final Uri url = Uri.parse(newRef.toString());
|
||||
final String newPath = url.getPath();
|
||||
|
||||
ReadableMapKeySetIterator iterator = props.keySetIterator();
|
||||
if (iterator.hasNextKey()) {
|
||||
Log.d(TAG, "Passed value to push");
|
||||
// lame way to check if the `props` are empty
|
||||
Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(props);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
if (error != null) {
|
||||
WritableMap err = Arguments.createMap();
|
||||
err.putInt("code", error.getCode());
|
||||
err.putString("details", error.getDetails());
|
||||
err.putString("description", error.getMessage());
|
||||
callback.invoke(err);
|
||||
} else {
|
||||
WritableMap res = Arguments.createMap();
|
||||
res.putString("status", "success");
|
||||
res.putString("ref", newPath);
|
||||
callback.invoke(null, res);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
newRef.setValue(m.get("value"), listener);
|
||||
} else {
|
||||
Log.d(TAG, "No value passed to push: " + newPath);
|
||||
WritableMap res = Arguments.createMap();
|
||||
res.putString("status", "success");
|
||||
res.putString("ref", newPath);
|
||||
callback.invoke(null, res);
|
||||
if (handler != null) {
|
||||
handler.signalUpdateReceived(updates);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a native transaction and store it's state in
|
||||
*
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param id
|
||||
* @param transactionId
|
||||
* @param applyLocally
|
||||
*/
|
||||
@ReactMethod
|
||||
public void startTransaction(final String path, final String id, final Boolean applyLocally) {
|
||||
public void transactionStart(final String appName, final String path, final int transactionId, final Boolean applyLocally) {
|
||||
AsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DatabaseReference transactionRef = FirebaseDatabase.getInstance().getReference(path);
|
||||
DatabaseReference reference = getReferenceForAppPath(appName, path);
|
||||
|
||||
transactionRef.runTransaction(new Transaction.Handler() {
|
||||
reference.runTransaction(new Transaction.Handler() {
|
||||
@Override
|
||||
public Transaction.Result doTransaction(MutableData mutableData) {
|
||||
final WritableMap updatesMap = Arguments.createMap();
|
||||
|
||||
updatesMap.putString("id", id);
|
||||
updatesMap.putString("type", "update");
|
||||
|
||||
if (!mutableData.hasChildren()) {
|
||||
Utils.mapPutValue("value", mutableData.getValue(), updatesMap);
|
||||
} else {
|
||||
Object value = Utils.castValue(mutableData);
|
||||
if (value instanceof WritableNativeArray) {
|
||||
updatesMap.putArray("value", (WritableArray) value);
|
||||
} else {
|
||||
updatesMap.putMap("value", (WritableMap) value);
|
||||
}
|
||||
}
|
||||
|
||||
RNFirebaseTransactionHandler rnFirebaseTransactionHandler = new RNFirebaseTransactionHandler();
|
||||
mTransactionHandlers.put(id, rnFirebaseTransactionHandler);
|
||||
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(transactionId, appName);
|
||||
transactionHandlers.put(transactionId, transactionHandler);
|
||||
final WritableMap updatesMap = transactionHandler.createUpdateMap(mutableData);
|
||||
|
||||
// emit the updates to js using an async task
|
||||
// otherwise it gets blocked by the lock await
|
||||
AsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -254,245 +126,305 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||
}
|
||||
});
|
||||
|
||||
// wait for js to return the updates (js calls transactionTryCommit)
|
||||
try {
|
||||
rnFirebaseTransactionHandler.await();
|
||||
transactionHandler.await();
|
||||
} catch (InterruptedException e) {
|
||||
rnFirebaseTransactionHandler.interrupted = true;
|
||||
transactionHandler.interrupted = true;
|
||||
return Transaction.abort();
|
||||
}
|
||||
|
||||
if (rnFirebaseTransactionHandler.abort) {
|
||||
if (transactionHandler.abort) {
|
||||
return Transaction.abort();
|
||||
}
|
||||
|
||||
mutableData.setValue(rnFirebaseTransactionHandler.value);
|
||||
mutableData.setValue(transactionHandler.value);
|
||||
return Transaction.success(mutableData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot dataSnapshot) {
|
||||
final WritableMap updatesMap = Arguments.createMap();
|
||||
updatesMap.putString("id", id);
|
||||
|
||||
RNFirebaseTransactionHandler rnFirebaseTransactionHandler = mTransactionHandlers.get(id);
|
||||
|
||||
// TODO error conversion util for database to create web sdk codes based on DatabaseError
|
||||
if (databaseError != null) {
|
||||
updatesMap.putString("type", "error");
|
||||
|
||||
updatesMap.putInt("code", databaseError.getCode());
|
||||
updatesMap.putString("message", databaseError.getMessage());
|
||||
} else if (rnFirebaseTransactionHandler.interrupted) {
|
||||
updatesMap.putString("type", "error");
|
||||
|
||||
updatesMap.putInt("code", 666);
|
||||
updatesMap.putString("message", "RNFirebase transaction was interrupted, aborting.");
|
||||
} else {
|
||||
updatesMap.putString("type", "complete");
|
||||
updatesMap.putBoolean("committed", committed);
|
||||
updatesMap.putMap("snapshot", Utils.snapshotToMap(dataSnapshot));
|
||||
}
|
||||
|
||||
Utils.sendEvent(getReactApplicationContext(), "database_transaction_event", updatesMap);
|
||||
mTransactionHandlers.remove(id);
|
||||
public void onComplete(DatabaseError error, boolean committed, DataSnapshot snapshot) {
|
||||
RNFirebaseTransactionHandler transactionHandler = transactionHandlers.get(transactionId);
|
||||
WritableMap resultMap = transactionHandler.createResultMap(error, committed, snapshot);
|
||||
Utils.sendEvent(getReactApplicationContext(), "database_transaction_event", resultMap);
|
||||
transactionHandlers.delete(transactionId);
|
||||
}
|
||||
|
||||
}, applyLocally);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ON DISCONNECT
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set a value on a ref when the client disconnects from the firebase server.
|
||||
*
|
||||
* @param id
|
||||
* @param updates
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param props
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void tryCommitTransaction(final String id, final ReadableMap updates) {
|
||||
Map<String, Object> updatesReturned = Utils.recursivelyDeconstructReadableMap(updates);
|
||||
RNFirebaseTransactionHandler rnFirebaseTransactionHandler = mTransactionHandlers.get(id);
|
||||
|
||||
if (rnFirebaseTransactionHandler != null) {
|
||||
rnFirebaseTransactionHandler.signalUpdateReceived(updatesReturned);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void on(final int refId, final String path, final ReadableArray modifiers, final int listenerId, final String eventName, final Callback callback) {
|
||||
RNFirebaseDatabaseReference ref = this.getDBHandle(refId, path, modifiers);
|
||||
|
||||
if (eventName.equals("value")) {
|
||||
ref.addValueEventListener(listenerId);
|
||||
} else {
|
||||
ref.addChildEventListener(listenerId, eventName);
|
||||
}
|
||||
|
||||
WritableMap resp = Arguments.createMap();
|
||||
resp.putString("status", "success");
|
||||
resp.putInt("refId", refId);
|
||||
resp.putString("handle", path);
|
||||
callback.invoke(null, resp);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void once(final int refId, final String path, final ReadableArray modifiers, final String eventName, final Callback callback) {
|
||||
RNFirebaseDatabaseReference ref = this.getDBHandle(refId, path, modifiers);
|
||||
|
||||
if (eventName.equals("value")) {
|
||||
ref.addOnceValueEventListener(callback);
|
||||
} else {
|
||||
ref.addChildOnceEventListener(eventName, 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 eventNames, 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 int refId,
|
||||
final ReadableArray listeners,
|
||||
final Callback callback) {
|
||||
|
||||
RNFirebaseDatabaseReference r = mReferences.get(refId);
|
||||
|
||||
if (r != null) {
|
||||
List<Object> listenersList = Utils.recursivelyDeconstructReadableArray(listeners);
|
||||
|
||||
for (Object l : listenersList) {
|
||||
Map<String, Object> listener = (Map) l;
|
||||
int listenerId = ((Double) listener.get("listenerId")).intValue();
|
||||
String eventName = (String) listener.get("eventName");
|
||||
r.removeEventListener(listenerId, eventName);
|
||||
if (!r.hasListeners()) {
|
||||
mReferences.remove(refId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Removed listeners refId: " + refId + " ; count: " + listeners.size());
|
||||
WritableMap resp = Arguments.createMap();
|
||||
resp.putInt("refId", refId);
|
||||
resp.putString("status", "success");
|
||||
callback.invoke(null, resp);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void onDisconnectSet(final String path, final ReadableMap props, final Callback callback) {
|
||||
public void onDisconnectSet(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
String type = props.getString("type");
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
OnDisconnect od = ref.onDisconnect();
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
|
||||
OnDisconnect onDisconnect = ref.onDisconnect();
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("onDisconnectSet", callback, error);
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case "object":
|
||||
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(props.getMap("value"));
|
||||
od.setValue(map, listener);
|
||||
onDisconnect.setValue(map, listener);
|
||||
break;
|
||||
case "array":
|
||||
List<Object> list = Utils.recursivelyDeconstructReadableArray(props.getArray("value"));
|
||||
od.setValue(list, listener);
|
||||
onDisconnect.setValue(list, listener);
|
||||
break;
|
||||
case "string":
|
||||
od.setValue(props.getString("value"), listener);
|
||||
onDisconnect.setValue(props.getString("value"), listener);
|
||||
break;
|
||||
case "number":
|
||||
od.setValue(props.getDouble("value"), listener);
|
||||
onDisconnect.setValue(props.getDouble("value"), listener);
|
||||
break;
|
||||
case "boolean":
|
||||
od.setValue(props.getBoolean("value"), listener);
|
||||
onDisconnect.setValue(props.getBoolean("value"), listener);
|
||||
break;
|
||||
case "null":
|
||||
od.setValue(null, listener);
|
||||
onDisconnect.setValue(null, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a value on a ref when the client disconnects from the firebase server.
|
||||
*
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param props
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectUpdate(final String path, final ReadableMap props, final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
OnDisconnect od = ref.onDisconnect();
|
||||
public void onDisconnectUpdate(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
OnDisconnect ondDisconnect = ref.onDisconnect();
|
||||
|
||||
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(props);
|
||||
od.updateChildren(map, new DatabaseReference.CompletionListener() {
|
||||
|
||||
ondDisconnect.updateChildren(map, new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("onDisconnectUpdate", callback, error);
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a ref when the client disconnects from the firebase server.
|
||||
*
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectRemove(final String path, final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
public void onDisconnectRemove(String appName, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
OnDisconnect onDisconnect = ref.onDisconnect();
|
||||
|
||||
OnDisconnect od = ref.onDisconnect();
|
||||
od.removeValue(new DatabaseReference.CompletionListener() {
|
||||
onDisconnect.removeValue(new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("onDisconnectRemove", callback, error);
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a pending onDisconnect action.
|
||||
*
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void onDisconnectCancel(final String path, final Callback callback) {
|
||||
DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
public void onDisconnectCancel(String appName, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
OnDisconnect onDisconnect = ref.onDisconnect();
|
||||
|
||||
OnDisconnect od = ref.onDisconnect();
|
||||
od.cancel(new DatabaseReference.CompletionListener() {
|
||||
onDisconnect.cancel(new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handleCallback("onDisconnectCancel", callback, error);
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param props
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void goOnline() {
|
||||
mFirebaseDatabase.goOnline();
|
||||
public void set(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
Object value = Utils.recursivelyDeconstructReadableMap(props).get("value");
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.setValue(value, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param priority
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void goOffline() {
|
||||
mFirebaseDatabase.goOffline();
|
||||
public void setPriority(String appName, String path, ReadableMap priority, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
Object priorityValue = Utils.recursivelyDeconstructReadableMap(priority).get("value");
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.setPriority(priorityValue, listener);
|
||||
}
|
||||
|
||||
private void handleCallback(
|
||||
final String methodName,
|
||||
final Callback callback,
|
||||
final DatabaseError databaseError) {
|
||||
if (databaseError != null) {
|
||||
WritableMap err = Arguments.createMap();
|
||||
err.putInt("code", databaseError.getCode());
|
||||
err.putString("details", databaseError.getDetails());
|
||||
err.putString("description", databaseError.getMessage());
|
||||
callback.invoke(err);
|
||||
/**
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param data
|
||||
* @param priority
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setWithPriority(String appName, String path, ReadableMap data, ReadableMap priority, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
Object dataValue = Utils.recursivelyDeconstructReadableMap(data).get("value");
|
||||
Object priorityValue = Utils.recursivelyDeconstructReadableMap(priority).get("value");
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.setValue(dataValue, priorityValue, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param props
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void update(String appName, String path, ReadableMap props, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
Map<String, Object> updates = Utils.recursivelyDeconstructReadableMap(props);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.updateChildren(updates, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param path
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void remove(String appName, String path, final Promise promise) {
|
||||
DatabaseReference ref = getReferenceForAppPath(appName, path);
|
||||
|
||||
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
@Override
|
||||
public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
ref.removeValue(listener);
|
||||
}
|
||||
|
||||
// Push no longer required, handled in JS now.
|
||||
|
||||
/**
|
||||
* Subcribe once to a firebase reference.
|
||||
*
|
||||
* @param appName
|
||||
* @param refId
|
||||
* @param path
|
||||
* @param modifiers
|
||||
* @param eventName
|
||||
* @param promise
|
||||
*/
|
||||
@ReactMethod
|
||||
public void once(String appName, int refId, String path, ReadableArray modifiers, String eventName, Promise promise) {
|
||||
RNFirebaseDatabaseReference internalRef = getInternalReferenceForApp(appName, refId, path, modifiers, false);
|
||||
|
||||
if (eventName.equals("value")) {
|
||||
internalRef.addOnceValueEventListener(promise);
|
||||
} else {
|
||||
WritableMap res = Arguments.createMap();
|
||||
res.putString("status", "success");
|
||||
res.putString("method", methodName);
|
||||
callback.invoke(null, res);
|
||||
internalRef.addChildOnceEventListener(eventName, promise);
|
||||
}
|
||||
}
|
||||
|
||||
private RNFirebaseDatabaseReference getDBHandle(final int refId, final String path,
|
||||
final ReadableArray modifiers) {
|
||||
RNFirebaseDatabaseReference r = mReferences.get(refId);
|
||||
|
||||
if (r == null) {
|
||||
ReactContext ctx = getReactApplicationContext();
|
||||
r = new RNFirebaseDatabaseReference(ctx, mFirebaseDatabase, refId, path, modifiers);
|
||||
mReferences.put(refId, r);
|
||||
|
||||
|
||||
/*
|
||||
* INTERNALS/UTILS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolve null or reject with a js like error if databaseError exists
|
||||
*
|
||||
* @param promise
|
||||
* @param databaseError
|
||||
*/
|
||||
static void handlePromise(Promise promise, DatabaseError databaseError) {
|
||||
if (databaseError != null) {
|
||||
WritableMap jsError = getJSError(databaseError);
|
||||
promise.reject(
|
||||
jsError.getString("code"),
|
||||
jsError.getString("message"),
|
||||
databaseError.toException()
|
||||
);
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNFirebaseDatabase";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -501,4 +433,130 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||
constants.put("serverValueTimestamp", ServerValue.TIMESTAMP);
|
||||
return constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a database instance for a specific firebase app instance
|
||||
*
|
||||
* @param appName
|
||||
* @return
|
||||
*/
|
||||
static FirebaseDatabase getDatabaseForApp(String appName) {
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
return FirebaseDatabase.getInstance(firebaseApp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a database reference for a specific app and path
|
||||
*
|
||||
* @param appName
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
private DatabaseReference getReferenceForAppPath(String appName, String path) {
|
||||
return getDatabaseForApp(appName).getReference(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appName
|
||||
* @param refId
|
||||
* @param path
|
||||
* @param modifiers
|
||||
* @param keep
|
||||
* @return
|
||||
*/
|
||||
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, int refId, String path, ReadableArray modifiers, Boolean keep) {
|
||||
RNFirebaseDatabaseReference existingRef = references.get(refId);
|
||||
|
||||
if (existingRef == null) {
|
||||
existingRef = new RNFirebaseDatabaseReference(
|
||||
getReactApplicationContext(),
|
||||
appName,
|
||||
refId,
|
||||
path,
|
||||
modifiers
|
||||
);
|
||||
|
||||
if (keep) references.put(refId, existingRef);
|
||||
}
|
||||
|
||||
return existingRef;
|
||||
}
|
||||
|
||||
// todo move to error util for use in other modules
|
||||
static String getMessageWithService(String message, String service, String fullCode) {
|
||||
// Service: Error message (service/code).
|
||||
return service + ": " + message + " (" + fullCode.toLowerCase() + ").";
|
||||
}
|
||||
|
||||
static String getCodeWithService(String service, String code) {
|
||||
return service.toUpperCase() + "/" + code.toUpperCase();
|
||||
}
|
||||
|
||||
static WritableMap getJSError(DatabaseError nativeError) {
|
||||
WritableMap errorMap = Arguments.createMap();
|
||||
errorMap.putInt("nativeErrorCode", nativeError.getCode());
|
||||
errorMap.putString("nativeErrorMessage", nativeError.getMessage());
|
||||
|
||||
String code;
|
||||
String message;
|
||||
String service = "Database";
|
||||
|
||||
switch (nativeError.getCode()) {
|
||||
case DatabaseError.DATA_STALE:
|
||||
code = getCodeWithService(service, "data-stale");
|
||||
message = getMessageWithService("The transaction needs to be run again with current data.", service, code);
|
||||
break;
|
||||
case DatabaseError.OPERATION_FAILED:
|
||||
code = getCodeWithService(service, "failure");
|
||||
message = getMessageWithService("The server indicated that this operation failed.", service, code);
|
||||
break;
|
||||
case DatabaseError.PERMISSION_DENIED:
|
||||
code = getCodeWithService(service, "permission-denied");
|
||||
message = getMessageWithService("Client doesn't have permission to access the desired data.", service, code);
|
||||
break;
|
||||
case DatabaseError.DISCONNECTED:
|
||||
code = getCodeWithService(service, "disconnected");
|
||||
message = getMessageWithService("The operation had to be aborted due to a network disconnect.", service, code);
|
||||
break;
|
||||
case DatabaseError.EXPIRED_TOKEN:
|
||||
code = getCodeWithService(service, "expired-token");
|
||||
message = getMessageWithService("The supplied auth token has expired.", service, code);
|
||||
break;
|
||||
case DatabaseError.INVALID_TOKEN:
|
||||
code = getCodeWithService(service, "invalid-token");
|
||||
message = getMessageWithService("The supplied auth token was invalid.", service, code);
|
||||
break;
|
||||
case DatabaseError.MAX_RETRIES:
|
||||
code = getCodeWithService(service, "max-retries");
|
||||
message = getMessageWithService("The transaction had too many retries.", service, code);
|
||||
break;
|
||||
case DatabaseError.OVERRIDDEN_BY_SET:
|
||||
code = getCodeWithService(service, "overridden-by-set");
|
||||
message = getMessageWithService("The transaction was overridden by a subsequent set.", service, code);
|
||||
break;
|
||||
case DatabaseError.UNAVAILABLE:
|
||||
code = getCodeWithService(service, "unavailable");
|
||||
message = getMessageWithService("The service is unavailable.", service, code);
|
||||
break;
|
||||
case DatabaseError.USER_CODE_EXCEPTION:
|
||||
code = getCodeWithService(service, "user-code-exception");
|
||||
message = getMessageWithService("User code called from the Firebase Database runloop threw an exception.", service, code);
|
||||
break;
|
||||
case DatabaseError.NETWORK_ERROR:
|
||||
code = getCodeWithService(service, "network-error");
|
||||
message = getMessageWithService("The operation could not be performed due to a network error.", service, code);
|
||||
break;
|
||||
case DatabaseError.WRITE_CANCELED:
|
||||
code = getCodeWithService(service, "write-cancelled");
|
||||
message = getMessageWithService("The write was canceled by the user.", service, code);
|
||||
break;
|
||||
default:
|
||||
code = getCodeWithService(service, "unknown");
|
||||
message = getMessageWithService("An unknown error occurred", service, code);
|
||||
}
|
||||
|
||||
errorMap.putString("code", code);
|
||||
errorMap.putString("message", message);
|
||||
return errorMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
package io.invertase.firebase.database;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
|
||||
import com.facebook.react.bridge.WritableNativeArray;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.database.MutableData;
|
||||
import com.google.firebase.database.ServerValue;
|
||||
import com.google.firebase.database.OnDisconnect;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.google.firebase.database.DatabaseException;
|
||||
import com.google.firebase.database.Transaction;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
public class RNFirebaseDatabaseOld extends ReactContextBaseJavaModule {
|
||||
// private static final String TAG = "RNFirebaseDatabase";
|
||||
// private HashMap<Integer, RNFirebaseDatabaseReference> mReferences = new HashMap<>();
|
||||
// private HashMap<String, RNFirebaseTransactionHandler> mTransactionHandlers = new HashMap<>();
|
||||
// private FirebaseDatabase mFirebaseDatabase;
|
||||
//
|
||||
// public RNFirebaseDatabaseOld(ReactApplicationContext reactContext) {
|
||||
// super(reactContext);
|
||||
// mFirebaseDatabase = FirebaseDatabase.getInstance();
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public String getName() {
|
||||
// return TAG;
|
||||
// }
|
||||
|
||||
// // Persistence
|
||||
// @ReactMethod
|
||||
// public void enablePersistence(
|
||||
// final Boolean enable,
|
||||
// final Callback callback) {
|
||||
// try {
|
||||
// mFirebaseDatabase.setPersistenceEnabled(enable);
|
||||
// } catch (DatabaseException t) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// WritableMap res = Arguments.createMap();
|
||||
// res.putString("status", "success");
|
||||
// callback.invoke(null, res);
|
||||
// }
|
||||
|
||||
// @ReactMethod
|
||||
// public void keepSynced(
|
||||
// final String path,
|
||||
// final Boolean enable,
|
||||
// final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// ref.keepSynced(enable);
|
||||
//
|
||||
// WritableMap res = Arguments.createMap();
|
||||
// res.putString("status", "success");
|
||||
// res.putString("path", path);
|
||||
// callback.invoke(null, res);
|
||||
// }
|
||||
|
||||
// // RNFirebaseDatabase
|
||||
// @ReactMethod
|
||||
// public void set(
|
||||
// final String path,
|
||||
// final ReadableMap props,
|
||||
// final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(props);
|
||||
//
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("set", callback, error);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// ref.setValue(m.get("value"), listener);
|
||||
// }
|
||||
|
||||
// @ReactMethod
|
||||
// public void priority(
|
||||
// final String path,
|
||||
// final ReadableMap priority,
|
||||
// final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// Map<String, Object> priorityMap = Utils.recursivelyDeconstructReadableMap(priority);
|
||||
//
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("priority", callback, error);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// ref.setPriority(priorityMap.get("value"), listener);
|
||||
// }
|
||||
//
|
||||
// @ReactMethod
|
||||
// public void withPriority(
|
||||
// final String path,
|
||||
// final ReadableMap data,
|
||||
// final ReadableMap priority,
|
||||
// final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// Map<String, Object> dataMap = Utils.recursivelyDeconstructReadableMap(data);
|
||||
// Map<String, Object> priorityMap = Utils.recursivelyDeconstructReadableMap(priority);
|
||||
//
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("withPriority", callback, error);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// ref.setValue(dataMap.get("value"), priorityMap.get("value"), listener);
|
||||
// }
|
||||
//
|
||||
// @ReactMethod
|
||||
// public void update(final String path,
|
||||
// final ReadableMap props,
|
||||
// final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(props);
|
||||
//
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("update", callback, error);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// ref.updateChildren(m, listener);
|
||||
// }
|
||||
|
||||
// @ReactMethod
|
||||
// public void remove(final String path,
|
||||
// final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("remove", callback, error);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// ref.removeValue(listener);
|
||||
// }
|
||||
|
||||
// @ReactMethod
|
||||
// public void push(final String path,
|
||||
// final ReadableMap props,
|
||||
// final Callback callback) {
|
||||
//
|
||||
// Log.d(TAG, "Called push with " + path);
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// DatabaseReference newRef = ref.push();
|
||||
//
|
||||
// final Uri url = Uri.parse(newRef.toString());
|
||||
// final String newPath = url.getPath();
|
||||
//
|
||||
// ReadableMapKeySetIterator iterator = props.keySetIterator();
|
||||
// if (iterator.hasNextKey()) {
|
||||
// Log.d(TAG, "Passed value to push");
|
||||
// // lame way to check if the `props` are empty
|
||||
// Map<String, Object> m = Utils.recursivelyDeconstructReadableMap(props);
|
||||
//
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// if (error != null) {
|
||||
// WritableMap err = Arguments.createMap();
|
||||
// err.putInt("code", error.getCode());
|
||||
// err.putString("details", error.getDetails());
|
||||
// err.putString("description", error.getMessage());
|
||||
// callback.invoke(err);
|
||||
// } else {
|
||||
// WritableMap res = Arguments.createMap();
|
||||
// res.putString("status", "success");
|
||||
// res.putString("ref", newPath);
|
||||
// callback.invoke(null, res);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// newRef.setValue(m.get("value"), listener);
|
||||
// } else {
|
||||
// Log.d(TAG, "No value passed to push: " + newPath);
|
||||
// WritableMap res = Arguments.createMap();
|
||||
// res.putString("status", "success");
|
||||
// res.putString("ref", newPath);
|
||||
// callback.invoke(null, res);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @param path
|
||||
// * @param id
|
||||
// * @param applyLocally
|
||||
// */
|
||||
// @ReactMethod
|
||||
// public void startTransaction(final String path, final String id, final Boolean applyLocally) {
|
||||
// AsyncTask.execute(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// DatabaseReference transactionRef = FirebaseDatabase.getInstance().getReference(path);
|
||||
//
|
||||
// transactionRef.runTransaction(new Transaction.Handler() {
|
||||
// @Override
|
||||
// public Transaction.Result doTransaction(MutableData mutableData) {
|
||||
// final WritableMap updatesMap = Arguments.createMap();
|
||||
//
|
||||
// updatesMap.putString("id", id);
|
||||
// updatesMap.putString("type", "update");
|
||||
//
|
||||
// if (!mutableData.hasChildren()) {
|
||||
// Utils.mapPutValue("value", mutableData.getValue(), updatesMap);
|
||||
// } else {
|
||||
// Object value = Utils.castValue(mutableData);
|
||||
// if (value instanceof WritableNativeArray) {
|
||||
// updatesMap.putArray("value", (WritableArray) value);
|
||||
// } else {
|
||||
// updatesMap.putMap("value", (WritableMap) value);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// RNFirebaseTransactionHandler rnFirebaseTransactionHandler = new RNFirebaseTransactionHandler();
|
||||
// mTransactionHandlers.put(id, rnFirebaseTransactionHandler);
|
||||
//
|
||||
// AsyncTask.execute(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// Utils.sendEvent(getReactApplicationContext(), "database_transaction_event", updatesMap);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// try {
|
||||
// rnFirebaseTransactionHandler.await();
|
||||
// } catch (InterruptedException e) {
|
||||
// rnFirebaseTransactionHandler.interrupted = true;
|
||||
// return Transaction.abort();
|
||||
// }
|
||||
//
|
||||
// if (rnFirebaseTransactionHandler.abort) {
|
||||
// return Transaction.abort();
|
||||
// }
|
||||
//
|
||||
// mutableData.setValue(rnFirebaseTransactionHandler.value);
|
||||
// return Transaction.success(mutableData);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot dataSnapshot) {
|
||||
// final WritableMap updatesMap = Arguments.createMap();
|
||||
// updatesMap.putString("id", id);
|
||||
//
|
||||
// RNFirebaseTransactionHandler rnFirebaseTransactionHandler = mTransactionHandlers.get(id);
|
||||
//
|
||||
// // TODO error conversion util for database to create web sdk codes based on DatabaseError
|
||||
// if (databaseError != null) {
|
||||
// updatesMap.putString("type", "error");
|
||||
//
|
||||
// updatesMap.putInt("code", databaseError.getCode());
|
||||
// updatesMap.putString("message", databaseError.getMessage());
|
||||
// } else if (rnFirebaseTransactionHandler.interrupted) {
|
||||
// updatesMap.putString("type", "error");
|
||||
//
|
||||
// updatesMap.putInt("code", 666);
|
||||
// updatesMap.putString("message", "RNFirebase transaction was interrupted, aborting.");
|
||||
// } else {
|
||||
// updatesMap.putString("type", "complete");
|
||||
// updatesMap.putBoolean("committed", committed);
|
||||
// updatesMap.putMap("snapshot", Utils.snapshotToMap(dataSnapshot));
|
||||
// }
|
||||
//
|
||||
// Utils.sendEvent(getReactApplicationContext(), "database_transaction_event", updatesMap);
|
||||
// mTransactionHandlers.remove(id);
|
||||
// }
|
||||
// }, applyLocally);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @param id
|
||||
// * @param updates
|
||||
// */
|
||||
// @ReactMethod
|
||||
// public void tryCommitTransaction(final String id, final ReadableMap updates) {
|
||||
// Map<String, Object> updatesReturned = Utils.recursivelyDeconstructReadableMap(updates);
|
||||
// RNFirebaseTransactionHandler rnFirebaseTransactionHandler = mTransactionHandlers.get(id);
|
||||
//
|
||||
// if (rnFirebaseTransactionHandler != null) {
|
||||
// rnFirebaseTransactionHandler.signalUpdateReceived(updatesReturned);
|
||||
// }
|
||||
// }
|
||||
|
||||
@ReactMethod
|
||||
public void on(final int refId, final String path, final ReadableArray modifiers, final int listenerId, final String eventName, final Callback callback) {
|
||||
RNFirebaseDatabaseReference ref = this.getDBHandle(refId, path, modifiers);
|
||||
|
||||
if (eventName.equals("value")) {
|
||||
ref.addValueEventListener(listenerId);
|
||||
} else {
|
||||
ref.addChildEventListener(listenerId, eventName);
|
||||
}
|
||||
|
||||
WritableMap resp = Arguments.createMap();
|
||||
resp.putString("status", "success");
|
||||
resp.putInt("refId", refId);
|
||||
resp.putString("handle", path);
|
||||
callback.invoke(null, resp);
|
||||
}
|
||||
|
||||
// @ReactMethod
|
||||
// public void once(final int refId, final String path, final ReadableArray modifiers, final String eventName, final Callback callback) {
|
||||
// RNFirebaseDatabaseReference ref = this.getDBHandle(refId, path, modifiers);
|
||||
//
|
||||
// if (eventName.equals("value")) {
|
||||
// ref.addOnceValueEventListener(callback);
|
||||
// } else {
|
||||
// ref.addChildOnceEventListener(eventName, 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 eventNames, 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 int refId,
|
||||
final ReadableArray listeners,
|
||||
final Callback callback) {
|
||||
|
||||
RNFirebaseDatabaseReference r = mReferences.get(refId);
|
||||
|
||||
if (r != null) {
|
||||
List<Object> listenersList = Utils.recursivelyDeconstructReadableArray(listeners);
|
||||
|
||||
for (Object l : listenersList) {
|
||||
Map<String, Object> listener = (Map) l;
|
||||
int listenerId = ((Double) listener.get("listenerId")).intValue();
|
||||
String eventName = (String) listener.get("eventName");
|
||||
r.removeEventListener(listenerId, eventName);
|
||||
if (!r.hasListeners()) {
|
||||
mReferences.remove(refId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Removed listeners refId: " + refId + " ; count: " + listeners.size());
|
||||
WritableMap resp = Arguments.createMap();
|
||||
resp.putInt("refId", refId);
|
||||
resp.putString("status", "success");
|
||||
callback.invoke(null, resp);
|
||||
}
|
||||
|
||||
// @ReactMethod
|
||||
// public void onDisconnectSet(final String path, final ReadableMap props, final Callback callback) {
|
||||
// String type = props.getString("type");
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// OnDisconnect od = ref.onDisconnect();
|
||||
// DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("onDisconnectSet", callback, error);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// switch (type) {
|
||||
// case "object":
|
||||
// Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(props.getMap("value"));
|
||||
// od.setValue(map, listener);
|
||||
// break;
|
||||
// case "array":
|
||||
// List<Object> list = Utils.recursivelyDeconstructReadableArray(props.getArray("value"));
|
||||
// od.setValue(list, listener);
|
||||
// break;
|
||||
// case "string":
|
||||
// od.setValue(props.getString("value"), listener);
|
||||
// break;
|
||||
// case "number":
|
||||
// od.setValue(props.getDouble("value"), listener);
|
||||
// break;
|
||||
// case "boolean":
|
||||
// od.setValue(props.getBoolean("value"), listener);
|
||||
// break;
|
||||
// case "null":
|
||||
// od.setValue(null, listener);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @ReactMethod
|
||||
// public void onDisconnectUpdate(final String path, final ReadableMap props, final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
// OnDisconnect od = ref.onDisconnect();
|
||||
// Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(props);
|
||||
// od.updateChildren(map, new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("onDisconnectUpdate", callback, error);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @ReactMethod
|
||||
// public void onDisconnectRemove(final String path, final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
//
|
||||
// OnDisconnect od = ref.onDisconnect();
|
||||
// od.removeValue(new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("onDisconnectRemove", callback, error);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @ReactMethod
|
||||
// public void onDisconnectCancel(final String path, final Callback callback) {
|
||||
// DatabaseReference ref = mFirebaseDatabase.getReference(path);
|
||||
//
|
||||
// OnDisconnect od = ref.onDisconnect();
|
||||
// od.cancel(new DatabaseReference.CompletionListener() {
|
||||
// @Override
|
||||
// public void onComplete(DatabaseError error, DatabaseReference ref) {
|
||||
// handleCallback("onDisconnectCancel", callback, error);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// @ReactMethod
|
||||
// public void goOnline() {
|
||||
// mFirebaseDatabase.goOnline();
|
||||
// }
|
||||
//
|
||||
// @ReactMethod
|
||||
// public void goOffline() {
|
||||
// mFirebaseDatabase.goOffline();
|
||||
// }
|
||||
|
||||
// private void handleCallback(
|
||||
// final String methodName,
|
||||
// final Callback callback,
|
||||
// final DatabaseError databaseError) {
|
||||
// if (databaseError != null) {
|
||||
// WritableMap err = Arguments.createMap();
|
||||
// err.putInt("code", databaseError.getCode());
|
||||
// err.putString("details", databaseError.getDetails());
|
||||
// err.putString("description", databaseError.getMessage());
|
||||
// callback.invoke(err);
|
||||
// } else {
|
||||
// WritableMap res = Arguments.createMap();
|
||||
// res.putString("status", "success");
|
||||
// res.putString("method", methodName);
|
||||
// callback.invoke(null, res);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private RNFirebaseDatabaseReference getDBHandle(final int refId, final String path,
|
||||
// final ReadableArray modifiers) {
|
||||
// RNFirebaseDatabaseReference r = mReferences.get(refId);
|
||||
//
|
||||
// if (r == null) {
|
||||
// ReactContext ctx = getReactApplicationContext();
|
||||
// r = new RNFirebaseDatabaseReference(ctx, mFirebaseDatabase, refId, path, modifiers);
|
||||
// mReferences.put(refId, r);
|
||||
// }
|
||||
//
|
||||
// return r;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public Map<String, Object> getConstants() {
|
||||
// final Map<String, Object> constants = new HashMap<>();
|
||||
// constants.put("serverValueTimestamp", ServerValue.TIMESTAMP);
|
||||
// return constants;
|
||||
// }
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import android.util.Log;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
@@ -25,28 +25,125 @@ import io.invertase.firebase.Utils;
|
||||
public class RNFirebaseDatabaseReference {
|
||||
private static final String TAG = "RNFirebaseDBReference";
|
||||
|
||||
private int mRefId;
|
||||
private String mPath;
|
||||
private Query mQuery;
|
||||
private ReactContext mReactContext;
|
||||
private SparseArray<ChildEventListener> mChildEventListeners;
|
||||
private SparseArray<ValueEventListener> mValueEventListeners;
|
||||
private int refId;
|
||||
private Query query;
|
||||
private String path;
|
||||
private String appName;
|
||||
private ReactContext reactContext;
|
||||
private SparseArray<ChildEventListener> childEventListeners;
|
||||
private SparseArray<ValueEventListener> valueEventListeners;
|
||||
|
||||
RNFirebaseDatabaseReference(final ReactContext context,
|
||||
final FirebaseDatabase firebaseDatabase,
|
||||
final int refId,
|
||||
final String path,
|
||||
final ReadableArray modifiersArray) {
|
||||
mPath = path;
|
||||
mRefId = refId;
|
||||
mReactContext = context;
|
||||
mChildEventListeners = new SparseArray<ChildEventListener>();
|
||||
mValueEventListeners = new SparseArray<ValueEventListener>();
|
||||
mQuery = this.buildDatabaseQueryAtPathAndModifiers(firebaseDatabase, path, modifiersArray);
|
||||
/**
|
||||
* @param context
|
||||
* @param app
|
||||
* @param id
|
||||
* @param refPath
|
||||
* @param modifiersArray
|
||||
*/
|
||||
RNFirebaseDatabaseReference(ReactContext context, String app, int id, String refPath, ReadableArray modifiersArray) {
|
||||
refId = id;
|
||||
appName = app;
|
||||
path = refPath;
|
||||
reactContext = context;
|
||||
|
||||
// todo only create if needed
|
||||
childEventListeners = new SparseArray<ChildEventListener>();
|
||||
valueEventListeners = new SparseArray<ValueEventListener>();
|
||||
|
||||
query = buildDatabaseQueryAtPathAndModifiers(path, modifiersArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for a single 'value' event from firebase.
|
||||
* @param promise
|
||||
*/
|
||||
void addOnceValueEventListener(final Promise promise) {
|
||||
ValueEventListener onceValueEventListener = new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(DataSnapshot dataSnapshot) {
|
||||
WritableMap data = Utils.snapshotToMap("value", path, dataSnapshot, null, refId, 0);
|
||||
promise.resolve(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(DatabaseError error) {
|
||||
RNFirebaseDatabase.handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
query.addListenerForSingleValueEvent(onceValueEventListener);
|
||||
|
||||
Log.d(TAG, "Added OnceValueEventListener for refId: " + refId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for single 'child_X' event from firebase.
|
||||
* @param eventName
|
||||
* @param promise
|
||||
*/
|
||||
void addChildOnceEventListener(final String eventName, final Promise promise) {
|
||||
ChildEventListener childEventListener = new ChildEventListener() {
|
||||
@Override
|
||||
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
if ("child_added".equals(eventName)) {
|
||||
query.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_added", path, dataSnapshot, previousChildName, refId, 0);
|
||||
promise.resolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
if ("child_changed".equals(eventName)) {
|
||||
query.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_changed", path, dataSnapshot, previousChildName, refId, 0);
|
||||
promise.resolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildRemoved(DataSnapshot dataSnapshot) {
|
||||
if ("child_removed".equals(eventName)) {
|
||||
query.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_removed", path, dataSnapshot, null, refId, 0);
|
||||
promise.resolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
if ("child_moved".equals(eventName)) {
|
||||
query.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_moved", path, dataSnapshot, previousChildName, refId, 0);
|
||||
promise.resolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(DatabaseError error) {
|
||||
query.removeEventListener(this);
|
||||
RNFirebaseDatabase.handlePromise(promise, error);
|
||||
}
|
||||
};
|
||||
|
||||
query.addChildEventListener(childEventListener);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// todo cleanup all below
|
||||
|
||||
void addChildEventListener(final int listenerId, final String eventName) {
|
||||
if (mChildEventListeners.get(listenerId) != null) {
|
||||
if (childEventListeners.get(listenerId) != null) {
|
||||
ChildEventListener childEventListener = new ChildEventListener() {
|
||||
@Override
|
||||
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
@@ -83,16 +180,16 @@ public class RNFirebaseDatabaseReference {
|
||||
}
|
||||
};
|
||||
|
||||
mChildEventListeners.put(listenerId, childEventListener);
|
||||
mQuery.addChildEventListener(childEventListener);
|
||||
Log.d(TAG, "Added ChildEventListener for refId: " + mRefId + " listenerId: " + listenerId);
|
||||
childEventListeners.put(listenerId, childEventListener);
|
||||
query.addChildEventListener(childEventListener);
|
||||
Log.d(TAG, "Added ChildEventListener for refId: " + refId + " listenerId: " + listenerId);
|
||||
} else {
|
||||
Log.d(TAG, "ChildEventListener for refId: " + mRefId + " listenerId: " + listenerId + " already exists");
|
||||
Log.d(TAG, "ChildEventListener for refId: " + refId + " listenerId: " + listenerId + " already exists");
|
||||
}
|
||||
}
|
||||
|
||||
void addValueEventListener(final int listenerId) {
|
||||
if (mValueEventListeners.get(listenerId) != null) {
|
||||
if (valueEventListeners.get(listenerId) != null) {
|
||||
ValueEventListener valueEventListener = new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(DataSnapshot dataSnapshot) {
|
||||
@@ -106,91 +203,15 @@ public class RNFirebaseDatabaseReference {
|
||||
}
|
||||
};
|
||||
|
||||
mValueEventListeners.put(listenerId, valueEventListener);
|
||||
mQuery.addValueEventListener(valueEventListener);
|
||||
Log.d(TAG, "Added ValueEventListener for refId: " + mRefId + " listenerId: " + listenerId);
|
||||
valueEventListeners.put(listenerId, valueEventListener);
|
||||
query.addValueEventListener(valueEventListener);
|
||||
Log.d(TAG, "Added ValueEventListener for refId: " + refId + " listenerId: " + listenerId);
|
||||
} else {
|
||||
Log.d(TAG, "ValueEventListener for refId: " + mRefId + " listenerId: " + listenerId + " already exists");
|
||||
Log.d(TAG, "ValueEventListener for refId: " + refId + " listenerId: " + listenerId + " already exists");
|
||||
}
|
||||
}
|
||||
|
||||
void addOnceValueEventListener(final Callback callback) {
|
||||
final ValueEventListener onceValueEventListener = new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(DataSnapshot dataSnapshot) {
|
||||
WritableMap data = Utils.snapshotToMap("value", mRefId, null, mPath, dataSnapshot, null);
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(DatabaseError error) {
|
||||
WritableMap err = Arguments.createMap();
|
||||
err.putInt("refId", mRefId);
|
||||
err.putString("path", mPath);
|
||||
err.putInt("code", error.getCode());
|
||||
err.putString("details", error.getDetails());
|
||||
err.putString("message", error.getMessage());
|
||||
callback.invoke(err);
|
||||
}
|
||||
};
|
||||
|
||||
mQuery.addListenerForSingleValueEvent(onceValueEventListener);
|
||||
Log.d(TAG, "Added OnceValueEventListener for refId: " + mRefId);
|
||||
}
|
||||
|
||||
void addChildOnceEventListener(final String eventName, final Callback callback) {
|
||||
ChildEventListener childEventListener = new ChildEventListener() {
|
||||
@Override
|
||||
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
if ("child_added".equals(eventName)) {
|
||||
mQuery.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_added", mRefId, null, mPath, dataSnapshot, previousChildName);
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
if ("child_changed".equals(eventName)) {
|
||||
mQuery.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_changed", mRefId, null, mPath, dataSnapshot, previousChildName);
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildRemoved(DataSnapshot dataSnapshot) {
|
||||
if ("child_removed".equals(eventName)) {
|
||||
mQuery.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_removed", mRefId, null, mPath, dataSnapshot, null);
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
|
||||
if ("child_moved".equals(eventName)) {
|
||||
mQuery.removeEventListener(this);
|
||||
WritableMap data = Utils.snapshotToMap("child_moved", mRefId, null, mPath, dataSnapshot, previousChildName);
|
||||
callback.invoke(null, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(DatabaseError error) {
|
||||
mQuery.removeEventListener(this);
|
||||
WritableMap err = Arguments.createMap();
|
||||
err.putInt("refId", mRefId);
|
||||
err.putString("path", mPath);
|
||||
err.putInt("code", error.getCode());
|
||||
err.putString("details", error.getDetails());
|
||||
err.putString("message", error.getMessage());
|
||||
callback.invoke(err);
|
||||
}
|
||||
};
|
||||
|
||||
mQuery.addChildEventListener(childEventListener);
|
||||
}
|
||||
|
||||
void removeEventListener(int listenerId, String eventName) {
|
||||
if ("value".equals(eventName)) {
|
||||
@@ -201,7 +222,7 @@ public class RNFirebaseDatabaseReference {
|
||||
}
|
||||
|
||||
boolean hasListeners() {
|
||||
return mChildEventListeners.size() > 0 || mValueEventListeners.size() > 0;
|
||||
return childEventListeners.size() > 0 || valueEventListeners.size() > 0;
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
@@ -211,51 +232,52 @@ public class RNFirebaseDatabaseReference {
|
||||
}
|
||||
|
||||
private void removeChildEventListener(Integer listenerId) {
|
||||
ChildEventListener listener = mChildEventListeners.get(listenerId);
|
||||
ChildEventListener listener = childEventListeners.get(listenerId);
|
||||
if (listener != null) {
|
||||
mQuery.removeEventListener(listener);
|
||||
mChildEventListeners.delete(listenerId);
|
||||
query.removeEventListener(listener);
|
||||
childEventListeners.delete(listenerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeValueEventListener(Integer listenerId) {
|
||||
ValueEventListener listener = mValueEventListeners.get(listenerId);
|
||||
ValueEventListener listener = valueEventListeners.get(listenerId);
|
||||
if (listener != null) {
|
||||
mQuery.removeEventListener(listener);
|
||||
mValueEventListeners.delete(listenerId);
|
||||
query.removeEventListener(listener);
|
||||
valueEventListeners.delete(listenerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDatabaseEvent(final String name, final Integer listenerId, final DataSnapshot dataSnapshot, @Nullable String previousChildName) {
|
||||
WritableMap data = Utils.snapshotToMap(name, mRefId, listenerId, mPath, dataSnapshot, previousChildName);
|
||||
WritableMap data = Utils.snapshotToMap(name, path, dataSnapshot, previousChildName, refId, listenerId);
|
||||
WritableMap evt = Arguments.createMap();
|
||||
evt.putString("eventName", name);
|
||||
evt.putMap("body", data);
|
||||
|
||||
Utils.sendEvent(mReactContext, "database_event", evt);
|
||||
Utils.sendEvent(reactContext, "database_event", evt);
|
||||
}
|
||||
|
||||
private void handleDatabaseError(final Integer listenerId, final DatabaseError error) {
|
||||
WritableMap errMap = Arguments.createMap();
|
||||
|
||||
errMap.putInt("refId", mRefId);
|
||||
errMap.putInt("refId", refId);
|
||||
if (listenerId != null) {
|
||||
errMap.putInt("listenerId", listenerId);
|
||||
}
|
||||
errMap.putString("path", mPath);
|
||||
errMap.putString("path", path);
|
||||
errMap.putInt("code", error.getCode());
|
||||
errMap.putString("details", error.getDetails());
|
||||
errMap.putString("message", error.getMessage());
|
||||
|
||||
Utils.sendEvent(mReactContext, "database_error", errMap);
|
||||
Utils.sendEvent(reactContext, "database_error", errMap);
|
||||
}
|
||||
|
||||
private Query buildDatabaseQueryAtPathAndModifiers(final FirebaseDatabase firebaseDatabase,
|
||||
final String path,
|
||||
final ReadableArray modifiers) {
|
||||
private Query buildDatabaseQueryAtPathAndModifiers(String path, ReadableArray modifiers) {
|
||||
FirebaseDatabase firebaseDatabase = RNFirebaseDatabase.getDatabaseForApp(appName);
|
||||
|
||||
Query query = firebaseDatabase.getReference(path);
|
||||
List<Object> modifiersList = Utils.recursivelyDeconstructReadableArray(modifiers);
|
||||
|
||||
// todo cleanup into utils
|
||||
for (Object m : modifiersList) {
|
||||
Map<String, Object> modifier = (Map) m;
|
||||
String type = (String) modifier.get("type");
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
package io.invertase.firebase.database;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeArray;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.MutableData;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
public class RNFirebaseTransactionHandler {
|
||||
private int transactionId;
|
||||
private String appName;
|
||||
private final ReentrantLock lock;
|
||||
private final Condition condition;
|
||||
private Map<String, Object> data;
|
||||
private volatile boolean isReady;
|
||||
private boolean signalled;
|
||||
|
||||
public Object value;
|
||||
public boolean interrupted;
|
||||
public boolean abort = false;
|
||||
boolean interrupted;
|
||||
boolean abort = false;
|
||||
boolean timeout = false;
|
||||
|
||||
RNFirebaseTransactionHandler() {
|
||||
RNFirebaseTransactionHandler(int id, String app) {
|
||||
appName = app;
|
||||
transactionId = id;
|
||||
lock = new ReentrantLock();
|
||||
condition = lock.newCondition();
|
||||
}
|
||||
@@ -24,19 +41,22 @@ public class RNFirebaseTransactionHandler {
|
||||
/**
|
||||
* Signal that the transaction data has been received
|
||||
*
|
||||
* @param updateData
|
||||
* @param updates
|
||||
*/
|
||||
public void signalUpdateReceived(Map<String, Object> updateData) {
|
||||
lock.lock();
|
||||
void signalUpdateReceived(ReadableMap updates) {
|
||||
Map<String, Object> updateData = Utils.recursivelyDeconstructReadableMap(updates);
|
||||
|
||||
abort = (Boolean) updateData.get("abort");
|
||||
lock.lock();
|
||||
value = updateData.get("value");
|
||||
abort = (Boolean) updateData.get("abort");
|
||||
|
||||
try {
|
||||
if (isReady)
|
||||
throw new IllegalStateException("This transactionUpdateCallback has already been called.");
|
||||
if (signalled) {
|
||||
throw new IllegalStateException("This transactionUpdateHandler has already been signalled.");
|
||||
}
|
||||
|
||||
signalled = true;
|
||||
data = updateData;
|
||||
isReady = true;
|
||||
condition.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
@@ -44,16 +64,20 @@ public class RNFirebaseTransactionHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for transactionUpdateReceived to signal condition
|
||||
* Wait for signalUpdateReceived to signal condition
|
||||
*
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
void await() throws InterruptedException {
|
||||
lock.lock();
|
||||
Boolean notTimedOut = false;
|
||||
|
||||
long timeoutExpired = System.currentTimeMillis() + 5000;
|
||||
|
||||
try {
|
||||
while (!notTimedOut && !isReady) {
|
||||
notTimedOut = condition.await(30, TimeUnit.SECONDS);
|
||||
while (!timeout && !condition.await(250, TimeUnit.MILLISECONDS) && !signalled) {
|
||||
if (!signalled && System.currentTimeMillis() > timeoutExpired) {
|
||||
timeout = true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
@@ -62,9 +86,68 @@ public class RNFirebaseTransactionHandler {
|
||||
|
||||
/**
|
||||
* Get the
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<String, Object> getUpdates() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a RN map of transaction mutable data for sending to js
|
||||
*
|
||||
* @param updatesData
|
||||
* @return
|
||||
*/
|
||||
WritableMap createUpdateMap(MutableData updatesData) {
|
||||
final WritableMap updatesMap = Arguments.createMap();
|
||||
|
||||
updatesMap.putInt("id", transactionId);
|
||||
updatesMap.putString("type", "update");
|
||||
|
||||
// all events get distributed js side based on app name
|
||||
updatesMap.putString("appName", appName);
|
||||
|
||||
if (!updatesData.hasChildren()) {
|
||||
Utils.mapPutValue("value", updatesData.getValue(), updatesMap);
|
||||
} else {
|
||||
Object value = Utils.castValue(updatesData);
|
||||
|
||||
if (value instanceof WritableNativeArray) {
|
||||
updatesMap.putArray("value", (WritableArray) value);
|
||||
} else {
|
||||
updatesMap.putMap("value", (WritableMap) value);
|
||||
}
|
||||
}
|
||||
|
||||
return updatesMap;
|
||||
}
|
||||
|
||||
|
||||
WritableMap createResultMap(@Nullable DatabaseError error, boolean committed, DataSnapshot snapshot) {
|
||||
WritableMap resultMap = Arguments.createMap();
|
||||
|
||||
resultMap.putInt("id", transactionId);
|
||||
resultMap.putString("appName", appName);
|
||||
|
||||
resultMap.putBoolean("timeout", timeout);
|
||||
resultMap.putBoolean("committed", committed);
|
||||
resultMap.putBoolean("interrupted", interrupted);
|
||||
|
||||
if (error != null || timeout || interrupted) {
|
||||
resultMap.putString("type", "error");
|
||||
if (error != null) resultMap.putMap("error", RNFirebaseDatabase.getJSError(error));
|
||||
if (error == null && timeout) {
|
||||
WritableMap timeoutError = Arguments.createMap();
|
||||
timeoutError.putString("code", "DATABASE/INTERNAL-TIMEOUT");
|
||||
timeoutError.putString("message", "A timeout occurred whilst waiting for RN JS thread to send transaction updates.");
|
||||
resultMap.putMap("error", timeoutError);
|
||||
}
|
||||
} else {
|
||||
resultMap.putString("type", "complete");
|
||||
resultMap.putMap("snapshot", Utils.snapshotToMap(snapshot));
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user