[bridge] import RNF from project directory directly via module alias + metro config
This commit is contained in:
parent
ed2efcdd48
commit
2122554549
|
@ -9,8 +9,16 @@
|
|||
"useInlineSourceMaps": true,
|
||||
"instrument": true,
|
||||
"include": [
|
||||
"firebase"
|
||||
"lib/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}],
|
||||
["module-resolver", {
|
||||
"alias": {
|
||||
"react-native-firebase": ".."
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import React, { Component } from 'react';
|
|||
import { AppRegistry, Text, View } from 'react-native';
|
||||
|
||||
import bridge from 'bridge/platform/react-native';
|
||||
import firebase from './firebase';
|
||||
import firebase from 'react-native-firebase';
|
||||
|
||||
require('sinon');
|
||||
require('should-sinon');
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
describe('.auth()', () => {
|
||||
beforeEach(async () => {
|
||||
await device.reloadReactNative();
|
||||
// bridge.root.setState({ message: this.currentTest.title });
|
||||
});
|
||||
|
||||
describe('.signInAnonymously()', () => {
|
||||
it('it should sign in anonymously', () => {
|
||||
const successCb = currentUser => {
|
||||
|
@ -13,9 +8,7 @@ describe('.auth()', () => {
|
|||
should.equal(currentUser.toJSON().email, null);
|
||||
currentUser.isAnonymous.should.equal(true);
|
||||
currentUser.providerId.should.equal('firebase');
|
||||
|
||||
currentUser.should.equal(firebase.auth().currentUser);
|
||||
|
||||
return firebase.auth().signOut();
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ const should = require('should');
|
|||
|
||||
describe('bridge', () => {
|
||||
beforeEach(async function beforeEach() {
|
||||
await device.reloadReactNative();
|
||||
// await device.reloadReactNative();
|
||||
bridge.root.setState({ message: this.currentTest.title });
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--recursive
|
||||
--timeout 120000
|
||||
--slow 2200
|
||||
--slow 750
|
||||
--bail
|
||||
--exit
|
||||
--require bridge/platform/node
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
import { NativeModules } from 'react-native';
|
||||
|
||||
import INTERNALS from './internals';
|
||||
import { isObject, isAndroid } from './utils';
|
||||
|
||||
import AdMob, { statics as AdMobStatics } from './modules/admob';
|
||||
import Auth, { statics as AuthStatics } from './modules/auth';
|
||||
import Analytics from './modules/analytics';
|
||||
import Crash from './modules/crash';
|
||||
import Performance from './modules/perf';
|
||||
import RemoteConfig from './modules/config';
|
||||
import Storage, { statics as StorageStatics } from './modules/storage';
|
||||
import Database, { statics as DatabaseStatics } from './modules/database';
|
||||
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
||||
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
|
||||
import Links, { statics as LinksStatics } from './modules/links';
|
||||
import Utils, { statics as UtilsStatics } from './modules/utils';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
export default class FirebaseApp {
|
||||
constructor(name: string, options: Object = {}) {
|
||||
this._name = name;
|
||||
this._namespaces = {};
|
||||
this._options = Object.assign({}, options);
|
||||
|
||||
// native ios/android to confirm initialized
|
||||
this._initialized = false;
|
||||
this._nativeInitialized = false;
|
||||
|
||||
// modules
|
||||
this.admob = this._staticsOrModuleInstance(AdMobStatics, AdMob);
|
||||
this.auth = this._staticsOrModuleInstance(AuthStatics, Auth);
|
||||
this.analytics = this._staticsOrModuleInstance({}, Analytics);
|
||||
this.config = this._staticsOrModuleInstance({}, RemoteConfig);
|
||||
this.crash = this._staticsOrModuleInstance({}, Crash);
|
||||
this.database = this._staticsOrModuleInstance(DatabaseStatics, Database);
|
||||
this.firestore = this._staticsOrModuleInstance(FirestoreStatics, Firestore);
|
||||
this.links = this._staticsOrModuleInstance(LinksStatics, Links);
|
||||
this.messaging = this._staticsOrModuleInstance(MessagingStatics, Messaging);
|
||||
this.perf = this._staticsOrModuleInstance({}, Performance);
|
||||
this.storage = this._staticsOrModuleInstance(StorageStatics, Storage);
|
||||
this.utils = this._staticsOrModuleInstance(UtilsStatics, Utils);
|
||||
this._extendedProps = {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param native
|
||||
* @private
|
||||
*/
|
||||
_initializeApp(native = false) {
|
||||
if (native) {
|
||||
// for apps already initialized natively that
|
||||
// we have info from RN constants
|
||||
this._initialized = true;
|
||||
this._nativeInitialized = true;
|
||||
} else {
|
||||
FirebaseCoreModule.initializeApp(this._name, this._options, (error, result) => {
|
||||
this._initialized = true;
|
||||
INTERNALS.SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
get name() {
|
||||
if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME) {
|
||||
// ios and android firebase sdk's return different
|
||||
// app names - so we just return what the web sdk
|
||||
// would if it was default.
|
||||
return '[DEFAULT]';
|
||||
}
|
||||
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
get options() {
|
||||
return Object.assign({}, this._options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented firebase web sdk method that allows adding additional properties onto
|
||||
* a firebase app instance.
|
||||
*
|
||||
* See: https://github.com/firebase/firebase-js-sdk/blob/master/tests/app/firebase_app.test.ts#L328
|
||||
*
|
||||
* @param props
|
||||
*/
|
||||
extendApp(props: Object) {
|
||||
if (!isObject(props)) throw new Error(INTERNALS.ERROR_MISSING_ARG('Object', 'extendApp'));
|
||||
const keys = Object.keys(props);
|
||||
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
const key = keys[i];
|
||||
|
||||
if (!this._extendedProps[key] && Object.hasOwnProperty.call(this, key)) {
|
||||
throw new Error(INTERNALS.ERROR_PROTECTED_PROP(key));
|
||||
}
|
||||
|
||||
this[key] = props[key];
|
||||
this._extendedProps[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
delete() {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete'));
|
||||
// TODO only the ios sdk currently supports delete, add back in when android also supports it
|
||||
// if (this._name === INTERNALS.STRINGS.DEFAULT_APP_NAME && this._nativeInitialized) {
|
||||
// return Promise.reject(
|
||||
// new Error('Unable to delete the default native firebase app instance.'),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// return FirebaseCoreModule.deleteApp(this._name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
onReady(): Promise {
|
||||
if (this._initialized) return Promise.resolve(this);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
INTERNALS.SharedEventEmitter.once(`AppReady:${this._name}`, ({ error }) => {
|
||||
if (error) return reject(new Error(error)); // error is a string as it's from native
|
||||
return resolve(this); // return app
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param statics
|
||||
* @param InstanceClass
|
||||
* @return {function()}
|
||||
* @private
|
||||
*/
|
||||
_staticsOrModuleInstance(statics = {}, InstanceClass): Function {
|
||||
const getInstance = () => {
|
||||
const _name = `_${InstanceClass._NAMESPACE}`;
|
||||
|
||||
if (isAndroid && InstanceClass._NAMESPACE !== Utils._NAMESPACE && !INTERNALS.FLAGS.checkedPlayServices) {
|
||||
INTERNALS.FLAGS.checkedPlayServices = true;
|
||||
this.utils().checkPlayServicesAvailability();
|
||||
}
|
||||
|
||||
if (!this._namespaces[_name]) {
|
||||
this._namespaces[_name] = new InstanceClass(this, this._options);
|
||||
}
|
||||
|
||||
return this._namespaces[_name];
|
||||
};
|
||||
|
||||
Object.assign(getInstance, statics, {
|
||||
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
|
||||
});
|
||||
|
||||
return getInstance;
|
||||
}
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
/**
|
||||
* @providesModule Firebase
|
||||
* @flow
|
||||
*/
|
||||
import { NativeModules, NativeEventEmitter } from 'react-native';
|
||||
|
||||
import INTERNALS from './internals';
|
||||
import FirebaseApp from './firebase-app';
|
||||
import { isObject, isString, isAndroid } from './utils';
|
||||
|
||||
// module imports
|
||||
import AdMob, { statics as AdMobStatics } from './modules/admob';
|
||||
import Auth, { statics as AuthStatics } from './modules/auth';
|
||||
import Analytics from './modules/analytics';
|
||||
import Crash from './modules/crash';
|
||||
import Performance from './modules/perf';
|
||||
import Links, { statics as LinksStatics } from './modules/links';
|
||||
import RemoteConfig from './modules/config';
|
||||
import Storage, { statics as StorageStatics } from './modules/storage';
|
||||
import Database, { statics as DatabaseStatics } from './modules/database';
|
||||
import Messaging, { statics as MessagingStatics } from './modules/messaging';
|
||||
import Firestore, { statics as FirestoreStatics } from './modules/firestore';
|
||||
import Utils, { statics as UtilsStatics } from './modules/utils';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
class FirebaseCore {
|
||||
constructor() {
|
||||
this._nativeEmitters = {};
|
||||
this._nativeSubscriptions = {};
|
||||
|
||||
if (!FirebaseCoreModule) {
|
||||
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
|
||||
}
|
||||
|
||||
this._initializeNativeApps();
|
||||
|
||||
// modules
|
||||
this.admob = this._appNamespaceOrStatics(AdMobStatics, AdMob);
|
||||
this.auth = this._appNamespaceOrStatics(AuthStatics, Auth);
|
||||
this.analytics = this._appNamespaceOrStatics({}, Analytics);
|
||||
this.config = this._appNamespaceOrStatics({}, RemoteConfig);
|
||||
this.crash = this._appNamespaceOrStatics({}, Crash);
|
||||
this.database = this._appNamespaceOrStatics(DatabaseStatics, Database);
|
||||
this.firestore = this._appNamespaceOrStatics(FirestoreStatics, Firestore);
|
||||
this.links = this._appNamespaceOrStatics(LinksStatics, Links);
|
||||
this.messaging = this._appNamespaceOrStatics(MessagingStatics, Messaging);
|
||||
this.perf = this._appNamespaceOrStatics(DatabaseStatics, Performance);
|
||||
this.storage = this._appNamespaceOrStatics(StorageStatics, Storage);
|
||||
this.utils = this._appNamespaceOrStatics(UtilsStatics, Utils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstraps all native app instances that were discovered on boot
|
||||
* @private
|
||||
*/
|
||||
_initializeNativeApps() {
|
||||
for (let i = 0, len = FirebaseCoreModule.apps.length; i < len; i++) {
|
||||
const app = FirebaseCoreModule.apps[i];
|
||||
const options = Object.assign({}, app);
|
||||
delete options.name;
|
||||
INTERNALS.APPS[app.name] = new FirebaseApp(app.name, options);
|
||||
INTERNALS.APPS[app.name]._initializeApp(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Web SDK initializeApp
|
||||
*
|
||||
* @param options
|
||||
* @param name
|
||||
* @return {*}
|
||||
*/
|
||||
initializeApp(options: Object = {}, name: string): FirebaseApp {
|
||||
if (name && !isString(name)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_INIT_STRING_NAME);
|
||||
}
|
||||
|
||||
const _name = (name || INTERNALS.STRINGS.DEFAULT_APP_NAME).toUpperCase();
|
||||
|
||||
// return an existing app if found
|
||||
// todo in v4 remove deprecation and throw an error
|
||||
if (INTERNALS.APPS[_name]) {
|
||||
console.warn(INTERNALS.STRINGS.WARN_INITIALIZE_DEPRECATION);
|
||||
return INTERNALS.APPS[_name];
|
||||
}
|
||||
|
||||
// only validate if app doesn't already exist
|
||||
// to allow apps already initialized natively
|
||||
// to still go through init without erroring (backwards compatibility)
|
||||
if (!isObject(options)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_INIT_OBJECT);
|
||||
}
|
||||
|
||||
if (!options.apiKey) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('apiKey'));
|
||||
}
|
||||
|
||||
if (!options.appId) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('appId'));
|
||||
}
|
||||
|
||||
if (!options.databaseURL) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('databaseURL'));
|
||||
}
|
||||
|
||||
if (!options.messagingSenderId) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('messagingSenderId'));
|
||||
}
|
||||
|
||||
if (!options.projectId) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('projectId'));
|
||||
}
|
||||
|
||||
if (!options.storageBucket) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_OPT('storageBucket'));
|
||||
}
|
||||
|
||||
INTERNALS.APPS[_name] = new FirebaseApp(_name, options);
|
||||
// only initialize if certain props are available
|
||||
if (options.databaseURL && options.apiKey) {
|
||||
INTERNALS.APPS[_name]._initializeApp();
|
||||
}
|
||||
|
||||
return INTERNALS.APPS[_name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a Firebase app instance.
|
||||
*
|
||||
* When called with no arguments, the default app is returned.
|
||||
* When an app name is provided, the app corresponding to that name is returned.
|
||||
*
|
||||
* @param name
|
||||
* @return {*}
|
||||
*/
|
||||
app(name?: string): FirebaseApp {
|
||||
const _name = name ? name.toUpperCase() : INTERNALS.STRINGS.DEFAULT_APP_NAME;
|
||||
const app = INTERNALS.APPS[_name];
|
||||
if (!app) throw new Error(INTERNALS.STRINGS.ERROR_APP_NOT_INIT(_name));
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* A (read-only) array of all initialized apps.
|
||||
* @return {Array}
|
||||
*/
|
||||
get apps(): Array<Object> {
|
||||
return Object.values(INTERNALS.APPS);
|
||||
}
|
||||
|
||||
/*
|
||||
* INTERNALS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Subscribe to a native event for js side distribution by appName
|
||||
* React Native events are hard set at compile - cant do dynamic event names
|
||||
* so we use a single event send it to js and js then internally can prefix it
|
||||
* and distribute dynamically.
|
||||
*
|
||||
* @param eventName
|
||||
* @param nativeEmitter
|
||||
* @private
|
||||
*/
|
||||
_subscribeForDistribution(eventName, nativeEmitter) {
|
||||
if (!this._nativeSubscriptions[eventName]) {
|
||||
nativeEmitter.addListener(eventName, (event) => {
|
||||
if (event.appName) {
|
||||
// native event has an appName property - auto prefix and internally emit
|
||||
INTERNALS.SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
|
||||
} else {
|
||||
// standard event - no need to prefix
|
||||
INTERNALS.SharedEventEmitter.emit(eventName, event);
|
||||
}
|
||||
});
|
||||
|
||||
this._nativeSubscriptions[eventName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param statics
|
||||
* @param InstanceClass
|
||||
* @return {function(FirebaseApp=)}
|
||||
* @private
|
||||
*/
|
||||
_appNamespaceOrStatics(statics = {}, InstanceClass): Function {
|
||||
const namespace = InstanceClass._NAMESPACE;
|
||||
|
||||
const getNamespace = (app?: FirebaseApp) => {
|
||||
let _app = app;
|
||||
|
||||
// throw an error if it's not a valid app instance
|
||||
if (_app && !(_app instanceof FirebaseApp)) throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
|
||||
|
||||
// default to the 'DEFAULT' app if no arg provided - will throw an error
|
||||
// if default app not initialized
|
||||
else if (!_app) _app = this.app(INTERNALS.STRINGS.DEFAULT_APP_NAME);
|
||||
return INTERNALS.APPS[_app._name][namespace](_app);
|
||||
};
|
||||
|
||||
Object.assign(getNamespace, statics, {
|
||||
nativeModuleExists: !!NativeModules[InstanceClass._NATIVE_MODULE],
|
||||
});
|
||||
|
||||
return getNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param nativeModule
|
||||
* @return {*}
|
||||
* @private
|
||||
*/
|
||||
_getOrSetNativeEmitter(name, nativeModule) {
|
||||
if (this._nativeEmitters[name]) {
|
||||
return this._nativeEmitters[name];
|
||||
}
|
||||
|
||||
return this._nativeEmitters[name] = new NativeEventEmitter(nativeModule);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new FirebaseCore();
|
|
@ -1,50 +0,0 @@
|
|||
/* eslint-disable */
|
||||
// declare module 'react-native' {
|
||||
// // noinspection ES6ConvertVarToLetConst
|
||||
// declare var exports: any;
|
||||
// }
|
||||
|
||||
declare type AuthResultType = {
|
||||
authenticated: boolean,
|
||||
user: Object|null
|
||||
} | null;
|
||||
|
||||
declare type CredentialType = {
|
||||
providerId: string,
|
||||
token: string,
|
||||
secret: string
|
||||
};
|
||||
|
||||
declare type DatabaseListener = {
|
||||
listenerId: number;
|
||||
eventName: string;
|
||||
successCallback: Function;
|
||||
failureCallback?: Function;
|
||||
};
|
||||
|
||||
declare type DatabaseModifier = {
|
||||
type: 'orderBy' | 'limit' | 'filter';
|
||||
name?: string;
|
||||
key?: string;
|
||||
limit?: number;
|
||||
value?: any;
|
||||
valueType?: string;
|
||||
};
|
||||
|
||||
declare type GoogleApiAvailabilityType = {
|
||||
status: number,
|
||||
isAvailable: boolean,
|
||||
isUserResolvableError?: boolean,
|
||||
hasResolution?: boolean,
|
||||
error?: string
|
||||
};
|
||||
|
||||
declare class FirebaseError {
|
||||
message: string,
|
||||
name: string,
|
||||
code: string,
|
||||
stack: string,
|
||||
path: string,
|
||||
details: string,
|
||||
modifiers: string
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
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/phone/ConfirmationResult';
|
||||
export type { default as User } from './modules/auth/User';
|
||||
|
||||
/*
|
||||
* 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';
|
||||
|
||||
/*
|
||||
* Export Messaging types
|
||||
*/
|
||||
export type {
|
||||
default as RemoteMessage,
|
||||
} from './modules/messaging/RemoteMessage';
|
||||
|
||||
/*
|
||||
* Export Notifications types
|
||||
*/
|
||||
export type {
|
||||
default as Notification,
|
||||
} from './modules/notifications/Notification';
|
|
@ -1,239 +0,0 @@
|
|||
import { Platform, NativeModules } from 'react-native';
|
||||
|
||||
import EventEmitter from './utils/emitter/EventEmitter';
|
||||
import SyncTree from './utils/SyncTree';
|
||||
|
||||
const DEFAULT_APP_NAME = Platform.OS === 'ios' ? '__FIRAPP_DEFAULT' : '[DEFAULT]';
|
||||
|
||||
const NAMESPACE_PODS = {
|
||||
admob: 'Firebase/AdMob',
|
||||
analytics: 'Firebase/Analytics',
|
||||
auth: 'Firebase/Auth',
|
||||
config: 'Firebase/RemoteConfig',
|
||||
crash: 'Firebase/Crash',
|
||||
database: 'Firebase/Database',
|
||||
links: 'Firebase/DynamicLinks',
|
||||
messaging: 'Firebase/Messaging',
|
||||
perf: 'Firebase/Performance',
|
||||
storage: 'Firebase/Storage',
|
||||
};
|
||||
|
||||
const GRADLE_DEPS = {
|
||||
admob: 'ads',
|
||||
};
|
||||
|
||||
const PLAY_SERVICES_CODES = {
|
||||
1: {
|
||||
code: 'SERVICE_MISSING',
|
||||
message: 'Google Play services is missing on this device.',
|
||||
},
|
||||
2: {
|
||||
code: 'SERVICE_VERSION_UPDATE_REQUIRED',
|
||||
message: 'The installed version of Google Play services on this device is out of date.',
|
||||
},
|
||||
3: {
|
||||
code: 'SERVICE_DISABLED',
|
||||
message: 'The installed version of Google Play services has been disabled on this device.',
|
||||
},
|
||||
9: {
|
||||
code: 'SERVICE_INVALID',
|
||||
message: 'The version of the Google Play services installed on this device is not authentic.',
|
||||
},
|
||||
18: {
|
||||
code: 'SERVICE_UPDATING',
|
||||
message: 'Google Play services is currently being updated on this device.',
|
||||
},
|
||||
19: {
|
||||
code: 'SERVICE_MISSING_PERMISSION',
|
||||
message: 'Google Play service doesn\'t have one or more required permissions.',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
// default options
|
||||
OPTIONS: {
|
||||
logLevel: 'warn',
|
||||
errorOnMissingPlayServices: true,
|
||||
promptOnMissingPlayServices: true,
|
||||
},
|
||||
|
||||
FLAGS: {
|
||||
checkedPlayServices: false,
|
||||
},
|
||||
|
||||
// track all initialized firebase apps
|
||||
APPS: {
|
||||
[DEFAULT_APP_NAME]: null,
|
||||
},
|
||||
|
||||
STRINGS: {
|
||||
WARN_INITIALIZE_DEPRECATION: 'Deprecation: Calling \'initializeApp()\' for apps that are already initialised natively ' +
|
||||
'is unnecessary, use \'firebase.app()\' instead to access the already initialized default app instance.',
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
get ERROR_MISSING_CORE() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return 'RNFirebase core module was not found natively on iOS, ensure you have ' +
|
||||
'correctly included the RNFirebase pod in your projects `Podfile` and have run `pod install`.' +
|
||||
'\r\n\r\n See http://invertase.link/ios for the ios setup guide.';
|
||||
}
|
||||
|
||||
return 'RNFirebase core module was not found natively on Android, ensure you have ' +
|
||||
'correctly added the RNFirebase and Firebase gradle dependencies to your `android/app/build.gradle` file.' +
|
||||
'\r\n\r\n See http://invertase.link/android for the android setup guide.';
|
||||
},
|
||||
|
||||
|
||||
ERROR_INIT_OBJECT: 'Firebase.initializeApp(options <-- requires a valid configuration object.',
|
||||
ERROR_INIT_STRING_NAME: 'Firebase.initializeApp(options, name <-- requires a valid string value.',
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_MISSING_CB(method) {
|
||||
return `Missing required callback for method ${method}().`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_MISSING_ARG(type, method) {
|
||||
return `Missing required argument of type '${type}' for method '${method}()'.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_MISSING_ARG_NAMED(name, type, method) {
|
||||
return `Missing required argument '${name}' of type '${type}' for method '${method}()'.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_ARG_INVALID_VALUE(name, expected, got) {
|
||||
return `Invalid value for argument '${name}' expected value '${expected}' but got '${got}'.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_PROTECTED_PROP(name) {
|
||||
return `Property '${name}' is protected and can not be overridden by extendApp.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
* @param namespace
|
||||
* @param nativeModule
|
||||
*/
|
||||
ERROR_MISSING_MODULE(namespace, nativeModule) {
|
||||
const snippet = `firebase.${namespace}()`;
|
||||
if (Platform.OS === 'ios') {
|
||||
return `You attempted to use a firebase module that's not installed natively on your iOS project by calling ${snippet}.` +
|
||||
'\r\n\r\nEnsure you have the required Firebase iOS SDK pod for this module included in your Podfile, in this instance ' +
|
||||
`confirm you've added "pod '${NAMESPACE_PODS[namespace]}'" to your Podfile` +
|
||||
'\r\n\r\nSee http://invertase.link/ios for full setup instructions.';
|
||||
}
|
||||
|
||||
const fbSDKDep = `'com.google.firebase:firebase-${GRADLE_DEPS[namespace] || namespace}'`;
|
||||
const rnFirebasePackage = `'io.invertase.firebase.${namespace}.${nativeModule}Package'`;
|
||||
const newInstance = `'new ${nativeModule}Package()'`;
|
||||
return `You attempted to use a firebase module that's not installed on your Android project by calling ${snippet}.` +
|
||||
`\r\n\r\nEnsure you have:\r\n\r\n1) Installed the required Firebase Android SDK dependency ${fbSDKDep} in your 'android/app/build.gradle' ` +
|
||||
`file.\r\n\r\n2) Imported the ${rnFirebasePackage} module in your 'MainApplication.java' file.\r\n\r\n3) Added the ` +
|
||||
`${newInstance} line inside of the RN 'getPackages()' method list.` +
|
||||
'\r\n\r\nSee http://invertase.link/android for full setup instructions.';
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_APP_NOT_INIT(appName) {
|
||||
return `The [${appName}] firebase app has not been initialized!`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param optName
|
||||
* @return {string}
|
||||
* @constructor
|
||||
*/
|
||||
ERROR_MISSING_OPT(optName) {
|
||||
return `Failed to initialize app. FirebaseOptions missing or invalid '${optName}' property.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_NOT_APP(namespace) {
|
||||
return `Invalid FirebaseApp instance passed to firebase.${namespace}(app <--).`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_UNSUPPORTED_CLASS_METHOD(className, method) {
|
||||
return `${className}.${method}() is unsupported by the native Firebase SDKs.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_UNSUPPORTED_CLASS_PROPERTY(className, property) {
|
||||
return `${className}.${property} is unsupported by the native Firebase SDKs.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_UNSUPPORTED_MODULE_METHOD(module, method) {
|
||||
return `firebase.${module._NAMESPACE}().${method}() is unsupported by the native Firebase SDKs.`;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_PLAY_SERVICES(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.';
|
||||
|
||||
if (statusCode === 2) {
|
||||
start = 'Google Play Services is out of date and may cause some firebase services like authentication to hang when used. It is recommended that you update it.';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prefer-template
|
||||
return `${start}\r\n\r\n` +
|
||||
'-------------------------\r\n' +
|
||||
(knownError ?
|
||||
`${knownError.code}: ${knownError.message} (code ${statusCode})` :
|
||||
`A specific play store availability reason reason was not available (unknown code: ${statusCode || null})`
|
||||
) +
|
||||
'\r\n-------------------------' +
|
||||
'\r\n\r\n' +
|
||||
'For more information on how to resolve this issue, configure Play Services checks or for guides on how to validate Play Services on your users devices see the link below:' +
|
||||
'\r\n\r\nhttp://invertase.link/play-services';
|
||||
},
|
||||
|
||||
|
||||
DEFAULT_APP_NAME,
|
||||
},
|
||||
|
||||
|
||||
SharedEventEmitter: new EventEmitter(),
|
||||
SyncTree: NativeModules.RNFirebaseDatabase ? new SyncTree(NativeModules.RNFirebaseDatabase) : null,
|
||||
|
||||
// internal utils
|
||||
deleteApp(name: String) {
|
||||
const app = this.APPS[name];
|
||||
if (!app) return Promise.resolve();
|
||||
|
||||
// https://firebase.google.com/docs/reference/js/firebase.app.App#delete
|
||||
return app.delete().then(() => {
|
||||
delete this.APPS[name];
|
||||
return true;
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1,100 +0,0 @@
|
|||
import React from 'react';
|
||||
import { ViewPropTypes, requireNativeComponent } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import EventTypes, { NativeExpressEventTypes } from './EventTypes';
|
||||
import { nativeToJSError } from '../../utils';
|
||||
|
||||
import AdRequest from './AdRequest';
|
||||
import VideoOptions from './VideoOptions';
|
||||
|
||||
const adMobPropTypes = {
|
||||
...ViewPropTypes,
|
||||
size: PropTypes.string.isRequired,
|
||||
unitId: PropTypes.string.isRequired,
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
request: PropTypes.object,
|
||||
video: PropTypes.object,
|
||||
/* eslint-enable react/forbid-prop-types */
|
||||
};
|
||||
Object.keys(EventTypes).forEach(eventType => {
|
||||
adMobPropTypes[eventType] = PropTypes.func;
|
||||
});
|
||||
Object.keys(NativeExpressEventTypes).forEach(eventType => {
|
||||
adMobPropTypes[eventType] = PropTypes.func;
|
||||
});
|
||||
|
||||
const nativeComponents = {};
|
||||
|
||||
function getNativeComponent(name) {
|
||||
if (nativeComponents[name]) return nativeComponents[name];
|
||||
const component = requireNativeComponent(name, AdMobComponent, {
|
||||
nativeOnly: {
|
||||
onBannerEvent: true,
|
||||
},
|
||||
});
|
||||
nativeComponents[name] = component;
|
||||
return component;
|
||||
}
|
||||
|
||||
class AdMobComponent extends React.Component {
|
||||
static propTypes = adMobPropTypes;
|
||||
|
||||
static defaultProps = {
|
||||
request: new AdRequest().addTestDevice().build(),
|
||||
video: new VideoOptions().build(),
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
this.nativeView = getNativeComponent(props.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a single banner event and pass to
|
||||
* any props watching it
|
||||
* @param nativeEvent
|
||||
*/
|
||||
onBannerEvent = ({ nativeEvent }) => {
|
||||
if (this.props[nativeEvent.type]) {
|
||||
if (nativeEvent.type === 'onAdFailedToLoad') {
|
||||
const { code, message } = nativeEvent.payload;
|
||||
this.props[nativeEvent.type](nativeToJSError(code, message));
|
||||
} else {
|
||||
this.props[nativeEvent.type](nativeEvent.payload || {});
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeEvent.type === 'onSizeChange')
|
||||
this.updateSize(nativeEvent.payload);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the JS size of the loaded banner
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
updateSize = ({ width, height }) => {
|
||||
this.setState({ width, height });
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the native component
|
||||
* @returns {XML}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<this.nativeView
|
||||
{...this.props}
|
||||
style={[this.props.style, { ...this.state }]}
|
||||
onBannerEvent={this.onBannerEvent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AdMobComponent;
|
|
@ -1,58 +0,0 @@
|
|||
export default class AdRequest {
|
||||
constructor() {
|
||||
this._props = {
|
||||
keywords: [],
|
||||
testDevices: [],
|
||||
};
|
||||
}
|
||||
|
||||
build() {
|
||||
return this._props;
|
||||
}
|
||||
|
||||
addTestDevice(deviceId?: string) {
|
||||
this._props.testDevices.push(deviceId || 'DEVICE_ID_EMULATOR');
|
||||
return this;
|
||||
}
|
||||
|
||||
addKeyword(keyword: string) {
|
||||
this._props.keywords.push(keyword);
|
||||
return this;
|
||||
}
|
||||
|
||||
setBirthday() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
setContentUrl(url: string) {
|
||||
this._props.contentUrl = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
setGender(gender: 'male | female | unknown') {
|
||||
const genders = ['male', 'female', 'unknown'];
|
||||
if (genders.includes(gender)) {
|
||||
this._props.gender = gender;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
setLocation() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
setRequestAgent(requestAgent: string) {
|
||||
this._props.requestAgent = requestAgent;
|
||||
return this;
|
||||
}
|
||||
|
||||
setIsDesignedForFamilies(isDesignedForFamilies: boolean) {
|
||||
this._props.isDesignedForFamilies = isDesignedForFamilies;
|
||||
return this;
|
||||
}
|
||||
|
||||
tagForChildDirectedTreatment(tagForChildDirectedTreatment: boolean) {
|
||||
this._props.tagForChildDirectedTreatment = tagForChildDirectedTreatment;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import React from 'react';
|
||||
import AdMobComponent from './AdMobComponent';
|
||||
|
||||
function Banner({ ...props }) {
|
||||
return <AdMobComponent {...props} class="RNFirebaseAdMobBanner" />;
|
||||
}
|
||||
|
||||
Banner.propTypes = AdMobComponent.propTypes;
|
||||
|
||||
Banner.defaultProps = {
|
||||
size: 'SMART_BANNER',
|
||||
};
|
||||
|
||||
export default Banner;
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export default {
|
||||
onAdLoaded: 'onAdLoaded',
|
||||
onAdOpened: 'onAdOpened',
|
||||
onAdLeftApplication: 'onAdLeftApplication',
|
||||
onAdClosed: 'onAdClosed',
|
||||
onAdFailedToLoad: 'onAdFailedToLoad',
|
||||
};
|
||||
|
||||
export const NativeExpressEventTypes = {
|
||||
onVideoEnd: 'onVideoEnd',
|
||||
onVideoMute: 'onVideoMute',
|
||||
onVideoPause: 'onVideoPause',
|
||||
onVideoPlay: 'onVideoPlay',
|
||||
onVideoStart: 'onVideoStart',
|
||||
};
|
||||
|
||||
export const RewardedVideoEventTypes = {
|
||||
onRewarded: 'onRewarded',
|
||||
onRewardedVideoStarted: 'onRewardedVideoStarted',
|
||||
};
|
|
@ -1,119 +0,0 @@
|
|||
import { Platform } from 'react-native';
|
||||
import { statics } from './';
|
||||
import AdRequest from './AdRequest';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { nativeToJSError } from '../../utils';
|
||||
import type AdMob from './';
|
||||
|
||||
let subscriptions = [];
|
||||
|
||||
export default class Interstitial {
|
||||
_admob: AdMob;
|
||||
|
||||
constructor(admob: AdMob, adUnit: string) {
|
||||
// Interstitials on iOS require a new instance each time
|
||||
if (Platform.OS === 'ios') {
|
||||
getNativeModule(admob).clearInterstitial(adUnit);
|
||||
}
|
||||
|
||||
for (let i = 0, len = subscriptions.length; i < len; i++) {
|
||||
subscriptions[i].remove();
|
||||
}
|
||||
subscriptions = [];
|
||||
|
||||
this._admob = admob;
|
||||
this.adUnit = adUnit;
|
||||
this.loaded = false;
|
||||
SharedEventEmitter.removeAllListeners(`interstitial_${adUnit}`);
|
||||
SharedEventEmitter.addListener(
|
||||
`interstitial_${adUnit}`,
|
||||
this._onInterstitialEvent
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a JS emit event
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onInterstitialEvent = event => {
|
||||
const eventType = `interstitial:${this.adUnit}:${event.type}`;
|
||||
|
||||
let emitData = Object.assign({}, event);
|
||||
|
||||
switch (event.type) {
|
||||
case 'onAdLoaded':
|
||||
this.loaded = true;
|
||||
break;
|
||||
case 'onAdFailedToLoad':
|
||||
emitData = nativeToJSError(event.payload.code, event.payload.message);
|
||||
emitData.type = event.type;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
SharedEventEmitter.emit(eventType, emitData);
|
||||
SharedEventEmitter.emit(`interstitial:${this.adUnit}:*`, emitData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an ad with an instance of AdRequest
|
||||
* @param request
|
||||
* @returns {*}
|
||||
*/
|
||||
loadAd(request?: AdRequest) {
|
||||
let adRequest = request;
|
||||
|
||||
if (!adRequest || !Object.keys(adRequest)) {
|
||||
adRequest = new AdRequest().addTestDevice().build();
|
||||
}
|
||||
|
||||
return getNativeModule(this._admob).interstitialLoadAd(
|
||||
this.adUnit,
|
||||
adRequest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a local instance of isLoaded
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the advert - will only show if loaded
|
||||
* @returns {*}
|
||||
*/
|
||||
show() {
|
||||
if (this.loaded) {
|
||||
getNativeModule(this._admob).interstitialShowAd(this.adUnit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to an Ad event
|
||||
* @param eventType
|
||||
* @param listenerCb
|
||||
* @returns {null}
|
||||
*/
|
||||
on(eventType, listenerCb) {
|
||||
if (!statics.EventTypes[eventType]) {
|
||||
console.warn(
|
||||
`Invalid event type provided, must be one of: ${Object.keys(
|
||||
statics.EventTypes
|
||||
).join(', ')}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const sub = SharedEventEmitter.addListener(
|
||||
`interstitial:${this.adUnit}:${eventType}`,
|
||||
listenerCb
|
||||
);
|
||||
subscriptions.push(sub);
|
||||
return sub;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import React from 'react';
|
||||
import AdMobComponent from './AdMobComponent';
|
||||
|
||||
function NativeExpress({ ...props }) {
|
||||
return <AdMobComponent {...props} class="RNFirebaseAdMobNativeExpress" />;
|
||||
}
|
||||
|
||||
NativeExpress.propTypes = AdMobComponent.propTypes;
|
||||
|
||||
NativeExpress.defaultProps = {
|
||||
size: 'SMART_BANNER',
|
||||
};
|
||||
|
||||
export default NativeExpress;
|
|
@ -1,118 +0,0 @@
|
|||
import { statics } from './';
|
||||
import AdRequest from './AdRequest';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { nativeToJSError } from '../../utils';
|
||||
import type AdMob from './';
|
||||
|
||||
let subscriptions = [];
|
||||
|
||||
export default class RewardedVideo {
|
||||
_admob: AdMob;
|
||||
|
||||
constructor(admob: AdMob, adUnit: string) {
|
||||
for (let i = 0, len = subscriptions.length; i < len; i++) {
|
||||
subscriptions[i].remove();
|
||||
}
|
||||
subscriptions = [];
|
||||
|
||||
this._admob = admob;
|
||||
this.adUnit = adUnit;
|
||||
this.loaded = false;
|
||||
SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`);
|
||||
SharedEventEmitter.addListener(
|
||||
`rewarded_video_${adUnit}`,
|
||||
this._onRewardedVideoEvent
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a JS emit event
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onRewardedVideoEvent = event => {
|
||||
const eventType = `rewarded_video:${this.adUnit}:${event.type}`;
|
||||
|
||||
let emitData = Object.assign({}, event);
|
||||
|
||||
switch (event.type) {
|
||||
case 'onAdLoaded':
|
||||
this.loaded = true;
|
||||
break;
|
||||
case 'onAdFailedToLoad':
|
||||
emitData = nativeToJSError(event.payload.code, event.payload.message);
|
||||
emitData.type = event.type;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
SharedEventEmitter.emit(eventType, emitData);
|
||||
SharedEventEmitter.emit(`rewarded_video:${this.adUnit}:*`, emitData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an ad with an instance of AdRequest
|
||||
* @param request
|
||||
* @returns {*}
|
||||
*/
|
||||
loadAd(request?: AdRequest) {
|
||||
let adRequest = request;
|
||||
|
||||
if (!adRequest || !Object.keys(adRequest)) {
|
||||
adRequest = new AdRequest().addTestDevice().build();
|
||||
}
|
||||
|
||||
return getNativeModule(this._admob).rewardedVideoLoadAd(
|
||||
this.adUnit,
|
||||
adRequest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a local instance of isLoaded
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the advert - will only show if loaded
|
||||
* @returns {*}
|
||||
*/
|
||||
show() {
|
||||
if (this.loaded) {
|
||||
getNativeModule(this._admob).rewardedVideoShowAd(this.adUnit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to an Ad event
|
||||
* @param eventType
|
||||
* @param listenerCb
|
||||
* @returns {null}
|
||||
*/
|
||||
on(eventType, listenerCb) {
|
||||
const types = {
|
||||
...statics.EventTypes,
|
||||
...statics.RewardedVideoEventTypes,
|
||||
};
|
||||
|
||||
if (!types[eventType]) {
|
||||
console.warn(
|
||||
`Invalid event type provided, must be one of: ${Object.keys(types).join(
|
||||
', '
|
||||
)}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const sub = SharedEventEmitter.addListener(
|
||||
`rewarded_video:${this.adUnit}:${eventType}`,
|
||||
listenerCb
|
||||
);
|
||||
subscriptions.push(sub);
|
||||
return sub;
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
export default class VideoOptions {
|
||||
constructor() {
|
||||
this._props = {
|
||||
startMuted: true,
|
||||
};
|
||||
}
|
||||
|
||||
build() {
|
||||
return this._props;
|
||||
}
|
||||
|
||||
setStartMuted(muted: boolean = true) {
|
||||
this._props.startMuted = muted;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AdMob representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
|
||||
import Interstitial from './Interstitial';
|
||||
import RewardedVideo from './RewardedVideo';
|
||||
import AdRequest from './AdRequest';
|
||||
import VideoOptions from './VideoOptions';
|
||||
import Banner from './Banner';
|
||||
import NativeExpress from './NativeExpress';
|
||||
|
||||
import EventTypes, {
|
||||
NativeExpressEventTypes,
|
||||
RewardedVideoEventTypes,
|
||||
} from './EventTypes';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
type NativeEvent = {
|
||||
adUnit: string,
|
||||
payload: Object,
|
||||
type: string,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = ['interstitial_event', 'rewarded_video_event'];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseAdMob';
|
||||
export const NAMESPACE = 'admob';
|
||||
|
||||
export default class AdMob extends ModuleBase {
|
||||
_appId: ?string;
|
||||
_initialized: boolean;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
this._initialized = false;
|
||||
this._appId = null;
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
'interstitial_event',
|
||||
this._onInterstitialEvent.bind(this)
|
||||
);
|
||||
SharedEventEmitter.addListener(
|
||||
'rewarded_video_event',
|
||||
this._onRewardedVideoEvent.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
_onInterstitialEvent(event: NativeEvent): void {
|
||||
const { adUnit } = event;
|
||||
const jsEventType = `interstitial_${adUnit}`;
|
||||
|
||||
if (SharedEventEmitter.listeners(jsEventType).length === 0) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
SharedEventEmitter.emit(jsEventType, event);
|
||||
}
|
||||
|
||||
_onRewardedVideoEvent(event: NativeEvent): void {
|
||||
const { adUnit } = event;
|
||||
const jsEventType = `rewarded_video_${adUnit}`;
|
||||
|
||||
if (SharedEventEmitter.listeners(jsEventType).length === 0) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
SharedEventEmitter.emit(jsEventType, event);
|
||||
}
|
||||
|
||||
initialize(appId: string): void {
|
||||
if (this._initialized) {
|
||||
getLogger(this).warn('AdMob has already been initialized!');
|
||||
} else {
|
||||
this._initialized = true;
|
||||
this._appId = appId;
|
||||
getNativeModule(this).initialize(appId);
|
||||
}
|
||||
}
|
||||
|
||||
openDebugMenu(): void {
|
||||
if (!this._initialized) {
|
||||
getLogger(this).warn(
|
||||
'AdMob needs to be initialized before opening the dev menu!'
|
||||
);
|
||||
} else {
|
||||
getLogger(this).info('Opening debug menu');
|
||||
getNativeModule(this).openDebugMenu(this._appId);
|
||||
}
|
||||
}
|
||||
|
||||
interstitial(adUnit: string): Interstitial {
|
||||
return new Interstitial(this, adUnit);
|
||||
}
|
||||
|
||||
rewarded(adUnit: string): RewardedVideo {
|
||||
return new RewardedVideo(this, adUnit);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Banner,
|
||||
NativeExpress,
|
||||
AdRequest,
|
||||
VideoOptions,
|
||||
EventTypes,
|
||||
RewardedVideoEventTypes,
|
||||
NativeExpressEventTypes,
|
||||
};
|
|
@ -1,167 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Analytics representation wrapper
|
||||
*/
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { isString, isObject } from '../../utils';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
const AlphaNumericUnderscore = /^[a-zA-Z0-9_]+$/;
|
||||
|
||||
const ReservedEventNames = [
|
||||
'app_clear_data',
|
||||
'app_uninstall',
|
||||
'app_update',
|
||||
'error',
|
||||
'first_open',
|
||||
'in_app_purchase',
|
||||
'notification_dismiss',
|
||||
'notification_foreground',
|
||||
'notification_open',
|
||||
'notification_receive',
|
||||
'os_update',
|
||||
'session_start',
|
||||
'user_engagement',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseAnalytics';
|
||||
export const NAMESPACE = 'analytics';
|
||||
|
||||
export default class Analytics extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an app event.
|
||||
* @param {string} name
|
||||
* @param params
|
||||
* @return {Promise}
|
||||
*/
|
||||
logEvent(name: string, params: Object = {}): void {
|
||||
if (!isString(name)) {
|
||||
throw new Error(
|
||||
`analytics.logEvent(): First argument 'name' is required and must be a string value.`
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof params !== 'undefined' && !isObject(params)) {
|
||||
throw new Error(
|
||||
`analytics.logEvent(): Second optional argument 'params' must be an object if provided.`
|
||||
);
|
||||
}
|
||||
|
||||
// check name is not a reserved event name
|
||||
if (ReservedEventNames.includes(name)) {
|
||||
throw new Error(
|
||||
`analytics.logEvent(): event name '${name}' is a reserved event name and can not be used.`
|
||||
);
|
||||
}
|
||||
|
||||
// name format validation
|
||||
if (!AlphaNumericUnderscore.test(name)) {
|
||||
throw new Error(
|
||||
`analytics.logEvent(): Event name '${name}' is invalid. Names should contain 1 to 32 alphanumeric characters or underscores.`
|
||||
);
|
||||
}
|
||||
|
||||
// maximum number of allowed params check
|
||||
if (params && Object.keys(params).length > 25)
|
||||
throw new Error(
|
||||
'analytics.logEvent(): Maximum number of parameters exceeded (25).'
|
||||
);
|
||||
|
||||
// Parameter names can be up to 24 characters long and must start with an alphabetic character
|
||||
// and contain only alphanumeric characters and underscores. Only String, long and double param
|
||||
// types are supported. String parameter values can be up to 36 characters long. The "firebase_"
|
||||
// prefix is reserved and should not be used for parameter names.
|
||||
|
||||
getNativeModule(this).logEvent(name, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether analytics collection is enabled for this app on this device.
|
||||
* @param enabled
|
||||
*/
|
||||
setAnalyticsCollectionEnabled(enabled: boolean): void {
|
||||
getNativeModule(this).setAnalyticsCollectionEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current screen name, which specifies the current visual context in your app.
|
||||
* @param screenName
|
||||
* @param screenClassOverride
|
||||
*/
|
||||
setCurrentScreen(screenName: string, screenClassOverride: string): void {
|
||||
getNativeModule(this).setCurrentScreen(screenName, screenClassOverride);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum engagement time required before starting a session. The default value is 10000 (10 seconds).
|
||||
* @param milliseconds
|
||||
*/
|
||||
setMinimumSessionDuration(milliseconds: number = 10000): void {
|
||||
getNativeModule(this).setMinimumSessionDuration(milliseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration of inactivity that terminates the current session. The default value is 1800000 (30 minutes).
|
||||
* @param milliseconds
|
||||
*/
|
||||
setSessionTimeoutDuration(milliseconds: number = 1800000): void {
|
||||
getNativeModule(this).setSessionTimeoutDuration(milliseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user ID property.
|
||||
* @param id
|
||||
*/
|
||||
setUserId(id: string | null): void {
|
||||
if (id !== null && !isString(id)) {
|
||||
throw new Error(
|
||||
'analytics.setUserId(): The supplied userId must be a string value or null.'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).setUserId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user property to a given value.
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
setUserProperty(name: string, value: string | null): void {
|
||||
if (value !== null && !isString(value)) {
|
||||
throw new Error(
|
||||
'analytics.setUserProperty(): The supplied property must be a string value or null.'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).setUserProperty(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple user properties to the supplied values.
|
||||
* @RNFirebaseSpecific
|
||||
* @param object
|
||||
*/
|
||||
setUserProperties(object: Object): void {
|
||||
Object.keys(object).forEach(property => {
|
||||
const value = object[property];
|
||||
if (value !== null && !isString(value)) {
|
||||
throw new Error(
|
||||
`analytics.setUserProperties(): The property with name '${property}' must be a string value or null.`
|
||||
);
|
||||
}
|
||||
getNativeModule(this).setUserProperty(property, object[property]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* ConfirmationResult representation wrapper
|
||||
*/
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import type Auth from './';
|
||||
import type User from './User';
|
||||
|
||||
export default class ConfirmationResult {
|
||||
_auth: Auth;
|
||||
_verificationId: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auth
|
||||
* @param verificationId The phone number authentication operation's verification ID.
|
||||
*/
|
||||
constructor(auth: Auth, verificationId: string) {
|
||||
this._auth = auth;
|
||||
this._verificationId = verificationId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param verificationCode
|
||||
* @return {*}
|
||||
*/
|
||||
confirm(verificationCode: string): Promise<User> {
|
||||
return getNativeModule(this._auth)
|
||||
._confirmVerificationCode(verificationCode)
|
||||
.then(user => this._auth._setUser(user));
|
||||
}
|
||||
|
||||
get verificationId(): string | null {
|
||||
return this._verificationId;
|
||||
}
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
// @flow
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import {
|
||||
generatePushID,
|
||||
isFunction,
|
||||
isAndroid,
|
||||
isIOS,
|
||||
isString,
|
||||
nativeToJSError,
|
||||
} from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Auth from './';
|
||||
|
||||
type PhoneAuthSnapshot = {
|
||||
state: 'sent' | 'timeout' | 'verified' | 'error',
|
||||
verificationId: string,
|
||||
code: string | null,
|
||||
error: Error | null,
|
||||
};
|
||||
|
||||
type PhoneAuthError = {
|
||||
code: string | null,
|
||||
verificationId: string,
|
||||
message: string | null,
|
||||
stack: string | null,
|
||||
};
|
||||
|
||||
export default class PhoneAuthListener {
|
||||
_auth: Auth;
|
||||
_timeout: number;
|
||||
_publicEvents: Object;
|
||||
_internalEvents: Object;
|
||||
_reject: Function | null;
|
||||
_resolve: Function | null;
|
||||
_credential: Object | null;
|
||||
_promise: Promise<*> | null;
|
||||
_phoneAuthRequestKey: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auth
|
||||
* @param phoneNumber
|
||||
* @param timeout
|
||||
*/
|
||||
constructor(auth: Auth, phoneNumber: string, timeout?: number) {
|
||||
this._auth = auth;
|
||||
this._reject = null;
|
||||
this._resolve = null;
|
||||
this._promise = null;
|
||||
this._credential = null;
|
||||
|
||||
this._timeout = timeout || 20; // 20 secs
|
||||
this._phoneAuthRequestKey = generatePushID();
|
||||
|
||||
// internal events
|
||||
this._internalEvents = {
|
||||
codeSent: `phone:auth:${this._phoneAuthRequestKey}:onCodeSent`,
|
||||
verificationFailed: `phone:auth:${
|
||||
this._phoneAuthRequestKey
|
||||
}:onVerificationFailed`,
|
||||
verificationComplete: `phone:auth:${
|
||||
this._phoneAuthRequestKey
|
||||
}:onVerificationComplete`,
|
||||
codeAutoRetrievalTimeout: `phone:auth:${
|
||||
this._phoneAuthRequestKey
|
||||
}:onCodeAutoRetrievalTimeout`,
|
||||
};
|
||||
|
||||
// user observer events
|
||||
this._publicEvents = {
|
||||
// error cb
|
||||
error: `phone:auth:${this._phoneAuthRequestKey}:error`,
|
||||
// observer
|
||||
event: `phone:auth:${this._phoneAuthRequestKey}:event`,
|
||||
// success cb
|
||||
success: `phone:auth:${this._phoneAuthRequestKey}:success`,
|
||||
};
|
||||
|
||||
// setup internal event listeners
|
||||
this._subscribeToEvents();
|
||||
|
||||
// start verification flow natively
|
||||
if (isAndroid) {
|
||||
getNativeModule(this._auth).verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey,
|
||||
this._timeout
|
||||
);
|
||||
}
|
||||
|
||||
if (isIOS) {
|
||||
getNativeModule(this._auth).verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to all EE events on this._internalEvents
|
||||
* @private
|
||||
*/
|
||||
_subscribeToEvents() {
|
||||
const events = Object.keys(this._internalEvents);
|
||||
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
const type = events[i];
|
||||
SharedEventEmitter.once(
|
||||
this._internalEvents[type],
|
||||
// $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
|
||||
this[`_${type}Handler`].bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe a users listener cb to the snapshot events.
|
||||
* @param observer
|
||||
* @private
|
||||
*/
|
||||
_addUserObserver(observer) {
|
||||
SharedEventEmitter.addListener(this._publicEvents.event, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a snapshot event to users event observer.
|
||||
* @param snapshot PhoneAuthSnapshot
|
||||
* @private
|
||||
*/
|
||||
_emitToObservers(snapshot: PhoneAuthSnapshot) {
|
||||
SharedEventEmitter.emit(this._publicEvents.event, snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a error snapshot event to any subscribed errorCb's
|
||||
* @param snapshot
|
||||
* @private
|
||||
*/
|
||||
_emitToErrorCb(snapshot) {
|
||||
const { error } = snapshot;
|
||||
if (this._reject) this._reject(error);
|
||||
SharedEventEmitter.emit(this._publicEvents.error, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a success snapshot event to any subscribed completeCb's
|
||||
* @param snapshot
|
||||
* @private
|
||||
*/
|
||||
_emitToSuccessCb(snapshot) {
|
||||
if (this._resolve) this._resolve(snapshot);
|
||||
SharedEventEmitter.emit(this._publicEvents.success, snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners for this phone auth instance
|
||||
* @private
|
||||
*/
|
||||
_removeAllListeners() {
|
||||
setTimeout(() => {
|
||||
// move to next event loop - not sure if needed
|
||||
// internal listeners
|
||||
Object.values(this._internalEvents).forEach(event => {
|
||||
SharedEventEmitter.removeAllListeners(event);
|
||||
});
|
||||
|
||||
// user observer listeners
|
||||
Object.values(this._publicEvents).forEach(publicEvent => {
|
||||
SharedEventEmitter.removeAllListeners(publicEvent);
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new internal deferred promise, if not already created
|
||||
* @private
|
||||
*/
|
||||
_promiseDeferred() {
|
||||
if (!this._promise) {
|
||||
this._promise = new Promise((resolve, reject) => {
|
||||
this._resolve = result => {
|
||||
this._resolve = null;
|
||||
return resolve(result);
|
||||
};
|
||||
|
||||
this._reject = possibleError => {
|
||||
this._reject = null;
|
||||
return reject(possibleError);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------
|
||||
--- INTERNAL EVENT HANDLERS
|
||||
---------------------------- */
|
||||
|
||||
/**
|
||||
* Internal code sent event handler
|
||||
* @private
|
||||
* @param credential
|
||||
*/
|
||||
_codeSentHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: credential.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'sent',
|
||||
};
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
|
||||
if (isIOS) {
|
||||
this._emitToSuccessCb(snapshot);
|
||||
}
|
||||
|
||||
if (isAndroid) {
|
||||
// android can auto retrieve so we don't emit to successCb immediately,
|
||||
// if auto retrieve times out then that will emit to successCb
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal code auto retrieve timeout event handler
|
||||
* @private
|
||||
* @param credential
|
||||
*/
|
||||
_codeAutoRetrievalTimeoutHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: credential.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'timeout',
|
||||
};
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToSuccessCb(snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal verification complete event handler
|
||||
* @param credential
|
||||
* @private
|
||||
*/
|
||||
_verificationCompleteHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: credential.verificationId,
|
||||
code: credential.code || null,
|
||||
error: null,
|
||||
state: 'verified',
|
||||
};
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToSuccessCb(snapshot);
|
||||
this._removeAllListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal verification failed event handler
|
||||
* @param state
|
||||
* @private
|
||||
*/
|
||||
_verificationFailedHandler(state) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: state.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'error',
|
||||
};
|
||||
|
||||
const { code, message, nativeErrorMessage } = state.error;
|
||||
snapshot.error = nativeToJSError(code, message, { nativeErrorMessage });
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToErrorCb(snapshot);
|
||||
this._removeAllListeners();
|
||||
}
|
||||
|
||||
/* -------------
|
||||
-- PUBLIC API
|
||||
--------------*/
|
||||
|
||||
on(
|
||||
event: string,
|
||||
observer: () => PhoneAuthSnapshot,
|
||||
errorCb?: () => PhoneAuthError,
|
||||
successCb?: () => PhoneAuthSnapshot
|
||||
): this {
|
||||
if (!isString(event)) {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('event', 'string', 'on')
|
||||
);
|
||||
}
|
||||
|
||||
if (event !== 'state_changed') {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_ARG_INVALID_VALUE(
|
||||
'event',
|
||||
'state_changed',
|
||||
event
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!isFunction(observer)) {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('observer', 'function', 'on')
|
||||
);
|
||||
}
|
||||
|
||||
this._addUserObserver(observer);
|
||||
|
||||
if (isFunction(errorCb)) {
|
||||
SharedEventEmitter.once(this._publicEvents.error, errorCb);
|
||||
}
|
||||
|
||||
if (isFunction(successCb)) {
|
||||
SharedEventEmitter.once(this._publicEvents.success, successCb);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise .then proxy
|
||||
* @param fn
|
||||
*/
|
||||
then(fn: () => PhoneAuthSnapshot) {
|
||||
this._promiseDeferred();
|
||||
// $FlowFixMe: Unsure how to annotate `bind` here
|
||||
if (this._promise) return this._promise.then.bind(this._promise)(fn);
|
||||
return undefined; // will never get here - just to keep flow happy
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise .catch proxy
|
||||
* @param fn
|
||||
*/
|
||||
catch(fn: () => Error) {
|
||||
this._promiseDeferred();
|
||||
// $FlowFixMe: Unsure how to annotate `bind` here
|
||||
if (this._promise) return this._promise.catch.bind(this._promise)(fn);
|
||||
return undefined; // will never get here - just to keep flow happy
|
||||
}
|
||||
}
|
|
@ -1,334 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* User representation wrapper
|
||||
*/
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Auth from './';
|
||||
import type {
|
||||
ActionCodeSettings,
|
||||
AuthCredential,
|
||||
NativeUser,
|
||||
UserCredential,
|
||||
UserInfo,
|
||||
UserMetadata,
|
||||
} from './types';
|
||||
|
||||
type UpdateProfile = {
|
||||
displayName?: string,
|
||||
photoURL?: string,
|
||||
};
|
||||
|
||||
export default class User {
|
||||
_auth: Auth;
|
||||
_user: NativeUser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auth Instance of Authentication class
|
||||
* @param user user result object from native
|
||||
*/
|
||||
constructor(auth: Auth, user: NativeUser) {
|
||||
this._auth = auth;
|
||||
this._user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* PROPERTIES
|
||||
*/
|
||||
|
||||
get displayName(): ?string {
|
||||
return this._user.displayName || null;
|
||||
}
|
||||
|
||||
get email(): ?string {
|
||||
return this._user.email || null;
|
||||
}
|
||||
|
||||
get emailVerified(): boolean {
|
||||
return this._user.emailVerified || false;
|
||||
}
|
||||
|
||||
get isAnonymous(): boolean {
|
||||
return this._user.isAnonymous || false;
|
||||
}
|
||||
|
||||
get metadata(): UserMetadata {
|
||||
return this._user.metadata;
|
||||
}
|
||||
|
||||
get phoneNumber(): ?string {
|
||||
return this._user.phoneNumber || null;
|
||||
}
|
||||
|
||||
get photoURL(): ?string {
|
||||
return this._user.photoURL || null;
|
||||
}
|
||||
|
||||
get providerData(): Array<UserInfo> {
|
||||
return this._user.providerData;
|
||||
}
|
||||
|
||||
get providerId(): string {
|
||||
return this._user.providerId;
|
||||
}
|
||||
|
||||
get uid(): string {
|
||||
return this._user.uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* METHODS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delete the current user
|
||||
* @return {Promise}
|
||||
*/
|
||||
delete(): Promise<void> {
|
||||
return getNativeModule(this._auth)
|
||||
.delete()
|
||||
.then(() => {
|
||||
this._auth._setUser();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* get the token of current user
|
||||
* @return {Promise}
|
||||
*/
|
||||
getIdToken(forceRefresh: boolean = false): Promise<string> {
|
||||
return getNativeModule(this._auth).getToken(forceRefresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the token of current user
|
||||
* @deprecated Deprecated getToken in favor of getIdToken.
|
||||
* @return {Promise}
|
||||
*/
|
||||
getToken(forceRefresh: boolean = false): Promise<Object> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.'
|
||||
);
|
||||
return getNativeModule(this._auth).getToken(forceRefresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated linkWithCredential in favor of linkAndRetrieveDataWithCredential.
|
||||
* @param credential
|
||||
*/
|
||||
linkWithCredential(credential: AuthCredential): Promise<User> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.linkWithCredential in favor of firebase.User.prototype.linkAndRetrieveDataWithCredential.'
|
||||
);
|
||||
return getNativeModule(this._auth)
|
||||
.linkWithCredential(
|
||||
credential.providerId,
|
||||
credential.token,
|
||||
credential.secret
|
||||
)
|
||||
.then(user => this._auth._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param credential
|
||||
*/
|
||||
linkAndRetrieveDataWithCredential(
|
||||
credential: AuthCredential
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this._auth)
|
||||
.linkAndRetrieveDataWithCredential(
|
||||
credential.providerId,
|
||||
credential.token,
|
||||
credential.secret
|
||||
)
|
||||
.then(userCredential => this._auth._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-authenticate a user with a third-party authentication provider
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
reauthenticateWithCredential(credential: AuthCredential): Promise<void> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.reauthenticateWithCredential in favor of firebase.User.prototype.reauthenticateAndRetrieveDataWithCredential.'
|
||||
);
|
||||
return getNativeModule(this._auth)
|
||||
.reauthenticateWithCredential(
|
||||
credential.providerId,
|
||||
credential.token,
|
||||
credential.secret
|
||||
)
|
||||
.then(user => {
|
||||
this._auth._setUser(user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-authenticate a user with a third-party authentication provider
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
reauthenticateAndRetrieveDataWithCredential(
|
||||
credential: AuthCredential
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this._auth)
|
||||
.reauthenticateAndRetrieveDataWithCredential(
|
||||
credential.providerId,
|
||||
credential.token,
|
||||
credential.secret
|
||||
)
|
||||
.then(userCredential => this._auth._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the current user
|
||||
* @return {Promise}
|
||||
*/
|
||||
reload(): Promise<void> {
|
||||
return getNativeModule(this._auth)
|
||||
.reload()
|
||||
.then(user => {
|
||||
this._auth._setUser(user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send verification email to current user.
|
||||
*/
|
||||
sendEmailVerification(
|
||||
actionCodeSettings?: ActionCodeSettings
|
||||
): Promise<void> {
|
||||
return getNativeModule(this._auth)
|
||||
.sendEmailVerification(actionCodeSettings)
|
||||
.then(user => {
|
||||
this._auth._setUser(user);
|
||||
});
|
||||
}
|
||||
|
||||
toJSON(): Object {
|
||||
return Object.assign({}, this._user);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerId
|
||||
* @return {Promise.<TResult>|*}
|
||||
*/
|
||||
unlink(providerId: string): Promise<User> {
|
||||
return getNativeModule(this._auth)
|
||||
.unlink(providerId)
|
||||
.then(user => this._auth._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current user's email
|
||||
*
|
||||
* @param {string} email The user's _new_ email
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
updateEmail(email: string): Promise<void> {
|
||||
return getNativeModule(this._auth)
|
||||
.updateEmail(email)
|
||||
.then(user => {
|
||||
this._auth._setUser(user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current user's password
|
||||
* @param {string} password the new password
|
||||
* @return {Promise}
|
||||
*/
|
||||
updatePassword(password: string): Promise<void> {
|
||||
return getNativeModule(this._auth)
|
||||
.updatePassword(password)
|
||||
.then(user => {
|
||||
this._auth._setUser(user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current user's profile
|
||||
* @param {Object} updates An object containing the keys listed [here](https://firebase.google.com/docs/auth/ios/manage-users#update_a_users_profile)
|
||||
* @return {Promise}
|
||||
*/
|
||||
updateProfile(updates: UpdateProfile = {}): Promise<void> {
|
||||
return getNativeModule(this._auth)
|
||||
.updateProfile(updates)
|
||||
.then(user => {
|
||||
this._auth._setUser(user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* KNOWN UNSUPPORTED METHODS
|
||||
*/
|
||||
|
||||
linkWithPhoneNumber() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
|
||||
'User',
|
||||
'linkWithPhoneNumber'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
linkWithPopup() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup')
|
||||
);
|
||||
}
|
||||
|
||||
linkWithRedirect() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
|
||||
'User',
|
||||
'linkWithRedirect'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
reauthenticateWithPhoneNumber() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
|
||||
'User',
|
||||
'reauthenticateWithPhoneNumber'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
reauthenticateWithPopup() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
|
||||
'User',
|
||||
'reauthenticateWithPopup'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
reauthenticateWithRedirect() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
|
||||
'User',
|
||||
'reauthenticateWithRedirect'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
updatePhoneNumber() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
|
||||
'User',
|
||||
'updatePhoneNumber'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get refreshToken(): string {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('User', 'refreshToken')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,526 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Auth representation wrapper
|
||||
*/
|
||||
import User from './User';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import ConfirmationResult from './phone/ConfirmationResult';
|
||||
import PhoneAuthListener from './phone/PhoneAuthListener';
|
||||
|
||||
// providers
|
||||
import EmailAuthProvider from './providers/EmailAuthProvider';
|
||||
import PhoneAuthProvider from './providers/PhoneAuthProvider';
|
||||
import GoogleAuthProvider from './providers/GoogleAuthProvider';
|
||||
import GithubAuthProvider from './providers/GithubAuthProvider';
|
||||
import OAuthProvider from './providers/OAuthProvider';
|
||||
import TwitterAuthProvider from './providers/TwitterAuthProvider';
|
||||
import FacebookAuthProvider from './providers/FacebookAuthProvider';
|
||||
|
||||
import type {
|
||||
ActionCodeInfo,
|
||||
ActionCodeSettings,
|
||||
AuthCredential,
|
||||
NativeUser,
|
||||
NativeUserCredential,
|
||||
UserCredential,
|
||||
} from './types';
|
||||
import type App from '../core/app';
|
||||
|
||||
type AuthState = {
|
||||
user?: NativeUser,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'auth_state_changed',
|
||||
'auth_id_token_changed',
|
||||
'phone_auth_state_changed',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseAuth';
|
||||
export const NAMESPACE = 'auth';
|
||||
|
||||
export default class Auth extends ModuleBase {
|
||||
_authResult: boolean;
|
||||
_languageCode: string;
|
||||
_user: User | null;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._user = null;
|
||||
this._authResult = false;
|
||||
this._languageCode =
|
||||
getNativeModule(this).APP_LANGUAGE[app._name] ||
|
||||
getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onAuthStateChanged
|
||||
getAppEventName(this, 'auth_state_changed'),
|
||||
(state: AuthState) => {
|
||||
this._setUser(state.user);
|
||||
SharedEventEmitter.emit(
|
||||
getAppEventName(this, 'onAuthStateChanged'),
|
||||
this._user
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public events based on event.type
|
||||
getAppEventName(this, 'phone_auth_state_changed'),
|
||||
(event: Object) => {
|
||||
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
|
||||
SharedEventEmitter.emit(eventKey, event.state);
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onIdTokenChanged
|
||||
getAppEventName(this, 'auth_id_token_changed'),
|
||||
(auth: AuthState) => {
|
||||
this._setUser(auth.user);
|
||||
SharedEventEmitter.emit(
|
||||
getAppEventName(this, 'onIdTokenChanged'),
|
||||
this._user
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
getNativeModule(this).addAuthStateListener();
|
||||
getNativeModule(this).addIdTokenListener();
|
||||
}
|
||||
|
||||
_setUser(user: ?NativeUser): ?User {
|
||||
this._authResult = true;
|
||||
this._user = user ? new User(this, user) : null;
|
||||
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
|
||||
return this._user;
|
||||
}
|
||||
|
||||
_setUserCredential(userCredential: NativeUserCredential): UserCredential {
|
||||
const user = new User(this, userCredential.user);
|
||||
this._authResult = true;
|
||||
this._user = user;
|
||||
SharedEventEmitter.emit(getAppEventName(this, 'onUserChanged'), this._user);
|
||||
return {
|
||||
additionalUserInfo: userCredential.additionalUserInfo,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* WEB API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Listen for auth changes.
|
||||
* @param listener
|
||||
*/
|
||||
onAuthStateChanged(listener: Function) {
|
||||
getLogger(this).info('Creating onAuthStateChanged listener');
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this, 'onAuthStateChanged'),
|
||||
listener
|
||||
);
|
||||
if (this._authResult) listener(this._user || null);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onAuthStateChanged listener');
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this, 'onAuthStateChanged'),
|
||||
listener
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for id token changes.
|
||||
* @param listener
|
||||
*/
|
||||
onIdTokenChanged(listener: Function) {
|
||||
getLogger(this).info('Creating onIdTokenChanged listener');
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this, 'onIdTokenChanged'),
|
||||
listener
|
||||
);
|
||||
if (this._authResult) listener(this._user || null);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onIdTokenChanged listener');
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this, 'onIdTokenChanged'),
|
||||
listener
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for user changes.
|
||||
* @param listener
|
||||
*/
|
||||
onUserChanged(listener: Function) {
|
||||
getLogger(this).info('Creating onUserChanged listener');
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this, 'onUserChanged'),
|
||||
listener
|
||||
);
|
||||
if (this._authResult) listener(this._user || null);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onUserChanged listener');
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this, 'onUserChanged'),
|
||||
listener
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the current user out
|
||||
* @return {Promise}
|
||||
*/
|
||||
signOut(): Promise<void> {
|
||||
return getNativeModule(this)
|
||||
.signOut()
|
||||
.then(() => {
|
||||
this._setUser();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a user in anonymously
|
||||
* @deprecated Deprecated signInAnonymously in favor of signInAnonymouslyAndRetrieveData.
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInAnonymously(): Promise<User> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.signInAnonymously in favor of firebase.User.prototype.signInAnonymouslyAndRetrieveData.'
|
||||
);
|
||||
return getNativeModule(this)
|
||||
.signInAnonymously()
|
||||
.then(user => this._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a user in anonymously
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInAnonymouslyAndRetrieveData(): Promise<UserCredential> {
|
||||
return getNativeModule(this)
|
||||
.signInAnonymouslyAndRetrieveData()
|
||||
.then(userCredential => this._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user with the email/password functionality
|
||||
* @deprecated Deprecated createUserWithEmailAndPassword in favor of createUserAndRetrieveDataWithEmailAndPassword.
|
||||
* @param {string} email The user's email
|
||||
* @param {string} password The user's password
|
||||
* @return {Promise} A promise indicating the completion
|
||||
*/
|
||||
createUserWithEmailAndPassword(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<User> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.createUserWithEmailAndPassword in favor of firebase.User.prototype.createUserAndRetrieveDataWithEmailAndPassword.'
|
||||
);
|
||||
return getNativeModule(this)
|
||||
.createUserWithEmailAndPassword(email, password)
|
||||
.then(user => this._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user with the email/password functionality
|
||||
* @param {string} email The user's email
|
||||
* @param {string} password The user's password
|
||||
* @return {Promise} A promise indicating the completion
|
||||
*/
|
||||
createUserAndRetrieveDataWithEmailAndPassword(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this)
|
||||
.createUserAndRetrieveDataWithEmailAndPassword(email, password)
|
||||
.then(userCredential => this._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a user in with email/password
|
||||
* @deprecated Deprecated signInWithEmailAndPassword in favor of signInAndRetrieveDataWithEmailAndPassword
|
||||
* @param {string} email The user's email
|
||||
* @param {string} password The user's password
|
||||
* @return {Promise} A promise that is resolved upon completion
|
||||
*/
|
||||
signInWithEmailAndPassword(email: string, password: string): Promise<User> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.signInWithEmailAndPassword in favor of firebase.User.prototype.signInAndRetrieveDataWithEmailAndPassword.'
|
||||
);
|
||||
return getNativeModule(this)
|
||||
.signInWithEmailAndPassword(email, password)
|
||||
.then(user => this._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a user in with email/password
|
||||
* @param {string} email The user's email
|
||||
* @param {string} password The user's password
|
||||
* @return {Promise} A promise that is resolved upon completion
|
||||
*/
|
||||
signInAndRetrieveDataWithEmailAndPassword(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this)
|
||||
.signInAndRetrieveDataWithEmailAndPassword(email, password)
|
||||
.then(userCredential => this._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the user in with a custom auth token
|
||||
* @deprecated Deprecated signInWithCustomToken in favor of signInAndRetrieveDataWithCustomToken
|
||||
* @param {string} customToken A self-signed custom auth token.
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInWithCustomToken(customToken: string): Promise<User> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.signInWithCustomToken in favor of firebase.User.prototype.signInAndRetrieveDataWithCustomToken.'
|
||||
);
|
||||
return getNativeModule(this)
|
||||
.signInWithCustomToken(customToken)
|
||||
.then(user => this._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the user in with a custom auth token
|
||||
* @param {string} customToken A self-signed custom auth token.
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInAndRetrieveDataWithCustomToken(
|
||||
customToken: string
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this)
|
||||
.signInAndRetrieveDataWithCustomToken(customToken)
|
||||
.then(userCredential => this._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the user in with a third-party authentication provider
|
||||
* @deprecated Deprecated signInWithCredential in favor of signInAndRetrieveDataWithCredential.
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInWithCredential(credential: AuthCredential): Promise<User> {
|
||||
console.warn(
|
||||
'Deprecated firebase.User.prototype.signInWithCredential in favor of firebase.User.prototype.signInAndRetrieveDataWithCredential.'
|
||||
);
|
||||
return getNativeModule(this)
|
||||
.signInWithCredential(
|
||||
credential.providerId,
|
||||
credential.token,
|
||||
credential.secret
|
||||
)
|
||||
.then(user => this._setUser(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the user in with a third-party authentication provider
|
||||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInAndRetrieveDataWithCredential(
|
||||
credential: AuthCredential
|
||||
): Promise<UserCredential> {
|
||||
return getNativeModule(this)
|
||||
.signInAndRetrieveDataWithCredential(
|
||||
credential.providerId,
|
||||
credential.token,
|
||||
credential.secret
|
||||
)
|
||||
.then(userCredential => this._setUserCredential(userCredential));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously signs in using a phone number.
|
||||
*
|
||||
*/
|
||||
signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
|
||||
return getNativeModule(this)
|
||||
.signInWithPhoneNumber(phoneNumber)
|
||||
.then(result => new ConfirmationResult(this, result.verificationId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PhoneAuthListener to listen to phone verification events,
|
||||
* on the final completion event a PhoneAuthCredential can be generated for
|
||||
* authentication purposes.
|
||||
*
|
||||
* @param phoneNumber
|
||||
* @param autoVerifyTimeout Android Only
|
||||
* @returns {PhoneAuthListener}
|
||||
*/
|
||||
verifyPhoneNumber(
|
||||
phoneNumber: string,
|
||||
autoVerifyTimeout?: number
|
||||
): PhoneAuthListener {
|
||||
return new PhoneAuthListener(this, phoneNumber, autoVerifyTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send reset password instructions via email
|
||||
* @param {string} email The email to send password reset instructions
|
||||
*/
|
||||
sendPasswordResetEmail(
|
||||
email: string,
|
||||
actionCodeSettings?: ActionCodeSettings
|
||||
): Promise<void> {
|
||||
return getNativeModule(this).sendPasswordResetEmail(
|
||||
email,
|
||||
actionCodeSettings
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the password reset process, given a confirmation code and new password.
|
||||
*
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#confirmPasswordReset
|
||||
* @param code
|
||||
* @param newPassword
|
||||
* @return {Promise.<Null>}
|
||||
*/
|
||||
confirmPasswordReset(code: string, newPassword: string): Promise<void> {
|
||||
return getNativeModule(this).confirmPasswordReset(code, newPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a verification code sent to the user by email or other out-of-band mechanism.
|
||||
*
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#applyActionCode
|
||||
* @param code
|
||||
* @return {Promise.<Null>}
|
||||
*/
|
||||
applyActionCode(code: string): Promise<void> {
|
||||
return getNativeModule(this).applyActionCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a verification code sent to the user by email or other out-of-band mechanism.
|
||||
*
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#checkActionCode
|
||||
* @param code
|
||||
* @return {Promise.<any>|Promise<ActionCodeInfo>}
|
||||
*/
|
||||
checkActionCode(code: string): Promise<ActionCodeInfo> {
|
||||
return getNativeModule(this).checkActionCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of authentication providers that can be used to sign in a given user (identified by its main email address).
|
||||
* @return {Promise}
|
||||
*/
|
||||
fetchProvidersForEmail(email: string): Promise<string[]> {
|
||||
return getNativeModule(this).fetchProvidersForEmail(email);
|
||||
}
|
||||
|
||||
verifyPasswordResetCode(code: string): Promise<string> {
|
||||
return getNativeModule(this).verifyPasswordResetCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language for the auth module
|
||||
* @param code
|
||||
* @returns {*}
|
||||
*/
|
||||
set languageCode(code: string) {
|
||||
this._languageCode = code;
|
||||
getNativeModule(this).setLanguageCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently signed in user
|
||||
* @return {Promise}
|
||||
*/
|
||||
get currentUser(): User | null {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
get languageCode(): string {
|
||||
return this._languageCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* KNOWN UNSUPPORTED METHODS
|
||||
*/
|
||||
|
||||
getRedirectResult() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'auth',
|
||||
'getRedirectResult'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
setPersistence() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'auth',
|
||||
'setPersistence'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
signInWithPopup() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'auth',
|
||||
'signInWithPopup'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
signInWithRedirect() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'auth',
|
||||
'signInWithRedirect'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// firebase issue - https://github.com/invertase/react-native-firebase/pull/655#issuecomment-349904680
|
||||
useDeviceLanguage() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'auth',
|
||||
'useDeviceLanguage'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
EmailAuthProvider,
|
||||
PhoneAuthProvider,
|
||||
GoogleAuthProvider,
|
||||
GithubAuthProvider,
|
||||
TwitterAuthProvider,
|
||||
FacebookAuthProvider,
|
||||
OAuthProvider,
|
||||
PhoneAuthState: {
|
||||
CODE_SENT: 'sent',
|
||||
AUTO_VERIFY_TIMEOUT: 'timeout',
|
||||
AUTO_VERIFIED: 'verified',
|
||||
ERROR: 'error',
|
||||
},
|
||||
};
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* ConfirmationResult representation wrapper
|
||||
*/
|
||||
import { getNativeModule } from '../../../utils/native';
|
||||
import type Auth from '../';
|
||||
import type User from '../User';
|
||||
|
||||
export default class ConfirmationResult {
|
||||
_auth: Auth;
|
||||
_verificationId: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auth
|
||||
* @param verificationId The phone number authentication operation's verification ID.
|
||||
*/
|
||||
constructor(auth: Auth, verificationId: string) {
|
||||
this._auth = auth;
|
||||
this._verificationId = verificationId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param verificationCode
|
||||
* @return {*}
|
||||
*/
|
||||
confirm(verificationCode: string): Promise<User> {
|
||||
return getNativeModule(this._auth)
|
||||
._confirmVerificationCode(verificationCode)
|
||||
.then(user => this._auth._setUser(user));
|
||||
}
|
||||
|
||||
get verificationId(): string | null {
|
||||
return this._verificationId;
|
||||
}
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
// @flow
|
||||
import INTERNALS from '../../../utils/internals';
|
||||
import { SharedEventEmitter } from '../../../utils/events';
|
||||
import {
|
||||
generatePushID,
|
||||
isFunction,
|
||||
isAndroid,
|
||||
isIOS,
|
||||
isString,
|
||||
nativeToJSError,
|
||||
} from '../../../utils';
|
||||
import { getNativeModule } from '../../../utils/native';
|
||||
|
||||
import type Auth from '../';
|
||||
|
||||
type PhoneAuthSnapshot = {
|
||||
state: 'sent' | 'timeout' | 'verified' | 'error',
|
||||
verificationId: string,
|
||||
code: string | null,
|
||||
error: Error | null,
|
||||
};
|
||||
|
||||
type PhoneAuthError = {
|
||||
code: string | null,
|
||||
verificationId: string,
|
||||
message: string | null,
|
||||
stack: string | null,
|
||||
};
|
||||
|
||||
export default class PhoneAuthListener {
|
||||
_auth: Auth;
|
||||
_timeout: number;
|
||||
_publicEvents: Object;
|
||||
_internalEvents: Object;
|
||||
_reject: Function | null;
|
||||
_resolve: Function | null;
|
||||
_credential: Object | null;
|
||||
_promise: Promise<*> | null;
|
||||
_phoneAuthRequestKey: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auth
|
||||
* @param phoneNumber
|
||||
* @param timeout
|
||||
*/
|
||||
constructor(auth: Auth, phoneNumber: string, timeout?: number) {
|
||||
this._auth = auth;
|
||||
this._reject = null;
|
||||
this._resolve = null;
|
||||
this._promise = null;
|
||||
this._credential = null;
|
||||
|
||||
this._timeout = timeout || 20; // 20 secs
|
||||
this._phoneAuthRequestKey = generatePushID();
|
||||
|
||||
// internal events
|
||||
this._internalEvents = {
|
||||
codeSent: `phone:auth:${this._phoneAuthRequestKey}:onCodeSent`,
|
||||
verificationFailed: `phone:auth:${
|
||||
this._phoneAuthRequestKey
|
||||
}:onVerificationFailed`,
|
||||
verificationComplete: `phone:auth:${
|
||||
this._phoneAuthRequestKey
|
||||
}:onVerificationComplete`,
|
||||
codeAutoRetrievalTimeout: `phone:auth:${
|
||||
this._phoneAuthRequestKey
|
||||
}:onCodeAutoRetrievalTimeout`,
|
||||
};
|
||||
|
||||
// user observer events
|
||||
this._publicEvents = {
|
||||
// error cb
|
||||
error: `phone:auth:${this._phoneAuthRequestKey}:error`,
|
||||
// observer
|
||||
event: `phone:auth:${this._phoneAuthRequestKey}:event`,
|
||||
// success cb
|
||||
success: `phone:auth:${this._phoneAuthRequestKey}:success`,
|
||||
};
|
||||
|
||||
// setup internal event listeners
|
||||
this._subscribeToEvents();
|
||||
|
||||
// start verification flow natively
|
||||
if (isAndroid) {
|
||||
getNativeModule(this._auth).verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey,
|
||||
this._timeout
|
||||
);
|
||||
}
|
||||
|
||||
if (isIOS) {
|
||||
getNativeModule(this._auth).verifyPhoneNumber(
|
||||
phoneNumber,
|
||||
this._phoneAuthRequestKey
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to all EE events on this._internalEvents
|
||||
* @private
|
||||
*/
|
||||
_subscribeToEvents() {
|
||||
const events = Object.keys(this._internalEvents);
|
||||
|
||||
for (let i = 0, len = events.length; i < len; i++) {
|
||||
const type = events[i];
|
||||
SharedEventEmitter.once(
|
||||
this._internalEvents[type],
|
||||
// $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
|
||||
this[`_${type}Handler`].bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe a users listener cb to the snapshot events.
|
||||
* @param observer
|
||||
* @private
|
||||
*/
|
||||
_addUserObserver(observer) {
|
||||
SharedEventEmitter.addListener(this._publicEvents.event, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a snapshot event to users event observer.
|
||||
* @param snapshot PhoneAuthSnapshot
|
||||
* @private
|
||||
*/
|
||||
_emitToObservers(snapshot: PhoneAuthSnapshot) {
|
||||
SharedEventEmitter.emit(this._publicEvents.event, snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a error snapshot event to any subscribed errorCb's
|
||||
* @param snapshot
|
||||
* @private
|
||||
*/
|
||||
_emitToErrorCb(snapshot) {
|
||||
const { error } = snapshot;
|
||||
if (this._reject) this._reject(error);
|
||||
SharedEventEmitter.emit(this._publicEvents.error, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a success snapshot event to any subscribed completeCb's
|
||||
* @param snapshot
|
||||
* @private
|
||||
*/
|
||||
_emitToSuccessCb(snapshot) {
|
||||
if (this._resolve) this._resolve(snapshot);
|
||||
SharedEventEmitter.emit(this._publicEvents.success, snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners for this phone auth instance
|
||||
* @private
|
||||
*/
|
||||
_removeAllListeners() {
|
||||
setTimeout(() => {
|
||||
// move to next event loop - not sure if needed
|
||||
// internal listeners
|
||||
Object.values(this._internalEvents).forEach(event => {
|
||||
SharedEventEmitter.removeAllListeners(event);
|
||||
});
|
||||
|
||||
// user observer listeners
|
||||
Object.values(this._publicEvents).forEach(publicEvent => {
|
||||
SharedEventEmitter.removeAllListeners(publicEvent);
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new internal deferred promise, if not already created
|
||||
* @private
|
||||
*/
|
||||
_promiseDeferred() {
|
||||
if (!this._promise) {
|
||||
this._promise = new Promise((resolve, reject) => {
|
||||
this._resolve = result => {
|
||||
this._resolve = null;
|
||||
return resolve(result);
|
||||
};
|
||||
|
||||
this._reject = possibleError => {
|
||||
this._reject = null;
|
||||
return reject(possibleError);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------
|
||||
--- INTERNAL EVENT HANDLERS
|
||||
---------------------------- */
|
||||
|
||||
/**
|
||||
* Internal code sent event handler
|
||||
* @private
|
||||
* @param credential
|
||||
*/
|
||||
_codeSentHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: credential.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'sent',
|
||||
};
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
|
||||
if (isIOS) {
|
||||
this._emitToSuccessCb(snapshot);
|
||||
}
|
||||
|
||||
if (isAndroid) {
|
||||
// android can auto retrieve so we don't emit to successCb immediately,
|
||||
// if auto retrieve times out then that will emit to successCb
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal code auto retrieve timeout event handler
|
||||
* @private
|
||||
* @param credential
|
||||
*/
|
||||
_codeAutoRetrievalTimeoutHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: credential.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'timeout',
|
||||
};
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToSuccessCb(snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal verification complete event handler
|
||||
* @param credential
|
||||
* @private
|
||||
*/
|
||||
_verificationCompleteHandler(credential) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: credential.verificationId,
|
||||
code: credential.code || null,
|
||||
error: null,
|
||||
state: 'verified',
|
||||
};
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToSuccessCb(snapshot);
|
||||
this._removeAllListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal verification failed event handler
|
||||
* @param state
|
||||
* @private
|
||||
*/
|
||||
_verificationFailedHandler(state) {
|
||||
const snapshot: PhoneAuthSnapshot = {
|
||||
verificationId: state.verificationId,
|
||||
code: null,
|
||||
error: null,
|
||||
state: 'error',
|
||||
};
|
||||
|
||||
const { code, message, nativeErrorMessage } = state.error;
|
||||
snapshot.error = nativeToJSError(code, message, { nativeErrorMessage });
|
||||
|
||||
this._emitToObservers(snapshot);
|
||||
this._emitToErrorCb(snapshot);
|
||||
this._removeAllListeners();
|
||||
}
|
||||
|
||||
/* -------------
|
||||
-- PUBLIC API
|
||||
--------------*/
|
||||
|
||||
on(
|
||||
event: string,
|
||||
observer: () => PhoneAuthSnapshot,
|
||||
errorCb?: () => PhoneAuthError,
|
||||
successCb?: () => PhoneAuthSnapshot
|
||||
): this {
|
||||
if (!isString(event)) {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('event', 'string', 'on')
|
||||
);
|
||||
}
|
||||
|
||||
if (event !== 'state_changed') {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_ARG_INVALID_VALUE(
|
||||
'event',
|
||||
'state_changed',
|
||||
event
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!isFunction(observer)) {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('observer', 'function', 'on')
|
||||
);
|
||||
}
|
||||
|
||||
this._addUserObserver(observer);
|
||||
|
||||
if (isFunction(errorCb)) {
|
||||
SharedEventEmitter.once(this._publicEvents.error, errorCb);
|
||||
}
|
||||
|
||||
if (isFunction(successCb)) {
|
||||
SharedEventEmitter.once(this._publicEvents.success, successCb);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise .then proxy
|
||||
* @param fn
|
||||
*/
|
||||
then(fn: () => PhoneAuthSnapshot) {
|
||||
this._promiseDeferred();
|
||||
// $FlowFixMe: Unsure how to annotate `bind` here
|
||||
if (this._promise) return this._promise.then.bind(this._promise)(fn);
|
||||
return undefined; // will never get here - just to keep flow happy
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise .catch proxy
|
||||
* @param fn
|
||||
*/
|
||||
catch(fn: () => Error) {
|
||||
this._promiseDeferred();
|
||||
// $FlowFixMe: Unsure how to annotate `bind` here
|
||||
if (this._promise) return this._promise.catch.bind(this._promise)(fn);
|
||||
return undefined; // will never get here - just to keep flow happy
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* EmailAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'password';
|
||||
|
||||
export default class EmailAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new EmailAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(email: string, password: string): AuthCredential {
|
||||
return {
|
||||
token: email,
|
||||
secret: password,
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* FacebookAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'facebook.com';
|
||||
|
||||
export default class FacebookAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new FacebookAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(token: string): AuthCredential {
|
||||
return {
|
||||
token,
|
||||
secret: '',
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* GithubAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'github.com';
|
||||
|
||||
export default class GithubAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new GithubAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(token: string): AuthCredential {
|
||||
return {
|
||||
token,
|
||||
secret: '',
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* EmailAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'google.com';
|
||||
|
||||
export default class GoogleAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new GoogleAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(token: string, secret: string): AuthCredential {
|
||||
return {
|
||||
token,
|
||||
secret,
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* OAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'oauth';
|
||||
|
||||
export default class OAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new OAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(idToken: string, accessToken: string): AuthCredential {
|
||||
return {
|
||||
token: idToken,
|
||||
secret: accessToken,
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* PhoneAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'phone';
|
||||
|
||||
export default class PhoneAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new PhoneAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(verificationId: string, code: string): AuthCredential {
|
||||
return {
|
||||
token: verificationId,
|
||||
secret: code,
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* TwitterAuthProvider representation wrapper
|
||||
*/
|
||||
import type { AuthCredential } from '../types';
|
||||
|
||||
const providerId = 'twitter.com';
|
||||
|
||||
export default class TwitterAuthProvider {
|
||||
constructor() {
|
||||
throw new Error(
|
||||
'`new TwitterAuthProvider()` is not supported on the native Firebase SDKs.'
|
||||
);
|
||||
}
|
||||
|
||||
static get PROVIDER_ID(): string {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
static credential(token: string, secret: string): AuthCredential {
|
||||
return {
|
||||
token,
|
||||
secret,
|
||||
providerId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
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,
|
||||
minimumVersion?: string,
|
||||
packageName: string,
|
||||
},
|
||||
handleCodeInApp?: boolean,
|
||||
iOS: {
|
||||
bundleId?: string,
|
||||
},
|
||||
url: string,
|
||||
};
|
||||
|
||||
export type AdditionalUserInfo = {
|
||||
isNewUser: boolean,
|
||||
profile?: Object,
|
||||
providerId: string,
|
||||
username?: string,
|
||||
};
|
||||
|
||||
export type AuthCredential = {
|
||||
providerId: string,
|
||||
token: string,
|
||||
secret: string,
|
||||
};
|
||||
|
||||
export type UserCredential = {|
|
||||
additionalUserInfo?: AdditionalUserInfo,
|
||||
user: User,
|
||||
|};
|
||||
|
||||
export type UserInfo = {
|
||||
displayName?: string,
|
||||
email?: string,
|
||||
phoneNumber?: string,
|
||||
photoURL?: string,
|
||||
providerId: string,
|
||||
uid: string,
|
||||
};
|
||||
|
||||
export type UserMetadata = {
|
||||
creationTime?: string,
|
||||
lastSignInTime?: string,
|
||||
};
|
||||
|
||||
export type NativeUser = {
|
||||
displayName?: string,
|
||||
email?: string,
|
||||
emailVerified?: boolean,
|
||||
isAnonymous?: boolean,
|
||||
metadata: UserMetadata,
|
||||
phoneNumber?: string,
|
||||
photoURL?: string,
|
||||
providerData: UserInfo[],
|
||||
providerId: string,
|
||||
uid: string,
|
||||
};
|
||||
|
||||
export type NativeUserCredential = {|
|
||||
additionalUserInfo?: AdditionalUserInfo,
|
||||
user: NativeUser,
|
||||
|};
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// todo move out
|
||||
export class ReferenceBase extends Base {
|
||||
constructor(path: string) {
|
||||
super();
|
||||
this.path = path || '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* The last part of a Reference's path (after the last '/')
|
||||
* The key of a root Reference is null.
|
||||
* @type {String}
|
||||
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#key}
|
||||
*/
|
||||
get key(): string | null {
|
||||
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
|
||||
}
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Remote Config representation wrapper
|
||||
*/
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
type NativeValue = {
|
||||
stringValue?: string,
|
||||
numberValue?: number,
|
||||
dataValue?: Object,
|
||||
boolValue?: boolean,
|
||||
source:
|
||||
| 'remoteConfigSourceRemote'
|
||||
| 'remoteConfigSourceDefault'
|
||||
| ' remoteConfigSourceStatic',
|
||||
};
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseRemoteConfig';
|
||||
export const NAMESPACE = 'config';
|
||||
|
||||
/**
|
||||
* @class Config
|
||||
*/
|
||||
export default class RemoteConfig extends ModuleBase {
|
||||
_developerModeEnabled: boolean;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._developerModeEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a native map to single JS value
|
||||
* @param nativeValue
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_nativeValueToJS(nativeValue: NativeValue) {
|
||||
return {
|
||||
source: nativeValue.source,
|
||||
val() {
|
||||
if (
|
||||
nativeValue.boolValue !== null &&
|
||||
(nativeValue.stringValue === 'true' ||
|
||||
nativeValue.stringValue === 'false' ||
|
||||
nativeValue.stringValue === null)
|
||||
)
|
||||
return nativeValue.boolValue;
|
||||
if (
|
||||
nativeValue.numberValue !== null &&
|
||||
nativeValue.numberValue !== undefined &&
|
||||
(nativeValue.stringValue == null ||
|
||||
nativeValue.stringValue === '' ||
|
||||
nativeValue.numberValue.toString() === nativeValue.stringValue)
|
||||
)
|
||||
return nativeValue.numberValue;
|
||||
if (
|
||||
nativeValue.dataValue !== nativeValue.stringValue &&
|
||||
(nativeValue.stringValue == null || nativeValue.stringValue === '')
|
||||
)
|
||||
return nativeValue.dataValue;
|
||||
return nativeValue.stringValue;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable Remote Config developer mode to allow for frequent refreshes of the cache
|
||||
*/
|
||||
enableDeveloperMode() {
|
||||
if (!this._developerModeEnabled) {
|
||||
getLogger(this).debug('Enabled developer mode');
|
||||
getNativeModule(this).enableDeveloperMode();
|
||||
this._developerModeEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Remote Config data
|
||||
* Call activateFetched to make fetched data available in app
|
||||
* @returns {*|Promise.<String>}:
|
||||
*/
|
||||
fetch(expiration?: number) {
|
||||
if (expiration !== undefined) {
|
||||
getLogger(this).debug(
|
||||
`Fetching remote config data with expiration ${expiration.toString()}`
|
||||
);
|
||||
return getNativeModule(this).fetchWithExpirationDuration(expiration);
|
||||
}
|
||||
getLogger(this).debug('Fetching remote config data');
|
||||
return getNativeModule(this).fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies Fetched Config data to the Active Config
|
||||
* @returns {*|Promise.<Bool>}
|
||||
* resolves if there was a Fetched Config, and it was activated,
|
||||
* rejects if no Fetched Config was found, or the Fetched Config was already activated.
|
||||
*/
|
||||
activateFetched() {
|
||||
getLogger(this).debug('Activating remote config');
|
||||
return getNativeModule(this).activateFetched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config value of the default namespace.
|
||||
* @param key: Config key
|
||||
* @returns {*|Promise.<Object>}, will always resolve
|
||||
* Object looks like
|
||||
* {
|
||||
* "stringValue" : stringValue,
|
||||
* "numberValue" : numberValue,
|
||||
* "dataValue" : dataValue,
|
||||
* "boolValue" : boolValue,
|
||||
* "source" : OneOf<String>(remoteConfigSourceRemote|remoteConfigSourceDefault|remoteConfigSourceStatic)
|
||||
* }
|
||||
*/
|
||||
getValue(key: string) {
|
||||
return getNativeModule(this)
|
||||
.getValue(key || '')
|
||||
.then(this._nativeValueToJS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config value of the default namespace.
|
||||
* @param keys: Config key
|
||||
* @returns {*|Promise.<Object>}, will always resolve.
|
||||
* Result will be a dictionary of key and config objects
|
||||
* Object looks like
|
||||
* {
|
||||
* "stringValue" : stringValue,
|
||||
* "numberValue" : numberValue,
|
||||
* "dataValue" : dataValue,
|
||||
* "boolValue" : boolValue,
|
||||
* "source" : OneOf<String>(remoteConfigSourceRemote|remoteConfigSourceDefault|remoteConfigSourceStatic)
|
||||
* }
|
||||
*/
|
||||
getValues(keys: Array<string>) {
|
||||
return getNativeModule(this)
|
||||
.getValues(keys || [])
|
||||
.then(nativeValues => {
|
||||
const values: { [string]: Object } = {};
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
values[keys[i]] = this._nativeValueToJS(nativeValues[i]);
|
||||
}
|
||||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of parameter keys that start with the given prefix, from the default namespace
|
||||
* @param prefix: The key prefix to look for. If prefix is nil or empty, returns all the keys.
|
||||
* @returns {*|Promise.<Array<String>>}
|
||||
*/
|
||||
getKeysByPrefix(prefix?: string) {
|
||||
return getNativeModule(this).getKeysByPrefix(prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets config defaults for parameter keys and values in the default namespace config.
|
||||
* @param defaults: A dictionary mapping a String key to a Object values.
|
||||
*/
|
||||
setDefaults(defaults: Object) {
|
||||
getNativeModule(this).setDefaults(defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default configs from plist for default namespace;
|
||||
* @param resource: The plist file name or resource ID
|
||||
*/
|
||||
setDefaultsFromResource(resource: string | number) {
|
||||
getNativeModule(this).setDefaultsFromResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* @flow
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import APPS from '../../utils/apps';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { isObject } from '../../utils';
|
||||
|
||||
import AdMob, { NAMESPACE as AdmobNamespace } from '../admob';
|
||||
import Auth, { NAMESPACE as AuthNamespace } from '../auth';
|
||||
import Analytics, { NAMESPACE as AnalyticsNamespace } from '../analytics';
|
||||
import Config, { NAMESPACE as ConfigNamespace } from '../config';
|
||||
import Crash, { NAMESPACE as CrashNamespace } from '../crash';
|
||||
import Crashlytics, {
|
||||
NAMESPACE as CrashlyticsNamespace,
|
||||
} from '../fabric/crashlytics';
|
||||
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
|
||||
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
|
||||
import InstanceId, { NAMESPACE as InstanceIdNamespace } from '../instanceid';
|
||||
import Invites, { NAMESPACE as InvitesNamespace } from '../invites';
|
||||
import Links, { NAMESPACE as LinksNamespace } from '../links';
|
||||
import Messaging, { NAMESPACE as MessagingNamespace } from '../messaging';
|
||||
import Notifications, {
|
||||
NAMESPACE as NotificationsNamespace,
|
||||
} from '../notifications';
|
||||
import Performance, { NAMESPACE as PerfNamespace } from '../perf';
|
||||
import Storage, { NAMESPACE as StorageNamespace } from '../storage';
|
||||
import Utils, { NAMESPACE as UtilsNamespace } from '../utils';
|
||||
|
||||
import type { FirebaseOptions } from '../../types';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
export default class App {
|
||||
_extendedProps: { [string]: boolean };
|
||||
_initialized: boolean = false;
|
||||
_name: string;
|
||||
_nativeInitialized: boolean = false;
|
||||
_options: FirebaseOptions;
|
||||
admob: () => AdMob;
|
||||
analytics: () => Analytics;
|
||||
auth: () => Auth;
|
||||
config: () => Config;
|
||||
crash: () => Crash;
|
||||
database: () => Database;
|
||||
fabric: {
|
||||
crashlytics: () => Crashlytics,
|
||||
};
|
||||
firestore: () => Firestore;
|
||||
instanceid: () => InstanceId;
|
||||
invites: () => Invites;
|
||||
links: () => Links;
|
||||
messaging: () => Messaging;
|
||||
notifications: () => Notifications;
|
||||
perf: () => Performance;
|
||||
storage: () => Storage;
|
||||
utils: () => Utils;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
options: FirebaseOptions,
|
||||
fromNative: boolean = false
|
||||
) {
|
||||
this._name = name;
|
||||
this._options = Object.assign({}, options);
|
||||
|
||||
if (fromNative) {
|
||||
this._initialized = true;
|
||||
this._nativeInitialized = true;
|
||||
} else if (options.databaseURL && options.apiKey) {
|
||||
FirebaseCoreModule.initializeApp(
|
||||
this._name,
|
||||
this._options,
|
||||
(error, result) => {
|
||||
this._initialized = true;
|
||||
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// modules
|
||||
this.admob = APPS.appModule(this, AdmobNamespace, AdMob);
|
||||
this.analytics = APPS.appModule(this, AnalyticsNamespace, Analytics);
|
||||
this.auth = APPS.appModule(this, AuthNamespace, Auth);
|
||||
this.config = APPS.appModule(this, ConfigNamespace, Config);
|
||||
this.crash = APPS.appModule(this, CrashNamespace, Crash);
|
||||
this.database = APPS.appModule(this, DatabaseNamespace, Database);
|
||||
this.fabric = {
|
||||
crashlytics: APPS.appModule(this, CrashlyticsNamespace, Crashlytics),
|
||||
};
|
||||
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
|
||||
this.instanceid = APPS.appModule(this, InstanceIdNamespace, InstanceId);
|
||||
this.invites = APPS.appModule(this, InvitesNamespace, Invites);
|
||||
this.links = APPS.appModule(this, LinksNamespace, Links);
|
||||
this.messaging = APPS.appModule(this, MessagingNamespace, Messaging);
|
||||
this.notifications = APPS.appModule(
|
||||
this,
|
||||
NotificationsNamespace,
|
||||
Notifications
|
||||
);
|
||||
this.perf = APPS.appModule(this, PerfNamespace, Performance);
|
||||
this.storage = APPS.appModule(this, StorageNamespace, Storage);
|
||||
this.utils = APPS.appModule(this, UtilsNamespace, Utils);
|
||||
this._extendedProps = {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
get options(): FirebaseOptions {
|
||||
return Object.assign({}, this._options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented firebase web sdk method that allows adding additional properties onto
|
||||
* a firebase app instance.
|
||||
*
|
||||
* See: https://github.com/firebase/firebase-js-sdk/blob/master/tests/app/firebase_app.test.ts#L328
|
||||
*
|
||||
* @param props
|
||||
*/
|
||||
extendApp(props: Object) {
|
||||
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++) {
|
||||
const key = keys[i];
|
||||
|
||||
if (!this._extendedProps[key] && Object.hasOwnProperty.call(this, key)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_PROTECTED_PROP(key));
|
||||
}
|
||||
|
||||
// $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
|
||||
this[key] = props[key];
|
||||
this._extendedProps[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
delete() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', 'delete')
|
||||
);
|
||||
// TODO only the ios sdk currently supports delete, add back in when android also supports it
|
||||
// if (this._name === APPS.DEFAULT_APP_NAME && this._nativeInitialized) {
|
||||
// return Promise.reject(
|
||||
// new Error('Unable to delete the default native firebase app instance.'),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// return FirebaseCoreModule.deleteApp(this._name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
onReady(): Promise<App> {
|
||||
if (this._initialized) return Promise.resolve(this);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
SharedEventEmitter.once(`AppReady:${this._name}`, ({ error }) => {
|
||||
if (error) return reject(new Error(error)); // error is a string as it's from native
|
||||
return resolve(this); // return app
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* toString returns the name of the app.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
toString() {
|
||||
return this._name;
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import APPS from '../../utils/apps';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import App from './app';
|
||||
import VERSION from '../../version';
|
||||
|
||||
// module imports
|
||||
import {
|
||||
statics as AdMobStatics,
|
||||
MODULE_NAME as AdmobModuleName,
|
||||
} from '../admob';
|
||||
import { statics as AuthStatics, MODULE_NAME as AuthModuleName } from '../auth';
|
||||
import {
|
||||
statics as AnalyticsStatics,
|
||||
MODULE_NAME as AnalyticsModuleName,
|
||||
} from '../analytics';
|
||||
import {
|
||||
statics as ConfigStatics,
|
||||
MODULE_NAME as ConfigModuleName,
|
||||
} from '../config';
|
||||
import {
|
||||
statics as CrashStatics,
|
||||
MODULE_NAME as CrashModuleName,
|
||||
} from '../crash';
|
||||
import {
|
||||
statics as CrashlyticsStatics,
|
||||
MODULE_NAME as CrashlyticsModuleName,
|
||||
} from '../fabric/crashlytics';
|
||||
import {
|
||||
statics as DatabaseStatics,
|
||||
MODULE_NAME as DatabaseModuleName,
|
||||
} from '../database';
|
||||
import {
|
||||
statics as FirestoreStatics,
|
||||
MODULE_NAME as FirestoreModuleName,
|
||||
} from '../firestore';
|
||||
import {
|
||||
statics as InstanceIdStatics,
|
||||
MODULE_NAME as InstanceIdModuleName,
|
||||
} from '../instanceid';
|
||||
import {
|
||||
statics as InvitesStatics,
|
||||
MODULE_NAME as InvitesModuleName,
|
||||
} from '../invites';
|
||||
import {
|
||||
statics as LinksStatics,
|
||||
MODULE_NAME as LinksModuleName,
|
||||
} from '../links';
|
||||
import {
|
||||
statics as MessagingStatics,
|
||||
MODULE_NAME as MessagingModuleName,
|
||||
} from '../messaging';
|
||||
import {
|
||||
statics as NotificationsStatics,
|
||||
MODULE_NAME as NotificationsModuleName,
|
||||
} from '../notifications';
|
||||
import {
|
||||
statics as PerformanceStatics,
|
||||
MODULE_NAME as PerfModuleName,
|
||||
} from '../perf';
|
||||
import {
|
||||
statics as StorageStatics,
|
||||
MODULE_NAME as StorageModuleName,
|
||||
} from '../storage';
|
||||
import {
|
||||
statics as UtilsStatics,
|
||||
MODULE_NAME as UtilsModuleName,
|
||||
} from '../utils';
|
||||
|
||||
import type {
|
||||
AdMobModule,
|
||||
AnalyticsModule,
|
||||
AuthModule,
|
||||
ConfigModule,
|
||||
CrashModule,
|
||||
DatabaseModule,
|
||||
FabricModule,
|
||||
FirebaseOptions,
|
||||
FirestoreModule,
|
||||
InstanceIdModule,
|
||||
InvitesModule,
|
||||
LinksModule,
|
||||
MessagingModule,
|
||||
NotificationsModule,
|
||||
PerformanceModule,
|
||||
StorageModule,
|
||||
UtilsModule,
|
||||
} from '../../types';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
class Firebase {
|
||||
admob: AdMobModule;
|
||||
analytics: AnalyticsModule;
|
||||
auth: AuthModule;
|
||||
config: ConfigModule;
|
||||
crash: CrashModule;
|
||||
database: DatabaseModule;
|
||||
fabric: FabricModule;
|
||||
firestore: FirestoreModule;
|
||||
instanceid: InstanceIdModule;
|
||||
invites: InvitesModule;
|
||||
links: LinksModule;
|
||||
messaging: MessagingModule;
|
||||
notifications: NotificationsModule;
|
||||
perf: PerformanceModule;
|
||||
storage: StorageModule;
|
||||
utils: UtilsModule;
|
||||
|
||||
constructor() {
|
||||
if (!FirebaseCoreModule) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE);
|
||||
}
|
||||
APPS.initializeNativeApps();
|
||||
|
||||
// modules
|
||||
this.admob = APPS.moduleAndStatics('admob', AdMobStatics, AdmobModuleName);
|
||||
this.analytics = APPS.moduleAndStatics(
|
||||
'analytics',
|
||||
AnalyticsStatics,
|
||||
AnalyticsModuleName
|
||||
);
|
||||
this.auth = APPS.moduleAndStatics('auth', AuthStatics, AuthModuleName);
|
||||
this.config = APPS.moduleAndStatics(
|
||||
'config',
|
||||
ConfigStatics,
|
||||
ConfigModuleName
|
||||
);
|
||||
this.crash = APPS.moduleAndStatics('crash', CrashStatics, CrashModuleName);
|
||||
this.database = APPS.moduleAndStatics(
|
||||
'database',
|
||||
DatabaseStatics,
|
||||
DatabaseModuleName
|
||||
);
|
||||
this.fabric = {
|
||||
crashlytics: APPS.moduleAndStatics(
|
||||
'crashlytics',
|
||||
CrashlyticsStatics,
|
||||
CrashlyticsModuleName
|
||||
),
|
||||
};
|
||||
this.firestore = APPS.moduleAndStatics(
|
||||
'firestore',
|
||||
FirestoreStatics,
|
||||
FirestoreModuleName
|
||||
);
|
||||
this.instanceid = APPS.moduleAndStatics(
|
||||
'instanceid',
|
||||
InstanceIdStatics,
|
||||
InstanceIdModuleName
|
||||
);
|
||||
this.invites = APPS.moduleAndStatics(
|
||||
'invites',
|
||||
InvitesStatics,
|
||||
InvitesModuleName
|
||||
);
|
||||
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
|
||||
this.messaging = APPS.moduleAndStatics(
|
||||
'messaging',
|
||||
MessagingStatics,
|
||||
MessagingModuleName
|
||||
);
|
||||
this.notifications = APPS.moduleAndStatics(
|
||||
'notifications',
|
||||
NotificationsStatics,
|
||||
NotificationsModuleName
|
||||
);
|
||||
this.perf = APPS.moduleAndStatics(
|
||||
'perf',
|
||||
PerformanceStatics,
|
||||
PerfModuleName
|
||||
);
|
||||
this.storage = APPS.moduleAndStatics(
|
||||
'storage',
|
||||
StorageStatics,
|
||||
StorageModuleName
|
||||
);
|
||||
this.utils = APPS.moduleAndStatics('utils', UtilsStatics, UtilsModuleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Web SDK initializeApp
|
||||
*
|
||||
* @param options
|
||||
* @param name
|
||||
* @return {*}
|
||||
*/
|
||||
initializeApp(options: FirebaseOptions, name: string): App {
|
||||
return APPS.initializeApp(options, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a Firebase app instance.
|
||||
*
|
||||
* When called with no arguments, the default app is returned.
|
||||
* When an app name is provided, the app corresponding to that name is returned.
|
||||
*
|
||||
* @param name
|
||||
* @return {*}
|
||||
*/
|
||||
app(name?: string): App {
|
||||
return APPS.app(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A (read-only) array of all initialized apps.
|
||||
* @return {Array}
|
||||
*/
|
||||
get apps(): Array<App> {
|
||||
return APPS.apps();
|
||||
}
|
||||
|
||||
/**
|
||||
* The current SDK version.
|
||||
* @return {string}
|
||||
*/
|
||||
get SDK_VERSION(): string {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Firebase();
|
|
@ -1,87 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Crash Reporting representation wrapper
|
||||
*/
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { FirebaseError } from '../../types';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseCrash';
|
||||
export const NAMESPACE = 'crash';
|
||||
|
||||
export default class Crash extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/Disables crash reporting
|
||||
* @param enabled
|
||||
*/
|
||||
setCrashCollectionEnabled(enabled: boolean): void {
|
||||
getNativeModule(this).setCrashCollectionEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not crash reporting is currently enabled
|
||||
* @returns {Promise.<boolean>}
|
||||
*/
|
||||
isCrashCollectionEnabled(): Promise<boolean> {
|
||||
return getNativeModule(this).isCrashCollectionEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message that will appear in a subsequent crash report.
|
||||
* @param {string} message
|
||||
*/
|
||||
log(message: string): void {
|
||||
getNativeModule(this).log(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message that will appear in a subsequent crash report as well as in logcat.
|
||||
* NOTE: Android only functionality. iOS will just log the message.
|
||||
* @param {string} message
|
||||
* @param {number} level
|
||||
* @param {string} tag
|
||||
*/
|
||||
logcat(level: number, tag: string, message: string): void {
|
||||
getNativeModule(this).logcat(level, tag, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a crash report for the given message. This method should be used for unexpected
|
||||
* exceptions where recovery is not possible.
|
||||
* NOTE: on iOS, this will cause the app to crash as it's the only way to ensure the exception
|
||||
* gets sent to Firebase. Otherwise it just gets lost as a log message.
|
||||
* @param {Error} error
|
||||
* @param maxStackSize
|
||||
*/
|
||||
report(error: FirebaseError, maxStackSize: number = 10): void {
|
||||
if (!error || !error.message) return;
|
||||
|
||||
let errorMessage = `Message: ${error.message}\r\n`;
|
||||
|
||||
if (error.code) {
|
||||
errorMessage = `${errorMessage}Code: ${error.code}\r\n`;
|
||||
}
|
||||
|
||||
const stackRows = error.stack.split('\n');
|
||||
errorMessage = `${errorMessage}\r\nStack: \r\n`;
|
||||
for (let i = 0, len = stackRows.length; i < len; i++) {
|
||||
if (i === maxStackSize) break;
|
||||
errorMessage = `${errorMessage} - ${stackRows[i]}\r\n`;
|
||||
}
|
||||
|
||||
getNativeModule(this).report(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,146 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* DataSnapshot representation wrapper
|
||||
*/
|
||||
import { isObject, deepGet, deepExists } from './../../utils';
|
||||
import type Reference from './Reference';
|
||||
|
||||
/**
|
||||
* @class DataSnapshot
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot
|
||||
*/
|
||||
export default class DataSnapshot {
|
||||
ref: Reference;
|
||||
key: string;
|
||||
|
||||
_value: any;
|
||||
_priority: any;
|
||||
_childKeys: Array<string>;
|
||||
|
||||
constructor(ref: Reference, snapshot: Object) {
|
||||
this.key = snapshot.key;
|
||||
|
||||
if (ref.key !== snapshot.key) {
|
||||
this.ref = ref.child(snapshot.key);
|
||||
} else {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
// internal use only
|
||||
this._value = snapshot.value;
|
||||
this._priority = snapshot.priority === undefined ? null : snapshot.priority;
|
||||
this._childKeys = snapshot.childKeys || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a JavaScript value from a DataSnapshot.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#val
|
||||
* @returns {any}
|
||||
*/
|
||||
val(): any {
|
||||
// clone via JSON stringify/parse - prevent modification of this._value
|
||||
if (isObject(this._value) || Array.isArray(this._value))
|
||||
return JSON.parse(JSON.stringify(this._value));
|
||||
return this._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets another DataSnapshot for the location at the specified relative path.
|
||||
* @param path
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach
|
||||
* @returns {Snapshot}
|
||||
*/
|
||||
child(path: string): DataSnapshot {
|
||||
const value = deepGet(this._value, path);
|
||||
const childRef = this.ref.child(path);
|
||||
return new DataSnapshot(childRef, {
|
||||
value,
|
||||
key: childRef.key,
|
||||
exists: value !== null,
|
||||
|
||||
// todo this is wrong - child keys needs to be the ordered keys, from FB
|
||||
// todo potential solution is build up a tree/map of a snapshot and its children
|
||||
// todo natively and send that back to JS to be use in this class.
|
||||
childKeys: isObject(value) ? Object.keys(value) : [],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this DataSnapshot contains any data.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#exists
|
||||
* @returns {boolean}
|
||||
*/
|
||||
exists(): boolean {
|
||||
return this._value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerates the top-level children in the DataSnapshot.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach
|
||||
* @param action
|
||||
*/
|
||||
forEach(action: (key: any) => any): boolean {
|
||||
if (!this._childKeys.length) return false;
|
||||
let cancelled = false;
|
||||
|
||||
for (let i = 0, len = this._childKeys.length; i < len; i++) {
|
||||
const key = this._childKeys[i];
|
||||
const childSnapshot = this.child(key);
|
||||
const returnValue = action(childSnapshot);
|
||||
|
||||
if (returnValue === true) {
|
||||
cancelled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the priority value of the data in this DataSnapshot.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#getPriority
|
||||
* @returns {String|Number|null}
|
||||
*/
|
||||
getPriority(): string | number | null {
|
||||
return this._priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified child path has (non-null) data.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#hasChild
|
||||
* @param path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasChild(path: string): boolean {
|
||||
return deepExists(this._value, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the DataSnapshot has any non-null child properties.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#hasChildren
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasChildren(): boolean {
|
||||
return this.numChildren() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of child properties of this DataSnapshot.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#numChildren
|
||||
* @returns {Number}
|
||||
*/
|
||||
numChildren(): number {
|
||||
if (!isObject(this._value)) return 0;
|
||||
return Object.keys(this._value).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON-serializable representation of this object.
|
||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#toJSON
|
||||
* @returns {any}
|
||||
*/
|
||||
toJSON(): Object {
|
||||
return this.val();
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* OnDisconnect representation wrapper
|
||||
*/
|
||||
import { typeOf } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import type Database from './';
|
||||
import type Reference from './Reference';
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect
|
||||
* @class OmDisconnect
|
||||
*/
|
||||
export default class OnDisconnect {
|
||||
_database: Database;
|
||||
ref: Reference;
|
||||
path: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ref
|
||||
*/
|
||||
constructor(ref: Reference) {
|
||||
this.ref = ref;
|
||||
this.path = ref.path;
|
||||
this._database = ref._database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect#set
|
||||
* @param value
|
||||
* @returns {*}
|
||||
*/
|
||||
set(value: string | Object): Promise<void> {
|
||||
return getNativeModule(this._database).onDisconnectSet(this.path, {
|
||||
type: typeOf(value),
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect#update
|
||||
* @param values
|
||||
* @returns {*}
|
||||
*/
|
||||
update(values: Object): Promise<void> {
|
||||
return getNativeModule(this._database).onDisconnectUpdate(
|
||||
this.path,
|
||||
values
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect#remove
|
||||
* @returns {*}
|
||||
*/
|
||||
remove(): Promise<void> {
|
||||
return getNativeModule(this._database).onDisconnectRemove(this.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect#cancel
|
||||
* @returns {*}
|
||||
*/
|
||||
cancel(): Promise<void> {
|
||||
return getNativeModule(this._database).onDisconnectCancel(this.path);
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Query representation wrapper
|
||||
*/
|
||||
import { objectToUniqueId } from '../../utils';
|
||||
|
||||
import type { DatabaseModifier } from '../../types';
|
||||
import type Reference from './Reference';
|
||||
|
||||
// todo doc methods
|
||||
|
||||
/**
|
||||
* @class Query
|
||||
*/
|
||||
export default class Query {
|
||||
_reference: Reference;
|
||||
modifiers: Array<DatabaseModifier>;
|
||||
|
||||
constructor(ref: Reference, existingModifiers?: Array<DatabaseModifier>) {
|
||||
this.modifiers = existingModifiers ? [...existingModifiers] : [];
|
||||
this._reference = ref;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param key
|
||||
* @return {Reference|*}
|
||||
*/
|
||||
orderBy(name: string, key?: string) {
|
||||
this.modifiers.push({
|
||||
id: `orderBy-${name}:${key || ''}`,
|
||||
type: 'orderBy',
|
||||
name,
|
||||
key,
|
||||
});
|
||||
|
||||
return this._reference;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param limit
|
||||
* @return {Reference|*}
|
||||
*/
|
||||
limit(name: string, limit: number) {
|
||||
this.modifiers.push({
|
||||
id: `limit-${name}:${limit}`,
|
||||
type: 'limit',
|
||||
name,
|
||||
limit,
|
||||
});
|
||||
|
||||
return this._reference;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
* @param key
|
||||
* @return {Reference|*}
|
||||
*/
|
||||
filter(name: string, value: any, key?: string) {
|
||||
this.modifiers.push({
|
||||
id: `filter-${name}:${objectToUniqueId(value)}:${key || ''}`,
|
||||
type: 'filter',
|
||||
name,
|
||||
value,
|
||||
valueType: typeof value,
|
||||
key,
|
||||
});
|
||||
|
||||
return this._reference;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {[*]}
|
||||
*/
|
||||
getModifiers(): Array<DatabaseModifier> {
|
||||
return [...this.modifiers];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
queryIdentifier() {
|
||||
// sort modifiers to enforce ordering
|
||||
const sortedModifiers = this.getModifiers().sort((a, b) => {
|
||||
if (a.id < b.id) return -1;
|
||||
if (a.id > b.id) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Convert modifiers to unique key
|
||||
let key = '{';
|
||||
for (let i = 0; i < sortedModifiers.length; i++) {
|
||||
if (i !== 0) key += ',';
|
||||
key += sortedModifiers[i].id;
|
||||
}
|
||||
key += '}';
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
|
@ -1,894 +0,0 @@
|
|||
/**
|
||||
* @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<DatabaseModifier>
|
||||
) {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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.<any>}
|
||||
*/
|
||||
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);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
* @param onComplete
|
||||
* @returns {*}
|
||||
*/
|
||||
push(value: any, onComplete?: Function): Reference | Promise<void> {
|
||||
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
|
||||
// $FlowExpectedError: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
|
||||
.then(() => onComplete(null, newRef))
|
||||
// $FlowExpectedError: 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.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.databaseUrl}$/${
|
||||
this.path
|
||||
}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a string that uniquely identifies this
|
||||
* combination of path and query modifiers
|
||||
*
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
_getRefKey() {
|
||||
return `$${this._database.databaseUrl}$/${
|
||||
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,
|
||||
dbURL: this._database.databaseUrl,
|
||||
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,
|
||||
dbURL: this._database.databaseUrl,
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Database representation wrapper
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import Reference from './Reference';
|
||||
import TransactionHandler from './transaction';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
import firebase from '../core/firebase';
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'database_transaction_event',
|
||||
// 'database_server_offset', // TODO
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseDatabase';
|
||||
export const NAMESPACE = 'database';
|
||||
|
||||
/**
|
||||
* @class Database
|
||||
*/
|
||||
export default class Database extends ModuleBase {
|
||||
_offsetRef: Reference;
|
||||
_serverTimeOffset: number;
|
||||
_transactionHandler: TransactionHandler;
|
||||
_serviceUrl: string;
|
||||
|
||||
constructor(appOrUrl: App | string, options: Object = {}) {
|
||||
let app;
|
||||
let serviceUrl;
|
||||
if (typeof appOrUrl === 'string') {
|
||||
app = firebase.app();
|
||||
serviceUrl = appOrUrl.endsWith('/') ? appOrUrl : `${appOrUrl}/`;
|
||||
} else {
|
||||
app = appOrUrl;
|
||||
serviceUrl = app.options.databaseURL;
|
||||
}
|
||||
|
||||
super(
|
||||
app,
|
||||
{
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: true,
|
||||
namespace: NAMESPACE,
|
||||
},
|
||||
serviceUrl
|
||||
);
|
||||
|
||||
this._serviceUrl = serviceUrl;
|
||||
this._transactionHandler = new TransactionHandler(this);
|
||||
|
||||
if (options.persistence) {
|
||||
getNativeModule(this).setPersistence(options.persistence);
|
||||
}
|
||||
|
||||
// server time listener
|
||||
// setTimeout used to avoid setPersistence race conditions
|
||||
// todo move this and persistence to native side, create a db configure() method natively perhaps?
|
||||
// todo and then native can call setPersistence and then emit offset events
|
||||
setTimeout(() => {
|
||||
this._serverTimeOffset = 0;
|
||||
this._offsetRef = this.ref('.info/serverTimeOffset');
|
||||
this._offsetRef.on('value', snapshot => {
|
||||
this._serverTimeOffset = snapshot.val() || this._serverTimeOffset;
|
||||
});
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {number}
|
||||
*/
|
||||
getServerTime(): number {
|
||||
return new Date(Date.now() + this._serverTimeOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
goOnline(): void {
|
||||
getNativeModule(this).goOnline();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
goOffline(): void {
|
||||
getNativeModule(this).goOffline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new firebase reference instance
|
||||
* @param path
|
||||
* @returns {Reference}
|
||||
*/
|
||||
ref(path: string): Reference {
|
||||
return new Reference(this, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database url
|
||||
* @returns {string}
|
||||
*/
|
||||
get databaseUrl(): string {
|
||||
return this._serviceUrl;
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
ServerValue: NativeModules.RNFirebaseDatabase
|
||||
? {
|
||||
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || {
|
||||
'.sv': 'timestamp',
|
||||
},
|
||||
}
|
||||
: {},
|
||||
enableLogging(enabled: boolean) {
|
||||
if (NativeModules[MODULE_NAME]) {
|
||||
NativeModules[MODULE_NAME].enableLogging(enabled);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -1,164 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Database Transaction representation wrapper
|
||||
*/
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import type Database from './';
|
||||
|
||||
let transactionId = 0;
|
||||
|
||||
/**
|
||||
* Uses the push id generator to create a transaction id
|
||||
* @returns {number}
|
||||
* @private
|
||||
*/
|
||||
const generateTransactionId = (): number => transactionId++;
|
||||
|
||||
/**
|
||||
* @class TransactionHandler
|
||||
*/
|
||||
export default class TransactionHandler {
|
||||
_database: Database;
|
||||
_transactions: { [number]: Object };
|
||||
|
||||
constructor(database: Database) {
|
||||
this._transactions = {};
|
||||
this._database = database;
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this._database, 'database_transaction_event'),
|
||||
this._handleTransactionEvent.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new transaction and start it natively.
|
||||
* @param reference
|
||||
* @param transactionUpdater
|
||||
* @param onComplete
|
||||
* @param applyLocally
|
||||
*/
|
||||
add(
|
||||
reference: Object,
|
||||
transactionUpdater: Function,
|
||||
onComplete?: Function,
|
||||
applyLocally?: boolean = false
|
||||
) {
|
||||
const id = generateTransactionId();
|
||||
|
||||
this._transactions[id] = {
|
||||
id,
|
||||
reference,
|
||||
transactionUpdater,
|
||||
onComplete,
|
||||
applyLocally,
|
||||
completed: false,
|
||||
started: true,
|
||||
};
|
||||
|
||||
getNativeModule(this._database).transactionStart(
|
||||
reference.path,
|
||||
id,
|
||||
applyLocally
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNALS
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_handleTransactionEvent(event: Object = {}) {
|
||||
switch (event.type) {
|
||||
case 'update':
|
||||
return this._handleUpdate(event);
|
||||
case 'error':
|
||||
return this._handleError(event);
|
||||
case 'complete':
|
||||
return this._handleComplete(event);
|
||||
default:
|
||||
getLogger(this._database).warn(
|
||||
`Unknown transaction event type: '${event.type}'`,
|
||||
event
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_handleUpdate(event: Object = {}) {
|
||||
let newValue;
|
||||
const { id, value } = event;
|
||||
|
||||
try {
|
||||
const transaction = this._transactions[id];
|
||||
if (!transaction) return;
|
||||
|
||||
newValue = transaction.transactionUpdater(value);
|
||||
} finally {
|
||||
let abort = false;
|
||||
|
||||
if (newValue === undefined) {
|
||||
abort = true;
|
||||
}
|
||||
|
||||
getNativeModule(this._database).transactionTryCommit(id, {
|
||||
value: newValue,
|
||||
abort,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_handleError(event: Object = {}) {
|
||||
const transaction = this._transactions[event.id];
|
||||
if (transaction && !transaction.completed) {
|
||||
transaction.completed = true;
|
||||
try {
|
||||
transaction.onComplete(event.error, false, null);
|
||||
} finally {
|
||||
setImmediate(() => {
|
||||
delete this._transactions[event.id];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_handleComplete(event: Object = {}) {
|
||||
const transaction = this._transactions[event.id];
|
||||
if (transaction && !transaction.completed) {
|
||||
transaction.completed = true;
|
||||
try {
|
||||
transaction.onComplete(
|
||||
null,
|
||||
event.committed,
|
||||
Object.assign({}, event.snapshot)
|
||||
);
|
||||
} finally {
|
||||
setImmediate(() => {
|
||||
delete this._transactions[event.id];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Crash Reporting representation wrapper
|
||||
*/
|
||||
import ModuleBase from '../../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../../utils/native';
|
||||
|
||||
import type App from '../../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseCrashlytics';
|
||||
export const NAMESPACE = 'crashlytics';
|
||||
|
||||
export default class Crashlytics extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a crash. Useful for testing your application is set up correctly.
|
||||
*/
|
||||
crash(): void {
|
||||
getNativeModule(this).crash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message that will appear in any subsequent crash reports.
|
||||
* @param {string} message
|
||||
*/
|
||||
log(message: string): void {
|
||||
getNativeModule(this).log(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a non fatal exception.
|
||||
* @param {string} code
|
||||
* @param {string} message
|
||||
*/
|
||||
recordError(code: number, message: string): void {
|
||||
getNativeModule(this).recordError(code, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a boolean value to show alongside any subsequent crash reports.
|
||||
*/
|
||||
setBoolValue(key: string, value: boolean): void {
|
||||
getNativeModule(this).setBoolValue(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a float value to show alongside any subsequent crash reports.
|
||||
*/
|
||||
setFloatValue(key: string, value: number): void {
|
||||
getNativeModule(this).setFloatValue(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an integer value to show alongside any subsequent crash reports.
|
||||
*/
|
||||
setIntValue(key: string, value: number): void {
|
||||
getNativeModule(this).setIntValue(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a string value to show alongside any subsequent crash reports.
|
||||
*/
|
||||
setStringValue(key: string, value: string): void {
|
||||
getNativeModule(this).setStringValue(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user ID to show alongside any subsequent crash reports.
|
||||
*/
|
||||
setUserIdentifier(userId: string): void {
|
||||
getNativeModule(this).setUserIdentifier(userId);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,109 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* CollectionReference representation wrapper
|
||||
*/
|
||||
import DocumentReference from './DocumentReference';
|
||||
import Query from './Query';
|
||||
import { firestoreAutoId } from '../../utils';
|
||||
|
||||
import type Firestore from './';
|
||||
import type {
|
||||
QueryDirection,
|
||||
QueryListenOptions,
|
||||
QueryOperator,
|
||||
} from './types';
|
||||
import type FieldPath from './FieldPath';
|
||||
import type Path from './Path';
|
||||
import type { Observer, ObserverOnError, ObserverOnNext } from './Query';
|
||||
import type QuerySnapshot from './QuerySnapshot';
|
||||
|
||||
/**
|
||||
* @class CollectionReference
|
||||
*/
|
||||
export default class CollectionReference {
|
||||
_collectionPath: Path;
|
||||
_firestore: Firestore;
|
||||
_query: Query;
|
||||
|
||||
constructor(firestore: Firestore, collectionPath: Path) {
|
||||
this._collectionPath = collectionPath;
|
||||
this._firestore = firestore;
|
||||
this._query = new Query(firestore, collectionPath);
|
||||
}
|
||||
|
||||
get firestore(): Firestore {
|
||||
return this._firestore;
|
||||
}
|
||||
|
||||
get id(): string | null {
|
||||
return this._collectionPath.id;
|
||||
}
|
||||
|
||||
get parent(): DocumentReference | null {
|
||||
const parentPath = this._collectionPath.parent();
|
||||
return parentPath
|
||||
? new DocumentReference(this._firestore, parentPath)
|
||||
: null;
|
||||
}
|
||||
|
||||
add(data: Object): Promise<DocumentReference> {
|
||||
const documentRef = this.doc();
|
||||
return documentRef.set(data).then(() => Promise.resolve(documentRef));
|
||||
}
|
||||
|
||||
doc(documentPath?: string): DocumentReference {
|
||||
const newPath = documentPath || firestoreAutoId();
|
||||
|
||||
const path = this._collectionPath.child(newPath);
|
||||
if (!path.isDocument) {
|
||||
throw new Error('Argument "documentPath" must point to a document.');
|
||||
}
|
||||
|
||||
return new DocumentReference(this._firestore, path);
|
||||
}
|
||||
|
||||
// From Query
|
||||
endAt(...snapshotOrVarArgs: any[]): Query {
|
||||
return this._query.endAt(snapshotOrVarArgs);
|
||||
}
|
||||
|
||||
endBefore(...snapshotOrVarArgs: any[]): Query {
|
||||
return this._query.endBefore(snapshotOrVarArgs);
|
||||
}
|
||||
|
||||
get(): Promise<QuerySnapshot> {
|
||||
return this._query.get();
|
||||
}
|
||||
|
||||
limit(limit: number): Query {
|
||||
return this._query.limit(limit);
|
||||
}
|
||||
|
||||
onSnapshot(
|
||||
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
|
||||
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
|
||||
onError?: ObserverOnError
|
||||
): () => void {
|
||||
return this._query.onSnapshot(
|
||||
optionsOrObserverOrOnNext,
|
||||
observerOrOnNextOrOnError,
|
||||
onError
|
||||
);
|
||||
}
|
||||
|
||||
orderBy(fieldPath: string | FieldPath, directionStr?: QueryDirection): Query {
|
||||
return this._query.orderBy(fieldPath, directionStr);
|
||||
}
|
||||
|
||||
startAfter(...snapshotOrVarArgs: any[]): Query {
|
||||
return this._query.startAfter(snapshotOrVarArgs);
|
||||
}
|
||||
|
||||
startAt(...snapshotOrVarArgs: any[]): Query {
|
||||
return this._query.startAt(snapshotOrVarArgs);
|
||||
}
|
||||
|
||||
where(fieldPath: string, opStr: QueryOperator, value: any): Query {
|
||||
return this._query.where(fieldPath, opStr, value);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* DocumentChange representation wrapper
|
||||
*/
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
|
||||
import type Firestore from './';
|
||||
import type { NativeDocumentChange } from './types';
|
||||
|
||||
/**
|
||||
* @class DocumentChange
|
||||
*/
|
||||
export default class DocumentChange {
|
||||
_document: DocumentSnapshot;
|
||||
_newIndex: number;
|
||||
_oldIndex: number;
|
||||
_type: string;
|
||||
|
||||
constructor(firestore: Firestore, nativeData: NativeDocumentChange) {
|
||||
this._document = new DocumentSnapshot(firestore, nativeData.document);
|
||||
this._newIndex = nativeData.newIndex;
|
||||
this._oldIndex = nativeData.oldIndex;
|
||||
this._type = nativeData.type;
|
||||
}
|
||||
|
||||
get doc(): DocumentSnapshot {
|
||||
return this._document;
|
||||
}
|
||||
|
||||
get newIndex(): number {
|
||||
return this._newIndex;
|
||||
}
|
||||
|
||||
get oldIndex(): number {
|
||||
return this._oldIndex;
|
||||
}
|
||||
|
||||
get type(): string {
|
||||
return this._type;
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* DocumentReference representation wrapper
|
||||
*/
|
||||
import CollectionReference from './CollectionReference';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import { parseUpdateArgs } from './utils';
|
||||
import { buildNativeMap } from './utils/serialize';
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Firestore from './';
|
||||
import type {
|
||||
DocumentListenOptions,
|
||||
NativeDocumentSnapshot,
|
||||
SetOptions,
|
||||
} from './types';
|
||||
import type Path from './Path';
|
||||
|
||||
type ObserverOnError = Object => void;
|
||||
type ObserverOnNext = DocumentSnapshot => void;
|
||||
|
||||
type Observer = {
|
||||
error?: ObserverOnError,
|
||||
next: ObserverOnNext,
|
||||
};
|
||||
|
||||
/**
|
||||
* @class DocumentReference
|
||||
*/
|
||||
export default class DocumentReference {
|
||||
_documentPath: Path;
|
||||
_firestore: Firestore;
|
||||
|
||||
constructor(firestore: Firestore, documentPath: Path) {
|
||||
this._documentPath = documentPath;
|
||||
this._firestore = firestore;
|
||||
}
|
||||
|
||||
get firestore(): Firestore {
|
||||
return this._firestore;
|
||||
}
|
||||
|
||||
get id(): string | null {
|
||||
return this._documentPath.id;
|
||||
}
|
||||
|
||||
get parent(): CollectionReference {
|
||||
const parentPath = this._documentPath.parent();
|
||||
// $FlowExpectedError: parentPath can never be null
|
||||
return new CollectionReference(this._firestore, parentPath);
|
||||
}
|
||||
|
||||
get path(): string {
|
||||
return this._documentPath.relativeName;
|
||||
}
|
||||
|
||||
collection(collectionPath: string): CollectionReference {
|
||||
const path = this._documentPath.child(collectionPath);
|
||||
if (!path.isCollection) {
|
||||
throw new Error('Argument "collectionPath" must point to a collection.');
|
||||
}
|
||||
|
||||
return new CollectionReference(this._firestore, path);
|
||||
}
|
||||
|
||||
delete(): Promise<void> {
|
||||
return getNativeModule(this._firestore).documentDelete(this.path);
|
||||
}
|
||||
|
||||
get(): Promise<DocumentSnapshot> {
|
||||
return getNativeModule(this._firestore)
|
||||
.documentGet(this.path)
|
||||
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||
}
|
||||
|
||||
onSnapshot(
|
||||
optionsOrObserverOrOnNext:
|
||||
| DocumentListenOptions
|
||||
| Observer
|
||||
| ObserverOnNext,
|
||||
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
|
||||
onError?: ObserverOnError
|
||||
) {
|
||||
let observer: Observer;
|
||||
let docListenOptions = {};
|
||||
// Called with: onNext, ?onError
|
||||
if (isFunction(optionsOrObserverOrOnNext)) {
|
||||
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
|
||||
throw new Error(
|
||||
'DocumentReference.onSnapshot failed: Second argument must be a valid function.'
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
observer = {
|
||||
next: optionsOrObserverOrOnNext,
|
||||
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.'
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
observer = {
|
||||
next: optionsOrObserverOrOnNext.next,
|
||||
error: optionsOrObserverOrOnNext.error,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
'DocumentReference.onSnapshot failed: Observer.next must be a valid function.'
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
optionsOrObserverOrOnNext,
|
||||
'includeMetadataChanges'
|
||||
)
|
||||
) {
|
||||
docListenOptions = optionsOrObserverOrOnNext;
|
||||
// Called with: Options, onNext, ?onError
|
||||
if (isFunction(observerOrOnNextOrOnError)) {
|
||||
if (onError && !isFunction(onError)) {
|
||||
throw new Error(
|
||||
'DocumentReference.onSnapshot failed: Third argument must be a valid function.'
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
observer = {
|
||||
next: observerOrOnNextOrOnError,
|
||||
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 = {
|
||||
next: observerOrOnNextOrOnError.next,
|
||||
error: observerOrOnNextOrOnError.error,
|
||||
};
|
||||
} 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: NativeDocumentSnapshot) => {
|
||||
const documentSnapshot = new DocumentSnapshot(
|
||||
this.firestore,
|
||||
nativeDocumentSnapshot
|
||||
);
|
||||
observer.next(documentSnapshot);
|
||||
};
|
||||
|
||||
// Listen to snapshot events
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
|
||||
// Listen for snapshot error events
|
||||
if (observer.error) {
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(
|
||||
this._firestore,
|
||||
`onDocumentSnapshotError:${listenerId}`
|
||||
),
|
||||
observer.error
|
||||
);
|
||||
}
|
||||
|
||||
// Add the native listener
|
||||
getNativeModule(this._firestore).documentOnSnapshot(
|
||||
this.path,
|
||||
listenerId,
|
||||
docListenOptions
|
||||
);
|
||||
|
||||
// Return an unsubscribe method
|
||||
return this._offDocumentSnapshot.bind(this, listenerId, listener);
|
||||
}
|
||||
|
||||
set(data: Object, options?: SetOptions): Promise<void> {
|
||||
const nativeData = buildNativeMap(data);
|
||||
return getNativeModule(this._firestore).documentSet(
|
||||
this.path,
|
||||
nativeData,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
update(...args: any[]): Promise<void> {
|
||||
const data = parseUpdateArgs(args, 'DocumentReference.update');
|
||||
const nativeData = buildNativeMap(data);
|
||||
return getNativeModule(this._firestore).documentUpdate(
|
||||
this.path,
|
||||
nativeData
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNALS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Remove document snapshot listener
|
||||
* @param listener
|
||||
*/
|
||||
_offDocumentSnapshot(listenerId: string, listener: Function) {
|
||||
getLogger(this._firestore).info('Removing onDocumentSnapshot listener');
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
getNativeModule(this._firestore).documentOffSnapshot(this.path, listenerId);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* DocumentSnapshot representation wrapper
|
||||
*/
|
||||
import DocumentReference from './DocumentReference';
|
||||
import FieldPath from './FieldPath';
|
||||
import Path from './Path';
|
||||
import { isObject } from '../../utils';
|
||||
import { parseNativeMap } from './utils/serialize';
|
||||
|
||||
import type Firestore from './';
|
||||
import type { NativeDocumentSnapshot, SnapshotMetadata } from './types';
|
||||
|
||||
const extractFieldPathData = (data: Object | void, segments: string[]): any => {
|
||||
if (!data || !isObject(data)) {
|
||||
return undefined;
|
||||
}
|
||||
const pathValue = data[segments[0]];
|
||||
if (segments.length === 1) {
|
||||
return pathValue;
|
||||
}
|
||||
return extractFieldPathData(pathValue, segments.slice(1));
|
||||
};
|
||||
|
||||
/**
|
||||
* @class DocumentSnapshot
|
||||
*/
|
||||
export default class DocumentSnapshot {
|
||||
_data: Object | void;
|
||||
_metadata: SnapshotMetadata;
|
||||
_ref: DocumentReference;
|
||||
|
||||
constructor(firestore: Firestore, nativeData: NativeDocumentSnapshot) {
|
||||
this._data = parseNativeMap(firestore, nativeData.data);
|
||||
this._metadata = nativeData.metadata;
|
||||
this._ref = new DocumentReference(
|
||||
firestore,
|
||||
Path.fromName(nativeData.path)
|
||||
);
|
||||
}
|
||||
|
||||
get exists(): boolean {
|
||||
return this._data !== undefined;
|
||||
}
|
||||
|
||||
get id(): string | null {
|
||||
return this._ref.id;
|
||||
}
|
||||
|
||||
get metadata(): SnapshotMetadata {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
get ref(): DocumentReference {
|
||||
return this._ref;
|
||||
}
|
||||
|
||||
data(): Object | void {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get(fieldPath: string | FieldPath): any {
|
||||
if (fieldPath instanceof FieldPath) {
|
||||
return extractFieldPathData(this._data, fieldPath._segments);
|
||||
}
|
||||
return this._data ? this._data[fieldPath] : undefined;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* FieldPath representation wrapper
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class FieldPath
|
||||
*/
|
||||
export default class FieldPath {
|
||||
_segments: string[];
|
||||
|
||||
constructor(...segments: string[]) {
|
||||
// TODO: Validation
|
||||
this._segments = segments;
|
||||
}
|
||||
|
||||
static documentId(): FieldPath {
|
||||
return DOCUMENT_ID;
|
||||
}
|
||||
}
|
||||
|
||||
export const DOCUMENT_ID = new FieldPath('__name__');
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* FieldValue representation wrapper
|
||||
*/
|
||||
|
||||
export default class FieldValue {
|
||||
static delete(): FieldValue {
|
||||
return DELETE_FIELD_VALUE;
|
||||
}
|
||||
|
||||
static serverTimestamp(): FieldValue {
|
||||
return SERVER_TIMESTAMP_FIELD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
export const DELETE_FIELD_VALUE = new FieldValue();
|
||||
export const SERVER_TIMESTAMP_FIELD_VALUE = new FieldValue();
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* GeoPoint representation wrapper
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class GeoPoint
|
||||
*/
|
||||
export default class GeoPoint {
|
||||
_latitude: number;
|
||||
_longitude: number;
|
||||
|
||||
constructor(latitude: number, longitude: number) {
|
||||
// TODO: Validation
|
||||
// validate.isNumber('latitude', latitude);
|
||||
// validate.isNumber('longitude', longitude);
|
||||
|
||||
this._latitude = latitude;
|
||||
this._longitude = longitude;
|
||||
}
|
||||
|
||||
get latitude(): number {
|
||||
return this._latitude;
|
||||
}
|
||||
|
||||
get longitude(): number {
|
||||
return this._longitude;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Path representation wrapper
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class Path
|
||||
*/
|
||||
export default class Path {
|
||||
_parts: string[];
|
||||
|
||||
constructor(pathComponents: string[]) {
|
||||
this._parts = pathComponents;
|
||||
}
|
||||
|
||||
get id(): string | null {
|
||||
return this._parts.length > 0 ? this._parts[this._parts.length - 1] : null;
|
||||
}
|
||||
|
||||
get isDocument(): boolean {
|
||||
return this._parts.length > 0 && this._parts.length % 2 === 0;
|
||||
}
|
||||
|
||||
get isCollection(): boolean {
|
||||
return this._parts.length % 2 === 1;
|
||||
}
|
||||
|
||||
get relativeName(): string {
|
||||
return this._parts.join('/');
|
||||
}
|
||||
|
||||
child(relativePath: string): Path {
|
||||
return new Path(this._parts.concat(relativePath.split('/')));
|
||||
}
|
||||
|
||||
parent(): Path | null {
|
||||
return this._parts.length > 0
|
||||
? new Path(this._parts.slice(0, this._parts.length - 1))
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @package
|
||||
*/
|
||||
static fromName(name: string): Path {
|
||||
const parts = name.split('/');
|
||||
return parts.length === 0 ? new Path([]) : new Path(parts);
|
||||
}
|
||||
}
|
|
@ -1,459 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Query representation wrapper
|
||||
*/
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import FieldPath from './FieldPath';
|
||||
import QuerySnapshot from './QuerySnapshot';
|
||||
import { buildNativeArray, buildTypeMap } from './utils/serialize';
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Firestore from './';
|
||||
import type Path from './Path';
|
||||
import type {
|
||||
QueryDirection,
|
||||
QueryOperator,
|
||||
QueryListenOptions,
|
||||
} from './types';
|
||||
|
||||
const DIRECTIONS: { [QueryDirection]: string } = {
|
||||
ASC: 'ASCENDING',
|
||||
asc: 'ASCENDING',
|
||||
DESC: 'DESCENDING',
|
||||
desc: 'DESCENDING',
|
||||
};
|
||||
|
||||
const OPERATORS: { [QueryOperator]: string } = {
|
||||
'=': 'EQUAL',
|
||||
'==': 'EQUAL',
|
||||
'>': 'GREATER_THAN',
|
||||
'>=': 'GREATER_THAN_OR_EQUAL',
|
||||
'<': 'LESS_THAN',
|
||||
'<=': 'LESS_THAN_OR_EQUAL',
|
||||
};
|
||||
|
||||
type NativeFieldPath = {|
|
||||
elements?: string[],
|
||||
string?: string,
|
||||
type: 'fieldpath' | 'string',
|
||||
|};
|
||||
type FieldFilter = {|
|
||||
fieldPath: NativeFieldPath,
|
||||
operator: string,
|
||||
value: any,
|
||||
|};
|
||||
type FieldOrder = {|
|
||||
direction: string,
|
||||
fieldPath: NativeFieldPath,
|
||||
|};
|
||||
type QueryOptions = {
|
||||
endAt?: any[],
|
||||
endBefore?: any[],
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
selectFields?: string[],
|
||||
startAfter?: any[],
|
||||
startAt?: any[],
|
||||
};
|
||||
|
||||
export type ObserverOnError = Object => void;
|
||||
export type ObserverOnNext = QuerySnapshot => void;
|
||||
|
||||
export type Observer = {
|
||||
error?: ObserverOnError,
|
||||
next: ObserverOnNext,
|
||||
};
|
||||
|
||||
const buildNativeFieldPath = (
|
||||
fieldPath: string | FieldPath
|
||||
): NativeFieldPath => {
|
||||
if (fieldPath instanceof FieldPath) {
|
||||
return {
|
||||
elements: fieldPath._segments,
|
||||
type: 'fieldpath',
|
||||
};
|
||||
}
|
||||
return {
|
||||
string: fieldPath,
|
||||
type: 'string',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Query
|
||||
*/
|
||||
export default class Query {
|
||||
_fieldFilters: FieldFilter[];
|
||||
_fieldOrders: FieldOrder[];
|
||||
_firestore: Firestore;
|
||||
_iid: number;
|
||||
_queryOptions: QueryOptions;
|
||||
_referencePath: Path;
|
||||
|
||||
constructor(
|
||||
firestore: Firestore,
|
||||
path: Path,
|
||||
fieldFilters?: FieldFilter[],
|
||||
fieldOrders?: FieldOrder[],
|
||||
queryOptions?: QueryOptions
|
||||
) {
|
||||
this._fieldFilters = fieldFilters || [];
|
||||
this._fieldOrders = fieldOrders || [];
|
||||
this._firestore = firestore;
|
||||
this._queryOptions = queryOptions || {};
|
||||
this._referencePath = path;
|
||||
}
|
||||
|
||||
get firestore(): Firestore {
|
||||
return this._firestore;
|
||||
}
|
||||
|
||||
endAt(...snapshotOrVarArgs: any[]): Query {
|
||||
const options = {
|
||||
...this._queryOptions,
|
||||
endAt: this._buildOrderByOption(snapshotOrVarArgs),
|
||||
};
|
||||
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
endBefore(...snapshotOrVarArgs: any[]): Query {
|
||||
const options = {
|
||||
...this._queryOptions,
|
||||
endBefore: this._buildOrderByOption(snapshotOrVarArgs),
|
||||
};
|
||||
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
get(): Promise<QuerySnapshot> {
|
||||
return getNativeModule(this._firestore)
|
||||
.collectionGet(
|
||||
this._referencePath.relativeName,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions
|
||||
)
|
||||
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
|
||||
}
|
||||
|
||||
limit(limit: number): Query {
|
||||
// TODO: Validation
|
||||
// validate.isInteger('n', n);
|
||||
|
||||
const options = {
|
||||
...this._queryOptions,
|
||||
limit,
|
||||
};
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
onSnapshot(
|
||||
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
|
||||
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
|
||||
onError?: ObserverOnError
|
||||
) {
|
||||
let observer: Observer;
|
||||
let queryListenOptions = {};
|
||||
// Called with: onNext, ?onError
|
||||
if (isFunction(optionsOrObserverOrOnNext)) {
|
||||
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
|
||||
throw new Error(
|
||||
'Query.onSnapshot failed: Second argument must be a valid function.'
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
observer = {
|
||||
next: optionsOrObserverOrOnNext,
|
||||
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.'
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
observer = {
|
||||
next: optionsOrObserverOrOnNext.next,
|
||||
error: optionsOrObserverOrOnNext.error,
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
'Query.onSnapshot failed: Observer.next must be a valid function.'
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
optionsOrObserverOrOnNext,
|
||||
'includeDocumentMetadataChanges'
|
||||
) ||
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
optionsOrObserverOrOnNext,
|
||||
'includeQueryMetadataChanges'
|
||||
)
|
||||
) {
|
||||
queryListenOptions = optionsOrObserverOrOnNext;
|
||||
// Called with: Options, onNext, ?onError
|
||||
if (isFunction(observerOrOnNextOrOnError)) {
|
||||
if (onError && !isFunction(onError)) {
|
||||
throw new Error(
|
||||
'Query.onSnapshot failed: Third argument must be a valid function.'
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
observer = {
|
||||
next: observerOrOnNextOrOnError,
|
||||
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 = {
|
||||
next: observerOrOnNextOrOnError.next,
|
||||
error: observerOrOnNextOrOnError.error,
|
||||
};
|
||||
} 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
|
||||
);
|
||||
observer.next(querySnapshot);
|
||||
};
|
||||
|
||||
// Listen to snapshot events
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
|
||||
// Listen for snapshot error events
|
||||
if (observer.error) {
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
|
||||
observer.error
|
||||
);
|
||||
}
|
||||
|
||||
// Add the native listener
|
||||
getNativeModule(this._firestore).collectionOnSnapshot(
|
||||
this._referencePath.relativeName,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions,
|
||||
listenerId,
|
||||
queryListenOptions
|
||||
);
|
||||
|
||||
// Return an unsubscribe method
|
||||
return this._offCollectionSnapshot.bind(this, listenerId, listener);
|
||||
}
|
||||
|
||||
orderBy(
|
||||
fieldPath: string | FieldPath,
|
||||
directionStr?: QueryDirection = 'asc'
|
||||
): Query {
|
||||
// TODO: Validation
|
||||
// validate.isFieldPath('fieldPath', fieldPath);
|
||||
// validate.isOptionalFieldOrder('directionStr', directionStr);
|
||||
|
||||
if (
|
||||
this._queryOptions.startAt ||
|
||||
this._queryOptions.startAfter ||
|
||||
this._queryOptions.endAt ||
|
||||
this._queryOptions.endBefore
|
||||
) {
|
||||
throw new Error(
|
||||
'Cannot specify an orderBy() constraint after calling ' +
|
||||
'startAt(), startAfter(), endBefore() or endAt().'
|
||||
);
|
||||
}
|
||||
|
||||
const newOrder: FieldOrder = {
|
||||
direction: DIRECTIONS[directionStr],
|
||||
fieldPath: buildNativeFieldPath(fieldPath),
|
||||
};
|
||||
const combinedOrders = this._fieldOrders.concat(newOrder);
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
this._fieldFilters,
|
||||
combinedOrders,
|
||||
this._queryOptions
|
||||
);
|
||||
}
|
||||
|
||||
startAfter(...snapshotOrVarArgs: any[]): Query {
|
||||
const options = {
|
||||
...this._queryOptions,
|
||||
startAfter: this._buildOrderByOption(snapshotOrVarArgs),
|
||||
};
|
||||
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
startAt(...snapshotOrVarArgs: any[]): Query {
|
||||
const options = {
|
||||
...this._queryOptions,
|
||||
startAt: this._buildOrderByOption(snapshotOrVarArgs),
|
||||
};
|
||||
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
where(
|
||||
fieldPath: string | FieldPath,
|
||||
opStr: QueryOperator,
|
||||
value: any
|
||||
): Query {
|
||||
// TODO: Validation
|
||||
// validate.isFieldPath('fieldPath', fieldPath);
|
||||
// validate.isFieldFilter('fieldFilter', opStr, value);
|
||||
const nativeValue = buildTypeMap(value);
|
||||
const newFilter: FieldFilter = {
|
||||
fieldPath: buildNativeFieldPath(fieldPath),
|
||||
operator: OPERATORS[opStr],
|
||||
value: nativeValue,
|
||||
};
|
||||
const combinedFilters = this._fieldFilters.concat(newFilter);
|
||||
return new Query(
|
||||
this.firestore,
|
||||
this._referencePath,
|
||||
combinedFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNALS
|
||||
*/
|
||||
|
||||
_buildOrderByOption(snapshotOrVarArgs: any[]) {
|
||||
// TODO: Validation
|
||||
let values;
|
||||
if (
|
||||
snapshotOrVarArgs.length === 1 &&
|
||||
snapshotOrVarArgs[0] instanceof DocumentSnapshot
|
||||
) {
|
||||
const docSnapshot: DocumentSnapshot = snapshotOrVarArgs[0];
|
||||
values = [];
|
||||
for (let i = 0; i < this._fieldOrders.length; i++) {
|
||||
const fieldOrder = this._fieldOrders[i];
|
||||
if (
|
||||
fieldOrder.fieldPath.type === 'string' &&
|
||||
fieldOrder.fieldPath.string
|
||||
) {
|
||||
values.push(docSnapshot.get(fieldOrder.fieldPath.string));
|
||||
} else if (fieldOrder.fieldPath.fieldpath) {
|
||||
const fieldPath = new FieldPath(...fieldOrder.fieldPath.fieldpath);
|
||||
values.push(docSnapshot.get(fieldPath));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values = snapshotOrVarArgs;
|
||||
}
|
||||
|
||||
return buildNativeArray(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove query snapshot listener
|
||||
* @param listener
|
||||
*/
|
||||
_offCollectionSnapshot(listenerId: string, listener: Function) {
|
||||
getLogger(this._firestore).info('Removing onQuerySnapshot listener');
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
getNativeModule(this._firestore).collectionOffSnapshot(
|
||||
this._referencePath.relativeName,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions,
|
||||
listenerId
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* QuerySnapshot representation wrapper
|
||||
*/
|
||||
import DocumentChange from './DocumentChange';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
|
||||
import type Firestore from './';
|
||||
import type {
|
||||
NativeDocumentChange,
|
||||
NativeDocumentSnapshot,
|
||||
SnapshotMetadata,
|
||||
} from './types';
|
||||
import type Query from './Query';
|
||||
|
||||
type NativeQuerySnapshot = {
|
||||
changes: NativeDocumentChange[],
|
||||
documents: NativeDocumentSnapshot[],
|
||||
metadata: SnapshotMetadata,
|
||||
};
|
||||
|
||||
/**
|
||||
* @class QuerySnapshot
|
||||
*/
|
||||
export default class QuerySnapshot {
|
||||
_changes: DocumentChange[];
|
||||
_docs: DocumentSnapshot[];
|
||||
_metadata: SnapshotMetadata;
|
||||
_query: Query;
|
||||
|
||||
constructor(
|
||||
firestore: Firestore,
|
||||
query: Query,
|
||||
nativeData: NativeQuerySnapshot
|
||||
) {
|
||||
this._changes = nativeData.changes.map(
|
||||
change => new DocumentChange(firestore, change)
|
||||
);
|
||||
this._docs = nativeData.documents.map(
|
||||
doc => new DocumentSnapshot(firestore, doc)
|
||||
);
|
||||
this._metadata = nativeData.metadata;
|
||||
this._query = query;
|
||||
}
|
||||
|
||||
get docChanges(): DocumentChange[] {
|
||||
return this._changes;
|
||||
}
|
||||
|
||||
get docs(): DocumentSnapshot[] {
|
||||
return this._docs;
|
||||
}
|
||||
|
||||
get empty(): boolean {
|
||||
return this._docs.length === 0;
|
||||
}
|
||||
|
||||
get metadata(): SnapshotMetadata {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
get query(): Query {
|
||||
return this._query;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this._docs.length;
|
||||
}
|
||||
|
||||
forEach(callback: DocumentSnapshot => any) {
|
||||
// TODO: Validation
|
||||
// validate.isFunction('callback', callback);
|
||||
|
||||
this._docs.forEach(doc => {
|
||||
callback(doc);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Firestore Transaction representation wrapper
|
||||
*/
|
||||
import { parseUpdateArgs } from './utils';
|
||||
import { buildNativeMap } from './utils/serialize';
|
||||
|
||||
import type Firestore from './';
|
||||
import type { TransactionMeta } from './TransactionHandler';
|
||||
import type DocumentReference from './DocumentReference';
|
||||
import DocumentSnapshot from './DocumentSnapshot';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
type Command = {
|
||||
type: 'set' | 'update' | 'delete',
|
||||
path: string,
|
||||
data?: { [string]: any },
|
||||
options?: SetOptions | {},
|
||||
};
|
||||
|
||||
type SetOptions = {
|
||||
merge: boolean,
|
||||
};
|
||||
|
||||
// TODO docs state all get requests must be made FIRST before any modifications
|
||||
// TODO so need to validate that
|
||||
|
||||
/**
|
||||
* @class Transaction
|
||||
*/
|
||||
export default class Transaction {
|
||||
_pendingResult: ?any;
|
||||
_firestore: Firestore;
|
||||
_meta: TransactionMeta;
|
||||
_commandBuffer: Array<Command>;
|
||||
|
||||
constructor(firestore: Firestore, meta: TransactionMeta) {
|
||||
this._meta = meta;
|
||||
this._commandBuffer = [];
|
||||
this._firestore = firestore;
|
||||
this._pendingResult = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* INTERNAL API
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Clears the command buffer and any pending result in prep for
|
||||
* the next transaction iteration attempt.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_prepare() {
|
||||
this._commandBuffer = [];
|
||||
this._pendingResult = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* PUBLIC API
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Reads the document referenced by the provided DocumentReference.
|
||||
*
|
||||
* @param documentRef DocumentReference A reference to the document to be retrieved. Value must not be null.
|
||||
*
|
||||
* @returns Promise<DocumentSnapshot>
|
||||
*/
|
||||
get(documentRef: DocumentReference): Promise<DocumentSnapshot> {
|
||||
// todo validate doc ref
|
||||
return getNativeModule(this._firestore)
|
||||
.transactionGetDocument(this._meta.id, documentRef.path)
|
||||
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to the document referred to by the provided DocumentReference.
|
||||
* If the document does not exist yet, it will be created. If you pass options,
|
||||
* the provided data can be merged into the existing document.
|
||||
*
|
||||
* @param documentRef DocumentReference A reference to the document to be created. Value must not be null.
|
||||
* @param data Object An object of the fields and values for the document.
|
||||
* @param options SetOptions An object to configure the set behavior.
|
||||
* Pass {merge: true} to only replace the values specified in the data argument.
|
||||
* Fields omitted will remain untouched.
|
||||
*
|
||||
* @returns {Transaction}
|
||||
*/
|
||||
set(
|
||||
documentRef: DocumentReference,
|
||||
data: Object,
|
||||
options?: SetOptions
|
||||
): Transaction {
|
||||
// todo validate doc ref
|
||||
// todo validate data is object
|
||||
this._commandBuffer.push({
|
||||
type: 'set',
|
||||
path: documentRef.path,
|
||||
data: buildNativeMap(data),
|
||||
options: options || {},
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates fields in the document referred to by this DocumentReference.
|
||||
* The update will fail if applied to a document that does not exist. Nested
|
||||
* fields can be updated by providing dot-separated field path strings or by providing FieldPath objects.
|
||||
*
|
||||
* @param documentRef DocumentReference A reference to the document to be updated. Value must not be null.
|
||||
* @param args any Either an object containing all of the fields and values to update,
|
||||
* or a series of arguments alternating between fields (as string or FieldPath
|
||||
* objects) and values.
|
||||
*
|
||||
* @returns {Transaction}
|
||||
*/
|
||||
update(documentRef: DocumentReference, ...args: Array<any>): Transaction {
|
||||
// todo validate doc ref
|
||||
const data = parseUpdateArgs(args, 'Transaction.update');
|
||||
this._commandBuffer.push({
|
||||
type: 'update',
|
||||
path: documentRef.path,
|
||||
data: buildNativeMap(data),
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the document referred to by the provided DocumentReference.
|
||||
*
|
||||
* @param documentRef DocumentReference A reference to the document to be deleted. Value must not be null.
|
||||
*
|
||||
* @returns {Transaction}
|
||||
*/
|
||||
delete(documentRef: DocumentReference): Transaction {
|
||||
// todo validate doc ref
|
||||
this._commandBuffer.push({
|
||||
type: 'delete',
|
||||
path: documentRef.path,
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Firestore Transaction representation wrapper
|
||||
*/
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import Transaction from './Transaction';
|
||||
import type Firestore from './';
|
||||
|
||||
let transactionId = 0;
|
||||
|
||||
/**
|
||||
* Uses the push id generator to create a transaction id
|
||||
* @returns {number}
|
||||
* @private
|
||||
*/
|
||||
const generateTransactionId = (): number => transactionId++;
|
||||
|
||||
export type TransactionMeta = {
|
||||
id: number,
|
||||
stack: string[],
|
||||
reject?: Function,
|
||||
resolve?: Function,
|
||||
transaction: Transaction,
|
||||
updateFunction: (transaction: Transaction) => Promise<any>,
|
||||
};
|
||||
|
||||
type TransactionEvent = {
|
||||
id: number,
|
||||
type: 'update' | 'error' | 'complete',
|
||||
error: ?{ code: string, message: string },
|
||||
};
|
||||
|
||||
/**
|
||||
* @class TransactionHandler
|
||||
*/
|
||||
export default class TransactionHandler {
|
||||
_firestore: Firestore;
|
||||
_pending: {
|
||||
[number]: {
|
||||
meta: TransactionMeta,
|
||||
transaction: Transaction,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(firestore: Firestore) {
|
||||
this._pending = {};
|
||||
this._firestore = firestore;
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this._firestore, 'firestore_transaction_event'),
|
||||
this._handleTransactionEvent.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* INTERNAL API
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a new transaction and start it natively.
|
||||
* @param updateFunction
|
||||
*/
|
||||
_add(
|
||||
updateFunction: (transaction: Transaction) => Promise<any>
|
||||
): Promise<any> {
|
||||
const id = generateTransactionId();
|
||||
// $FlowExpectedError: Transaction has to be populated
|
||||
const meta: TransactionMeta = {
|
||||
id,
|
||||
updateFunction,
|
||||
stack: new Error().stack
|
||||
.split('\n')
|
||||
.slice(4)
|
||||
.join('\n'),
|
||||
};
|
||||
|
||||
this._pending[id] = {
|
||||
meta,
|
||||
transaction: new Transaction(this._firestore, meta),
|
||||
};
|
||||
|
||||
// deferred promise
|
||||
return new Promise((resolve, reject) => {
|
||||
getNativeModule(this._firestore).transactionBegin(id);
|
||||
meta.resolve = r => {
|
||||
resolve(r);
|
||||
this._remove(id);
|
||||
};
|
||||
meta.reject = e => {
|
||||
reject(e);
|
||||
this._remove(id);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a local instance of a transaction meta
|
||||
*
|
||||
* @param id
|
||||
* @private
|
||||
*/
|
||||
_remove(id) {
|
||||
getNativeModule(this._firestore).transactionDispose(id);
|
||||
delete this._pending[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* EVENTS
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles incoming native transaction events and distributes to correct
|
||||
* internal handler by event.type
|
||||
*
|
||||
* @param event
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_handleTransactionEvent(event: TransactionEvent) {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (event.type) {
|
||||
case 'update':
|
||||
this._handleUpdate(event);
|
||||
break;
|
||||
case 'error':
|
||||
this._handleError(event);
|
||||
break;
|
||||
case 'complete':
|
||||
this._handleComplete(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming native transaction update events
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
async _handleUpdate(event: TransactionEvent) {
|
||||
const { id } = event;
|
||||
// abort if no longer exists js side
|
||||
if (!this._pending[id]) return this._remove(id);
|
||||
|
||||
const { meta, transaction } = this._pending[id];
|
||||
const { updateFunction, reject } = meta;
|
||||
|
||||
// clear any saved state from previous transaction runs
|
||||
transaction._prepare();
|
||||
|
||||
let finalError;
|
||||
let updateFailed;
|
||||
let pendingResult;
|
||||
|
||||
// run the users custom update functionality
|
||||
try {
|
||||
const possiblePromise = updateFunction(transaction);
|
||||
|
||||
// validate user has returned a promise in their update function
|
||||
// TODO must it actually return a promise? Can't find any usages of it without one...
|
||||
if (!possiblePromise || !possiblePromise.then) {
|
||||
finalError = new Error(
|
||||
'Update function for `firestore.runTransaction(updateFunction)` must return a Promise.'
|
||||
);
|
||||
} else {
|
||||
pendingResult = await possiblePromise;
|
||||
}
|
||||
} catch (exception) {
|
||||
// exception can still be falsey if user `Promise.reject();` 's with no args
|
||||
// so we track the exception with a updateFailed boolean to ensure no fall-through
|
||||
updateFailed = true;
|
||||
finalError = exception;
|
||||
}
|
||||
|
||||
// reject the final promise and remove from native
|
||||
// update is failed when either the users updateFunction
|
||||
// throws an error or rejects a promise
|
||||
if (updateFailed) {
|
||||
// $FlowExpectedError: Reject will always be present
|
||||
return reject(finalError);
|
||||
}
|
||||
|
||||
// capture the resolved result as we'll need this
|
||||
// to resolve the runTransaction() promise when
|
||||
// native emits that the transaction is final
|
||||
transaction._pendingResult = pendingResult;
|
||||
|
||||
// send the buffered update/set/delete commands for native to process
|
||||
return getNativeModule(this._firestore).transactionApplyBuffer(
|
||||
id,
|
||||
transaction._commandBuffer
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming native transaction error events
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_handleError(event: TransactionEvent) {
|
||||
const { id, error } = event;
|
||||
const { meta } = this._pending[id];
|
||||
|
||||
if (meta && error) {
|
||||
const { code, message } = error;
|
||||
// build a JS error and replace its stack
|
||||
// with the captured one at start of transaction
|
||||
// so it's actually relevant to the user
|
||||
const errorWithStack = new Error(message);
|
||||
// $FlowExpectedError: code is needed for Firebase errors
|
||||
errorWithStack.code = code;
|
||||
// $FlowExpectedError: stack should be a stack trace
|
||||
errorWithStack.stack = meta.stack;
|
||||
|
||||
// $FlowExpectedError: Reject will always be present
|
||||
meta.reject(errorWithStack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming native transaction complete events
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_handleComplete(event: TransactionEvent) {
|
||||
const { id } = event;
|
||||
const { meta, transaction } = this._pending[id];
|
||||
|
||||
if (meta) {
|
||||
const pendingResult = transaction._pendingResult;
|
||||
// $FlowExpectedError: Resolve will always be present
|
||||
meta.resolve(pendingResult);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* WriteBatch representation wrapper
|
||||
*/
|
||||
import { parseUpdateArgs } from './utils';
|
||||
import { buildNativeMap } from './utils/serialize';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type DocumentReference from './DocumentReference';
|
||||
import type Firestore from './';
|
||||
import type { SetOptions } from './types';
|
||||
|
||||
type DocumentWrite = {
|
||||
data?: Object,
|
||||
options?: Object,
|
||||
path: string,
|
||||
type: 'DELETE' | 'SET' | 'UPDATE',
|
||||
};
|
||||
|
||||
/**
|
||||
* @class WriteBatch
|
||||
*/
|
||||
export default class WriteBatch {
|
||||
_firestore: Firestore;
|
||||
_writes: DocumentWrite[];
|
||||
|
||||
constructor(firestore: Firestore) {
|
||||
this._firestore = firestore;
|
||||
this._writes = [];
|
||||
}
|
||||
|
||||
commit(): Promise<void> {
|
||||
return getNativeModule(this._firestore).documentBatch(this._writes);
|
||||
}
|
||||
|
||||
delete(docRef: DocumentReference): WriteBatch {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference('docRef', docRef);
|
||||
// validate.isOptionalPrecondition('deleteOptions', deleteOptions);
|
||||
this._writes.push({
|
||||
path: docRef.path,
|
||||
type: 'DELETE',
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
set(docRef: DocumentReference, data: Object, options?: SetOptions) {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference('docRef', docRef);
|
||||
// validate.isDocument('data', data);
|
||||
// validate.isOptionalPrecondition('options', writeOptions);
|
||||
const nativeData = buildNativeMap(data);
|
||||
this._writes.push({
|
||||
data: nativeData,
|
||||
options,
|
||||
path: docRef.path,
|
||||
type: 'SET',
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
update(docRef: DocumentReference, ...args: any[]): WriteBatch {
|
||||
// TODO: Validation
|
||||
// validate.isDocumentReference('docRef', docRef);
|
||||
const data = parseUpdateArgs(args, 'WriteBatch.update');
|
||||
this._writes.push({
|
||||
data: buildNativeMap(data),
|
||||
path: docRef.path,
|
||||
type: 'UPDATE',
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Firestore representation wrapper
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import CollectionReference from './CollectionReference';
|
||||
import DocumentReference from './DocumentReference';
|
||||
import FieldPath from './FieldPath';
|
||||
import FieldValue from './FieldValue';
|
||||
import GeoPoint from './GeoPoint';
|
||||
import Path from './Path';
|
||||
import WriteBatch from './WriteBatch';
|
||||
import TransactionHandler from './TransactionHandler';
|
||||
import Transaction from './Transaction';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
|
||||
import type DocumentSnapshot from './DocumentSnapshot';
|
||||
import type App from '../core/app';
|
||||
import type QuerySnapshot from './QuerySnapshot';
|
||||
|
||||
type CollectionSyncEvent = {
|
||||
appName: string,
|
||||
querySnapshot?: QuerySnapshot,
|
||||
error?: Object,
|
||||
listenerId: string,
|
||||
path: string,
|
||||
};
|
||||
|
||||
type DocumentSyncEvent = {
|
||||
appName: string,
|
||||
documentSnapshot?: DocumentSnapshot,
|
||||
error?: Object,
|
||||
listenerId: string,
|
||||
path: string,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'firestore_transaction_event',
|
||||
'firestore_document_sync_event',
|
||||
'firestore_collection_sync_event',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseFirestore';
|
||||
export const NAMESPACE = 'firestore';
|
||||
|
||||
/**
|
||||
* @class Firestore
|
||||
*/
|
||||
export default class Firestore extends ModuleBase {
|
||||
_referencePath: Path;
|
||||
_transactionHandler: TransactionHandler;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
this._referencePath = new Path([]);
|
||||
this._transactionHandler = new TransactionHandler(this);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onCollectionSnapshot
|
||||
getAppEventName(this, 'firestore_collection_sync_event'),
|
||||
this._onCollectionSyncEvent.bind(this)
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onDocumentSnapshot
|
||||
getAppEventName(this, 'firestore_document_sync_event'),
|
||||
this._onDocumentSyncEvent.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* PUBLIC API
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a write batch, used for performing multiple writes as a single atomic operation.
|
||||
*
|
||||
* @returns {WriteBatch}
|
||||
*/
|
||||
batch(): WriteBatch {
|
||||
return new WriteBatch(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a CollectionReference instance that refers to the collection at the specified path.
|
||||
*
|
||||
* @param collectionPath
|
||||
* @returns {CollectionReference}
|
||||
*/
|
||||
collection(collectionPath: string): CollectionReference {
|
||||
const path = this._referencePath.child(collectionPath);
|
||||
if (!path.isCollection) {
|
||||
throw new Error('Argument "collectionPath" must point to a collection.');
|
||||
}
|
||||
|
||||
return new CollectionReference(this, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a DocumentReference instance that refers to the document at the specified path.
|
||||
*
|
||||
* @param documentPath
|
||||
* @returns {DocumentReference}
|
||||
*/
|
||||
doc(documentPath: string): DocumentReference {
|
||||
const path = this._referencePath.child(documentPath);
|
||||
if (!path.isDocument) {
|
||||
throw new Error('Argument "documentPath" must point to a document.');
|
||||
}
|
||||
|
||||
return new DocumentReference(this, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* UNSUPPORTED
|
||||
* -------------
|
||||
*/
|
||||
|
||||
setLogLevel(): void {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'firestore',
|
||||
'setLogLevel'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
enableNetwork(): void {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'firestore',
|
||||
'enableNetwork'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
disableNetwork(): void {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'firestore',
|
||||
'disableNetwork'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* MISC
|
||||
* -------------
|
||||
*/
|
||||
|
||||
enablePersistence(): Promise<void> {
|
||||
throw new Error('Persistence is enabled by default on the Firestore SDKs');
|
||||
}
|
||||
|
||||
settings(): void {
|
||||
throw new Error('firebase.firestore().settings() coming soon');
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* INTERNALS
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal collection sync listener
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onCollectionSyncEvent(event: CollectionSyncEvent) {
|
||||
if (event.error) {
|
||||
SharedEventEmitter.emit(
|
||||
getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`),
|
||||
event.error
|
||||
);
|
||||
} else {
|
||||
SharedEventEmitter.emit(
|
||||
getAppEventName(this, `onQuerySnapshot:${event.listenerId}`),
|
||||
event.querySnapshot
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal document sync listener
|
||||
*
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onDocumentSyncEvent(event: DocumentSyncEvent) {
|
||||
if (event.error) {
|
||||
SharedEventEmitter.emit(
|
||||
getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`),
|
||||
event.error
|
||||
);
|
||||
} else {
|
||||
SharedEventEmitter.emit(
|
||||
getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`),
|
||||
event.documentSnapshot
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
FieldPath,
|
||||
FieldValue,
|
||||
GeoPoint,
|
||||
enableLogging(enabled: boolean) {
|
||||
if (NativeModules[MODULE_NAME]) {
|
||||
NativeModules[MODULE_NAME].enableLogging(enabled);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* @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,
|
||||
};
|
|
@ -1,74 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
import FieldPath from '../FieldPath';
|
||||
import { isObject, isString } from '../../../utils';
|
||||
|
||||
const buildFieldPathData = (segments: string[], value: any): Object => {
|
||||
if (segments.length === 1) {
|
||||
return {
|
||||
[segments[0]]: value,
|
||||
};
|
||||
}
|
||||
return {
|
||||
[segments[0]]: buildFieldPathData(segments.slice(1), value),
|
||||
};
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const mergeFieldPathData = (
|
||||
data: Object,
|
||||
segments: string[],
|
||||
value: any
|
||||
): Object => {
|
||||
if (segments.length === 1) {
|
||||
return {
|
||||
...data,
|
||||
[segments[0]]: value,
|
||||
};
|
||||
} else if (data[segments[0]]) {
|
||||
return {
|
||||
...data,
|
||||
[segments[0]]: mergeFieldPathData(
|
||||
data[segments[0]],
|
||||
segments.slice(1),
|
||||
value
|
||||
),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...data,
|
||||
[segments[0]]: buildFieldPathData(segments.slice(1), value),
|
||||
};
|
||||
};
|
||||
|
||||
export const parseUpdateArgs = (args: any[], methodName: string) => {
|
||||
let data = {};
|
||||
if (args.length === 1) {
|
||||
if (!isObject(args[0])) {
|
||||
throw new Error(
|
||||
`${methodName} failed: If using a single update argument, it must be an object.`
|
||||
);
|
||||
}
|
||||
[data] = args;
|
||||
} else if (args.length % 2 === 1) {
|
||||
throw new Error(
|
||||
`${methodName} failed: The update arguments must be 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)) {
|
||||
data[key] = value;
|
||||
} else if (key instanceof FieldPath) {
|
||||
data = mergeFieldPathData(data, key._segments, value);
|
||||
} else {
|
||||
throw new Error(
|
||||
`${methodName} failed: Argument at index ${i} must be a string or FieldPath`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
|
@ -1,162 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import DocumentReference from '../DocumentReference';
|
||||
import { DOCUMENT_ID } from '../FieldPath';
|
||||
import {
|
||||
DELETE_FIELD_VALUE,
|
||||
SERVER_TIMESTAMP_FIELD_VALUE,
|
||||
} from '../FieldValue';
|
||||
import GeoPoint from '../GeoPoint';
|
||||
import Path from '../Path';
|
||||
import { typeOf } from '../../../utils';
|
||||
|
||||
import type Firestore from '../';
|
||||
import type { NativeTypeMap } from '../types';
|
||||
|
||||
/*
|
||||
* Functions that build up the data needed to represent
|
||||
* the different types available within Firestore
|
||||
* for transmission to the native side
|
||||
*/
|
||||
|
||||
export const buildNativeMap = (data: Object): { [string]: NativeTypeMap } => {
|
||||
const nativeData = {};
|
||||
if (data) {
|
||||
Object.keys(data).forEach(key => {
|
||||
const typeMap = buildTypeMap(data[key]);
|
||||
if (typeMap) {
|
||||
nativeData[key] = typeMap;
|
||||
}
|
||||
});
|
||||
}
|
||||
return nativeData;
|
||||
};
|
||||
|
||||
export const buildNativeArray = (array: Object[]): NativeTypeMap[] => {
|
||||
const nativeArray = [];
|
||||
if (array) {
|
||||
array.forEach(value => {
|
||||
const typeMap = buildTypeMap(value);
|
||||
if (typeMap) {
|
||||
nativeArray.push(typeMap);
|
||||
}
|
||||
});
|
||||
}
|
||||
return nativeArray;
|
||||
};
|
||||
|
||||
export const buildTypeMap = (value: any): NativeTypeMap | null => {
|
||||
const type = typeOf(value);
|
||||
if (value === null || value === undefined || Number.isNaN(value)) {
|
||||
return {
|
||||
type: 'null',
|
||||
value: null,
|
||||
};
|
||||
} else if (value === DELETE_FIELD_VALUE) {
|
||||
return {
|
||||
type: 'fieldvalue',
|
||||
value: 'delete',
|
||||
};
|
||||
} else if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
|
||||
return {
|
||||
type: 'fieldvalue',
|
||||
value: 'timestamp',
|
||||
};
|
||||
} else if (value === DOCUMENT_ID) {
|
||||
return {
|
||||
type: 'documentid',
|
||||
value: null,
|
||||
};
|
||||
} else if (type === 'boolean' || type === 'number' || type === 'string') {
|
||||
return {
|
||||
type,
|
||||
value,
|
||||
};
|
||||
} else if (type === 'array') {
|
||||
return {
|
||||
type,
|
||||
value: buildNativeArray(value),
|
||||
};
|
||||
} else if (type === 'object') {
|
||||
if (value instanceof DocumentReference) {
|
||||
return {
|
||||
type: 'reference',
|
||||
value: value.path,
|
||||
};
|
||||
} else if (value instanceof GeoPoint) {
|
||||
return {
|
||||
type: 'geopoint',
|
||||
value: {
|
||||
latitude: value.latitude,
|
||||
longitude: value.longitude,
|
||||
},
|
||||
};
|
||||
} else if (value instanceof Date) {
|
||||
return {
|
||||
type: 'date',
|
||||
value: value.getTime(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: 'object',
|
||||
value: buildNativeMap(value),
|
||||
};
|
||||
}
|
||||
console.warn(`Unknown data type received ${type}`);
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions that parse the received from the native
|
||||
* side and converts to the correct Firestore JS types
|
||||
*/
|
||||
|
||||
export const parseNativeMap = (
|
||||
firestore: Firestore,
|
||||
nativeData: { [string]: NativeTypeMap }
|
||||
): Object | void => {
|
||||
let data;
|
||||
if (nativeData) {
|
||||
data = {};
|
||||
Object.keys(nativeData).forEach(key => {
|
||||
data[key] = parseTypeMap(firestore, nativeData[key]);
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const parseNativeArray = (
|
||||
firestore: Firestore,
|
||||
nativeArray: NativeTypeMap[]
|
||||
): any[] => {
|
||||
const array = [];
|
||||
if (nativeArray) {
|
||||
nativeArray.forEach(typeMap => {
|
||||
array.push(parseTypeMap(firestore, typeMap));
|
||||
});
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
const parseTypeMap = (firestore: Firestore, typeMap: NativeTypeMap): any => {
|
||||
const { type, value } = typeMap;
|
||||
if (type === 'null') {
|
||||
return null;
|
||||
} else if (type === 'boolean' || type === 'number' || type === 'string') {
|
||||
return value;
|
||||
} else if (type === 'array') {
|
||||
return parseNativeArray(firestore, value);
|
||||
} else if (type === 'object') {
|
||||
return parseNativeMap(firestore, value);
|
||||
} else if (type === 'reference') {
|
||||
return new DocumentReference(firestore, Path.fromName(value));
|
||||
} else if (type === 'geopoint') {
|
||||
return new GeoPoint(value.latitude, value.longitude);
|
||||
} else if (type === 'date') {
|
||||
return new Date(value);
|
||||
}
|
||||
console.warn(`Unknown data type received ${type}`);
|
||||
return value;
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Instance ID representation wrapper
|
||||
*/
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseInstanceId';
|
||||
export const NAMESPACE = 'instanceid';
|
||||
|
||||
export default class InstanceId extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
hasShards: false,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
delete(): Promise<void> {
|
||||
return getNativeModule(this).delete();
|
||||
}
|
||||
|
||||
get(): Promise<string> {
|
||||
return getNativeModule(this).get();
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidInvitation representation wrapper
|
||||
*/
|
||||
import type Invitation from './Invitation';
|
||||
import type { NativeAndroidInvitation } from './types';
|
||||
|
||||
export default class AndroidInvitation {
|
||||
_additionalReferralParameters: { [string]: string } | void;
|
||||
_emailHtmlContent: string | void;
|
||||
_emailSubject: string | void;
|
||||
_googleAnalyticsTrackingId: string | void;
|
||||
_invitation: Invitation;
|
||||
|
||||
constructor(invitation: Invitation) {
|
||||
this._invitation = invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param additionalReferralParameters
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAdditionalReferralParameters(additionalReferralParameters: {
|
||||
[string]: string,
|
||||
}): Invitation {
|
||||
this._additionalReferralParameters = additionalReferralParameters;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailHtmlContent
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setEmailHtmlContent(emailHtmlContent: string): Invitation {
|
||||
this._emailHtmlContent = emailHtmlContent;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param emailSubject
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setEmailSubject(emailSubject: string): Invitation {
|
||||
this._emailSubject = emailSubject;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param googleAnalyticsTrackingId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setGoogleAnalyticsTrackingId(googleAnalyticsTrackingId: string): Invitation {
|
||||
this._googleAnalyticsTrackingId = googleAnalyticsTrackingId;
|
||||
return this._invitation;
|
||||
}
|
||||
|
||||
build(): NativeAndroidInvitation {
|
||||
return {
|
||||
additionalReferralParameters: this._additionalReferralParameters,
|
||||
emailHtmlContent: this._emailHtmlContent,
|
||||
emailSubject: this._emailSubject,
|
||||
googleAnalyticsTrackingId: this._googleAnalyticsTrackingId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Invitation representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidInvitation from './AndroidInvitation';
|
||||
|
||||
import type { NativeInvitation } from './types';
|
||||
|
||||
export default class Invitation {
|
||||
_android: AndroidInvitation;
|
||||
_androidClientId: string | void;
|
||||
_androidMinimumVersionCode: number | void;
|
||||
_callToActionText: string | void;
|
||||
_customImage: string | void;
|
||||
_deepLink: string | void;
|
||||
_iosClientId: string | void;
|
||||
_message: string;
|
||||
_title: string;
|
||||
|
||||
constructor(title: string, message: string) {
|
||||
this._android = new AndroidInvitation(this);
|
||||
this._message = message;
|
||||
this._title = title;
|
||||
}
|
||||
|
||||
get android(): AndroidInvitation {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param androidClientId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAndroidClientId(androidClientId: string): Invitation {
|
||||
this._androidClientId = androidClientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param androidMinimumVersionCode
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setAndroidMinimumVersionCode(androidMinimumVersionCode: number): Invitation {
|
||||
this._androidMinimumVersionCode = androidMinimumVersionCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param callToActionText
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setCallToActionText(callToActionText: string): Invitation {
|
||||
this._callToActionText = callToActionText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param customImage
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setCustomImage(customImage: string): Invitation {
|
||||
this._customImage = customImage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param deepLink
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setDeepLink(deepLink: string): Invitation {
|
||||
this._deepLink = deepLink;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iosClientId
|
||||
* @returns {Invitation}
|
||||
*/
|
||||
setIOSClientId(iosClientId: string): Invitation {
|
||||
this._iosClientId = iosClientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeInvitation {
|
||||
if (!this._message) {
|
||||
throw new Error('Invitation: Missing required `message` property');
|
||||
} else if (!this._title) {
|
||||
throw new Error('Invitation: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
android: Platform.OS === 'android' ? this._android.build() : undefined,
|
||||
androidClientId: this._androidClientId,
|
||||
androidMinimumVersionCode: this._androidMinimumVersionCode,
|
||||
callToActionText: this._callToActionText,
|
||||
customImage: this._customImage,
|
||||
deepLink: this._deepLink,
|
||||
iosClientId: this._iosClientId,
|
||||
message: this._message,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Invites representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import Invitation from './Invitation';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseInvites';
|
||||
export const NAMESPACE = 'invites';
|
||||
const NATIVE_EVENTS = ['invites_invitation_received'];
|
||||
|
||||
type InvitationOpen = {
|
||||
deepLink: string,
|
||||
invitationId: string,
|
||||
};
|
||||
|
||||
export default class Invites extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
hasShards: false,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'invites_invitation_received',
|
||||
(invitation: InvitationOpen) => {
|
||||
SharedEventEmitter.emit('onInvitation', invitation);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the invitation that triggered application open
|
||||
* @returns {Promise.<Object>}
|
||||
*/
|
||||
getInitialInvitation(): Promise<string> {
|
||||
return getNativeModule(this).getInitialInvitation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to invites
|
||||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onInvitation(listener: InvitationOpen => any) {
|
||||
getLogger(this).info('Creating onInvitation listener');
|
||||
|
||||
SharedEventEmitter.addListener('onInvitation', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onInvitation listener');
|
||||
SharedEventEmitter.removeListener('onInvitation', listener);
|
||||
};
|
||||
}
|
||||
|
||||
sendInvitation(invitation: Invitation): Promise<string[]> {
|
||||
if (!(invitation instanceof Invitation)) {
|
||||
throw new Error(
|
||||
`Invites:sendInvitation expects an 'Invitation' but got type ${typeof invitation}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).sendInvitation(invitation.build());
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Invitation,
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type NativeAndroidInvitation = {|
|
||||
additionalReferralParameters?: { [string]: string },
|
||||
emailHtmlContent?: string,
|
||||
emailSubject?: string,
|
||||
googleAnalyticsTrackingId?: string,
|
||||
|};
|
||||
|
||||
export type NativeInvitation = {|
|
||||
android?: NativeAndroidInvitation,
|
||||
androidClientId?: string,
|
||||
androidMinimumVersionCode?: number,
|
||||
callToActionText?: string,
|
||||
customImage?: string,
|
||||
deepLink?: string,
|
||||
iosClientId?: string,
|
||||
message: string,
|
||||
title: string,
|
||||
|};
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AnalyticsParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeAnalyticsParameters } from './types';
|
||||
|
||||
export default class AnalyticsParameters {
|
||||
_campaign: string | void;
|
||||
_content: string | void;
|
||||
_link: DynamicLink;
|
||||
_medium: string | void;
|
||||
_source: string | void;
|
||||
_term: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param campaign
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setCampaign(campaign: string): DynamicLink {
|
||||
this._campaign = campaign;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param content
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setContent(content: string): DynamicLink {
|
||||
this._content = content;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param medium
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setMedium(medium: string): DynamicLink {
|
||||
this._medium = medium;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param source
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setSource(source: string): DynamicLink {
|
||||
this._source = source;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param term
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setTerm(term: string): DynamicLink {
|
||||
this._term = term;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeAnalyticsParameters {
|
||||
return {
|
||||
campaign: this._campaign,
|
||||
content: this._content,
|
||||
medium: this._medium,
|
||||
source: this._source,
|
||||
term: this._term,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeAndroidParameters } from './types';
|
||||
|
||||
export default class AndroidParameters {
|
||||
_fallbackUrl: string | void;
|
||||
_link: DynamicLink;
|
||||
_minimumVersion: number | void;
|
||||
_packageName: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fallbackUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setFallbackUrl(fallbackUrl: string): DynamicLink {
|
||||
this._fallbackUrl = fallbackUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param minimumVersion
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setMinimumVersion(minimumVersion: number): DynamicLink {
|
||||
this._minimumVersion = minimumVersion;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param packageName
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setPackageName(packageName: string): DynamicLink {
|
||||
this._packageName = packageName;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeAndroidParameters {
|
||||
if ((this._fallbackUrl || this._minimumVersion) && !this._packageName) {
|
||||
throw new Error(
|
||||
'AndroidParameters: Missing required `packageName` property'
|
||||
);
|
||||
}
|
||||
return {
|
||||
fallbackUrl: this._fallbackUrl,
|
||||
minimumVersion: this._minimumVersion,
|
||||
packageName: this._packageName,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* DynamicLink representation wrapper
|
||||
*/
|
||||
import AnalyticsParameters from './AnalyticsParameters';
|
||||
import AndroidParameters from './AndroidParameters';
|
||||
import IOSParameters from './IOSParameters';
|
||||
import ITunesParameters from './ITunesParameters';
|
||||
import NavigationParameters from './NavigationParameters';
|
||||
import SocialParameters from './SocialParameters';
|
||||
|
||||
import type { NativeDynamicLink } from './types';
|
||||
|
||||
export default class DynamicLink {
|
||||
_analytics: AnalyticsParameters;
|
||||
_android: AndroidParameters;
|
||||
_dynamicLinkDomain: string;
|
||||
_ios: IOSParameters;
|
||||
_itunes: ITunesParameters;
|
||||
_link: string;
|
||||
_navigation: NavigationParameters;
|
||||
_social: SocialParameters;
|
||||
|
||||
constructor(link: string, dynamicLinkDomain: string) {
|
||||
this._analytics = new AnalyticsParameters(this);
|
||||
this._android = new AndroidParameters(this);
|
||||
this._dynamicLinkDomain = dynamicLinkDomain;
|
||||
this._ios = new IOSParameters(this);
|
||||
this._itunes = new ITunesParameters(this);
|
||||
this._link = link;
|
||||
this._navigation = new NavigationParameters(this);
|
||||
this._social = new SocialParameters(this);
|
||||
}
|
||||
|
||||
get analytics(): AnalyticsParameters {
|
||||
return this._analytics;
|
||||
}
|
||||
|
||||
get android(): AndroidParameters {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
get ios(): IOSParameters {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
get itunes(): ITunesParameters {
|
||||
return this._itunes;
|
||||
}
|
||||
|
||||
get navigation(): NavigationParameters {
|
||||
return this._navigation;
|
||||
}
|
||||
|
||||
get social(): SocialParameters {
|
||||
return this._social;
|
||||
}
|
||||
|
||||
build(): NativeDynamicLink {
|
||||
if (!this._link) {
|
||||
throw new Error('DynamicLink: Missing required `link` property');
|
||||
} else if (!this._dynamicLinkDomain) {
|
||||
throw new Error(
|
||||
'DynamicLink: Missing required `dynamicLinkDomain` property'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
analytics: this._analytics.build(),
|
||||
android: this._android.build(),
|
||||
dynamicLinkDomain: this._dynamicLinkDomain,
|
||||
ios: this._ios.build(),
|
||||
itunes: this._itunes.build(),
|
||||
link: this._link,
|
||||
navigation: this._navigation.build(),
|
||||
social: this._social.build(),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* IOSParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeIOSParameters } from './types';
|
||||
|
||||
export default class IOSParameters {
|
||||
_appStoreId: string | void;
|
||||
_bundleId: string | void;
|
||||
_customScheme: string | void;
|
||||
_fallbackUrl: string | void;
|
||||
_iPadBundleId: string | void;
|
||||
_iPadFallbackUrl: string | void;
|
||||
_link: DynamicLink;
|
||||
_minimumVersion: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param appStoreId
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setAppStoreId(appStoreId: string): DynamicLink {
|
||||
this._appStoreId = appStoreId;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bundleId
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setBundleId(bundleId: string): DynamicLink {
|
||||
this._bundleId = bundleId;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param customScheme
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setCustomScheme(customScheme: string): DynamicLink {
|
||||
this._customScheme = customScheme;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fallbackUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setFallbackUrl(fallbackUrl: string): DynamicLink {
|
||||
this._fallbackUrl = fallbackUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iPadBundleId
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setIPadBundleId(iPadBundleId: string): DynamicLink {
|
||||
this._iPadBundleId = iPadBundleId;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param iPadFallbackUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setIPadFallbackUrl(iPadFallbackUrl: string): DynamicLink {
|
||||
this._iPadFallbackUrl = iPadFallbackUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param minimumVersion
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setMinimumVersion(minimumVersion: string): DynamicLink {
|
||||
this._minimumVersion = minimumVersion;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeIOSParameters {
|
||||
if (
|
||||
(this._appStoreId ||
|
||||
this._customScheme ||
|
||||
this._fallbackUrl ||
|
||||
this._iPadBundleId ||
|
||||
this._iPadFallbackUrl ||
|
||||
this._minimumVersion) &&
|
||||
!this._bundleId
|
||||
) {
|
||||
throw new Error('IOSParameters: Missing required `bundleId` property');
|
||||
}
|
||||
return {
|
||||
appStoreId: this._appStoreId,
|
||||
bundleId: this._bundleId,
|
||||
customScheme: this._customScheme,
|
||||
fallbackUrl: this._fallbackUrl,
|
||||
iPadBundleId: this._iPadBundleId,
|
||||
iPadFallbackUrl: this._iPadFallbackUrl,
|
||||
minimumVersion: this._minimumVersion,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* ITunesParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeITunesParameters } from './types';
|
||||
|
||||
export default class ITunesParameters {
|
||||
_affiliateToken: string | void;
|
||||
_campaignToken: string | void;
|
||||
_link: DynamicLink;
|
||||
_providerToken: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param affiliateToken
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setAffiliateToken(affiliateToken: string): DynamicLink {
|
||||
this._affiliateToken = affiliateToken;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param campaignToken
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setCampaignToken(campaignToken: string): DynamicLink {
|
||||
this._campaignToken = campaignToken;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerToken
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setProviderToken(providerToken: string): DynamicLink {
|
||||
this._providerToken = providerToken;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeITunesParameters {
|
||||
return {
|
||||
affiliateToken: this._affiliateToken,
|
||||
campaignToken: this._campaignToken,
|
||||
providerToken: this._providerToken,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* NavigationParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeNavigationParameters } from './types';
|
||||
|
||||
export default class NavigationParameters {
|
||||
_forcedRedirectEnabled: string | void;
|
||||
_link: DynamicLink;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param forcedRedirectEnabled
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setForcedRedirectEnabled(forcedRedirectEnabled: string): DynamicLink {
|
||||
this._forcedRedirectEnabled = forcedRedirectEnabled;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeNavigationParameters {
|
||||
return {
|
||||
forcedRedirectEnabled: this._forcedRedirectEnabled,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* SocialParameters representation wrapper
|
||||
*/
|
||||
import type DynamicLink from './DynamicLink';
|
||||
import type { NativeSocialParameters } from './types';
|
||||
|
||||
export default class SocialParameters {
|
||||
_descriptionText: string | void;
|
||||
_imageUrl: string | void;
|
||||
_link: DynamicLink;
|
||||
_title: string | void;
|
||||
|
||||
constructor(link: DynamicLink) {
|
||||
this._link = link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param descriptionText
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setDescriptionText(descriptionText: string): DynamicLink {
|
||||
this._descriptionText = descriptionText;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param imageUrl
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setImageUrl(imageUrl: string): DynamicLink {
|
||||
this._imageUrl = imageUrl;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param title
|
||||
* @returns {DynamicLink}
|
||||
*/
|
||||
setTitle(title: string): DynamicLink {
|
||||
this._title = title;
|
||||
return this._link;
|
||||
}
|
||||
|
||||
build(): NativeSocialParameters {
|
||||
return {
|
||||
descriptionText: this._descriptionText,
|
||||
imageUrl: this._imageUrl,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Dynamic Links representation wrapper
|
||||
*/
|
||||
import DynamicLink from './DynamicLink';
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
const NATIVE_EVENTS = ['links_link_received'];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseLinks';
|
||||
export const NAMESPACE = 'links';
|
||||
|
||||
/**
|
||||
* @class Links
|
||||
*/
|
||||
export default class Links extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'links_link_received',
|
||||
(link: string) => {
|
||||
SharedEventEmitter.emit('onLink', link);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create long Dynamic Link from parameters
|
||||
* @param parameters
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
createDynamicLink(link: DynamicLink): Promise<string> {
|
||||
if (!(link instanceof DynamicLink)) {
|
||||
throw new Error(
|
||||
`Links:createDynamicLink expects a 'DynamicLink' but got type ${typeof link}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).createDynamicLink(link.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create short Dynamic Link from parameters
|
||||
* @param parameters
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
createShortDynamicLink(
|
||||
link: DynamicLink,
|
||||
type?: 'SHORT' | 'UNGUESSABLE'
|
||||
): Promise<String> {
|
||||
if (!(link instanceof DynamicLink)) {
|
||||
throw new Error(
|
||||
`Links:createShortDynamicLink expects a 'DynamicLink' but got type ${typeof link}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).createShortDynamicLink(link.build(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the link that triggered application open
|
||||
* @returns {Promise.<String>}
|
||||
*/
|
||||
getInitialLink(): Promise<string> {
|
||||
return getNativeModule(this).getInitialLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to dynamic links
|
||||
* @param listener
|
||||
* @returns {Function}
|
||||
*/
|
||||
onLink(listener: string => any): () => any {
|
||||
getLogger(this).info('Creating onLink listener');
|
||||
|
||||
SharedEventEmitter.addListener('onLink', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onLink listener');
|
||||
SharedEventEmitter.removeListener('onLink', listener);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,53 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type NativeAnalyticsParameters = {|
|
||||
campaign?: string,
|
||||
content?: string,
|
||||
medium?: string,
|
||||
source?: string,
|
||||
term?: string,
|
||||
|};
|
||||
|
||||
export type NativeAndroidParameters = {|
|
||||
fallbackUrl?: string,
|
||||
minimumVersion?: number,
|
||||
packageName?: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSParameters = {|
|
||||
appStoreId?: string,
|
||||
bundleId?: string,
|
||||
customScheme?: string,
|
||||
fallbackUrl?: string,
|
||||
iPadBundleId?: string,
|
||||
iPadFallbackUrl?: string,
|
||||
minimumVersion?: string,
|
||||
|};
|
||||
|
||||
export type NativeITunesParameters = {|
|
||||
affiliateToken?: string,
|
||||
campaignToken?: string,
|
||||
providerToken?: string,
|
||||
|};
|
||||
|
||||
export type NativeNavigationParameters = {|
|
||||
forcedRedirectEnabled?: string,
|
||||
|};
|
||||
|
||||
export type NativeSocialParameters = {|
|
||||
descriptionText?: string,
|
||||
imageUrl?: string,
|
||||
title?: string,
|
||||
|};
|
||||
|
||||
export type NativeDynamicLink = {|
|
||||
analytics: NativeAnalyticsParameters,
|
||||
android: NativeAndroidParameters,
|
||||
dynamicLinkDomain: string,
|
||||
ios: NativeIOSParameters,
|
||||
itunes: NativeITunesParameters,
|
||||
link: string,
|
||||
navigation: NativeNavigationParameters,
|
||||
social: NativeSocialParameters,
|
||||
|};
|
|
@ -1,155 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* RemoteMessage representation wrapper
|
||||
*/
|
||||
import { isObject, generatePushID } from './../../utils';
|
||||
|
||||
import type {
|
||||
NativeInboundRemoteMessage,
|
||||
NativeOutboundRemoteMessage,
|
||||
} from './types';
|
||||
|
||||
export default class RemoteMessage {
|
||||
_collapseKey: string | void;
|
||||
_data: { [string]: string };
|
||||
_from: string | void;
|
||||
_messageId: string;
|
||||
_messageType: string | void;
|
||||
_sentTime: number | void;
|
||||
_to: string;
|
||||
_ttl: number;
|
||||
|
||||
constructor(inboundMessage?: NativeInboundRemoteMessage) {
|
||||
if (inboundMessage) {
|
||||
this._collapseKey = inboundMessage.collapseKey;
|
||||
this._data = inboundMessage.data;
|
||||
this._from = inboundMessage.from;
|
||||
this._messageId = inboundMessage.messageId;
|
||||
this._messageType = inboundMessage.messageType;
|
||||
this._sentTime = inboundMessage.sentTime;
|
||||
}
|
||||
// defaults
|
||||
this._data = this._data || {};
|
||||
// TODO: Is this the best way to generate an ID?
|
||||
this._messageId = this._messageId || generatePushID();
|
||||
this._ttl = 3600;
|
||||
}
|
||||
|
||||
get collapseKey(): ?string {
|
||||
return this._collapseKey;
|
||||
}
|
||||
|
||||
get data(): { [string]: string } {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get from(): ?string {
|
||||
return this._from;
|
||||
}
|
||||
|
||||
get messageId(): ?string {
|
||||
return this._messageId;
|
||||
}
|
||||
|
||||
get messageType(): ?string {
|
||||
return this._messageType;
|
||||
}
|
||||
|
||||
get sentTime(): ?number {
|
||||
return this._sentTime;
|
||||
}
|
||||
|
||||
get to(): ?string {
|
||||
return this._to;
|
||||
}
|
||||
|
||||
get ttl(): ?number {
|
||||
return this._ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param collapseKey
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setCollapseKey(collapseKey: string): RemoteMessage {
|
||||
this._collapseKey = collapseKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setData(data: { [string]: string } = {}) {
|
||||
if (!isObject(data)) {
|
||||
throw new Error(
|
||||
`RemoteMessage:setData expects an object but got type '${typeof data}'.`
|
||||
);
|
||||
}
|
||||
this._data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param messageId
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setMessageId(messageId: string): RemoteMessage {
|
||||
this._messageId = messageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param messageType
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setMessageType(messageType: string): RemoteMessage {
|
||||
this._messageType = messageType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param to
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setTo(to: string): RemoteMessage {
|
||||
this._to = to;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ttl
|
||||
* @returns {RemoteMessage}
|
||||
*/
|
||||
setTtl(ttl: number): RemoteMessage {
|
||||
this._ttl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeOutboundRemoteMessage {
|
||||
if (!this._data) {
|
||||
throw new Error('RemoteMessage: Missing required `data` property');
|
||||
} else if (!this._messageId) {
|
||||
throw new Error('RemoteMessage: Missing required `messageId` property');
|
||||
} else if (!this._to) {
|
||||
throw new Error('RemoteMessage: Missing required `to` property');
|
||||
} else if (!this._ttl) {
|
||||
throw new Error('RemoteMessage: Missing required `ttl` property');
|
||||
}
|
||||
|
||||
return {
|
||||
collapseKey: this._collapseKey,
|
||||
data: this._data,
|
||||
messageId: this._messageId,
|
||||
messageType: this._messageType,
|
||||
to: this._to,
|
||||
ttl: this._ttl,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Messaging (FCM) representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { isFunction, isObject } from '../../utils';
|
||||
import RemoteMessage from './RemoteMessage';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NativeInboundRemoteMessage } from './types';
|
||||
|
||||
type OnMessage = RemoteMessage => any;
|
||||
|
||||
type OnMessageObserver = {
|
||||
next: OnMessage,
|
||||
};
|
||||
|
||||
type OnTokenRefresh = String => any;
|
||||
|
||||
type OnTokenRefreshObserver = {
|
||||
next: OnTokenRefresh,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'messaging_message_received',
|
||||
'messaging_token_refreshed',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseMessaging';
|
||||
export const NAMESPACE = 'messaging';
|
||||
|
||||
/**
|
||||
* @class Messaging
|
||||
*/
|
||||
export default class Messaging extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'messaging_message_received',
|
||||
(message: NativeInboundRemoteMessage) => {
|
||||
SharedEventEmitter.emit('onMessage', new RemoteMessage(message));
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'messaging_token_refreshed',
|
||||
(token: string) => {
|
||||
SharedEventEmitter.emit('onTokenRefresh', token);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getToken(): Promise<string> {
|
||||
return getNativeModule(this).getToken();
|
||||
}
|
||||
|
||||
onMessage(nextOrObserver: OnMessage | OnMessageObserver): () => any {
|
||||
let listener: RemoteMessage => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Messaging.onMessage failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onMessage listener');
|
||||
|
||||
SharedEventEmitter.addListener('onMessage', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onMessage listener');
|
||||
SharedEventEmitter.removeListener('onMessage', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onTokenRefresh(
|
||||
nextOrObserver: OnTokenRefresh | OnTokenRefreshObserver
|
||||
): () => any {
|
||||
let listener: String => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowExpectedError: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Messaging.OnTokenRefresh failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onTokenRefresh listener');
|
||||
SharedEventEmitter.addListener('onTokenRefresh', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onTokenRefresh listener');
|
||||
SharedEventEmitter.removeListener('onTokenRefresh', listener);
|
||||
};
|
||||
}
|
||||
|
||||
requestPermission(): Promise<void> {
|
||||
return getNativeModule(this).requestPermission();
|
||||
}
|
||||
|
||||
/**
|
||||
* NON WEB-SDK METHODS
|
||||
*/
|
||||
hasPermission(): Promise<boolean> {
|
||||
return getNativeModule(this).hasPermission();
|
||||
}
|
||||
|
||||
sendMessage(remoteMessage: RemoteMessage): Promise<void> {
|
||||
if (!(remoteMessage instanceof RemoteMessage)) {
|
||||
throw new Error(
|
||||
`Messaging:sendMessage expects a 'RemoteMessage' but got type ${typeof remoteMessage}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).sendMessage(remoteMessage.build());
|
||||
}
|
||||
|
||||
subscribeToTopic(topic: string): void {
|
||||
getNativeModule(this).subscribeToTopic(topic);
|
||||
}
|
||||
|
||||
unsubscribeFromTopic(topic: string): void {
|
||||
getNativeModule(this).unsubscribeFromTopic(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* KNOWN UNSUPPORTED METHODS
|
||||
*/
|
||||
|
||||
deleteToken() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'messaging',
|
||||
'deleteToken'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
setBackgroundMessageHandler() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'messaging',
|
||||
'setBackgroundMessageHandler'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
useServiceWorker() {
|
||||
throw new Error(
|
||||
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
|
||||
'messaging',
|
||||
'useServiceWorker'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
RemoteMessage,
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type Notification = {
|
||||
body: string,
|
||||
bodyLocalizationArgs?: string[],
|
||||
bodyLocalizationKey?: string,
|
||||
clickAction?: string,
|
||||
color?: string,
|
||||
icon?: string,
|
||||
link?: string,
|
||||
sound: string,
|
||||
subtitle?: string,
|
||||
tag?: string,
|
||||
title: string,
|
||||
titleLocalizationArgs?: string[],
|
||||
titleLocalizationKey?: string,
|
||||
};
|
||||
|
||||
export type NativeInboundRemoteMessage = {
|
||||
collapseKey?: string,
|
||||
data: { [string]: string },
|
||||
from?: string,
|
||||
messageId: string,
|
||||
messageType?: string,
|
||||
sentTime?: number,
|
||||
to?: string,
|
||||
ttl?: number,
|
||||
};
|
||||
|
||||
export type NativeOutboundRemoteMessage = {
|
||||
collapseKey?: string,
|
||||
data: { [string]: string },
|
||||
messageId: string,
|
||||
messageType?: string,
|
||||
to: string,
|
||||
ttl: number,
|
||||
};
|
|
@ -1,150 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidAction representation wrapper
|
||||
*/
|
||||
import RemoteInput, {
|
||||
fromNativeAndroidRemoteInput,
|
||||
} from './AndroidRemoteInput';
|
||||
import { SemanticAction } from './types';
|
||||
import type { NativeAndroidAction, SemanticActionType } from './types';
|
||||
|
||||
export default class AndroidAction {
|
||||
_action: string;
|
||||
_allowGeneratedReplies: boolean | void;
|
||||
_icon: string;
|
||||
_remoteInputs: RemoteInput[];
|
||||
_semanticAction: SemanticActionType | void;
|
||||
_showUserInterface: boolean | void;
|
||||
_title: string;
|
||||
|
||||
constructor(action: string, icon: string, title: string) {
|
||||
this._action = action;
|
||||
this._icon = icon;
|
||||
this._remoteInputs = [];
|
||||
this._title = title;
|
||||
}
|
||||
|
||||
get action(): string {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
get allowGeneratedReplies(): ?boolean {
|
||||
return this._allowGeneratedReplies;
|
||||
}
|
||||
|
||||
get icon(): string {
|
||||
return this._icon;
|
||||
}
|
||||
|
||||
get remoteInputs(): RemoteInput[] {
|
||||
return this._remoteInputs;
|
||||
}
|
||||
|
||||
get semanticAction(): ?SemanticActionType {
|
||||
return this._semanticAction;
|
||||
}
|
||||
|
||||
get showUserInterface(): ?boolean {
|
||||
return this._showUserInterface;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param remoteInput
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
addRemoteInput(remoteInput: RemoteInput): AndroidAction {
|
||||
if (!(remoteInput instanceof RemoteInput)) {
|
||||
throw new Error(
|
||||
`AndroidAction:addRemoteInput expects an 'RemoteInput' but got type ${typeof remoteInput}`
|
||||
);
|
||||
}
|
||||
this._remoteInputs.push(remoteInput);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param allowGeneratedReplies
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
setAllowGenerateReplies(allowGeneratedReplies: boolean): AndroidAction {
|
||||
this._allowGeneratedReplies = allowGeneratedReplies;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param semanticAction
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
setSemanticAction(semanticAction: SemanticActionType): AndroidAction {
|
||||
if (!Object.values(SemanticAction).includes(semanticAction)) {
|
||||
throw new Error(
|
||||
`AndroidAction:setSemanticAction Invalid Semantic Action: ${semanticAction}`
|
||||
);
|
||||
}
|
||||
this._semanticAction = semanticAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showUserInterface
|
||||
* @returns {AndroidAction}
|
||||
*/
|
||||
setShowUserInterface(showUserInterface: boolean): AndroidAction {
|
||||
this._showUserInterface = showUserInterface;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeAndroidAction {
|
||||
if (!this._action) {
|
||||
throw new Error('AndroidAction: Missing required `action` property');
|
||||
} else if (!this._icon) {
|
||||
throw new Error('AndroidAction: Missing required `icon` property');
|
||||
} else if (!this._title) {
|
||||
throw new Error('AndroidAction: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
action: this._action,
|
||||
allowGeneratedReplies: this._allowGeneratedReplies,
|
||||
icon: this._icon,
|
||||
remoteInputs: this._remoteInputs.map(remoteInput => remoteInput.build()),
|
||||
semanticAction: this._semanticAction,
|
||||
showUserInterface: this._showUserInterface,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const fromNativeAndroidAction = (
|
||||
nativeAction: NativeAndroidAction
|
||||
): AndroidAction => {
|
||||
const action = new AndroidAction(
|
||||
nativeAction.action,
|
||||
nativeAction.icon,
|
||||
nativeAction.title
|
||||
);
|
||||
if (nativeAction.allowGeneratedReplies) {
|
||||
action.setAllowGenerateReplies(nativeAction.allowGeneratedReplies);
|
||||
}
|
||||
if (nativeAction.remoteInputs) {
|
||||
nativeAction.remoteInputs.forEach(remoteInput => {
|
||||
action.addRemoteInput(fromNativeAndroidRemoteInput(remoteInput));
|
||||
});
|
||||
}
|
||||
if (nativeAction.semanticAction) {
|
||||
action.setSemanticAction(nativeAction.semanticAction);
|
||||
}
|
||||
if (nativeAction.showUserInterface) {
|
||||
action.setShowUserInterface(nativeAction.showUserInterface);
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
|
@ -1,198 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidChannel representation wrapper
|
||||
*/
|
||||
import { Importance, Visibility } from './types';
|
||||
import type { ImportanceType, VisibilityType } from './types';
|
||||
|
||||
type NativeAndroidChannel = {|
|
||||
bypassDnd?: boolean,
|
||||
channelId: string,
|
||||
description?: string,
|
||||
group?: string,
|
||||
importance: ImportanceType,
|
||||
lightColor?: string,
|
||||
lockScreenVisibility?: VisibilityType,
|
||||
name: string,
|
||||
showBadge?: boolean,
|
||||
sound?: string,
|
||||
vibrationPattern?: number[],
|
||||
|};
|
||||
|
||||
export default class AndroidChannel {
|
||||
_bypassDnd: boolean | void;
|
||||
_channelId: string;
|
||||
_description: string | void;
|
||||
_group: string | void;
|
||||
_importance: ImportanceType;
|
||||
_lightColor: string | void;
|
||||
_lockScreenVisibility: VisibilityType;
|
||||
_name: string;
|
||||
_showBadge: boolean | void;
|
||||
_sound: string | void;
|
||||
_vibrationPattern: number[] | void;
|
||||
|
||||
constructor(channelId: string, name: string, importance: ImportanceType) {
|
||||
if (!Object.values(Importance).includes(importance)) {
|
||||
throw new Error(`AndroidChannel() Invalid Importance: ${importance}`);
|
||||
}
|
||||
this._channelId = channelId;
|
||||
this._name = name;
|
||||
this._importance = importance;
|
||||
}
|
||||
|
||||
get bypassDnd(): ?boolean {
|
||||
return this._bypassDnd;
|
||||
}
|
||||
|
||||
get channelId(): string {
|
||||
return this._channelId;
|
||||
}
|
||||
|
||||
get description(): ?string {
|
||||
return this._description;
|
||||
}
|
||||
|
||||
get group(): ?string {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
get importance(): ImportanceType {
|
||||
return this._importance;
|
||||
}
|
||||
|
||||
get lightColor(): ?string {
|
||||
return this._lightColor;
|
||||
}
|
||||
|
||||
get lockScreenVisibility(): ?VisibilityType {
|
||||
return this._lockScreenVisibility;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get showBadge(): ?boolean {
|
||||
return this._showBadge;
|
||||
}
|
||||
|
||||
get sound(): ?string {
|
||||
return this._sound;
|
||||
}
|
||||
|
||||
get vibrationPattern(): ?(number[]) {
|
||||
return this._vibrationPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bypassDnd
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setBypassDnd(bypassDnd: boolean): AndroidChannel {
|
||||
this._bypassDnd = bypassDnd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param description
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setDescription(description: string): AndroidChannel {
|
||||
this._description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setGroup(groupId: string): AndroidChannel {
|
||||
this._group = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lightColor
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setLightColor(lightColor: string): AndroidChannel {
|
||||
this._lightColor = lightColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lockScreenVisibility
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setLockScreenVisibility(
|
||||
lockScreenVisibility: VisibilityType
|
||||
): AndroidChannel {
|
||||
if (!Object.values(Visibility).includes(lockScreenVisibility)) {
|
||||
throw new Error(
|
||||
`AndroidChannel:setLockScreenVisibility Invalid Visibility: ${lockScreenVisibility}`
|
||||
);
|
||||
}
|
||||
this._lockScreenVisibility = lockScreenVisibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showBadge
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setShowBadge(showBadge: boolean): AndroidChannel {
|
||||
this._showBadge = showBadge;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sound
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setSound(sound: string): AndroidChannel {
|
||||
this._sound = sound;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vibrationPattern
|
||||
* @returns {AndroidChannel}
|
||||
*/
|
||||
setVibrationPattern(vibrationPattern: number[]): AndroidChannel {
|
||||
this._vibrationPattern = vibrationPattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeAndroidChannel {
|
||||
if (!this._channelId) {
|
||||
throw new Error('AndroidChannel: Missing required `channelId` property');
|
||||
} else if (!this._importance) {
|
||||
throw new Error('AndroidChannel: Missing required `importance` property');
|
||||
} else if (!this._name) {
|
||||
throw new Error('AndroidChannel: Missing required `name` property');
|
||||
}
|
||||
|
||||
return {
|
||||
bypassDnd: this._bypassDnd,
|
||||
channelId: this._channelId,
|
||||
description: this._description,
|
||||
group: this._group,
|
||||
importance: this._importance,
|
||||
lightColor: this._lightColor,
|
||||
lockScreenVisibility: this._lockScreenVisibility,
|
||||
name: this._name,
|
||||
showBadge: this._showBadge,
|
||||
sound: this._sound,
|
||||
vibrationPattern: this._vibrationPattern,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidChannelGroup representation wrapper
|
||||
*/
|
||||
|
||||
type NativeAndroidChannelGroup = {|
|
||||
groupId: string,
|
||||
name: string,
|
||||
|};
|
||||
|
||||
export default class AndroidChannelGroup {
|
||||
_groupId: string;
|
||||
_name: string;
|
||||
|
||||
constructor(groupId: string, name: string) {
|
||||
this._groupId = groupId;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
get groupId(): string {
|
||||
return this._groupId;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
build(): NativeAndroidChannelGroup {
|
||||
if (!this._groupId) {
|
||||
throw new Error(
|
||||
'AndroidChannelGroup: Missing required `groupId` property'
|
||||
);
|
||||
} else if (!this._name) {
|
||||
throw new Error('AndroidChannelGroup: Missing required `name` property');
|
||||
}
|
||||
|
||||
return {
|
||||
groupId: this._groupId,
|
||||
name: this._name,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,676 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidNotification representation wrapper
|
||||
*/
|
||||
import AndroidAction, { fromNativeAndroidAction } from './AndroidAction';
|
||||
import { BadgeIconType, Category, GroupAlert, Priority } from './types';
|
||||
import type Notification from './Notification';
|
||||
import type {
|
||||
BadgeIconTypeType,
|
||||
CategoryType,
|
||||
DefaultsType,
|
||||
GroupAlertType,
|
||||
Lights,
|
||||
NativeAndroidNotification,
|
||||
PriorityType,
|
||||
Progress,
|
||||
SmallIcon,
|
||||
VisibilityType,
|
||||
} from './types';
|
||||
|
||||
export default class AndroidNotification {
|
||||
_actions: AndroidAction[];
|
||||
_autoCancel: boolean | void;
|
||||
_badgeIconType: BadgeIconTypeType | void;
|
||||
_category: CategoryType | void;
|
||||
_channelId: string;
|
||||
_clickAction: string | void;
|
||||
_color: string | void;
|
||||
_colorized: boolean | void;
|
||||
_contentInfo: string | void;
|
||||
_defaults: DefaultsType[] | void;
|
||||
_group: string | void;
|
||||
_groupAlertBehaviour: GroupAlertType | void;
|
||||
_groupSummary: boolean | void;
|
||||
_largeIcon: string | void;
|
||||
_lights: Lights | void;
|
||||
_localOnly: boolean | void;
|
||||
_notification: Notification;
|
||||
_number: number | void;
|
||||
_ongoing: boolean | void;
|
||||
_onlyAlertOnce: boolean | void;
|
||||
_people: string[];
|
||||
_priority: PriorityType | void;
|
||||
_progress: Progress | void;
|
||||
// _publicVersion: Notification;
|
||||
_remoteInputHistory: string[] | void;
|
||||
_shortcutId: string | void;
|
||||
_showWhen: boolean | void;
|
||||
_smallIcon: SmallIcon;
|
||||
_sortKey: string | void;
|
||||
// TODO: style: Style; // Need to figure out if this can work
|
||||
_ticker: string | void;
|
||||
_timeoutAfter: number | void;
|
||||
_usesChronometer: boolean | void;
|
||||
_vibrate: number[] | void;
|
||||
_visibility: VisibilityType | void;
|
||||
_when: number | void;
|
||||
|
||||
// android unsupported
|
||||
// content: RemoteViews
|
||||
// contentIntent: PendingIntent - need to look at what this is
|
||||
// customBigContentView: RemoteViews
|
||||
// customContentView: RemoteViews
|
||||
// customHeadsUpContentView: RemoteViews
|
||||
// deleteIntent: PendingIntent
|
||||
// fullScreenIntent: PendingIntent
|
||||
// sound.streamType
|
||||
|
||||
constructor(notification: Notification, data?: NativeAndroidNotification) {
|
||||
this._notification = notification;
|
||||
|
||||
if (data) {
|
||||
this._actions = data.actions
|
||||
? data.actions.map(action => fromNativeAndroidAction(action))
|
||||
: [];
|
||||
this._autoCancel = data.autoCancel;
|
||||
this._badgeIconType = data.badgeIconType;
|
||||
this._category = data.category;
|
||||
this._channelId = data.channelId;
|
||||
this._clickAction = data.clickAction;
|
||||
this._color = data.color;
|
||||
this._colorized = data.colorized;
|
||||
this._contentInfo = data.contentInfo;
|
||||
this._defaults = data.defaults;
|
||||
this._group = data.group;
|
||||
this._groupAlertBehaviour = data.groupAlertBehaviour;
|
||||
this._groupSummary = data.groupSummary;
|
||||
this._largeIcon = data.largeIcon;
|
||||
this._lights = data.lights;
|
||||
this._localOnly = data.localOnly;
|
||||
this._number = data.number;
|
||||
this._ongoing = data.ongoing;
|
||||
this._onlyAlertOnce = data.onlyAlertOnce;
|
||||
this._people = data.people;
|
||||
this._priority = data.priority;
|
||||
this._progress = data.progress;
|
||||
// _publicVersion: Notification;
|
||||
this._remoteInputHistory = data.remoteInputHistory;
|
||||
this._shortcutId = data.shortcutId;
|
||||
this._showWhen = data.showWhen;
|
||||
this._smallIcon = data.smallIcon;
|
||||
this._sortKey = data.sortKey;
|
||||
this._ticker = data.ticker;
|
||||
this._timeoutAfter = data.timeoutAfter;
|
||||
this._usesChronometer = data.usesChronometer;
|
||||
this._vibrate = data.vibrate;
|
||||
this._visibility = data.visibility;
|
||||
this._when = data.when;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._actions = this._actions || [];
|
||||
this._people = this._people || [];
|
||||
this._smallIcon = this._smallIcon || {
|
||||
icon: 'ic_launcher',
|
||||
};
|
||||
}
|
||||
|
||||
get actions(): AndroidAction[] {
|
||||
return this._actions;
|
||||
}
|
||||
|
||||
get autoCancel(): ?boolean {
|
||||
return this._autoCancel;
|
||||
}
|
||||
|
||||
get badgeIconType(): ?BadgeIconTypeType {
|
||||
return this._badgeIconType;
|
||||
}
|
||||
|
||||
get category(): ?CategoryType {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
get channelId(): string {
|
||||
return this._channelId;
|
||||
}
|
||||
|
||||
get clickAction(): ?string {
|
||||
return this._clickAction;
|
||||
}
|
||||
|
||||
get color(): ?string {
|
||||
return this._color;
|
||||
}
|
||||
|
||||
get colorized(): ?boolean {
|
||||
return this._colorized;
|
||||
}
|
||||
|
||||
get contentInfo(): ?string {
|
||||
return this._contentInfo;
|
||||
}
|
||||
|
||||
get defaults(): ?(DefaultsType[]) {
|
||||
return this._defaults;
|
||||
}
|
||||
|
||||
get group(): ?string {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
get groupAlertBehaviour(): ?GroupAlertType {
|
||||
return this._groupAlertBehaviour;
|
||||
}
|
||||
|
||||
get groupSummary(): ?boolean {
|
||||
return this._groupSummary;
|
||||
}
|
||||
|
||||
get largeIcon(): ?string {
|
||||
return this._largeIcon;
|
||||
}
|
||||
|
||||
get lights(): ?Lights {
|
||||
return this._lights;
|
||||
}
|
||||
|
||||
get localOnly(): ?boolean {
|
||||
return this._localOnly;
|
||||
}
|
||||
|
||||
get number(): ?number {
|
||||
return this._number;
|
||||
}
|
||||
|
||||
get ongoing(): ?boolean {
|
||||
return this._ongoing;
|
||||
}
|
||||
|
||||
get onlyAlertOnce(): ?boolean {
|
||||
return this._onlyAlertOnce;
|
||||
}
|
||||
|
||||
get people(): string[] {
|
||||
return this._people;
|
||||
}
|
||||
|
||||
get priority(): ?PriorityType {
|
||||
return this._priority;
|
||||
}
|
||||
|
||||
get progress(): ?Progress {
|
||||
return this._progress;
|
||||
}
|
||||
|
||||
get remoteInputHistory(): ?(string[]) {
|
||||
return this._remoteInputHistory;
|
||||
}
|
||||
|
||||
get shortcutId(): ?string {
|
||||
return this._shortcutId;
|
||||
}
|
||||
|
||||
get showWhen(): ?boolean {
|
||||
return this._showWhen;
|
||||
}
|
||||
|
||||
get smallIcon(): SmallIcon {
|
||||
return this._smallIcon;
|
||||
}
|
||||
|
||||
get sortKey(): ?string {
|
||||
return this._sortKey;
|
||||
}
|
||||
|
||||
get ticker(): ?string {
|
||||
return this._ticker;
|
||||
}
|
||||
|
||||
get timeoutAfter(): ?number {
|
||||
return this._timeoutAfter;
|
||||
}
|
||||
|
||||
get usesChronometer(): ?boolean {
|
||||
return this._usesChronometer;
|
||||
}
|
||||
|
||||
get vibrate(): ?(number[]) {
|
||||
return this._vibrate;
|
||||
}
|
||||
|
||||
get visibility(): ?VisibilityType {
|
||||
return this._visibility;
|
||||
}
|
||||
|
||||
get when(): ?number {
|
||||
return this._when;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param action
|
||||
* @returns {Notification}
|
||||
*/
|
||||
addAction(action: AndroidAction): Notification {
|
||||
if (!(action instanceof AndroidAction)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:addAction expects an 'AndroidAction' but got type ${typeof action}`
|
||||
);
|
||||
}
|
||||
this._actions.push(action);
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param person
|
||||
* @returns {Notification}
|
||||
*/
|
||||
addPerson(person: string): Notification {
|
||||
this._people.push(person);
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param autoCancel
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setAutoCancel(autoCancel: boolean): Notification {
|
||||
this._autoCancel = autoCancel;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param badgeIconType
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setBadgeIconType(badgeIconType: BadgeIconTypeType): Notification {
|
||||
if (!Object.values(BadgeIconType).includes(badgeIconType)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setBadgeIconType Invalid BadgeIconType: ${badgeIconType}`
|
||||
);
|
||||
}
|
||||
this._badgeIconType = badgeIconType;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param category
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setCategory(category: CategoryType): Notification {
|
||||
if (!Object.values(Category).includes(category)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setCategory Invalid Category: ${category}`
|
||||
);
|
||||
}
|
||||
this._category = category;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param channelId
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setChannelId(channelId: string): Notification {
|
||||
this._channelId = channelId;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clickAction
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setClickAction(clickAction: string): Notification {
|
||||
this._clickAction = clickAction;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param color
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setColor(color: string): Notification {
|
||||
this._color = color;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param colorized
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setColorized(colorized: boolean): Notification {
|
||||
this._colorized = colorized;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param contentInfo
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setContentInfo(contentInfo: string): Notification {
|
||||
this._contentInfo = contentInfo;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param defaults
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setDefaults(defaults: DefaultsType[]): Notification {
|
||||
this._defaults = defaults;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setGroup(group: string): Notification {
|
||||
this._group = group;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupAlertBehaviour
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setGroupAlertBehaviour(groupAlertBehaviour: GroupAlertType): Notification {
|
||||
if (!Object.values(GroupAlert).includes(groupAlertBehaviour)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setGroupAlertBehaviour Invalid GroupAlert: ${groupAlertBehaviour}`
|
||||
);
|
||||
}
|
||||
this._groupAlertBehaviour = groupAlertBehaviour;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupSummary
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setGroupSummary(groupSummary: boolean): Notification {
|
||||
this._groupSummary = groupSummary;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param largeIcon
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLargeIcon(largeIcon: string): Notification {
|
||||
this._largeIcon = largeIcon;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param argb
|
||||
* @param onMs
|
||||
* @param offMs
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLights(argb: number, onMs: number, offMs: number): Notification {
|
||||
this._lights = {
|
||||
argb,
|
||||
onMs,
|
||||
offMs,
|
||||
};
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param localOnly
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLocalOnly(localOnly: boolean): Notification {
|
||||
this._localOnly = localOnly;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param number
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setNumber(number: number): Notification {
|
||||
this._number = number;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ongoing
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setOngoing(ongoing: boolean): Notification {
|
||||
this._ongoing = ongoing;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param onlyAlertOnce
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setOnlyAlertOnce(onlyAlertOnce: boolean): Notification {
|
||||
this._onlyAlertOnce = onlyAlertOnce;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param priority
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setPriority(priority: PriorityType): Notification {
|
||||
if (!Object.values(Priority).includes(priority)) {
|
||||
throw new Error(
|
||||
`AndroidNotification:setPriority Invalid Priority: ${priority}`
|
||||
);
|
||||
}
|
||||
this._priority = priority;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param max
|
||||
* @param progress
|
||||
* @param indeterminate
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setProgress(
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean
|
||||
): Notification {
|
||||
this._progress = {
|
||||
max,
|
||||
progress,
|
||||
indeterminate,
|
||||
};
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param publicVersion
|
||||
* @returns {Notification}
|
||||
*/
|
||||
/* setPublicVersion(publicVersion: Notification): Notification {
|
||||
this._publicVersion = publicVersion;
|
||||
return this._notification;
|
||||
} */
|
||||
|
||||
/**
|
||||
*
|
||||
* @param remoteInputHistory
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setRemoteInputHistory(remoteInputHistory: string[]): Notification {
|
||||
this._remoteInputHistory = remoteInputHistory;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param shortcutId
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setShortcutId(shortcutId: string): Notification {
|
||||
this._shortcutId = shortcutId;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showWhen
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setShowWhen(showWhen: boolean): Notification {
|
||||
this._showWhen = showWhen;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param icon
|
||||
* @param level
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSmallIcon(icon: string, level?: number): Notification {
|
||||
this._smallIcon = {
|
||||
icon,
|
||||
level,
|
||||
};
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sortKey
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSortKey(sortKey: string): Notification {
|
||||
this._sortKey = sortKey;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ticker
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setTicker(ticker: string): Notification {
|
||||
this._ticker = ticker;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param timeoutAfter
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setTimeoutAfter(timeoutAfter: number): Notification {
|
||||
this._timeoutAfter = timeoutAfter;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param usesChronometer
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setUsesChronometer(usesChronometer: boolean): Notification {
|
||||
this._usesChronometer = usesChronometer;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vibrate
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setVibrate(vibrate: number[]): Notification {
|
||||
this._vibrate = vibrate;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param when
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setWhen(when: number): Notification {
|
||||
this._when = when;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
build(): NativeAndroidNotification {
|
||||
// TODO: Validation of required fields
|
||||
if (!this._channelId) {
|
||||
throw new Error(
|
||||
'AndroidNotification: Missing required `channelId` property'
|
||||
);
|
||||
} else if (!this._smallIcon) {
|
||||
throw new Error(
|
||||
'AndroidNotification: Missing required `smallIcon` property'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
actions: this._actions.map(action => action.build()),
|
||||
autoCancel: this._autoCancel,
|
||||
badgeIconType: this._badgeIconType,
|
||||
category: this._category,
|
||||
channelId: this._channelId,
|
||||
clickAction: this._clickAction,
|
||||
color: this._color,
|
||||
colorized: this._colorized,
|
||||
contentInfo: this._contentInfo,
|
||||
defaults: this._defaults,
|
||||
group: this._group,
|
||||
groupAlertBehaviour: this._groupAlertBehaviour,
|
||||
groupSummary: this._groupSummary,
|
||||
largeIcon: this._largeIcon,
|
||||
lights: this._lights,
|
||||
localOnly: this._localOnly,
|
||||
number: this._number,
|
||||
ongoing: this._ongoing,
|
||||
onlyAlertOnce: this._onlyAlertOnce,
|
||||
people: this._people,
|
||||
priority: this._priority,
|
||||
progress: this._progress,
|
||||
// publicVersion: this._publicVersion,
|
||||
remoteInputHistory: this._remoteInputHistory,
|
||||
shortcutId: this._shortcutId,
|
||||
showWhen: this._showWhen,
|
||||
smallIcon: this._smallIcon,
|
||||
sortKey: this._sortKey,
|
||||
// TODO: style: Style,
|
||||
ticker: this._ticker,
|
||||
timeoutAfter: this._timeoutAfter,
|
||||
usesChronometer: this._usesChronometer,
|
||||
vibrate: this._vibrate,
|
||||
visibility: this._visibility,
|
||||
when: this._when,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidNotifications representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidChannel from './AndroidChannel';
|
||||
import AndroidChannelGroup from './AndroidChannelGroup';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type Notifications from './';
|
||||
|
||||
export default class AndroidNotifications {
|
||||
_notifications: Notifications;
|
||||
|
||||
constructor(notifications: Notifications) {
|
||||
this._notifications = notifications;
|
||||
}
|
||||
|
||||
createChannel(channel: AndroidChannel): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!(channel instanceof AndroidChannel)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannel expects an 'AndroidChannel' but got type ${typeof channel}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannel(
|
||||
channel.build()
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createChannelGroup(channelGroup: AndroidChannelGroup): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!(channelGroup instanceof AndroidChannelGroup)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannelGroup expects an 'AndroidChannelGroup' but got type ${typeof channelGroup}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannelGroup(
|
||||
channelGroup.build()
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createChannelGroups(channelGroups: AndroidChannelGroup[]): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!Array.isArray(channelGroups)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannelGroups expects an 'Array' but got type ${typeof channelGroups}`
|
||||
);
|
||||
}
|
||||
const nativeChannelGroups = [];
|
||||
for (let i = 0; i < channelGroups.length; i++) {
|
||||
const channelGroup = channelGroups[i];
|
||||
if (!(channelGroup instanceof AndroidChannelGroup)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannelGroups expects array items of type 'AndroidChannelGroup' but got type ${typeof channelGroup}`
|
||||
);
|
||||
}
|
||||
nativeChannelGroups.push(channelGroup.build());
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannelGroups(
|
||||
nativeChannelGroups
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createChannels(channels: AndroidChannel[]): Promise<void> {
|
||||
if (Platform.OS === 'android') {
|
||||
if (!Array.isArray(channels)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannels expects an 'Array' but got type ${typeof channels}`
|
||||
);
|
||||
}
|
||||
const nativeChannels = [];
|
||||
for (let i = 0; i < channels.length; i++) {
|
||||
const channel = channels[i];
|
||||
if (!(channel instanceof AndroidChannel)) {
|
||||
throw new Error(
|
||||
`AndroidNotifications:createChannels expects array items of type 'AndroidChannel' but got type ${typeof channel}`
|
||||
);
|
||||
}
|
||||
nativeChannels.push(channel.build());
|
||||
}
|
||||
return getNativeModule(this._notifications).createChannels(
|
||||
nativeChannels
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* AndroidRemoteInput representation wrapper
|
||||
*/
|
||||
|
||||
import type { AndroidAllowDataType, NativeAndroidRemoteInput } from './types';
|
||||
|
||||
export default class AndroidRemoteInput {
|
||||
_allowedDataTypes: AndroidAllowDataType[];
|
||||
_allowFreeFormInput: boolean | void;
|
||||
_choices: string[];
|
||||
_label: string | void;
|
||||
_resultKey: string;
|
||||
|
||||
constructor(resultKey: string) {
|
||||
this._allowedDataTypes = [];
|
||||
this._choices = [];
|
||||
this._resultKey = resultKey;
|
||||
}
|
||||
|
||||
get allowedDataTypes(): AndroidAllowDataType[] {
|
||||
return this._allowedDataTypes;
|
||||
}
|
||||
|
||||
get allowFreeFormInput(): ?boolean {
|
||||
return this._allowFreeFormInput;
|
||||
}
|
||||
|
||||
get choices(): string[] {
|
||||
return this._choices;
|
||||
}
|
||||
|
||||
get label(): ?string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
get resultKey(): string {
|
||||
return this._resultKey;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mimeType
|
||||
* @param allow
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setAllowDataType(mimeType: string, allow: boolean): AndroidRemoteInput {
|
||||
this._allowedDataTypes.push({
|
||||
allow,
|
||||
mimeType,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param allowFreeFormInput
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setAllowFreeFormInput(allowFreeFormInput: boolean): AndroidRemoteInput {
|
||||
this._allowFreeFormInput = allowFreeFormInput;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param choices
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setChoices(choices: string[]): AndroidRemoteInput {
|
||||
this._choices = choices;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param label
|
||||
* @returns {AndroidRemoteInput}
|
||||
*/
|
||||
setLabel(label: string): AndroidRemoteInput {
|
||||
this._label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeAndroidRemoteInput {
|
||||
if (!this._resultKey) {
|
||||
throw new Error(
|
||||
'AndroidRemoteInput: Missing required `resultKey` property'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
allowedDataTypes: this._allowedDataTypes,
|
||||
allowFreeFormInput: this._allowFreeFormInput,
|
||||
choices: this._choices,
|
||||
label: this._label,
|
||||
resultKey: this._resultKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const fromNativeAndroidRemoteInput = (
|
||||
nativeRemoteInput: NativeAndroidRemoteInput
|
||||
): AndroidRemoteInput => {
|
||||
const remoteInput = new AndroidRemoteInput(nativeRemoteInput.resultKey);
|
||||
if (nativeRemoteInput.allowDataType) {
|
||||
for (let i = 0; i < nativeRemoteInput.allowDataType.length; i++) {
|
||||
const allowDataType = nativeRemoteInput.allowDataType[i];
|
||||
remoteInput.setAllowDataType(allowDataType.mimeType, allowDataType.allow);
|
||||
}
|
||||
}
|
||||
if (nativeRemoteInput.allowFreeFormInput) {
|
||||
remoteInput.setAllowFreeFormInput(nativeRemoteInput.allowFreeFormInput);
|
||||
}
|
||||
if (nativeRemoteInput.choices) {
|
||||
remoteInput.setChoices(nativeRemoteInput.choices);
|
||||
}
|
||||
if (nativeRemoteInput.label) {
|
||||
remoteInput.setLabel(nativeRemoteInput.label);
|
||||
}
|
||||
|
||||
return remoteInput;
|
||||
};
|
|
@ -1,160 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* IOSNotification representation wrapper
|
||||
*/
|
||||
import type Notification from './Notification';
|
||||
import type {
|
||||
IOSAttachment,
|
||||
IOSAttachmentOptions,
|
||||
NativeIOSNotification,
|
||||
} from './types';
|
||||
|
||||
export default class IOSNotification {
|
||||
_alertAction: string | void; // alertAction | N/A
|
||||
_attachments: IOSAttachment[]; // N/A | attachments
|
||||
_badge: number | void; // applicationIconBadgeNumber | badge
|
||||
_category: string | void;
|
||||
_hasAction: boolean | void; // hasAction | N/A
|
||||
_launchImage: string | void; // alertLaunchImage | launchImageName
|
||||
_notification: Notification;
|
||||
_threadIdentifier: string | void; // N/A | threadIdentifier
|
||||
|
||||
constructor(notification: Notification, data?: NativeIOSNotification) {
|
||||
this._notification = notification;
|
||||
|
||||
if (data) {
|
||||
this._alertAction = data.alertAction;
|
||||
this._attachments = data.attachments;
|
||||
this._badge = data.badge;
|
||||
this._category = data.category;
|
||||
this._hasAction = data.hasAction;
|
||||
this._launchImage = data.launchImage;
|
||||
this._threadIdentifier = data.threadIdentifier;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._attachments = this._attachments || [];
|
||||
}
|
||||
|
||||
get alertAction(): ?string {
|
||||
return this._alertAction;
|
||||
}
|
||||
|
||||
get attachments(): IOSAttachment[] {
|
||||
return this._attachments;
|
||||
}
|
||||
|
||||
get badge(): ?number {
|
||||
return this._badge;
|
||||
}
|
||||
|
||||
get category(): ?string {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
get hasAction(): ?boolean {
|
||||
return this._hasAction;
|
||||
}
|
||||
|
||||
get launchImage(): ?string {
|
||||
return this._launchImage;
|
||||
}
|
||||
|
||||
get threadIdentifier(): ?string {
|
||||
return this._threadIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier
|
||||
* @param url
|
||||
* @param options
|
||||
* @returns {Notification}
|
||||
*/
|
||||
addAttachment(
|
||||
identifier: string,
|
||||
url: string,
|
||||
options?: IOSAttachmentOptions
|
||||
): Notification {
|
||||
this._attachments.push({
|
||||
identifier,
|
||||
options,
|
||||
url,
|
||||
});
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param alertAction
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setAlertAction(alertAction: string): Notification {
|
||||
this._alertAction = alertAction;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param badge
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setBadge(badge: number): Notification {
|
||||
this._badge = badge;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param category
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setCategory(category: string): Notification {
|
||||
this._category = category;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hasAction
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setHasAction(hasAction: boolean): Notification {
|
||||
this._hasAction = hasAction;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param launchImage
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setLaunchImage(launchImage: string): Notification {
|
||||
this._launchImage = launchImage;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param threadIdentifier
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setThreadIdentifier(threadIdentifier: string): Notification {
|
||||
this._threadIdentifier = threadIdentifier;
|
||||
return this._notification;
|
||||
}
|
||||
|
||||
build(): NativeIOSNotification {
|
||||
// TODO: Validation of required fields
|
||||
|
||||
return {
|
||||
alertAction: this._alertAction,
|
||||
attachments: this._attachments,
|
||||
badge: this._badge,
|
||||
category: this._category,
|
||||
hasAction: this._hasAction,
|
||||
launchImage: this._launchImage,
|
||||
threadIdentifier: this._threadIdentifier,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Notification representation wrapper
|
||||
*/
|
||||
import { Platform } from 'react-native';
|
||||
import AndroidNotification from './AndroidNotification';
|
||||
import IOSNotification from './IOSNotification';
|
||||
import { generatePushID, isObject } from '../../utils';
|
||||
|
||||
import type { NativeNotification } from './types';
|
||||
|
||||
export type NotificationOpen = {|
|
||||
action: string,
|
||||
notification: Notification,
|
||||
results?: { [string]: string },
|
||||
|};
|
||||
|
||||
export default class Notification {
|
||||
// iOS 8/9 | 10+ | Android
|
||||
_android: AndroidNotification;
|
||||
_body: string; // alertBody | body | contentText
|
||||
_data: { [string]: string }; // userInfo | userInfo | extras
|
||||
_ios: IOSNotification;
|
||||
_notificationId: string;
|
||||
_sound: string | void; // soundName | sound | sound
|
||||
_subtitle: string | void; // N/A | subtitle | subText
|
||||
_title: string; // alertTitle | title | contentTitle
|
||||
|
||||
constructor(data?: NativeNotification) {
|
||||
this._android = new AndroidNotification(this, data && data.android);
|
||||
this._ios = new IOSNotification(this, data && data.ios);
|
||||
|
||||
if (data) {
|
||||
this._body = data.body;
|
||||
this._data = data.data;
|
||||
this._notificationId = data.notificationId;
|
||||
this._sound = data.sound;
|
||||
this._subtitle = data.subtitle;
|
||||
this._title = data.title;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._data = this._data || {};
|
||||
// TODO: Is this the best way to generate an ID?
|
||||
this._notificationId = this._notificationId || generatePushID();
|
||||
}
|
||||
|
||||
get android(): AndroidNotification {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
get body(): string {
|
||||
return this._body;
|
||||
}
|
||||
|
||||
get data(): { [string]: string } {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get ios(): IOSNotification {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
get notificationId(): string {
|
||||
return this._notificationId;
|
||||
}
|
||||
|
||||
get sound(): ?string {
|
||||
return this._sound;
|
||||
}
|
||||
|
||||
get subtitle(): ?string {
|
||||
return this._subtitle;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setBody(body: string): Notification {
|
||||
this._body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setData(data: Object = {}): Notification {
|
||||
if (!isObject(data)) {
|
||||
throw new Error(
|
||||
`Notification:withData expects an object but got type '${typeof data}'.`
|
||||
);
|
||||
}
|
||||
this._data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param notificationId
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setNotificationId(notificationId: string): Notification {
|
||||
this._notificationId = notificationId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sound
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSound(sound: string): Notification {
|
||||
this._sound = sound;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param subtitle
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setSubtitle(subtitle: string): Notification {
|
||||
this._subtitle = subtitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param title
|
||||
* @returns {Notification}
|
||||
*/
|
||||
setTitle(title: string): Notification {
|
||||
this._title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): NativeNotification {
|
||||
// Android required fields: body, title, smallicon
|
||||
// iOS required fields: TODO
|
||||
if (!this._body) {
|
||||
throw new Error('Notification: Missing required `body` property');
|
||||
} else if (!this._notificationId) {
|
||||
throw new Error(
|
||||
'Notification: Missing required `notificationId` property'
|
||||
);
|
||||
} else if (!this._title) {
|
||||
throw new Error('Notification: Missing required `title` property');
|
||||
}
|
||||
|
||||
return {
|
||||
android: Platform.OS === 'android' ? this._android.build() : undefined,
|
||||
body: this._body,
|
||||
data: this._data,
|
||||
ios: Platform.OS === 'ios' ? this._ios.build() : undefined,
|
||||
notificationId: this._notificationId,
|
||||
sound: this._sound,
|
||||
subtitle: this._subtitle,
|
||||
title: this._title,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,318 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Notifications representation wrapper
|
||||
*/
|
||||
import { SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import { isFunction, isObject } from '../../utils';
|
||||
import AndroidAction from './AndroidAction';
|
||||
import AndroidChannel from './AndroidChannel';
|
||||
import AndroidChannelGroup from './AndroidChannelGroup';
|
||||
import AndroidNotifications from './AndroidNotifications';
|
||||
import AndroidRemoteInput from './AndroidRemoteInput';
|
||||
import Notification from './Notification';
|
||||
import {
|
||||
BadgeIconType,
|
||||
Category,
|
||||
Defaults,
|
||||
GroupAlert,
|
||||
Importance,
|
||||
Priority,
|
||||
SemanticAction,
|
||||
Visibility,
|
||||
} from './types';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NotificationOpen } from './Notification';
|
||||
import type {
|
||||
NativeNotification,
|
||||
NativeNotificationOpen,
|
||||
Schedule,
|
||||
} from './types';
|
||||
|
||||
type OnNotification = Notification => any;
|
||||
|
||||
type OnNotificationObserver = {
|
||||
next: OnNotification,
|
||||
};
|
||||
|
||||
type OnNotificationOpened = NotificationOpen => any;
|
||||
|
||||
type OnNotificationOpenedObserver = {
|
||||
next: NotificationOpen,
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'notifications_notification_displayed',
|
||||
'notifications_notification_opened',
|
||||
'notifications_notification_received',
|
||||
];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseNotifications';
|
||||
export const NAMESPACE = 'notifications';
|
||||
|
||||
// iOS 8/9 scheduling
|
||||
// fireDate: Date;
|
||||
// timeZone: TimeZone;
|
||||
// repeatInterval: NSCalendar.Unit;
|
||||
// repeatCalendar: Calendar;
|
||||
// region: CLRegion;
|
||||
// regionTriggersOnce: boolean;
|
||||
|
||||
// iOS 10 scheduling
|
||||
// TODO
|
||||
|
||||
// Android scheduling
|
||||
// TODO
|
||||
|
||||
/**
|
||||
* @class Notifications
|
||||
*/
|
||||
export default class Notifications extends ModuleBase {
|
||||
_android: AndroidNotifications;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
hasShards: false,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
this._android = new AndroidNotifications(this);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotificationDisplayed
|
||||
'notifications_notification_displayed',
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotificationDisplayed',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotificationOpened
|
||||
'notifications_notification_opened',
|
||||
(notificationOpen: NativeNotificationOpen) => {
|
||||
SharedEventEmitter.emit('onNotificationOpened', {
|
||||
action: notificationOpen.action,
|
||||
notification: new Notification(notificationOpen.notification),
|
||||
results: notificationOpen.results,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotification
|
||||
'notifications_notification_received',
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotification',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get android(): AndroidNotifications {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all notifications
|
||||
*/
|
||||
cancelAllNotifications(): void {
|
||||
getNativeModule(this).cancelAllNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a notification by id.
|
||||
* @param notificationId
|
||||
*/
|
||||
cancelNotification(notificationId: string): void {
|
||||
if (!notificationId) {
|
||||
throw new Error(
|
||||
'Notifications: cancelNotification expects a `notificationId`'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).cancelNotification(notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a notification
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
displayNotification(notification: Notification): Promise<void> {
|
||||
if (!(notification instanceof Notification)) {
|
||||
throw new Error(
|
||||
`Notifications:displayNotification expects a 'Notification' but got type ${typeof notification}`
|
||||
);
|
||||
}
|
||||
return getNativeModule(this).displayNotification(notification.build());
|
||||
}
|
||||
|
||||
getBadge(): Promise<number> {
|
||||
return getNativeModule(this).getBadge();
|
||||
}
|
||||
|
||||
getInitialNotification(): Promise<NotificationOpen> {
|
||||
return getNativeModule(this)
|
||||
.getInitialNotification()
|
||||
.then((notificationOpen: NativeNotificationOpen) => {
|
||||
if (notificationOpen) {
|
||||
return {
|
||||
action: notificationOpen.action,
|
||||
notification: new Notification(notificationOpen.notification),
|
||||
results: notificationOpen.results,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all scheduled notifications
|
||||
* @returns {Promise.<Array>}
|
||||
*/
|
||||
getScheduledNotifications(): Promise<Notification[]> {
|
||||
return getNativeModule(this).getScheduledNotifications();
|
||||
}
|
||||
|
||||
onNotification(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotification failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotification listener');
|
||||
SharedEventEmitter.addListener('onNotification', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotification listener');
|
||||
SharedEventEmitter.removeListener('onNotification', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onNotificationDisplayed(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotificationDisplayed failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotificationDisplayed listener');
|
||||
SharedEventEmitter.addListener('onNotificationDisplayed', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotificationDisplayed listener');
|
||||
SharedEventEmitter.removeListener('onNotificationDisplayed', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onNotificationOpened(
|
||||
nextOrObserver: OnNotificationOpened | OnNotificationOpenedObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotificationOpened failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotificationOpened listener');
|
||||
SharedEventEmitter.addListener('onNotificationOpened', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotificationOpened listener');
|
||||
SharedEventEmitter.removeListener('onNotificationOpened', listener);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all delivered notifications.
|
||||
*/
|
||||
removeAllDeliveredNotifications(): void {
|
||||
getNativeModule(this).removeAllDeliveredNotifications();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a delivered notification.
|
||||
* @param notificationId
|
||||
*/
|
||||
removeDeliveredNotification(notificationId: string): void {
|
||||
if (!notificationId) {
|
||||
throw new Error(
|
||||
'Notifications: removeDeliveredNotification expects a `notificationId`'
|
||||
);
|
||||
}
|
||||
getNativeModule(this).removeDeliveredNotification(notificationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a notification
|
||||
* @param notification
|
||||
* @returns {*}
|
||||
*/
|
||||
scheduleNotification(
|
||||
notification: Notification,
|
||||
schedule: Schedule
|
||||
): Promise<void> {
|
||||
if (!(notification instanceof Notification)) {
|
||||
throw new Error(
|
||||
`Notifications:scheduleNotification expects a 'Notification' but got type ${typeof notification}`
|
||||
);
|
||||
}
|
||||
const nativeNotification = notification.build();
|
||||
nativeNotification.schedule = schedule;
|
||||
return getNativeModule(this).scheduleNotification(nativeNotification);
|
||||
}
|
||||
|
||||
setBadge(badge: number): void {
|
||||
getNativeModule(this).setBadge(badge);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
Android: {
|
||||
Action: AndroidAction,
|
||||
BadgeIconType,
|
||||
Category,
|
||||
Channel: AndroidChannel,
|
||||
ChannelGroup: AndroidChannelGroup,
|
||||
Defaults,
|
||||
GroupAlert,
|
||||
Importance,
|
||||
Priority,
|
||||
RemoteInput: AndroidRemoteInput,
|
||||
SemanticAction,
|
||||
Visibility,
|
||||
},
|
||||
Notification,
|
||||
};
|
|
@ -1,216 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export const BadgeIconType = {
|
||||
Large: 2,
|
||||
None: 0,
|
||||
Small: 1,
|
||||
};
|
||||
|
||||
export const Category = {
|
||||
Alarm: 'alarm',
|
||||
Call: 'call',
|
||||
Email: 'email',
|
||||
Error: 'err',
|
||||
Event: 'event',
|
||||
Message: 'msg',
|
||||
Progress: 'progress',
|
||||
Promo: 'promo',
|
||||
Recommendation: 'recommendation',
|
||||
Reminder: 'reminder',
|
||||
Service: 'service',
|
||||
Social: 'social',
|
||||
Status: 'status',
|
||||
System: 'system',
|
||||
Transport: 'transport',
|
||||
};
|
||||
|
||||
export const Defaults = {
|
||||
All: -1,
|
||||
Lights: 4,
|
||||
Sound: 1,
|
||||
Vibrate: 2,
|
||||
};
|
||||
|
||||
export const GroupAlert = {
|
||||
All: 0,
|
||||
Children: 2,
|
||||
Summary: 1,
|
||||
};
|
||||
|
||||
export const Importance = {
|
||||
Default: 3,
|
||||
High: 4,
|
||||
Low: 2,
|
||||
Max: 5,
|
||||
Min: 1,
|
||||
None: 3,
|
||||
Unspecified: -1000,
|
||||
};
|
||||
|
||||
export const Priority = {
|
||||
Default: 0,
|
||||
High: 1,
|
||||
Low: -1,
|
||||
Max: 2,
|
||||
Min: -2,
|
||||
};
|
||||
|
||||
export const SemanticAction = {
|
||||
Archive: 5,
|
||||
Call: 10,
|
||||
Delete: 4,
|
||||
MarkAsRead: 2,
|
||||
MarkAsUnread: 3,
|
||||
Mute: 6,
|
||||
None: 0,
|
||||
Reply: 1,
|
||||
ThumbsDown: 9,
|
||||
ThumbsUp: 8,
|
||||
Unmute: 7,
|
||||
};
|
||||
|
||||
export const Visibility = {
|
||||
Private: 0,
|
||||
Public: 1,
|
||||
Secret: -1,
|
||||
};
|
||||
|
||||
export type BadgeIconTypeType = $Values<typeof BadgeIconType>;
|
||||
export type CategoryType = $Values<typeof Category>;
|
||||
export type DefaultsType = $Values<typeof Defaults>;
|
||||
export type GroupAlertType = $Values<typeof GroupAlert>;
|
||||
export type ImportanceType = $Values<typeof Importance>;
|
||||
export type PriorityType = $Values<typeof Priority>;
|
||||
export type SemanticActionType = $Values<typeof SemanticAction>;
|
||||
export type VisibilityType = $Values<typeof Visibility>;
|
||||
|
||||
export type Lights = {|
|
||||
argb: number,
|
||||
onMs: number,
|
||||
offMs: number,
|
||||
|};
|
||||
|
||||
export type Progress = {|
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean,
|
||||
|};
|
||||
|
||||
export type SmallIcon = {|
|
||||
icon: string,
|
||||
level?: number,
|
||||
|};
|
||||
|
||||
export type AndroidAllowDataType = {
|
||||
allow: boolean,
|
||||
mimeType: string,
|
||||
};
|
||||
|
||||
export type NativeAndroidRemoteInput = {|
|
||||
allowedDataTypes: AndroidAllowDataType[],
|
||||
allowFreeFormInput?: boolean,
|
||||
choices: string[],
|
||||
label?: string,
|
||||
resultKey: string,
|
||||
|};
|
||||
|
||||
export type NativeAndroidAction = {|
|
||||
action: string,
|
||||
allowGeneratedReplies?: boolean,
|
||||
icon: string,
|
||||
remoteInputs: NativeAndroidRemoteInput[],
|
||||
semanticAction?: SemanticActionType,
|
||||
showUserInterface?: boolean,
|
||||
title: string,
|
||||
|};
|
||||
|
||||
export type NativeAndroidNotification = {|
|
||||
actions?: NativeAndroidAction[],
|
||||
autoCancel?: boolean,
|
||||
badgeIconType?: BadgeIconTypeType,
|
||||
category?: CategoryType,
|
||||
channelId: string,
|
||||
clickAction?: string,
|
||||
color?: string,
|
||||
colorized?: boolean,
|
||||
contentInfo?: string,
|
||||
defaults?: DefaultsType[],
|
||||
group?: string,
|
||||
groupAlertBehaviour?: GroupAlertType,
|
||||
groupSummary?: boolean,
|
||||
largeIcon?: string,
|
||||
lights?: Lights,
|
||||
localOnly?: boolean,
|
||||
number?: number,
|
||||
ongoing?: boolean,
|
||||
onlyAlertOnce?: boolean,
|
||||
people: string[],
|
||||
priority?: PriorityType,
|
||||
progress?: Progress,
|
||||
// publicVersion: Notification,
|
||||
remoteInputHistory?: string[],
|
||||
shortcutId?: string,
|
||||
showWhen?: boolean,
|
||||
smallIcon: SmallIcon,
|
||||
sortKey?: string,
|
||||
// TODO: style: Style,
|
||||
ticker?: string,
|
||||
timeoutAfter?: number,
|
||||
usesChronometer?: boolean,
|
||||
vibrate?: number[],
|
||||
visibility?: VisibilityType,
|
||||
when?: number,
|
||||
|};
|
||||
|
||||
export type IOSAttachmentOptions = {|
|
||||
typeHint: string,
|
||||
thumbnailHidden: boolean,
|
||||
thumbnailClippingRect: {
|
||||
height: number,
|
||||
width: number,
|
||||
x: number,
|
||||
y: number,
|
||||
},
|
||||
thumbnailTime: number,
|
||||
|};
|
||||
|
||||
export type IOSAttachment = {|
|
||||
identifier: string,
|
||||
options?: IOSAttachmentOptions,
|
||||
url: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSNotification = {|
|
||||
alertAction?: string,
|
||||
attachments: IOSAttachment[],
|
||||
badge?: number,
|
||||
category?: string,
|
||||
hasAction?: boolean,
|
||||
launchImage?: string,
|
||||
threadIdentifier?: string,
|
||||
|};
|
||||
|
||||
export type Schedule = {|
|
||||
exact?: boolean,
|
||||
fireDate: number,
|
||||
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
|
||||
|};
|
||||
|
||||
export type NativeNotification = {|
|
||||
android?: NativeAndroidNotification,
|
||||
body: string,
|
||||
data: { [string]: string },
|
||||
ios?: NativeIOSNotification,
|
||||
notificationId: string,
|
||||
schedule?: Schedule,
|
||||
sound?: string,
|
||||
subtitle?: string,
|
||||
title: string,
|
||||
|};
|
||||
|
||||
export type NativeNotificationOpen = {|
|
||||
action: string,
|
||||
notification: NativeNotification,
|
||||
results?: { [string]: string },
|
||||
|};
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Trace representation wrapper
|
||||
*/
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import type PerformanceMonitoring from './';
|
||||
|
||||
export default class Trace {
|
||||
identifier: string;
|
||||
_perf: PerformanceMonitoring;
|
||||
|
||||
constructor(perf: PerformanceMonitoring, identifier: string) {
|
||||
this._perf = perf;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
start(): void {
|
||||
getNativeModule(this._perf).start(this.identifier);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
getNativeModule(this._perf).stop(this.identifier);
|
||||
}
|
||||
|
||||
incrementCounter(event: string): void {
|
||||
getNativeModule(this._perf).incrementCounter(this.identifier, event);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Performance monitoring representation wrapper
|
||||
*/
|
||||
import Trace from './Trace';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
export const MODULE_NAME = 'RNFirebasePerformance';
|
||||
export const NAMESPACE = 'perf';
|
||||
|
||||
export default class PerformanceMonitoring extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Globally enable or disable performance monitoring
|
||||
* @param enabled
|
||||
* @returns {*}
|
||||
*/
|
||||
setPerformanceCollectionEnabled(enabled: boolean): void {
|
||||
getNativeModule(this).setPerformanceCollectionEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new trace instance
|
||||
* @param trace
|
||||
*/
|
||||
newTrace(trace: string): Trace {
|
||||
return new Trace(this, trace);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {};
|
|
@ -1,164 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* Storage representation wrapper
|
||||
*/
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import StorageRef from './reference';
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
|
||||
const FirebaseStorage = NativeModules.RNFirebaseStorage;
|
||||
|
||||
const NATIVE_EVENTS = ['storage_event', 'storage_error'];
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseStorage';
|
||||
export const NAMESPACE = 'storage';
|
||||
|
||||
export default class Storage extends ModuleBase {
|
||||
/**
|
||||
*
|
||||
* @param app
|
||||
* @param options
|
||||
*/
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
events: NATIVE_EVENTS,
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: true,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this, 'storage_event'),
|
||||
this._handleStorageEvent.bind(this)
|
||||
);
|
||||
|
||||
SharedEventEmitter.addListener(
|
||||
getAppEventName(this, 'storage_error'),
|
||||
this._handleStorageEvent.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference for the given path in the default bucket.
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#ref
|
||||
* @param path
|
||||
* @returns {StorageReference}
|
||||
*/
|
||||
ref(path: string): StorageRef {
|
||||
return new StorageRef(this, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference for the given absolute URL.
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#refFromURL
|
||||
* @param url
|
||||
* @returns {StorageReference}
|
||||
*/
|
||||
refFromURL(url: string): StorageRef {
|
||||
// TODO don't think this is correct?
|
||||
return new StorageRef(this, `url::${url}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* setMaxOperationRetryTime
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxOperationRetryTime
|
||||
* @param time The new maximum operation retry time in milliseconds.
|
||||
*/
|
||||
setMaxOperationRetryTime(time: number): void {
|
||||
getNativeModule(this).setMaxOperationRetryTime(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* setMaxUploadRetryTime
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Storage#setMaxUploadRetryTime
|
||||
* @param time The new maximum upload retry time in milliseconds.
|
||||
*/
|
||||
setMaxUploadRetryTime(time: number): void {
|
||||
getNativeModule(this).setMaxUploadRetryTime(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* setMaxDownloadRetryTime
|
||||
* @url N/A
|
||||
* @param time The new maximum download retry time in milliseconds.
|
||||
*/
|
||||
setMaxDownloadRetryTime(time: number): void {
|
||||
getNativeModule(this).setMaxDownloadRetryTime(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNALS
|
||||
*/
|
||||
_getSubEventName(path: string, eventName: string) {
|
||||
return getAppEventName(this, `${path}-${eventName}`);
|
||||
}
|
||||
|
||||
_handleStorageEvent(event: Object) {
|
||||
const { path, eventName } = event;
|
||||
const body = event.body || {};
|
||||
|
||||
getLogger(this).debug('_handleStorageEvent: ', path, eventName, body);
|
||||
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
|
||||
}
|
||||
|
||||
_handleStorageError(err: Object) {
|
||||
const { path, eventName } = err;
|
||||
const body = err.body || {};
|
||||
|
||||
getLogger(this).debug('_handleStorageError ->', err);
|
||||
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
|
||||
}
|
||||
|
||||
_addListener(
|
||||
path: string,
|
||||
eventName: string,
|
||||
cb: (evt: Object) => Object
|
||||
): void {
|
||||
SharedEventEmitter.addListener(this._getSubEventName(path, eventName), cb);
|
||||
}
|
||||
|
||||
_removeListener(
|
||||
path: string,
|
||||
eventName: string,
|
||||
origCB: (evt: Object) => Object
|
||||
): void {
|
||||
SharedEventEmitter.removeListener(
|
||||
this._getSubEventName(path, eventName),
|
||||
origCB
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics = {
|
||||
TaskEvent: {
|
||||
STATE_CHANGED: 'state_changed',
|
||||
},
|
||||
TaskState: {
|
||||
RUNNING: 'running',
|
||||
PAUSED: 'paused',
|
||||
SUCCESS: 'success',
|
||||
CANCELLED: 'cancelled',
|
||||
ERROR: 'error',
|
||||
},
|
||||
Native: FirebaseStorage
|
||||
? {
|
||||
MAIN_BUNDLE_PATH: FirebaseStorage.MAIN_BUNDLE_PATH,
|
||||
CACHES_DIRECTORY_PATH: FirebaseStorage.CACHES_DIRECTORY_PATH,
|
||||
DOCUMENT_DIRECTORY_PATH: FirebaseStorage.DOCUMENT_DIRECTORY_PATH,
|
||||
EXTERNAL_DIRECTORY_PATH: FirebaseStorage.EXTERNAL_DIRECTORY_PATH,
|
||||
EXTERNAL_STORAGE_DIRECTORY_PATH:
|
||||
FirebaseStorage.EXTERNAL_STORAGE_DIRECTORY_PATH,
|
||||
TEMP_DIRECTORY_PATH: FirebaseStorage.TEMP_DIRECTORY_PATH,
|
||||
LIBRARY_DIRECTORY_PATH: FirebaseStorage.LIBRARY_DIRECTORY_PATH,
|
||||
FILETYPE_REGULAR: FirebaseStorage.FILETYPE_REGULAR,
|
||||
FILETYPE_DIRECTORY: FirebaseStorage.FILETYPE_DIRECTORY,
|
||||
}
|
||||
: {},
|
||||
};
|
|
@ -1,106 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* StorageReference representation wrapper
|
||||
*/
|
||||
import ReferenceBase from '../../utils/ReferenceBase';
|
||||
import StorageTask, { UPLOAD_TASK, DOWNLOAD_TASK } from './task';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
import type Storage from './';
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference
|
||||
*/
|
||||
export default class StorageReference extends ReferenceBase {
|
||||
_storage: Storage;
|
||||
|
||||
constructor(storage: Storage, path: string) {
|
||||
super(path);
|
||||
this._storage = storage;
|
||||
}
|
||||
|
||||
get fullPath(): string {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `gs://${this._storage.app.options.storageBucket}${this.path}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#child
|
||||
* @param path
|
||||
* @returns {StorageReference}
|
||||
*/
|
||||
child(path: string): StorageReference {
|
||||
return new StorageReference(this._storage, `${this.path}/${path}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
|
||||
* @returns {Promise.<T>|*}
|
||||
*/
|
||||
delete(): Promise<void> {
|
||||
return getNativeModule(this._storage).delete(this.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getDownloadURL
|
||||
* @returns {Promise.<T>|*}
|
||||
*/
|
||||
getDownloadURL(): Promise<string> {
|
||||
return getNativeModule(this._storage).getDownloadURL(this.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
|
||||
* @returns {Promise.<T>|*}
|
||||
*/
|
||||
getMetadata(): Promise<Object> {
|
||||
return getNativeModule(this._storage).getMetadata(this.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
|
||||
* @param metadata
|
||||
* @returns {Promise.<T>|*}
|
||||
*/
|
||||
updateMetadata(metadata: Object = {}): Promise<Object> {
|
||||
return getNativeModule(this._storage).updateMetadata(this.path, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a reference to the device
|
||||
* @param {String} filePath Where to store the file
|
||||
* @return {Promise}
|
||||
*/
|
||||
downloadFile(filePath: string): Promise<Object> {
|
||||
return new StorageTask(
|
||||
DOWNLOAD_TASK,
|
||||
getNativeModule(this._storage).downloadFile(this.path, filePath),
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias to putFile
|
||||
* @returns {StorageReference.putFile}
|
||||
*/
|
||||
get put(): (Object, Object) => Promise<Object> {
|
||||
return this.putFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file path
|
||||
* @param {string} filePath The local path of the file
|
||||
* @param {object} metadata An object containing metadata
|
||||
* @return {Promise}
|
||||
*/
|
||||
putFile(filePath: Object, metadata: Object = {}): Promise<Object> {
|
||||
const _filePath = filePath.replace('file://', '');
|
||||
return new StorageTask(
|
||||
UPLOAD_TASK,
|
||||
getNativeModule(this._storage).putFile(this.path, _filePath, metadata),
|
||||
this
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
* UploadTask representation wrapper
|
||||
*/
|
||||
import { statics as StorageStatics } from './';
|
||||
import { isFunction } from './../../utils';
|
||||
import type Storage from './';
|
||||
import type StorageReference from './reference';
|
||||
|
||||
export const UPLOAD_TASK = 'upload';
|
||||
export const DOWNLOAD_TASK = 'download';
|
||||
|
||||
declare type UploadTaskSnapshotType = {
|
||||
bytesTransferred: number,
|
||||
downloadURL: string | null,
|
||||
metadata: Object, // TODO flow type def for https://firebase.google.com/docs/reference/js/firebase.storage.FullMetadata.html
|
||||
ref: StorageReference,
|
||||
state:
|
||||
| typeof StorageStatics.TaskState.RUNNING
|
||||
| typeof StorageStatics.TaskState.PAUSED
|
||||
| typeof StorageStatics.TaskState.SUCCESS
|
||||
| typeof StorageStatics.TaskState.CANCELLED
|
||||
| typeof StorageStatics.TaskState.ERROR,
|
||||
task: StorageTask,
|
||||
totalBytes: number,
|
||||
};
|
||||
|
||||
declare type FuncSnapshotType =
|
||||
| null
|
||||
| ((snapshot: UploadTaskSnapshotType) => any);
|
||||
|
||||
declare type FuncErrorType = null | ((error: Error) => any);
|
||||
|
||||
declare type NextOrObserverType =
|
||||
| null
|
||||
| {
|
||||
next?: FuncSnapshotType,
|
||||
error?: FuncErrorType,
|
||||
complete?: FuncSnapshotType,
|
||||
}
|
||||
| FuncSnapshotType;
|
||||
|
||||
/**
|
||||
* @url https://firebase.google.com/docs/reference/js/firebase.storage.UploadTask
|
||||
*/
|
||||
export default class StorageTask {
|
||||
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK;
|
||||
ref: StorageReference;
|
||||
storage: Storage;
|
||||
path: string;
|
||||
then: () => Promise<*>;
|
||||
catch: () => Promise<*>;
|
||||
|
||||
constructor(
|
||||
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK,
|
||||
promise: Promise<*>,
|
||||
storageRef: StorageReference
|
||||
) {
|
||||
this.type = type;
|
||||
this.ref = storageRef;
|
||||
this.storage = storageRef._storage;
|
||||
this.path = storageRef.path;
|
||||
|
||||
// 'proxy' original promise
|
||||
this.then = promise.then.bind(promise);
|
||||
this.catch = promise.catch.bind(promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts a native snapshot result object attaches ref / task instances
|
||||
* and calls the original function
|
||||
* @returns {Promise.<T>}
|
||||
* @private
|
||||
*/
|
||||
_interceptSnapshotEvent(f: ?Function): null | (() => *) {
|
||||
if (!isFunction(f)) return null;
|
||||
return snapshot => {
|
||||
const _snapshot = Object.assign({}, snapshot);
|
||||
_snapshot.task = this;
|
||||
_snapshot.ref = this.ref;
|
||||
return f && f(_snapshot);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts a error object form native and converts to a JS Error
|
||||
* @param f
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_interceptErrorEvent(f: ?Function): null | (Error => *) {
|
||||
if (!isFunction(f)) return null;
|
||||
return error => {
|
||||
const _error = new Error(error.message);
|
||||
// $FlowExpectedError
|
||||
_error.code = error.code;
|
||||
return f && f(_error);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nextOrObserver
|
||||
* @param error
|
||||
* @param complete
|
||||
* @returns {function()}
|
||||
* @private
|
||||
*/
|
||||
_subscribe(
|
||||
nextOrObserver: NextOrObserverType,
|
||||
error: FuncErrorType,
|
||||
complete: FuncSnapshotType
|
||||
): Function {
|
||||
let _error;
|
||||
let _next;
|
||||
let _complete;
|
||||
|
||||
if (typeof nextOrObserver === 'function') {
|
||||
_error = this._interceptErrorEvent(error);
|
||||
_next = this._interceptSnapshotEvent(nextOrObserver);
|
||||
_complete = this._interceptSnapshotEvent(complete);
|
||||
} else if (nextOrObserver) {
|
||||
_error = this._interceptErrorEvent(nextOrObserver.error);
|
||||
_next = this._interceptSnapshotEvent(nextOrObserver.next);
|
||||
_complete = this._interceptSnapshotEvent(nextOrObserver.complete);
|
||||
}
|
||||
|
||||
if (_next) {
|
||||
this.storage._addListener(
|
||||
this.path,
|
||||
StorageStatics.TaskEvent.STATE_CHANGED,
|
||||
_next
|
||||
);
|
||||
}
|
||||
if (_error) {
|
||||
this.storage._addListener(this.path, `${this.type}_failure`, _error);
|
||||
}
|
||||
if (_complete) {
|
||||
this.storage._addListener(this.path, `${this.type}_success`, _complete);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (_next)
|
||||
this.storage._removeListener(
|
||||
this.path,
|
||||
StorageStatics.TaskEvent.STATE_CHANGED,
|
||||
_next
|
||||
);
|
||||
if (_error)
|
||||
this.storage._removeListener(this.path, `${this.type}_failure`, _error);
|
||||
if (_complete)
|
||||
this.storage._removeListener(
|
||||
this.path,
|
||||
`${this.type}_success`,
|
||||
_complete
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event
|
||||
* @param nextOrObserver
|
||||
* @param error
|
||||
* @param complete
|
||||
* @returns {function()}
|
||||
*/
|
||||
on(
|
||||
event: string = StorageStatics.TaskEvent.STATE_CHANGED,
|
||||
nextOrObserver: NextOrObserverType,
|
||||
error: FuncErrorType,
|
||||
complete: FuncSnapshotType
|
||||
): Function {
|
||||
if (!event) {
|
||||
throw new Error(
|
||||
"StorageTask.on listener is missing required string argument 'event'."
|
||||
);
|
||||
}
|
||||
|
||||
if (event !== StorageStatics.TaskEvent.STATE_CHANGED) {
|
||||
throw new Error(
|
||||
`StorageTask.on event argument must be a string with a value of '${
|
||||
StorageStatics.TaskEvent.STATE_CHANGED
|
||||
}'`
|
||||
);
|
||||
}
|
||||
|
||||
// if only event provided return the subscriber function
|
||||
if (!nextOrObserver && !error && !complete) {
|
||||
return this._subscribe.bind(this);
|
||||
}
|
||||
|
||||
return this._subscribe(nextOrObserver, error, complete);
|
||||
}
|
||||
|
||||
pause() {
|
||||
throw new Error(
|
||||
'.pause() is not currently supported by react-native-firebase'
|
||||
);
|
||||
}
|
||||
|
||||
resume() {
|
||||
// todo
|
||||
throw new Error(
|
||||
'.resume() is not currently supported by react-native-firebase'
|
||||
);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
// todo
|
||||
throw new Error(
|
||||
'.cancel() is not currently supported by react-native-firebase'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
// @flow
|
||||
import { NativeModules } from 'react-native';
|
||||
import INTERNALS from '../../utils/internals';
|
||||
import { isIOS } from '../../utils';
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import type App from '../core/app';
|
||||
|
||||
const FirebaseCoreModule = NativeModules.RNFirebase;
|
||||
|
||||
type GoogleApiAvailabilityType = {
|
||||
status: number,
|
||||
isAvailable: boolean,
|
||||
isUserResolvableError?: boolean,
|
||||
hasResolution?: boolean,
|
||||
error?: string,
|
||||
};
|
||||
|
||||
export const MODULE_NAME = 'RNFirebaseUtils';
|
||||
export const NAMESPACE = 'utils';
|
||||
|
||||
export default class RNFirebaseUtils extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
moduleName: MODULE_NAME,
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
checkPlayServicesAvailability() {
|
||||
if (isIOS) return;
|
||||
|
||||
const { status } = this.playServicesAvailability;
|
||||
|
||||
if (!this.playServicesAvailability.isAvailable) {
|
||||
if (
|
||||
INTERNALS.OPTIONS.promptOnMissingPlayServices &&
|
||||
this.playServicesAvailability.isUserResolvableError
|
||||
) {
|
||||
this.promptForPlayServices();
|
||||
} else {
|
||||
const error = INTERNALS.STRINGS.ERROR_PLAY_SERVICES(status);
|
||||
if (INTERNALS.OPTIONS.errorOnMissingPlayServices) {
|
||||
if (status === 2)
|
||||
console.warn(error); // only warn if it exists but may need an update
|
||||
else throw new Error(error);
|
||||
} else {
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promptForPlayServices() {
|
||||
if (isIOS) return null;
|
||||
return FirebaseCoreModule.promptForPlayServices();
|
||||
}
|
||||
|
||||
resolutionForPlayServices() {
|
||||
if (isIOS) return null;
|
||||
return FirebaseCoreModule.resolutionForPlayServices();
|
||||
}
|
||||
|
||||
makePlayServicesAvailable() {
|
||||
if (isIOS) return null;
|
||||
return FirebaseCoreModule.makePlayServicesAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the global logging level for all logs.
|
||||
*
|
||||
* @param logLevel
|
||||
*/
|
||||
set logLevel(logLevel: string) {
|
||||
INTERNALS.OPTIONS.logLevel = logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = {};
|
|
@ -1,232 +0,0 @@
|
|||
/* @flow */
|
||||
import type AdMob from '../modules/admob';
|
||||
import { typeof statics as AdMobStatics } from '../modules/admob';
|
||||
import type Analytics from '../modules/analytics';
|
||||
import { typeof statics as AnalyticsStatics } from '../modules/analytics';
|
||||
import type Auth from '../modules/auth';
|
||||
import { typeof statics as AuthStatics } from '../modules/auth';
|
||||
import type Config from '../modules/config';
|
||||
import { typeof statics as ConfigStatics } from '../modules/config';
|
||||
import type Crash from '../modules/crash';
|
||||
import { typeof statics as CrashStatics } from '../modules/crash';
|
||||
import type Crashlytics from '../modules/fabric/crashlytics';
|
||||
import { typeof statics as CrashlyticsStatics } from '../modules/fabric/crashlytics';
|
||||
import type Database from '../modules/database';
|
||||
import { typeof statics as DatabaseStatics } from '../modules/database';
|
||||
import type Firestore from '../modules/firestore';
|
||||
import { typeof statics as FirestoreStatics } from '../modules/firestore';
|
||||
import type InstanceId from '../modules/instanceid';
|
||||
import { typeof statics as InstanceIdStatics } from '../modules/instanceid';
|
||||
import type Invites from '../modules/invites';
|
||||
import { typeof statics as InvitesStatics } from '../modules/invites';
|
||||
import type Links from '../modules/links';
|
||||
import { typeof statics as LinksStatics } from '../modules/links';
|
||||
import type Messaging from '../modules/messaging';
|
||||
import { typeof statics as MessagingStatics } from '../modules/messaging';
|
||||
import type Notifications from '../modules/notifications';
|
||||
import { typeof statics as NotificationsStatics } from '../modules/notifications';
|
||||
import type ModuleBase from '../utils/ModuleBase';
|
||||
import type Performance from '../modules/perf';
|
||||
import { typeof statics as PerformanceStatics } from '../modules/perf';
|
||||
import type Storage from '../modules/storage';
|
||||
import { typeof statics as StorageStatics } from '../modules/storage';
|
||||
import type Utils from '../modules/utils';
|
||||
import { typeof statics as UtilsStatics } from '../modules/utils';
|
||||
|
||||
/* Core types */
|
||||
export type FirebaseError = {
|
||||
message: string,
|
||||
name: string,
|
||||
code: string,
|
||||
stack: string,
|
||||
path: string,
|
||||
details: string,
|
||||
modifiers: string,
|
||||
};
|
||||
|
||||
export type FirebaseModule = $Subtype<ModuleBase>;
|
||||
|
||||
export type FirebaseModuleConfig = {
|
||||
events?: string[],
|
||||
moduleName: FirebaseModuleName,
|
||||
multiApp: boolean,
|
||||
hasShards: boolean,
|
||||
namespace: FirebaseNamespace,
|
||||
};
|
||||
|
||||
export type FirebaseModuleName =
|
||||
| 'RNFirebaseAdMob'
|
||||
| 'RNFirebaseAnalytics'
|
||||
| 'RNFirebaseAuth'
|
||||
| 'RNFirebaseRemoteConfig'
|
||||
| 'RNFirebaseCrash'
|
||||
| 'RNFirebaseCrashlytics'
|
||||
| 'RNFirebaseDatabase'
|
||||
| 'RNFirebaseFirestore'
|
||||
| 'RNFirebaseInstanceId'
|
||||
| 'RNFirebaseInvites'
|
||||
| 'RNFirebaseLinks'
|
||||
| 'RNFirebaseMessaging'
|
||||
| 'RNFirebaseNotifications'
|
||||
| 'RNFirebasePerformance'
|
||||
| 'RNFirebaseStorage'
|
||||
| 'RNFirebaseUtils';
|
||||
|
||||
export type FirebaseNamespace =
|
||||
| 'admob'
|
||||
| 'analytics'
|
||||
| 'auth'
|
||||
| 'config'
|
||||
| 'crash'
|
||||
| 'crashlytics'
|
||||
| 'database'
|
||||
| 'firestore'
|
||||
| 'instanceid'
|
||||
| 'invites'
|
||||
| 'links'
|
||||
| 'messaging'
|
||||
| 'notifications'
|
||||
| 'perf'
|
||||
| 'storage'
|
||||
| 'utils';
|
||||
|
||||
export type FirebaseOptions = {
|
||||
apiKey: string,
|
||||
appId: string,
|
||||
databaseURL: string,
|
||||
messagingSenderId: string,
|
||||
projectId: string,
|
||||
storageBucket: string,
|
||||
};
|
||||
|
||||
export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = {
|
||||
(): M,
|
||||
nativeModuleExists: boolean,
|
||||
} & S;
|
||||
|
||||
export type FirebaseStatics = $Subtype<Object>;
|
||||
|
||||
/* Admob types */
|
||||
|
||||
export type AdMobModule = {
|
||||
(): AdMob,
|
||||
nativeModuleExists: boolean,
|
||||
} & AdMobStatics;
|
||||
|
||||
/* Analytics types */
|
||||
|
||||
export type AnalyticsModule = {
|
||||
(): Analytics,
|
||||
nativeModuleExists: boolean,
|
||||
} & AnalyticsStatics;
|
||||
|
||||
/* Remote Config types */
|
||||
|
||||
export type ConfigModule = {
|
||||
(): Config,
|
||||
nativeModuleExists: boolean,
|
||||
} & ConfigStatics;
|
||||
|
||||
/* Auth types */
|
||||
|
||||
export type AuthModule = {
|
||||
(): Auth,
|
||||
nativeModuleExists: boolean,
|
||||
} & AuthStatics;
|
||||
|
||||
/* Crash types */
|
||||
|
||||
export type CrashModule = {
|
||||
(): Crash,
|
||||
nativeModuleExists: boolean,
|
||||
} & CrashStatics;
|
||||
|
||||
/* Database types */
|
||||
|
||||
export type DatabaseModule = {
|
||||
(): Database,
|
||||
nativeModuleExists: boolean,
|
||||
} & DatabaseStatics;
|
||||
|
||||
export type DatabaseModifier = {
|
||||
id: string,
|
||||
type: 'orderBy' | 'limit' | 'filter',
|
||||
name?: string,
|
||||
key?: string,
|
||||
limit?: number,
|
||||
value?: any,
|
||||
valueType?: string,
|
||||
};
|
||||
|
||||
/* Fabric types */
|
||||
export type CrashlyticsModule = {
|
||||
(): Crashlytics,
|
||||
nativeModuleExists: boolean,
|
||||
} & CrashlyticsStatics;
|
||||
|
||||
export type FabricModule = {
|
||||
crashlytics: CrashlyticsModule,
|
||||
};
|
||||
|
||||
/* Firestore types */
|
||||
|
||||
export type FirestoreModule = {
|
||||
(): Firestore,
|
||||
nativeModuleExists: boolean,
|
||||
} & FirestoreStatics;
|
||||
|
||||
/* InstanceId types */
|
||||
|
||||
export type InstanceIdModule = {
|
||||
(): InstanceId,
|
||||
nativeModuleExists: boolean,
|
||||
} & InstanceIdStatics;
|
||||
|
||||
/* Invites types */
|
||||
|
||||
export type InvitesModule = {
|
||||
(): Invites,
|
||||
nativeModuleExists: boolean,
|
||||
} & InvitesStatics;
|
||||
|
||||
/* Links types */
|
||||
|
||||
export type LinksModule = {
|
||||
(): Links,
|
||||
nativeModuleExists: boolean,
|
||||
} & LinksStatics;
|
||||
|
||||
/* Messaging types */
|
||||
|
||||
export type MessagingModule = {
|
||||
(): Messaging,
|
||||
nativeModuleExists: boolean,
|
||||
} & MessagingStatics;
|
||||
|
||||
/* Notifications types */
|
||||
|
||||
export type NotificationsModule = {
|
||||
(): Notifications,
|
||||
nativeModuleExists: boolean,
|
||||
} & NotificationsStatics;
|
||||
|
||||
/* Performance types */
|
||||
|
||||
export type PerformanceModule = {
|
||||
(): Performance,
|
||||
nativeModuleExists: boolean,
|
||||
} & PerformanceStatics;
|
||||
|
||||
/* Storage types */
|
||||
|
||||
export type StorageModule = {
|
||||
(): Storage,
|
||||
nativeModuleExists: boolean,
|
||||
} & StorageStatics;
|
||||
|
||||
/* Utils types */
|
||||
|
||||
export type UtilsModule = {
|
||||
(): Utils,
|
||||
nativeModuleExists: boolean,
|
||||
} & UtilsStatics;
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
import { initialiseLogger } from './log';
|
||||
import { initialiseNativeModule } from './native';
|
||||
|
||||
import type App from '../modules/core/app';
|
||||
import type { FirebaseModuleConfig, FirebaseNamespace } from '../types';
|
||||
|
||||
export default class ModuleBase {
|
||||
_app: App;
|
||||
_serviceUrl: ?string;
|
||||
namespace: FirebaseNamespace;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param app
|
||||
* @param config
|
||||
*/
|
||||
constructor(app: App, config: FirebaseModuleConfig, serviceUrl: ?string) {
|
||||
if (!config.moduleName) {
|
||||
throw new Error('Missing module name');
|
||||
}
|
||||
if (!config.namespace) {
|
||||
throw new Error('Missing namespace');
|
||||
}
|
||||
const { moduleName } = config;
|
||||
this._app = app;
|
||||
this._serviceUrl = serviceUrl;
|
||||
this.namespace = config.namespace;
|
||||
|
||||
// check if native module exists as all native
|
||||
initialiseNativeModule(this, config, serviceUrl);
|
||||
initialiseLogger(
|
||||
this,
|
||||
`${app.name}:${moduleName.replace('RNFirebase', '')}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the App instance for current module
|
||||
* @return {*}
|
||||
*/
|
||||
get app(): App {
|
||||
return this._app;
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
export default class ReferenceBase {
|
||||
path: string;
|
||||
|
||||
constructor(path: string) {
|
||||
if (path) {
|
||||
this.path =
|
||||
path.length > 1 && path.endsWith('/')
|
||||
? path.substring(0, path.length - 1)
|
||||
: path;
|
||||
} else {
|
||||
this.path = '/';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The last part of a Reference's path (after the last '/')
|
||||
* The key of a root Reference is null.
|
||||
* @type {String}
|
||||
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#key}
|
||||
*/
|
||||
get key(): string | null {
|
||||
return this.path === '/'
|
||||
? null
|
||||
: this.path.substring(this.path.lastIndexOf('/') + 1);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue