merge master into omer_links

This commit is contained in:
Omer Levy
2017-10-08 03:52:19 +03:00
31 changed files with 1213 additions and 618 deletions

View File

@@ -5,12 +5,21 @@
import CollectionReference from './CollectionReference';
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import { firestoreAutoId } from '../../utils';
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
export type WriteOptions = {
merge?: boolean,
}
type DocumentListenOptions = {
includeMetadataChanges: boolean,
}
type Observer = {
next: (DocumentSnapshot) => void,
error?: (Object) => void,
}
/**
* @class DocumentReference
*/
@@ -60,13 +69,64 @@ export default class DocumentReference {
.then(result => new DocumentSnapshot(this._firestore, result));
}
onSnapshot(onNext: Function, onError?: Function): () => void {
// TODO: Validation
onSnapshot(
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void
) {
let observer = {};
let docListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
observer.next = optionsOrObserverOrOnNext;
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a valid function.');
}
observer.error = observerOrOnNextOrOnError;
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
// Called with: Observer
if (optionsOrObserverOrOnNext.next) {
if (isFunction(optionsOrObserverOrOnNext.next)) {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
}
observer = optionsOrObserverOrOnNext;
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
}
} else if (optionsOrObserverOrOnNext.includeMetadataChanges) {
docListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
observer.next = observerOrOnNextOrOnError;
if (onError && !isFunction(onError)) {
throw new Error('DocumentReference.onSnapshot failed: Third argument must be a valid function.');
}
observer.error = onError;
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
}
observer = observerOrOnNextOrOnError;
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
}
} else {
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a function or observer.');
}
} else {
throw new Error('DocumentReference.onSnapshot failed: First argument must be a function, observer or options.');
}
} else {
throw new Error('DocumentReference.onSnapshot failed: Called with invalid arguments.');
}
const listenerId = firestoreAutoId();
const listener = (nativeDocumentSnapshot) => {
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
onNext(documentSnapshot);
observer.next(documentSnapshot);
};
// Listen to snapshot events
@@ -76,16 +136,16 @@ export default class DocumentReference {
);
// Listen for snapshot error events
if (onError) {
if (observer.error) {
this._firestore.on(
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
onError,
observer.error,
);
}
// Add the native listener
this._firestore._native
.documentOnSnapshot(this.path, listenerId);
.documentOnSnapshot(this.path, listenerId, docListenOptions);
// Return an unsubscribe method
return this._offDocumentSnapshot.bind(this, listenerId, listener);
@@ -96,7 +156,25 @@ export default class DocumentReference {
.documentSet(this.path, data, writeOptions);
}
update(data: Object): Promise<void> {
update(...args: Object | string[]): Promise<void> {
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
throw new Error('DocumentReference.update failed: If using a single argument, it must be an object.');
}
data = args[0];
} else if (args.length % 2 === 1) {
throw new Error('DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.');
} else {
for (let i = 0; i < args.length; i += 2) {
const key = args[i];
const value = args[i + 1];
if (!isString(key)) {
throw new Error(`DocumentReference.update failed: Argument at index ${i} must be a string`);
}
data[key] = value;
}
}
return this._firestore._native
.documentUpdate(this.path, data);
}

View File

