[bridge] import RNF from project directory directly via module alias + metro config

This commit is contained in:
Salakar 2018-03-30 17:09:32 +01:00
parent ed2efcdd48
commit 2122554549
114 changed files with 86 additions and 15507 deletions

View File

@ -9,8 +9,16 @@
"useInlineSourceMaps": true,
"instrument": true,
"include": [
"firebase"
"lib/*"
],
"exclude": [
"node_modules"
]
}],
["module-resolver", {
"alias": {
"react-native-firebase": ".."
}
}]
]
}

View File

@ -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');

View File

@ -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();
};

View File

@ -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 });
});

View File

@ -1,6 +1,6 @@
--recursive
--timeout 120000
--slow 2200
--slow 750
--bail
--exit
--require bridge/platform/node

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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

View File

@ -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';

View File

@ -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;
});
},
};

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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',
};

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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,
};

View File

@ -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 = {};

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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')
);
}
}

View File

@ -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',
},
};

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
|};

View File

@ -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);
}
}

View File

@ -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 = {};

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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 = {};

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
},
};

View File

@ -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];
});
}
}
}
}

View File

@ -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 = {};

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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__');

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
);
}
}

View File

@ -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);
});
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
},
};

View File

@ -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,
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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 = {};

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};

View File

@ -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,
|};

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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(),
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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 = {};

View File

@ -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,
|};

View File

@ -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,
};
}
}

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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;
};

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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();
}
}

View File

@ -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;
};

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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,
};

View File

@ -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 },
|};

View File

@ -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);
}
}

View File

@ -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 = {};

View File

@ -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,
}
: {},
};

View File

@ -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
);
}
}

View File

@ -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'
);
}
}

View File

@ -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 = {};

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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