2
0
mirror of synced 2025-01-09 13:45:50 +00:00

[types] Get flow type working again; Fix majority of firestore type issues

This commit is contained in:
Chris Bianca 2017-11-17 11:07:52 +00:00
parent 5c43c88f6a
commit 8bd9684644
24 changed files with 2743 additions and 953 deletions

View File

@ -34,7 +34,13 @@
"import/extensions": 0, "import/extensions": 0,
"import/no-unresolved": 0, "import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0, "import/no-extraneous-dependencies": 0,
"react/jsx-filename-extension": 0 "react/jsx-filename-extension": 0,
"no-unused-expressions": 0,
"flowtype/no-unused-expressions": ['error', {
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
}]
}, },
"globals": { "globals": {
"__DEV__": true, "__DEV__": true,

View File

@ -91,7 +91,7 @@ unsafe.enable_getters_and_setters=true
esproposal.class_static_fields=enable esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable esproposal.class_instance_fields=enable
munge_underscores=true munge_underscores=false
module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
@ -105,4 +105,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version] [version]
^0.46.0 ^0.56.0

View File

@ -1,3 +1,6 @@
/*
* @flow
*/
import { NativeModules } from 'react-native'; import { NativeModules } from 'react-native';
import INTERNALS from './internals'; import INTERNALS from './internals';
@ -16,10 +19,31 @@ import Firestore, { statics as FirestoreStatics } from './modules/firestore';
import Links, { statics as LinksStatics } from './modules/links'; import Links, { statics as LinksStatics } from './modules/links';
import Utils, { statics as UtilsStatics } from './modules/utils'; import Utils, { statics as UtilsStatics } from './modules/utils';
import type { FirebaseOptions } from './firebase';
const FirebaseCoreModule = NativeModules.RNFirebase; const FirebaseCoreModule = NativeModules.RNFirebase;
export default class FirebaseApp { export default class FirebaseApp {
constructor(name: string, options: Object = {}) { _extendedProps: { [string] : boolean };
_initialized: boolean;
_name: string;
_namespaces: { [string]: Object };
_nativeInitialized: boolean;
_options: FirebaseOptions;
admob: () => AdMob;
auth: () => Auth;
analytics: () => Analytics;
config: () => RemoteConfig;
crash: () => Crash;
database: () => Database;
firestore: () => Firestore;
links: () => Links;
messaging: () => Messaging;
perf: () => Performance;
storage: () => Storage;
utils: () => Utils;
constructor(name: string, options: FirebaseOptions) {
this._name = name; this._name = name;
this._namespaces = {}; this._namespaces = {};
this._options = Object.assign({}, options); this._options = Object.assign({}, options);
@ -49,7 +73,7 @@ export default class FirebaseApp {
* @param native * @param native
* @private * @private
*/ */
_initializeApp(native = false) { _initializeApp(native: boolean = false) {
if (native) { if (native) {
// for apps already initialized natively that // for apps already initialized natively that
// we have info from RN constants // we have info from RN constants
@ -67,7 +91,7 @@ export default class FirebaseApp {
* *
* @return {*} * @return {*}
*/ */
get name() { get name(): string {
if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME) { if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME) {
// ios and android firebase sdk's return different // ios and android firebase sdk's return different
// app names - so we just return what the web sdk // app names - so we just return what the web sdk
@ -82,7 +106,7 @@ export default class FirebaseApp {
* *
* @return {*} * @return {*}
*/ */
get options() { get options(): FirebaseOptions {
return Object.assign({}, this._options); return Object.assign({}, this._options);
} }
@ -95,14 +119,14 @@ export default class FirebaseApp {
* @param props * @param props
*/ */
extendApp(props: Object) { extendApp(props: Object) {
if (!isObject(props)) throw new Error(INTERNALS.ERROR_MISSING_ARG('Object', 'extendApp')); if (!isObject(props)) throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG('Object', 'extendApp'));
const keys = Object.keys(props); const keys = Object.keys(props);
for (let i = 0, len = keys.length; i < len; i++) { for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i]; const key = keys[i];
if (!this._extendedProps[key] && Object.hasOwnProperty.call(this, key)) { if (!this._extendedProps[key] && Object.hasOwnProperty.call(this, key)) {
throw new Error(INTERNALS.ERROR_PROTECTED_PROP(key)); throw new Error(INTERNALS.STRINGS.ERROR_PROTECTED_PROP(key));
} }
this[key] = props[key]; this[key] = props[key];
@ -131,7 +155,7 @@ export default class FirebaseApp {
* *
* @return {*} * @return {*}
*/ */
onReady(): Promise { onReady(): Promise<FirebaseApp> {
if (this._initialized) return Promise.resolve(this); if (this._initialized) return Promise.resolve(this);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -144,13 +168,12 @@ export default class FirebaseApp {
/** /**
* *
* @param name
* @param statics * @param statics
* @param InstanceClass * @param InstanceClass
* @return {function()} * @return {function()}
* @private * @private
*/ */
_staticsOrModuleInstance(statics = {}, InstanceClass): Function { _staticsOrModuleInstance(statics: Object = {}, InstanceClass: Class<*>) {
const getInstance = () => { const getInstance = () => {
const _name = `_${InstanceClass._NAMESPACE}`; const _name = `_${InstanceClass._NAMESPACE}`;

View File

@ -6,7 +6,7 @@ import { NativeModules, NativeEventEmitter } from 'react-native';
import INTERNALS from './internals'; import INTERNALS from './internals';
import FirebaseApp from './firebase-app'; import FirebaseApp from './firebase-app';
import { isObject, isString, isAndroid } from './utils'; import { isObject, isString } from './utils';
// module imports // module imports
import AdMob, { statics as AdMobStatics } from './modules/admob'; import AdMob, { statics as AdMobStatics } from './modules/admob';
@ -24,7 +24,31 @@ import Utils, { statics as UtilsStatics } from './modules/utils';
const FirebaseCoreModule = NativeModules.RNFirebase; const FirebaseCoreModule = NativeModules.RNFirebase;
export type FirebaseOptions = {
apiKey: string,
appId: string,
databaseURL: string,
messagingSenderId: string,
projectId: string,
storageBucket: string,
}
class FirebaseCore { class FirebaseCore {
_nativeEmitters: { [string]: NativeEventEmitter };
_nativeSubscriptions: { [string]: boolean };
admob: () => AdMob;
auth: () => Auth;
analytics: () => Analytics;
config: () => RemoteConfig;
crash: () => Crash;
database: () => Database;
firestore: () => Firestore;
links: () => Links;
messaging: () => Messaging;
perf: () => Performance;
storage: () => Storage;
utils: () => Utils;
constructor() { constructor() {
this._nativeEmitters = {}; this._nativeEmitters = {};
this._nativeSubscriptions = {}; this._nativeSubscriptions = {};
@ -71,7 +95,7 @@ class FirebaseCore {
* @param name * @param name
* @return {*} * @return {*}
*/ */
initializeApp(options: Object = {}, name: string): FirebaseApp { initializeApp(options: FirebaseOptions, name: string): FirebaseApp {
if (name && !isString(name)) { if (name && !isString(name)) {
throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME); throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME);
} }
@ -163,7 +187,7 @@ class FirebaseCore {
* @param nativeEmitter * @param nativeEmitter
* @private * @private
*/ */
_subscribeForDistribution(eventName, nativeEmitter) { _subscribeForDistribution(eventName: string, nativeEmitter: NativeEventEmitter) {
if (!this._nativeSubscriptions[eventName]) { if (!this._nativeSubscriptions[eventName]) {
nativeEmitter.addListener(eventName, (event) => { nativeEmitter.addListener(eventName, (event) => {
if (event.appName) { if (event.appName) {
@ -186,7 +210,7 @@ class FirebaseCore {
* @return {function(FirebaseApp=)} * @return {function(FirebaseApp=)}
* @private * @private
*/ */
_appNamespaceOrStatics(statics = {}, InstanceClass): Function { _appNamespaceOrStatics(statics: Object = {}, InstanceClass: Class<*>): Function {
const namespace = InstanceClass._NAMESPACE; const namespace = InstanceClass._NAMESPACE;
const getNamespace = (app?: FirebaseApp) => { const getNamespace = (app?: FirebaseApp) => {
@ -215,14 +239,13 @@ class FirebaseCore {
* @return {*} * @return {*}
* @private * @private
*/ */
_getOrSetNativeEmitter(name, nativeModule) { _getOrSetNativeEmitter(name: string, nativeModule: Object): NativeEventEmitter {
if (this._nativeEmitters[name]) { if (this._nativeEmitters[name]) {
return this._nativeEmitters[name]; return this._nativeEmitters[name];
} }
return this._nativeEmitters[name] = new NativeEventEmitter(nativeModule); return this._nativeEmitters[name] = new NativeEventEmitter(nativeModule);
} }
} }
export default new FirebaseCore(); export default new FirebaseCore();

View File

@ -1,6 +1,10 @@
/**
* @flow
*/
import { Platform, NativeModules } from 'react-native'; import { Platform, NativeModules } from 'react-native';
import EventEmitter from './utils/emitter/EventEmitter'; import EventEmitter from './utils/emitter/EventEmitter';
import ModuleBase from './utils/ModuleBase';
import SyncTree from './utils/SyncTree'; import SyncTree from './utils/SyncTree';
const DEFAULT_APP_NAME = Platform.OS === 'ios' ? '__FIRAPP_DEFAULT' : '[DEFAULT]'; const DEFAULT_APP_NAME = Platform.OS === 'ios' ? '__FIRAPP_DEFAULT' : '[DEFAULT]';
@ -92,35 +96,35 @@ export default {
/** /**
* @return {string} * @return {string}
*/ */
ERROR_MISSING_CB(method) { ERROR_MISSING_CB(method: string) {
return `Missing required callback for method ${method}().`; return `Missing required callback for method ${method}().`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_MISSING_ARG(type, method) { ERROR_MISSING_ARG(type: string, method: string) {
return `Missing required argument of type '${type}' for method '${method}()'.`; return `Missing required argument of type '${type}' for method '${method}()'.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_MISSING_ARG_NAMED(name, type, method) { ERROR_MISSING_ARG_NAMED(name: string, type: string, method: string) {
return `Missing required argument '${name}' of type '${type}' for method '${method}()'.`; return `Missing required argument '${name}' of type '${type}' for method '${method}()'.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_ARG_INVALID_VALUE(name, expected, got) { ERROR_ARG_INVALID_VALUE(name: string, expected: string, got: string) {
return `Invalid value for argument '${name}' expected value '${expected}' but got '${got}'.`; return `Invalid value for argument '${name}' expected value '${expected}' but got '${got}'.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_PROTECTED_PROP(name) { ERROR_PROTECTED_PROP(name: string) {
return `Property '${name}' is protected and can not be overridden by extendApp.`; return `Property '${name}' is protected and can not be overridden by extendApp.`;
}, },
@ -129,7 +133,7 @@ export default {
* @param namespace * @param namespace
* @param nativeModule * @param nativeModule
*/ */
ERROR_MISSING_MODULE(namespace, nativeModule) { ERROR_MISSING_MODULE(namespace: string, nativeModule: string) {
const snippet = `firebase.${namespace}()`; const snippet = `firebase.${namespace}()`;
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` + return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
@ -151,7 +155,7 @@ export default {
/** /**
* @return {string} * @return {string}
*/ */
ERROR_APP_NOT_INIT(appName) { ERROR_APP_NOT_INIT(appName: string) {
return `The [${appName}] firebase app has not been initialized!`; return `The [${appName}] firebase app has not been initialized!`;
}, },
@ -160,35 +164,35 @@ export default {
* @return {string} * @return {string}
* @constructor * @constructor
*/ */
ERROR_MISSING_OPT(optName) { ERROR_MISSING_OPT(optName: string) {
return `Failed to initialize app. FirebaseOptions missing or invalid '${optName}' property.`; return `Failed to initialize app. FirebaseOptions missing or invalid '${optName}' property.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_NOT_APP(namespace) { ERROR_NOT_APP(namespace: string) {
return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`; return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_UNSUPPORTED_CLASS_METHOD(className, method) { ERROR_UNSUPPORTED_CLASS_METHOD(className: string, method: string) {
return `${className}.${method}() is unsupported by the native Firebase SDKs.`; return `${className}.${method}() is unsupported by the native Firebase SDKs.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_UNSUPPORTED_CLASS_PROPERTY(className, property) { ERROR_UNSUPPORTED_CLASS_PROPERTY(className: string, property: string) {
return `${className}.${property} is unsupported by the native Firebase SDKs.`; return `${className}.${property} is unsupported by the native Firebase SDKs.`;
}, },
/** /**
* @return {string} * @return {string}
*/ */
ERROR_UNSUPPORTED_MODULE_METHOD(module, method) { ERROR_UNSUPPORTED_MODULE_METHOD(module: Class<ModuleBase>, method: string) {
return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`; return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`;
}, },
@ -196,7 +200,7 @@ export default {
/** /**
* @return {string} * @return {string}
*/ */
ERROR_PLAY_SERVICES(statusCode) { ERROR_PLAY_SERVICES(statusCode: number) {
const knownError = PLAY_SERVICES_CODES[statusCode]; const knownError = PLAY_SERVICES_CODES[statusCode];
let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.'; let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
@ -208,8 +212,8 @@ export default {
return `${start}\r\n\r\n` + return `${start}\r\n\r\n` +
'-------------------------\r\n' + '-------------------------\r\n' +
(knownError ? (knownError ?
`${knownError.code}: ${knownError.message} (code ${statusCode})` : `${knownError.code}: ${knownError.message} (code ${statusCode})` :
`A specific play store availability reason reason was not available (unknown code: ${statusCode || null})` `A specific play store availability reason reason was not available (unknown code: ${statusCode})`
) + ) +
'\r\n-------------------------' + '\r\n-------------------------' +
'\r\n\r\n' + '\r\n\r\n' +
@ -226,9 +230,9 @@ export default {
SyncTree: NativeModules.RNFirebaseDatabase ? new SyncTree(NativeModules.RNFirebaseDatabase) : null, SyncTree: NativeModules.RNFirebaseDatabase ? new SyncTree(NativeModules.RNFirebaseDatabase) : null,
// internal utils // internal utils
deleteApp(name: String) { deleteApp(name: String): Promise<boolean> {
const app = this.APPS[name]; const app = this.APPS[name];
if (!app) return Promise.resolve(); if (!app) return Promise.resolve(true);
// https://firebase.google.com/docs/reference/js/firebase.app.App#delete // https://firebase.google.com/docs/reference/js/firebase.app.App#delete
return app.delete().then(() => { return app.delete().then(() => {

View File

@ -3,19 +3,19 @@
* CollectionReference representation wrapper * CollectionReference representation wrapper
*/ */
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import Path from './Path'; import Query, { type Direction, type Operator } from './Query';
import Query from './Query';
import QuerySnapshot from './QuerySnapshot';
import { firestoreAutoId } from '../../utils'; import { firestoreAutoId } from '../../utils';
import type { Direction, Operator } from './Query'; import type Firestore from './';
import type Path from './Path';
import type QuerySnapshot from './QuerySnapshot';
/** /**
* @class CollectionReference * @class CollectionReference
*/ */
export default class CollectionReference { export default class CollectionReference {
_collectionPath: Path; _collectionPath: Path;
_firestore: Object; _firestore: Firestore;
_query: Query; _query: Query;
constructor(firestore: Object, collectionPath: Path) { constructor(firestore: Object, collectionPath: Path) {
@ -71,10 +71,6 @@ export default class CollectionReference {
return this._query.limit(n); return this._query.limit(n);
} }
offset(n: number): Query {
return this._query.offset(n);
}
onSnapshot(onNext: () => any, onError?: () => any): () => void { onSnapshot(onNext: () => any, onError?: () => any): () => void {
return this._query.onSnapshot(onNext, onError); return this._query.onSnapshot(onNext, onError);
} }
@ -91,10 +87,6 @@ export default class CollectionReference {
return this._query.startAt(fieldValues); return this._query.startAt(fieldValues);
} }
stream(): Stream<DocumentSnapshot> {
return this._query.stream();
}
where(fieldPath: string, opStr: Operator, value: any): Query { where(fieldPath: string, opStr: Operator, value: any): Query {
return this._query.where(fieldPath, opStr, value); return this._query.where(fieldPath, opStr, value);
} }

View File

@ -2,17 +2,19 @@
* @flow * @flow
* DocumentChange representation wrapper * DocumentChange representation wrapper
*/ */
import DocumentSnapshot from './DocumentSnapshot'; import DocumentSnapshot, { type DocumentSnapshotNativeData } from './DocumentSnapshot';
import type Firestore from './';
export type DocumentChangeNativeData = { export type DocumentChangeNativeData = {
document: DocumentSnapshot, document: DocumentSnapshotNativeData,
newIndex: number, newIndex: number,
oldIndex: number, oldIndex: number,
type: string, type: string,
} }
/** /**
* @class DocumentChange * @class DocumentChange
*/ */
export default class DocumentChange { export default class DocumentChange {
@ -21,7 +23,7 @@ export default class DocumentChange {
_oldIndex: number; _oldIndex: number;
_type: string; _type: string;
constructor(firestore: Object, nativeData: DocumentChangeNativeData) { constructor(firestore: Firestore, nativeData: DocumentChangeNativeData) {
this._document = new DocumentSnapshot(firestore, nativeData.document); this._document = new DocumentSnapshot(firestore, nativeData.document);
this._newIndex = nativeData.newIndex; this._newIndex = nativeData.newIndex;
this._oldIndex = nativeData.oldIndex; this._oldIndex = nativeData.oldIndex;

View File

@ -3,11 +3,13 @@
* DocumentReference representation wrapper * DocumentReference representation wrapper
*/ */
import CollectionReference from './CollectionReference'; import CollectionReference from './CollectionReference';
import DocumentSnapshot from './DocumentSnapshot'; import DocumentSnapshot, { type DocumentSnapshotNativeData } from './DocumentSnapshot';
import Path from './Path';
import { buildNativeMap } from './utils/serialize'; import { buildNativeMap } from './utils/serialize';
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils'; import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
import type Firestore from './';
import type Path from './Path';
export type WriteOptions = { export type WriteOptions = {
merge?: boolean, merge?: boolean,
} }
@ -16,24 +18,27 @@ type DocumentListenOptions = {
includeMetadataChanges: boolean, includeMetadataChanges: boolean,
} }
type ObserverOnError = (Object) => void;
type ObserverOnNext = (DocumentSnapshot) => void;
type Observer = { type Observer = {
next: (DocumentSnapshot) => void, error?: ObserverOnError,
error?: (Object) => void, next: ObserverOnNext,
} }
/** /**
* @class DocumentReference * @class DocumentReference
*/ */
export default class DocumentReference { export default class DocumentReference {
_documentPath: Path; _documentPath: Path;
_firestore: Object; _firestore: Firestore;
constructor(firestore: Object, documentPath: Path) { constructor(firestore: Firestore, documentPath: Path) {
this._documentPath = documentPath; this._documentPath = documentPath;
this._firestore = firestore; this._firestore = firestore;
} }
get firestore(): Object { get firestore(): Firestore {
return this._firestore; return this._firestore;
} }
@ -43,6 +48,9 @@ export default class DocumentReference {
get parent(): CollectionReference { get parent(): CollectionReference {
const parentPath = this._documentPath.parent(); const parentPath = this._documentPath.parent();
if (!parentPath) {
throw new Error('Invalid document path');
}
return new CollectionReference(this._firestore, parentPath); return new CollectionReference(this._firestore, parentPath);
} }
@ -71,9 +79,9 @@ export default class DocumentReference {
} }
onSnapshot( onSnapshot(
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | (DocumentSnapshot) => void, optionsOrObserverOrOnNext: DocumentListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void, observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: (Object) => void onError?: ObserverOnError,
) { ) {
let observer = {}; let observer = {};
let docListenOptions = {}; let docListenOptions = {};
@ -125,8 +133,8 @@ export default class DocumentReference {
} }
const listenerId = firestoreAutoId(); const listenerId = firestoreAutoId();
const listener = (nativeDocumentSnapshot) => { const listener = (nativeDocumentSnapshot: DocumentSnapshotNativeData) => {
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot); const documentSnapshot = new DocumentSnapshot(this.firestore, nativeDocumentSnapshot);
observer.next(documentSnapshot); observer.next(documentSnapshot);
}; };
@ -158,7 +166,7 @@ export default class DocumentReference {
.documentSet(this.path, nativeData, writeOptions); .documentSet(this.path, nativeData, writeOptions);
} }
update(...args: Object | string[]): Promise<void> { update(...args: Array<any>): Promise<void> {
let data = {}; let data = {};
if (args.length === 1) { if (args.length === 1) {
if (!isObject(args[0])) { if (!isObject(args[0])) {
@ -190,7 +198,7 @@ export default class DocumentReference {
* Remove document snapshot listener * Remove document snapshot listener
* @param listener * @param listener
*/ */
_offDocumentSnapshot(listenerId: number, listener: Function) { _offDocumentSnapshot(listenerId: string, listener: Function) {
this._firestore.log.info('Removing onDocumentSnapshot listener'); this._firestore.log.info('Removing onDocumentSnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`), listener); this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`), listener); this._firestore.removeListener(this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`), listener);

View File

@ -4,7 +4,9 @@
*/ */
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import Path from './Path'; import Path from './Path';
import { parseNativeMap } from './utils/serialize'; import { parseNativeMap, type TypeMap } from './utils/serialize';
import type Firestore from './';
export type SnapshotMetadata = { export type SnapshotMetadata = {
fromCache: boolean, fromCache: boolean,
@ -12,7 +14,7 @@ export type SnapshotMetadata = {
} }
export type DocumentSnapshotNativeData = { export type DocumentSnapshotNativeData = {
data: Object, data: { [string]: TypeMap },
metadata: SnapshotMetadata, metadata: SnapshotMetadata,
path: string, path: string,
} }
@ -21,11 +23,11 @@ export type DocumentSnapshotNativeData = {
* @class DocumentSnapshot * @class DocumentSnapshot
*/ */
export default class DocumentSnapshot { export default class DocumentSnapshot {
_data: Object; _data: Object | void;
_metadata: SnapshotMetadata; _metadata: SnapshotMetadata;
_ref: DocumentReference; _ref: DocumentReference;
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) { constructor(firestore: Firestore, nativeData: DocumentSnapshotNativeData) {
this._data = parseNativeMap(firestore, nativeData.data); this._data = parseNativeMap(firestore, nativeData.data);
this._metadata = nativeData.metadata; this._metadata = nativeData.metadata;
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path)); this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
@ -47,11 +49,11 @@ export default class DocumentSnapshot {
return this._ref; return this._ref;
} }
data(): Object { data(): Object | void {
return this._data; return this._data;
} }
get(fieldPath: string): any { get(fieldPath: string): any {
return this._data[fieldPath]; return this._data ? this._data[fieldPath] : undefined;
} }
} }

View File

@ -3,7 +3,7 @@
* GeoPoint representation wrapper * GeoPoint representation wrapper
*/ */
/** /**
* @class GeoPoint * @class GeoPoint
*/ */
export default class GeoPoint { export default class GeoPoint {
@ -19,11 +19,11 @@ export default class GeoPoint {
this._longitude = longitude; this._longitude = longitude;
} }
get latitude() { get latitude(): number {
return this._latitude; return this._latitude;
} }
get longitude() { get longitude(): number {
return this._longitude; return this._longitude;
} }
} }

View File

@ -3,7 +3,7 @@
* Path representation wrapper * Path representation wrapper
*/ */
/** /**
* @class Path * @class Path
*/ */
export default class Path { export default class Path {

View File

@ -2,12 +2,14 @@
* @flow * @flow
* Query representation wrapper * Query representation wrapper
*/ */
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import QuerySnapshot from './QuerySnapshot'; import QuerySnapshot from './QuerySnapshot';
import { buildNativeArray, buildTypeMap } from './utils/serialize'; import { buildNativeArray, buildTypeMap } from './utils/serialize';
import { firestoreAutoId, isFunction, isObject } from '../../utils'; import { firestoreAutoId, isFunction, isObject } from '../../utils';
import type DocumentSnapshot from './DocumentSnapshot';
import type Firestore from './';
import type Path from './Path';
const DIRECTIONS = { const DIRECTIONS = {
ASC: 'ASCENDING', ASC: 'ASCENDING',
asc: 'ASCENDING', asc: 'ASCENDING',
@ -54,19 +56,25 @@ type Observer = {
error?: (Object) => void, error?: (Object) => void,
} }
/** /**
* @class Query * @class Query
*/ */
export default class Query { export default class Query {
_fieldFilters: FieldFilter[]; _fieldFilters: FieldFilter[];
_fieldOrders: FieldOrder[]; _fieldOrders: FieldOrder[];
_firestore: Object; _firestore: Firestore;
_iid: number; _iid: number;
_queryOptions: QueryOptions; _queryOptions: QueryOptions;
_referencePath: Path; _referencePath: Path;
constructor(firestore: Object, path: Path, fieldFilters?: FieldFilter[], constructor(
fieldOrders?: FieldOrder[], queryOptions?: QueryOptions) { firestore: Firestore,
path: Path,
fieldFilters?:
FieldFilter[],
fieldOrders?: FieldOrder[],
queryOptions?: QueryOptions,
) {
this._fieldFilters = fieldFilters || []; this._fieldFilters = fieldFilters || [];
this._fieldOrders = fieldOrders || []; this._fieldOrders = fieldOrders || [];
this._firestore = firestore; this._firestore = firestore;
@ -78,24 +86,34 @@ export default class Query {
return this._firestore; return this._firestore;
} }
endAt(...snapshotOrVarArgs: any): Query { endAt(...snapshotOrVarArgs: any[]): Query {
const options = { const options = {
...this._queryOptions, ...this._queryOptions,
endAt: this._buildOrderByOption(snapshotOrVarArgs), endAt: this._buildOrderByOption(snapshotOrVarArgs),
}; };
return new Query(this.firestore, this._referencePath, this._fieldFilters, return new Query(
this._fieldOrders, options); this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
} }
endBefore(...snapshotOrVarArgs: any): Query { endBefore(...snapshotOrVarArgs: any[]): Query {
const options = { const options = {
...this._queryOptions, ...this._queryOptions,
endBefore: this._buildOrderByOption(snapshotOrVarArgs), endBefore: this._buildOrderByOption(snapshotOrVarArgs),
}; };
return new Query(this.firestore, this._referencePath, this._fieldFilters, return new Query(
this._fieldOrders, options); this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
} }
get(): Promise<QuerySnapshot> { get(): Promise<QuerySnapshot> {
@ -117,14 +135,19 @@ export default class Query {
...this._queryOptions, ...this._queryOptions,
limit, limit,
}; };
return new Query(this.firestore, this._referencePath, this._fieldFilters, return new Query(
this._fieldOrders, options); this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
} }
onSnapshot( onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void, optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void, observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void, onError?: (Object) => void,
) { ) {
let observer = {}; let observer = {};
let queryListenOptions = {}; let queryListenOptions = {};
@ -199,12 +222,12 @@ export default class Query {
// Add the native listener // Add the native listener
this._firestore._native this._firestore._native
.collectionOnSnapshot( .collectionOnSnapshot(
this._referencePath.relativeName, this._referencePath.relativeName,
this._fieldFilters, this._fieldFilters,
this._fieldOrders, this._fieldOrders,
this._queryOptions, this._queryOptions,
listenerId, listenerId,
queryListenOptions, queryListenOptions,
); );
// Return an unsubscribe method // Return an unsubscribe method
@ -226,28 +249,43 @@ export default class Query {
fieldPath, fieldPath,
}; };
const combinedOrders = this._fieldOrders.concat(newOrder); const combinedOrders = this._fieldOrders.concat(newOrder);
return new Query(this.firestore, this._referencePath, this._fieldFilters, return new Query(
combinedOrders, this._queryOptions); this.firestore,
this._referencePath,
this._fieldFilters,
combinedOrders,
this._queryOptions,
);
} }
startAfter(...snapshotOrVarArgs: any): Query { startAfter(...snapshotOrVarArgs: any[]): Query {
const options = { const options = {
...this._queryOptions, ...this._queryOptions,
startAfter: this._buildOrderByOption(snapshotOrVarArgs), startAfter: this._buildOrderByOption(snapshotOrVarArgs),
}; };
return new Query(this.firestore, this._referencePath, this._fieldFilters, return new Query(
this._fieldOrders, options); this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
} }
startAt(...snapshotOrVarArgs: any): Query { startAt(...snapshotOrVarArgs: any[]): Query {
const options = { const options = {
...this._queryOptions, ...this._queryOptions,
startAt: this._buildOrderByOption(snapshotOrVarArgs), startAt: this._buildOrderByOption(snapshotOrVarArgs),
}; };
return new Query(this.firestore, this._referencePath, this._fieldFilters, return new Query(
this._fieldOrders, options); this.firestore,
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
);
} }
where(fieldPath: string, opStr: Operator, value: any): Query { where(fieldPath: string, opStr: Operator, value: any): Query {
@ -261,8 +299,13 @@ export default class Query {
value: nativeValue, value: nativeValue,
}; };
const combinedFilters = this._fieldFilters.concat(newFilter); const combinedFilters = this._fieldFilters.concat(newFilter);
return new Query(this.firestore, this._referencePath, combinedFilters, return new Query(
this._fieldOrders, this._queryOptions); this.firestore,
this._referencePath,
combinedFilters,
this._fieldOrders,
this._queryOptions,
);
} }
/** /**
@ -290,7 +333,7 @@ export default class Query {
* Remove query snapshot listener * Remove query snapshot listener
* @param listener * @param listener
*/ */
_offCollectionSnapshot(listenerId: number, listener: Function) { _offCollectionSnapshot(listenerId: string, listener: Function) {
this._firestore.log.info('Removing onQuerySnapshot listener'); this._firestore.log.info('Removing onQuerySnapshot listener');
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener); this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener);
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener); this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener);
@ -300,7 +343,7 @@ export default class Query {
this._fieldFilters, this._fieldFilters,
this._fieldOrders, this._fieldOrders,
this._queryOptions, this._queryOptions,
listenerId listenerId,
); );
} }
} }

View File

@ -2,12 +2,11 @@
* @flow * @flow
* QuerySnapshot representation wrapper * QuerySnapshot representation wrapper
*/ */
import DocumentChange from './DocumentChange'; import DocumentChange, { type DocumentChangeNativeData } from './DocumentChange';
import DocumentSnapshot from './DocumentSnapshot'; import DocumentSnapshot, { type DocumentSnapshotNativeData, type SnapshotMetadata } from './DocumentSnapshot';
import Query from './Query';
import type { DocumentChangeNativeData } from './DocumentChange'; import type Firestore from './';
import type { DocumentSnapshotNativeData, SnapshotMetadata } from './DocumentSnapshot'; import type Query from './Query';
type QuerySnapshotNativeData = { type QuerySnapshotNativeData = {
changes: DocumentChangeNativeData[], changes: DocumentChangeNativeData[],
@ -15,7 +14,7 @@ type QuerySnapshotNativeData = {
metadata: SnapshotMetadata, metadata: SnapshotMetadata,
} }
/** /**
* @class QuerySnapshot * @class QuerySnapshot
*/ */
export default class QuerySnapshot { export default class QuerySnapshot {
@ -24,7 +23,7 @@ export default class QuerySnapshot {
_metadata: SnapshotMetadata; _metadata: SnapshotMetadata;
_query: Query; _query: Query;
constructor(firestore: Object, query: Query, nativeData: QuerySnapshotNativeData) { constructor(firestore: Firestore, query: Query, nativeData: QuerySnapshotNativeData) {
this._changes = nativeData.changes.map(change => new DocumentChange(firestore, change)); this._changes = nativeData.changes.map(change => new DocumentChange(firestore, change));
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc)); this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
this._metadata = nativeData.metadata; this._metadata = nativeData.metadata;
@ -43,14 +42,14 @@ export default class QuerySnapshot {
return this._docs.length === 0; return this._docs.length === 0;
} }
get query(): Query {
return this._query;
}
get metadata(): SnapshotMetadata { get metadata(): SnapshotMetadata {
return this._metadata; return this._metadata;
} }
get query(): Query {
return this._query;
}
get size(): number { get size(): number {
return this._docs.length; return this._docs.length;
} }
@ -59,8 +58,8 @@ export default class QuerySnapshot {
// TODO: Validation // TODO: Validation
// validate.isFunction('callback', callback); // validate.isFunction('callback', callback);
for (const doc of this._docs) { this._docs.forEach((doc) => {
callback(doc); callback(doc);
} });
} }
} }

View File

@ -2,11 +2,11 @@
* @flow * @flow
* WriteBatch representation wrapper * WriteBatch representation wrapper
*/ */
import DocumentReference from './DocumentReference';
import { buildNativeMap } from './utils/serialize'; import { buildNativeMap } from './utils/serialize';
import { isObject, isString } from '../../utils'; import { isObject, isString } from '../../utils';
import type { WriteOptions } from './DocumentReference'; import type DocumentReference, { WriteOptions } from './DocumentReference';
import type Firestore from './';
type DocumentWrite = { type DocumentWrite = {
data?: Object, data?: Object,
@ -15,14 +15,14 @@ type DocumentWrite = {
type: 'DELETE' | 'SET' | 'UPDATE', type: 'DELETE' | 'SET' | 'UPDATE',
} }
/** /**
* @class WriteBatch * @class WriteBatch
*/ */
export default class WriteBatch { export default class WriteBatch {
_firestore: Object; _firestore: Firestore;
_writes: DocumentWrite[]; _writes: DocumentWrite[];
constructor(firestore: Object) { constructor(firestore: Firestore) {
this._firestore = firestore; this._firestore = firestore;
this._writes = []; this._writes = [];
} }
@ -60,7 +60,7 @@ export default class WriteBatch {
return this; return this;
} }
update(docRef: DocumentReference, ...args: Object | string[]): WriteBatch { update(docRef: DocumentReference, ...args: any[]): WriteBatch {
// TODO: Validation // TODO: Validation
// validate.isDocumentReference('docRef', docRef); // validate.isDocumentReference('docRef', docRef);
let data = {}; let data = {};

View File

@ -5,13 +5,16 @@
import ModuleBase from './../../utils/ModuleBase'; import ModuleBase from './../../utils/ModuleBase';
import CollectionReference from './CollectionReference'; import CollectionReference from './CollectionReference';
import DocumentReference from './DocumentReference'; import DocumentReference from './DocumentReference';
import DocumentSnapshot from './DocumentSnapshot';
import FieldValue from './FieldValue'; import FieldValue from './FieldValue';
import GeoPoint from './GeoPoint'; import GeoPoint from './GeoPoint';
import Path from './Path'; import Path from './Path';
import WriteBatch from './WriteBatch'; import WriteBatch from './WriteBatch';
import INTERNALS from './../../internals'; import INTERNALS from './../../internals';
import type DocumentSnapshot from './DocumentSnapshot';
import type FirebaseApp from '../../firebase-app';
import type QuerySnapshot from './QuerySnapshot';
type CollectionSyncEvent = { type CollectionSyncEvent = {
appName: string, appName: string,
querySnapshot?: QuerySnapshot, querySnapshot?: QuerySnapshot,
@ -37,21 +40,21 @@ export default class Firestore extends ModuleBase {
_referencePath: Path; _referencePath: Path;
constructor(firebaseApp: Object, options: Object = {}) { constructor(firebaseApp: FirebaseApp, options: Object = {}) {
super(firebaseApp, options, true); super(firebaseApp, options, true);
this._referencePath = new Path([]); this._referencePath = new Path([]);
this.addListener( this.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onCollectionSnapshot // public event name: onCollectionSnapshot
this._getAppEventName('firestore_collection_sync_event'), super._getAppEventName('firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this), this._onCollectionSyncEvent.bind(this),
); );
this.addListener( this.addListener(
// sub to internal native event - this fans out to // sub to internal native event - this fans out to
// public event name: onDocumentSnapshot // public event name: onDocumentSnapshot
this._getAppEventName('firestore_document_sync_event'), super._getAppEventName('firestore_document_sync_event'),
this._onDocumentSyncEvent.bind(this), this._onDocumentSyncEvent.bind(this),
); );
} }
@ -92,15 +95,15 @@ export default class Firestore extends ModuleBase {
throw new Error('Persistence is enabled by default on the Firestore SDKs'); throw new Error('Persistence is enabled by default on the Firestore SDKs');
} }
runTransaction(updateFunction): Promise<any> { runTransaction(): Promise<any> {
throw new Error('firebase.firestore().runTransaction() coming soon'); throw new Error('firebase.firestore().runTransaction() coming soon');
} }
setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void { setLogLevel(): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel')); throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel'));
} }
settings(settings: Object): void { settings(): void {
throw new Error('firebase.firestore().settings() coming soon'); throw new Error('firebase.firestore().settings() coming soon');
} }
@ -115,9 +118,9 @@ export default class Firestore extends ModuleBase {
*/ */
_onCollectionSyncEvent(event: CollectionSyncEvent) { _onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) { if (event.error) {
this.emit(this._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error); this.emit(super._getAppEventName(`onQuerySnapshotError:${event.listenerId}`), event.error);
} else { } else {
this.emit(this._getAppEventName(`onQuerySnapshot:${event.listenerId}`), event.querySnapshot); this.emit(super._getAppEventName(`onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
} }
} }
@ -128,9 +131,9 @@ export default class Firestore extends ModuleBase {
*/ */
_onDocumentSyncEvent(event: DocumentSyncEvent) { _onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) { if (event.error) {
this.emit(this._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error); this.emit(super._getAppEventName(`onDocumentSnapshotError:${event.listenerId}`), event.error);
} else { } else {
this.emit(this._getAppEventName(`onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot); this.emit(super._getAppEventName(`onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
} }
} }
} }

View File

@ -1,4 +1,6 @@
// @flow /**
* @flow
*/
import DocumentReference from '../DocumentReference'; import DocumentReference from '../DocumentReference';
import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue'; import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue';
@ -6,8 +8,10 @@ import GeoPoint from '../GeoPoint';
import Path from '../Path'; import Path from '../Path';
import { typeOf } from '../../../utils'; import { typeOf } from '../../../utils';
type TypeMap = { import type Firestore from '../';
type: 'array' | 'boolean' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
export type TypeMap = {
type: 'array' | 'boolean' | 'date' | 'fieldvalue' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
value: any, value: any,
} }
@ -17,65 +21,86 @@ type TypeMap = {
* for transmission to the native side * for transmission to the native side
*/ */
export const buildNativeMap = (data: Object): Object => { export const buildNativeMap = (data: Object): { [string]: TypeMap } => {
const nativeData = {}; const nativeData = {};
if (data) { if (data) {
Object.keys(data).forEach((key) => { Object.keys(data).forEach((key) => {
nativeData[key] = buildTypeMap(data[key]); const typeMap = buildTypeMap(data[key]);
if (typeMap) {
nativeData[key] = typeMap;
}
}); });
} }
return nativeData; return nativeData;
}; };
export const buildNativeArray = (array: Object[]): any[] => { export const buildNativeArray = (array: Object[]): TypeMap[] => {
const nativeArray = []; const nativeArray = [];
if (array) { if (array) {
array.forEach((value) => { array.forEach((value) => {
nativeArray.push(buildTypeMap(value)); const typeMap = buildTypeMap(value);
if (typeMap) {
nativeArray.push(typeMap);
}
}); });
} }
return nativeArray; return nativeArray;
}; };
export const buildTypeMap = (value: any): any => { export const buildTypeMap = (value: any): TypeMap | null => {
const typeMap = {};
const type = typeOf(value); const type = typeOf(value);
if (value === null || value === undefined) { if (value === null || value === undefined) {
typeMap.type = 'null'; return {
typeMap.value = null; type: 'null',
value: null,
};
} else if (value === DELETE_FIELD_VALUE) { } else if (value === DELETE_FIELD_VALUE) {
typeMap.type = 'fieldvalue'; return {
typeMap.value = 'delete'; type: 'fieldvalue',
value: 'delete',
};
} else if (value === SERVER_TIMESTAMP_FIELD_VALUE) { } else if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
typeMap.type = 'fieldvalue'; return {
typeMap.value = 'timestamp'; type: 'fieldvalue',
value: 'timestamp',
};
} else if (type === 'boolean' || type === 'number' || type === 'string') { } else if (type === 'boolean' || type === 'number' || type === 'string') {
typeMap.type = type; return {
typeMap.value = value; type,
value,
};
} else if (type === 'array') { } else if (type === 'array') {
typeMap.type = type; return {
typeMap.value = buildNativeArray(value); type,
value: buildNativeArray(value),
};
} else if (type === 'object') { } else if (type === 'object') {
if (value instanceof DocumentReference) { if (value instanceof DocumentReference) {
typeMap.type = 'reference'; return {
typeMap.value = value.path; type: 'reference',
value: value.path,
};
} else if (value instanceof GeoPoint) { } else if (value instanceof GeoPoint) {
typeMap.type = 'geopoint'; return {
typeMap.value = { type: 'geopoint',
latitude: value.latitude, value: {
longitude: value.longitude, latitude: value.latitude,
longitude: value.longitude,
},
}; };
} else if (value instanceof Date) { } else if (value instanceof Date) {
typeMap.type = 'date'; return {
typeMap.value = value.getTime(); type: 'date',
} else { value: value.getTime(),
typeMap.type = 'object'; };
typeMap.value = buildNativeMap(value);
} }
} else { return {
console.warn(`Unknown data type received ${type}`); type: 'object',
value: buildNativeMap(value),
};
} }
return typeMap; console.warn(`Unknown data type received ${type}`);
return null;
}; };
/* /*
@ -83,7 +108,7 @@ export const buildTypeMap = (value: any): any => {
* side and converts to the correct Firestore JS types * side and converts to the correct Firestore JS types
*/ */
export const parseNativeMap = (firestore: Object, nativeData: Object): Object => { export const parseNativeMap = (firestore: Firestore, nativeData: { [string]: TypeMap }): Object | void => {
let data; let data;
if (nativeData) { if (nativeData) {
data = {}; data = {};
@ -94,7 +119,7 @@ export const parseNativeMap = (firestore: Object, nativeData: Object): Object =>
return data; return data;
}; };
const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => { const parseNativeArray = (firestore: Firestore, nativeArray: TypeMap[]): any[] => {
const array = []; const array = [];
if (nativeArray) { if (nativeArray) {
nativeArray.forEach((typeMap) => { nativeArray.forEach((typeMap) => {
@ -104,7 +129,7 @@ const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => {
return array; return array;
}; };
const parseTypeMap = (firestore: Object, typeMap: TypeMap): any => { const parseTypeMap = (firestore: Firestore, typeMap: TypeMap): any => {
const { type, value } = typeMap; const { type, value } = typeMap;
if (type === 'null') { if (type === 'null') {
return null; return null;

View File

@ -6,9 +6,10 @@ import { NativeModules } from 'react-native';
import Log from '../utils/log'; import Log from '../utils/log';
import INTERNALS from './../internals'; import INTERNALS from './../internals';
import FirebaseCore from './../firebase'; import FirebaseCore from './../firebase';
import FirebaseApp from '../firebase-app';
import { nativeWithApp } from './../utils'; import { nativeWithApp } from './../utils';
import type FirebaseApp from '../firebase-app';
const logs = {}; const logs = {};
// Firebase Native SDKs that support multiple app instances // Firebase Native SDKs that support multiple app instances
@ -50,7 +51,7 @@ export default class ModuleBase {
_options: Object; _options: Object;
_appName: string; _appName: string;
_namespace: string; _namespace: string;
_firebaseApp: Object; _firebaseApp: FirebaseApp;
_eventEmitter: Object; _eventEmitter: Object;
static _NAMESPACE: string; static _NAMESPACE: string;
static _NATIVE_MODULE: string; static _NATIVE_MODULE: string;
@ -61,7 +62,7 @@ export default class ModuleBase {
* @param options * @param options
* @param withEventEmitter * @param withEventEmitter
*/ */
constructor(firebaseApp: Object, options: Object, withEventEmitter: boolean = false) { constructor(firebaseApp: FirebaseApp, options: Object, withEventEmitter: boolean = false) {
this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', ''); this._module = this.constructor._NATIVE_MODULE.replace('RNFirebase', '');
this._firebaseApp = firebaseApp; this._firebaseApp = firebaseApp;
this._appName = firebaseApp._name; this._appName = firebaseApp._name;
@ -73,12 +74,7 @@ export default class ModuleBase {
const nativeModule = NativeModules[this.constructor._NATIVE_MODULE]; const nativeModule = NativeModules[this.constructor._NATIVE_MODULE];
if (!nativeModule) { if (!nativeModule) {
throw new Error( throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(this.constructor._NAMESPACE, this.constructor._NATIVE_MODULE));
INTERNALS.STRINGS.ERROR_MISSING_MODULE(
this.constructor._NAMESPACE,
this.constructor._NATIVE_MODULE,
),
);
} }
// used by the modules that extend ModuleBase // used by the modules that extend ModuleBase
@ -100,7 +96,7 @@ export default class ModuleBase {
* @param moduleName * @param moduleName
* @private * @private
*/ */
_setupEventEmitter(nativeModule, moduleName) { _setupEventEmitter(nativeModule: Object, moduleName: string) {
this._eventEmitter = FirebaseCore._getOrSetNativeEmitter(`${this._appName}-${this._module}`, nativeModule); this._eventEmitter = FirebaseCore._getOrSetNativeEmitter(`${this._appName}-${this._module}`, nativeModule);
const events = NATIVE_MODULE_EVENTS[moduleName]; const events = NATIVE_MODULE_EVENTS[moduleName];
@ -117,7 +113,7 @@ export default class ModuleBase {
* @return {string} * @return {string}
* @private * @private
*/ */
_getAppEventName(eventName) { _getAppEventName(eventName: string) {
return `${this._appName}-${eventName}`; return `${this._appName}-${eventName}`;
} }
@ -131,9 +127,7 @@ export default class ModuleBase {
get log(): Log { get log(): Log {
if (logs[this._namespace]) return logs[this._namespace]; if (logs[this._namespace]) return logs[this._namespace];
return logs[this._namespace] = Log.createLogger( return logs[this._namespace] = Log.createLogger(`🔥 ${this._namespace.toUpperCase()}`);
`🔥 ${this._namespace.toUpperCase()}`,
);
} }
/* /*

View File

@ -1,8 +1,16 @@
/** /**
* @flow * @flow
*/ */
import Log from './log';
import type Database from '../modules/database';
import type Storage from '../modules/storage';
export default class ReferenceBase { export default class ReferenceBase {
constructor(path: string, module) { _module: Database | Storage;
path: string;
constructor(path: string, module: Database | Storage) {
this._module = module; this._module = module;
this.path = path || '/'; this.path = path || '/';
} }
@ -17,7 +25,7 @@ export default class ReferenceBase {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1); return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
} }
get log() { get log(): Log {
return this._module.log; return this._module.log;
} }
} }

View File

@ -1,3 +1,6 @@
/**
* @flow
*/
import { NativeEventEmitter } from 'react-native'; import { NativeEventEmitter } from 'react-native';
import INTERNALS from './../internals'; import INTERNALS from './../internals';
@ -6,13 +9,13 @@ import DatabaseReference from './../modules/database/reference';
import { isString, nativeToJSError } from './../utils'; import { isString, nativeToJSError } from './../utils';
type Registration = { type Registration = {
key: String, key: string,
path: String, path: string,
once?: Boolean, once?: boolean,
appName: String, appName: string,
eventType: String, eventType: string,
listener: Function, listener: Function,
eventRegistrationKey: String, eventRegistrationKey: string,
ref: DatabaseReference, ref: DatabaseReference,
} }
@ -21,7 +24,12 @@ type Registration = {
* subscriptions and keep the listeners in sync in js vs native. * subscriptions and keep the listeners in sync in js vs native.
*/ */
export default class SyncTree { export default class SyncTree {
constructor(databaseNative) { _databaseNative: Object;
_nativeEmitter: NativeEventEmitter;
_reverseLookup: { [string]: Registration };
_tree: { [string]: { [string]: Array }};
constructor(databaseNative: Object) {
this._tree = {}; this._tree = {};
this._reverseLookup = {}; this._reverseLookup = {};
this._databaseNative = databaseNative; this._databaseNative = databaseNative;
@ -110,7 +118,7 @@ export default class SyncTree {
* @param registration * @param registration
* @return {null} * @return {null}
*/ */
getRegistration(registration): Registration | null { getRegistration(registration: string): Registration | null {
return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null; return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null;
} }
@ -120,7 +128,7 @@ export default class SyncTree {
* @param registrations * @param registrations
* @return {number} * @return {number}
*/ */
removeListenersForRegistrations(registrations) { removeListenersForRegistrations(registrations: string | string[]) {
if (isString(registrations)) { if (isString(registrations)) {
this.removeRegistration(registrations); this.removeRegistration(registrations);
INTERNALS.SharedEventEmitter.removeAllListeners(registrations); INTERNALS.SharedEventEmitter.removeAllListeners(registrations);
@ -143,7 +151,7 @@ export default class SyncTree {
* @param registrations * @param registrations
* @return {Array} array of registrations removed * @return {Array} array of registrations removed
*/ */
removeListenerRegistrations(listener, registrations) { removeListenerRegistrations(listener, registrations: string[]) {
if (!Array.isArray(registrations)) return []; if (!Array.isArray(registrations)) return [];
const removed = []; const removed = [];
@ -173,7 +181,7 @@ export default class SyncTree {
* @param path * @param path
* @return {Array} * @return {Array}
*/ */
getRegistrationsByPath(path): Array { getRegistrationsByPath(path: string): Array {
const out = []; const out = [];
const eventKeys = Object.keys(this._tree[path] || {}); const eventKeys = Object.keys(this._tree[path] || {});
@ -191,7 +199,7 @@ export default class SyncTree {
* @param eventType * @param eventType
* @return {Array} * @return {Array}
*/ */
getRegistrationsByPathEvent(path, eventType): Array { getRegistrationsByPathEvent(path: string, eventType: string): Array {
if (!this._tree[path]) return []; if (!this._tree[path]) return [];
if (!this._tree[path][eventType]) return []; if (!this._tree[path][eventType]) return [];
@ -228,8 +236,13 @@ export default class SyncTree {
* @param listener * @param listener
* @return {String} * @return {String}
*/ */
addRegistration(parameters: Registration, listener): String { addRegistration(parameters: Registration, listener: Function): string {
const { path, eventType, eventRegistrationKey, once } = parameters; const {
path,
eventType,
eventRegistrationKey,
once,
} = parameters;
if (!this._tree[path]) this._tree[path] = {}; if (!this._tree[path]) this._tree[path] = {};
if (!this._tree[path][eventType]) this._tree[path][eventType] = {}; if (!this._tree[path][eventType]) this._tree[path][eventType] = {};
@ -256,7 +269,7 @@ export default class SyncTree {
* @param registration * @param registration
* @return {boolean} * @return {boolean}
*/ */
removeRegistration(registration: String): Boolean { removeRegistration(registration: string): boolean {
if (!this._reverseLookup[registration]) return false; if (!this._reverseLookup[registration]) return false;
const { path, eventType, once } = this._reverseLookup[registration]; const { path, eventType, once } = this._reverseLookup[registration];
@ -298,4 +311,3 @@ export default class SyncTree {
}; };
} }
} }

View File

@ -6,18 +6,10 @@ import { Platform } from 'react-native';
// modeled after base64 web-safe chars, but ordered by ASCII // modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const hasOwnProperty = Object.hasOwnProperty; const { hasOwnProperty } = Object;
// const DEFAULT_CHUNK_SIZE = 50; // const DEFAULT_CHUNK_SIZE = 50;
// Source: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical
const REGEXP_FIELD_NAME = new RegExp(
`^(?:\\.?((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))$`
);
const REGEXP_FIELD_PATH = new RegExp(
`^((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+)(?:\\.((?:(?:[A-Za-z_][A-Za-z_0-9]*)|(?:[A-Za-z_][A-Za-z_0-9]*))+))*$`
);
/** /**
* Deep get a value from an object. * Deep get a value from an object.
* @website https://github.com/Salakar/deeps * @website https://github.com/Salakar/deeps
@ -26,9 +18,7 @@ const REGEXP_FIELD_PATH = new RegExp(
* @param joiner * @param joiner
* @returns {*} * @returns {*}
*/ */
export function deepGet(object: Object, export function deepGet(object: Object, path: string, joiner?: string = '/'): any {
path: string,
joiner?: string = '/'): any {
const keys = path.split(joiner); const keys = path.split(joiner);
let i = 0; let i = 0;
@ -52,9 +42,7 @@ export function deepGet(object: Object,
* @param joiner * @param joiner
* @returns {*} * @returns {*}
*/ */
export function deepExists(object: Object, export function deepExists(object: Object, path: string, joiner?: string = '/'): boolean {
path: string,
joiner?: string = '/'): boolean {
const keys = path.split(joiner); const keys = path.split(joiner);
let i = 0; let i = 0;
@ -98,7 +86,7 @@ export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): bool
* @param arr2 * @param arr2
* @returns {boolean} * @returns {boolean}
*/ */
export function isArrayContainedInOther(arr1: Array, arr2: Array): boolean { export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) { if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false; return false;
} }
@ -112,8 +100,8 @@ export function isArrayContainedInOther(arr1: Array, arr2: Array): boolean {
* @param item * @param item
* @returns {boolean} * @returns {boolean}
*/ */
export function isObject(item: any): boolean { export function isObject(item: mixed): boolean %checks {
return (item && typeof item === 'object' && !Array.isArray(item) && item !== null); return item ? (typeof item === 'object' && !Array.isArray(item) && item !== null) : false;
} }
/** /**
@ -121,8 +109,8 @@ export function isObject(item: any): boolean {
* @param item * @param item
* @returns {*|boolean} * @returns {*|boolean}
*/ */
export function isFunction(item?: any): boolean { export function isFunction(item?: mixed): boolean %checks {
return Boolean(item && typeof item === 'function'); return item ? typeof item === 'function' : false;
} }
/** /**
@ -130,20 +118,10 @@ export function isFunction(item?: any): boolean {
* @param value * @param value
* @return {boolean} * @return {boolean}
*/ */
export function isString(value: any): boolean { export function isString(value: mixed): boolean %checks {
return typeof value === 'string'; return typeof value === 'string';
} }
/**
* Firestore field name/path validator.
* @param field
* @param paths
* @return {boolean}
*/
export function isValidFirestoreField(field, paths) {
return (paths ? REGEXP_FIELD_PATH : REGEXP_FIELD_NAME).test(field);
}
// platform checks // platform checks
export const isIOS = Platform.OS === 'ios'; export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android'; export const isAndroid = Platform.OS === 'android';
@ -167,7 +145,7 @@ export function tryJSONParse(string: string | null): any {
* @param data * @param data
* @returns {*} * @returns {*}
*/ */
export function tryJSONStringify(data: any): string | null { export function tryJSONStringify(data: mixed): string | null {
try { try {
return JSON.stringify(data); return JSON.stringify(data);
} catch (jsonError) { } catch (jsonError) {

3070
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -75,13 +75,13 @@
"babel-preset-react-native": "^1.9.0", "babel-preset-react-native": "^1.9.0",
"debug": "^2.2.0", "debug": "^2.2.0",
"enzyme": "^2.4.1", "enzyme": "^2.4.1",
"eslint": "^3.8.1", "eslint": "^4.11.0",
"eslint-config-airbnb": "^12.0.0", "eslint-config-airbnb": "^16.1.0",
"eslint-plugin-flowtype": "^2.20.0", "eslint-plugin-flowtype": "^2.39.1",
"eslint-plugin-import": "^2.0.1", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^2.2.3", "eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^6.4.1", "eslint-plugin-react": "^7.4.0",
"flow-bin": "^0.55.0", "flow-bin": "^0.56.0",
"react": "^16.0.0", "react": "^16.0.0",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
"react-native": "^0.48.0", "react-native": "^0.48.0",

View File

@ -208,7 +208,7 @@ SPEC CHECKSUMS:
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8 Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee
RNFirebase: a76befd482c5e84df7f69893358abda498ee9f76 RNFirebase: 1b8adf4dfe740fbc4a69a147715c2edfd041eb92
yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45 PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45

View File

@ -1033,7 +1033,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
6AE1012F46FF8A4D1D818A12 /* [CP] Copy Pods Resources */ = { 6AE1012F46FF8A4D1D818A12 /* [CP] Copy Pods Resources */ = {