diff --git a/lib/modules/database/reference.js b/lib/modules/database/reference.js index 7cc4e478..31753d75 100644 --- a/lib/modules/database/reference.js +++ b/lib/modules/database/reference.js @@ -5,7 +5,7 @@ import Query from './query.js'; import Snapshot from './snapshot'; import Disconnect from './disconnect'; import ReferenceBase from './../../utils/ReferenceBase'; -import { isFunction, isObject, tryJSONParse, tryJSONStringify, generatePushID } from './../../utils'; +import { promiseOrCallback, isFunction, isObject, tryJSONParse, tryJSONStringify, generatePushID } from './../../utils'; // Unique Reference ID for native events let refId = 1; @@ -64,70 +64,110 @@ export default class Reference extends ReferenceBase { this._refListeners = {}; this._database = database; this._query = new Query(this, path, existingModifiers); - // TODO this.log.debug('Created new Reference', this._refId, this.path); + this.log = this._database.log; + this.log.debug('Created new Reference', this._refId, this.path); } /** + * By calling `keepSynced(true)` on a location, the data for that location will + * automatically be downloaded and kept in sync, even when no listeners are + * attached for that location. Additionally, while a location is kept synced, + * it will not be evicted from the persistent disk cache. * + * @link https://firebase.google.com/docs/reference/android/com/google/firebase/database/Query.html#keepSynced(boolean) * @param bool * @returns {*} */ keepSynced(bool: boolean) { - return this._database._native.keepSynced(this.path, bool); + return this._database._native.keepSynced(this._refId, this.path, this._query.getModifiers(), bool); } /** + * Writes data to this Database location. * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#set * @param value - * @returns {*} + * @param onComplete + * @returns {Promise} */ - set(value: any) { - return this._database._native.set(this.path, this._serializeAnyType(value)); + set(value: any, onComplete?: Function): Promise { + return promiseOrCallback( + this._database._native.set(this.path, this._serializeAnyType(value)), + onComplete, + ); } /** + * Sets a priority for the data at this Database location. * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#setPriority * @param priority - * @returns {*} + * @param onComplete + * @returns {Promise} */ - setPriority(priority: string | number | null) { + setPriority(priority: string | number | null, onComplete?: Function): Promise { const _priority = this._serializeAnyType(priority); - return this._database._native.setPriority(this.path, _priority); + + return promiseOrCallback( + this._database._native.setPriority(this.path, _priority), + onComplete, + ); } /** + * Writes data the Database location. Like set() but also specifies the priority for that data. * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#setWithPriority * @param value * @param priority - * @returns {*} + * @param onComplete + * @returns {Promise} */ - setWithPriority(value: any, priority: string | number | null) { + setWithPriority(value: any, priority: string | number | null, onComplete?: Function): Promise { const _value = this._serializeAnyType(value); const _priority = this._serializeAnyType(priority); - return this._database._native.setWithPriority(this.path, _value, _priority); + + return promiseOrCallback( + this._database._native.setWithPriority(this.path, _value, _priority), + onComplete, + ); } /** + * Writes multiple values to the Database at once. * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#update * @param val - * @returns {*} + * @param onComplete + * @returns {Promise} */ - update(val: Object) { + update(val: Object, onComplete?: Function): Promise { const value = this._serializeObject(val); - return this._database._native.update(this.path, value); + + return promiseOrCallback( + this._database._native.update(this.path, value), + onComplete, + ); } /** + * Removes the data at this Database location. * - * @returns {*} + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove + * @param onComplete + * @return {Promise} */ - remove() { - return this._database._native.remove(this.path); + remove(onComplete?: Function): Promise { + return promiseOrCallback( + this._database._native.remove(this.path), + onComplete, + ); } /** * Atomically modifies the data at this location. - * @url https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction + * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction * @param transactionUpdate * @param onComplete * @param applyLocally @@ -140,6 +180,7 @@ export default class Reference extends ReferenceBase { ); } + // todo cleanup / use new promiseOrCallback logic return new Promise((resolve, reject) => { const onCompleteWrapper = (error, committed, snapshotData) => { if (error) { @@ -190,7 +231,7 @@ export default class Reference extends ReferenceBase { * @param onComplete * @returns {*} */ - push(value: any, onComplete: Function) { + push(value: any, onComplete?: Function) { if (value === null || value === undefined) { return new Reference(this._database, `${this.path}/${generatePushID(this._database.serverTimeOffset)}`); } @@ -218,7 +259,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ orderByKey(): Reference { - return this.orderBy('orderByKey'); + return this._query.orderBy('orderByKey'); } /** @@ -226,7 +267,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ orderByPriority(): Reference { - return this.orderBy('orderByPriority'); + return this._query.orderBy('orderByPriority'); } /** @@ -234,7 +275,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ orderByValue(): Reference { - return this.orderBy('orderByValue'); + return this._query.orderBy('orderByValue'); } /** @@ -243,7 +284,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ orderByChild(key: string): Reference { - return this.orderBy('orderByChild', key); + return this._query.orderBy('orderByChild', key); } /** @@ -254,7 +295,7 @@ export default class Reference extends ReferenceBase { */ orderBy(name: string, key?: string): Reference { const newRef = new Reference(this._database, this.path, this._query.getModifiers()); - newRef.query.orderBy(name, key); + newRef._query.orderBy(name, key); return newRef; } @@ -268,7 +309,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ limitToLast(limit: number): Reference { - return this.limit('limitToLast', limit); + return this._query.limit('limitToLast', limit); } /** @@ -277,7 +318,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ limitToFirst(limit: number): Reference { - return this.limit('limitToFirst', limit); + return this._query.limit('limitToFirst', limit); } /** @@ -288,7 +329,7 @@ export default class Reference extends ReferenceBase { */ limit(name: string, limit: number): Reference { const newRef = new Reference(this._database, this.path, this._query.getModifiers()); - newRef.query.limit(name, limit); + newRef._query.limit(name, limit); return newRef; } @@ -303,7 +344,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ equalTo(value: any, key?: string): Reference { - return this.filter('equalTo', value, key); + return this._query.filter('equalTo', value, key); } /** @@ -313,7 +354,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ endAt(value: any, key?: string): Reference { - return this.filter('endAt', value, key); + return this._query.filter('endAt', value, key); } /** @@ -323,7 +364,7 @@ export default class Reference extends ReferenceBase { * @returns {Reference} */ startAt(value: any, key?: string): Reference { - return this.filter('startAt', value, key); + return this._query.filter('startAt', value, key); } /** @@ -335,7 +376,7 @@ export default class Reference extends ReferenceBase { */ filter(name: string, value: any, key?: string): Reference { const newRef = new Reference(this._database, this.path, this._query.getModifiers()); - newRef.query.filter(name, value, key); + newRef._query.filter(name, value, key); return newRef; } @@ -679,7 +720,6 @@ export default class Reference extends ReferenceBase { * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#off} */ off(eventType?: string = '', originalCallback?: () => any) { - // TODO this.log.debug('ref.off(): ', this._refId, eventType); // $FlowFixMe const listeners: Array = Object.values(this._refListeners); let listenersToRemove; diff --git a/lib/utils/ReferenceBase.js b/lib/utils/ReferenceBase.js index 5b0a655b..97fb64a6 100644 --- a/lib/utils/ReferenceBase.js +++ b/lib/utils/ReferenceBase.js @@ -7,8 +7,6 @@ export default class ReferenceBase { this.path = path || '/'; } - // todo add missing firebase reference props/methods - /** * The last part of a Reference's path (after the last '/') * The key of a root Reference is null. diff --git a/lib/utils/index.js b/lib/utils/index.js index 6a180bcb..488062d8 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -307,3 +307,29 @@ export function nativeWithApp(appName, NativeModule) { return native; } +/** + * Return the existing promise if no callback provided or + * exec the promise and callback if optionalCallback is valid. + * + * @param promise + * @param optionalCallback + * @return {Promise} + */ +export function promiseOrCallback(promise: Promise, optionalCallback?: Function) { + if (!isFunction(optionalCallback)) return promise; + + return promise.then((result) => { + // some of firebase internal tests & methods only check/return one arg + // see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62 + if (optionalCallback.length === 1) { + optionalCallback(null); + } else { + optionalCallback(null, result); + } + + return Promise.resolve(result); + }).catch((error) => { + optionalCallback(error); + return Promise.reject(error); + }); +}