@@ -5,8 +5,7 @@
import DocumentSnapshot from './DocumentSnapshot';
import Path from './Path';
import QuerySnapshot from './QuerySnapshot';
import INTERNALS from '../../internals';
import { firestoreAutoId } from '../../utils';
import { firestoreAutoId, isFunction, isObject } from '../../utils';
const DIRECTIONS = {
ASC: 'ASCENDING',
@@ -44,6 +43,15 @@ type QueryOptions = {
startAt?: any[],
}
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
type QueryListenOptions = {
includeQueryMetadataChanges: boolean,
includeQueryMetadataChanges: boolean,
}
type Observer = {
next: (DocumentSnapshot) => void,
error?: (Object) => void,
}
/**
* @class Query
@@ -116,13 +124,65 @@ export default class Query {
this._fieldOrders, options);
}
onSnapshot(onNext: () => any, onError?: () => any): () => void {
// TODO: Validation
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
onError?: (Object) => void,
) {
let observer = {};
let queryListenOptions = {};
// Called with: onNext, ?onError
if (isFunction(optionsOrObserverOrOnNext)) {
observer.next = optionsOrObserverOrOnNext;
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
throw new Error('Query.onSnapshot failed: Second argument must be a valid function.');
}
observer.error = observerOrOnNextOrOnError;
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
// Called with: Observer
if (optionsOrObserverOrOnNext.next) {
if (isFunction(optionsOrObserverOrOnNext.next)) {
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
}
observer = optionsOrObserverOrOnNext;
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
}
} else if (optionsOrObserverOrOnNext.includeDocumentMetadataChanges || optionsOrObserverOrOnNext.includeQueryMetadataChanges) {
queryListenOptions = optionsOrObserverOrOnNext;
// Called with: Options, onNext, ?onError
if (isFunction(observerOrOnNextOrOnError)) {
observer.next = observerOrOnNextOrOnError;
if (onError && !isFunction(onError)) {
throw new Error('Query.onSnapshot failed: Third argument must be a valid function.');
}
observer.error = onError;
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
if (isFunction(observerOrOnNextOrOnError.next)) {
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
}
observer = observerOrOnNextOrOnError;
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
}
} else {
throw new Error('Query.onSnapshot failed: Second argument must be a function or observer.');
}
} else {
throw new Error('Query.onSnapshot failed: First argument must be a function, observer or options.');
}
} else {
throw new Error('Query.onSnapshot failed: Called with invalid arguments.');
}
const listenerId = firestoreAutoId();
const listener = (nativeQuerySnapshot) => {
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
onNext(querySnapshot);
observer.next(querySnapshot);
};
// Listen to snapshot events
@@ -132,10 +192,10 @@ export default class Query {
);
// Listen for snapshot error events
if (onError) {
if (observer.error) {
this._firestore.on(
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
onError,
observer.error,
);
}
@@ -146,7 +206,8 @@ export default class Query {
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId
listenerId,
queryListenOptions,
);
// Return an unsubscribe method

View File

@@ -3,6 +3,7 @@
* WriteBatch representation wrapper
*/
import DocumentReference from './DocumentReference';
import { isObject, isString } from '../../utils';
import type { WriteOptions } from './DocumentReference';
@@ -58,11 +59,27 @@ export default class WriteBatch {
return this;
}
// TODO: Update to new method signature
update(docRef: DocumentReference, data: Object): WriteBatch {
update(docRef: DocumentReference, ...args: Object | string[]): WriteBatch {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data, true);
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
throw new Error('DocumentReference.update failed: If using two arguments, the second must be an object.');
}
data = args[0];
} else if (args.length % 2 === 1) {
throw new Error('DocumentReference.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.');
} else {
for (let i = 0; i < args.length; i += 2) {
const key = args[i];
const value = args[i + 1];
if (!isString(key)) {
throw new Error(`DocumentReference.update failed: Argument at index ${i + 1} must be a string`);
}
data[key] = value;
}
}
this._writes.push({
data,

View File

@@ -25,7 +25,7 @@ const WILL_PRESENT_RESULT = {
None: 'UNNotificationPresentationOptionNone',
};
const FirebaseMessaging = NativeModules.FirebaseMessaging;
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
/**
* IOS only finish function

131
lib/modules/utils/index.js Normal file
View File

@@ -0,0 +1,131 @@
// @flow
import { NativeModules } from 'react-native';
// import { version as ReactVersion } from 'react';
// import ReactNativeVersion from 'react-native/Libraries/Core/ReactNativeVersion';
import INTERNALS from './../../internals';
import { isIOS } from './../../utils';
import PACKAGE from './../../../package.json';
const FirebaseCoreModule = NativeModules.RNFirebase;
export default class RNFirebaseUtils {
static _NAMESPACE = 'utils';
static _NATIVE_DISABLED = true;
static _NATIVE_MODULE = 'RNFirebaseUtils';
/**
*
*/
checkPlayServicesAvailability() {
if (isIOS) return null;
const code = this.playServicesAvailability.code;
if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
this.promptForPlayServices();
} else {
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(code);
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) {
if (code === 2) console.warn(error); // only warn if it exists but may need an update
else throw new Error(error);
} else {
console.warn(error);
}
}
}
return null;
}
promptForPlayServices() {
if (isIOS) return null;
return FirebaseCoreModule.promptForPlayServices();
}
resolutionForPlayServices() {
if (isIOS) return null;
return FirebaseCoreModule.resolutionForPlayServices();
}
makePlayServicesAvailable() {
if (isIOS) return null;
return FirebaseCoreModule.makePlayServicesAvailable();
}
get sharedEventEmitter(): Object {
return INTERNALS.SharedEventEmitter;
}
/**
* Set the global logging level for all logs.
*
* @param booleanOrDebugString
*/
set logLevel(booleanOrDebugString) {
INTERNALS.OPTIONS.logLevel = booleanOrDebugString;
}
/**
* Returns an array of all current database registrations id strings
*/
get databaseRegistrations(): Array<string> {
return Object.keys(INTERNALS.SyncTree._reverseLookup);
}
/**
* Call with a registration id string to get the details off this reg
*/
get getDatabaseRegistrationDetails(): Function {
return INTERNALS.SyncTree.getRegistration.bind(INTERNALS.SyncTree);
}
/**
* Accepts an array or a single string of registration ids.
* This will remove the refs on both the js and native sides and their listeners.
* @return {function(this:T)}
*/
get removeDatabaseRegistration(): Function {
return INTERNALS.SyncTree.removeListenersForRegistrations.bind(INTERNALS.SyncTree);
}
/**
* Returns props from the android GoogleApiAvailability sdk
* @android
* @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
*/
get playServicesAvailability(): GoogleApiAvailabilityType {
return FirebaseCoreModule.playServicesAvailability || { isAvailable: true, status: 0 };
}
/**
* Enable/Disable throwing an error or warning on detecting a play services problem
* @android
* @param bool
*/
set errorOnMissingPlayServices(bool: Boolean) {
INTERNALS.OPTIONS.errorOnMissingPlayServices = bool;
}
/**
* Enable/Disable automatic prompting of the play services update dialog
* @android
* @param bool
*/
set promptOnMissingPlayServices(bool: Boolean) {
INTERNALS.OPTIONS.promptOnMissingPlayServices = bool;
}
}
export const statics = {
DEFAULT_APP_NAME: INTERNALS.STRINGS.DEFAULT_APP_NAME,
// VERSIONS: {
// react: ReactVersion,
// 'react-native': Object.values(ReactNativeVersion.version).slice(0, 3).join('.'),
// 'react-native-firebase': PACKAGE.version,
// },
};