2017-09-26 13:57:25 +00:00
|
|
|
/**
|
|
|
|
* @flow
|
|
|
|
* Firestore representation wrapper
|
|
|
|
*/
|
2017-11-28 07:41:55 +00:00
|
|
|
import { NativeModules } from 'react-native';
|
|
|
|
|
2017-12-22 15:24:31 +00:00
|
|
|
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
|
|
|
import ModuleBase from '../../utils/ModuleBase';
|
2017-09-26 13:57:25 +00:00
|
|
|
import CollectionReference from './CollectionReference';
|
|
|
|
import DocumentReference from './DocumentReference';
|
2018-01-11 18:28:14 +00:00
|
|
|
import FieldPath from './FieldPath';
|
2017-10-12 08:00:46 +00:00
|
|
|
import FieldValue from './FieldValue';
|
2017-09-26 13:57:25 +00:00
|
|
|
import GeoPoint from './GeoPoint';
|
2018-05-03 19:13:51 +00:00
|
|
|
import Blob from './Blob';
|
2017-09-26 13:57:25 +00:00
|
|
|
import Path from './Path';
|
|
|
|
import WriteBatch from './WriteBatch';
|
2018-02-11 23:37:21 +00:00
|
|
|
import TransactionHandler from './TransactionHandler';
|
|
|
|
import Transaction from './Transaction';
|
2018-04-16 17:20:08 +00:00
|
|
|
import { isBoolean, isObject, isString, hop } from '../../utils';
|
2018-03-27 16:31:25 +00:00
|
|
|
import { getNativeModule } from '../../utils/native';
|
2017-09-26 13:57:25 +00:00
|
|
|
|
2017-11-17 11:07:52 +00:00
|
|
|
import type DocumentSnapshot from './DocumentSnapshot';
|
2018-02-14 13:00:19 +00:00
|
|
|
import type App from '../core/app';
|
2017-11-17 11:07:52 +00:00
|
|
|
import type QuerySnapshot from './QuerySnapshot';
|
|
|
|
|
2017-10-03 09:12:25 +00:00
|
|
|
type CollectionSyncEvent = {
|
|
|
|
appName: string,
|
|
|
|
querySnapshot?: QuerySnapshot,
|
|
|
|
error?: Object,
|
|
|
|
listenerId: string,
|
|
|
|
path: string,
|
2018-01-25 18:25:39 +00:00
|
|
|
};
|
2017-10-03 09:12:25 +00:00
|
|
|
|
2017-10-02 12:11:38 +00:00
|
|
|
type DocumentSyncEvent = {
|
|
|
|
appName: string,
|
2017-10-03 09:12:25 +00:00
|
|
|
documentSnapshot?: DocumentSnapshot,
|
2017-10-02 12:11:38 +00:00
|
|
|
error?: Object,
|
2017-10-03 09:12:25 +00:00
|
|
|
listenerId: string,
|
2017-10-02 12:11:38 +00:00
|
|
|
path: string,
|
2018-01-25 18:25:39 +00:00
|
|
|
};
|
2017-10-02 12:11:38 +00:00
|
|
|
|
2018-04-13 08:58:53 +00:00
|
|
|
type Settings = {
|
|
|
|
host?: string,
|
|
|
|
persistence?: boolean,
|
|
|
|
ssl?: boolean,
|
|
|
|
timestampsInSnapshots?: boolean,
|
|
|
|
};
|
|
|
|
|
2017-12-22 15:57:33 +00:00
|
|
|
const NATIVE_EVENTS = [
|
2018-02-11 23:37:21 +00:00
|
|
|
'firestore_transaction_event',
|
2017-12-22 15:57:33 +00:00
|
|
|
'firestore_document_sync_event',
|
2018-02-11 23:37:21 +00:00
|
|
|
'firestore_collection_sync_event',
|
2017-12-22 15:57:33 +00:00
|
|
|
];
|
|
|
|
|
2018-04-13 07:41:17 +00:00
|
|
|
const LogLevels = ['debug', 'error', 'silent'];
|
|
|
|
|
2018-01-03 20:00:38 +00:00
|
|
|
export const MODULE_NAME = 'RNFirebaseFirestore';
|
|
|
|
export const NAMESPACE = 'firestore';
|
|
|
|
|
2017-12-22 15:24:31 +00:00
|
|
|
/**
|
|
|
|
* @class Firestore
|
|
|
|
*/
|
|
|
|
export default class Firestore extends ModuleBase {
|
2017-09-26 13:57:25 +00:00
|
|
|
_referencePath: Path;
|
2018-02-11 23:37:21 +00:00
|
|
|
_transactionHandler: TransactionHandler;
|
2017-09-26 13:57:25 +00:00
|
|
|
|
2018-01-05 17:20:02 +00:00
|
|
|
constructor(app: App) {
|
|
|
|
super(app, {
|
2018-01-03 20:00:38 +00:00
|
|
|
events: NATIVE_EVENTS,
|
|
|
|
moduleName: MODULE_NAME,
|
2018-01-09 17:31:00 +00:00
|
|
|
multiApp: true,
|
2017-11-21 23:37:05 +00:00
|
|
|
hasShards: false,
|
2018-01-03 20:00:38 +00:00
|
|
|
namespace: NAMESPACE,
|
|
|
|
});
|
2018-02-11 23:37:21 +00:00
|
|
|
|
2017-09-26 13:57:25 +00:00
|
|
|
this._referencePath = new Path([]);
|
2018-02-11 23:37:21 +00:00
|
|
|
this._transactionHandler = new TransactionHandler(this);
|
2017-10-02 12:11:38 +00:00
|
|
|
|
2017-12-22 15:24:31 +00:00
|
|
|
SharedEventEmitter.addListener(
|
2017-10-02 12:11:38 +00:00
|
|
|
// sub to internal native event - this fans out to
|
|
|
|
// public event name: onCollectionSnapshot
|
2017-12-22 15:24:31 +00:00
|
|
|
getAppEventName(this, 'firestore_collection_sync_event'),
|
2018-01-25 18:25:39 +00:00
|
|
|
this._onCollectionSyncEvent.bind(this)
|
2017-10-02 12:11:38 +00:00
|
|
|
);
|
|
|
|
|
2017-12-22 15:24:31 +00:00
|
|
|
SharedEventEmitter.addListener(
|
2017-10-02 12:11:38 +00:00
|
|
|
// sub to internal native event - this fans out to
|
|
|
|
// public event name: onDocumentSnapshot
|
2017-12-22 15:24:31 +00:00
|
|
|
getAppEventName(this, 'firestore_document_sync_event'),
|
2018-01-25 18:25:39 +00:00
|
|
|
this._onDocumentSyncEvent.bind(this)
|
2017-10-02 12:11:38 +00:00
|
|
|
);
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
2018-02-11 23:37:21 +00:00
|
|
|
/**
|
|
|
|
* -------------
|
|
|
|
* PUBLIC API
|
|
|
|
* -------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a write batch, used for performing multiple writes as a single atomic operation.
|
|
|
|
*
|
|
|
|
* @returns {WriteBatch}
|
|
|
|
*/
|
2017-09-26 13:57:25 +00:00
|
|
|
batch(): WriteBatch {
|
|
|
|
return new WriteBatch(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-11 23:37:21 +00:00
|
|
|
* Gets a CollectionReference instance that refers to the collection at the specified path.
|
2017-09-26 13:57:25 +00:00
|
|
|
*
|
|
|
|
* @param collectionPath
|
|
|
|
* @returns {CollectionReference}
|
|
|
|
*/
|
|
|
|
collection(collectionPath: string): CollectionReference {
|
2017-11-28 09:21:41 +00:00
|
|
|
const path = this._referencePath.child(collectionPath);
|
2017-09-26 13:57:25 +00:00
|
|
|
if (!path.isCollection) {
|
|
|
|
throw new Error('Argument "collectionPath" must point to a collection.');
|
|
|
|
}
|
|
|
|
|
|
|
|
return new CollectionReference(this, path);
|
|
|
|
}
|
|
|
|
|
2018-03-27 16:31:25 +00:00
|
|
|
disableNetwork(): void {
|
|
|
|
return getNativeModule(this).disableNetwork();
|
|
|
|
}
|
|
|
|
|
2017-09-26 13:57:25 +00:00
|
|
|
/**
|
2018-02-11 23:37:21 +00:00
|
|
|
* Gets a DocumentReference instance that refers to the document at the specified path.
|
2017-09-26 13:57:25 +00:00
|
|
|
*
|
|
|
|
* @param documentPath
|
|
|
|
* @returns {DocumentReference}
|
|
|
|
*/
|
|
|
|
doc(documentPath: string): DocumentReference {
|
2017-11-28 09:21:41 +00:00
|
|
|
const path = this._referencePath.child(documentPath);
|
2017-09-26 13:57:25 +00:00
|
|
|
if (!path.isDocument) {
|
|
|
|
throw new Error('Argument "documentPath" must point to a document.');
|
|
|
|
}
|
|
|
|
|
|
|
|
return new DocumentReference(this, path);
|
|
|
|
}
|
|
|
|
|
2018-03-27 16:31:25 +00:00
|
|
|
enableNetwork(): Promise<void> {
|
|
|
|
return getNativeModule(this).enableNetwork();
|
|
|
|
}
|
|
|
|
|
2018-02-11 23:37:21 +00:00
|
|
|
/**
|
|
|
|
* Executes the given updateFunction and then attempts to commit the
|
|
|
|
* changes applied within the transaction. If any document read within
|
|
|
|
* the transaction has changed, Cloud Firestore retries the updateFunction.
|
|
|
|
*
|
|
|
|
* If it fails to commit after 5 attempts, the transaction fails.
|
|
|
|
*
|
|
|
|
* @param updateFunction
|
|
|
|
* @returns {void|Promise<any>}
|
|
|
|
*/
|
|
|
|
runTransaction(
|
|
|
|
updateFunction: (transaction: Transaction) => Promise<any>
|
|
|
|
): Promise<any> {
|
|
|
|
return this._transactionHandler._add(updateFunction);
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
2018-04-13 08:58:53 +00:00
|
|
|
settings(settings: Settings): Promise<void> {
|
|
|
|
if (!isObject(settings)) {
|
|
|
|
return Promise.reject(
|
|
|
|
new Error('Firestore.settings failed: settings must be an object.')
|
|
|
|
);
|
2018-04-16 17:20:08 +00:00
|
|
|
} else if (hop(settings, 'host') && !isString(settings.host)) {
|
2018-04-13 08:58:53 +00:00
|
|
|
return Promise.reject(
|
|
|
|
new Error('Firestore.settings failed: settings.host must be a string.')
|
|
|
|
);
|
2018-04-16 17:20:08 +00:00
|
|
|
} else if (
|
|
|
|
hop(settings, 'persistence') &&
|
|
|
|
!isBoolean(settings.persistence)
|
|
|
|
) {
|
2018-04-13 08:58:53 +00:00
|
|
|
return Promise.reject(
|
|
|
|
new Error(
|
|
|
|
'Firestore.settings failed: settings.persistence must be boolean.'
|
|
|
|
)
|
|
|
|
);
|
2018-04-16 17:20:08 +00:00
|
|
|
} else if (hop(settings, 'ssl') && !isBoolean(settings.ssl)) {
|
2018-04-13 08:58:53 +00:00
|
|
|
return Promise.reject(
|
|
|
|
new Error('Firestore.settings failed: settings.ssl must be boolean.')
|
|
|
|
);
|
|
|
|
} else if (
|
2018-04-16 17:20:08 +00:00
|
|
|
hop(settings, 'timestampsInSnapshots') &&
|
2018-04-13 08:58:53 +00:00
|
|
|
!isBoolean(settings.timestampsInSnapshots)
|
|
|
|
) {
|
|
|
|
return Promise.reject(
|
|
|
|
new Error(
|
|
|
|
'Firestore.settings failed: settings.timestampsInSnapshots must be boolean.'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return getNativeModule(this).settings(settings);
|
|
|
|
}
|
2017-09-26 13:57:25 +00:00
|
|
|
|
2018-02-11 23:37:21 +00:00
|
|
|
/**
|
|
|
|
* -------------
|
2018-04-13 08:58:53 +00:00
|
|
|
* UNSUPPORTED
|
2018-02-11 23:37:21 +00:00
|
|
|
* -------------
|
|
|
|
*/
|
2017-09-26 13:57:25 +00:00
|
|
|
|
2018-02-11 23:37:21 +00:00
|
|
|
enablePersistence(): Promise<void> {
|
2018-04-13 08:58:53 +00:00
|
|
|
console.warn(
|
|
|
|
'Due to restrictions in the native SDK, persistence must be configured in firebase.firestore().settings()'
|
|
|
|
);
|
|
|
|
return Promise.resolve();
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
2017-12-22 15:24:31 +00:00
|
|
|
|
2018-02-11 23:37:21 +00:00
|
|
|
/**
|
|
|
|
* -------------
|
|
|
|
* INTERNALS
|
|
|
|
* -------------
|
|
|
|
*/
|
|
|
|
|
2017-12-22 15:24:31 +00:00
|
|
|
/**
|
|
|
|
* Internal collection sync listener
|
2018-02-11 23:37:21 +00:00
|
|
|
*
|
2017-12-22 15:24:31 +00:00
|
|
|
* @param event
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_onCollectionSyncEvent(event: CollectionSyncEvent) {
|
|
|
|
if (event.error) {
|
2018-01-25 18:25:39 +00:00
|
|
|
SharedEventEmitter.emit(
|
|
|
|
getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`),
|
|
|
|
event.error
|
|
|
|
);
|
2017-12-22 15:24:31 +00:00
|
|
|
} else {
|
2018-01-25 18:25:39 +00:00
|
|
|
SharedEventEmitter.emit(
|
|
|
|
getAppEventName(this, `onQuerySnapshot:${event.listenerId}`),
|
|
|
|
event.querySnapshot
|
|
|
|
);
|
2017-12-22 15:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal document sync listener
|
2018-02-11 23:37:21 +00:00
|
|
|
*
|
2017-12-22 15:24:31 +00:00
|
|
|
* @param event
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_onDocumentSyncEvent(event: DocumentSyncEvent) {
|
|
|
|
if (event.error) {
|
2018-01-25 18:25:39 +00:00
|
|
|
SharedEventEmitter.emit(
|
|
|
|
getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`),
|
|
|
|
event.error
|
|
|
|
);
|
2017-12-22 15:24:31 +00:00
|
|
|
} else {
|
2018-01-25 18:25:39 +00:00
|
|
|
SharedEventEmitter.emit(
|
|
|
|
getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`),
|
|
|
|
event.documentSnapshot
|
|
|
|
);
|
2017-12-22 15:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export const statics = {
|
2018-05-04 15:30:32 +00:00
|
|
|
Blob,
|
2018-01-11 18:28:14 +00:00
|
|
|
FieldPath,
|
2017-10-12 08:00:46 +00:00
|
|
|
FieldValue,
|
2017-10-05 09:18:24 +00:00
|
|
|
GeoPoint,
|
2018-04-13 07:41:17 +00:00
|
|
|
enableLogging(enabled: boolean): void {
|
|
|
|
// DEPRECATED: Remove method in v4.1.0
|
|
|
|
console.warn(
|
|
|
|
'firebase.firestore.enableLogging is deprecated, use firebase.firestore().setLogLevel instead.'
|
|
|
|
);
|
|
|
|
this.setLogLevel(enabled ? 'debug' : 'silent');
|
|
|
|
},
|
|
|
|
setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void {
|
|
|
|
if (LogLevels.indexOf(logLevel) === -1) {
|
|
|
|
throw new Error(
|
|
|
|
'Argument `logLevel` must be one of: `debug`, `error`, `silent`'
|
|
|
|
);
|
|
|
|
}
|
2018-01-03 20:00:38 +00:00
|
|
|
if (NativeModules[MODULE_NAME]) {
|
2018-04-13 07:41:17 +00:00
|
|
|
NativeModules[MODULE_NAME].setLogLevel(logLevel);
|
2017-11-28 07:41:55 +00:00
|
|
|
}
|
|
|
|
},
|
2017-09-26 13:57:25 +00:00
|
|
|
};
|