From 7c63ec4ca9c0444b003851c77af8dd0d7946a1c5 Mon Sep 17 00:00:00 2001 From: Michael Diarmid Date: Tue, 13 Feb 2018 14:23:09 +0000 Subject: [PATCH 01/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c74939e..2aa8743e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ License Backers on Open Collective Sponsors on Open Collective - Chat + Chat Follow on Twitter

From 1c5c02f6cfa7910f3ccd32316c3033c37152e99b Mon Sep 17 00:00:00 2001 From: Michael Diarmid Date: Tue, 13 Feb 2018 17:53:48 +0000 Subject: [PATCH 02/17] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84184cb9..14d7eca9 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "unmockedModulePathPatterns": ["./node_modules/react", "./node_modules/react-native", "./node_modules/react-native-mock", "./node_modules/react-addons-test-utils"] }, "license": "APACHE-2.0", - "keywords": ["react", "admob", "auth", "config", "digits", "phone-auth", "sms", "firestore", "cloud-firestore", "datastore", "remote-config", "transactions", "react-native", "react-native-firebase", "firebase", "fcm", "apn", "gcm", "analytics", "messaging", "database", "android", "ios", "crash", "firestack", "performance", "firestore", "dynamic-links", "crashlytics"], + "keywords": ["react", "admob", "auth", "config", "digits", "fabric", "phone-auth", "sms", "firestore", "cloud-firestore", "datastore", "remote-config", "transactions", "react-native", "react-native-firebase", "firebase", "fcm", "apn", "gcm", "analytics", "messaging", "database", "android", "ios", "crash", "firestack", "performance", "firestore", "dynamic-links", "crashlytics"], "peerDependencies": { "react": "*", "react-native": ">= 0.48.0", From c8106a3897a1b6865045437d5c33d4136086d1a9 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 10:02:34 +0000 Subject: [PATCH 03/17] [types] Temp rename of file to allow capitalisation --- lib/modules/database/reference-temp.js | 892 +++++++++++++++++++++++++ 1 file changed, 892 insertions(+) create mode 100644 lib/modules/database/reference-temp.js diff --git a/lib/modules/database/reference-temp.js b/lib/modules/database/reference-temp.js new file mode 100644 index 00000000..e9c98e7b --- /dev/null +++ b/lib/modules/database/reference-temp.js @@ -0,0 +1,892 @@ +/** + * @flow + * Database Reference representation wrapper + */ +import Query from './query'; +import DataSnapshot from './DataSnapshot'; +import OnDisconnect from './OnDisconnect'; +import { getLogger } from '../../utils/log'; +import { getNativeModule } from '../../utils/native'; +import ReferenceBase from '../../utils/ReferenceBase'; + +import { + promiseOrCallback, + isFunction, + isObject, + isString, + tryJSONParse, + tryJSONStringify, + generatePushID, +} from '../../utils'; + +import SyncTree from '../../utils/SyncTree'; + +import type Database from './'; +import type { DatabaseModifier, FirebaseError } from '../../types'; + +// track all event registrations by path +let listeners = 0; + +/** + * Enum for event types + * @readonly + * @enum {String} + */ +const ReferenceEventTypes = { + value: 'value', + child_added: 'child_added', + child_removed: 'child_removed', + child_changed: 'child_changed', + child_moved: 'child_moved', +}; + +type DatabaseListener = { + listenerId: number, + eventName: string, + successCallback: Function, + failureCallback?: Function, +}; + +/** + * @typedef {String} ReferenceLocation - Path to location in the database, relative + * to the root reference. Consists of a path where segments are separated by a + * forward slash (/) and ends in a ReferenceKey - except the root location, which + * has no ReferenceKey. + * + * @example + * // root reference location: '/' + * // non-root reference: '/path/to/referenceKey' + */ + +/** + * @typedef {String} ReferenceKey - Identifier for each location that is unique to that + * location, within the scope of its parent. The last part of a ReferenceLocation. + */ + +/** + * Represents a specific location in your Database that can be used for + * reading or writing data. + * + * You can reference the root using firebase.database().ref() or a child location + * by calling firebase.database().ref("child/path"). + * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference + * @class Reference + * @extends ReferenceBase + */ +export default class Reference extends ReferenceBase { + _database: Database; + _promise: ?Promise<*>; + _query: Query; + _refListeners: { [listenerId: number]: DatabaseListener }; + + constructor( + database: Database, + path: string, + existingModifiers?: Array + ) { + super(path); + this._promise = null; + this._refListeners = {}; + this._database = database; + this._query = new Query(this, existingModifiers); + getLogger(database).debug('Created new Reference', this._getRefKey()); + } + + /** + * 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): Promise { + return getNativeModule(this._database).keepSynced( + this._getRefKey(), + 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 + * @param onComplete + * @returns {Promise} + */ + set(value: any, onComplete?: Function): Promise { + return promiseOrCallback( + getNativeModule(this._database).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 + * @param onComplete + * @returns {Promise} + */ + setPriority( + priority: string | number | null, + onComplete?: Function + ): Promise { + const _priority = this._serializeAnyType(priority); + + return promiseOrCallback( + getNativeModule(this._database).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 + * @param onComplete + * @returns {Promise} + */ + setWithPriority( + value: any, + priority: string | number | null, + onComplete?: Function + ): Promise { + const _value = this._serializeAnyType(value); + const _priority = this._serializeAnyType(priority); + + return promiseOrCallback( + getNativeModule(this._database).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 + * @param onComplete + * @returns {Promise} + */ + update(val: Object, onComplete?: Function): Promise { + const value = this._serializeObject(val); + + return promiseOrCallback( + getNativeModule(this._database).update(this.path, value), + onComplete + ); + } + + /** + * Removes the data at this Database location. + * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove + * @param onComplete + * @return {Promise} + */ + remove(onComplete?: Function): Promise { + return promiseOrCallback( + getNativeModule(this._database).remove(this.path), + onComplete + ); + } + + /** + * Atomically modifies the data at this location. + * + * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction + * @param transactionUpdate + * @param onComplete + * @param applyLocally + */ + transaction( + transactionUpdate: Function, + onComplete: ( + error: ?Error, + committed: boolean, + snapshot: ?DataSnapshot + ) => *, + applyLocally: boolean = false + ) { + if (!isFunction(transactionUpdate)) { + return Promise.reject( + new Error('Missing transactionUpdate function argument.') + ); + } + + return new Promise((resolve, reject) => { + const onCompleteWrapper = (error, committed, snapshotData) => { + if (isFunction(onComplete)) { + if (error) { + onComplete(error, committed, null); + } else { + onComplete(null, committed, new DataSnapshot(this, snapshotData)); + } + } + + if (error) return reject(error); + return resolve({ + committed, + snapshot: new DataSnapshot(this, snapshotData), + }); + }; + + // start the transaction natively + this._database._transactionHandler.add( + this, + transactionUpdate, + onCompleteWrapper, + applyLocally + ); + }); + } + + /** + * + * @param eventName + * @param successCallback + * @param cancelOrContext + * @param context + * @returns {Promise.} + */ + once( + eventName: string = 'value', + successCallback: (snapshot: DataSnapshot) => void, + cancelOrContext: (error: FirebaseError) => void, + context?: Object + ) { + return getNativeModule(this._database) + .once(this._getRefKey(), this.path, this._query.getModifiers(), eventName) + .then(({ snapshot }) => { + const _snapshot = new DataSnapshot(this, snapshot); + + if (isFunction(successCallback)) { + if (isObject(cancelOrContext)) + successCallback.bind(cancelOrContext)(_snapshot); + if (context && isObject(context)) + successCallback.bind(context)(_snapshot); + successCallback(_snapshot); + } + + return _snapshot; + }) + .catch(error => { + if (isFunction(cancelOrContext)) return cancelOrContext(error); + return error; + }); + } + + /** + * + * @param value + * @param onComplete + * @returns {*} + */ + push(value: any, onComplete?: Function): Reference | Promise { + if (value === null || value === undefined) { + return new Reference( + this._database, + `${this.path}/${generatePushID(this._database._serverTimeOffset)}` + ); + } + + const newRef = new Reference( + this._database, + `${this.path}/${generatePushID(this._database._serverTimeOffset)}` + ); + const promise = newRef.set(value); + + // if callback provided then internally call the set promise with value + if (isFunction(onComplete)) { + return ( + promise + // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 + .then(() => onComplete(null, newRef)) + // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 + .catch(error => onComplete(error, null)) + ); + } + + // otherwise attach promise to 'thenable' reference and return the + // new reference + newRef._setThenable(promise); + return newRef; + } + + /** + * MODIFIERS + */ + + /** + * + * @returns {Reference} + */ + orderByKey(): Reference { + return this.orderBy('orderByKey'); + } + + /** + * + * @returns {Reference} + */ + orderByPriority(): Reference { + return this.orderBy('orderByPriority'); + } + + /** + * + * @returns {Reference} + */ + orderByValue(): Reference { + return this.orderBy('orderByValue'); + } + + /** + * + * @param key + * @returns {Reference} + */ + orderByChild(key: string): Reference { + return this.orderBy('orderByChild', key); + } + + /** + * + * @param name + * @param key + * @returns {Reference} + */ + orderBy(name: string, key?: string): Reference { + const newRef = new Reference( + this._database, + this.path, + this._query.getModifiers() + ); + newRef._query.orderBy(name, key); + return newRef; + } + + /** + * LIMITS + */ + + /** + * + * @param limit + * @returns {Reference} + */ + limitToLast(limit: number): Reference { + return this.limit('limitToLast', limit); + } + + /** + * + * @param limit + * @returns {Reference} + */ + limitToFirst(limit: number): Reference { + return this.limit('limitToFirst', limit); + } + + /** + * + * @param name + * @param limit + * @returns {Reference} + */ + limit(name: string, limit: number): Reference { + const newRef = new Reference( + this._database, + this.path, + this._query.getModifiers() + ); + newRef._query.limit(name, limit); + return newRef; + } + + /** + * FILTERS + */ + + /** + * + * @param value + * @param key + * @returns {Reference} + */ + equalTo(value: any, key?: string): Reference { + return this.filter('equalTo', value, key); + } + + /** + * + * @param value + * @param key + * @returns {Reference} + */ + endAt(value: any, key?: string): Reference { + return this.filter('endAt', value, key); + } + + /** + * + * @param value + * @param key + * @returns {Reference} + */ + startAt(value: any, key?: string): Reference { + return this.filter('startAt', value, key); + } + + /** + * + * @param name + * @param value + * @param key + * @returns {Reference} + */ + 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); + return newRef; + } + + /** + * + * @returns {OnDisconnect} + */ + onDisconnect(): OnDisconnect { + return new OnDisconnect(this); + } + + /** + * Creates a Reference to a child of the current Reference, using a relative path. + * No validation is performed on the path to ensure it has a valid format. + * @param {String} path relative to current ref's location + * @returns {!Reference} A new Reference to the path provided, relative to the current + * Reference + * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#child} + */ + child(path: string): Reference { + return new Reference(this._database, `${this.path}/${path}`); + } + + /** + * Return the ref as a path string + * @returns {string} + */ + toString(): string { + return `${this._database.app.options.databaseURL}/${this.path}`; + } + + /** + * Returns whether another Reference represent the same location and are from the + * same instance of firebase.app.App - multiple firebase apps not currently supported. + * @param {Reference} otherRef - Other reference to compare to this one + * @return {Boolean} Whether otherReference is equal to this one + * + * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#isEqual} + */ + isEqual(otherRef: Reference): boolean { + return ( + !!otherRef && + otherRef.constructor === Reference && + otherRef.key === this.key && + this._query.queryIdentifier() === otherRef._query.queryIdentifier() + ); + } + + /** + * GETTERS + */ + + /** + * The parent location of a Reference, or null for the root Reference. + * @type {Reference} + * + * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#parent} + */ + get parent(): Reference | null { + if (this.path === '/') return null; + return new Reference( + this._database, + this.path.substring(0, this.path.lastIndexOf('/')) + ); + } + + /** + * A reference to itself + * @type {!Reference} + * + * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#ref} + */ + get ref(): Reference { + return this; + } + + /** + * Reference to the root of the database: '/' + * @type {!Reference} + * + * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#root} + */ + get root(): Reference { + return new Reference(this._database, '/'); + } + + /** + * Access then method of promise if set + * @return {*} + */ + then(fnResolve: any => any, fnReject: any => any) { + if (isFunction(fnResolve) && this._promise && this._promise.then) { + return this._promise.then.bind(this._promise)( + result => { + this._promise = null; + return fnResolve(result); + }, + possibleErr => { + this._promise = null; + + if (isFunction(fnReject)) { + return fnReject(possibleErr); + } + + throw possibleErr; + } + ); + } + + throw new Error("Cannot read property 'then' of undefined."); + } + + /** + * Access catch method of promise if set + * @return {*} + */ + catch(fnReject: any => any) { + if (isFunction(fnReject) && this._promise && this._promise.catch) { + return this._promise.catch.bind(this._promise)(possibleErr => { + this._promise = null; + return fnReject(possibleErr); + }); + } + + throw new Error("Cannot read property 'catch' of undefined."); + } + + /** + * INTERNALS + */ + + /** + * Generate a unique registration key. + * + * @return {string} + */ + _getRegistrationKey(eventType: string): string { + return `$${this._database.app.name}$/${ + this.path + }$${this._query.queryIdentifier()}$${listeners}$${eventType}`; + } + + /** + * Generate a string that uniquely identifies this + * combination of path and query modifiers + * + * @return {string} + * @private + */ + _getRefKey(): string { + return `$${this._database.app.name}$/${ + this.path + }$${this._query.queryIdentifier()}`; + } + + /** + * Set the promise this 'thenable' reference relates to + * @param promise + * @private + */ + _setThenable(promise: Promise<*>) { + this._promise = promise; + } + + /** + * + * @param obj + * @returns {Object} + * @private + */ + _serializeObject(obj: Object) { + if (!isObject(obj)) return obj; + + // json stringify then parse it calls toString on Objects / Classes + // that support it i.e new Date() becomes a ISO string. + return tryJSONParse(tryJSONStringify(obj)); + } + + /** + * + * @param value + * @returns {*} + * @private + */ + _serializeAnyType(value: any) { + if (isObject(value)) { + return { + type: 'object', + value: this._serializeObject(value), + }; + } + + return { + type: typeof value, + value, + }; + } + + /** + * Register a listener for data changes at the current ref's location. + * The primary method of reading data from a Database. + * + * Listeners can be unbound using {@link off}. + * + * Event Types: + * + * - value: {@link callback}. + * - child_added: {@link callback} + * - child_removed: {@link callback} + * - child_changed: {@link callback} + * - child_moved: {@link callback} + * + * @param {ReferenceEventType} eventType - Type of event to attach a callback for. + * @param {ReferenceEventCallback} callback - Function that will be called + * when the event occurs with the new data. + * @param {cancelCallbackOrContext=} cancelCallbackOrContext - Optional callback that is called + * if the event subscription fails. {@link cancelCallbackOrContext} + * @param {*=} context - Optional object to bind the callbacks to when calling them. + * @returns {ReferenceEventCallback} callback function, unmodified (unbound), for + * convenience if you want to pass an inline function to on() and store it later for + * removing using off(). + * + * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on} + */ + on( + eventType: string, + callback: DataSnapshot => any, + cancelCallbackOrContext?: Object => any | Object, + context?: Object + ): Function { + if (!eventType) { + throw new Error( + 'Query.on failed: Function called with 0 arguments. Expects at least 2.' + ); + } + + if (!isString(eventType) || !ReferenceEventTypes[eventType]) { + throw new Error( + `Query.on failed: First argument must be a valid string event type: "${Object.keys( + ReferenceEventTypes + ).join(', ')}"` + ); + } + + if (!callback) { + throw new Error( + 'Query.on failed: Function called with 1 argument. Expects at least 2.' + ); + } + + if (!isFunction(callback)) { + throw new Error( + 'Query.on failed: Second argument must be a valid function.' + ); + } + + if ( + cancelCallbackOrContext && + !isFunction(cancelCallbackOrContext) && + !isObject(context) && + !isObject(cancelCallbackOrContext) + ) { + throw new Error( + 'Query.on failed: Function called with 3 arguments, but third optional argument `cancelCallbackOrContext` was not a function.' + ); + } + + if ( + cancelCallbackOrContext && + !isFunction(cancelCallbackOrContext) && + context + ) { + throw new Error( + 'Query.on failed: Function called with 4 arguments, but third optional argument `cancelCallbackOrContext` was not a function.' + ); + } + + const eventRegistrationKey = this._getRegistrationKey(eventType); + const registrationCancellationKey = `${eventRegistrationKey}$cancelled`; + const _context = + cancelCallbackOrContext && !isFunction(cancelCallbackOrContext) + ? cancelCallbackOrContext + : context; + const registrationObj = { + eventType, + ref: this, + path: this.path, + key: this._getRefKey(), + appName: this._database.app.name, + eventRegistrationKey, + }; + + SyncTree.addRegistration({ + ...registrationObj, + listener: _context ? callback.bind(_context) : callback, + }); + + if (cancelCallbackOrContext && isFunction(cancelCallbackOrContext)) { + // cancellations have their own separate registration + // as these are one off events, and they're not guaranteed + // to occur either, only happens on failure to register on native + SyncTree.addRegistration({ + ref: this, + once: true, + path: this.path, + key: this._getRefKey(), + appName: this._database.app.name, + eventType: `${eventType}$cancelled`, + eventRegistrationKey: registrationCancellationKey, + listener: _context + ? cancelCallbackOrContext.bind(_context) + : cancelCallbackOrContext, + }); + } + + // initialise the native listener if not already listening + getNativeModule(this._database).on({ + eventType, + path: this.path, + key: this._getRefKey(), + appName: this._database.app.name, + modifiers: this._query.getModifiers(), + hasCancellationCallback: isFunction(cancelCallbackOrContext), + registration: { + eventRegistrationKey, + key: registrationObj.key, + registrationCancellationKey, + }, + }); + + // increment number of listeners - just s short way of making + // every registration unique per .on() call + listeners += 1; + + // return original unbound successCallback for + // the purposes of calling .off(eventType, callback) at a later date + return callback; + } + + /** + * Detaches a callback previously attached with on(). + * + * Detach a callback previously attached with on(). Note that if on() was called + * multiple times with the same eventType and callback, the callback will be called + * multiple times for each event, and off() must be called multiple times to + * remove the callback. Calling off() on a parent listener will not automatically + * remove listeners registered on child nodes, off() must also be called on any + * child listeners to remove the callback. + * + * If a callback is not specified, all callbacks for the specified eventType will be removed. + * Similarly, if no eventType or callback is specified, all callbacks for the Reference will be removed. + * @param eventType + * @param originalCallback + */ + off(eventType?: string = '', originalCallback?: () => any) { + if (!arguments.length) { + // Firebase Docs: + // if no eventType or callback is specified, all callbacks for the Reference will be removed. + return SyncTree.removeListenersForRegistrations( + SyncTree.getRegistrationsByPath(this.path) + ); + } + + /* + * VALIDATE ARGS + */ + if ( + eventType && + (!isString(eventType) || !ReferenceEventTypes[eventType]) + ) { + throw new Error( + `Query.off failed: First argument must be a valid string event type: "${Object.keys( + ReferenceEventTypes + ).join(', ')}"` + ); + } + + if (originalCallback && !isFunction(originalCallback)) { + throw new Error( + 'Query.off failed: Function called with 2 arguments, but second optional argument was not a function.' + ); + } + + // Firebase Docs: + // Note that if on() was called + // multiple times with the same eventType and callback, the callback will be called + // multiple times for each event, and off() must be called multiple times to + // remove the callback. + // Remove only a single registration + if (eventType && originalCallback) { + const registration = SyncTree.getOneByPathEventListener( + this.path, + eventType, + originalCallback + ); + if (!registration) return []; + + // remove the paired cancellation registration if any exist + SyncTree.removeListenersForRegistrations([`${registration}$cancelled`]); + + // remove only the first registration to match firebase web sdk + // call multiple times to remove multiple registrations + return SyncTree.removeListenerRegistrations(originalCallback, [ + registration, + ]); + } + + // Firebase Docs: + // If a callback is not specified, all callbacks for the specified eventType will be removed. + const registrations = SyncTree.getRegistrationsByPathEvent( + this.path, + eventType + ); + + SyncTree.removeListenersForRegistrations( + SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`) + ); + + return SyncTree.removeListenersForRegistrations(registrations); + } +} From 0d348392ebd83ec24bf3e5dce450f4f6ad8d0b47 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 10:05:28 +0000 Subject: [PATCH 04/17] [types] More temp renames for capitalisation --- .../database/{query.js => query-temp.js} | 2 +- lib/modules/database/reference.js | 888 ------------------ 2 files changed, 1 insertion(+), 889 deletions(-) rename lib/modules/database/{query.js => query-temp.js} (97%) delete mode 100644 lib/modules/database/reference.js diff --git a/lib/modules/database/query.js b/lib/modules/database/query-temp.js similarity index 97% rename from lib/modules/database/query.js rename to lib/modules/database/query-temp.js index 974dba85..b58388be 100644 --- a/lib/modules/database/query.js +++ b/lib/modules/database/query-temp.js @@ -5,7 +5,7 @@ import { objectToUniqueId } from '../../utils'; import type { DatabaseModifier } from '../../types'; -import type Reference from './reference'; +import type Reference from './Reference'; // todo doc methods diff --git a/lib/modules/database/reference.js b/lib/modules/database/reference.js deleted file mode 100644 index b91e6f13..00000000 --- a/lib/modules/database/reference.js +++ /dev/null @@ -1,888 +0,0 @@ -/** - * @flow - * Database Reference representation wrapper - */ -import Query from './query'; -import Snapshot from './snapshot'; -import Disconnect from './disconnect'; -import { getLogger } from '../../utils/log'; -import { getNativeModule } from '../../utils/native'; -import ReferenceBase from '../../utils/ReferenceBase'; - -import { - promiseOrCallback, - isFunction, - isObject, - isString, - tryJSONParse, - tryJSONStringify, - generatePushID, -} from '../../utils'; - -import SyncTree from '../../utils/SyncTree'; - -import type Database from './'; -import type { DatabaseModifier, FirebaseError } from '../../types'; - -// track all event registrations by path -let listeners = 0; - -/** - * Enum for event types - * @readonly - * @enum {String} - */ -const ReferenceEventTypes = { - value: 'value', - child_added: 'child_added', - child_removed: 'child_removed', - child_changed: 'child_changed', - child_moved: 'child_moved', -}; - -type DatabaseListener = { - listenerId: number, - eventName: string, - successCallback: Function, - failureCallback?: Function, -}; - -/** - * @typedef {String} ReferenceLocation - Path to location in the database, relative - * to the root reference. Consists of a path where segments are separated by a - * forward slash (/) and ends in a ReferenceKey - except the root location, which - * has no ReferenceKey. - * - * @example - * // root reference location: '/' - * // non-root reference: '/path/to/referenceKey' - */ - -/** - * @typedef {String} ReferenceKey - Identifier for each location that is unique to that - * location, within the scope of its parent. The last part of a ReferenceLocation. - */ - -/** - * Represents a specific location in your Database that can be used for - * reading or writing data. - * - * You can reference the root using firebase.database().ref() or a child location - * by calling firebase.database().ref("child/path"). - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference - * @class Reference - * @extends ReferenceBase - */ -export default class Reference extends ReferenceBase { - _database: Database; - _promise: ?Promise<*>; - _query: Query; - _refListeners: { [listenerId: number]: DatabaseListener }; - - constructor( - database: Database, - path: string, - existingModifiers?: Array - ) { - super(path); - this._promise = null; - this._refListeners = {}; - this._database = database; - this._query = new Query(this, existingModifiers); - getLogger(database).debug('Created new Reference', this._getRefKey()); - } - - /** - * 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): Promise { - return getNativeModule(this._database).keepSynced( - this._getRefKey(), - 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 - * @param onComplete - * @returns {Promise} - */ - set(value: any, onComplete?: Function): Promise { - return promiseOrCallback( - getNativeModule(this._database).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 - * @param onComplete - * @returns {Promise} - */ - setPriority( - priority: string | number | null, - onComplete?: Function - ): Promise { - const _priority = this._serializeAnyType(priority); - - return promiseOrCallback( - getNativeModule(this._database).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 - * @param onComplete - * @returns {Promise} - */ - setWithPriority( - value: any, - priority: string | number | null, - onComplete?: Function - ): Promise { - const _value = this._serializeAnyType(value); - const _priority = this._serializeAnyType(priority); - - return promiseOrCallback( - getNativeModule(this._database).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 - * @param onComplete - * @returns {Promise} - */ - update(val: Object, onComplete?: Function): Promise { - const value = this._serializeObject(val); - - return promiseOrCallback( - getNativeModule(this._database).update(this.path, value), - onComplete - ); - } - - /** - * Removes the data at this Database location. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove - * @param onComplete - * @return {Promise} - */ - remove(onComplete?: Function): Promise { - return promiseOrCallback( - getNativeModule(this._database).remove(this.path), - onComplete - ); - } - - /** - * Atomically modifies the data at this location. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction - * @param transactionUpdate - * @param onComplete - * @param applyLocally - */ - transaction( - transactionUpdate: Function, - onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *, - applyLocally: boolean = false - ) { - if (!isFunction(transactionUpdate)) { - return Promise.reject( - new Error('Missing transactionUpdate function argument.') - ); - } - - return new Promise((resolve, reject) => { - const onCompleteWrapper = (error, committed, snapshotData) => { - if (isFunction(onComplete)) { - if (error) { - onComplete(error, committed, null); - } else { - onComplete(null, committed, new Snapshot(this, snapshotData)); - } - } - - if (error) return reject(error); - return resolve({ - committed, - snapshot: new Snapshot(this, snapshotData), - }); - }; - - // start the transaction natively - this._database._transactionHandler.add( - this, - transactionUpdate, - onCompleteWrapper, - applyLocally - ); - }); - } - - /** - * - * @param eventName - * @param successCallback - * @param cancelOrContext - * @param context - * @returns {Promise.} - */ - once( - eventName: string = 'value', - successCallback: (snapshot: Object) => void, - cancelOrContext: (error: FirebaseError) => void, - context?: Object - ) { - return getNativeModule(this._database) - .once(this._getRefKey(), this.path, this._query.getModifiers(), eventName) - .then(({ snapshot }) => { - const _snapshot = new Snapshot(this, snapshot); - - if (isFunction(successCallback)) { - if (isObject(cancelOrContext)) - successCallback.bind(cancelOrContext)(_snapshot); - if (context && isObject(context)) - successCallback.bind(context)(_snapshot); - successCallback(_snapshot); - } - - return _snapshot; - }) - .catch(error => { - if (isFunction(cancelOrContext)) return cancelOrContext(error); - return error; - }); - } - - /** - * - * @param value - * @param onComplete - * @returns {*} - */ - push(value: any, onComplete?: Function): Reference | Promise { - if (value === null || value === undefined) { - return new Reference( - this._database, - `${this.path}/${generatePushID(this._database._serverTimeOffset)}` - ); - } - - const newRef = new Reference( - this._database, - `${this.path}/${generatePushID(this._database._serverTimeOffset)}` - ); - const promise = newRef.set(value); - - // if callback provided then internally call the set promise with value - if (isFunction(onComplete)) { - return ( - promise - // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 - .then(() => onComplete(null, newRef)) - // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 - .catch(error => onComplete(error, null)) - ); - } - - // otherwise attach promise to 'thenable' reference and return the - // new reference - newRef._setThenable(promise); - return newRef; - } - - /** - * MODIFIERS - */ - - /** - * - * @returns {Reference} - */ - orderByKey(): Reference { - return this.orderBy('orderByKey'); - } - - /** - * - * @returns {Reference} - */ - orderByPriority(): Reference { - return this.orderBy('orderByPriority'); - } - - /** - * - * @returns {Reference} - */ - orderByValue(): Reference { - return this.orderBy('orderByValue'); - } - - /** - * - * @param key - * @returns {Reference} - */ - orderByChild(key: string): Reference { - return this.orderBy('orderByChild', key); - } - - /** - * - * @param name - * @param key - * @returns {Reference} - */ - orderBy(name: string, key?: string): Reference { - const newRef = new Reference( - this._database, - this.path, - this._query.getModifiers() - ); - newRef._query.orderBy(name, key); - return newRef; - } - - /** - * LIMITS - */ - - /** - * - * @param limit - * @returns {Reference} - */ - limitToLast(limit: number): Reference { - return this.limit('limitToLast', limit); - } - - /** - * - * @param limit - * @returns {Reference} - */ - limitToFirst(limit: number): Reference { - return this.limit('limitToFirst', limit); - } - - /** - * - * @param name - * @param limit - * @returns {Reference} - */ - limit(name: string, limit: number): Reference { - const newRef = new Reference( - this._database, - this.path, - this._query.getModifiers() - ); - newRef._query.limit(name, limit); - return newRef; - } - - /** - * FILTERS - */ - - /** - * - * @param value - * @param key - * @returns {Reference} - */ - equalTo(value: any, key?: string): Reference { - return this.filter('equalTo', value, key); - } - - /** - * - * @param value - * @param key - * @returns {Reference} - */ - endAt(value: any, key?: string): Reference { - return this.filter('endAt', value, key); - } - - /** - * - * @param value - * @param key - * @returns {Reference} - */ - startAt(value: any, key?: string): Reference { - return this.filter('startAt', value, key); - } - - /** - * - * @param name - * @param value - * @param key - * @returns {Reference} - */ - 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); - return newRef; - } - - /** - * - * @returns {Disconnect} - */ - onDisconnect(): Disconnect { - return new Disconnect(this); - } - - /** - * Creates a Reference to a child of the current Reference, using a relative path. - * No validation is performed on the path to ensure it has a valid format. - * @param {String} path relative to current ref's location - * @returns {!Reference} A new Reference to the path provided, relative to the current - * Reference - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#child} - */ - child(path: string): Reference { - return new Reference(this._database, `${this.path}/${path}`); - } - - /** - * Return the ref as a path string - * @returns {string} - */ - toString(): string { - return `${this._database.app.options.databaseURL}/${this.path}`; - } - - /** - * Returns whether another Reference represent the same location and are from the - * same instance of firebase.app.App - multiple firebase apps not currently supported. - * @param {Reference} otherRef - Other reference to compare to this one - * @return {Boolean} Whether otherReference is equal to this one - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#isEqual} - */ - isEqual(otherRef: Reference): boolean { - return ( - !!otherRef && - otherRef.constructor === Reference && - otherRef.key === this.key && - this._query.queryIdentifier() === otherRef._query.queryIdentifier() - ); - } - - /** - * GETTERS - */ - - /** - * The parent location of a Reference, or null for the root Reference. - * @type {Reference} - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#parent} - */ - get parent(): Reference | null { - if (this.path === '/') return null; - return new Reference( - this._database, - this.path.substring(0, this.path.lastIndexOf('/')) - ); - } - - /** - * A reference to itself - * @type {!Reference} - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#ref} - */ - get ref(): Reference { - return this; - } - - /** - * Reference to the root of the database: '/' - * @type {!Reference} - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#root} - */ - get root(): Reference { - return new Reference(this._database, '/'); - } - - /** - * Access then method of promise if set - * @return {*} - */ - then(fnResolve: any => any, fnReject: any => any) { - if (isFunction(fnResolve) && this._promise && this._promise.then) { - return this._promise.then.bind(this._promise)( - result => { - this._promise = null; - return fnResolve(result); - }, - possibleErr => { - this._promise = null; - - if (isFunction(fnReject)) { - return fnReject(possibleErr); - } - - throw possibleErr; - } - ); - } - - throw new Error("Cannot read property 'then' of undefined."); - } - - /** - * Access catch method of promise if set - * @return {*} - */ - catch(fnReject: any => any) { - if (isFunction(fnReject) && this._promise && this._promise.catch) { - return this._promise.catch.bind(this._promise)(possibleErr => { - this._promise = null; - return fnReject(possibleErr); - }); - } - - throw new Error("Cannot read property 'catch' of undefined."); - } - - /** - * INTERNALS - */ - - /** - * Generate a unique registration key. - * - * @return {string} - */ - _getRegistrationKey(eventType: string): string { - return `$${this._database.app.name}$/${ - this.path - }$${this._query.queryIdentifier()}$${listeners}$${eventType}`; - } - - /** - * Generate a string that uniquely identifies this - * combination of path and query modifiers - * - * @return {string} - * @private - */ - _getRefKey(): string { - return `$${this._database.app.name}$/${ - this.path - }$${this._query.queryIdentifier()}`; - } - - /** - * Set the promise this 'thenable' reference relates to - * @param promise - * @private - */ - _setThenable(promise: Promise<*>) { - this._promise = promise; - } - - /** - * - * @param obj - * @returns {Object} - * @private - */ - _serializeObject(obj: Object) { - if (!isObject(obj)) return obj; - - // json stringify then parse it calls toString on Objects / Classes - // that support it i.e new Date() becomes a ISO string. - return tryJSONParse(tryJSONStringify(obj)); - } - - /** - * - * @param value - * @returns {*} - * @private - */ - _serializeAnyType(value: any) { - if (isObject(value)) { - return { - type: 'object', - value: this._serializeObject(value), - }; - } - - return { - type: typeof value, - value, - }; - } - - /** - * Register a listener for data changes at the current ref's location. - * The primary method of reading data from a Database. - * - * Listeners can be unbound using {@link off}. - * - * Event Types: - * - * - value: {@link callback}. - * - child_added: {@link callback} - * - child_removed: {@link callback} - * - child_changed: {@link callback} - * - child_moved: {@link callback} - * - * @param {ReferenceEventType} eventType - Type of event to attach a callback for. - * @param {ReferenceEventCallback} callback - Function that will be called - * when the event occurs with the new data. - * @param {cancelCallbackOrContext=} cancelCallbackOrContext - Optional callback that is called - * if the event subscription fails. {@link cancelCallbackOrContext} - * @param {*=} context - Optional object to bind the callbacks to when calling them. - * @returns {ReferenceEventCallback} callback function, unmodified (unbound), for - * convenience if you want to pass an inline function to on() and store it later for - * removing using off(). - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on} - */ - on( - eventType: string, - callback: Snapshot => any, - cancelCallbackOrContext?: Object => any | Object, - context?: Object - ): Function { - if (!eventType) { - throw new Error( - 'Query.on failed: Function called with 0 arguments. Expects at least 2.' - ); - } - - if (!isString(eventType) || !ReferenceEventTypes[eventType]) { - throw new Error( - `Query.on failed: First argument must be a valid string event type: "${Object.keys( - ReferenceEventTypes - ).join(', ')}"` - ); - } - - if (!callback) { - throw new Error( - 'Query.on failed: Function called with 1 argument. Expects at least 2.' - ); - } - - if (!isFunction(callback)) { - throw new Error( - 'Query.on failed: Second argument must be a valid function.' - ); - } - - if ( - cancelCallbackOrContext && - !isFunction(cancelCallbackOrContext) && - !isObject(context) && - !isObject(cancelCallbackOrContext) - ) { - throw new Error( - 'Query.on failed: Function called with 3 arguments, but third optional argument `cancelCallbackOrContext` was not a function.' - ); - } - - if ( - cancelCallbackOrContext && - !isFunction(cancelCallbackOrContext) && - context - ) { - throw new Error( - 'Query.on failed: Function called with 4 arguments, but third optional argument `cancelCallbackOrContext` was not a function.' - ); - } - - const eventRegistrationKey = this._getRegistrationKey(eventType); - const registrationCancellationKey = `${eventRegistrationKey}$cancelled`; - const _context = - cancelCallbackOrContext && !isFunction(cancelCallbackOrContext) - ? cancelCallbackOrContext - : context; - const registrationObj = { - eventType, - ref: this, - path: this.path, - key: this._getRefKey(), - appName: this._database.app.name, - eventRegistrationKey, - }; - - SyncTree.addRegistration({ - ...registrationObj, - listener: _context ? callback.bind(_context) : callback, - }); - - if (cancelCallbackOrContext && isFunction(cancelCallbackOrContext)) { - // cancellations have their own separate registration - // as these are one off events, and they're not guaranteed - // to occur either, only happens on failure to register on native - SyncTree.addRegistration({ - ref: this, - once: true, - path: this.path, - key: this._getRefKey(), - appName: this._database.app.name, - eventType: `${eventType}$cancelled`, - eventRegistrationKey: registrationCancellationKey, - listener: _context - ? cancelCallbackOrContext.bind(_context) - : cancelCallbackOrContext, - }); - } - - // initialise the native listener if not already listening - getNativeModule(this._database).on({ - eventType, - path: this.path, - key: this._getRefKey(), - appName: this._database.app.name, - modifiers: this._query.getModifiers(), - hasCancellationCallback: isFunction(cancelCallbackOrContext), - registration: { - eventRegistrationKey, - key: registrationObj.key, - registrationCancellationKey, - }, - }); - - // increment number of listeners - just s short way of making - // every registration unique per .on() call - listeners += 1; - - // return original unbound successCallback for - // the purposes of calling .off(eventType, callback) at a later date - return callback; - } - - /** - * Detaches a callback previously attached with on(). - * - * Detach a callback previously attached with on(). Note that if on() was called - * multiple times with the same eventType and callback, the callback will be called - * multiple times for each event, and off() must be called multiple times to - * remove the callback. Calling off() on a parent listener will not automatically - * remove listeners registered on child nodes, off() must also be called on any - * child listeners to remove the callback. - * - * If a callback is not specified, all callbacks for the specified eventType will be removed. - * Similarly, if no eventType or callback is specified, all callbacks for the Reference will be removed. - * @param eventType - * @param originalCallback - */ - off(eventType?: string = '', originalCallback?: () => any) { - if (!arguments.length) { - // Firebase Docs: - // if no eventType or callback is specified, all callbacks for the Reference will be removed. - return SyncTree.removeListenersForRegistrations( - SyncTree.getRegistrationsByPath(this.path) - ); - } - - /* - * VALIDATE ARGS - */ - if ( - eventType && - (!isString(eventType) || !ReferenceEventTypes[eventType]) - ) { - throw new Error( - `Query.off failed: First argument must be a valid string event type: "${Object.keys( - ReferenceEventTypes - ).join(', ')}"` - ); - } - - if (originalCallback && !isFunction(originalCallback)) { - throw new Error( - 'Query.off failed: Function called with 2 arguments, but second optional argument was not a function.' - ); - } - - // Firebase Docs: - // Note that if on() was called - // multiple times with the same eventType and callback, the callback will be called - // multiple times for each event, and off() must be called multiple times to - // remove the callback. - // Remove only a single registration - if (eventType && originalCallback) { - const registration = SyncTree.getOneByPathEventListener( - this.path, - eventType, - originalCallback - ); - if (!registration) return []; - - // remove the paired cancellation registration if any exist - SyncTree.removeListenersForRegistrations([`${registration}$cancelled`]); - - // remove only the first registration to match firebase web sdk - // call multiple times to remove multiple registrations - return SyncTree.removeListenerRegistrations(originalCallback, [ - registration, - ]); - } - - // Firebase Docs: - // If a callback is not specified, all callbacks for the specified eventType will be removed. - const registrations = SyncTree.getRegistrationsByPathEvent( - this.path, - eventType - ); - - SyncTree.removeListenersForRegistrations( - SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`) - ); - - return SyncTree.removeListenersForRegistrations(registrations); - } -} From 1199b2bb337b9f0810fd35886b6a3674b088b476 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 10:07:07 +0000 Subject: [PATCH 05/17] [types] Rename back to proper capitalised file name --- lib/modules/database/{query-temp.js => Query.js} | 0 lib/modules/database/{reference-temp.js => Reference.js} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/modules/database/{query-temp.js => Query.js} (100%) rename lib/modules/database/{reference-temp.js => Reference.js} (99%) diff --git a/lib/modules/database/query-temp.js b/lib/modules/database/Query.js similarity index 100% rename from lib/modules/database/query-temp.js rename to lib/modules/database/Query.js diff --git a/lib/modules/database/reference-temp.js b/lib/modules/database/Reference.js similarity index 99% rename from lib/modules/database/reference-temp.js rename to lib/modules/database/Reference.js index e9c98e7b..76e502e7 100644 --- a/lib/modules/database/reference-temp.js +++ b/lib/modules/database/Reference.js @@ -2,7 +2,7 @@ * @flow * Database Reference representation wrapper */ -import Query from './query'; +import Query from './Query'; import DataSnapshot from './DataSnapshot'; import OnDisconnect from './OnDisconnect'; import { getLogger } from '../../utils/log'; From 4e81527246f6c9c9bde2da598bc89ab88d103669 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 13:00:19 +0000 Subject: [PATCH 06/17] [types] Export app, auth, database and firestore types --- lib/index.js | 60 +++++++++++++- lib/modules/admob/index.js | 2 +- lib/modules/analytics/index.js | 2 +- lib/modules/auth/User.js | 10 +-- lib/modules/auth/index.js | 11 +-- lib/modules/auth/types.js | 10 ++- lib/modules/config/index.js | 2 +- lib/modules/core/{firebase-app.js => app.js} | 0 lib/modules/core/firebase.js | 2 +- lib/modules/crash/index.js | 2 +- .../database/{snapshot.js => DataSnapshot.js} | 10 +-- .../{disconnect.js => OnDisconnect.js} | 8 +- lib/modules/database/index.js | 4 +- lib/modules/fabric/crashlytics/index.js | 2 +- lib/modules/firestore/CollectionReference.js | 21 ++--- lib/modules/firestore/DocumentChange.js | 4 +- lib/modules/firestore/DocumentReference.js | 19 ++--- lib/modules/firestore/DocumentSnapshot.js | 14 +--- lib/modules/firestore/Query.js | 22 ++--- lib/modules/firestore/QuerySnapshot.js | 22 ++--- lib/modules/firestore/WriteBatch.js | 12 +-- lib/modules/firestore/index.js | 2 +- lib/modules/firestore/types.js | 54 ++++++++++++ lib/modules/firestore/utils/serialize.js | 16 ++-- lib/modules/links/index.js | 2 +- lib/modules/messaging/index.js | 2 +- lib/modules/perf/index.js | 2 +- lib/modules/storage/index.js | 2 +- lib/modules/utils/index.js | 2 +- lib/types/index.js | 41 ---------- lib/utils/ModuleBase.js | 2 +- lib/utils/SyncTree.js | 8 +- lib/utils/apps.js | 2 +- tests/ios/Podfile.lock | 82 +++++++++---------- 34 files changed, 246 insertions(+), 210 deletions(-) rename lib/modules/core/{firebase-app.js => app.js} (100%) rename lib/modules/database/{snapshot.js => DataSnapshot.js} (95%) rename lib/modules/database/{disconnect.js => OnDisconnect.js} (91%) create mode 100644 lib/modules/firestore/types.js diff --git a/lib/index.js b/lib/index.js index b31d093b..80b57d44 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,6 +3,64 @@ */ import firebase from './modules/core/firebase'; +export default firebase; + +/* + * Export App types + */ +export type { default as App } from './modules/core/app'; + +/* + * Export Auth types + */ +export type { + ActionCodeInfo, + ActionCodeSettings, + AdditionalUserInfo, + AuthCredential, + UserCredential, + UserInfo, + UserMetadata, +} from './modules/auth/types'; +export type { + default as ConfirmationResult, +} from './modules/auth/ConfirmationResult'; export type { default as User } from './modules/auth/User'; -export default firebase; +/* + * Export Database types + */ +export type { default as DataSnapshot } from './modules/database/DataSnapshot'; +export type { default as OnDisconnect } from './modules/database/OnDisconnect'; +export type { default as Reference } from './modules/database/Reference'; +export type { default as DataQuery } from './modules/database/Query'; + +/* + * Export Firestore types + */ +export type { + DocumentListenOptions, + QueryListenOptions, + SetOptions, + SnapshotMetadata, +} from './modules/firestore/types'; +export type { + default as CollectionReference, +} from './modules/firestore/CollectionReference'; +export type { + default as DocumentChange, +} from './modules/firestore/DocumentChange'; +export type { + default as DocumentReference, +} from './modules/firestore/DocumentReference'; +export type { + default as DocumentSnapshot, +} from './modules/firestore/DocumentSnapshot'; +export type { default as FieldPath } from './modules/firestore/FieldPath'; +export type { default as FieldValue } from './modules/firestore/FieldValue'; +export type { default as GeoPoint } from './modules/firestore/GeoPoint'; +export type { default as Query } from './modules/firestore/Query'; +export type { + default as QuerySnapshot, +} from './modules/firestore/QuerySnapshot'; +export type { default as WriteBatch } from './modules/firestore/WriteBatch'; diff --git a/lib/modules/admob/index.js b/lib/modules/admob/index.js index 691a35bb..795099a3 100644 --- a/lib/modules/admob/index.js +++ b/lib/modules/admob/index.js @@ -19,7 +19,7 @@ import EventTypes, { RewardedVideoEventTypes, } from './EventTypes'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; type NativeEvent = { adUnit: string, diff --git a/lib/modules/analytics/index.js b/lib/modules/analytics/index.js index 373753d3..94588b93 100644 --- a/lib/modules/analytics/index.js +++ b/lib/modules/analytics/index.js @@ -5,7 +5,7 @@ import ModuleBase from '../../utils/ModuleBase'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; const AlphaNumericUnderscore = /^[a-zA-Z0-9_]+$/; diff --git a/lib/modules/auth/User.js b/lib/modules/auth/User.js index cd3b3af6..e509bb94 100644 --- a/lib/modules/auth/User.js +++ b/lib/modules/auth/User.js @@ -11,18 +11,10 @@ import type { AuthCredential, NativeUser, UserCredential, + UserInfo, UserMetadata, } from './types'; -type UserInfo = { - displayName?: string, - email?: string, - phoneNumber?: string, - photoURL?: string, - providerId: string, - uid: string, -}; - type UpdateProfile = { displayName?: string, photoURL?: string, diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index 2b18b9df..e7cbe2ed 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -22,26 +22,19 @@ import FacebookAuthProvider from './providers/FacebookAuthProvider'; import PhoneAuthListener from './PhoneAuthListener'; import type { + ActionCodeInfo, ActionCodeSettings, AuthCredential, NativeUser, NativeUserCredential, UserCredential, } from './types'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; type AuthState = { user?: NativeUser, }; -type ActionCodeInfo = { - data: { - email?: string, - fromEmail?: string, - }, - operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL', -}; - const NATIVE_EVENTS = [ 'auth_state_changed', 'auth_id_token_changed', diff --git a/lib/modules/auth/types.js b/lib/modules/auth/types.js index 8f40d619..64655509 100644 --- a/lib/modules/auth/types.js +++ b/lib/modules/auth/types.js @@ -3,6 +3,14 @@ */ import type User from './User'; +export type ActionCodeInfo = { + data: { + email?: string, + fromEmail?: string, + }, + operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL', +}; + export type ActionCodeSettings = { android: { installApp?: boolean, @@ -16,7 +24,7 @@ export type ActionCodeSettings = { url: string, }; -type AdditionalUserInfo = { +export type AdditionalUserInfo = { isNewUser: boolean, profile?: Object, providerId: string, diff --git a/lib/modules/config/index.js b/lib/modules/config/index.js index f6535d6d..c07f18c8 100644 --- a/lib/modules/config/index.js +++ b/lib/modules/config/index.js @@ -6,7 +6,7 @@ import { getLogger } from '../../utils/log'; import ModuleBase from '../../utils/ModuleBase'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; type NativeValue = { stringValue?: string, diff --git a/lib/modules/core/firebase-app.js b/lib/modules/core/app.js similarity index 100% rename from lib/modules/core/firebase-app.js rename to lib/modules/core/app.js diff --git a/lib/modules/core/firebase.js b/lib/modules/core/firebase.js index 13aad29d..c5274d57 100644 --- a/lib/modules/core/firebase.js +++ b/lib/modules/core/firebase.js @@ -6,7 +6,7 @@ import { NativeModules } from 'react-native'; import APPS from '../../utils/apps'; import INTERNALS from '../../utils/internals'; -import App from './firebase-app'; +import App from './app'; import VERSION from '../../version'; // module imports diff --git a/lib/modules/crash/index.js b/lib/modules/crash/index.js index 314d910a..e18ec36b 100644 --- a/lib/modules/crash/index.js +++ b/lib/modules/crash/index.js @@ -5,7 +5,7 @@ import ModuleBase from '../../utils/ModuleBase'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; import type { FirebaseError } from '../../types'; export const MODULE_NAME = 'RNFirebaseCrash'; diff --git a/lib/modules/database/snapshot.js b/lib/modules/database/DataSnapshot.js similarity index 95% rename from lib/modules/database/snapshot.js rename to lib/modules/database/DataSnapshot.js index 572ea85b..31597cbf 100644 --- a/lib/modules/database/snapshot.js +++ b/lib/modules/database/DataSnapshot.js @@ -1,15 +1,15 @@ /** * @flow - * Snapshot representation wrapper + * DataSnapshot representation wrapper */ import { isObject, deepGet, deepExists } from './../../utils'; -import type Reference from './reference'; +import type Reference from './Reference'; /** * @class DataSnapshot * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot */ -export default class Snapshot { +export default class DataSnapshot { ref: Reference; key: string; @@ -50,10 +50,10 @@ export default class Snapshot { * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach * @returns {Snapshot} */ - child(path: string): Snapshot { + child(path: string): DataSnapshot { const value = deepGet(this._value, path); const childRef = this.ref.child(path); - return new Snapshot(childRef, { + return new DataSnapshot(childRef, { value, key: childRef.key, exists: value !== null, diff --git a/lib/modules/database/disconnect.js b/lib/modules/database/OnDisconnect.js similarity index 91% rename from lib/modules/database/disconnect.js rename to lib/modules/database/OnDisconnect.js index 3ec940b6..1662b085 100644 --- a/lib/modules/database/disconnect.js +++ b/lib/modules/database/OnDisconnect.js @@ -1,17 +1,17 @@ /** * @flow - * Disconnect representation wrapper + * OnDisconnect representation wrapper */ import { typeOf } from '../../utils'; import { getNativeModule } from '../../utils/native'; import type Database from './'; -import type Reference from './reference'; +import type Reference from './Reference'; /** * @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect - * @class Disconnect + * @class OmDisconnect */ -export default class Disconnect { +export default class OnDisconnect { _database: Database; ref: Reference; path: string; diff --git a/lib/modules/database/index.js b/lib/modules/database/index.js index c921c565..4967e0e8 100644 --- a/lib/modules/database/index.js +++ b/lib/modules/database/index.js @@ -4,12 +4,12 @@ */ import { NativeModules } from 'react-native'; -import Reference from './reference'; +import Reference from './Reference'; import TransactionHandler from './transaction'; import ModuleBase from '../../utils/ModuleBase'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; const NATIVE_EVENTS = [ 'database_transaction_event', diff --git a/lib/modules/fabric/crashlytics/index.js b/lib/modules/fabric/crashlytics/index.js index b95b9d2d..6b8a4484 100644 --- a/lib/modules/fabric/crashlytics/index.js +++ b/lib/modules/fabric/crashlytics/index.js @@ -5,7 +5,7 @@ import ModuleBase from '../../../utils/ModuleBase'; import { getNativeModule } from '../../../utils/native'; -import type App from '../../core/firebase-app'; +import type App from '../../core/app'; export const MODULE_NAME = 'RNFirebaseCrashlytics'; export const NAMESPACE = 'crashlytics'; diff --git a/lib/modules/firestore/CollectionReference.js b/lib/modules/firestore/CollectionReference.js index 19807680..57e6d2a3 100644 --- a/lib/modules/firestore/CollectionReference.js +++ b/lib/modules/firestore/CollectionReference.js @@ -8,17 +8,13 @@ import { firestoreAutoId } from '../../utils'; import type Firestore from './'; import type { - FirestoreQueryDirection, - FirestoreQueryOperator, -} from '../../types'; + QueryDirection, + QueryListenOptions, + QueryOperator, +} from './types'; import type FieldPath from './FieldPath'; import type Path from './Path'; -import type { - Observer, - ObserverOnError, - ObserverOnNext, - QueryListenOptions, -} from './Query'; +import type { Observer, ObserverOnError, ObserverOnNext } from './Query'; import type QuerySnapshot from './QuerySnapshot'; /** @@ -95,10 +91,7 @@ export default class CollectionReference { ); } - orderBy( - fieldPath: string | FieldPath, - directionStr?: FirestoreQueryDirection - ): Query { + orderBy(fieldPath: string | FieldPath, directionStr?: QueryDirection): Query { return this._query.orderBy(fieldPath, directionStr); } @@ -110,7 +103,7 @@ export default class CollectionReference { return this._query.startAt(snapshotOrVarArgs); } - where(fieldPath: string, opStr: FirestoreQueryOperator, value: any): Query { + where(fieldPath: string, opStr: QueryOperator, value: any): Query { return this._query.where(fieldPath, opStr, value); } } diff --git a/lib/modules/firestore/DocumentChange.js b/lib/modules/firestore/DocumentChange.js index c0656d0a..6adf9577 100644 --- a/lib/modules/firestore/DocumentChange.js +++ b/lib/modules/firestore/DocumentChange.js @@ -5,7 +5,7 @@ import DocumentSnapshot from './DocumentSnapshot'; import type Firestore from './'; -import type { FirestoreNativeDocumentChange } from '../../types'; +import type { NativeDocumentChange } from './types'; /** * @class DocumentChange @@ -16,7 +16,7 @@ export default class DocumentChange { _oldIndex: number; _type: string; - constructor(firestore: Firestore, nativeData: FirestoreNativeDocumentChange) { + constructor(firestore: Firestore, nativeData: NativeDocumentChange) { this._document = new DocumentSnapshot(firestore, nativeData.document); this._newIndex = nativeData.newIndex; this._oldIndex = nativeData.oldIndex; diff --git a/lib/modules/firestore/DocumentReference.js b/lib/modules/firestore/DocumentReference.js index 9fb9ac9c..de5bd8d0 100644 --- a/lib/modules/firestore/DocumentReference.js +++ b/lib/modules/firestore/DocumentReference.js @@ -14,15 +14,12 @@ import { getNativeModule } from '../../utils/native'; import type Firestore from './'; import type { - FirestoreNativeDocumentSnapshot, - FirestoreWriteOptions, -} from '../../types'; + DocumentListenOptions, + NativeDocumentSnapshot, + SetOptions, +} from './types'; import type Path from './Path'; -type DocumentListenOptions = { - includeMetadataChanges: boolean, -}; - type ObserverOnError = Object => void; type ObserverOnNext = DocumentSnapshot => void; @@ -189,9 +186,7 @@ export default class DocumentReference { } const listenerId = firestoreAutoId(); - const listener = ( - nativeDocumentSnapshot: FirestoreNativeDocumentSnapshot - ) => { + const listener = (nativeDocumentSnapshot: NativeDocumentSnapshot) => { const documentSnapshot = new DocumentSnapshot( this.firestore, nativeDocumentSnapshot @@ -227,12 +222,12 @@ export default class DocumentReference { return this._offDocumentSnapshot.bind(this, listenerId, listener); } - set(data: Object, writeOptions?: FirestoreWriteOptions): Promise { + set(data: Object, options?: SetOptions): Promise { const nativeData = buildNativeMap(data); return getNativeModule(this._firestore).documentSet( this.path, nativeData, - writeOptions + options ); } diff --git a/lib/modules/firestore/DocumentSnapshot.js b/lib/modules/firestore/DocumentSnapshot.js index 691c82c4..23e8ef10 100644 --- a/lib/modules/firestore/DocumentSnapshot.js +++ b/lib/modules/firestore/DocumentSnapshot.js @@ -9,10 +9,7 @@ import { isObject } from '../../utils'; import { parseNativeMap } from './utils/serialize'; import type Firestore from './'; -import type { - FirestoreNativeDocumentSnapshot, - FirestoreSnapshotMetadata, -} from '../../types'; +import type { NativeDocumentSnapshot, SnapshotMetadata } from './types'; const extractFieldPathData = (data: Object | void, segments: string[]): any => { if (!data || !isObject(data)) { @@ -30,13 +27,10 @@ const extractFieldPathData = (data: Object | void, segments: string[]): any => { */ export default class DocumentSnapshot { _data: Object | void; - _metadata: FirestoreSnapshotMetadata; + _metadata: SnapshotMetadata; _ref: DocumentReference; - constructor( - firestore: Firestore, - nativeData: FirestoreNativeDocumentSnapshot - ) { + constructor(firestore: Firestore, nativeData: NativeDocumentSnapshot) { this._data = parseNativeMap(firestore, nativeData.data); this._metadata = nativeData.metadata; this._ref = new DocumentReference( @@ -53,7 +47,7 @@ export default class DocumentSnapshot { return this._ref.id; } - get metadata(): FirestoreSnapshotMetadata { + get metadata(): SnapshotMetadata { return this._metadata; } diff --git a/lib/modules/firestore/Query.js b/lib/modules/firestore/Query.js index 176a1dcc..a162d9ec 100644 --- a/lib/modules/firestore/Query.js +++ b/lib/modules/firestore/Query.js @@ -12,20 +12,21 @@ import { firestoreAutoId, isFunction, isObject } from '../../utils'; import { getNativeModule } from '../../utils/native'; import type Firestore from './'; -import type { - FirestoreQueryDirection, - FirestoreQueryOperator, -} from '../../types'; import type Path from './Path'; +import type { + QueryDirection, + QueryOperator, + QueryListenOptions, +} from './types'; -const DIRECTIONS: { [FirestoreQueryDirection]: string } = { +const DIRECTIONS: { [QueryDirection]: string } = { ASC: 'ASCENDING', asc: 'ASCENDING', DESC: 'DESCENDING', desc: 'DESCENDING', }; -const OPERATORS: { [FirestoreQueryOperator]: string } = { +const OPERATORS: { [QueryOperator]: string } = { '=': 'EQUAL', '==': 'EQUAL', '>': 'GREATER_THAN', @@ -58,11 +59,6 @@ type QueryOptions = { startAt?: any[], }; -export type QueryListenOptions = {| - includeDocumentMetadataChanges: boolean, - includeQueryMetadataChanges: boolean, -|}; - export type ObserverOnError = Object => void; export type ObserverOnNext = QuerySnapshot => void; @@ -320,7 +316,7 @@ export default class Query { orderBy( fieldPath: string | FieldPath, - directionStr?: FirestoreQueryDirection = 'asc' + directionStr?: QueryDirection = 'asc' ): Query { // TODO: Validation // validate.isFieldPath('fieldPath', fieldPath); @@ -379,7 +375,7 @@ export default class Query { where( fieldPath: string | FieldPath, - opStr: FirestoreQueryOperator, + opStr: QueryOperator, value: any ): Query { // TODO: Validation diff --git a/lib/modules/firestore/QuerySnapshot.js b/lib/modules/firestore/QuerySnapshot.js index 3ff070db..d7748cf8 100644 --- a/lib/modules/firestore/QuerySnapshot.js +++ b/lib/modules/firestore/QuerySnapshot.js @@ -7,16 +7,16 @@ import DocumentSnapshot from './DocumentSnapshot'; import type Firestore from './'; import type { - FirestoreNativeDocumentChange, - FirestoreNativeDocumentSnapshot, - FirestoreSnapshotMetadata, -} from '../../types'; + NativeDocumentChange, + NativeDocumentSnapshot, + SnapshotMetadata, +} from './types'; import type Query from './Query'; -type QuerySnapshotNativeData = { - changes: FirestoreNativeDocumentChange[], - documents: FirestoreNativeDocumentSnapshot[], - metadata: FirestoreSnapshotMetadata, +type NativeQuerySnapshot = { + changes: NativeDocumentChange[], + documents: NativeDocumentSnapshot[], + metadata: SnapshotMetadata, }; /** @@ -25,13 +25,13 @@ type QuerySnapshotNativeData = { export default class QuerySnapshot { _changes: DocumentChange[]; _docs: DocumentSnapshot[]; - _metadata: FirestoreSnapshotMetadata; + _metadata: SnapshotMetadata; _query: Query; constructor( firestore: Firestore, query: Query, - nativeData: QuerySnapshotNativeData + nativeData: NativeQuerySnapshot ) { this._changes = nativeData.changes.map( change => new DocumentChange(firestore, change) @@ -55,7 +55,7 @@ export default class QuerySnapshot { return this._docs.length === 0; } - get metadata(): FirestoreSnapshotMetadata { + get metadata(): SnapshotMetadata { return this._metadata; } diff --git a/lib/modules/firestore/WriteBatch.js b/lib/modules/firestore/WriteBatch.js index afb3dc47..fe4f15a9 100644 --- a/lib/modules/firestore/WriteBatch.js +++ b/lib/modules/firestore/WriteBatch.js @@ -10,7 +10,7 @@ import { getNativeModule } from '../../utils/native'; import type DocumentReference from './DocumentReference'; import type Firestore from './'; -import type { FirestoreWriteOptions } from '../../types'; +import type { SetOptions } from './types'; type DocumentWrite = { data?: Object, @@ -47,19 +47,15 @@ export default class WriteBatch { return this; } - set( - docRef: DocumentReference, - data: Object, - writeOptions?: FirestoreWriteOptions - ) { + set(docRef: DocumentReference, data: Object, options?: SetOptions) { // TODO: Validation // validate.isDocumentReference('docRef', docRef); // validate.isDocument('data', data); - // validate.isOptionalPrecondition('writeOptions', writeOptions); + // validate.isOptionalPrecondition('options', writeOptions); const nativeData = buildNativeMap(data); this._writes.push({ data: nativeData, - options: writeOptions, + options, path: docRef.path, type: 'SET', }); diff --git a/lib/modules/firestore/index.js b/lib/modules/firestore/index.js index 7b5a185e..d9d02b74 100644 --- a/lib/modules/firestore/index.js +++ b/lib/modules/firestore/index.js @@ -16,7 +16,7 @@ import WriteBatch from './WriteBatch'; import INTERNALS from '../../utils/internals'; import type DocumentSnapshot from './DocumentSnapshot'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; import type QuerySnapshot from './QuerySnapshot'; type CollectionSyncEvent = { diff --git a/lib/modules/firestore/types.js b/lib/modules/firestore/types.js new file mode 100644 index 00000000..6ed935dc --- /dev/null +++ b/lib/modules/firestore/types.js @@ -0,0 +1,54 @@ +/* + * @flow + */ + +export type DocumentListenOptions = { + includeMetadataChanges: boolean, +}; + +export type QueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc'; + +export type QueryListenOptions = {| + includeDocumentMetadataChanges: boolean, + includeQueryMetadataChanges: boolean, +|}; + +export type QueryOperator = '<' | '<=' | '=' | '==' | '>' | '>='; + +export type SetOptions = { + merge?: boolean, +}; + +export type SnapshotMetadata = { + fromCache: boolean, + hasPendingWrites: boolean, +}; + +export type NativeDocumentChange = { + document: NativeDocumentSnapshot, + newIndex: number, + oldIndex: number, + type: string, +}; + +export type NativeDocumentSnapshot = { + data: { [string]: NativeTypeMap }, + metadata: SnapshotMetadata, + path: string, +}; + +export type NativeTypeMap = { + type: + | 'array' + | 'boolean' + | 'date' + | 'documentid' + | 'fieldvalue' + | 'geopoint' + | 'null' + | 'number' + | 'object' + | 'reference' + | 'string', + value: any, +}; diff --git a/lib/modules/firestore/utils/serialize.js b/lib/modules/firestore/utils/serialize.js index 24b8f0a5..90e4d936 100644 --- a/lib/modules/firestore/utils/serialize.js +++ b/lib/modules/firestore/utils/serialize.js @@ -13,7 +13,7 @@ import Path from '../Path'; import { typeOf } from '../../../utils'; import type Firestore from '../'; -import type { FirestoreTypeMap } from '../../../types'; +import type { NativeTypeMap } from '../types'; /* * Functions that build up the data needed to represent @@ -21,9 +21,7 @@ import type { FirestoreTypeMap } from '../../../types'; * for transmission to the native side */ -export const buildNativeMap = ( - data: Object -): { [string]: FirestoreTypeMap } => { +export const buildNativeMap = (data: Object): { [string]: NativeTypeMap } => { const nativeData = {}; if (data) { Object.keys(data).forEach(key => { @@ -36,7 +34,7 @@ export const buildNativeMap = ( return nativeData; }; -export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => { +export const buildNativeArray = (array: Object[]): NativeTypeMap[] => { const nativeArray = []; if (array) { array.forEach(value => { @@ -49,7 +47,7 @@ export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => { return nativeArray; }; -export const buildTypeMap = (value: any): FirestoreTypeMap | null => { +export const buildTypeMap = (value: any): NativeTypeMap | null => { const type = typeOf(value); if (value === null || value === undefined) { return { @@ -117,7 +115,7 @@ export const buildTypeMap = (value: any): FirestoreTypeMap | null => { export const parseNativeMap = ( firestore: Firestore, - nativeData: { [string]: FirestoreTypeMap } + nativeData: { [string]: NativeTypeMap } ): Object | void => { let data; if (nativeData) { @@ -131,7 +129,7 @@ export const parseNativeMap = ( const parseNativeArray = ( firestore: Firestore, - nativeArray: FirestoreTypeMap[] + nativeArray: NativeTypeMap[] ): any[] => { const array = []; if (nativeArray) { @@ -142,7 +140,7 @@ const parseNativeArray = ( return array; }; -const parseTypeMap = (firestore: Firestore, typeMap: FirestoreTypeMap): any => { +const parseTypeMap = (firestore: Firestore, typeMap: NativeTypeMap): any => { const { type, value } = typeMap; if (type === 'null') { return null; diff --git a/lib/modules/links/index.js b/lib/modules/links/index.js index ae475204..28c31879 100644 --- a/lib/modules/links/index.js +++ b/lib/modules/links/index.js @@ -7,7 +7,7 @@ import ModuleBase from '../../utils/ModuleBase'; import { areObjectKeysContainedInOther, isObject, isString } from '../../utils'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; const EVENT_TYPE = { Link: 'dynamic_link_received', diff --git a/lib/modules/messaging/index.js b/lib/modules/messaging/index.js index 4030c5ba..fee50492 100644 --- a/lib/modules/messaging/index.js +++ b/lib/modules/messaging/index.js @@ -8,7 +8,7 @@ import ModuleBase from '../../utils/ModuleBase'; import RemoteMessage from './RemoteMessage'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; const EVENT_TYPE = { RefreshToken: 'messaging_token_refreshed', diff --git a/lib/modules/perf/index.js b/lib/modules/perf/index.js index 8b0eac38..e394d370 100644 --- a/lib/modules/perf/index.js +++ b/lib/modules/perf/index.js @@ -6,7 +6,7 @@ import Trace from './Trace'; import ModuleBase from '../../utils/ModuleBase'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; export const MODULE_NAME = 'RNFirebasePerformance'; export const NAMESPACE = 'perf'; diff --git a/lib/modules/storage/index.js b/lib/modules/storage/index.js index f2fce5f2..3803feb9 100644 --- a/lib/modules/storage/index.js +++ b/lib/modules/storage/index.js @@ -10,7 +10,7 @@ import { getLogger } from '../../utils/log'; import ModuleBase from '../../utils/ModuleBase'; import { getNativeModule } from '../../utils/native'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; const FirebaseStorage = NativeModules.RNFirebaseStorage; diff --git a/lib/modules/utils/index.js b/lib/modules/utils/index.js index f42fbf94..7215f2b1 100644 --- a/lib/modules/utils/index.js +++ b/lib/modules/utils/index.js @@ -3,7 +3,7 @@ import { NativeModules } from 'react-native'; import INTERNALS from '../../utils/internals'; import { isIOS } from '../../utils'; import ModuleBase from '../../utils/ModuleBase'; -import type App from '../core/firebase-app'; +import type App from '../core/app'; const FirebaseCoreModule = NativeModules.RNFirebase; diff --git a/lib/types/index.js b/lib/types/index.js index ac1dc42f..dd54f999 100644 --- a/lib/types/index.js +++ b/lib/types/index.js @@ -162,47 +162,6 @@ export type FirestoreModule = { nativeModuleExists: boolean, } & FirestoreStatics; -export type FirestoreNativeDocumentChange = { - document: FirestoreNativeDocumentSnapshot, - newIndex: number, - oldIndex: number, - type: string, -}; - -export type FirestoreNativeDocumentSnapshot = { - data: { [string]: FirestoreTypeMap }, - metadata: FirestoreSnapshotMetadata, - path: string, -}; - -export type FirestoreSnapshotMetadata = { - fromCache: boolean, - hasPendingWrites: boolean, -}; - -export type FirestoreQueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc'; -export type FirestoreQueryOperator = '<' | '<=' | '=' | '==' | '>' | '>='; - -export type FirestoreTypeMap = { - type: - | 'array' - | 'boolean' - | 'date' - | 'documentid' - | 'fieldvalue' - | 'geopoint' - | 'null' - | 'number' - | 'object' - | 'reference' - | 'string', - value: any, -}; - -export type FirestoreWriteOptions = { - merge?: boolean, -}; - /* Links types */ export type LinksModule = { diff --git a/lib/utils/ModuleBase.js b/lib/utils/ModuleBase.js index 8b68c7fd..83dd5f48 100644 --- a/lib/utils/ModuleBase.js +++ b/lib/utils/ModuleBase.js @@ -4,7 +4,7 @@ import { initialiseLogger } from './log'; import { initialiseNativeModule } from './native'; -import type App from '../modules/core/firebase-app'; +import type App from '../modules/core/app'; import type { FirebaseModuleConfig, FirebaseNamespace } from '../types'; export default class ModuleBase { diff --git a/lib/utils/SyncTree.js b/lib/utils/SyncTree.js index 17de49e3..7c72b7b6 100644 --- a/lib/utils/SyncTree.js +++ b/lib/utils/SyncTree.js @@ -4,11 +4,11 @@ import { NativeEventEmitter, NativeModules } from 'react-native'; import { SharedEventEmitter } from './events'; -import DatabaseSnapshot from '../modules/database/snapshot'; -import DatabaseReference from '../modules/database/reference'; +import DataSnapshot from '../modules/database/DataSnapshot'; +import DatabaseReference from '../modules/database/Reference'; import { isString, nativeToJSError } from '../utils'; -type Listener = DatabaseSnapshot => any; +type Listener = DataSnapshot => any; type Registration = { key: string, @@ -83,7 +83,7 @@ class SyncTree { // forward on to users .on(successCallback <-- listener return SharedEventEmitter.emit( eventRegistrationKey, - new DatabaseSnapshot(registration.ref, snapshot), + new DataSnapshot(registration.ref, snapshot), previousChildName ); } diff --git a/lib/utils/apps.js b/lib/utils/apps.js index 7ff37b34..253265e2 100644 --- a/lib/utils/apps.js +++ b/lib/utils/apps.js @@ -2,7 +2,7 @@ * @flow */ import { NativeModules } from 'react-native'; -import App from '../modules/core/firebase-app'; +import App from '../modules/core/app'; import INTERNALS from './internals'; import { isAndroid, isObject, isString } from './'; diff --git a/tests/ios/Podfile.lock b/tests/ios/Podfile.lock index 480652fa..22babbd1 100644 --- a/tests/ios/Podfile.lock +++ b/tests/ios/Podfile.lock @@ -7,44 +7,44 @@ PODS: - BoringSSL/Interface (9.2) - Crashlytics (3.9.3): - Fabric (~> 1.7.2) - - Fabric (1.7.2) - - Firebase/AdMob (4.8.1): + - Fabric (1.7.3) + - Firebase/AdMob (4.8.2): - Firebase/Core - Google-Mobile-Ads-SDK (= 7.27.0) - - Firebase/Auth (4.8.1): + - Firebase/Auth (4.8.2): - Firebase/Core - FirebaseAuth (= 4.4.2) - - Firebase/Core (4.8.1): - - FirebaseAnalytics (= 4.0.7) + - Firebase/Core (4.8.2): + - FirebaseAnalytics (= 4.0.9) - FirebaseCore (= 4.0.14) - - Firebase/Crash (4.8.1): + - Firebase/Crash (4.8.2): - Firebase/Core - FirebaseCrash (= 2.0.2) - - Firebase/Database (4.8.1): + - Firebase/Database (4.8.2): - Firebase/Core - FirebaseDatabase (= 4.1.4) - - Firebase/DynamicLinks (4.8.1): + - Firebase/DynamicLinks (4.8.2): - Firebase/Core - FirebaseDynamicLinks (= 2.3.2) - - Firebase/Firestore (4.8.1): + - Firebase/Firestore (4.8.2): - Firebase/Core - FirebaseFirestore (= 0.10.0) - - Firebase/Messaging (4.8.1): + - Firebase/Messaging (4.8.2): - Firebase/Core - FirebaseMessaging (= 2.0.8) - - Firebase/Performance (4.8.1): + - Firebase/Performance (4.8.2): - Firebase/Core - FirebasePerformance (= 1.1.1) - - Firebase/RemoteConfig (4.8.1): + - Firebase/RemoteConfig (4.8.2): - Firebase/Core - FirebaseRemoteConfig (= 2.1.1) - - Firebase/Storage (4.8.1): + - Firebase/Storage (4.8.2): - Firebase/Core - FirebaseStorage (= 2.1.2) - FirebaseABTesting (1.0.0): - FirebaseCore (~> 4.0) - Protobuf (~> 3.1) - - FirebaseAnalytics (4.0.7): + - FirebaseAnalytics (4.0.9): - FirebaseCore (~> 4.0) - FirebaseInstanceID (~> 2.0) - GoogleToolboxForMac/NSData+zlib (~> 2.1) @@ -114,26 +114,26 @@ PODS: - GoogleToolboxForMac/Defines (= 2.1.3) - GoogleToolboxForMac/NSString+URLArguments (= 2.1.3) - GoogleToolboxForMac/NSString+URLArguments (2.1.3) - - gRPC (1.8.4): - - gRPC-RxLibrary (= 1.8.4) - - gRPC/Main (= 1.8.4) - - gRPC-Core (1.8.4): - - gRPC-Core/Implementation (= 1.8.4) - - gRPC-Core/Interface (= 1.8.4) - - gRPC-Core/Implementation (1.8.4): + - gRPC (1.9.1): + - gRPC-RxLibrary (= 1.9.1) + - gRPC/Main (= 1.9.1) + - gRPC-Core (1.9.1): + - gRPC-Core/Implementation (= 1.9.1) + - gRPC-Core/Interface (= 1.9.1) + - gRPC-Core/Implementation (1.9.1): - BoringSSL (~> 9.0) - - gRPC-Core/Interface (= 1.8.4) + - gRPC-Core/Interface (= 1.9.1) - nanopb (~> 0.3) - - gRPC-Core/Interface (1.8.4) - - gRPC-ProtoRPC (1.8.4): - - gRPC (= 1.8.4) - - gRPC-RxLibrary (= 1.8.4) + - gRPC-Core/Interface (1.9.1) + - gRPC-ProtoRPC (1.9.1): + - gRPC (= 1.9.1) + - gRPC-RxLibrary (= 1.9.1) - Protobuf (~> 3.0) - - gRPC-RxLibrary (1.8.4) - - gRPC/Main (1.8.4): - - gRPC-Core (= 1.8.4) - - gRPC-RxLibrary (= 1.8.4) - - GTMSessionFetcher/Core (1.1.12) + - gRPC-RxLibrary (1.9.1) + - gRPC/Main (1.9.1): + - gRPC-Core (= 1.9.1) + - gRPC-RxLibrary (= 1.9.1) + - GTMSessionFetcher/Core (1.1.13) - leveldb-library (1.20) - nanopb (0.3.8): - nanopb/decode (= 0.3.8) @@ -164,7 +164,7 @@ PODS: - React/Core - React/fishhook - React/RCTBlob - - RNFirebase (3.2.0): + - RNFirebase (3.2.4): - React - yoga (0.52.0.React) @@ -201,10 +201,10 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: BoringSSL: f3d6b8ce199b9c450a8cfc14895d07a2627fc232 Crashlytics: dbb07d01876c171c5ccbdf7826410380189e452c - Fabric: 9cd6a848efcf1b8b07497e0b6a2e7d336353ba15 - Firebase: 2721056b8885eef90233b03f37be64358d35d262 + Fabric: bb495bb9a7a7677c6d03a1f8b83d95bc49b47e41 + Firebase: 7d3b8cd837ad9fcd391657734c0d56dab8e9a5a3 FirebaseABTesting: d07d0ee833b842d5153549e4c7e2e2cb1c23a3f9 - FirebaseAnalytics: 617afa8c26b57a0c3f11361b248bc9e17bfd8dfd + FirebaseAnalytics: 388b630c15713f5dbf364071f5f3d6077fb52f4e FirebaseAuth: bd2738c5c1e92b108ba5f7f7335908097a4e50bb FirebaseCore: 2e0b98fb2d64ca8140136beff15772bdd14d2dd7 FirebaseCrash: cded0fc566c03651aea606a101bc156085f333ca @@ -219,16 +219,16 @@ SPEC CHECKSUMS: FirebaseSwizzlingUtilities: f1c49a5a372ac852c853722a5891a0a5e2344a6c Google-Mobile-Ads-SDK: 83f7f890e638ce8f1debd440ea363338c9f6be3b GoogleToolboxForMac: 2501e2ad72a52eb3dfe7bd9aee7dad11b858bd20 - gRPC: 572520c17b794362388d5c95396329592a3c199b - gRPC-Core: af0d4f0a53735e335fccc815c50c0a03da695287 - gRPC-ProtoRPC: 6596fde8d27e0718d7de1de1dc99a951d832a809 - gRPC-RxLibrary: f6b1432a667c3354c7b345affed9886c0d4ff549 - GTMSessionFetcher: ebaa1f79a5366922c1735f1566901f50beba23b7 + gRPC: 58828d611419d49da19ad02a60679ffa10a10a87 + gRPC-Core: 66413bf1f2d038a6221bc7bfcbeeaa5a117cee29 + gRPC-ProtoRPC: f29e8b7445e0d3c0311678ab121e6c164da4ca5e + gRPC-RxLibrary: 8e0067bfe8a054022c7a81470baace4f2f633b48 + GTMSessionFetcher: 5bb1eae636127de695590f50e7d248483eb891e6 leveldb-library: '08cba283675b7ed2d99629a4bc5fd052cd2bb6a5' nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 Protobuf: 8a9838fba8dae3389230e1b7f8c104aa32389c03 React: 61a6bdf17a9ff16875c230e6ff278d9de274e16c - RNFirebase: 22b1917fec663706907bc901ed665ac4f8b9bfd6 + RNFirebase: 011e47909cf54070f72d50b8d61eb7b347774d29 yoga: 646606bf554d54a16711f35596178522fbc00480 PODFILE CHECKSUM: 67c98bcb203cb992da590bcab6f690f727653ca5 From 5356b949d755edc1c22ed4f41cc2c6e03af14e32 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 15:10:41 +0000 Subject: [PATCH 07/17] 3.2.5 --- package-lock.json | 2 +- package.json | 57 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e75de32..2628af9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-native-firebase", - "version": "3.2.4", + "version": "3.2.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 14d7eca9..2fa4c641 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-firebase", - "version": "3.2.4", + "version": "3.2.5", "author": "Invertase (http://invertase.io)", "description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Messaging (FCM), Remote Config, Storage and Performance.", "main": "dist/index.js", @@ -32,10 +32,46 @@ "jest": { "preset": "jest-react-native", "setupFiles": [], - "unmockedModulePathPatterns": ["./node_modules/react", "./node_modules/react-native", "./node_modules/react-native-mock", "./node_modules/react-addons-test-utils"] + "unmockedModulePathPatterns": [ + "./node_modules/react", + "./node_modules/react-native", + "./node_modules/react-native-mock", + "./node_modules/react-addons-test-utils" + ] }, "license": "APACHE-2.0", - "keywords": ["react", "admob", "auth", "config", "digits", "fabric", "phone-auth", "sms", "firestore", "cloud-firestore", "datastore", "remote-config", "transactions", "react-native", "react-native-firebase", "firebase", "fcm", "apn", "gcm", "analytics", "messaging", "database", "android", "ios", "crash", "firestack", "performance", "firestore", "dynamic-links", "crashlytics"], + "keywords": [ + "react", + "admob", + "auth", + "config", + "digits", + "fabric", + "phone-auth", + "sms", + "firestore", + "cloud-firestore", + "datastore", + "remote-config", + "transactions", + "react-native", + "react-native-firebase", + "firebase", + "fcm", + "apn", + "gcm", + "analytics", + "messaging", + "database", + "android", + "ios", + "crash", + "firestack", + "performance", + "firestore", + "dynamic-links", + "crashlytics" + ], "peerDependencies": { "react": "*", "react-native": ">= 0.48.0", @@ -93,8 +129,17 @@ "logo": "https://opencollective.com/opencollective/logo.txt" }, "lint-staged": { - "lib/**/*.js": ["eslint --fix", "git add"], - "tests/{src|lib}/**/*.js": ["eslint --fix", "git add"], - "*.{json,md,scss}": ["prettier --write", "git add"] + "lib/**/*.js": [ + "eslint --fix", + "git add" + ], + "tests/{src|lib}/**/*.js": [ + "eslint --fix", + "git add" + ], + "*.{json,md,scss}": [ + "prettier --write", + "git add" + ] } } From 60d847cf781475e747fddfb779ed76bc28deaf35 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhus Date: Thu, 15 Feb 2018 00:17:05 +0100 Subject: [PATCH 08/17] [types] Use literal values for strings --- lib/modules/config/index.js | 6 +++--- lib/utils/emitter/EventEmitter.js | 2 +- lib/utils/index.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/modules/config/index.js b/lib/modules/config/index.js index c07f18c8..9f5136f2 100644 --- a/lib/modules/config/index.js +++ b/lib/modules/config/index.js @@ -123,7 +123,7 @@ export default class RemoteConfig extends ModuleBase { * "source" : OneOf(remoteConfigSourceRemote|remoteConfigSourceDefault|remoteConfigSourceStatic) * } */ - getValue(key: String) { + getValue(key: string) { return getNativeModule(this) .getValue(key || '') .then(this._nativeValueToJS); @@ -160,7 +160,7 @@ export default class RemoteConfig extends ModuleBase { * @param prefix: The key prefix to look for. If prefix is nil or empty, returns all the keys. * @returns {*|Promise.>} */ - getKeysByPrefix(prefix?: String) { + getKeysByPrefix(prefix?: string) { return getNativeModule(this).getKeysByPrefix(prefix); } @@ -176,7 +176,7 @@ export default class RemoteConfig extends ModuleBase { * Sets default configs from plist for default namespace; * @param resource: The plist file name or resource ID */ - setDefaultsFromResource(resource: String | number) { + setDefaultsFromResource(resource: string | number) { getNativeModule(this).setDefaultsFromResource(resource); } } diff --git a/lib/utils/emitter/EventEmitter.js b/lib/utils/emitter/EventEmitter.js index 4c805daf..96487b1e 100644 --- a/lib/utils/emitter/EventEmitter.js +++ b/lib/utils/emitter/EventEmitter.js @@ -201,7 +201,7 @@ class EventEmitter { * }); // removes the listener if already registered * */ - removeListener(eventType: String, listener) { + removeListener(eventType: string, listener) { const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any); if (subscriptions) { for (let i = 0, l = subscriptions.length; i < l; i++) { diff --git a/lib/utils/index.js b/lib/utils/index.js index 5ff9ba85..d832503c 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -282,7 +282,7 @@ export function typeOf(value: any): string { // * @param string // * @return {string} // */ -// export function capitalizeFirstLetter(string: String) { +// export function capitalizeFirstLetter(string: string) { // return `${string.charAt(0).toUpperCase()}${string.slice(1)}`; // } From 1623f389b8172f22a6024bbd8587362c0b950207 Mon Sep 17 00:00:00 2001 From: skovhus Date: Thu, 15 Feb 2018 19:07:15 +0100 Subject: [PATCH 09/17] Remove error log from RNFirebaseRemoteConfig Leave it up to the consumer of the library to handle any errors instead of forcing a red error screen. In case the app is offline, it is perfectly fine to get a FIRRemoteConfigErrorInternalError error. --- ios/RNFirebase/config/RNFirebaseRemoteConfig.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios/RNFirebase/config/RNFirebaseRemoteConfig.m b/ios/RNFirebase/config/RNFirebaseRemoteConfig.m index 0b3cf6e1..fe9c4061 100644 --- a/ios/RNFirebase/config/RNFirebaseRemoteConfig.m +++ b/ios/RNFirebase/config/RNFirebaseRemoteConfig.m @@ -49,7 +49,6 @@ RCT_EXPORT_METHOD(fetch: (RCTPromiseRejectBlock) reject) { [[FIRRemoteConfig remoteConfig] fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError *__nullable error) { if (error) { - RCTLogError(@"\nError: %@", RCTJSErrorFromNSError(error)); reject(convertFIRRemoteConfigFetchStatusToNSString(status), error.localizedDescription, error); } else { resolve(convertFIRRemoteConfigFetchStatusToNSString(status)); @@ -64,7 +63,6 @@ RCT_EXPORT_METHOD(fetchWithExpirationDuration: rejecter:(RCTPromiseRejectBlock)reject) { [[FIRRemoteConfig remoteConfig] fetchWithExpirationDuration:expirationDuration.doubleValue completionHandler:^(FIRRemoteConfigFetchStatus status, NSError *__nullable error) { if (error) { - RCTLogError(@"\nError: %@", RCTJSErrorFromNSError(error)); reject(convertFIRRemoteConfigFetchStatusToNSString(status), error.localizedDescription, error); } else { resolve(convertFIRRemoteConfigFetchStatusToNSString(status)); From c57c6a74a848fafead8bc838e28bf56c29dd6a54 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Sat, 17 Feb 2018 12:55:19 +0000 Subject: [PATCH 10/17] [flow] Remove custom flow error tag --- .flowconfig | 1 - lib/modules/auth/PhoneAuthListener.js | 2 +- lib/modules/core/app.js | 2 +- lib/modules/database/Reference.js | 4 ++-- lib/modules/firestore/DocumentReference.js | 6 +++--- lib/modules/firestore/Query.js | 6 +++--- lib/modules/storage/task.js | 2 +- lib/utils/apps.js | 4 ++-- lib/utils/internals.js | 12 ++++++------ 9 files changed, 19 insertions(+), 20 deletions(-) diff --git a/.flowconfig b/.flowconfig index 945dfd07..60cc3456 100644 --- a/.flowconfig +++ b/.flowconfig @@ -55,7 +55,6 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(si suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError -suppress_comment=\\(.\\|\n\\)*\\$FlowBug.* unsafe.enable_getters_and_setters=true diff --git a/lib/modules/auth/PhoneAuthListener.js b/lib/modules/auth/PhoneAuthListener.js index 24033318..d85bcace 100644 --- a/lib/modules/auth/PhoneAuthListener.js +++ b/lib/modules/auth/PhoneAuthListener.js @@ -109,7 +109,7 @@ export default class PhoneAuthListener { const type = events[i]; SharedEventEmitter.once( this._internalEvents[type], - // $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 + // $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 this[`_${type}Handler`].bind(this) ); } diff --git a/lib/modules/core/app.js b/lib/modules/core/app.js index 1f81e6aa..95a8a499 100644 --- a/lib/modules/core/app.js +++ b/lib/modules/core/app.js @@ -129,7 +129,7 @@ export default class App { throw new Error(INTERNALS.STRINGS.ERROR_PROTECTED_PROP(key)); } - // $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 + // $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 this[key] = props[key]; this._extendedProps[key] = true; } diff --git a/lib/modules/database/Reference.js b/lib/modules/database/Reference.js index 76e502e7..9702da0e 100644 --- a/lib/modules/database/Reference.js +++ b/lib/modules/database/Reference.js @@ -317,9 +317,9 @@ export default class Reference extends ReferenceBase { if (isFunction(onComplete)) { return ( promise - // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 + // $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 .then(() => onComplete(null, newRef)) - // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 + // $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 .catch(error => onComplete(error, null)) ); } diff --git a/lib/modules/firestore/DocumentReference.js b/lib/modules/firestore/DocumentReference.js index de5bd8d0..91772833 100644 --- a/lib/modules/firestore/DocumentReference.js +++ b/lib/modules/firestore/DocumentReference.js @@ -96,7 +96,7 @@ export default class DocumentReference { 'DocumentReference.onSnapshot failed: Second argument must be a valid function.' ); } - // $FlowBug: Not coping with the overloaded method signature + // $FlowExpectedError: Not coping with the overloaded method signature observer = { next: optionsOrObserverOrOnNext, error: observerOrOnNextOrOnError, @@ -116,7 +116,7 @@ export default class DocumentReference { 'DocumentReference.onSnapshot failed: Observer.error must be a valid function.' ); } - // $FlowBug: Not coping with the overloaded method signature + // $FlowExpectedError: Not coping with the overloaded method signature observer = { next: optionsOrObserverOrOnNext.next, error: optionsOrObserverOrOnNext.error, @@ -140,7 +140,7 @@ export default class DocumentReference { 'DocumentReference.onSnapshot failed: Third argument must be a valid function.' ); } - // $FlowBug: Not coping with the overloaded method signature + // $FlowExpectedError: Not coping with the overloaded method signature observer = { next: observerOrOnNextOrOnError, error: onError, diff --git a/lib/modules/firestore/Query.js b/lib/modules/firestore/Query.js index a162d9ec..270b87e2 100644 --- a/lib/modules/firestore/Query.js +++ b/lib/modules/firestore/Query.js @@ -183,7 +183,7 @@ export default class Query { 'Query.onSnapshot failed: Second argument must be a valid function.' ); } - // $FlowBug: Not coping with the overloaded method signature + // $FlowExpectedError: Not coping with the overloaded method signature observer = { next: optionsOrObserverOrOnNext, error: observerOrOnNextOrOnError, @@ -203,7 +203,7 @@ export default class Query { 'Query.onSnapshot failed: Observer.error must be a valid function.' ); } - // $FlowBug: Not coping with the overloaded method signature + // $FlowExpectedError: Not coping with the overloaded method signature observer = { next: optionsOrObserverOrOnNext.next, error: optionsOrObserverOrOnNext.error, @@ -231,7 +231,7 @@ export default class Query { 'Query.onSnapshot failed: Third argument must be a valid function.' ); } - // $FlowBug: Not coping with the overloaded method signature + // $FlowExpectedError: Not coping with the overloaded method signature observer = { next: observerOrOnNextOrOnError, error: onError, diff --git a/lib/modules/storage/task.js b/lib/modules/storage/task.js index 95b0605f..0fdc6329 100644 --- a/lib/modules/storage/task.js +++ b/lib/modules/storage/task.js @@ -92,7 +92,7 @@ export default class StorageTask { if (!isFunction(f)) return null; return error => { const _error = new Error(error.message); - // $FlowFixMe + // $FlowExpectedError _error.code = error.code; return f && f(_error); }; diff --git a/lib/utils/apps.js b/lib/utils/apps.js index 253265e2..cc061736 100644 --- a/lib/utils/apps.js +++ b/lib/utils/apps.js @@ -32,7 +32,7 @@ export default { }, apps(): Array { - // $FlowBug: Object.values always returns mixed type: https://github.com/facebook/flow/issues/2221 + // $FlowExpectedError: Object.values always returns mixed type: https://github.com/facebook/flow/issues/2221 return Object.values(APPS); }, @@ -174,7 +174,7 @@ export default { if (namespace === 'crashlytics') { return _app.fabric[namespace](); } - // $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 + // $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 const module = _app[namespace]; return module(); }; diff --git a/lib/utils/internals.js b/lib/utils/internals.js index 05e611f7..de46d40a 100644 --- a/lib/utils/internals.js +++ b/lib/utils/internals.js @@ -21,35 +21,35 @@ const GRADLE_DEPS = { }; const PLAY_SERVICES_CODES = { - // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 + // $FlowExpectedError: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 1: { code: 'SERVICE_MISSING', message: 'Google Play services is missing on this device.', }, - // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 + // $FlowExpectedError: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 2: { code: 'SERVICE_VERSION_UPDATE_REQUIRED', message: 'The installed version of Google Play services on this device is out of date.', }, - // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 + // $FlowExpectedError: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 3: { code: 'SERVICE_DISABLED', message: 'The installed version of Google Play services has been disabled on this device.', }, - // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 + // $FlowExpectedError: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 9: { code: 'SERVICE_INVALID', message: 'The version of the Google Play services installed on this device is not authentic.', }, - // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 + // $FlowExpectedError: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 18: { code: 'SERVICE_UPDATING', message: 'Google Play services is currently being updated on this device.', }, - // $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 + // $FlowExpectedError: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380 19: { code: 'SERVICE_MISSING_PERMISSION', message: From 5ca722251795ba36fc6d9cea0d6deab2399002f2 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Sat, 17 Feb 2018 13:22:46 +0000 Subject: [PATCH 11/17] [build] Remove unnecessary `providesModule` declaration --- lib/modules/core/firebase.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/modules/core/firebase.js b/lib/modules/core/firebase.js index c5274d57..90bc69ce 100644 --- a/lib/modules/core/firebase.js +++ b/lib/modules/core/firebase.js @@ -1,5 +1,4 @@ /** - * @providesModule Firebase * @flow */ import { NativeModules } from 'react-native'; From 0b9681efc2e98693699fcc3bb34d944dcafb1e96 Mon Sep 17 00:00:00 2001 From: Michael Diarmid Date: Mon, 19 Feb 2018 11:00:37 +0000 Subject: [PATCH 12/17] Update CONTRIBUTING.md --- CONTRIBUTING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79b02ec8..21a22ed8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,10 +7,12 @@ First, thank you for considering contributing to react-native-firebase! It's peo We welcome any type of contribution, not only code. You can help with * **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) +* **Docs**: improve reference coverage, add more examples, fix any typos or anything else you can spot. At the top of every page on our docs site click the `Edit` pencil to go to that pages markdown file, or view the [Docs Repo](https://github.com/invertase/react-native-firebase-docs) directly * **Marketing**: writing blog posts, howto's, printing stickers, ... * **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... * **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. * **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-native-firebase). +* **Premium Starter Kits**: purchasing one of our starter kits helps us continue to develop the library and support its ever growing community (and you get something too!). [View Kits](http://invertase.link/get-started-premium) ## Your First Contribution @@ -32,8 +34,7 @@ Anyone can file an expense. If the expense makes sense for the development of th ## Questions -If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!). -You can also reach us at oss@invertase.io +For questions and support please use our [Discord chat](https://discord.gg/C9aK28N) or [Stack Overflow](https://stackoverflow.com/questions/tagged/react-native-firebase). The issue list of this repo is **exclusively** for bug reports. ## Credits From fd028932c8d44033bea1f4852546b7e8755c7792 Mon Sep 17 00:00:00 2001 From: Michael Diarmid Date: Mon, 19 Feb 2018 11:01:24 +0000 Subject: [PATCH 13/17] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21a22ed8..946e8ee4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ First, thank you for considering contributing to react-native-firebase! It's peo We welcome any type of contribution, not only code. You can help with * **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) -* **Docs**: improve reference coverage, add more examples, fix any typos or anything else you can spot. At the top of every page on our docs site click the `Edit` pencil to go to that pages markdown file, or view the [Docs Repo](https://github.com/invertase/react-native-firebase-docs) directly +* **Docs**: improve reference coverage, add more examples, fix any typos or anything else you can spot. At the top of every page on our docs site you can click the `Edit` pencil to go to that pages markdown file, or view the [Docs Repo](https://github.com/invertase/react-native-firebase-docs) directly * **Marketing**: writing blog posts, howto's, printing stickers, ... * **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... * **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. From 6f5eda42155267b48ec65963a0470d64006f239f Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Thu, 22 Feb 2018 15:54:48 +0000 Subject: [PATCH 14/17] 3.2.6 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2628af9a..5ff661e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-native-firebase", - "version": "3.2.5", + "version": "3.2.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2fa4c641..082e8229 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-firebase", - "version": "3.2.5", + "version": "3.2.6", "author": "Invertase (http://invertase.io)", "description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Messaging (FCM), Remote Config, Storage and Performance.", "main": "dist/index.js", From e73812a1e081751d8f82d0b8ddaadb0134fb4dcb Mon Sep 17 00:00:00 2001 From: Salakar Date: Sat, 24 Feb 2018 05:53:07 +0000 Subject: [PATCH 15/17] fix #848 --- lib/modules/core/app.js | 13 ++++++++++++- lib/utils/apps.js | 6 ++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/modules/core/app.js b/lib/modules/core/app.js index 95a8a499..c77ed4b6 100644 --- a/lib/modules/core/app.js +++ b/lib/modules/core/app.js @@ -116,10 +116,12 @@ export default class App { * @param props */ extendApp(props: Object) { - if (!isObject(props)) + if (!isObject(props)) { throw new Error( INTERNALS.STRINGS.ERROR_MISSING_ARG('Object', 'extendApp') ); + } + const keys = Object.keys(props); for (let i = 0, len = keys.length; i < len; i++) { @@ -167,4 +169,13 @@ export default class App { }); }); } + + /** + * toString returns the name of the app. + * + * @return {string} + */ + toString() { + return this._name; + } } diff --git a/lib/utils/apps.js b/lib/utils/apps.js index cc061736..ac6f84c9 100644 --- a/lib/utils/apps.js +++ b/lib/utils/apps.js @@ -38,7 +38,8 @@ export default { /** * - * @param statics + * @param app + * @param namespace * @param InstanceClass * @return {function()} * @private @@ -152,8 +153,9 @@ export default { /** * + * @param namespace * @param statics - * @param InstanceClass + * @param moduleName * @return {function(App=)} */ moduleAndStatics( From b31569ba4c306ccbd02d8242eee738a84eb5d80d Mon Sep 17 00:00:00 2001 From: Salakar Date: Sat, 24 Feb 2018 05:55:58 +0000 Subject: [PATCH 16/17] 3.2.7 --- package-lock.json | 2 +- package.json | 57 +++++------------------------------------------ 2 files changed, 7 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ff661e3..e3763c91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-native-firebase", - "version": "3.2.6", + "version": "3.2.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 082e8229..24966bab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-firebase", - "version": "3.2.6", + "version": "3.2.7", "author": "Invertase (http://invertase.io)", "description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Messaging (FCM), Remote Config, Storage and Performance.", "main": "dist/index.js", @@ -32,46 +32,10 @@ "jest": { "preset": "jest-react-native", "setupFiles": [], - "unmockedModulePathPatterns": [ - "./node_modules/react", - "./node_modules/react-native", - "./node_modules/react-native-mock", - "./node_modules/react-addons-test-utils" - ] + "unmockedModulePathPatterns": ["./node_modules/react", "./node_modules/react-native", "./node_modules/react-native-mock", "./node_modules/react-addons-test-utils"] }, "license": "APACHE-2.0", - "keywords": [ - "react", - "admob", - "auth", - "config", - "digits", - "fabric", - "phone-auth", - "sms", - "firestore", - "cloud-firestore", - "datastore", - "remote-config", - "transactions", - "react-native", - "react-native-firebase", - "firebase", - "fcm", - "apn", - "gcm", - "analytics", - "messaging", - "database", - "android", - "ios", - "crash", - "firestack", - "performance", - "firestore", - "dynamic-links", - "crashlytics" - ], + "keywords": ["react", "admob", "auth", "config", "digits", "fabric", "phone-auth", "sms", "firestore", "cloud-firestore", "datastore", "remote-config", "transactions", "react-native", "react-native-firebase", "firebase", "fcm", "apn", "gcm", "analytics", "messaging", "database", "android", "ios", "crash", "firestack", "performance", "firestore", "dynamic-links", "crashlytics"], "peerDependencies": { "react": "*", "react-native": ">= 0.48.0", @@ -129,17 +93,8 @@ "logo": "https://opencollective.com/opencollective/logo.txt" }, "lint-staged": { - "lib/**/*.js": [ - "eslint --fix", - "git add" - ], - "tests/{src|lib}/**/*.js": [ - "eslint --fix", - "git add" - ], - "*.{json,md,scss}": [ - "prettier --write", - "git add" - ] + "lib/**/*.js": ["eslint --fix", "git add"], + "tests/{src|lib}/**/*.js": ["eslint --fix", "git add"], + "*.{json,md,scss}": ["prettier --write", "git add"] } } From 7ec62f94151c2a69cfe0edc987be8a7845a1f82f Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Thu, 1 Mar 2018 15:04:33 +0000 Subject: [PATCH 17/17] [tests] Add missing core tests --- tests/src/tests/core/coreTests.js | 81 +++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/tests/src/tests/core/coreTests.js b/tests/src/tests/core/coreTests.js index c7990982..6d34a374 100644 --- a/tests/src/tests/core/coreTests.js +++ b/tests/src/tests/core/coreTests.js @@ -1,7 +1,7 @@ import { Platform } from 'react-native'; import should from 'should'; -import RNFirebase from './../../../firebase'; +import firebase from './../../../firebase'; const androidTestConfig = { // firebase android sdk completely ignores client id @@ -33,52 +33,47 @@ function rand(from = 1, to = 9999) { } function coreTests({ describe, it }) { - describe('Core', () => { + describe('Firebase', () => { it('it should create js apps for natively initialized apps', () => { - should.equal(RNFirebase.app()._nativeInitialized, true); + should.equal(firebase.app()._nativeInitialized, true); return Promise.resolve(); }); it('natively initialized apps should have options available in js', () => { should.equal( - RNFirebase.app().options.apiKey, + firebase.app().options.apiKey, Platform.OS === 'ios' ? iosTestConfig.apiKey : androidTestConfig.apiKey ); should.equal( - RNFirebase.app().options.appId, + firebase.app().options.appId, Platform.OS === 'ios' ? iosTestConfig.appId : androidTestConfig.appId ); should.equal( - RNFirebase.app().options.databaseURL, + firebase.app().options.databaseURL, iosTestConfig.databaseURL ); should.equal( - RNFirebase.app().options.messagingSenderId, + firebase.app().options.messagingSenderId, iosTestConfig.messagingSenderId ); - should.equal(RNFirebase.app().options.projectId, iosTestConfig.projectId); + should.equal(firebase.app().options.projectId, iosTestConfig.projectId); should.equal( - RNFirebase.app().options.storageBucket, + firebase.app().options.storageBucket, iosTestConfig.storageBucket ); return Promise.resolve(); }); it('it should resolve onReady for natively initialized apps', () => - RNFirebase.app().onReady()); - - it('it should provide an array of apps', () => { - should.equal(!!RNFirebase.apps.length, true); - should.equal(RNFirebase.apps.includes(RNFirebase.app('[DEFAULT]')), true); - return Promise.resolve(); - }); + firebase.app().onReady()); it('it should initialize dynamic apps', () => { const name = `testscoreapp${rand()}`; - return RNFirebase.initializeApp( - Platform.OS === 'ios' ? iosTestConfig : androidTestConfig, - name - ) + return firebase + .initializeApp( + Platform.OS === 'ios' ? iosTestConfig : androidTestConfig, + name + ) .onReady() .then(newApp => { newApp.name.should.equal(name.toUpperCase()); @@ -90,6 +85,52 @@ function coreTests({ describe, it }) { return Promise.resolve(); }); }); + + it('SDK_VERSION should return a string version', () => { + firebase.SDK_VERSION.should.be.a.String(); + }); + }); + + describe('App', () => { + it('apps should provide an array of apps', () => { + should.equal(!!firebase.apps.length, true); + should.equal(firebase.apps.includes(firebase.app('[DEFAULT]')), true); + return Promise.resolve(); + }); + + it('delete is unsupported', () => { + (() => { + firebase.app().delete(); + }).should.throw( + 'app.delete() is unsupported by the native Firebase SDKs.' + ); + }); + + it('extendApp should error if an object is not supplied', () => { + (() => { + firebase.app().extendApp('string'); + }).should.throw( + "Missing required argument of type 'Object' for method 'extendApp()'." + ); + }); + + it('extendApp should error if a protected property is supplied', () => { + (() => { + firebase.app().extendApp({ + database: {}, + }); + }).should.throw( + "Property 'database' is protected and can not be overridden by extendApp." + ); + }); + + it('extendApp should provide additional functionality', () => { + const extension = {}; + firebase.app().extendApp({ + extension, + }); + firebase.app().extension.should.equal(extension); + }); }); }