[js][android] Support multiple listeners on a single ref

This commit is contained in:
Chris Bianca
2017-04-26 12:21:53 +01:00
parent e8854f1a2e
commit ef306162b4
9 changed files with 395 additions and 360 deletions

View File

@@ -19,9 +19,8 @@ const FirebaseDatabaseEvt = new NativeEventEmitter(FirebaseDatabase);
export default class Database extends Base {
constructor(firebase: Object, options: Object = {}) {
super(firebase, options);
this.subscriptions = {};
this.references = {};
this.serverTimeOffset = 0;
this.errorSubscriptions = {};
this.persistenceEnabled = false;
this.namespace = 'firebase:database';
this.transaction = new TransactionHandler(firebase, this, FirebaseDatabaseEvt);
@@ -68,20 +67,12 @@ export default class Database extends Base {
* @param errorCb
* @returns {*}
*/
on(path: string, modifiersString: string, modifiers: Array<string>, eventName: string, cb: () => void, errorCb: () => void) {
const handle = this._handle(path, modifiersString);
this.log.debug('adding on listener', handle);
if (!this.subscriptions[handle]) this.subscriptions[handle] = {};
if (!this.subscriptions[handle][eventName]) this.subscriptions[handle][eventName] = [];
this.subscriptions[handle][eventName].push(cb);
if (errorCb) {
if (!this.errorSubscriptions[handle]) this.errorSubscriptions[handle] = [];
this.errorSubscriptions[handle].push(errorCb);
}
return promisify('on', FirebaseDatabase)(path, modifiersString, modifiers, eventName);
on(ref: Reference, listener: DatabaseListener) {
const { refId, path, query } = ref;
const { listenerId, eventName } = listener;
this.log.debug('on() : ', ref.refId, listenerId, eventName);
this.references[refId] = ref;
return promisify('on', FirebaseDatabase)(refId, path, query.getModifiers(), listenerId, eventName);
}
/**
@@ -92,49 +83,30 @@ export default class Database extends Base {
* @param origCB
* @returns {*}
*/
off(path: string, modifiersString: string, eventName?: string, origCB?: () => void) {
const handle = this._handle(path, modifiersString);
this.log.debug('off() : ', handle, eventName);
off(refId: number, listeners: Array<DatabaseListener>, remainingListenersCount: number) {
this.log.debug('off() : ', refId, listeners);
if (!this.subscriptions[handle] || (eventName && !this.subscriptions[handle][eventName])) {
this.log.warn('off() called, but not currently listening at that location (bad path)', handle, eventName);
return Promise.resolve();
}
// Delete the reference if there are no more listeners
if (remainingListenersCount === 0) delete this.references[refId];
if (eventName && origCB) {
const i = this.subscriptions[handle][eventName].indexOf(origCB);
if (listeners.length === 0) return Promise.resolve();
if (i === -1) {
this.log.warn('off() called, but the callback specified is not listening at that location (bad path)', handle, eventName);
return Promise.resolve();
}
this.subscriptions[handle][eventName].splice(i, 1);
if (this.subscriptions[handle][eventName].length > 0) return Promise.resolve();
} else if (eventName) {
this.subscriptions[handle][eventName] = [];
} else {
this.subscriptions[handle] = {};
}
this.errorSubscriptions[handle] = [];
return promisify('off', FirebaseDatabase)(path, modifiersString, eventName);
return promisify('off', FirebaseDatabase)(refId, listeners.map(listener => ({
listenerId: listener.listenerId,
eventName: listener.eventName,
})));
}
/**
* Removes all event handlers and their native subscriptions
* Removes all references and their native listeners
* @returns {Promise.<*>}
*/
cleanup() {
const promises = [];
Object.keys(this.subscriptions).forEach((handle) => {
Object.keys(this.subscriptions[handle]).forEach((eventName) => {
const separator = handle.indexOf('|');
const path = handle.substring(0, separator);
const modifiersString = handle.substring(separator + 1);
promises.push(this.off(path, modifiersString, eventName));
});
Object.keys(this.references).forEach((refId) => {
const ref = this.references[refId];
promises.push(this.off(refId, ref.listeners, 0));
});
return Promise.all(promises);
}
@@ -169,18 +141,6 @@ export default class Database extends Base {
return Promise.reject({ status: 'Already enabled' });
}
/**
*
* @param path
* @param modifiersString
* @returns {string}
* @private
*/
_handle(path: string = '', modifiersString: string = '') {
return `${path}|${modifiersString}`;
}
/**
*
* @param event
@@ -188,18 +148,14 @@ export default class Database extends Base {
*/
_handleDatabaseEvent(event: Object) {
const body = event.body || {};
const { path, modifiersString, eventName, snapshot } = body;
const handle = this._handle(path, modifiersString);
this.log.debug('_handleDatabaseEvent: ', handle, eventName, snapshot && snapshot.key);
if (this.subscriptions[handle] && this.subscriptions[handle][eventName]) {
this.subscriptions[handle][eventName].forEach((cb) => {
cb(new Snapshot(new Reference(this, path, modifiersString.split('|')), snapshot), body);
});
const { refId, listenerId, path, eventName, snapshot } = body;
this.log.debug('_handleDatabaseEvent: ', refId, listenerId, path, eventName, snapshot && snapshot.key);
if (this.references[refId] && this.references[refId].listeners[listenerId]) {
const cb = this.references[refId].listeners[listenerId].successCallback;
cb(new Snapshot(this.references[refId], snapshot));
} else {
FirebaseDatabase.off(path, modifiersString, eventName, () => {
this.log.debug('_handleDatabaseEvent: No JS listener registered, removed native listener', handle, eventName);
FirebaseDatabase.off(refId, [{ listenerId, eventName }], () => {
this.log.debug('_handleDatabaseEvent: No JS listener registered, removed native listener', refId, listenerId, eventName);
});
}
}
@@ -235,13 +191,15 @@ export default class Database extends Base {
* @private
*/
_handleDatabaseError(error: Object = {}) {
const { path, modifiers } = error;
const handle = this._handle(path, modifiers);
const { refId, listenerId, path } = error;
const firebaseError = this._toFirebaseError(error);
this.log.debug('_handleDatabaseError ->', handle, 'database_error', error);
this.log.debug('_handleDatabaseError ->', refId, listenerId, path, 'database_error', error);
if (this.errorSubscriptions[handle]) this.errorSubscriptions[handle].forEach(listener => listener(firebaseError));
if (this.references[refId] && this.references[refId].listeners[listenerId]) {
const failureCb = this.references[refId].listeners[listenerId].failureCallback;
if (failureCb) failureCb(firebaseError);
}
}
}
@@ -250,4 +208,3 @@ export const statics = {
TIMESTAMP: FirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
},
};