2
0
mirror of synced 2025-01-11 14:44:12 +00:00

Merge branch 'master' into typescript-definitions-static-module-fix

This commit is contained in:
pheromonez 2018-01-30 10:07:07 +11:00 committed by GitHub
commit 157f0109cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
130 changed files with 5316 additions and 2714 deletions

View File

@ -1,46 +1,36 @@
{
"extends": "airbnb",
"extends": [
"airbnb",
"prettier",
"prettier/flowtype",
"prettier/react"
],
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [
"flowtype"
"flowtype",
"prettier"
],
"env": {
"es6": true,
"jasmine": true
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": {
"prettier/prettier": ["error", {
"trailingComma": "es5",
"singleQuote": true
}],
"react/forbid-prop-types": "warn",
"react/jsx-filename-extension": [
"off", { "extensions": [".js", ".jsx"] }
],
"class-methods-use-this": 0,
"no-plusplus": 0,
"no-underscore-dangle": 0,
"no-return-assign": 0,
"no-undef": 0,
"no-use-before-define": 0,
"arrow-body-style": 0,
"import/prefer-default-export": 0,
"radix": 0,
"new-cap": 0,
"max-len": 0,
"no-continue": 0,
"no-console": 0,
"global-require": 0,
"import/extensions": 0,
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"react/jsx-filename-extension": 0,
"no-unused-expressions": 0,
"flowtype/no-unused-expressions": ['error', {
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
}]
"no-plusplus": 0,
"no-undef": 0,
"no-underscore-dangle": "off",
"no-use-before-define": 0
},
"globals": {
"__DEV__": true,

View File

@ -453,31 +453,15 @@
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"${BUILT_PRODUCTS_DIR}",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS",
"${SRCROOT}/../../../ios/Pods/Fabric/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAuth/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCrash/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDatabase/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseFirestore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseInstanceID/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseMessaging/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebasePerformance/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseRemoteConfig/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseStorage/Frameworks",
"${SRCROOT}/../../../ios/Pods/Google-Mobile-Ads-SDK/Frameworks/frameworks",
"${BUILT_PRODUCTS_DIR}/**",
"${SRCROOT}/../../../ios/Pods/**",
"${SRCROOT}/../../../ios/Firebase/**",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";
@ -498,31 +482,15 @@
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"${BUILT_PRODUCTS_DIR}",
"${SRCROOT}/../../../ios/Pods/Crashlytics/iOS",
"${SRCROOT}/../../../ios/Pods/Fabric/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAnalytics/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseAuth/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseCrash/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDatabase/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseDynamicLinks/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseFirestore/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseInstanceID/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseMessaging/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebasePerformance/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseRemoteConfig/Frameworks",
"${SRCROOT}/../../../ios/Pods/FirebaseStorage/Frameworks",
"${SRCROOT}/../../../ios/Pods/Google-Mobile-Ads-SDK/Frameworks/frameworks",
"${BUILT_PRODUCTS_DIR}/**",
"${SRCROOT}/../../../ios/Pods/**",
"${SRCROOT}/../../../ios/Firebase/**",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../react-native/React/**",
"${SRCROOT}/../../../ios/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Crashlytics",
"${SRCROOT}/../../../ios/Pods/Headers/Public/Fabric",
"${SRCROOT}/../../../ios/Pods/Firebase/**",
"${SRCROOT}/../../../ios/Pods/Headers/Public/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LIBRARY_SEARCH_PATHS = "$(inherited)";

262
lib/index.d.ts vendored
View File

@ -21,8 +21,8 @@ declare module "react-native-firebase" {
database: FirebaseModuleAndStatics<RNFirebase.database.Database, RNFirebase.database.DatabaseStatics>;
fabric: {
crashlytics: FirebaseModuleAndStatics<RNFirebase.crashlytics.Crashlytics>;
};
// firestore: FirebaseModuleAndStatics<RNFirebase.firestore.Firestore>;
};
firestore: FirebaseModuleAndStatics<RNFirebase.firestore.Firestore, RNFirebase.firestore.FirestoreStatics>;
links: FirebaseModuleAndStatics<RNFirebase.links.Links>;
messaging: FirebaseModuleAndStatics<RNFirebase.messaging.Messaging>;
// perf: FirebaseModuleAndStatics<RNFirebase.perf.Perf>;
@ -58,7 +58,7 @@ declare module "react-native-firebase" {
fabric: {
crashlytics(): RNFirebase.crashlytics.Crashlytics,
};
// firestore(): RNFirebase.firestore.Firestore;
firestore(): RNFirebase.firestore.Firestore;
links(): RNFirebase.links.Links;
messaging(): RNFirebase.messaging.Messaging;
// perf(): RNFirebase.perf.Performance;
@ -1043,7 +1043,7 @@ declare module "react-native-firebase" {
*/
onLink(listener: (url) => void): () => void;
}
/**
* Configuration when creating a Dynamic Link (standard or short). For
* more information about each parameter, see the official Firebase docs:
@ -1076,5 +1076,259 @@ declare module "react-native-firebase" {
},
}
}
namespace firestore {
interface Firestore {
batch(): WriteBatch;
collection(collectionPath: string): CollectionReference;
doc(documentPath: string): DocumentReference;
/** NOT SUPPORTED YET */
// enablePersistence(): Promise<void>;
/** NOT SUPPORTED YET */
// runTransaction(): Promise<any>;
/** NOT SUPPORTED YET */
// settings(): void;
}
interface FirestoreStatics {
FieldPath: typeof FieldPath;
FieldValue: typeof FieldValue;
GeoPoint: typeof GeoPoint;
enableLogging(enabled: boolean): void;
}
interface CollectionReference {
readonly firestore: Firestore;
readonly id: string;
readonly parent: DocumentReference;
add(data: object): Promise<DocumentReference>;
doc(documentPath?: string): DocumentReference;
endAt(snapshot: DocumentSnapshot): Query;
endAt(...varargs: any[]): Query;
endBefore(snapshot: DocumentSnapshot): Query;
endBefore(...varargs: any[]): Query;
get(): Promise<QuerySnapshot>;
limit(limit: number): Query;
onSnapshot(onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(observer: Query.Observer): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, observer: Query.Observer): () => void;
orderBy(fieldPath: string | FieldPath, directionStr?: Types.QueryDirection): Query;
startAfter(snapshot: DocumentSnapshot): Query;
startAfter(...varargs: any[]): Query;
startAt(snapshot: DocumentSnapshot): Query;
startAt(...varargs: any[]): Query;
where(fieldPath: string, op: Types.QueryOperator, value: any): Query;
}
interface DocumentChange {
readonly doc: DocumentSnapshot;
readonly newIndex: number;
readonly oldIndex: number;
readonly type: string;
}
interface DocumentReference {
readonly firestore: Firestore;
readonly id: string | null;
readonly parent: CollectionReference;
readonly path: string;
collection(collectionPath: string): CollectionReference;
delete(): Promise<void>;
get(): Promise<DocumentSnapshot>;
onSnapshot(onNext: DocumentReference.ObserverOnNext, onError?: DocumentReference.ObserverOnError): () => void;
onSnapshot(observer: DocumentReference.Observer): () => void;
onSnapshot(documentListenOptions: DocumentReference.DocumentListenOptions, onNext: DocumentReference.ObserverOnNext, onError?: DocumentReference.ObserverOnError): () => void;
onSnapshot(documentListenOptions: DocumentReference.DocumentListenOptions, observer: DocumentReference.Observer): () => void;
set(data: object, writeOptions?: Types.WriteOptions): Promise<void>;
update(obj: object): Promise<void>;
update(key1: Types.UpdateKey, val1: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any): Promise<void>;
update(key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any, key5: Types.UpdateKey, val5: any): Promise<void>;
}
namespace DocumentReference {
interface DocumentListenOptions {
includeMetadataChanges: boolean;
}
type ObserverOnNext = (documentSnapshot: DocumentSnapshot) => void;
type ObserverOnError = (err: object) => void;
interface Observer {
next: ObserverOnNext;
error?: ObserverOnError;
}
}
interface DocumentSnapshot {
readonly exists: boolean;
readonly id: string | null;
readonly metadata: Types.SnapshotMetadata;
readonly ref: DocumentReference;
data(): object | void;
get(fieldPath: string | FieldPath): any | undefined;
}
class FieldPath {
static documentId(): FieldPath;
constructor(...segments: string[]);
}
class FieldValue {
static delete(): FieldValue;
static serverTimestamp(): FieldValue;
}
class GeoPoint {
constructor(latitude: number, longitude: number);
readonly latitude: number;
readonly longitude: number;
}
class Path {
static fromName(name: string): Path;
constructor(pathComponents: string[]);
readonly id: string | null;
readonly isDocument: boolean;
readonly isCollection: boolean;
readonly relativeName: string;
child(relativePath: string): Path;
parent(): Path | null;
}
interface Query {
readonly firestore: Firestore;
endAt(snapshot: DocumentSnapshot): Query;
endAt(...varargs: any[]): Query;
endBefore(snapshot: DocumentSnapshot): Query;
endBefore(...varargs: any[]): Query;
get(): Promise<QuerySnapshot>;
limit(limit: number): Query;
onSnapshot(onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(observer: Query.Observer): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, onNext: Query.ObserverOnNext, onError?: Query.ObserverOnError): () => void;
onSnapshot(queryListenOptions: Query.QueryListenOptions, observer: Query.Observer): () => void;
orderBy(fieldPath: string | FieldPath, directionStr?: Types.QueryDirection): Query;
startAfter(snapshot: DocumentSnapshot): Query;
startAfter(...varargs: any[]): Query;
startAt(snapshot: DocumentSnapshot): Query;
startAt(...varargs: any[]): Query;
where(fieldPath: string, op: Types.QueryOperator, value: any): Query;
}
namespace Query {
interface NativeFieldPath {
elements?: string[];
string?: string;
type: 'fieldpath' | 'string';
}
interface FieldFilter {
fieldPath: NativeFieldPath;
operator: string;
value: any;
}
interface FieldOrder {
direction: string;
fieldPath: NativeFieldPath;
}
interface QueryOptions {
endAt?: any[];
endBefore?: any[];
limit?: number;
offset?: number;
selectFields?: string[];
startAfter?: any[];
startAt?: any[];
}
// The JS code expects at least one of 'includeDocumentMetadataChanges'
// or 'includeQueryMetadataChanges' to be defined.
interface _IncludeDocumentMetadataChanges {
includeDocumentMetadataChanges: boolean;
}
interface _IncludeQueryMetadataChanges {
includeQueryMetadataChanges: boolean;
}
type QueryListenOptions = _IncludeDocumentMetadataChanges | _IncludeQueryMetadataChanges | (_IncludeDocumentMetadataChanges & _IncludeQueryMetadataChanges);
type ObserverOnNext = (querySnapshot: QuerySnapshot) => void;
type ObserverOnError = (err: object) => void;
interface Observer {
next: ObserverOnNext;
error?: ObserverOnError;
}
}
interface QuerySnapshot {
readonly docChanges: DocumentChange[];
readonly docs: DocumentSnapshot[];
readonly empty: boolean;
readonly metadata: Types.SnapshotMetadata;
readonly query: Query;
readonly size: number;
forEach(callback: (snapshot: DocumentSnapshot) => any);
}
namespace QuerySnapshot {
interface NativeData {
changes: Types.NativeDocumentChange[];
documents: Types.NativeDocumentSnapshot[];
metadata: Types.SnapshotMetadata;
}
}
interface WriteBatch {
commit(): Promise<void>;
delete(docRef: DocumentReference): WriteBatch;
set(docRef: DocumentReference, data: object, options?: Types.WriteOptions): WriteBatch;
// multiple overrides for update() to allow strong-typed var_args
update(docRef: DocumentReference, obj: object): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any): WriteBatch;
update(docRef: DocumentReference, key1: Types.UpdateKey, val1: any, key2: Types.UpdateKey, val2: any, key3: Types.UpdateKey, val3: any, key4: Types.UpdateKey, val4: any, key5: Types.UpdateKey, val5: any): WriteBatch;
}
namespace Types {
interface NativeDocumentChange {
document: NativeDocumentSnapshot;
newIndex: number;
oldIndex: number;
type: string;
}
interface NativeDocumentSnapshot {
data: {
[key: string]: TypeMap;
};
metadata: SnapshotMetadata;
path: string;
}
interface SnapshotMetadata {
fromCache: boolean;
hasPendingWrites: boolean;
}
type QueryDirection = 'asc' | 'ASC' | 'desc' | 'DESC';
type QueryOperator = '=' | '==' | '>' | '>=' | '<' | '<=';
interface TypeMap {
type: 'array' | 'boolean' | 'date' | 'documentid' | 'fieldvalue' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string';
value: any;
}
/** The key in update() function for DocumentReference and WriteBatch. */
type UpdateKey = string | FieldPath
interface WriteOptions {
merge?: boolean;
}
}
}
}
}

View File

@ -11,13 +11,15 @@ 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) => {
Object.keys(EventTypes).forEach(eventType => {
adMobPropTypes[eventType] = PropTypes.func;
});
Object.keys(NativeExpressEventTypes).forEach((eventType) => {
Object.keys(NativeExpressEventTypes).forEach(eventType => {
adMobPropTypes[eventType] = PropTypes.func;
});
@ -67,7 +69,8 @@ class AdMobComponent extends React.Component {
}
}
if (nativeEvent.type === 'onSizeChange') this.updateSize(nativeEvent.payload);
if (nativeEvent.type === 'onSizeChange')
this.updateSize(nativeEvent.payload);
};
/**

View File

@ -1,5 +1,4 @@
export default class AdRequest {
constructor() {
this._props = {
keywords: [],
@ -12,7 +11,7 @@ export default class AdRequest {
}
addTestDevice(deviceId?: string) {
this._props.testDevices.push(deviceId ? deviceId : 'DEVICE_ID_EMULATOR');
this._props.testDevices.push(deviceId || 'DEVICE_ID_EMULATOR');
return this;
}

View File

@ -2,12 +2,7 @@ import React from 'react';
import AdMobComponent from './AdMobComponent';
function Banner({ ...props }) {
return (
<AdMobComponent
{...props}
class={'RNFirebaseAdMobBanner'}
/>
);
return <AdMobComponent {...props} class="RNFirebaseAdMobBanner" />;
}
Banner.propTypes = AdMobComponent.propTypes;

View File

@ -1,4 +1,3 @@
export default {
onAdLoaded: 'onAdLoaded',
onAdOpened: 'onAdOpened',
@ -18,4 +17,4 @@ export const NativeExpressEventTypes = {
export const RewardedVideoEventTypes = {
onRewarded: 'onRewarded',
onRewardedVideoStarted: 'onRewardedVideoStarted',
};
};

View File

@ -9,7 +9,6 @@ const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
let subscriptions = [];
export default class Interstitial {
constructor(admob: Object, adUnit: string) {
// Interstitials on iOS require a new instance each time
if (Platform.OS === 'ios') {
@ -25,7 +24,10 @@ export default class Interstitial {
this.adUnit = adUnit;
this.loaded = false;
SharedEventEmitter.removeAllListeners(`interstitial_${adUnit}`);
SharedEventEmitter.addListener(`interstitial_${adUnit}`, this._onInterstitialEvent);
SharedEventEmitter.addListener(
`interstitial_${adUnit}`,
this._onInterstitialEvent
);
}
/**
@ -33,7 +35,7 @@ export default class Interstitial {
* @param event
* @private
*/
_onInterstitialEvent = (event) => {
_onInterstitialEvent = event => {
const eventType = `interstitial:${this.adUnit}:${event.type}`;
let emitData = Object.assign({}, event);
@ -94,11 +96,18 @@ export default class Interstitial {
*/
on(eventType, listenerCb) {
if (!statics.EventTypes[eventType]) {
console.warn(`Invalid event type provided, must be one of: ${Object.keys(statics.EventTypes).join(', ')}`);
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);
const sub = SharedEventEmitter.addListener(
`interstitial:${this.adUnit}:${eventType}`,
listenerCb
);
subscriptions.push(sub);
return sub;
}

View File

@ -2,12 +2,7 @@ import React from 'react';
import AdMobComponent from './AdMobComponent';
function NativeExpress({ ...props }) {
return (
<AdMobComponent
{...props}
class={'RNFirebaseAdMobNativeExpress'}
/>
);
return <AdMobComponent {...props} class="RNFirebaseAdMobNativeExpress" />;
}
NativeExpress.propTypes = AdMobComponent.propTypes;

View File

@ -9,7 +9,6 @@ const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
let subscriptions = [];
export default class RewardedVideo {
constructor(admob: Object, adUnit: string) {
for (let i = 0, len = subscriptions.length; i < len; i++) {
subscriptions[i].remove();
@ -20,7 +19,10 @@ export default class RewardedVideo {
this.adUnit = adUnit;
this.loaded = false;
SharedEventEmitter.removeAllListeners(`rewarded_video_${adUnit}`);
SharedEventEmitter.addListener(`rewarded_video_${adUnit}`, this._onRewardedVideoEvent);
SharedEventEmitter.addListener(
`rewarded_video_${adUnit}`,
this._onRewardedVideoEvent
);
}
/**
@ -28,7 +30,7 @@ export default class RewardedVideo {
* @param event
* @private
*/
_onRewardedVideoEvent = (event) => {
_onRewardedVideoEvent = event => {
const eventType = `rewarded_video:${this.adUnit}:${event.type}`;
let emitData = Object.assign({}, event);
@ -94,11 +96,18 @@ export default class RewardedVideo {
};
if (!types[eventType]) {
console.warn(`Invalid event type provided, must be one of: ${Object.keys(types).join(', ')}`);
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);
const sub = SharedEventEmitter.addListener(
`rewarded_video:${this.adUnit}:${eventType}`,
listenerCb
);
subscriptions.push(sub);
return sub;
}

View File

@ -1,5 +1,4 @@
export default class VideoOptions {
constructor() {
this._props = {
startMuted: true,

View File

@ -25,12 +25,9 @@ type NativeEvent = {
adUnit: string,
payload: Object,
type: string,
}
};
const NATIVE_EVENTS = [
'interstitial_event',
'rewarded_video_event',
];
const NATIVE_EVENTS = ['interstitial_event', 'rewarded_video_event'];
export const MODULE_NAME = 'RNFirebaseAdmob';
export const NAMESPACE = 'admob';
@ -50,8 +47,14 @@ export default class AdMob extends ModuleBase {
this._initialized = false;
this._appId = null;
SharedEventEmitter.addListener('interstitial_event', this._onInterstitialEvent.bind(this));
SharedEventEmitter.addListener('rewarded_video_event', this._onRewardedVideoEvent.bind(this));
SharedEventEmitter.addListener(
'interstitial_event',
this._onInterstitialEvent.bind(this)
);
SharedEventEmitter.addListener(
'rewarded_video_event',
this._onRewardedVideoEvent.bind(this)
);
}
_onInterstitialEvent(event: NativeEvent): void {
@ -88,7 +91,9 @@ export default class AdMob extends ModuleBase {
openDebugMenu(): void {
if (!this._initialized) {
getLogger(this).warn('AdMob needs to be initialized before opening the dev menu!');
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);

View File

@ -46,16 +46,21 @@ export default class Analytics extends ModuleBase {
logEvent(name: string, params: Object = {}): void {
// check name is not a reserved event name
if (ReservedEventNames.includes(name)) {
throw new Error(`event name '${name}' is a reserved event name and can not be used.`);
throw new Error(
`event name '${name}' is a reserved event name and can not be used.`
);
}
// name format validation
if (!AlphaNumericUnderscore.test(name)) {
throw new Error(`Event name '${name}' is invalid. Names should contain 1 to 32 alphanumeric characters or underscores.`);
throw new Error(
`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('Maximum number of parameters exceeded (25).');
if (params && Object.keys(params).length > 25)
throw new Error('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
@ -121,9 +126,9 @@ export default class Analytics extends ModuleBase {
* @param object
*/
setUserProperties(object: Object): void {
for (const property of Object.keys(object)) {
Object.keys(object).forEach(property => {
getNativeModule(this).setUserProperty(property, object[property]);
}
});
}
}

View File

@ -1,7 +1,14 @@
// @flow
import INTERNALS from '../../utils/internals';
import { SharedEventEmitter } from '../../utils/events';
import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from '../../utils';
import {
generatePushID,
isFunction,
isAndroid,
isIOS,
isString,
nativeToJSError,
} from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
@ -50,9 +57,15 @@ export default class PhoneAuthListener {
// 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`,
verificationFailed: `phone:auth:${
this._phoneAuthRequestKey
}:onVerificationFailed`,
verificationComplete: `phone:auth:${
this._phoneAuthRequestKey
}:onVerificationComplete`,
codeAutoRetrievalTimeout: `phone:auth:${
this._phoneAuthRequestKey
}:onCodeAutoRetrievalTimeout`,
};
// user observer events
@ -73,14 +86,14 @@ export default class PhoneAuthListener {
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._timeout,
this._timeout
);
}
if (isIOS) {
getNativeModule(this._auth).verifyPhoneNumber(
phoneNumber,
this._phoneAuthRequestKey,
this._phoneAuthRequestKey
);
}
}
@ -94,8 +107,11 @@ export default class PhoneAuthListener {
for (let i = 0, len = events.length; i < len; i++) {
const type = events[i];
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
SharedEventEmitter.once(this._internalEvents[type], this[`_${type}Handler`].bind(this));
SharedEventEmitter.once(
this._internalEvents[type],
// $FlowBug: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
this[`_${type}Handler`].bind(this)
);
}
}
@ -143,14 +159,15 @@ export default class PhoneAuthListener {
* @private
*/
_removeAllListeners() {
setTimeout(() => { // move to next event loop - not sure if needed
setTimeout(() => {
// move to next event loop - not sure if needed
// internal listeners
Object.values(this._internalEvents).forEach((event) => {
Object.values(this._internalEvents).forEach(event => {
SharedEventEmitter.removeAllListeners(event);
});
// user observer listeners
Object.values(this._publicEvents).forEach((publicEvent) => {
Object.values(this._publicEvents).forEach(publicEvent => {
SharedEventEmitter.removeAllListeners(publicEvent);
});
}, 0);
@ -163,12 +180,12 @@ export default class PhoneAuthListener {
_promiseDeferred() {
if (!this._promise) {
this._promise = new Promise((resolve, reject) => {
this._resolve = (result) => {
this._resolve = result => {
this._resolve = null;
return resolve(result);
};
this._reject = (possibleError) => {
this._reject = possibleError => {
this._reject = null;
return reject(possibleError);
};
@ -261,22 +278,36 @@ export default class PhoneAuthListener {
this._removeAllListeners();
}
/* -------------
-- PUBLIC API
--------------*/
on(event: string, observer: () => PhoneAuthSnapshot, errorCb?: () => PhoneAuthError, successCb?: () => PhoneAuthSnapshot): this {
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'));
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));
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'));
throw new Error(
INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('observer', 'function', 'on')
);
}
this._addUserObserver(observer);

View File

@ -6,7 +6,13 @@ import INTERNALS from '../../utils/internals';
import { getNativeModule } from '../../utils/native';
import type Auth from './';
import type { ActionCodeSettings, AuthCredential, NativeUser, UserCredential, UserMetadata } from './types';
import type {
ActionCodeSettings,
AuthCredential,
NativeUser,
UserCredential,
UserMetadata,
} from './types';
type UserInfo = {
displayName?: string,
@ -15,12 +21,12 @@ type UserInfo = {
photoURL?: string,
providerId: string,
uid: string,
}
};
type UpdateProfile = {
displayName?: string,
photoURL?: string,
}
};
export default class User {
_auth: Auth;
@ -110,7 +116,9 @@ export default class User {
* @return {Promise}
*/
getToken(forceRefresh: boolean = false): Promise<Object> {
console.warn('Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.');
console.warn(
'Deprecated firebase.User.prototype.getToken in favor of firebase.User.prototype.getIdToken.'
);
return getNativeModule(this._auth).getToken(forceRefresh);
}
@ -119,9 +127,15 @@ export default class User {
* @param credential
*/
linkWithCredential(credential: AuthCredential): Promise<User> {
console.warn('Deprecated firebase.User.prototype.linkWithCredential in favor of firebase.User.prototype.linkAndRetrieveDataWithCredential.');
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)
.linkWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(user => this._auth._setUser(user));
}
@ -129,9 +143,15 @@ export default class User {
*
* @param credential
*/
linkAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential> {
linkAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
return getNativeModule(this._auth)
.linkAndRetrieveDataWithCredential(credential.providerId, credential.token, credential.secret)
.linkAndRetrieveDataWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(userCredential => this._auth._setUserCredential(userCredential));
}
@ -140,10 +160,16 @@ export default class User {
* @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.');
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) => {
.reauthenticateWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(user => {
this._auth._setUser(user);
});
}
@ -152,9 +178,15 @@ export default class User {
* Re-authenticate a user with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
reauthenticateAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential> {
reauthenticateAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
return getNativeModule(this._auth)
.reauthenticateAndRetrieveDataWithCredential(credential.providerId, credential.token, credential.secret)
.reauthenticateAndRetrieveDataWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(userCredential => this._auth._setUserCredential(userCredential));
}
@ -165,7 +197,7 @@ export default class User {
reload(): Promise<void> {
return getNativeModule(this._auth)
.reload()
.then((user) => {
.then(user => {
this._auth._setUser(user);
});
}
@ -173,10 +205,12 @@ export default class User {
/**
* Send verification email to current user.
*/
sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise<void> {
sendEmailVerification(
actionCodeSettings?: ActionCodeSettings
): Promise<void> {
return getNativeModule(this._auth)
.sendEmailVerification(actionCodeSettings)
.then((user) => {
.then(user => {
this._auth._setUser(user);
});
}
@ -205,7 +239,7 @@ export default class User {
updateEmail(email: string): Promise<void> {
return getNativeModule(this._auth)
.updateEmail(email)
.then((user) => {
.then(user => {
this._auth._setUser(user);
});
}
@ -218,7 +252,7 @@ export default class User {
updatePassword(password: string): Promise<void> {
return getNativeModule(this._auth)
.updatePassword(password)
.then((user) => {
.then(user => {
this._auth._setUser(user);
});
}
@ -231,7 +265,7 @@ export default class User {
updateProfile(updates: UpdateProfile = {}): Promise<void> {
return getNativeModule(this._auth)
.updateProfile(updates)
.then((user) => {
.then(user => {
this._auth._setUser(user);
});
}
@ -241,34 +275,68 @@ export default class User {
*/
linkWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPhoneNumber'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'linkWithPhoneNumber'
)
);
}
linkWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithPopup')
);
}
linkWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'linkWithRedirect'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'linkWithRedirect'
)
);
}
reauthenticateWithPhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPhoneNumber'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithPhoneNumber'
)
);
}
reauthenticateWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithPopup'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithPopup'
)
);
}
reauthenticateWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', 'reauthenticateWithRedirect'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD(
'User',
'reauthenticateWithRedirect'
)
);
}
updatePhoneNumber() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('User', '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'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('User', 'refreshToken')
);
}
}

View File

@ -21,7 +21,13 @@ import FacebookAuthProvider from './providers/FacebookAuthProvider';
import PhoneAuthListener from './PhoneAuthListener';
import type { ActionCodeSettings, AuthCredential, NativeUser, NativeUserCredential, UserCredential } from './types';
import type {
ActionCodeSettings,
AuthCredential,
NativeUser,
NativeUserCredential,
UserCredential,
} from './types';
import type App from '../core/firebase-app';
type AuthState = {
@ -34,7 +40,7 @@ type ActionCodeInfo = {
fromEmail?: string,
},
operation: 'PASSWORD_RESET' | 'VERIFY_EMAIL' | 'RECOVER_EMAIL',
}
};
const NATIVE_EVENTS = [
'auth_state_changed',
@ -59,7 +65,9 @@ export default class Auth extends ModuleBase {
});
this._user = null;
this._authResult = false;
this._languageCode = getNativeModule(this).APP_LANGUAGE[app._name] || getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
this._languageCode =
getNativeModule(this).APP_LANGUAGE[app._name] ||
getNativeModule(this).APP_LANGUAGE['[DEFAULT]'];
SharedEventEmitter.addListener(
// sub to internal native event - this fans out to
@ -67,8 +75,11 @@ export default class Auth extends ModuleBase {
getAppEventName(this, 'auth_state_changed'),
(state: AuthState) => {
this._setUser(state.user);
SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user);
},
SharedEventEmitter.emit(
getAppEventName(this, 'onAuthStateChanged'),
this._user
);
}
);
SharedEventEmitter.addListener(
@ -78,7 +89,7 @@ export default class Auth extends ModuleBase {
(event: Object) => {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
SharedEventEmitter.emit(eventKey, event.state);
},
}
);
SharedEventEmitter.addListener(
@ -87,8 +98,11 @@ export default class Auth extends ModuleBase {
getAppEventName(this, 'auth_id_token_changed'),
(auth: AuthState) => {
this._setUser(auth.user);
SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user);
},
SharedEventEmitter.emit(
getAppEventName(this, 'onIdTokenChanged'),
this._user
);
}
);
getNativeModule(this).addAuthStateListener();
@ -123,12 +137,18 @@ export default class Auth extends ModuleBase {
*/
onAuthStateChanged(listener: Function) {
getLogger(this).info('Creating onAuthStateChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, '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);
SharedEventEmitter.removeListener(
getAppEventName(this, 'onAuthStateChanged'),
listener
);
};
}
@ -138,12 +158,18 @@ export default class Auth extends ModuleBase {
*/
onIdTokenChanged(listener: Function) {
getLogger(this).info('Creating onIdTokenChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, '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);
SharedEventEmitter.removeListener(
getAppEventName(this, 'onIdTokenChanged'),
listener
);
};
}
@ -153,12 +179,18 @@ export default class Auth extends ModuleBase {
*/
onUserChanged(listener: Function) {
getLogger(this).info('Creating onUserChanged listener');
SharedEventEmitter.addListener(getAppEventName(this, '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);
SharedEventEmitter.removeListener(
getAppEventName(this, 'onUserChanged'),
listener
);
};
}
@ -180,7 +212,9 @@ export default class Auth extends ModuleBase {
* @return {Promise} A promise resolved upon completion
*/
signInAnonymously(): Promise<User> {
console.warn('Deprecated firebase.User.prototype.signInAnonymously in favor of firebase.User.prototype.signInAnonymouslyAndRetrieveData.');
console.warn(
'Deprecated firebase.User.prototype.signInAnonymously in favor of firebase.User.prototype.signInAnonymouslyAndRetrieveData.'
);
return getNativeModule(this)
.signInAnonymously()
.then(user => this._setUser(user));
@ -203,8 +237,13 @@ export default class Auth extends ModuleBase {
* @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.');
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));
@ -216,7 +255,10 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password
* @return {Promise} A promise indicating the completion
*/
createUserAndRetrieveDataWithEmailAndPassword(email: string, password: string): Promise<User> {
createUserAndRetrieveDataWithEmailAndPassword(
email: string,
password: string
): Promise<User> {
return getNativeModule(this)
.createUserAndRetrieveDataWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
@ -230,7 +272,9 @@ export default class Auth extends ModuleBase {
* @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.');
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));
@ -242,7 +286,10 @@ export default class Auth extends ModuleBase {
* @param {string} password The user's password
* @return {Promise} A promise that is resolved upon completion
*/
signInAndRetrieveDataWithEmailAndPassword(email: string, password: string): Promise<UserCredential> {
signInAndRetrieveDataWithEmailAndPassword(
email: string,
password: string
): Promise<UserCredential> {
return getNativeModule(this)
.signInAndRetrieveDataWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
@ -255,7 +302,9 @@ export default class Auth extends ModuleBase {
* @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.');
console.warn(
'Deprecated firebase.User.prototype.signInWithCustomToken in favor of firebase.User.prototype.signInAndRetrieveDataWithCustomToken.'
);
return getNativeModule(this)
.signInWithCustomToken(customToken)
.then(user => this._setUser(user));
@ -266,7 +315,9 @@ export default class Auth extends ModuleBase {
* @param {string} customToken A self-signed custom auth token.
* @return {Promise} A promise resolved upon completion
*/
signInAndRetrieveDataWithCustomToken(customToken: string): Promise<UserCredential> {
signInAndRetrieveDataWithCustomToken(
customToken: string
): Promise<UserCredential> {
return getNativeModule(this)
.signInAndRetrieveDataWithCustomToken(customToken)
.then(userCredential => this._setUserCredential(userCredential));
@ -278,9 +329,15 @@ export default class Auth extends ModuleBase {
* @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.');
console.warn(
'Deprecated firebase.User.prototype.signInWithCredential in favor of firebase.User.prototype.signInAndRetrieveDataWithCredential.'
);
return getNativeModule(this)
.signInWithCredential(credential.providerId, credential.token, credential.secret)
.signInWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(user => this._setUser(user));
}
@ -288,9 +345,15 @@ export default class Auth extends ModuleBase {
* Sign the user in with a third-party authentication provider
* @return {Promise} A promise resolved upon completion
*/
signInAndRetrieveDataWithCredential(credential: AuthCredential): Promise<UserCredential> {
signInAndRetrieveDataWithCredential(
credential: AuthCredential
): Promise<UserCredential> {
return getNativeModule(this)
.signInAndRetrieveDataWithCredential(credential.providerId, credential.token, credential.secret)
.signInAndRetrieveDataWithCredential(
credential.providerId,
credential.token,
credential.secret
)
.then(userCredential => this._setUserCredential(userCredential));
}
@ -301,9 +364,7 @@ export default class Auth extends ModuleBase {
signInWithPhoneNumber(phoneNumber: string): Promise<ConfirmationResult> {
return getNativeModule(this)
.signInWithPhoneNumber(phoneNumber)
.then((result) => {
return new ConfirmationResult(this, result.verificationId);
});
.then(result => new ConfirmationResult(this, result.verificationId));
}
/**
@ -315,7 +376,10 @@ export default class Auth extends ModuleBase {
* @param autoVerifyTimeout Android Only
* @returns {PhoneAuthListener}
*/
verifyPhoneNumber(phoneNumber: string, autoVerifyTimeout?: number): PhoneAuthListener {
verifyPhoneNumber(
phoneNumber: string,
autoVerifyTimeout?: number
): PhoneAuthListener {
return new PhoneAuthListener(this, phoneNumber, autoVerifyTimeout);
}
@ -323,8 +387,14 @@ export default class Auth extends ModuleBase {
* 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);
sendPasswordResetEmail(
email: string,
actionCodeSettings?: ActionCodeSettings
): Promise<void> {
return getNativeModule(this).sendPasswordResetEmail(
email,
actionCodeSettings
);
}
/**
@ -400,24 +470,49 @@ export default class Auth extends ModuleBase {
*/
getRedirectResult() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'getRedirectResult'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'getRedirectResult'
)
);
}
setPersistence() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'setPersistence'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'setPersistence'
)
);
}
signInWithPopup() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', 'signInWithPopup'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'signInWithPopup'
)
);
}
signInWithRedirect() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('auth', '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'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'auth',
'useDeviceLanguage'
)
);
}
}

View File

@ -8,7 +8,9 @@ const providerId = 'password';
export default class EmailAuthProvider {
constructor() {
throw new Error('`new EmailAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new EmailAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -8,7 +8,9 @@ const providerId = 'facebook.com';
export default class FacebookAuthProvider {
constructor() {
throw new Error('`new FacebookAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new FacebookAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -8,7 +8,9 @@ const providerId = 'github.com';
export default class GithubAuthProvider {
constructor() {
throw new Error('`new GithubAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new GithubAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -8,7 +8,9 @@ const providerId = 'google.com';
export default class GoogleAuthProvider {
constructor() {
throw new Error('`new GoogleAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new GoogleAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -8,7 +8,9 @@ const providerId = 'oauth';
export default class OAuthProvider {
constructor() {
throw new Error('`new OAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new OAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -8,7 +8,9 @@ const providerId = 'phone';
export default class PhoneAuthProvider {
constructor() {
throw new Error('`new PhoneAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new PhoneAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -8,7 +8,9 @@ const providerId = 'twitter.com';
export default class TwitterAuthProvider {
constructor() {
throw new Error('`new TwitterAuthProvider()` is not supported on the native Firebase SDKs.');
throw new Error(
'`new TwitterAuthProvider()` is not supported on the native Firebase SDKs.'
);
}
static get PROVIDER_ID(): string {

View File

@ -14,25 +14,25 @@ export type ActionCodeSettings = {
bundleId?: string,
},
url: string,
}
};
type AdditionalUserInfo = {
isNewUser: boolean,
profile?: Object,
providerId: string,
username?: string,
}
};
export type AuthCredential = {
providerId: string,
token: string,
secret: string
}
secret: string,
};
export type UserCredential = {|
additionalUserInfo?: AdditionalUserInfo,
user: User,
|}
|};
export type UserInfo = {
displayName?: string,
@ -41,12 +41,12 @@ export type UserInfo = {
photoURL?: string,
providerId: string,
uid: string,
}
};
export type UserMetadata = {
creationTime?: string,
lastSignInTime?: string,
}
};
export type NativeUser = {
displayName?: string,
@ -59,9 +59,9 @@ export type NativeUser = {
providerData: UserInfo[],
providerId: string,
uid: string,
}
};
export type NativeUserCredential = {|
additionalUserInfo?: AdditionalUserInfo,
user: NativeUser,
|}
|};

View File

@ -13,8 +13,11 @@ type NativeValue = {
numberValue?: number,
dataValue?: Object,
boolValue?: boolean,
source: 'remoteConfigSourceRemote' | 'remoteConfigSourceDefault' | ' remoteConfigSourceStatic',
}
source:
| 'remoteConfigSourceRemote'
| 'remoteConfigSourceDefault'
| ' remoteConfigSourceStatic',
};
export const MODULE_NAME = 'RNFirebaseRemoteConfig';
export const NAMESPACE = 'config';
@ -44,9 +47,26 @@ export default class RemoteConfig extends ModuleBase {
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;
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;
},
};
@ -70,7 +90,9 @@ export default class RemoteConfig extends ModuleBase {
*/
fetch(expiration?: number) {
if (expiration !== undefined) {
getLogger(this).debug(`Fetching remote config data with expiration ${expiration.toString()}`);
getLogger(this).debug(
`Fetching remote config data with expiration ${expiration.toString()}`
);
return getNativeModule(this).fetchWithExpirationDuration(expiration);
}
getLogger(this).debug('Fetching remote config data');
@ -124,7 +146,7 @@ export default class RemoteConfig extends ModuleBase {
getValues(keys: Array<String>) {
return getNativeModule(this)
.getValues(keys || [])
.then((nativeValues) => {
.then(nativeValues => {
const values: { [String]: Object } = {};
for (let i = 0, len = keys.length; i < len; i++) {
values[keys[i]] = this._nativeValueToJS(nativeValues[i]);

View File

@ -13,7 +13,9 @@ 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 Crashlytics, {
NAMESPACE as CrashlyticsNamespace,
} from '../fabric/crashlytics';
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
import Links, { NAMESPACE as LinksNamespace } from '../links';
@ -22,15 +24,12 @@ 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';
import type { FirebaseOptions } from '../../types';
const FirebaseCoreModule = NativeModules.RNFirebase;
export default class App {
_extendedProps: { [string] : boolean };
_extendedProps: { [string]: boolean };
_initialized: boolean = false;
_name: string;
_nativeInitialized: boolean = false;
@ -51,7 +50,11 @@ export default class App {
storage: () => Storage;
utils: () => Utils;
constructor(name: string, options: FirebaseOptions, fromNative: boolean = false) {
constructor(
name: string,
options: FirebaseOptions,
fromNative: boolean = false
) {
this._name = name;
this._options = Object.assign({}, options);
@ -59,10 +62,14 @@ export default class App {
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 });
});
FirebaseCoreModule.initializeApp(
this._name,
this._options,
(error, result) => {
this._initialized = true;
SharedEventEmitter.emit(`AppReady:${this._name}`, { error, result });
}
);
}
// modules
@ -109,7 +116,10 @@ export default class App {
* @param props
*/
extendApp(props: Object) {
if (!isObject(props)) throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG('Object', 'extendApp'));
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++) {
@ -130,7 +140,9 @@ export default class App {
* @return {Promise}
*/
delete() {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('app', '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(
@ -141,7 +153,6 @@ export default class App {
// return FirebaseCoreModule.deleteApp(this._name);
}
/**
*
* @return {*}

View File

@ -10,19 +10,55 @@ import App from './firebase-app';
import VERSION from '../../version';
// module imports
import { statics as AdMobStatics, MODULE_NAME as AdmobModuleName } from '../admob';
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 LinksStatics, MODULE_NAME as LinksModuleName } from '../links';
import { statics as MessagingStatics, MODULE_NAME as MessagingModuleName } from '../messaging';
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 {
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 LinksStatics,
MODULE_NAME as LinksModuleName,
} from '../links';
import {
statics as MessagingStatics,
MODULE_NAME as MessagingModuleName,
} from '../messaging';
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,
@ -60,25 +96,57 @@ class Firebase {
constructor() {
if (!FirebaseCoreModule) {
throw (new Error(INTERNALS.STRINGS.ERROR_MISSING_CORE));
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.analytics = APPS.moduleAndStatics(
'analytics',
AnalyticsStatics,
AnalyticsModuleName
);
this.auth = APPS.moduleAndStatics('auth', AuthStatics, AuthModuleName);
this.config = APPS.moduleAndStatics('config', ConfigStatics, ConfigModuleName);
this.config = APPS.moduleAndStatics(
'config',
ConfigStatics,
ConfigModuleName
);
this.crash = APPS.moduleAndStatics('crash', CrashStatics, CrashModuleName);
this.database = APPS.moduleAndStatics('database', DatabaseStatics, DatabaseModuleName);
this.database = APPS.moduleAndStatics(
'database',
DatabaseStatics,
DatabaseModuleName
);
this.fabric = {
crashlytics: APPS.moduleAndStatics('crashlytics', CrashlyticsStatics, CrashlyticsModuleName),
crashlytics: APPS.moduleAndStatics(
'crashlytics',
CrashlyticsStatics,
CrashlyticsModuleName
),
};
this.firestore = APPS.moduleAndStatics('firestore', FirestoreStatics, FirestoreModuleName);
this.firestore = APPS.moduleAndStatics(
'firestore',
FirestoreStatics,
FirestoreModuleName
);
this.links = APPS.moduleAndStatics('links', LinksStatics, LinksModuleName);
this.messaging = APPS.moduleAndStatics('messaging', MessagingStatics, MessagingModuleName);
this.perf = APPS.moduleAndStatics('perf', PerformanceStatics, PerfModuleName);
this.storage = APPS.moduleAndStatics('storage', StorageStatics, StorageModuleName);
this.messaging = APPS.moduleAndStatics(
'messaging',
MessagingStatics,
MessagingModuleName
);
this.perf = APPS.moduleAndStatics(
'perf',
PerformanceStatics,
PerfModuleName
);
this.storage = APPS.moduleAndStatics(
'storage',
StorageStatics,
StorageModuleName
);
this.utils = APPS.moduleAndStatics('utils', UtilsStatics, UtilsModuleName);
}

View File

@ -7,7 +7,6 @@ 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 Disconnect
@ -33,7 +32,10 @@ export default class Disconnect {
* @returns {*}
*/
set(value: string | Object): Promise<void> {
return getNativeModule(this._database).onDisconnectSet(this.path, { type: typeOf(value), value });
return getNativeModule(this._database).onDisconnectSet(this.path, {
type: typeOf(value),
value,
});
}
/**
@ -42,7 +44,10 @@ export default class Disconnect {
* @returns {*}
*/
update(values: Object): Promise<void> {
return getNativeModule(this._database).onDisconnectUpdate(this.path, values);
return getNativeModule(this._database).onDisconnectUpdate(
this.path,
values
);
}
/**

View File

@ -47,7 +47,7 @@ export default class Database extends ModuleBase {
setTimeout(() => {
this._serverTimeOffset = 0;
this._offsetRef = this.ref('.info/serverTimeOffset');
this._offsetRef.on('value', (snapshot) => {
this._offsetRef.on('value', snapshot => {
this._serverTimeOffset = snapshot.val() || this._serverTimeOffset;
});
}, 1);
@ -86,9 +86,13 @@ export default class Database extends ModuleBase {
}
export const statics = {
ServerValue: NativeModules.RNFirebaseDatabase ? {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
} : {},
ServerValue: NativeModules.RNFirebaseDatabase
? {
TIMESTAMP: NativeModules.RNFirebaseDatabase.serverValueTimestamp || {
'.sv': 'timestamp',
},
}
: {},
enableLogging(enabled: boolean) {
if (NativeModules[MODULE_NAME]) {
NativeModules[MODULE_NAME].enableLogging(enabled);

View File

@ -5,7 +5,7 @@
import { objectToUniqueId } from '../../utils';
import type { DatabaseModifier } from '../../types';
import type Reference from './reference.js';
import type Reference from './reference';
// todo doc methods
@ -16,7 +16,7 @@ export default class Query {
_reference: Reference;
modifiers: Array<DatabaseModifier>;
constructor(ref: Reference, path: string, existingModifiers?: Array<DatabaseModifier>) {
constructor(ref: Reference, existingModifiers?: Array<DatabaseModifier>) {
this.modifiers = existingModifiers ? [...existingModifiers] : [];
this._reference = ref;
}

View File

@ -2,7 +2,7 @@
* @flow
* Database Reference representation wrapper
*/
import Query from './query.js';
import Query from './query';
import Snapshot from './snapshot';
import Disconnect from './disconnect';
import { getLogger } from '../../utils/log';
@ -41,11 +41,11 @@ const ReferenceEventTypes = {
};
type DatabaseListener = {
listenerId: number;
eventName: string;
successCallback: Function;
failureCallback?: Function;
}
listenerId: number,
eventName: string,
successCallback: Function,
failureCallback?: Function,
};
/**
* @typedef {String} ReferenceLocation - Path to location in the database, relative
@ -80,12 +80,16 @@ export default class Reference extends ReferenceBase {
_query: Query;
_refListeners: { [listenerId: number]: DatabaseListener };
constructor(database: Database, path: string, existingModifiers?: Array<DatabaseModifier>) {
constructor(
database: Database,
path: string,
existingModifiers?: Array<DatabaseModifier>
) {
super(path);
this._promise = null;
this._refListeners = {};
this._database = database;
this._query = new Query(this, path, existingModifiers);
this._query = new Query(this, existingModifiers);
getLogger(database).debug('Created new Reference', this._getRefKey());
}
@ -100,7 +104,12 @@ export default class Reference extends ReferenceBase {
* @returns {*}
*/
keepSynced(bool: boolean): Promise<void> {
return getNativeModule(this._database).keepSynced(this._getRefKey(), this.path, this._query.getModifiers(), bool);
return getNativeModule(this._database).keepSynced(
this._getRefKey(),
this.path,
this._query.getModifiers(),
bool
);
}
/**
@ -113,8 +122,11 @@ export default class Reference extends ReferenceBase {
*/
set(value: any, onComplete?: Function): Promise<void> {
return promiseOrCallback(
getNativeModule(this._database).set(this.path, this._serializeAnyType(value)),
onComplete,
getNativeModule(this._database).set(
this.path,
this._serializeAnyType(value)
),
onComplete
);
}
@ -126,12 +138,15 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
setPriority(priority: string | number | null, onComplete?: Function): Promise<void> {
setPriority(
priority: string | number | null,
onComplete?: Function
): Promise<void> {
const _priority = this._serializeAnyType(priority);
return promiseOrCallback(
getNativeModule(this._database).setPriority(this.path, _priority),
onComplete,
onComplete
);
}
@ -144,13 +159,21 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @returns {Promise}
*/
setWithPriority(value: any, priority: string | number | null, onComplete?: Function): Promise<void> {
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,
getNativeModule(this._database).setWithPriority(
this.path,
_value,
_priority
),
onComplete
);
}
@ -167,7 +190,7 @@ export default class Reference extends ReferenceBase {
return promiseOrCallback(
getNativeModule(this._database).update(this.path, value),
onComplete,
onComplete
);
}
@ -181,7 +204,7 @@ export default class Reference extends ReferenceBase {
remove(onComplete?: Function): Promise<void> {
return promiseOrCallback(
getNativeModule(this._database).remove(this.path),
onComplete,
onComplete
);
}
@ -196,10 +219,12 @@ export default class Reference extends ReferenceBase {
transaction(
transactionUpdate: Function,
onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *,
applyLocally: boolean = false,
applyLocally: boolean = false
) {
if (!isFunction(transactionUpdate)) {
return Promise.reject(new Error('Missing transactionUpdate function argument.'));
return Promise.reject(
new Error('Missing transactionUpdate function argument.')
);
}
return new Promise((resolve, reject) => {
@ -213,15 +238,22 @@ export default class Reference extends ReferenceBase {
}
if (error) return reject(error);
return resolve({ committed, snapshot: new Snapshot(this, snapshotData) });
return resolve({
committed,
snapshot: new Snapshot(this, snapshotData),
});
};
// start the transaction natively
this._database._transactionHandler.add(this, transactionUpdate, onCompleteWrapper, applyLocally);
this._database._transactionHandler.add(
this,
transactionUpdate,
onCompleteWrapper,
applyLocally
);
});
}
/**
*
* @param eventName
@ -234,21 +266,24 @@ export default class Reference extends ReferenceBase {
eventName: string = 'value',
successCallback: (snapshot: Object) => void,
cancelOrContext: (error: FirebaseError) => void,
context?: Object,
context?: Object
) {
return getNativeModule(this._database).once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
return getNativeModule(this._database)
.once(this._getRefKey(), this.path, this._query.getModifiers(), eventName)
.then(({ snapshot }) => {
const _snapshot = new Snapshot(this, snapshot);
if (isFunction(successCallback)) {
if (isObject(cancelOrContext)) successCallback.bind(cancelOrContext)(_snapshot);
if (context && isObject(context)) successCallback.bind(context)(_snapshot);
if (isObject(cancelOrContext))
successCallback.bind(cancelOrContext)(_snapshot);
if (context && isObject(context))
successCallback.bind(context)(_snapshot);
successCallback(_snapshot);
}
return _snapshot;
})
.catch((error) => {
.catch(error => {
if (isFunction(cancelOrContext)) return cancelOrContext(error);
return error;
});
@ -262,19 +297,27 @@ export default class Reference extends ReferenceBase {
*/
push(value: any, onComplete?: Function): Reference | Promise<void> {
if (value === null || value === undefined) {
return new Reference(this._database, `${this.path}/${generatePushID(this._database._serverTimeOffset)}`);
return new Reference(
this._database,
`${this.path}/${generatePushID(this._database._serverTimeOffset)}`
);
}
const newRef = 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
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.then(() => onComplete(null, newRef))
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.catch(error => onComplete(error, null));
return (
promise
// $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655
.then(() => onComplete(null, newRef))
// $FlowBug: 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
@ -327,7 +370,11 @@ export default class Reference extends ReferenceBase {
* @returns {Reference}
*/
orderBy(name: string, key?: string): Reference {
const newRef = new Reference(this._database, this.path, this._query.getModifiers());
const newRef = new Reference(
this._database,
this.path,
this._query.getModifiers()
);
newRef._query.orderBy(name, key);
return newRef;
}
@ -361,7 +408,11 @@ export default class Reference extends ReferenceBase {
* @returns {Reference}
*/
limit(name: string, limit: number): Reference {
const newRef = new Reference(this._database, this.path, this._query.getModifiers());
const newRef = new Reference(
this._database,
this.path,
this._query.getModifiers()
);
newRef._query.limit(name, limit);
return newRef;
}
@ -408,7 +459,11 @@ export default class Reference extends ReferenceBase {
* @returns {Reference}
*/
filter(name: string, value: any, key?: string): Reference {
const newRef = new Reference(this._database, this.path, this._query.getModifiers());
const newRef = new Reference(
this._database,
this.path,
this._query.getModifiers()
);
newRef._query.filter(name, value, key);
return newRef;
}
@ -438,7 +493,7 @@ export default class Reference extends ReferenceBase {
* @returns {string}
*/
toString(): string {
return this.path;
return `${this._database.app.options.databaseURL}/${this.path}`;
}
/**
@ -450,10 +505,12 @@ export default class Reference extends ReferenceBase {
* {@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();
return (
!!otherRef &&
otherRef.constructor === Reference &&
otherRef.key === this.key &&
this._query.queryIdentifier() === otherRef._query.queryIdentifier()
);
}
/**
@ -468,7 +525,10 @@ export default class Reference extends ReferenceBase {
*/
get parent(): Reference | null {
if (this.path === '/') return null;
return new Reference(this._database, this.path.substring(0, this.path.lastIndexOf('/')));
return new Reference(
this._database,
this.path.substring(0, this.path.lastIndexOf('/'))
);
}
/**
@ -495,20 +555,23 @@ export default class Reference extends ReferenceBase {
* Access then method of promise if set
* @return {*}
*/
then(fnResolve: (any) => any, fnReject: (any) => any) {
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;
return this._promise.then.bind(this._promise)(
result => {
this._promise = null;
return fnResolve(result);
},
possibleErr => {
this._promise = null;
if (isFunction(fnReject)) {
return fnReject(possibleErr);
if (isFunction(fnReject)) {
return fnReject(possibleErr);
}
throw possibleErr;
}
throw possibleErr;
});
);
}
throw new Error("Cannot read property 'then' of undefined.");
@ -518,9 +581,9 @@ export default class Reference extends ReferenceBase {
* Access catch method of promise if set
* @return {*}
*/
catch(fnReject: (any) => any) {
catch(fnReject: any => any) {
if (isFunction(fnReject) && this._promise && this._promise.catch) {
return this._promise.catch.bind(this._promise)((possibleErr) => {
return this._promise.catch.bind(this._promise)(possibleErr => {
this._promise = null;
return fnReject(possibleErr);
});
@ -539,7 +602,9 @@ export default class Reference extends ReferenceBase {
* @return {string}
*/
_getRegistrationKey(eventType: string): string {
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
return `$${this._database.app.name}$/${
this.path
}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
}
/**
@ -550,7 +615,9 @@ export default class Reference extends ReferenceBase {
* @private
*/
_getRefKey(): string {
return `$${this._database.app.name}$/${this.path}$${this._query.queryIdentifier()}`;
return `$${this._database.app.name}$/${
this.path
}$${this._query.queryIdentifier()}`;
}
/**
@ -562,7 +629,6 @@ export default class Reference extends ReferenceBase {
this._promise = promise;
}
/**
*
* @param obj
@ -623,34 +689,65 @@ export default class Reference extends ReferenceBase {
*
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on}
*/
on(eventType: string, callback: (Snapshot) => any, cancelCallbackOrContext?: (Object) => any | Object, context?: Object): Function {
on(
eventType: string,
callback: Snapshot => 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.');
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(', ')}"`);
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.');
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.');
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) &&
!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.');
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 _context =
cancelCallbackOrContext && !isFunction(cancelCallbackOrContext)
? cancelCallbackOrContext
: context;
const registrationObj = {
eventType,
ref: this,
@ -677,7 +774,9 @@ export default class Reference extends ReferenceBase {
appName: this._database.app.name,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
listener: _context ? cancelCallbackOrContext.bind(_context) : cancelCallbackOrContext,
listener: _context
? cancelCallbackOrContext.bind(_context)
: cancelCallbackOrContext,
});
}
@ -724,18 +823,29 @@ export default class Reference extends ReferenceBase {
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));
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 (
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.');
throw new Error(
'Query.off failed: Function called with 2 arguments, but second optional argument was not a function.'
);
}
// Firebase Docs:
@ -745,7 +855,11 @@ export default class Reference extends ReferenceBase {
// remove the callback.
// Remove only a single registration
if (eventType && originalCallback) {
const registration = SyncTree.getOneByPathEventListener(this.path, eventType, originalCallback);
const registration = SyncTree.getOneByPathEventListener(
this.path,
eventType,
originalCallback
);
if (!registration) return [];
// remove the paired cancellation registration if any exist
@ -753,15 +867,20 @@ export default class Reference extends ReferenceBase {
// remove only the first registration to match firebase web sdk
// call multiple times to remove multiple registrations
return SyncTree.removeListenerRegistrations(originalCallback, [registration]);
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);
const registrations = SyncTree.getRegistrationsByPathEvent(
this.path,
eventType
);
SyncTree.removeListenersForRegistrations(
SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`),
SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`)
);
return SyncTree.removeListenersForRegistrations(registrations);

View File

@ -3,7 +3,7 @@
* Snapshot representation wrapper
*/
import { isObject, deepGet, deepExists } from './../../utils';
import type Reference from './reference.js';
import type Reference from './reference';
/**
* @class DataSnapshot
@ -39,7 +39,8 @@ export default class Snapshot {
*/
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));
if (isObject(this._value) || Array.isArray(this._value))
return JSON.parse(JSON.stringify(this._value));
return this._value;
}

View File

@ -14,9 +14,7 @@ let transactionId = 0;
* @returns {number}
* @private
*/
const generateTransactionId = (): number => {
return transactionId++;
};
const generateTransactionId = (): number => transactionId++;
/**
* @class TransactionHandler
@ -24,7 +22,7 @@ const generateTransactionId = (): number => {
export default class TransactionHandler {
_database: Database;
_transactionListener: Function;
_transactions: { [number]: Object }
_transactions: { [number]: Object };
constructor(database: Database) {
this._transactions = {};
@ -32,7 +30,7 @@ export default class TransactionHandler {
this._transactionListener = SharedEventEmitter.addListener(
getAppEventName(this._database, 'database_transaction_event'),
this._handleTransactionEvent.bind(this),
this._handleTransactionEvent.bind(this)
);
}
@ -43,7 +41,12 @@ export default class TransactionHandler {
* @param onComplete
* @param applyLocally
*/
add(reference: Object, transactionUpdater: Function, onComplete?: Function, applyLocally?: boolean = false) {
add(
reference: Object,
transactionUpdater: Function,
onComplete?: Function,
applyLocally?: boolean = false
) {
const id = generateTransactionId();
this._transactions[id] = {
@ -56,7 +59,11 @@ export default class TransactionHandler {
started: true,
};
getNativeModule(this._database).transactionStart(reference.path, id, applyLocally);
getNativeModule(this._database).transactionStart(
reference.path,
id,
applyLocally
);
}
/**
@ -78,7 +85,10 @@ export default class TransactionHandler {
case 'complete':
return this._handleComplete(event);
default:
getLogger(this._database).warn(`Unknown transaction event type: '${event.type}'`, event);
getLogger(this._database).warn(
`Unknown transaction event type: '${event.type}'`,
event
);
return undefined;
}
}
@ -104,7 +114,10 @@ export default class TransactionHandler {
abort = true;
}
getNativeModule(this._database).transactionTryCommit(id, { value: newValue, abort });
getNativeModule(this._database).transactionTryCommit(id, {
value: newValue,
abort,
});
}
}
@ -137,7 +150,11 @@ export default class TransactionHandler {
if (transaction && !transaction.completed) {
transaction.completed = true;
try {
transaction.onComplete(null, event.committed, Object.assign({}, event.snapshot));
transaction.onComplete(
null,
event.committed,
Object.assign({}, event.snapshot)
);
} finally {
setImmediate(() => {
delete this._transactions[event.id];

View File

@ -7,10 +7,18 @@ import Query from './Query';
import { firestoreAutoId } from '../../utils';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type {
FirestoreQueryDirection,
FirestoreQueryOperator,
} from '../../types';
import type FieldPath from './FieldPath';
import type Path from './Path';
import type { Observer, ObserverOnError, ObserverOnNext, QueryListenOptions } from './Query';
import type {
Observer,
ObserverOnError,
ObserverOnNext,
QueryListenOptions,
} from './Query';
import type QuerySnapshot from './QuerySnapshot';
/**
@ -37,13 +45,14 @@ export default class CollectionReference {
get parent(): DocumentReference | null {
const parentPath = this._collectionPath.parent();
return parentPath ? new DocumentReference(this._firestore, parentPath) : null;
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));
return documentRef.set(data).then(() => Promise.resolve(documentRef));
}
doc(documentPath?: string): DocumentReference {
@ -77,12 +86,19 @@ export default class CollectionReference {
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: ObserverOnError,
onError?: ObserverOnError
): () => void {
return this._query.onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError);
return this._query.onSnapshot(
optionsOrObserverOrOnNext,
observerOrOnNextOrOnError,
onError
);
}
orderBy(fieldPath: string | FieldPath, directionStr?: FirestoreQueryDirection): Query {
orderBy(
fieldPath: string | FieldPath,
directionStr?: FirestoreQueryDirection
): Query {
return this._query.orderBy(fieldPath, directionStr);
}

View File

@ -13,20 +13,23 @@ import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreWriteOptions } from '../../types';
import type {
FirestoreNativeDocumentSnapshot,
FirestoreWriteOptions,
} from '../../types';
import type Path from './Path';
type DocumentListenOptions = {
includeMetadataChanges: boolean,
}
};
type ObserverOnError = (Object) => void;
type ObserverOnNext = (DocumentSnapshot) => void;
type ObserverOnError = Object => void;
type ObserverOnNext = DocumentSnapshot => void;
type Observer = {
error?: ObserverOnError,
next: ObserverOnNext,
}
};
/**
* @class DocumentReference
@ -70,8 +73,7 @@ export default class DocumentReference {
}
delete(): Promise<void> {
return getNativeModule(this._firestore)
.documentDelete(this.path);
return getNativeModule(this._firestore).documentDelete(this.path);
}
get(): Promise<DocumentSnapshot> {
@ -81,28 +83,41 @@ export default class DocumentReference {
}
onSnapshot(
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | ObserverOnNext,
optionsOrObserverOrOnNext:
| DocumentListenOptions
| Observer
| ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: 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.');
throw new Error(
'DocumentReference.onSnapshot failed: Second argument must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext,
error: observerOrOnNextOrOnError,
};
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
} 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.');
if (
optionsOrObserverOrOnNext.error &&
!isFunction(optionsOrObserverOrOnNext.error)
) {
throw new Error(
'DocumentReference.onSnapshot failed: Observer.error must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
@ -110,66 +125,103 @@ export default class DocumentReference {
error: optionsOrObserverOrOnNext.error,
};
} else {
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
throw new Error(
'DocumentReference.onSnapshot failed: Observer.next must be a valid function.'
);
}
} else if (Object.prototype.hasOwnProperty.call(optionsOrObserverOrOnNext, 'includeMetadataChanges')) {
} 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.');
throw new Error(
'DocumentReference.onSnapshot failed: Third argument must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
// 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.');
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.');
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.');
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.');
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.');
throw new Error(
'DocumentReference.onSnapshot failed: Called with invalid arguments.'
);
}
const listenerId = firestoreAutoId();
const listener = (nativeDocumentSnapshot: FirestoreNativeDocumentSnapshot) => {
const documentSnapshot = new DocumentSnapshot(this.firestore, nativeDocumentSnapshot);
const listener = (
nativeDocumentSnapshot: FirestoreNativeDocumentSnapshot
) => {
const documentSnapshot = new DocumentSnapshot(
this.firestore,
nativeDocumentSnapshot
);
observer.next(documentSnapshot);
};
// Listen to snapshot events
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshot:${listenerId}`),
listener,
listener
);
// Listen for snapshot error events
if (observer.error) {
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onDocumentSnapshotError:${listenerId}`),
observer.error,
getAppEventName(
this._firestore,
`onDocumentSnapshotError:${listenerId}`
),
observer.error
);
}
// Add the native listener
getNativeModule(this._firestore)
.documentOnSnapshot(this.path, listenerId, docListenOptions);
getNativeModule(this._firestore).documentOnSnapshot(
this.path,
listenerId,
docListenOptions
);
// Return an unsubscribe method
return this._offDocumentSnapshot.bind(this, listenerId, listener);
@ -177,19 +229,27 @@ export default class DocumentReference {
set(data: Object, writeOptions?: FirestoreWriteOptions): Promise<void> {
const nativeData = buildNativeMap(data);
return getNativeModule(this._firestore)
.documentSet(this.path, nativeData, writeOptions);
return getNativeModule(this._firestore).documentSet(
this.path,
nativeData,
writeOptions
);
}
update(...args: any[]): Promise<void> {
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
throw new Error('DocumentReference.update failed: If using a single argument, it must be an object.');
throw new Error(
'DocumentReference.update failed: If using a single argument, it must be an object.'
);
}
// eslint-disable-next-line prefer-destructuring
data = args[0];
} else if (args.length % 2 === 1) {
throw new Error('DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.');
throw new Error(
'DocumentReference.update failed: Must have either a single object argument, or equal numbers of key/value pairs.'
);
} else {
for (let i = 0; i < args.length; i += 2) {
const key = args[i];
@ -199,13 +259,17 @@ export default class DocumentReference {
} else if (key instanceof FieldPath) {
data = mergeFieldPathData(data, key._segments, value);
} else {
throw new Error(`DocumentReference.update failed: Argument at index ${i} must be a string or FieldPath`);
throw new Error(
`DocumentReference.update failed: Argument at index ${i} must be a string or FieldPath`
);
}
}
}
const nativeData = buildNativeMap(data);
return getNativeModule(this._firestore)
.documentUpdate(this.path, nativeData);
return getNativeModule(this._firestore).documentUpdate(
this.path,
nativeData
);
}
/**
@ -218,9 +282,14 @@ export default class DocumentReference {
*/
_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);
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

@ -9,7 +9,10 @@ import { isObject } from '../../utils';
import { parseNativeMap } from './utils/serialize';
import type Firestore from './';
import type { FirestoreNativeDocumentSnapshot, FirestoreSnapshotMetadata } from '../../types';
import type {
FirestoreNativeDocumentSnapshot,
FirestoreSnapshotMetadata,
} from '../../types';
const extractFieldPathData = (data: Object | void, segments: string[]): any => {
if (!data || !isObject(data)) {
@ -30,10 +33,16 @@ export default class DocumentSnapshot {
_metadata: FirestoreSnapshotMetadata;
_ref: DocumentReference;
constructor(firestore: Firestore, nativeData: FirestoreNativeDocumentSnapshot) {
constructor(
firestore: Firestore,
nativeData: FirestoreNativeDocumentSnapshot
) {
this._data = parseNativeMap(firestore, nativeData.data);
this._metadata = nativeData.metadata;
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
this._ref = new DocumentReference(
firestore,
Path.fromName(nativeData.path)
);
}
get exists(): boolean {

View File

@ -12,7 +12,10 @@ import { firestoreAutoId, isFunction, isObject } from '../../utils';
import { getNativeModule } from '../../utils/native';
import type Firestore from './';
import type { FirestoreQueryDirection, FirestoreQueryOperator } from '../../types';
import type {
FirestoreQueryDirection,
FirestoreQueryOperator,
} from '../../types';
import type Path from './Path';
const DIRECTIONS: { [FirestoreQueryDirection]: string } = {
@ -35,16 +38,16 @@ 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[],
@ -53,22 +56,24 @@ type QueryOptions = {
selectFields?: string[],
startAfter?: any[],
startAt?: any[],
}
};
export type QueryListenOptions = {|
includeDocumentMetadataChanges: boolean,
includeQueryMetadataChanges: boolean,
|}
|};
export type ObserverOnError = (Object) => void;
export type ObserverOnNext = (QuerySnapshot) => void;
export type ObserverOnError = Object => void;
export type ObserverOnNext = QuerySnapshot => void;
export type Observer = {
error?: ObserverOnError,
next: ObserverOnNext,
}
};
const buildNativeFieldPath = (fieldPath: string | FieldPath): NativeFieldPath => {
const buildNativeFieldPath = (
fieldPath: string | FieldPath
): NativeFieldPath => {
if (fieldPath instanceof FieldPath) {
return {
elements: fieldPath._segments,
@ -95,10 +100,9 @@ export default class Query {
constructor(
firestore: Firestore,
path: Path,
fieldFilters?:
FieldFilter[],
fieldFilters?: FieldFilter[],
fieldOrders?: FieldOrder[],
queryOptions?: QueryOptions,
queryOptions?: QueryOptions
) {
this._fieldFilters = fieldFilters || [];
this._fieldOrders = fieldOrders || [];
@ -122,7 +126,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
@ -137,7 +141,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
@ -147,7 +151,7 @@ export default class Query {
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
this._queryOptions
)
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
}
@ -165,33 +169,43 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
onSnapshot(
optionsOrObserverOrOnNext: QueryListenOptions | Observer | ObserverOnNext,
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
onError?: 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.');
throw new Error(
'Query.onSnapshot failed: Second argument must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: optionsOrObserverOrOnNext,
error: observerOrOnNextOrOnError,
};
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
} 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.');
if (
optionsOrObserverOrOnNext.error &&
!isFunction(optionsOrObserverOrOnNext.error)
) {
throw new Error(
'Query.onSnapshot failed: Observer.error must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
@ -199,87 +213,124 @@ export default class Query {
error: optionsOrObserverOrOnNext.error,
};
} else {
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
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')) {
} 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.');
throw new Error(
'Query.onSnapshot failed: Third argument must be a valid function.'
);
}
// $FlowBug: Not coping with the overloaded method signature
observer = {
next: observerOrOnNextOrOnError,
error: onError,
};
// Called with Options, Observer
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
// 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.');
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.');
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.');
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.');
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.');
throw new Error(
'Query.onSnapshot failed: Called with invalid arguments.'
);
}
const listenerId = firestoreAutoId();
const listener = (nativeQuerySnapshot) => {
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
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,
listener
);
// Listen for snapshot error events
if (observer.error) {
SharedEventEmitter.addListener(
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
observer.error,
observer.error
);
}
// Add the native listener
getNativeModule(this._firestore)
.collectionOnSnapshot(
this._referencePath.relativeName,
this._fieldFilters,
this._fieldOrders,
this._queryOptions,
listenerId,
queryListenOptions,
);
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?: FirestoreQueryDirection = 'asc'): Query {
orderBy(
fieldPath: string | FieldPath,
directionStr?: FirestoreQueryDirection = 'asc'
): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isOptionalFieldOrder('directionStr', directionStr);
if (this._queryOptions.startAt || this._queryOptions.endAt) {
throw new Error('Cannot specify an orderBy() constraint after calling ' +
'startAt(), startAfter(), endBefore() or endAt().');
throw new Error(
'Cannot specify an orderBy() constraint after calling ' +
'startAt(), startAfter(), endBefore() or endAt().'
);
}
const newOrder: FieldOrder = {
@ -292,7 +343,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
combinedOrders,
this._queryOptions,
this._queryOptions
);
}
@ -307,7 +358,7 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
@ -322,11 +373,15 @@ export default class Query {
this._referencePath,
this._fieldFilters,
this._fieldOrders,
options,
options
);
}
where(fieldPath: string | FieldPath, opStr: FirestoreQueryOperator, value: any): Query {
where(
fieldPath: string | FieldPath,
opStr: FirestoreQueryOperator,
value: any
): Query {
// TODO: Validation
// validate.isFieldPath('fieldPath', fieldPath);
// validate.isFieldFilter('fieldFilter', opStr, value);
@ -342,7 +397,7 @@ export default class Query {
this._referencePath,
combinedFilters,
this._fieldOrders,
this._queryOptions,
this._queryOptions
);
}
@ -353,12 +408,18 @@ export default class Query {
_buildOrderByOption(snapshotOrVarArgs: any[]) {
// TODO: Validation
let values;
if (snapshotOrVarArgs.length === 1 && snapshotOrVarArgs[0] instanceof DocumentSnapshot) {
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) {
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);
@ -378,15 +439,20 @@ export default class Query {
*/
_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,
);
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

@ -6,14 +6,18 @@ import DocumentChange from './DocumentChange';
import DocumentSnapshot from './DocumentSnapshot';
import type Firestore from './';
import type { FirestoreNativeDocumentChange, FirestoreNativeDocumentSnapshot, FirestoreSnapshotMetadata } from '../../types';
import type {
FirestoreNativeDocumentChange,
FirestoreNativeDocumentSnapshot,
FirestoreSnapshotMetadata,
} from '../../types';
import type Query from './Query';
type QuerySnapshotNativeData = {
changes: FirestoreNativeDocumentChange[],
documents: FirestoreNativeDocumentSnapshot[],
metadata: FirestoreSnapshotMetadata,
}
};
/**
* @class QuerySnapshot
@ -24,9 +28,17 @@ export default class QuerySnapshot {
_metadata: FirestoreSnapshotMetadata;
_query: Query;
constructor(firestore: Firestore, query: Query, nativeData: QuerySnapshotNativeData) {
this._changes = nativeData.changes.map(change => new DocumentChange(firestore, change));
this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc));
constructor(
firestore: Firestore,
query: Query,
nativeData: QuerySnapshotNativeData
) {
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;
}
@ -59,7 +71,7 @@ export default class QuerySnapshot {
// TODO: Validation
// validate.isFunction('callback', callback);
this._docs.forEach((doc) => {
this._docs.forEach(doc => {
callback(doc);
});
}

View File

@ -17,7 +17,7 @@ type DocumentWrite = {
options?: Object,
path: string,
type: 'DELETE' | 'SET' | 'UPDATE',
}
};
/**
* @class WriteBatch
@ -47,7 +47,11 @@ export default class WriteBatch {
return this;
}
set(docRef: DocumentReference, data: Object, writeOptions?: FirestoreWriteOptions) {
set(
docRef: DocumentReference,
data: Object,
writeOptions?: FirestoreWriteOptions
) {
// TODO: Validation
// validate.isDocumentReference('docRef', docRef);
// validate.isDocument('data', data);
@ -69,11 +73,16 @@ export default class WriteBatch {
let data = {};
if (args.length === 1) {
if (!isObject(args[0])) {
throw new Error('WriteBatch.update failed: If using two arguments, the second must be an object.');
throw new Error(
'WriteBatch.update failed: If using two arguments, the second must be an object.'
);
}
// eslint-disable-next-line prefer-destructuring
data = args[0];
} else if (args.length % 2 === 1) {
throw new Error('WriteBatch.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.');
throw new Error(
'WriteBatch.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.'
);
} else {
for (let i = 0; i < args.length; i += 2) {
const key = args[i];
@ -83,7 +92,9 @@ export default class WriteBatch {
} else if (key instanceof FieldPath) {
data = mergeFieldPathData(data, key._segments, value);
} else {
throw new Error(`WriteBatch.update failed: Argument at index ${i} must be a string or FieldPath`);
throw new Error(
`WriteBatch.update failed: Argument at index ${i} must be a string or FieldPath`
);
}
}
}

View File

@ -25,7 +25,7 @@ type CollectionSyncEvent = {
error?: Object,
listenerId: string,
path: string,
}
};
type DocumentSyncEvent = {
appName: string,
@ -33,7 +33,7 @@ type DocumentSyncEvent = {
error?: Object,
listenerId: string,
path: string,
}
};
const NATIVE_EVENTS = [
'firestore_collection_sync_event',
@ -62,14 +62,14 @@ export default class Firestore extends ModuleBase {
// sub to internal native event - this fans out to
// public event name: onCollectionSnapshot
getAppEventName(this, 'firestore_collection_sync_event'),
this._onCollectionSyncEvent.bind(this),
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),
this._onDocumentSyncEvent.bind(this)
);
}
@ -114,7 +114,12 @@ export default class Firestore extends ModuleBase {
}
setLogLevel(): void {
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('firestore', 'setLogLevel'));
throw new Error(
INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(
'firestore',
'setLogLevel'
)
);
}
settings(): void {
@ -128,9 +133,15 @@ export default class Firestore extends ModuleBase {
*/
_onCollectionSyncEvent(event: CollectionSyncEvent) {
if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`), event.error);
SharedEventEmitter.emit(
getAppEventName(this, `onQuerySnapshotError:${event.listenerId}`),
event.error
);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onQuerySnapshot:${event.listenerId}`), event.querySnapshot);
SharedEventEmitter.emit(
getAppEventName(this, `onQuerySnapshot:${event.listenerId}`),
event.querySnapshot
);
}
}
@ -141,9 +152,15 @@ export default class Firestore extends ModuleBase {
*/
_onDocumentSyncEvent(event: DocumentSyncEvent) {
if (event.error) {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`), event.error);
SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshotError:${event.listenerId}`),
event.error
);
} else {
SharedEventEmitter.emit(getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`), event.documentSnapshot);
SharedEventEmitter.emit(
getAppEventName(this, `onDocumentSnapshot:${event.listenerId}`),
event.documentSnapshot
);
}
}
}

View File

@ -13,7 +13,12 @@ const buildFieldPathData = (segments: string[], value: any): Object => {
};
};
export const mergeFieldPathData = (data: Object, segments: string[], value: any): Object => {
// eslint-disable-next-line import/prefer-default-export
export const mergeFieldPathData = (
data: Object,
segments: string[],
value: any
): Object => {
if (segments.length === 1) {
return {
...data,
@ -22,7 +27,11 @@ export const mergeFieldPathData = (data: Object, segments: string[], value: any)
} else if (data[segments[0]]) {
return {
...data,
[segments[0]]: mergeFieldPathData(data[segments[0]], segments.slice(1), value),
[segments[0]]: mergeFieldPathData(
data[segments[0]],
segments.slice(1),
value
),
};
}
return {

View File

@ -4,7 +4,10 @@
import DocumentReference from '../DocumentReference';
import { DOCUMENT_ID } from '../FieldPath';
import { DELETE_FIELD_VALUE, SERVER_TIMESTAMP_FIELD_VALUE } from '../FieldValue';
import {
DELETE_FIELD_VALUE,
SERVER_TIMESTAMP_FIELD_VALUE,
} from '../FieldValue';
import GeoPoint from '../GeoPoint';
import Path from '../Path';
import { typeOf } from '../../../utils';
@ -18,10 +21,12 @@ import type { FirestoreTypeMap } from '../../../types';
* for transmission to the native side
*/
export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } => {
export const buildNativeMap = (
data: Object
): { [string]: FirestoreTypeMap } => {
const nativeData = {};
if (data) {
Object.keys(data).forEach((key) => {
Object.keys(data).forEach(key => {
const typeMap = buildTypeMap(data[key]);
if (typeMap) {
nativeData[key] = typeMap;
@ -34,7 +39,7 @@ export const buildNativeMap = (data: Object): { [string]: FirestoreTypeMap } =>
export const buildNativeArray = (array: Object[]): FirestoreTypeMap[] => {
const nativeArray = [];
if (array) {
array.forEach((value) => {
array.forEach(value => {
const typeMap = buildTypeMap(value);
if (typeMap) {
nativeArray.push(typeMap);
@ -110,21 +115,27 @@ export const buildTypeMap = (value: any): FirestoreTypeMap | null => {
* side and converts to the correct Firestore JS types
*/
export const parseNativeMap = (firestore: Firestore, nativeData: { [string]: FirestoreTypeMap }): Object | void => {
export const parseNativeMap = (
firestore: Firestore,
nativeData: { [string]: FirestoreTypeMap }
): Object | void => {
let data;
if (nativeData) {
data = {};
Object.keys(nativeData).forEach((key) => {
Object.keys(nativeData).forEach(key => {
data[key] = parseTypeMap(firestore, nativeData[key]);
});
}
return data;
};
const parseNativeArray = (firestore: Firestore, nativeArray: FirestoreTypeMap[]): any[] => {
const parseNativeArray = (
firestore: Firestore,
nativeArray: FirestoreTypeMap[]
): any[] => {
const array = [];
if (nativeArray) {
nativeArray.forEach((typeMap) => {
nativeArray.forEach(typeMap => {
array.push(parseTypeMap(firestore, typeMap));
});
}

View File

@ -13,9 +13,7 @@ const EVENT_TYPE = {
Link: 'dynamic_link_received',
};
const NATIVE_EVENTS = [
EVENT_TYPE.Link,
];
const NATIVE_EVENTS = [EVENT_TYPE.Link];
export const MODULE_NAME = 'RNFirebaseLinks';
export const NAMESPACE = 'links';
@ -59,10 +57,16 @@ function checkForMandatoryParameters(parameters: Object): void {
if (!isString(parameters.link)) {
throw new Error('No link was specified.');
}
if (isObject(parameters.androidInfo) && !isString(parameters.androidInfo.androidPackageName)) {
if (
isObject(parameters.androidInfo) &&
!isString(parameters.androidInfo.androidPackageName)
) {
throw new Error('No androidPackageName was specified.');
}
if (isObject(parameters.iosInfo) && !isString(parameters.iosInfo.iosBundleId)) {
if (
isObject(parameters.iosInfo) &&
!isString(parameters.iosInfo.iosBundleId)
) {
throw new Error('No iosBundleId was specified.');
}
}
@ -98,7 +102,10 @@ export default class Links extends ModuleBase {
* @returns {Function}
*/
onLink(listener: Function): () => any {
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.Link, listener);
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Link,
listener
);
return () => rnListener.remove();
}

View File

@ -12,7 +12,9 @@ export default class RemoteMessage {
id: generatePushID(),
ttl: 3600,
// add the googleapis sender id part if not already added.
sender: `${sender}`.includes('@') ? sender : `${sender}@gcm.googleapis.com`,
sender: `${sender}`.includes('@')
? sender
: `${sender}@gcm.googleapis.com`,
type: 'remote',
data: {},
};
@ -57,7 +59,6 @@ export default class RemoteMessage {
return this;
}
/**
*
* @param data
@ -65,7 +66,9 @@ export default class RemoteMessage {
*/
setData(data: Object = {}) {
if (!isObject(data)) {
throw new Error(`RemoteMessage:setData expects an object as the first parameter but got type '${typeof data}'.`);
throw new Error(
`RemoteMessage:setData expects an object as the first parameter but got type '${typeof data}'.`
);
}
const props = Object.keys(data);

View File

@ -33,10 +33,7 @@ const WILL_PRESENT_RESULT = {
None: 'UNNotificationPresentationOptionNone',
};
const NATIVE_EVENTS = [
EVENT_TYPE.RefreshToken,
EVENT_TYPE.Notification,
];
const NATIVE_EVENTS = [EVENT_TYPE.RefreshToken, EVENT_TYPE.Notification];
const FirebaseMessaging = NativeModules.RNFirebaseMessaging;
@ -49,7 +46,6 @@ function finish(data) {
return;
}
if (!this._finishCalled && this._completionHandlerId) {
let result = data;
@ -59,21 +55,35 @@ function finish(data) {
case NOTIFICATION_TYPE.Remote:
result = result || REMOTE_NOTIFICATION_RESULT.NoData;
if (!Object.values(REMOTE_NOTIFICATION_RESULT).includes(result)) {
throw new Error('Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT');
throw new Error(
'Invalid REMOTE_NOTIFICATION_RESULT value, use messaging().REMOTE_NOTIFICATION_RESULT'
);
}
FirebaseMessaging.finishRemoteNotification(this._completionHandlerId, result);
FirebaseMessaging.finishRemoteNotification(
this._completionHandlerId,
result
);
return;
case NOTIFICATION_TYPE.NotificationResponse:
FirebaseMessaging.finishNotificationResponse(this._completionHandlerId);
return;
case NOTIFICATION_TYPE.WillPresent:
result = result || (this.show_in_foreground ? WILL_PRESENT_RESULT.All : WILL_PRESENT_RESULT.None);
result =
result ||
(this.show_in_foreground
? WILL_PRESENT_RESULT.All
: WILL_PRESENT_RESULT.None);
if (!Object.values(WILL_PRESENT_RESULT).includes(result)) {
throw new Error('Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT');
throw new Error(
'Invalid WILL_PRESENT_RESULT value, use messaging().WILL_PRESENT_RESULT'
);
}
FirebaseMessaging.finishWillPresentNotification(this._completionHandlerId, result);
FirebaseMessaging.finishWillPresentNotification(
this._completionHandlerId,
result
);
break;
default:
}
@ -155,7 +165,10 @@ export default class Messaging extends ModuleBase {
*/
scheduleLocalNotification(notification: Object): Promise<void> {
const _notification = Object.assign({}, notification);
if (!notification.id) return Promise.reject(new Error('An id is required to schedule a local notification.'));
if (!notification.id)
return Promise.reject(
new Error('An id is required to schedule a local notification.')
);
_notification.local_notification = true;
return getNativeModule(this).scheduleLocalNotification(_notification);
}
@ -188,7 +201,8 @@ export default class Messaging extends ModuleBase {
*/
removeDeliveredNotification(id: string): Promise<void> {
if (!id) return Promise.reject(new Error('Missing notification id'));
if (id === '*') return getNativeModule(this).removeAllDeliveredNotifications();
if (id === '*')
return getNativeModule(this).removeAllDeliveredNotifications();
return getNativeModule(this).removeDeliveredNotification(id);
}
@ -201,7 +215,6 @@ export default class Messaging extends ModuleBase {
return getNativeModule(this).requestPermissions();
}
/**
* Set notification count badge number
* @param n
@ -223,10 +236,10 @@ export default class Messaging extends ModuleBase {
* @param listener
* @returns {*}
*/
onMessage(listener: (Object) => any): () => any {
onMessage(listener: Object => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.Notification,
async (event) => {
async event => {
const data = {
...event,
finish,
@ -236,7 +249,7 @@ export default class Messaging extends ModuleBase {
if (!data._finishCalled) {
data.finish();
}
},
}
);
return () => rnListener.remove();
}
@ -246,8 +259,11 @@ export default class Messaging extends ModuleBase {
* @param listener
* @returns {*}
*/
onTokenRefresh(listener: (string) => any): () => any {
const rnListener = SharedEventEmitter.addListener(EVENT_TYPE.RefreshToken, listener);
onTokenRefresh(listener: string => any): () => any {
const rnListener = SharedEventEmitter.addListener(
EVENT_TYPE.RefreshToken,
listener
);
return () => rnListener.remove();
}
@ -273,7 +289,9 @@ export default class Messaging extends ModuleBase {
*/
send(remoteMessage: RemoteMessage): Promise<void> {
if (!(remoteMessage instanceof RemoteMessage)) {
throw new Error('messaging().send requires an instance of RemoteMessage as the first argument.');
throw new Error(
'messaging().send requires an instance of RemoteMessage as the first argument.'
);
}
return getNativeModule(this).send(remoteMessage.toJSON());

View File

@ -14,10 +14,7 @@ import type App from '../core/firebase-app';
const FirebaseStorage = NativeModules.RNFirebaseStorage;
const NATIVE_EVENTS = [
'storage_event',
'storage_error',
];
const NATIVE_EVENTS = ['storage_event', 'storage_error'];
export const MODULE_NAME = 'RNFirebaseStorage';
export const NAMESPACE = 'storage';
@ -38,12 +35,12 @@ export default class Storage extends ModuleBase {
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_event'),
this._handleStorageEvent.bind(this),
this._handleStorageEvent.bind(this)
);
SharedEventEmitter.addListener(
getAppEventName(this, 'storage_error'),
this._handleStorageEvent.bind(this),
this._handleStorageEvent.bind(this)
);
}
@ -118,12 +115,23 @@ export default class Storage extends ModuleBase {
SharedEventEmitter.emit(this._getSubEventName(path, eventName), body);
}
_addListener(path: string, eventName: string, cb: (evt: Object) => Object): void {
_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);
_removeListener(
path: string,
eventName: string,
origCB: (evt: Object) => Object
): void {
SharedEventEmitter.removeListener(
this._getSubEventName(path, eventName),
origCB
);
}
}
@ -138,15 +146,18 @@ export const statics = {
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,
} : {},
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

@ -7,7 +7,6 @@ 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
*/
@ -75,7 +74,11 @@ export default class StorageReference extends ReferenceBase {
* @return {Promise}
*/
downloadFile(filePath: string): Promise<Object> {
return new StorageTask(DOWNLOAD_TASK, getNativeModule(this._storage).downloadFile(this.path, filePath), this);
return new StorageTask(
DOWNLOAD_TASK,
getNativeModule(this._storage).downloadFile(this.path, filePath),
this
);
}
/**
@ -94,6 +97,10 @@ export default class StorageReference extends ReferenceBase {
*/
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);
return new StorageTask(
UPLOAD_TASK,
getNativeModule(this._storage).putFile(this.path, _filePath, metadata),
this
);
}
}

View File

@ -12,31 +12,33 @@ export const DOWNLOAD_TASK = 'download';
declare type UploadTaskSnapshotType = {
bytesTransferred: number,
downloadURL: string|null,
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
state:
| typeof StorageStatics.TaskState.RUNNING
| typeof StorageStatics.TaskState.PAUSED
| typeof StorageStatics.TaskState.SUCCESS
| typeof StorageStatics.TaskState.CANCELLED
| typeof StorageStatics.TaskState.ERROR
),
| typeof StorageStatics.TaskState.ERROR,
task: StorageTask,
totalBytes: number,
};
declare type FuncSnapshotType = null|(snapshot: UploadTaskSnapshotType) => any;
declare type FuncSnapshotType =
| null
| ((snapshot: UploadTaskSnapshotType) => any);
declare type FuncErrorType = null|(error: Error) => any;
declare type FuncErrorType = null | ((error: Error) => any);
declare type NextOrObserverType = null |
{
next?: FuncSnapshotType,
error?: FuncErrorType,
complete?:FuncSnapshotType
} |
FuncSnapshotType;
declare type NextOrObserverType =
| null
| {
next?: FuncSnapshotType,
error?: FuncErrorType,
complete?: FuncSnapshotType,
}
| FuncSnapshotType;
/**
* @url https://firebase.google.com/docs/reference/js/firebase.storage.UploadTask
@ -49,7 +51,11 @@ export default class StorageTask {
then: () => Promise<*>;
catch: () => Promise<*>;
constructor(type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK, promise: Promise<*>, storageRef: StorageReference) {
constructor(
type: typeof UPLOAD_TASK | typeof DOWNLOAD_TASK,
promise: Promise<*>,
storageRef: StorageReference
) {
this.type = type;
this.ref = storageRef;
this.storage = storageRef._storage;
@ -66,9 +72,9 @@ export default class StorageTask {
* @returns {Promise.<T>}
* @private
*/
_interceptSnapshotEvent(f: ?Function): null | () => * {
_interceptSnapshotEvent(f: ?Function): null | (() => *) {
if (!isFunction(f)) return null;
return (snapshot) => {
return snapshot => {
const _snapshot = Object.assign({}, snapshot);
_snapshot.task = this;
_snapshot.ref = this.ref;
@ -82,9 +88,9 @@ export default class StorageTask {
* @returns {*}
* @private
*/
_interceptErrorEvent(f: ?Function): null | (Error) => * {
_interceptErrorEvent(f: ?Function): null | (Error => *) {
if (!isFunction(f)) return null;
return (error) => {
return error => {
const _error = new Error(error.message);
// $FlowFixMe
_error.code = error.code;
@ -100,7 +106,11 @@ export default class StorageTask {
* @returns {function()}
* @private
*/
_subscribe(nextOrObserver: NextOrObserverType, error: FuncErrorType, complete: FuncSnapshotType): Function {
_subscribe(
nextOrObserver: NextOrObserverType,
error: FuncErrorType,
complete: FuncSnapshotType
): Function {
let _error;
let _next;
let _complete;
@ -119,28 +129,31 @@ export default class StorageTask {
this.storage._addListener(
this.path,
StorageStatics.TaskEvent.STATE_CHANGED,
_next,
_next
);
}
if (_error) {
this.storage._addListener(
this.path,
`${this.type}_failure`,
_error,
);
this.storage._addListener(this.path, `${this.type}_failure`, _error);
}
if (_complete) {
this.storage._addListener(
this.path,
`${this.type}_success`,
_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);
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
);
};
}
@ -152,13 +165,24 @@ export default class StorageTask {
* @param complete
* @returns {function()}
*/
on(event: string = StorageStatics.TaskEvent.STATE_CHANGED, nextOrObserver: NextOrObserverType, error: FuncErrorType, complete: FuncSnapshotType): 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\'.');
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}'`);
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
@ -170,16 +194,22 @@ export default class StorageTask {
}
pause() {
throw new Error('.pause() is not currently supported by react-native-firebase');
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');
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');
throw new Error(
'.cancel() is not currently supported by react-native-firebase'
);
}
}

View File

@ -12,8 +12,8 @@ type GoogleApiAvailabilityType = {
isAvailable: boolean,
isUserResolvableError?: boolean,
hasResolution?: boolean,
error?: string
}
error?: string,
};
export const MODULE_NAME = 'RNFirebaseUtils';
export const NAMESPACE = 'utils';
@ -36,12 +36,16 @@ export default class RNFirebaseUtils extends ModuleBase {
const { status } = this.playServicesAvailability;
if (!this.playServicesAvailability.isAvailable) {
if (INTERNALS.OPTIONS.promptOnMissingPlayServices && this.playServicesAvailability.isUserResolvableError) {
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
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);
@ -80,7 +84,12 @@ export default class RNFirebaseUtils extends ModuleBase {
* @return {RNFirebase.GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
*/
get playServicesAvailability(): GoogleApiAvailabilityType {
return FirebaseCoreModule.playServicesAvailability || { isAvailable: true, status: 0 };
return (
FirebaseCoreModule.playServicesAvailability || {
isAvailable: true,
status: 0,
}
);
}
/**
@ -102,5 +111,4 @@ export default class RNFirebaseUtils extends ModuleBase {
}
}
export const statics = {};

View File

@ -35,8 +35,8 @@ export type FirebaseError = {
stack: string,
path: string,
details: string,
modifiers: string
}
modifiers: string,
};
export type FirebaseModule = $Subtype<ModuleBase>;
@ -45,15 +45,36 @@ export type FirebaseModuleConfig = {
moduleName: FirebaseModuleName,
multiApp: boolean,
namespace: FirebaseNamespace,
}
};
export type FirebaseModuleName = 'RNFirebaseAdmob' | 'RNFirebaseAnalytics' | 'RNFirebaseAuth'
| 'RNFirebaseRemoteConfig' | 'RNFirebaseCrash' | 'RNFirebaseCrashlytics' | 'RNFirebaseDatabase'
| 'RNFirebaseFirestore' | 'RNFirebaseLinks' | 'RNFirebaseMessaging' | 'RNFirebasePerformance'
| 'RNFirebaseStorage' | 'RNFirebaseUtils';
export type FirebaseModuleName =
| 'RNFirebaseAdmob'
| 'RNFirebaseAnalytics'
| 'RNFirebaseAuth'
| 'RNFirebaseRemoteConfig'
| 'RNFirebaseCrash'
| 'RNFirebaseCrashlytics'
| 'RNFirebaseDatabase'
| 'RNFirebaseFirestore'
| 'RNFirebaseLinks'
| 'RNFirebaseMessaging'
| 'RNFirebasePerformance'
| 'RNFirebaseStorage'
| 'RNFirebaseUtils';
export type FirebaseNamespace = 'admob' | 'analytics' | 'auth' | 'config' | 'crash'
| 'crashlytics' | 'database' | 'firestore' | 'links' | 'messaging' | 'perf' | 'storage'
export type FirebaseNamespace =
| 'admob'
| 'analytics'
| 'auth'
| 'config'
| 'crash'
| 'crashlytics'
| 'database'
| 'firestore'
| 'links'
| 'messaging'
| 'perf'
| 'storage'
| 'utils';
export type FirebaseOptions = {
@ -63,7 +84,7 @@ export type FirebaseOptions = {
messagingSenderId: string,
projectId: string,
storageBucket: string,
}
};
export type FirebaseModuleAndStatics<M: FirebaseModule, S: FirebaseStatics> = {
(): M,
@ -115,14 +136,14 @@ export type DatabaseModule = {
} & DatabaseStatics;
export type DatabaseModifier = {
id: string;
type: 'orderBy' | 'limit' | 'filter';
name?: string;
key?: string;
limit?: number;
value?: any;
valueType?: string;
}
id: string,
type: 'orderBy' | 'limit' | 'filter',
name?: string,
key?: string,
limit?: number,
value?: any,
valueType?: string,
};
/* Fabric types */
export type CrashlyticsModule = {
@ -132,7 +153,7 @@ export type CrashlyticsModule = {
export type FabricModule = {
crashlytics: CrashlyticsModule,
}
};
/* Firestore types */
@ -146,30 +167,41 @@ export type FirestoreNativeDocumentChange = {
newIndex: number,
oldIndex: number,
type: string,
}
};
export type FirestoreNativeDocumentSnapshot = {
data: { [string]: FirestoreTypeMap },
metadata: FirestoreSnapshotMetadata,
path: string,
}
};
export type FirestoreSnapshotMetadata = {
fromCache: boolean,
hasPendingWrites: boolean,
}
};
export type FirestoreQueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc';
export type FirestoreQueryOperator = '<' | '<=' | '=' | '==' | '>' | '>=';
export type FirestoreTypeMap = {
type: 'array' | 'boolean' | 'date' | 'documentid' | 'fieldvalue' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
type:
| 'array'
| 'boolean'
| 'date'
| 'documentid'
| 'fieldvalue'
| 'geopoint'
| 'null'
| 'number'
| 'object'
| 'reference'
| 'string',
value: any,
}
};
export type FirestoreWriteOptions = {
merge?: boolean,
}
};
/* Links types */

View File

@ -29,7 +29,10 @@ export default class ModuleBase {
// check if native module exists as all native
initialiseNativeModule(this, config);
initialiseLogger(this, `${app.name}:${moduleName.replace('RNFirebase', '')}`);
initialiseLogger(
this,
`${app.name}:${moduleName.replace('RNFirebase', '')}`
);
}
/**

View File

@ -5,7 +5,14 @@ export default class ReferenceBase {
path: string;
constructor(path: string) {
this.path = path || '/';
if (path) {
this.path =
path.length > 1 && path.endsWith('/')
? path.substring(0, path.length - 1)
: path;
} else {
this.path = '/';
}
}
/**
@ -15,6 +22,8 @@ export default class ReferenceBase {
* {@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);
return this.path === '/'
? null
: this.path.substring(this.path.lastIndexOf('/') + 1);
}
}

View File

@ -8,7 +8,7 @@ import DatabaseSnapshot from '../modules/database/snapshot';
import DatabaseReference from '../modules/database/reference';
import { isString, nativeToJSError } from '../utils';
type Listener = (DatabaseSnapshot) => any;
type Listener = DatabaseSnapshot => any;
type Registration = {
key: string,
@ -19,7 +19,7 @@ type Registration = {
listener: Listener,
eventRegistrationKey: string,
ref: DatabaseReference,
}
};
/**
* Internally used to manage firebase database realtime event
@ -28,16 +28,18 @@ type Registration = {
class SyncTree {
_nativeEmitter: NativeEventEmitter;
_reverseLookup: { [string]: Registration };
_tree: { [string]: { [string]: { [string]: Listener }}};
_tree: { [string]: { [string]: { [string]: Listener } } };
constructor() {
this._tree = {};
this._reverseLookup = {};
if (NativeModules.RNFirebaseDatabase) {
this._nativeEmitter = new NativeEventEmitter(NativeModules.RNFirebaseDatabase);
this._nativeEmitter = new NativeEventEmitter(
NativeModules.RNFirebaseDatabase
);
this._nativeEmitter.addListener(
'database_sync_event',
this._handleSyncEvent.bind(this),
this._handleSyncEvent.bind(this)
);
}
}
@ -82,11 +84,10 @@ class SyncTree {
return SharedEventEmitter.emit(
eventRegistrationKey,
new DatabaseSnapshot(registration.ref, snapshot),
previousChildName,
previousChildName
);
}
/**
* Routes native database query listener cancellation events to their js counterparts.
*
@ -96,7 +97,10 @@ class SyncTree {
_handleErrorEvent(event) {
// console.log('SyncTree.ERROR >>>', event);
const { code, message } = event.error;
const { eventRegistrationKey, registrationCancellationKey } = event.registration;
const {
eventRegistrationKey,
registrationCancellationKey,
} = event.registration;
const registration = this.getRegistration(registrationCancellationKey);
@ -121,7 +125,9 @@ class SyncTree {
* @return {null}
*/
getRegistration(registration: string): Registration | null {
return this._reverseLookup[registration] ? Object.assign({}, this._reverseLookup[registration]) : null;
return this._reverseLookup[registration]
? Object.assign({}, this._reverseLookup[registration])
: null;
}
/**
@ -159,7 +165,9 @@ class SyncTree {
for (let i = 0, len = registrations.length; i < len; i++) {
const registration = registrations[i];
const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
const subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(
registration
);
if (subscriptions) {
for (let j = 0, l = subscriptions.length; j < l; j++) {
const subscription = subscriptions[j];
@ -188,7 +196,10 @@ class SyncTree {
const eventKeys = Object.keys(this._tree[path] || {});
for (let i = 0, len = eventKeys.length; i < len; i++) {
Array.prototype.push.apply(out, Object.keys(this._tree[path][eventKeys[i]]));
Array.prototype.push.apply(
out,
Object.keys(this._tree[path][eventKeys[i]])
);
}
return out;
@ -216,11 +227,17 @@ class SyncTree {
* @param listener
* @return {Array}
*/
getOneByPathEventListener(path: string, eventType: string, listener: Function): ?string {
getOneByPathEventListener(
path: string,
eventType: string,
listener: Function
): ?string {
if (!this._tree[path]) return null;
if (!this._tree[path][eventType]) return null;
const registrationsForPathEvent = Object.entries(this._tree[path][eventType]);
const registrationsForPathEvent = Object.entries(
this._tree[path][eventType]
);
for (let i = 0; i < registrationsForPathEvent.length; i++) {
const registration = registrationsForPathEvent[i];
@ -230,7 +247,6 @@ class SyncTree {
return null;
}
/**
* Register a new listener.
*
@ -256,7 +272,7 @@ class SyncTree {
if (once) {
SharedEventEmitter.once(
eventRegistrationKey,
this._onOnceRemoveRegistration(eventRegistrationKey, listener),
this._onOnceRemoveRegistration(eventRegistrationKey, listener)
);
} else {
SharedEventEmitter.addListener(eventRegistrationKey, listener);

View File

@ -18,7 +18,7 @@ import type {
const FirebaseCoreModule = NativeModules.RNFirebase;
const APPS: { [string]: App } = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule }} = {};
const APP_MODULES: { [App]: { [string]: FirebaseModule } } = {};
const DEFAULT_APP_NAME = '[DEFAULT]';
export default {
@ -43,13 +43,21 @@ export default {
* @return {function()}
* @private
*/
appModule<M: FirebaseModule>(app: App, namespace: FirebaseNamespace, InstanceClass: Class<M>): () => FirebaseModule {
appModule<M: FirebaseModule>(
app: App,
namespace: FirebaseNamespace,
InstanceClass: Class<M>
): () => FirebaseModule {
return (): M => {
if (!APP_MODULES[app]) {
APP_MODULES[app] = {};
}
if (isAndroid && namespace !== 'utils' && !INTERNALS.FLAGS.checkedPlayServices) {
if (
isAndroid &&
namespace !== 'utils' &&
!INTERNALS.FLAGS.checkedPlayServices
) {
INTERNALS.FLAGS.checkedPlayServices = true;
app.utils().checkPlayServicesAvailability();
}
@ -148,16 +156,21 @@ export default {
* @param InstanceClass
* @return {function(App=)}
*/
moduleAndStatics<M: FirebaseModule, S: FirebaseStatics>(namespace: FirebaseNamespace, statics: S, moduleName: FirebaseModuleName): FirebaseModuleAndStatics<M, S> {
moduleAndStatics<M: FirebaseModule, S: FirebaseStatics>(
namespace: FirebaseNamespace,
statics: S,
moduleName: FirebaseModuleName
): FirebaseModuleAndStatics<M, S> {
const getModule = (app?: App): FirebaseModule => {
let _app = app;
// throw an error if it's not a valid app instance
if (_app && !(_app instanceof App)) 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(DEFAULT_APP_NAME);
if (_app && !(_app instanceof App))
throw new Error(INTERNALS.STRINGS.ERROR_NOT_APP(namespace));
else if (!_app)
// default to the 'DEFAULT' app if no arg provided - will throw an error
// if default app not initialized
_app = this.app(DEFAULT_APP_NAME);
if (namespace === 'crashlytics') {
return _app.fabric[namespace]();
}

View File

@ -12,11 +12,15 @@ const NATIVE_SUBSCRIPTIONS: { [string]: boolean } = {};
export const SharedEventEmitter = new EventEmitter();
export const getAppEventName = (module: ModuleBase, eventName: string): string => {
return `${module.app.name}-${eventName}`;
};
export const getAppEventName = (
module: ModuleBase,
eventName: string
): string => `${module.app.name}-${eventName}`;
const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): NativeEventEmitter => {
const getNativeEmitter = (
moduleName: FirebaseModuleName,
module: ModuleBase
): NativeEventEmitter => {
const name = `${module.app.name}-${moduleName}`;
const nativeModule = NativeModules[moduleName];
if (!NATIVE_EMITTERS[name]) {
@ -35,10 +39,14 @@ const getNativeEmitter = (moduleName: FirebaseModuleName, module: ModuleBase): N
* @param eventName
* @private
*/
const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: ModuleBase, eventName: string): void => {
const subscribeToNativeModuleEvents = (
moduleName: FirebaseModuleName,
module: ModuleBase,
eventName: string
): void => {
if (!NATIVE_SUBSCRIPTIONS[eventName]) {
const nativeEmitter = getNativeEmitter(moduleName, module);
nativeEmitter.addListener(eventName, (event) => {
nativeEmitter.addListener(eventName, event => {
if (event.appName) {
// native event has an appName property - auto prefix and internally emit
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
@ -52,7 +60,10 @@ const subscribeToNativeModuleEvents = (moduleName: FirebaseModuleName, module: M
}
};
export const initialiseNativeModuleEventEmitter = (module: ModuleBase, config: FirebaseModuleConfig): void => {
export const initialiseNativeModuleEventEmitter = (
module: ModuleBase,
config: FirebaseModuleConfig
): void => {
const { events, moduleName } = config;
if (events && events.length) {
for (let i = 0, len = events.length; i < len; i++) {

View File

@ -4,8 +4,10 @@ import { Platform } from 'react-native';
// todo cleanup unused utilities from legacy code
// modeled after base64 web-safe chars, but ordered by ASCII
const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const PUSH_CHARS =
'-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
const AUTO_ID_CHARS =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const { hasOwnProperty } = Object;
// const DEFAULT_CHUNK_SIZE = 50;
@ -18,7 +20,11 @@ const { hasOwnProperty } = Object;
* @param joiner
* @returns {*}
*/
export function deepGet(object: Object, path: string, joiner?: string = '/'): any {
export function deepGet(
object: Object,
path: string,
joiner?: string = '/'
): any {
const keys = path.split(joiner);
let i = 0;
@ -42,7 +48,11 @@ export function deepGet(object: Object, path: string, joiner?: string = '/'): an
* @param joiner
* @returns {*}
*/
export function deepExists(object: Object, path: string, joiner?: string = '/'): boolean {
export function deepExists(
object: Object,
path: string,
joiner?: string = '/'
): boolean {
const keys = path.split(joiner);
let i = 0;
@ -64,18 +74,23 @@ export function deepExists(object: Object, path: string, joiner?: string = '/'):
* @param obj2
* @returns {boolean}
*/
export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): boolean {
export function areObjectKeysContainedInOther(
obj1: Object,
obj2: Object
): boolean {
if (!isObject(obj1) || !isObject(obj2)) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (isArrayContainedInOther(keys1, keys2)) {
return keys1.filter((key) => {
return isObject(obj1[key]);
}).reduce((acc, cur) => {
return acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]);
}, true);
return keys1
.filter(key => isObject(obj1[key]))
.reduce(
(acc, cur) =>
acc && areObjectKeysContainedInOther(obj1[cur], obj2[cur]),
true
);
}
return false;
}
@ -86,13 +101,14 @@ export function areObjectKeysContainedInOther(obj1 : Object, obj2: Object): bool
* @param arr2
* @returns {boolean}
*/
export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean {
export function isArrayContainedInOther(
arr1: Array<*>,
arr2: Array<*>
): boolean {
if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
return false;
}
return arr1.reduce((acc, cur) => {
return acc && arr2.includes(cur);
}, true);
return arr1.reduce((acc, cur) => acc && arr2.includes(cur), true);
}
/**
@ -101,7 +117,9 @@ export function isArrayContainedInOther(arr1: Array<*>, arr2: Array<*>): boolean
* @returns {boolean}
*/
export function isObject(item: mixed): boolean %checks {
return item ? (typeof item === 'object' && !Array.isArray(item) && item !== null) : false;
return item
? typeof item === 'object' && !Array.isArray(item) && item !== null
: false;
}
/**
@ -126,7 +144,6 @@ export function isString(value: mixed): boolean %checks {
export const isIOS = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';
/**
*
* @param string
@ -153,16 +170,16 @@ export function tryJSONStringify(data: mixed): string | null {
}
}
// noinspection Eslint
export const windowOrGlobal = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this;
export const windowOrGlobal =
// eslint-disable-next-line no-restricted-globals
(typeof self === 'object' && self.self === self && self) ||
(typeof global === 'object' && global.global === global && global) ||
this;
/**
* No operation func
*/
export function noop(): void {
}
export function noop(): void {}
// /**
// * Delays chunks based on sizes per event loop.
@ -286,7 +303,7 @@ const lastRandChars = [];
export function generatePushID(serverTimeOffset?: number = 0): string {
const timeStampChars = new Array(8);
let now = new Date().getTime() + serverTimeOffset;
const duplicateTime = (now === lastPushTime);
const duplicateTime = now === lastPushTime;
lastPushTime = now;
@ -295,7 +312,8 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
now = Math.floor(now / 64);
}
if (now !== 0) throw new Error('We should have converted the entire timestamp.');
if (now !== 0)
throw new Error('We should have converted the entire timestamp.');
let id = timeStampChars.join('');
@ -330,7 +348,11 @@ export function generatePushID(serverTimeOffset?: number = 0): string {
* @param additionalProps
* @returns {Error}
*/
export function nativeToJSError(code: string, message: string, additionalProps?: Object = {}) {
export function nativeToJSError(
code: string,
message: string,
additionalProps?: Object = {}
) {
const error: Object = new Error(message);
error.code = code;
Object.assign(error, additionalProps);
@ -362,7 +384,6 @@ export function objectToUniqueId(object: Object): string {
return key;
}
/**
* Return the existing promise if no callback provided or
* exec the promise and callback if optionalCallback is valid.
@ -371,23 +392,28 @@ export function objectToUniqueId(object: Object): string {
* @param optionalCallback
* @return {Promise}
*/
export function promiseOrCallback(promise: Promise<*>, optionalCallback?: Function): Promise<*> {
export function promiseOrCallback(
promise: Promise<*>,
optionalCallback?: Function
): Promise<*> {
if (!isFunction(optionalCallback)) return promise;
return promise.then((result) => {
// some of firebase internal tests & methods only check/return one arg
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
if (optionalCallback && optionalCallback.length === 1) {
optionalCallback(null);
} else if (optionalCallback) {
optionalCallback(null, result);
}
return promise
.then(result => {
// some of firebase internal tests & methods only check/return one arg
// see https://github.com/firebase/firebase-js-sdk/blob/master/src/utils/promise.ts#L62
if (optionalCallback && optionalCallback.length === 1) {
optionalCallback(null);
} else if (optionalCallback) {
optionalCallback(null, result);
}
return Promise.resolve(result);
}).catch((error) => {
if (optionalCallback) optionalCallback(error);
return Promise.reject(error);
});
return Promise.resolve(result);
})
.catch(error => {
if (optionalCallback) optionalCallback(error);
return Promise.reject(error);
});
}
/**
@ -398,7 +424,9 @@ export function firestoreAutoId(): string {
let autoId = '';
for (let i = 0; i < 20; i++) {
autoId += AUTO_ID_CHARS.charAt(Math.floor(Math.random() * AUTO_ID_CHARS.length));
autoId += AUTO_ID_CHARS.charAt(
Math.floor(Math.random() * AUTO_ID_CHARS.length)
);
}
return autoId;
}

View File

@ -29,17 +29,20 @@ const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
2: {
code: 'SERVICE_VERSION_UPDATE_REQUIRED',
message: 'The installed version of Google Play services on this device is out of date.',
message:
'The installed version of Google Play services on this device is out of date.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
3: {
code: 'SERVICE_DISABLED',
message: 'The installed version of Google Play services has been disabled on this device.',
message:
'The installed version of Google Play services has been disabled on this device.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
9: {
code: 'SERVICE_INVALID',
message: 'The version of the Google Play services installed on this device is not authentic.',
message:
'The version of the Google Play services installed on this device is not authentic.',
},
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
18: {
@ -49,7 +52,8 @@ const PLAY_SERVICES_CODES = {
// $FlowBug: Doesn't like numerical object keys: https://github.com/facebook/flow/issues/380
19: {
code: 'SERVICE_MISSING_PERMISSION',
message: 'Google Play service doesn\'t have one or more required permissions.',
message:
"Google Play service doesn't have one or more required permissions.",
},
};
@ -66,27 +70,33 @@ export default {
},
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.',
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 ' +
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.';
'\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 ' +
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.';
'\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.',
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}
@ -131,20 +141,28 @@ export default {
ERROR_MISSING_MODULE(namespace: string, nativeModule: string) {
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}.` +
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.';
`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 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}.` +
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.';
'\r\n\r\nSee http://invertase.link/android for full setup instructions.'
);
},
/**
@ -191,29 +209,30 @@ export default {
return `firebase.${namespace}().${method}() is unsupported by the native Firebase SDKs.`;
},
/**
* @return {string}
*/
ERROR_PLAY_SERVICES(statusCode: number) {
const knownError = PLAY_SERVICES_CODES[statusCode];
let start = 'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
let start =
'Google Play Services is required to run firebase services on android but a valid installation was not found on this device.';
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.';
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})`
) +
'\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';
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})`
}\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`
);
},
},
};

View File

@ -5,7 +5,7 @@ import { windowOrGlobal } from './';
import type ModuleBase from './ModuleBase';
((base) => {
(base => {
window = base || window;
// $FlowFixMe: Why are we using localStorage at all?
if (!window.localStorage) window.localStorage = {};
@ -15,7 +15,8 @@ import type ModuleBase from './ModuleBase';
const NATIVE_LOGGERS: { [string]: Object } = {};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
const getModuleKey = (module: ModuleBase): string =>
`${module.app.name}:${module.namespace}`;
export const getLogger = (module: ModuleBase) => {
const key = getModuleKey(module);
@ -25,12 +26,14 @@ export const getLogger = (module: ModuleBase) => {
export const initialiseLogger = (module: ModuleBase, logNamespace: string) => {
const key = getModuleKey(module);
if (!NATIVE_LOGGERS[key]) {
// eslint-disable-next-line global-require
NATIVE_LOGGERS[key] = require('bows')(`🔥 ${logNamespace.toUpperCase()}`);
}
};
export default class Log {
static createLogger(namespace: string) {
// eslint-disable-next-line global-require
return require('bows')(namespace);
}

View File

@ -21,28 +21,32 @@ const nativeWithApp = (appName: string, NativeModule: Object): Object => {
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => {
return NativeModule[method](...[appName, ...args]);
};
native[method] = (...args) => NativeModule[method](...[appName, ...args]);
}
return native;
};
const getModuleKey = (module: ModuleBase): string => `${module.app.name}:${module.namespace}`;
const getModuleKey = (module: ModuleBase): string =>
`${module.app.name}:${module.namespace}`;
export const getNativeModule = (module: ModuleBase): Object => {
const key = getModuleKey(module);
return NATIVE_MODULES[key];
};
export const initialiseNativeModule = (module: ModuleBase, config: FirebaseModuleConfig): Object => {
export const initialiseNativeModule = (
module: ModuleBase,
config: FirebaseModuleConfig
): Object => {
const { moduleName, multiApp, namespace } = config;
const nativeModule = NativeModules[moduleName];
const key = getModuleKey(module);
if (!nativeModule && namespace !== 'utils') {
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_MODULE(namespace, moduleName));
throw new Error(
INTERNALS.STRINGS.ERROR_MISSING_MODULE(namespace, moduleName)
);
}
// used by the modules that extend ModuleBase

626
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "react-native-firebase",
"version": "3.2.0",
"version": "3.2.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -102,6 +102,12 @@
"integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
"dev": true
},
"any-observable": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.2.0.tgz",
"integrity": "sha1-xnhwBYADV5AJCD9UrAq6+1wz0kI=",
"dev": true
},
"anymatch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@ -112,6 +118,12 @@
"normalize-path": "2.1.1"
}
},
"app-root-path": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz",
"integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=",
"dev": true
},
"are-we-there-yet": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
@ -1423,6 +1435,12 @@
"readdirp": "2.1.0"
}
},
"ci-info": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.2.tgz",
"integrity": "sha512-uTGIPNx/nSpBdsF6xnseRXLLtfr9VLqkz8ZqHXr3Y7b6SftyRxBGjwMtJj1OhNbmlc1wZzLNAlAcvyIiE8a6ZA==",
"dev": true
},
"circular-json": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
@ -1438,6 +1456,30 @@
"restore-cursor": "1.0.1"
}
},
"cli-spinners": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz",
"integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=",
"dev": true
},
"cli-truncate": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
"integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
"dev": true,
"requires": {
"slice-ansi": "0.0.4",
"string-width": "1.0.2"
},
"dependencies": {
"slice-ansi": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
"integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
"dev": true
}
}
},
"cli-width": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
@ -1723,6 +1765,30 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"cosmiconfig": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz",
"integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==",
"dev": true,
"requires": {
"is-directory": "0.3.1",
"js-yaml": "3.10.0",
"parse-json": "4.0.0",
"require-from-string": "2.0.1"
},
"dependencies": {
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "1.3.1",
"json-parse-better-errors": "1.0.1"
}
}
}
},
"crc": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.3.0.tgz",
@ -1827,6 +1893,12 @@
"assert-plus": "1.0.0"
}
},
"date-fns": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
"integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==",
"dev": true
},
"dateformat": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
@ -1848,6 +1920,12 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"dedent": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
"dev": true
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@ -2034,6 +2112,12 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"dev": true
},
"elegant-spinner": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
"integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=",
"dev": true
},
"emoji-regex": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
@ -2294,6 +2378,15 @@
"eslint-restricted-globals": "0.1.1"
}
},
"eslint-config-prettier": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz",
"integrity": "sha512-ag8YEyBXsm3nmOv1Hz991VtNNDMRa+MNy8cY47Pl4bw6iuzqKbJajXdqUpiw13STdLLrznxgm1hj9NhxeOYq0A==",
"dev": true,
"requires": {
"get-stdin": "5.0.1"
}
},
"eslint-import-resolver-node": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz",
@ -2368,6 +2461,24 @@
"jsx-ast-utils": "1.4.1"
}
},
"eslint-plugin-prettier": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.5.0.tgz",
"integrity": "sha512-L06bewYpt2Wb8Uk7os8f/0cL5DjddL38t1M/nOpjw5MqVFBn1RIIBBE6tfr37lHUH7AvAubZsvu/bDmNl4RBKQ==",
"dev": true,
"requires": {
"fast-diff": "1.1.2",
"jest-docblock": "21.2.0"
},
"dependencies": {
"jest-docblock": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz",
"integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==",
"dev": true
}
}
},
"eslint-plugin-react": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz",
@ -2606,6 +2717,12 @@
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
"dev": true
},
"fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
@ -2747,6 +2864,12 @@
"parents": "1.0.1"
}
},
"find-parent-dir": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
"integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=",
"dev": true
},
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
@ -3910,6 +4033,18 @@
"integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
"dev": true
},
"get-own-enumerable-property-symbols": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz",
"integrity": "sha512-TtY/sbOemiMKPRUDDanGCSgBYe7Mf0vbRsWnBZ+9yghpZ1MvcpSpuZFjHdEeY/LZjZy0vdLjS77L6HosisFiug==",
"dev": true
},
"get-stdin": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
"integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=",
"dev": true
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
@ -4181,6 +4316,25 @@
"sshpk": "1.13.1"
}
},
"husky": {
"version": "0.14.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-0.14.3.tgz",
"integrity": "sha512-e21wivqHpstpoiWA/Yi8eFti8E+sQDSS53cpJsPptPs295QTOQR0ZwnHo2TXy1XOpZFD9rPOd3NpmqTK6uMLJA==",
"dev": true,
"requires": {
"is-ci": "1.1.0",
"normalize-path": "1.0.0",
"strip-indent": "2.0.0"
},
"dependencies": {
"normalize-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz",
"integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=",
"dev": true
}
}
},
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
@ -4204,6 +4358,15 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"indent-string": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
"integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
"dev": true,
"requires": {
"repeating": "2.0.1"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -4430,12 +4593,27 @@
"integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
"dev": true
},
"is-ci": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz",
"integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==",
"dev": true,
"requires": {
"ci-info": "1.1.2"
}
},
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-directory": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
},
"is-dotfile": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
@ -4499,6 +4677,29 @@
"kind-of": "3.2.2"
}
},
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
"is-observable": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/is-observable/-/is-observable-0.2.0.tgz",
"integrity": "sha1-s2ExHYPG5dcmyr9eJQsCNxBvWuI=",
"dev": true,
"requires": {
"symbol-observable": "0.2.4"
},
"dependencies": {
"symbol-observable": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-0.2.4.tgz",
"integrity": "sha1-lag9smGG1q9+ehjb2XYKL4bQj0A=",
"dev": true
}
}
},
"is-path-cwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
@ -4549,6 +4750,12 @@
"has": "1.0.1"
}
},
"is-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
"integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
"dev": true
},
"is-resolvable": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
@ -4638,6 +4845,12 @@
"detect-newline": "2.1.0"
}
},
"jest-get-type": {
"version": "21.2.0",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-21.2.0.tgz",
"integrity": "sha512-y2fFw3C+D0yjNSDp7ab1kcd6NUYfy3waPTlD8yWkAtiocJdBRQqNoRqVfMNxgj+IjT0V5cBIHJO0z9vuSSZ43Q==",
"dev": true
},
"jest-haste-map": {
"version": "22.0.3",
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.0.3.tgz",
@ -4652,6 +4865,65 @@
"sane": "2.2.0"
}
},
"jest-validate": {
"version": "21.2.1",
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-21.2.1.tgz",
"integrity": "sha512-k4HLI1rZQjlU+EC682RlQ6oZvLrE5SCh3brseQc24vbZTxzT/k/3urar5QMCVgjadmSO7lECeGdc6YxnM3yEGg==",
"dev": true,
"requires": {
"chalk": "2.3.0",
"jest-get-type": "21.2.0",
"leven": "2.1.0",
"pretty-format": "21.2.1"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"dev": true,
"requires": {
"color-convert": "1.9.1"
}
},
"chalk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
"integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
"dev": true,
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.5.0"
}
},
"pretty-format": {
"version": "21.2.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-21.2.1.tgz",
"integrity": "sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A==",
"dev": true,
"requires": {
"ansi-regex": "3.0.0",
"ansi-styles": "3.2.0"
}
},
"supports-color": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
}
}
},
"jest-worker": {
"version": "22.0.3",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-22.0.3.tgz",
@ -4694,6 +4966,12 @@
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true
},
"json-parse-better-errors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz",
"integrity": "sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw==",
"dev": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@ -4808,6 +5086,12 @@
"integrity": "sha1-0wpzxrggHY99jnlWupYWCHpo4O4=",
"dev": true
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
"dev": true
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@ -4818,6 +5102,197 @@
"type-check": "0.3.2"
}
},
"lint-staged": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-6.0.1.tgz",
"integrity": "sha512-GZnFshBzIpJMHO5aSqXGVJh5G1agKTrKGQOs6cTKA6a62PvZ7l2RawbpOrFdzjzkezxm7+LpKeleNt83gd9yRA==",
"dev": true,
"requires": {
"app-root-path": "2.0.1",
"chalk": "2.3.0",
"commander": "2.11.0",
"cosmiconfig": "4.0.0",
"debug": "3.1.0",
"dedent": "0.7.0",
"execa": "0.8.0",
"find-parent-dir": "0.3.0",
"is-glob": "4.0.0",
"jest-validate": "21.2.1",
"listr": "0.13.0",
"lodash": "4.17.4",
"log-symbols": "2.2.0",
"minimatch": "3.0.4",
"npm-which": "3.0.1",
"p-map": "1.2.0",
"path-is-inside": "1.0.2",
"pify": "3.0.0",
"staged-git-files": "0.0.4",
"stringify-object": "3.2.1"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"dev": true,
"requires": {
"color-convert": "1.9.1"
}
},
"chalk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
"integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
"dev": true,
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.5.0"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"execa": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz",
"integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=",
"dev": true,
"requires": {
"cross-spawn": "5.1.0",
"get-stream": "3.0.0",
"is-stream": "1.1.0",
"npm-run-path": "2.0.2",
"p-finally": "1.0.0",
"signal-exit": "3.0.2",
"strip-eof": "1.0.0"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
},
"is-glob": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
"integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
"dev": true,
"requires": {
"is-extglob": "2.1.1"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"supports-color": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
}
}
},
"listr": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/listr/-/listr-0.13.0.tgz",
"integrity": "sha1-ILsLowuuZg7oTMBQPfS+PVYjiH0=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"cli-truncate": "0.2.1",
"figures": "1.7.0",
"indent-string": "2.1.0",
"is-observable": "0.2.0",
"is-promise": "2.1.0",
"is-stream": "1.1.0",
"listr-silent-renderer": "1.1.1",
"listr-update-renderer": "0.4.0",
"listr-verbose-renderer": "0.4.1",
"log-symbols": "1.0.2",
"log-update": "1.0.2",
"ora": "0.2.3",
"p-map": "1.2.0",
"rxjs": "5.5.6",
"stream-to-observable": "0.2.0",
"strip-ansi": "3.0.1"
},
"dependencies": {
"log-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
"integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
"dev": true,
"requires": {
"chalk": "1.1.3"
}
}
}
},
"listr-silent-renderer": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz",
"integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=",
"dev": true
},
"listr-update-renderer": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz",
"integrity": "sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"cli-truncate": "0.2.1",
"elegant-spinner": "1.0.1",
"figures": "1.7.0",
"indent-string": "3.2.0",
"log-symbols": "1.0.2",
"log-update": "1.0.2",
"strip-ansi": "3.0.1"
},
"dependencies": {
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"dev": true
},
"log-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
"integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
"dev": true,
"requires": {
"chalk": "1.1.3"
}
}
}
},
"listr-verbose-renderer": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
"integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"cli-cursor": "1.0.2",
"date-fns": "1.29.0",
"figures": "1.7.0"
}
},
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@ -5074,6 +5549,56 @@
"lodash.escape": "3.2.0"
}
},
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
"dev": true,
"requires": {
"chalk": "2.3.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"dev": true,
"requires": {
"color-convert": "1.9.1"
}
},
"chalk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
"integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
"dev": true,
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.5.0"
}
},
"supports-color": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
}
}
},
"log-update": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz",
"integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=",
"dev": true,
"requires": {
"ansi-escapes": "1.4.0",
"cli-cursor": "1.0.2"
}
},
"loose-envify": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
@ -5542,6 +6067,15 @@
"remove-trailing-separator": "1.1.0"
}
},
"npm-path": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz",
"integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==",
"dev": true,
"requires": {
"which": "1.3.0"
}
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@ -5551,6 +6085,17 @@
"path-key": "2.0.1"
}
},
"npm-which": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz",
"integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=",
"dev": true,
"requires": {
"commander": "2.11.0",
"npm-path": "2.0.4",
"which": "1.3.0"
}
},
"npmlog": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz",
@ -5879,6 +6424,18 @@
"integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=",
"dev": true
},
"ora": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz",
"integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"cli-cursor": "1.0.2",
"cli-spinners": "0.1.2",
"object-assign": "4.1.1"
}
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@ -5947,6 +6504,12 @@
"p-limit": "1.1.0"
}
},
"p-map": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
"integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
"dev": true
},
"parents": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
@ -6120,6 +6683,12 @@
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
"dev": true
},
"prettier": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.10.2.tgz",
"integrity": "sha512-TcdNoQIWFoHblurqqU6d1ysopjq7UX0oRcT/hJ8qvBAELiYWn+Ugf0AXdnzISEJ7vuhNnQ98N8jR8Sh53x4IZg==",
"dev": true
},
"pretty-format": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-4.3.1.tgz",
@ -6646,6 +7215,12 @@
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-from-string": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.1.tgz",
"integrity": "sha1-xUUjPp19pmFunVmt+zn8n1iGdv8=",
"dev": true
},
"require-main-filename": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
@ -6749,6 +7324,23 @@
"rx-lite": "3.1.2"
}
},
"rxjs": {
"version": "5.5.6",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz",
"integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==",
"dev": true,
"requires": {
"symbol-observable": "1.0.1"
},
"dependencies": {
"symbol-observable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
"integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
"dev": true
}
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
@ -7129,6 +7721,12 @@
"integrity": "sha1-ATl5IuX2Ls8whFUiyVxP4dJefU4=",
"dev": true
},
"staged-git-files": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-0.0.4.tgz",
"integrity": "sha1-15fhtVHKemOd7AI33G60u5vhfTU=",
"dev": true
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
@ -7176,6 +7774,15 @@
}
}
},
"stream-to-observable": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.2.0.tgz",
"integrity": "sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA=",
"dev": true,
"requires": {
"any-observable": "0.2.0"
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@ -7196,6 +7803,17 @@
"safe-buffer": "5.1.1"
}
},
"stringify-object": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.2.1.tgz",
"integrity": "sha512-jPcQYw/52HUPP8uOE4kkjxl5bB9LfHkKCTptIk3qw7ozP5XMIMlHMLjt00GGSwW6DJAf/njY5EU6Vpwl4LlBKQ==",
"dev": true,
"requires": {
"get-own-enumerable-property-symbols": "2.0.1",
"is-obj": "1.0.1",
"is-regexp": "1.0.0"
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@ -7222,6 +7840,12 @@
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
"strip-indent": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
"integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=",
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "react-native-firebase",
"version": "3.2.0",
"version": "3.2.2",
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Messaging (FCM), Remote Config, Storage and Performance.",
"main": "dist/index.js",
@ -11,7 +11,9 @@
"build-lib": "BABEL_ENV=publish babel lib -d dist --ignore __tests__ --copy-files",
"clean": "rimraf dist/",
"flow": "flow",
"lint": "eslint ./src",
"format": "eslint --fix ./lib ./tests/src ./tests/lib",
"lint": "eslint ./lib ./tests/src ./tests/lib",
"precommit": "lint-staged",
"prepublish": "npm run clean && npm run build",
"postinstall": "postinstall-build dist && opencollective postinstall || exit 0",
"test-cli": "node ./bin/test.js",
@ -82,13 +84,18 @@
"enzyme": "^2.4.1",
"eslint": "^4.11.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-flowtype": "^2.39.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.4.0",
"flow-bin": "^0.61.0",
"flow-copy-source": "^1.2.1",
"genversion": "^2.0.1",
"husky": "^0.14.3",
"lint-staged": "^6.0.1",
"prettier": "1.10.2",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-native": "^0.52.0",
@ -116,5 +123,19 @@
"type": "opencollective",
"url": "https://opencollective.com/react-native-firebase",
"logo": "https://opencollective.com/opencollective/logo.txt"
},
"lint-staged": {
"lib/**/*.js": [
"eslint --fix",
"git add"
],
"tests/src/**/*.js": [
"eslint --fix",
"git add"
],
"tests/lib/**/*.js": [
"eslint --fix",
"git add"
]
}
}

4
prettier.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
trailingComma: 'es5',
singleQuote: true,
};

View File

@ -1,42 +1,39 @@
{
"extends": "airbnb",
"extends": [
"airbnb",
"prettier",
"prettier/flowtype",
"prettier/react"
],
"parser": "babel-eslint",
"ecmaFeatures": {
"jsx": true
},
"plugins": [
"flowtype"
"flowtype",
"prettier"
],
"env": {
"es6": true,
"jasmine": true
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"rules": {
"no-plusplus": 0,
"prettier/prettier": ["error", {
"trailingComma": "es5",
"singleQuote": true
}],
"react/forbid-prop-types": "warn",
"react/jsx-filename-extension": [
"off", { "extensions": [".js", ".jsx"] }
],
"class-methods-use-this": 0,
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"arrow-body-style": 0,
"import/prefer-default-export": 0,
"radix": 0,
"new-cap": 0,
"max-len": 0,
"no-continue": 0,
"no-console": 0,
"global-require": 0,
"import/extensions": 0,
"import/no-unresolved": 0,
"import/no-extraneous-dependencies": 0,
"react/jsx-filename-extension": 0
"no-plusplus": 0,
"no-undef": 0,
"no-underscore-dangle": "off",
"no-use-before-define": 0
},
"globals": {
"__DEV__": true,
"window": true,
"fetch": true,
"window": true
}
}

View File

@ -2,4 +2,3 @@ import { AppRegistry } from 'react-native';
import bootstrap from './src/main';
AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap);

View File

@ -199,7 +199,6 @@ class TestDSL {
this.it(description, _options, _testFunction);
}
/**
* Defines a new focused test. Focused tests are the only
* ones that appear and are run when the test suite is executed.
@ -240,12 +239,4 @@ class TestDSL {
}
}
/**
* Log a test DSL error to the console.
* @param {String} error - Message to included in message logged to the console
*/
function testDSLError(error) {
console.error(`ReactNativeFirebaseTests.TestDSLError: ${error}`);
console.error('This test was ignored.');
}
export default TestDSL;

View File

@ -25,7 +25,6 @@ function cleanStack(stack, maxLines = 5) {
return out.join('\r\n');
}
/**
* Class that encapsulates synchronously running a suite's tests.
*/
@ -50,9 +49,13 @@ class TestRun {
this.rootContextId = testDefinitions.rootTestContextId;
this.testContexts = tests.reduce((memo, test) => {
const testContextId = test.testContextId;
const { testContextId } = test;
this._recursivelyAddContextsTo(memo, testContextId, testDefinitions.testContexts);
this._recursivelyAddContextsTo(
memo,
testContextId,
testDefinitions.testContexts
);
memo[testContextId].tests.unshift(test);
@ -101,7 +104,7 @@ class TestRun {
target[id].childContextIds[childContextId] = true;
}
const parentContextId = testContext.parentContextId;
const { parentContextId } = testContext;
if (parentContextId) {
this._recursivelyAddContextsTo(target, parentContextId, source, id);
@ -123,7 +126,11 @@ class TestRun {
const store = this.testSuite.reduxStore;
if (!store) {
testRuntimeError(`Failed to run ${this.testSuite.name} tests as no Redux store has been provided`);
testRuntimeError(
`Failed to run ${
this.testSuite.name
} tests as no Redux store has been provided`
);
}
this._updateStatus(EVENTS.TEST_SUITE_STATUS, {
@ -152,17 +159,19 @@ class TestRun {
progress: 100,
time: Date.now() - this.runStartTime,
message: `${errors.length} test${errors.length > 1 ? 's' : ''} has error(s).`,
message: `${errors.length} test${
errors.length > 1 ? 's' : ''
} has error(s).`,
});
} else {
this._updateStatus(EVENTS.TEST_SUITE_STATUS, ({
this._updateStatus(EVENTS.TEST_SUITE_STATUS, {
suiteId: this.testSuite.id,
status: RunStatus.OK,
progress: 100,
time: Date.now() - this.runStartTime,
message: '',
}));
});
}
}
}
@ -179,19 +188,35 @@ class TestRun {
* @returns {Promise.<void>} Resolves once all tests and their hooks have run
* @private
*/
async _runTestsInContext(testContext, beforeEachHooks = [], afterEachHooks = []) {
async _runTestsInContext(
testContext,
beforeEachHooks = [],
afterEachHooks = []
) {
const beforeHookRan = await this._runContextHooks(testContext, 'before');
if (beforeHookRan) {
beforeEachHooks.push(testContext.beforeEachHooks || []);
afterEachHooks.unshift(testContext.afterEachHooks || []);
await this._runTests(testContext, testContext.tests, flatten(beforeEachHooks), flatten(afterEachHooks));
await this._runTests(
testContext,
testContext.tests,
flatten(beforeEachHooks),
flatten(afterEachHooks)
);
await Promise.each(Object.keys(testContext.childContextIds), (childContextId) => {
const childContext = this.testContexts[childContextId];
return this._runTestsInContext(childContext, beforeEachHooks, afterEachHooks);
});
await Promise.each(
Object.keys(testContext.childContextIds),
childContextId => {
const childContext = this.testContexts[childContextId];
return this._runTestsInContext(
childContext,
beforeEachHooks,
afterEachHooks
);
}
);
beforeEachHooks.pop();
afterEachHooks.shift();
@ -215,21 +240,39 @@ class TestRun {
}
_runHookChain(test, testStart, testContext, hookName, hooks) {
return Promise.each(hooks, async (hook) => {
const error = await this._safelyRunFunction(hook.callback, hook.timeout, `${hookName} hook`);
return Promise.each(hooks, async hook => {
const error = await this._safelyRunFunction(
hook.callback,
hook.timeout,
`${hookName} hook`
);
if (error) {
const errorPrefix = `Error occurred in "${testContext.name}" ${hookName} Hook: `;
const errorPrefix = `Error occurred in "${
testContext.name
}" ${hookName} Hook: `;
if (test) {
this._reportTestError(test, error, Date.now() - testStart, errorPrefix);
this._reportTestError(
test,
error,
Date.now() - testStart,
errorPrefix
);
} else {
this._reportAllTestsAsFailed(testContext, error, testStart, errorPrefix);
this._reportAllTestsAsFailed(
testContext,
error,
testStart,
errorPrefix
);
}
throw new Error();
}
}).then(() => true).catch(() => false);
})
.then(() => true)
.catch(() => false);
}
/**
@ -241,12 +284,17 @@ class TestRun {
* @private
*/
_reportAllTestsAsFailed(testContext, error, testStart, errorPrefix) {
testContext.tests.forEach((test) => {
testContext.tests.forEach(test => {
this._reportTestError(test, error, Date.now() - testStart, errorPrefix);
});
testContext.childContextIds.forEach((contextId) => {
this._reportAllTestsAsFailed(this.testContext[contextId], error, testStart, errorPrefix);
testContext.childContextIds.forEach(contextId => {
this._reportAllTestsAsFailed(
this.testContext[contextId],
error,
testStart,
errorPrefix
);
});
}
@ -262,7 +310,7 @@ class TestRun {
* @private
*/
async _runTests(testContext, tests, beforeEachHooks, afterEachHooks) {
return Promise.each(tests, async (test) => {
return Promise.each(tests, async test => {
this._updateStatus(EVENTS.TEST_STATUS, {
testId: test.id,
status: RunStatus.RUNNING,
@ -272,16 +320,29 @@ class TestRun {
const testStart = Date.now();
const beforeEachRan = await this._runHookChain(test, testStart, testContext, 'beforeEach', beforeEachHooks);
const beforeEachRan = await this._runHookChain(
test,
testStart,
testContext,
'beforeEach',
beforeEachHooks
);
if (beforeEachRan) {
const error = await this._safelyRunFunction(test.func.bind(null, [test, this.testSuite.reduxStore.getState()]), test.timeout, 'Test');
const error = await this._safelyRunFunction(
test.func.bind(null, [test, this.testSuite.reduxStore.getState()]),
test.timeout,
'Test'
);
// Update test status
if (error) {
this._reportTestError(test, error, Date.now() - testStart);
console.groupCollapsed(`%c ❌ Test Failed: ${test.description} (${this.testSuite.name})`, 'color: #f44336;');
console.groupCollapsed(
`%c ❌ Test Failed: ${test.description} (${this.testSuite.name})`,
'color: #f44336;'
);
console.log(`Test Description: ${test.description}`);
console.log(`Test Time Taken: ${Date.now() - testStart}`);
console.log(`Suite Name: ${this.testSuite.name}`);
@ -299,7 +360,10 @@ class TestRun {
message: '',
});
console.groupCollapsed(`%c ✅ Test Passed: ${test.description} (${this.testSuite.name})`, 'color: #4CAF50;');
console.groupCollapsed(
`%c ✅ Test Passed: ${test.description} (${this.testSuite.name})`,
'color: #4CAF50;'
);
console.log(`Test Description: ${test.description}`);
console.log(`Test Time Taken: ${Date.now() - testStart}`);
console.log(`Suite Name: ${this.testSuite.name}`);
@ -314,14 +378,20 @@ class TestRun {
this._updateStatus(EVENTS.TEST_SUITE_STATUS, {
suiteId: this.testSuite.id,
status: RunStatus.RUNNING,
progress: (this.completedTests / this.tests.length) * 100,
progress: this.completedTests / this.tests.length * 100,
time: Date.now() - this.runStartTime,
message: '',
});
await this._runHookChain(test, testStart, testContext, 'afterEach', afterEachHooks);
await this._runHookChain(
test,
testStart,
testContext,
'afterEach',
afterEachHooks
);
}
}).catch((error) => {
}).catch(error => {
this._updateStatus(EVENTS.TEST_SUITE_STATUS, {
suiteId: this.testSuite.id,
status: RunStatus.ERR,
@ -346,7 +416,9 @@ class TestRun {
testId: test.id,
status: RunStatus.ERR,
time,
message: `${errorPrefix}${error.message ? `${error.name}: ${error.message}` : error}`,
message: `${errorPrefix}${
error.message ? `${error.name}: ${error.message}` : error
}`,
stackTrace: cleanStack(error.stack),
});
}
@ -368,9 +440,12 @@ class TestRun {
}
// Asynchronous Error
return capturePromiseErrors(syncResultOrPromise.result, timeOutDuration, description);
return capturePromiseErrors(
syncResultOrPromise.result,
timeOutDuration,
description
);
}
}
/**
@ -410,15 +485,10 @@ function capturePromiseErrors(target, timeoutDuration, description) {
try {
returnValue = Promise.resolve(target)
.then(() => {
return null;
}, (error) => {
return Promise.resolve(error);
})
.catch((error) => {
return Promise.resolve(error);
})
.timeout(timeoutDuration,
.then(() => null, error => Promise.resolve(error))
.catch(error => Promise.resolve(error))
.timeout(
timeoutDuration,
`${description} took longer than ${timeoutDuration}ms. This can be extended with the timeout option.`
);
} catch (error) {
@ -434,9 +504,7 @@ function capturePromiseErrors(target, timeoutDuration, description) {
* @returns {*[]} One-dimensional array
*/
function flatten(list) {
return list.reduce((memo, contextHooks) => {
return memo.concat(contextHooks);
}, []);
return list.reduce((memo, contextHooks) => memo.concat(contextHooks), []);
}
/**

View File

@ -110,31 +110,41 @@ class TestSuite {
* testSuite.run([1, 2]);
*/
async run(testIds = undefined) {
const testsToRun = (() => {
return (testIds || Object.keys(this.testDefinitions.tests)).reduce((memo, id) => {
const test = this.testDefinitions.tests[id];
const testsToRun = (() =>
(testIds || Object.keys(this.testDefinitions.tests)).reduce(
(memo, id) => {
const test = this.testDefinitions.tests[id];
if (!test) {
throw new RangeError(`ReactNativeFirebaseTests.TestRunError: Test with id ${id} not found in test suite ${this.name}`);
}
if (!test) {
throw new RangeError(
`ReactNativeFirebaseTests.TestRunError: Test with id ${id} not found in test suite ${
this.name
}`
);
}
if (!this.testDefinitions.pendingTestIds[id]) {
memo.push(test);
}
if (!this.testDefinitions.pendingTestIds[id]) {
memo.push(test);
}
return memo;
}, []);
})();
return memo;
},
[]
))();
const testRun = new TestRun(this, testsToRun.reverse(), this.testDefinitions);
const testRun = new TestRun(
this,
testsToRun.reverse(),
this.testDefinitions
);
testRun.onChange('TEST_SUITE_STATUS', (values) => {
testRun.onChange('TEST_SUITE_STATUS', values => {
if (this.suiteChangHandler) {
this.suiteChangHandler(values);
}
});
testRun.onChange('TEST_STATUS', (values) => {
testRun.onChange('TEST_STATUS', values => {
if (this.testChangHandler) {
this.testChangHandler(values);
}
@ -142,7 +152,6 @@ class TestSuite {
await testRun.execute();
}
}
export default TestSuite;

View File

@ -41,7 +41,7 @@ function assignContextId() {
* @enum {String} ContextOperator
*/
const CONTEXT_OPERATORS = {
/** Perform OR of test value with context chain values **/
/** Perform OR of test value with context chain values * */
OR: 'OR',
};
@ -132,13 +132,18 @@ class TestSuiteDefinition {
const hookAttribute = `${hookName}Hooks`;
if (callback && typeof callback === 'function') {
this.currentTestContext[hookAttribute] = this.currentTestContext[hookAttribute] || [];
this.currentTestContext[hookAttribute] =
this.currentTestContext[hookAttribute] || [];
this.currentTestContext[hookAttribute].push({
callback,
timeout: options.timeout || 15000,
});
} else {
testDefinitionError(`non-function value ${callback} passed to ${hookName} for '${this.currentTestContext.name}'`);
testDefinitionError(
`non-function value ${callback} passed to ${hookName} for '${
this.currentTestContext.name
}'`
);
}
}
@ -162,7 +167,10 @@ class TestSuiteDefinition {
pushTestContext(name, options = {}) {
const testContextId = assignContextId();
const parentContext = this.currentTestContext;
this.currentTestContext = this._initialiseContext(testContextId, Object.assign({ name, parentContextId: parentContext.id }, options));
this.currentTestContext = this._initialiseContext(
testContextId,
Object.assign({ name, parentContextId: parentContext.id }, options)
);
}
/**
@ -170,7 +178,7 @@ class TestSuiteDefinition {
* current context.
*/
popTestContext() {
const parentContextId = this.currentTestContext.parentContextId;
const { parentContextId } = this.currentTestContext;
this.currentTestContext = this.testContexts[parentContextId];
}
@ -199,7 +207,9 @@ class TestSuiteDefinition {
this._createTest(testId, {
testContextId: this.currentTestContext.id,
testSuiteId: this.testSuite.id,
description: this._testDescriptionContextPrefix(this.currentTestContext) + description,
description:
this._testDescriptionContextPrefix(this.currentTestContext) +
description,
func: _testFunction,
timeout: _options.timeout || 5000,
});
@ -237,11 +247,17 @@ class TestSuiteDefinition {
* @private
*/
_testDescriptionContextPrefix({ id, name, parentContextId }, suffix = '') {
if (id === this.rootTestContextId || parentContextId === this.rootTestContextId) {
if (
id === this.rootTestContextId ||
parentContextId === this.rootTestContextId
) {
return suffix;
}
return this._testDescriptionContextPrefix(this.testContexts[parentContextId], `${name} ${suffix}`);
return this._testDescriptionContextPrefix(
this.testContexts[parentContextId],
`${name} ${suffix}`
);
}
/**
@ -278,8 +294,18 @@ class TestSuiteDefinition {
const newTestContext = {
id: testContextId,
name,
focus: this._incorporateParentValue(parentContext, 'focus', focus, CONTEXT_OPERATORS.OR),
pending: this._incorporateParentValue(parentContext, 'pending', pending, CONTEXT_OPERATORS.OR),
focus: this._incorporateParentValue(
parentContext,
'focus',
focus,
CONTEXT_OPERATORS.OR
),
pending: this._incorporateParentValue(
parentContext,
'pending',
pending,
CONTEXT_OPERATORS.OR
),
parentContextId,
testIds: [],
testSuiteId: this.testSuite.id,
@ -327,7 +353,10 @@ class TestSuiteDefinition {
* @returns {Test} New test matching provided options
* @private
*/
_createTest(testId, { testContextId, description, func, testSuiteId, timeout }) {
_createTest(
testId,
{ testContextId, description, func, testSuiteId, timeout }
) {
const newTest = {
id: testId,
testContextId,
@ -344,7 +373,6 @@ class TestSuiteDefinition {
return newTest;
}
}
/**

View File

@ -8,10 +8,11 @@ export function setNetworkState(isConnected: boolean): Object {
};
}
export function setAppState(appState: 'active' | 'background' | 'inactive'): Object {
export function setAppState(
appState: 'active' | 'background' | 'inactive'
): Object {
return {
type: APP_SET_APP_STATE,
appState,
};
}

View File

@ -14,7 +14,13 @@ export function setSuiteStatus({ suiteId, status, time, message, progress }) {
};
}
export function setTestStatus({ testId, status, stackTrace, time = 0, message = null }) {
export function setTestStatus({
testId,
status,
stackTrace,
time = 0,
message = null,
}) {
return {
type: TEST_SET_STATUS,
testId,

View File

@ -5,10 +5,7 @@ import { StyleSheet, View, Text } from 'react-native';
function Banner({ type, children, style, textStyle }) {
return (
<View style={[styles.banner, styles[type || 'default'], style]}>
<Text
numberOfLines={1}
style={[styles.bannerText, textStyle]}
>
<Text numberOfLines={1} style={[styles.bannerText, textStyle]}>
{children}
</Text>
</View>
@ -16,18 +13,14 @@ function Banner({ type, children, style, textStyle }) {
}
Banner.propTypes = {
type: PropTypes.oneOf([
'success',
'warning',
'error',
'info',
]),
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
]).isRequired,
/* eslint-disable react/require-default-props */
/* eslint-disable react/no-typos */
type: PropTypes.oneOf(['success', 'warning', 'error', 'info']),
children: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired,
style: View.propTypes.style,
textStyle: Text.propTypes.style,
/* eslint-enable react/require-default-props */
/* eslint-enable react/no-typos */
};
const styles = StyleSheet.create({

View File

@ -4,6 +4,7 @@ import VectorIcon from 'react-native-vector-icons/MaterialIcons';
type Props = {
name: string,
/* eslint-disable react/require-default-props */
size?: number,
color?: string,
allowFontScaling?: boolean,
@ -11,11 +12,11 @@ type Props = {
rotate?: number,
onPress?: () => void,
underlayColor?: string,
/* eslint-enable react/require-default-props */
};
// TODO Spin?
class Icon extends React.Component {
constructor() {
super();
this.measured = false;
@ -36,15 +37,24 @@ class Icon extends React.Component {
props: Props;
render() {
const { name, size = 24, color = '#757575', allowFontScaling = true, style, rotate, onPress, underlayColor } = this.props;
const {
name,
size = 24,
color = '#757575',
allowFontScaling = true,
style,
rotate,
onPress,
underlayColor,
} = this.props;
const icon = (
<View
onLayout={(e) => this.setDimensions(e)}
onLayout={e => this.setDimensions(e)}
style={[
style,
rotate ? { transform: [{ rotate: `${rotate}deg` }] } : null,
]}
style,
rotate ? { transform: [{ rotate: `${rotate}deg` }] } : null,
]}
>
<VectorIcon
name={name.toLowerCase().replace(/\s+/g, '-')}
@ -69,7 +79,6 @@ class Icon extends React.Component {
</TouchableHighlight>
);
}
}
export default Icon;

View File

@ -20,7 +20,10 @@ class OverviewControlButton extends Component {
testSuitesAreRunning() {
const { testSuites } = this.props;
return some(Object.values(testSuites), ({ status }) => status === RunStatus.RUNNING);
return some(
Object.values(testSuites),
({ status }) => status === RunStatus.RUNNING
);
}
handleOnPress() {
@ -31,18 +34,12 @@ class OverviewControlButton extends Component {
render() {
if (this.testSuitesAreRunning()) {
return (
<Icon
color={'#ffffff'}
size={28}
name="autorenew"
/>
);
return <Icon color="#ffffff" size={28} name="autorenew" />;
}
return (
<Icon
color={'#ffffff'}
color="#ffffff"
size={28}
name="play circle filled"
onPress={this.handleOnPress}
@ -58,8 +55,12 @@ OverviewControlButton.propTypes = {
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
};
function mapStateToProps({ tests, testSuites, focusedTestIds, pendingTestIds }) {
function mapStateToProps({
tests,
testSuites,
focusedTestIds,
pendingTestIds,
}) {
return {
tests,
testSuites,

View File

@ -1,44 +1,32 @@
import { View, Text } from 'react-native';
import React, { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import RunStatus from '../../lib/RunStatus';
import Icon from './Icon';
class StatusIndicator extends Component {
render() {
const { status, progress } = this.props;
switch (status) {
case RunStatus.RUNNING:
if (progress > 0) {
return (
<View style={{ width: 30, flex: 1, justifyContent: 'flex-end' }}>
<Text style={{ fontSize: 11, marginBottom: 20 }}>
{progress.toFixed(0)}%
</Text>
</View>
);
}
const StatusIndicator = ({ status, progress }) => {
switch (status) {
case RunStatus.RUNNING:
if (progress > 0) {
return (
<Icon color={'rgba(0, 0, 0, 0.2)'} name="autorenew" />
<View style={{ width: 30, flex: 1, justifyContent: 'flex-end' }}>
<Text style={{ fontSize: 11, marginBottom: 20 }}>
{progress.toFixed(0)}%
</Text>
</View>
);
case RunStatus.OK:
return (
<Icon name={'done'} />
);
case RunStatus.ERR:
return (
<Icon color={'#f44336'} name="clear" />
);
default:
return null;
}
}
return <Icon color="rgba(0, 0, 0, 0.2)" name="autorenew" />;
case RunStatus.OK:
return <Icon name="done" />;
case RunStatus.ERR:
return <Icon color="#f44336" name="clear" />;
default:
return null;
}
}
};
StatusIndicator.propTypes = {
status: PropTypes.oneOf(Object.values(RunStatus)),
@ -47,7 +35,7 @@ StatusIndicator.propTypes = {
StatusIndicator.defaultProps = {
status: null,
progress: 0
progress: 0,
};
module.exports = StatusIndicator;

View File

@ -21,7 +21,7 @@ class TestControlButton extends Component {
}
handleOnPress() {
const { test: { id, description } } = this.props;
const { test: { id } } = this.props;
runTest(id);
// Toast.show(`Running ${description}.`);
@ -33,7 +33,7 @@ class TestControlButton extends Component {
if (status !== RunStatus.STARTED && !this.testIsPending()) {
return (
<Icon
color={'#ffffff'}
color="#ffffff"
size={28}
name="play circle filled"
onPress={this.handleOnPress}
@ -43,7 +43,6 @@ class TestControlButton extends Component {
return null;
}
}
TestControlButton.propTypes = {
@ -56,9 +55,7 @@ TestControlButton.propTypes = {
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
};
TestControlButton.defaultProps = {
};
TestControlButton.defaultProps = {};
function mapStateToProps({ tests, pendingTestIds }, { testId }) {
const test = tests[testId];

View File

@ -13,12 +13,19 @@ class TestSuiteControlButton extends Component {
constructor(props, context) {
super(props, context);
this.toggleOnlyShowFailingTests = this.toggleOnlyShowFailingTests.bind(this);
this.toggleOnlyShowFailingTests = this.toggleOnlyShowFailingTests.bind(
this
);
this.startTestSuite = this.startTestSuite.bind(this);
}
startTestSuite() {
const { testSuite: { name, testIds }, tests, focusedTestIds, pendingTestIds } = this.props;
const {
testSuite: { testIds },
tests,
focusedTestIds,
pendingTestIds,
} = this.props;
const testSuiteTests = testIds.reduce((memo, testId) => {
// eslint-disable-next-line no-param-reassign
@ -51,7 +58,7 @@ class TestSuiteControlButton extends Component {
} else if (status !== RunStatus.RUNNING) {
return (
<Icon
color={'#ffffff'}
color="#ffffff"
size={28}
name="play circle filled"
onPress={this.startTestSuite}
@ -61,7 +68,6 @@ class TestSuiteControlButton extends Component {
return null;
}
}
TestSuiteControlButton.propTypes = {
@ -82,8 +88,10 @@ TestSuiteControlButton.defaultProps = {
onlyShowFailingTests: false,
};
function mapStateToProps({ tests, testSuites, focusedTestIds, pendingTestIds }, { testSuiteId }) {
function mapStateToProps(
{ tests, testSuites, focusedTestIds, pendingTestIds },
{ testSuiteId }
) {
const testSuite = testSuites[testSuiteId];
return {

View File

@ -1,6 +1,6 @@
// @flow
import React from 'react';
import { View, Text, AppState, NetInfo, StatusBar, Platform } from 'react-native';
import { AppState, NetInfo, StatusBar, Platform } from 'react-native';
import { connect } from 'react-redux';
import Navigator from '../navigator';
@ -11,7 +11,6 @@ type Props = {
};
class CoreContainer extends React.Component {
constructor() {
super();
this._isConnected = false;
@ -28,10 +27,13 @@ class CoreContainer extends React.Component {
StatusBar.setBarStyle('light-content');
}
AppState.addEventListener('change', this.handleAppStateChange);
NetInfo.isConnected.fetch().then((isConnected) => {
NetInfo.isConnected.fetch().then(isConnected => {
this.handleAppStateChange('active'); // Force connect (react debugger issue)
this.props.dispatch(setNetworkState(isConnected));
NetInfo.isConnected.addEventListener('connectionChange', this.handleNetworkChange);
NetInfo.isConnected.addEventListener(
'connectionChange',
this.handleNetworkChange
);
});
}
@ -40,7 +42,10 @@ class CoreContainer extends React.Component {
*/
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppStateChange);
NetInfo.isConnected.removeEventListener('connectionChange', this.handleNetworkChange);
NetInfo.isConnected.removeEventListener(
'connectionChange',
this.handleNetworkChange
);
}
props: Props;
@ -51,7 +56,7 @@ class CoreContainer extends React.Component {
* https://facebook.github.io/react-native/docs/appstate.html
* @param state
*/
handleAppStateChange = (state) => {
handleAppStateChange = state => {
this.props.dispatch(setAppState(state));
if (state === 'active' && this._isConnected) {
// firestack.database().goOnline();
@ -65,7 +70,7 @@ class CoreContainer extends React.Component {
* https://facebook.github.io/react-native/docs/netinfo.html
* @param isConnected
*/
handleNetworkChange = (isConnected) => {
handleNetworkChange = isConnected => {
this._isConnected = isConnected;
this.props.dispatch(setNetworkState(isConnected));
if (isConnected) {

View File

@ -17,7 +17,8 @@ const config = {
const android = {
// firebase android sdk completely ignores client id
clientId: '305229645282-j8ij0jev9ut24odmlk9i215pas808ugn.apps.googleusercontent.com',
clientId:
'305229645282-j8ij0jev9ut24odmlk9i215pas808ugn.apps.googleusercontent.com',
appId: '1:305229645282:android:efe37851d57e1d05',
apiKey: 'AIzaSyDnVqNhxU0Biit9nCo4RorAh5ulQQwko3E',
databaseURL: 'https://rnfirebase-b9ad4.firebaseio.com',
@ -26,9 +27,9 @@ const android = {
projectId: 'rnfirebase-b9ad4',
};
const ios = {
clientId: '305229645282-22imndi01abc2p6esgtu1i1m9mqrd0ib.apps.googleusercontent.com',
clientId:
'305229645282-22imndi01abc2p6esgtu1i1m9mqrd0ib.apps.googleusercontent.com',
androidClientId: android.clientId,
appId: '1:305229645282:ios:7b45748cb1117d2d',
apiKey: 'AIzaSyDnVqNhxU0Biit9nCo4RorAh5ulQQwko3E',
@ -41,34 +42,52 @@ const ios = {
const instances = {
web: firebase.initializeApp(config),
native: RNfirebase,
another: RNfirebase.initializeApp(Platform.OS === 'ios' ? ios : android, 'anotherApp'),
another: RNfirebase.initializeApp(
Platform.OS === 'ios' ? ios : android,
'anotherApp'
),
};
console.log('RNApps -->', RNfirebase.apps);
// natively initialized apps are already available at app run time,
// no need for ready checks
instances.native.auth().signInAnonymously().then((user) => {
console.log('defaultApp user ->', user.toJSON());
});
instances.native
.auth()
.signInAnonymously()
.then(user => {
console.log('defaultApp user ->', user.toJSON());
});
// dynamically initialized apps need a ready check
instances.another.onReady().then((app) => {
app.auth().signInAnonymously().then((user) => {
console.log('anotherApp user ->', user.toJSON());
});
instances.another.onReady().then(app => {
app
.auth()
.signInAnonymously()
.then(user => {
console.log('anotherApp user ->', user.toJSON());
});
});
instances.web.database().ref('tests/types').set(DatabaseContents.DEFAULT);
instances.web
.database()
.ref('tests/types')
.set(DatabaseContents.DEFAULT);
instances.web.database().ref('tests/priority').setWithPriority({
foo: 'bar',
}, 666);
instances.web.database().ref('tests/query').set(DatabaseContents.QUERY);
instances.web
.database()
.ref('tests/priority')
.setWithPriority(
{
foo: 'bar',
},
666
);
instances.web
.database()
.ref('tests/query')
.set(DatabaseContents.QUERY);
// instances.native.messaging().subscribeToTopic('fcm_test');

View File

@ -1,3 +0,0 @@
// import fs from 'fs';
import path from 'path';

View File

@ -24,8 +24,7 @@ const unitId = {
const firebase = fb.native;
// Components
const Banner = firebase.admob.Banner;
const NativeExpress = firebase.admob.NativeExpress;
const { Banner, NativeExpress } = firebase.admob;
// API
const interstitial = firebase.admob().interstitial(unitId.interstitial);
@ -34,24 +33,19 @@ interstitial.loadAd();
const rewarded = firebase.admob().rewarded(unitId.rewarded);
rewarded.loadAd();
function bootstrap() {
// Remove logging on production
if (!__DEV__) {
console.log = () => {
};
console.warn = () => {
};
console.error = () => {
};
console.log = () => {};
console.warn = () => {};
console.error = () => {};
console.disableYellowBox = true;
}
class Root extends Component {
showInterstitial() {
interstitial.show();
};
}
showRewarded() {
rewarded.show();
@ -60,22 +54,13 @@ function bootstrap() {
render() {
return (
<View>
<Banner
unitId={unitId.banner}
size={'SMART_BANNER'}
/>
<Banner unitId={unitId.banner} size="SMART_BANNER" />
<NativeExpress
unitId={'ca-app-pub-3940256099942544/2793859312'}
size={'300x200'}
/>
<Button
title={'Show Interstitial'}
onPress={this.showInterstitial}
/>
<Button
title={'Show Rewarded Video'}
onPress={this.showRewarded}
unitId="ca-app-pub-3940256099942544/2793859312"
size="300x200"
/>
<Button title="Show Interstitial" onPress={this.showInterstitial} />
<Button title="Show Rewarded Video" onPress={this.showRewarded} />
</View>
);
}

View File

@ -6,7 +6,9 @@ import { setupSuites } from './tests/index';
global.Promise = require('bluebird');
console.ignoredYellowBox = ['Setting a timer for a long period of time, i.e. multiple minutes'];
console.ignoredYellowBox = [
'Setting a timer for a long period of time, i.e. multiple minutes',
];
type State = {
loading: boolean,
@ -16,12 +18,9 @@ type State = {
function bootstrap() {
// Remove logging on production
if (!__DEV__) {
console.log = () => {
};
console.warn = () => {
};
console.error = () => {
};
console.log = () => {};
console.warn = () => {};
console.error = () => {};
console.disableYellowBox = true;
}
@ -37,7 +36,7 @@ function bootstrap() {
state: State;
componentDidMount() {
setupStore((store) => {
setupStore(store => {
setupSuites(store);
this.setState({
store,

View File

@ -1,15 +1,23 @@
import React, { Component } from 'react';
import { View, Button, Text, TextInput, Image, ActivityIndicator, Platform } from 'react-native';
import {
View,
Button,
Text,
TextInput,
Image,
ActivityIndicator,
Platform,
} from 'react-native';
import fb from './firebase';
const firebase = fb.native;
const imageUrl = 'https://www.shareicon.net/data/512x512/2016/07/19/798524_sms_512x512.png';
const imageUrl =
'https://www.shareicon.net/data/512x512/2016/07/19/798524_sms_512x512.png';
export default class PhoneAuth extends Component {
static getDefaultState() {
return {
message: '',
error: '',
codeInput: '',
phoneNumber: '+44',
@ -39,72 +47,90 @@ export default class PhoneAuth extends Component {
*/
afterVerify = () => {
const { codeInput, verificationId } = this.state;
const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, codeInput);
const credential = firebase.auth.PhoneAuthProvider.credential(
verificationId,
codeInput
);
// TODO do something with credential for example:
firebase.auth()
firebase
.auth()
.signInWithCredential(credential)
.then((user) => {
.then(user => {
console.log('PHONE AUTH USER ->>>>>', user.toJSON());
this.setState({ user: user.toJSON() });
}).catch(console.error);
})
.catch(console.error);
};
signIn = () => {
const { phoneNumber } = this.state;
this.setState({ message: 'Sending code ...', error: '', started: true, autoVerifyCountDown: this.timeout }, () => {
firebase.auth()
.verifyPhoneNumber(phoneNumber)
.on('state_changed', (phoneAuthSnapshot) => {
console.log(phoneAuthSnapshot);
switch (phoneAuthSnapshot.state) {
case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent'
// update state with code sent and if android start a interval timer
// for auto verify - to provide visual feedback
this.setState({
sent: true,
message: 'Code Sent!',
verificationId: phoneAuthSnapshot.verificationId,
autoVerifyCountDown: this.timeout,
}, () => {
if (this.state.auto) {
this._autoVerifyInterval = setInterval(this._tick.bind(this), 1000);
}
});
break;
case firebase.auth.PhoneAuthState.ERROR: // or 'error'
// restart the phone flow again on error
clearInterval(this._autoVerifyInterval);
this.setState({
...PhoneAuth.getDefaultState(),
error: phoneAuthSnapshot.error.message,
});
break;
this.setState(
{
error: '',
started: true,
autoVerifyCountDown: this.timeout,
},
() => {
firebase
.auth()
.verifyPhoneNumber(phoneNumber)
.on('state_changed', phoneAuthSnapshot => {
console.log(phoneAuthSnapshot);
switch (phoneAuthSnapshot.state) {
case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent'
// update state with code sent and if android start a interval timer
// for auto verify - to provide visual feedback
this.setState(
{
sent: true,
verificationId: phoneAuthSnapshot.verificationId,
autoVerifyCountDown: this.timeout,
},
() => {
if (this.state.auto) {
this._autoVerifyInterval = setInterval(
this._tick.bind(this),
1000
);
}
}
);
break;
case firebase.auth.PhoneAuthState.ERROR: // or 'error'
// restart the phone flow again on error
clearInterval(this._autoVerifyInterval);
this.setState({
...PhoneAuth.getDefaultState(),
error: phoneAuthSnapshot.error.message,
});
break;
// ---------------------
// ANDROID ONLY EVENTS
// ---------------------
case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout'
clearInterval(this._autoVerifyInterval);
this.setState({
sent: true,
auto: false,
verificationId: phoneAuthSnapshot.verificationId,
});
break;
case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified'
clearInterval(this._autoVerifyInterval);
this.setState({
sent: true,
codeInput: phoneAuthSnapshot.code,
verificationId: phoneAuthSnapshot.verificationId,
});
break;
default:
// will never get here - just for linting
}
});
});
// ---------------------
// ANDROID ONLY EVENTS
// ---------------------
case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout'
clearInterval(this._autoVerifyInterval);
this.setState({
sent: true,
auto: false,
verificationId: phoneAuthSnapshot.verificationId,
});
break;
case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified'
clearInterval(this._autoVerifyInterval);
this.setState({
sent: true,
codeInput: phoneAuthSnapshot.code,
verificationId: phoneAuthSnapshot.verificationId,
});
break;
default:
// will never get here - just for linting
}
});
}
);
};
renderInputPhoneNumber() {
@ -116,10 +142,14 @@ export default class PhoneAuth extends Component {
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ phoneNumber: value })}
placeholder={'Phone number ... '}
placeholder="Phone number ... "
value={phoneNumber}
/>
<Button title="Begin Verification" color="green" onPress={this.signIn} />
<Button
title="Begin Verification"
color="green"
onPress={this.signIn}
/>
</View>
);
}
@ -129,35 +159,37 @@ export default class PhoneAuth extends Component {
return (
<View style={{ paddingBottom: 15 }}>
<Text
style={{ paddingBottom: 25 }}
>
<Text style={{ paddingBottom: 25 }}>
{`Sending verification code to '${phoneNumber}'.`}
</Text>
<ActivityIndicator animating style={{ padding: 50 }} size={'large'} />
<ActivityIndicator animating style={{ padding: 50 }} size="large" />
</View>
);
}
renderAutoVerifyProgress() {
const { autoVerifyCountDown, started, error, sent, phoneNumber } = this.state;
const {
autoVerifyCountDown,
started,
error,
sent,
phoneNumber,
} = this.state;
if (!sent && started && !error.length) {
return this.renderSendingCode();
}
return (
<View style={{ padding: 0 }}>
<Text
style={{ paddingBottom: 25 }}
>
<Text style={{ paddingBottom: 25 }}>
{`Verification code has been successfully sent to '${phoneNumber}'.`}
</Text>
<Text
style={{ marginBottom: 25 }}
>
<Text style={{ marginBottom: 25 }}>
{`We'll now attempt to automatically verify the code for you. This will timeout in ${autoVerifyCountDown} seconds.`}
</Text>
<Button
style={{ paddingTop: 25 }} title="I have a code already" color="green"
style={{ paddingTop: 25 }}
title="I have a code already"
color="green"
onPress={() => this.setState({ auto: false })}
/>
</View>
@ -168,12 +200,15 @@ export default class PhoneAuth extends Component {
const { error } = this.state;
return (
<View style={{ padding: 10, borderRadius: 5, margin: 10, backgroundColor: 'rgb(255,0,0)' }}>
<Text
style={{ color: '#fff' }}
>
{error}
</Text>
<View
style={{
padding: 10,
borderRadius: 5,
margin: 10,
backgroundColor: 'rgb(255,0,0)',
}}
>
<Text style={{ color: '#fff' }}>{error}</Text>
</View>
);
}
@ -181,7 +216,9 @@ export default class PhoneAuth extends Component {
render() {
const { started, error, codeInput, sent, auto, user } = this.state;
return (
<View style={{ flex: 1, backgroundColor: user ? 'rgb(0, 200, 0)' : '#fff' }}>
<View
style={{ flex: 1, backgroundColor: user ? 'rgb(0, 200, 0)' : '#fff' }}
>
<View
style={{
padding: 5,
@ -190,11 +227,23 @@ export default class PhoneAuth extends Component {
flex: 1,
}}
>
<Image source={{ uri: imageUrl }} style={{ width: 128, height: 128, marginTop: 25, marginBottom: 15 }} />
<Text style={{ fontSize: 25, marginBottom: 20 }}>Phone Auth Example</Text>
<Image
source={{ uri: imageUrl }}
style={{
width: 128,
height: 128,
marginTop: 25,
marginBottom: 15,
}}
/>
<Text style={{ fontSize: 25, marginBottom: 20 }}>
Phone Auth Example
</Text>
{error && error.length ? this.renderError() : null}
{!started && !sent ? this.renderInputPhoneNumber() : null}
{started && auto && !codeInput.length ? this.renderAutoVerifyProgress() : null}
{started && auto && !codeInput.length
? this.renderAutoVerifyProgress()
: null}
{!user && started && sent && (codeInput.length || !auto) ? (
<View style={{ marginTop: 15 }}>
<Text>Enter verification code below:</Text>
@ -202,10 +251,14 @@ export default class PhoneAuth extends Component {
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ codeInput: value })}
placeholder={'Code ... '}
placeholder="Code ... "
value={codeInput}
/>
<Button title="Confirm Code" color="#841584" onPress={this.afterVerify} />
<Button
title="Confirm Code"
color="#841584"
onPress={this.afterVerify}
/>
</View>
) : null}
{user ? (
@ -219,7 +272,6 @@ export default class PhoneAuth extends Component {
}
}
/*
{ user ? (
<View
@ -238,7 +290,6 @@ export default class PhoneAuth extends Component {
) : null}
*/
// Example usage if handling here and not in optionalCompleteCb:
// const { verificationId, code } = phoneAuthSnapshot;
// const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, code);

View File

@ -1,4 +1,3 @@
import * as fcmTypes from '../actions/FCMActions';
import * as appTypes from '../actions/AppActions';
type State = {
@ -14,7 +13,6 @@ const initialState = {
};
function device(state: State = initialState, action: Object): State {
if (action.type === appTypes.APP_SET_NETWORK_STATE) {
return {
...state,

View File

@ -1,17 +1,19 @@
import * as testActions from '../actions/TestActions';
import { flatten, unflatten } from 'deeps';
import * as testActions from '../actions/TestActions';
import { initialState } from '../tests/index';
const initState = initialState();
function testsReducers(state = initState.testSuites, action: Object): State {
if (action.type === testActions.TEST_SET_SUITE_STATUS) {
const flattened = flatten(state);
if (action.status) flattened[`${action.suiteId}.status`] = action.status;
if (action.message) flattened[`${action.suiteId}.message`] = action.message;
if (action.progress) flattened[`${action.suiteId}.progress`] = action.progress;
if (action.progress) {
flattened[`${action.suiteId}.progress`] = action.progress;
}
// eslint-disable-next-line no-restricted-globals
if (!isNaN(action.time)) flattened[`${action.suiteId}.time`] = action.time;
return unflatten(flattened);

View File

@ -1,6 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, Text, ListView, TouchableHighlight } from 'react-native';
import {
StyleSheet,
View,
Text,
ListView,
TouchableHighlight,
} from 'react-native';
import { connect } from 'react-redux';
import some from 'lodash.some';
@ -31,10 +37,7 @@ class Overview extends React.Component {
*/
static renderSeparator(sectionID, rowID) {
return (
<View
key={`separator_${sectionID}_${rowID}`}
style={styles.separator}
/>
<View key={`separator_${sectionID}_${rowID}`} style={styles.separator} />
);
}
@ -51,9 +54,10 @@ class Overview extends React.Component {
return Object.keys(testSuites).reduce((memo, testSuiteId) => {
const testSuite = testSuites[testSuiteId];
const testSuiteHasFocusedTests = some(testSuite.testIds, (testId) => {
return focusedTestIds[testId];
});
const testSuiteHasFocusedTests = some(
testSuite.testIds,
testId => focusedTestIds[testId]
);
if (testSuiteHasFocusedTests) {
// eslint-disable-next-line no-param-reassign
@ -85,7 +89,6 @@ class Overview extends React.Component {
};
}
/**
* Copies latest test suite status into state so they may be rendered as a ListView
* @param {Object} nextProps - next props used to render component
@ -94,7 +97,9 @@ class Overview extends React.Component {
*/
componentWillReceiveProps({ testSuites, focusedTestIds }) {
this.setState({
dataBlob: this.dataSource.cloneWithRows(Overview.testSuitesToShow({ testSuites, focusedTestIds })),
dataBlob: this.dataSource.cloneWithRows(
Overview.testSuitesToShow({ testSuites, focusedTestIds })
),
});
}
@ -122,19 +127,18 @@ class Overview extends React.Component {
return (
<TouchableHighlight
key={`row_${rowId}`}
underlayColor={'rgba(0, 0, 0, 0.054)'}
underlayColor="rgba(0, 0, 0, 0.054)"
onPress={() => {
this.goToTestSuite(testSuite);
highlight();
}}
>
<View style={[styles.row, status === RunStatus.ERR ? styles.error : null]}>
<View
style={[styles.row, status === RunStatus.ERR ? styles.error : null]}
>
<View>
<Text style={styles.title}>{name}</Text>
<Text
style={styles.description}
numberOfLines={1}
>
<Text style={styles.description} numberOfLines={1}>
{description}
</Text>
</View>
@ -157,9 +161,7 @@ class Overview extends React.Component {
if (pendingTestsCount > 0) {
return (
<Banner type="warning">
{pendingTestsCount} pending test(s).
</Banner>
<Banner type="warning">{pendingTestsCount} pending test(s).</Banner>
);
}
@ -189,24 +191,21 @@ class Overview extends React.Component {
if (isRunning) {
return (
<Banner type={isErrors ? 'error' : 'warning'}>Running ({(totalTime / 1000).toFixed(0)}s) {totalProgress.toFixed(2)}%</Banner>
<Banner type={isErrors ? 'error' : 'warning'}>
Running ({(totalTime / 1000).toFixed(0)}s) {totalProgress.toFixed(2)}%
</Banner>
);
} else if (totalProgress > 0) {
if (isErrors) {
return (
<Banner type={'error'}>Tests Complete with errors</Banner>
);
return <Banner type="error">Tests Complete with errors</Banner>;
}
return (
<Banner type={'success'}>Tests Complete</Banner>
);
return <Banner type="success">Tests Complete</Banner>;
}
return null;
}
/**
* Renders ListView of test suites that should be visible, taking into consideration
* any focused tests
@ -215,8 +214,8 @@ class Overview extends React.Component {
render() {
return (
<View style={styles.container}>
{ this.renderPendingTestsBanner() }
{ this.renderStatusBanner() }
{this.renderPendingTestsBanner()}
{this.renderStatusBanner()}
<ListView
enableEmptySections
dataSource={this.state.dataBlob}
@ -229,21 +228,27 @@ class Overview extends React.Component {
}
Overview.propTypes = {
testSuites: PropTypes.objectOf(PropTypes.shape({
id: PropTypes.number.isRequired,
description: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
status: PropTypes.oneOf(Object.values(RunStatus)),
})).isRequired,
testSuites: PropTypes.objectOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
description: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
status: PropTypes.oneOf(Object.values(RunStatus)),
})
).isRequired,
tests: PropTypes.objectOf(PropTypes.shape({
testSuiteId: PropTypes.number.isRequired,
})).isRequired,
// eslint-disable-next-line react/no-unused-prop-types
tests: PropTypes.objectOf(
PropTypes.shape({
testSuiteId: PropTypes.number.isRequired,
})
).isRequired,
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired,
}).isRequired,
// eslint-disable-next-line react/no-unused-prop-types
running: PropTypes.bool.isRequired,
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
@ -283,13 +288,21 @@ const styles = StyleSheet.create({
},
});
function mapStateToProps({ testSuites, tests, pendingTestIds, focusedTestIds }) {
function mapStateToProps({
testSuites,
tests,
pendingTestIds,
focusedTestIds,
}) {
return {
testSuites,
tests,
pendingTestIds,
focusedTestIds,
running: Object.values(testSuites).filter(suite => suite.status === RunStatus.RUNNING).length > 0,
running:
Object.values(testSuites).filter(
suite => suite.status === RunStatus.RUNNING
).length > 0,
};
}

View File

@ -1,7 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, Text, ListView, TouchableHighlight } from 'react-native';
import {
StyleSheet,
View,
Text,
ListView,
TouchableHighlight,
} from 'react-native';
import { connect } from 'react-redux';
import RunStatus from '../../lib/RunStatus';
@ -12,23 +18,25 @@ import StatusIndicator from '../components/StatusIndicator';
import TestSuiteControlButton from '../components/TestSuiteControlButton';
class Suite extends React.Component {
static navigationOptions = ({ navigation: { state: { params: { title, testSuiteId, onlyShowFailingTests } }, setParams } }) => {
return {
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ flexDirection: 'row', marginRight: 8 }}>
<TestSuiteControlButton
testSuiteId={testSuiteId}
onlyShowFailingTests={onlyShowFailingTests}
onFilterChange={setParams}
/>
</View>
),
};
};
static navigationOptions = ({
navigation: {
state: { params: { title, testSuiteId, onlyShowFailingTests } },
setParams,
},
}) => ({
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ flexDirection: 'row', marginRight: 8 }}>
<TestSuiteControlButton
testSuiteId={testSuiteId}
onlyShowFailingTests={onlyShowFailingTests}
onFilterChange={setParams}
/>
</View>
),
});
/**
* Render test group header
@ -38,13 +46,8 @@ class Suite extends React.Component {
*/
static renderHeader(data, title) {
return (
<View
key={`header_${title}`}
style={styles.header}
>
<Text style={styles.headerText}>
{title.toUpperCase()}
</Text>
<View key={`header_${title}`} style={styles.header}>
<Text style={styles.headerText}>{title.toUpperCase()}</Text>
</View>
);
}
@ -58,7 +61,9 @@ class Suite extends React.Component {
});
this.state = {
dataBlob: this.dataSource.cloneWithRowsAndSections(buildRowsWithSections(props)),
dataBlob: this.dataSource.cloneWithRowsAndSections(
buildRowsWithSections(props)
),
};
}
@ -78,7 +83,7 @@ class Suite extends React.Component {
return Object.values(testContexts).reduce((sections, context) => {
const { name } = context;
context.testIds.forEach((testId) => {
context.testIds.forEach(testId => {
const test = tests[testId];
if (test && test.status === RunStatus.ERR) {
@ -125,16 +130,16 @@ class Suite extends React.Component {
return (
<TouchableHighlight
key={`row_${rowId}`}
underlayColor={'rgba(0, 0, 0, 0.054)'}
underlayColor="rgba(0, 0, 0, 0.054)"
onPress={() => {
this.goToTest(test);
highlight();
}}
>
<View style={[styles.row, status === RunStatus.ERR ? styles.error : null]}>
<View
style={[{ flex: 9 }, styles.rowContent]}
>
<View
style={[styles.row, status === RunStatus.ERR ? styles.error : null]}
>
<View style={[{ flex: 9 }, styles.rowContent]}>
<Text
numberOfLines={2}
style={pendingTestIds[id] ? styles.disabledRow : {}}
@ -142,7 +147,9 @@ class Suite extends React.Component {
{description}
</Text>
</View>
<View style={[{ flex: 1 }, styles.rowContent, [{ alignItems: 'center' }]]}>
<View
style={[{ flex: 1 }, styles.rowContent, [{ alignItems: 'center' }]]}
>
<StatusIndicator status={status} />
</View>
</View>
@ -158,10 +165,7 @@ class Suite extends React.Component {
*/
renderSeparator(sectionID, rowID) {
return (
<View
key={`separator_${sectionID}_${rowID}`}
style={styles.separator}
/>
<View key={`separator_${sectionID}_${rowID}`} style={styles.separator} />
);
}
@ -170,7 +174,7 @@ class Suite extends React.Component {
let pendingTestsCount = 0;
testIds.forEach((testId) => {
testIds.forEach(testId => {
if (pendingTestIds[testId]) {
pendingTestsCount += 1;
}
@ -178,9 +182,7 @@ class Suite extends React.Component {
if (pendingTestsCount) {
return (
<Banner type="warning">
{pendingTestsCount} pending test(s).
</Banner>
<Banner type="warning">{pendingTestsCount} pending test(s).</Banner>
);
}
@ -192,25 +194,18 @@ class Suite extends React.Component {
switch (status) {
case RunStatus.RUNNING:
return (
<Banner type={'warning'}>
Tests are currently running ({ progress.toFixed(2) }%).
<Banner type="warning">
Tests are currently running ({progress.toFixed(2)}%).
</Banner>
);
case RunStatus.OK:
return (
<Banner type={'success'}>
Tests passed. ({ time }ms)
</Banner>
);
return <Banner type="success">Tests passed. ({time}ms)</Banner>;
case RunStatus.ERR:
return (
<Banner type={'error'}>
<Banner type="error">
{message} ({time}ms)
</Banner>
);
@ -229,9 +224,8 @@ class Suite extends React.Component {
return (
<View style={styles.container}>
{ this.renderPendingTestsBanner() }
{ this.renderStatusBanner() }
{this.renderPendingTestsBanner()}
{this.renderStatusBanner()}
<ListView
dataSource={dataBlob}
@ -242,7 +236,6 @@ class Suite extends React.Component {
</View>
);
}
}
Suite.propTypes = {
@ -262,18 +255,23 @@ Suite.propTypes = {
message: PropTypes.string,
}).isRequired,
testContexts: PropTypes.objectOf(PropTypes.shape({
name: PropTypes.string.isRequired,
testIds: PropTypes.arrayOf(PropTypes.number).isRequired,
})).isRequired,
testContexts: PropTypes.objectOf(
PropTypes.shape({
name: PropTypes.string.isRequired,
testIds: PropTypes.arrayOf(PropTypes.number).isRequired,
})
).isRequired,
tests: PropTypes.objectOf(PropTypes.shape({
id: PropTypes.number,
description: PropTypes.string,
status: PropTypes.oneOf(Object.values(RunStatus)),
})).isRequired,
tests: PropTypes.objectOf(
PropTypes.shape({
id: PropTypes.number,
description: PropTypes.string,
status: PropTypes.oneOf(Object.values(RunStatus)),
})
).isRequired,
pendingTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
// eslint-disable-next-line react/no-unused-prop-types
focusedTestIds: PropTypes.objectOf(PropTypes.bool).isRequired,
};
@ -346,11 +344,16 @@ function buildRowsWithSections({ testContexts, tests, focusedTestIds }) {
}, []);
if (contextTestsToShow.length > 0) {
const effectiveContext = highestNonRootAncestor(testContext, testContexts);
const effectiveContext = highestNonRootAncestor(
testContext,
testContexts
);
// eslint-disable-next-line no-param-reassign
sections[effectiveContext.name] = sections[effectiveContext.name] || [];
// eslint-disable-next-line no-param-reassign
sections[effectiveContext.name] = sections[effectiveContext.name].concat(contextTestsToShow);
sections[effectiveContext.name] = sections[effectiveContext.name].concat(
contextTestsToShow
);
}
return sections;
@ -358,11 +361,12 @@ function buildRowsWithSections({ testContexts, tests, focusedTestIds }) {
}
function highestNonRootAncestor(testContext, testContexts) {
const parentContextId = testContext.parentContextId;
const { parentContextId } = testContext;
if (parentContextId) {
const parentContext = testContexts[parentContextId];
const parentContextIsNotRoot = parentContext && parentContext.parentContextId;
const parentContextIsNotRoot =
parentContext && parentContext.parentContextId;
if (parentContextIsNotRoot) {
return highestNonRootAncestor(parentContext, testContexts);
@ -372,16 +376,28 @@ function highestNonRootAncestor(testContext, testContexts) {
return testContext;
}
function mapStateToProps(state, { navigation: { state: { params: { testSuiteId } } } }) {
const { tests, testContexts, testSuites, pendingTestIds, focusedTestIds } = state;
function mapStateToProps(
state,
{ navigation: { state: { params: { testSuiteId } } } }
) {
const {
tests,
testContexts,
testSuites,
pendingTestIds,
focusedTestIds,
} = state;
const testSuite = testSuites[testSuiteId];
const testSuiteContexts = testSuite.testContextIds.reduce((suiteContexts, contextId) => {
// eslint-disable-next-line no-param-reassign
suiteContexts[contextId] = testContexts[contextId];
const testSuiteContexts = testSuite.testContextIds.reduce(
(suiteContexts, contextId) => {
// eslint-disable-next-line no-param-reassign
suiteContexts[contextId] = testContexts[contextId];
return suiteContexts;
}, {});
return suiteContexts;
},
{}
);
const testSuiteTests = testSuite.testIds.reduce((suiteTests, testId) => {
// eslint-disable-next-line no-param-reassign

View File

@ -10,40 +10,27 @@ import RunStatus from '../../lib/RunStatus';
import TestControlButton from '../components/TestControlButton';
class Test extends React.Component {
static navigationOptions = ({ navigation: { state: { params: { title, testId } } } }) => {
return {
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ marginRight: 8 }}>
<TestControlButton testId={testId} />
</View>
),
};
};
static navigationOptions = ({
navigation: { state: { params: { title, testId } } },
}) => ({
title,
headerTintColor: '#ffffff',
headerStyle: { backgroundColor: '#1976D2' },
headerRight: (
<View style={{ marginRight: 8 }}>
<TestControlButton testId={testId} />
</View>
),
});
static renderBanner({ status, time }) {
switch (status) {
case RunStatus.RUNNING:
return (
<Banner type={'warning'}>
Test is currently running.
</Banner>
);
return <Banner type="warning">Test is currently running.</Banner>;
case RunStatus.OK:
return (
<Banner type={'success'}>
Test passed. ({time}ms)
</Banner>
);
return <Banner type="success">Test passed. ({time}ms)</Banner>;
case RunStatus.ERR:
return (
<Banner type={'error'}>
Test failed. ({time}ms)
</Banner>
);
return <Banner type="error">Test failed. ({time}ms)</Banner>;
default:
return null;
}
@ -56,30 +43,33 @@ class Test extends React.Component {
}
render() {
const { test: { stackTrace, message, description, func, status, time }, testContextName } = this.props;
const {
test: { stackTrace, message, description, func, status, time },
testContextName,
} = this.props;
return (
<View style={styles.container}>
{Test.renderBanner({ status, time })}
<ScrollView >
<ScrollView>
<View style={styles.sectionContainer}>
<Text style={styles.heading}>{testContextName}</Text>
<Text style={styles.description}>{description}</Text>
</View>
{message ? <View style={styles.sectionContainer}>
<Text style={styles.headingWarn}>Test Error Message</Text>
<Text style={styles.message}>{message || 'None.'}</Text>
</View> : null }
{stackTrace ? <View style={styles.sectionContainer}>
<Text style={styles.headingWarn}>Test Error Stack</Text>
<Text style={styles.description}>
{stackTrace || 'None.'}
</Text>
</View> : null }
{message ? (
<View style={styles.sectionContainer}>
<Text style={styles.headingWarn}>Test Error Message</Text>
<Text style={styles.message}>{message || 'None.'}</Text>
</View>
) : null}
{stackTrace ? (
<View style={styles.sectionContainer}>
<Text style={styles.headingWarn}>Test Error Stack</Text>
<Text style={styles.description}>{stackTrace || 'None.'}</Text>
</View>
) : null}
<View style={styles.sectionContainer}>
<Text style={styles.heading}>
Test Code Preview
</Text>
<Text style={styles.heading}>Test Code Preview</Text>
<Text style={styles.description}>
{beautify(removeLastLine(removeFirstLine(func.toString())), {
indent_size: 4,
@ -103,6 +93,7 @@ Test.propTypes = {
description: PropTypes.string,
}).isRequired,
// eslint-disable-next-line react/require-default-props
testContextName: PropTypes.string,
navigation: PropTypes.shape({
@ -159,11 +150,17 @@ const styles = StyleSheet.create({
*/
function select({ tests, testContexts }, { navigation: { state: { params: { testId } } } }) {
function select(
{ tests, testContexts },
{ navigation: { state: { params: { testId } } } }
) {
const test = tests[testId];
let testContext = testContexts[test.testContextId];
while (testContext.parentContextId && testContexts[testContext.parentContextId].parentContextId) {
while (
testContext.parentContextId &&
testContexts[testContext.parentContextId].parentContextId
) {
testContext = testContexts[testContext.parentContextId];
}
return {

View File

@ -1,7 +1,7 @@
import { AsyncStorage } from 'react-native';
import { applyMiddleware, createStore, compose } from 'redux';
import thunk from 'redux-thunk';
import reduxLogger from 'redux-logger';
// import reduxLogger from 'redux-logger';
import { persistStore, autoRehydrate } from 'redux-persist';
import whitelist from './whitelist';
@ -10,11 +10,11 @@ import reducers from '../reducers';
function setup(done) {
const isDev = global.isDebuggingInChrome || __DEV__;
const logger = reduxLogger({
/* const logger = reduxLogger({
predicate: () => false,
collapsed: true,
duration: true,
});
}); */
// AsyncStorage.clear();
@ -25,10 +25,14 @@ function setup(done) {
if (isDev) {
// middlewares.push(applyMiddleware(...[logger]));
middlewares.push(applyMiddleware(require('redux-immutable-state-invariant')()));
middlewares.push(
// eslint-disable-next-line global-require
applyMiddleware(require('redux-immutable-state-invariant')())
);
}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, {}, composeEnhancers(...middlewares));

View File

@ -1,12 +1,13 @@
export default function addTests({ before, fdescribe, describe, it, firebase }) {
export default function addTests({ before, describe, it, firebase }) {
before(() => {
firebase.native.admob().initialize('ca-app-pub-3940256099942544~3347511713');
firebase.native
.admob()
.initialize('ca-app-pub-3940256099942544~3347511713');
});
describe('AdMob', () => {
it('should return static variables', () => {
return new Promise((resolve) => {
it('should return static variables', () =>
new Promise(resolve => {
const statics = firebase.native.admob;
statics.should.have.property('Banner').and.be.a.Function();
@ -14,11 +15,10 @@ export default function addTests({ before, fdescribe, describe, it, firebase })
statics.should.have.property('AdRequest').and.be.a.Function();
statics.should.have.property('VideoOptions').and.be.a.Function();
resolve();
});
});
}));
it('should return instance methods', () => {
return new Promise((resolve) => {
it('should return instance methods', () =>
new Promise(resolve => {
const admob = firebase.native.admob();
admob.should.have.property('initialize').and.be.a.Function();
@ -26,14 +26,12 @@ export default function addTests({ before, fdescribe, describe, it, firebase })
admob.should.have.property('interstitial').and.be.a.Function();
admob.should.have.property('rewarded').and.be.a.Function();
resolve();
});
});
}));
});
describe('AdRequest', () => {
it('should return AdRequest methods', () => {
return new Promise((resolve) => {
it('should return AdRequest methods', () =>
new Promise(resolve => {
const request = new firebase.native.admob.AdRequest();
request.should.have.property('build').and.be.a.Function();
@ -44,25 +42,27 @@ export default function addTests({ before, fdescribe, describe, it, firebase })
request.should.have.property('setGender').and.be.a.Function();
request.should.have.property('setLocation').and.be.a.Function();
request.should.have.property('setRequestAgent').and.be.a.Function();
request.should.have.property('setIsDesignedForFamilies').and.be.a.Function();
request.should.have.property('tagForChildDirectedTreatment').and.be.a.Function();
request.should.have
.property('setIsDesignedForFamilies')
.and.be.a.Function();
request.should.have
.property('tagForChildDirectedTreatment')
.and.be.a.Function();
resolve();
});
});
}));
it('should build an empty request', () => {
return new Promise((resolve) => {
it('should build an empty request', () =>
new Promise(resolve => {
const request = new firebase.native.admob.AdRequest();
const build = request.build();
build.should.have.property('keywords').and.be.a.Array();
build.should.have.property('testDevices').and.be.a.Array();
resolve();
});
});
}));
it('should build a full request', () => {
return new Promise((resolve) => {
it('should build a full request', () =>
new Promise(resolve => {
const request = new firebase.native.admob.AdRequest();
request
@ -85,39 +85,42 @@ export default function addTests({ before, fdescribe, describe, it, firebase })
build.keywords.should.containEql('foo');
build.keywords.should.containEql('bar');
build.testDevices.should.containEql('foobar');
build.should.have.property('contentUrl').and.be.a.equal('http://google.com');
build.should.have
.property('contentUrl')
.and.be.a.equal('http://google.com');
build.should.have.property('gender').and.be.a.equal('female');
build.should.have.property('requestAgent').and.be.a.equal('foobar');
build.should.have.property('isDesignedForFamilies').and.be.a.equal(true);
build.should.have.property('tagForChildDirectedTreatment').and.be.a.equal(true);
build.should.have
.property('isDesignedForFamilies')
.and.be.a.equal(true);
build.should.have
.property('tagForChildDirectedTreatment')
.and.be.a.equal(true);
resolve();
});
});
}));
});
describe('VideoOptions', () => {
it('should return VideoOptions methods', () => {
return new Promise((resolve) => {
it('should return VideoOptions methods', () =>
new Promise(resolve => {
const options = new firebase.native.admob.VideoOptions();
options.should.have.property('build').and.be.a.Function();
options.should.have.property('setStartMuted').and.be.a.Function();
resolve();
});
});
}));
it('should build an empty request', () => {
return new Promise((resolve) => {
it('should build an empty request', () =>
new Promise(resolve => {
const options = new firebase.native.admob.VideoOptions();
const build = options.build();
build.should.have.property('startMuted').and.be.a.equal(true);
resolve();
});
});
}));
it('should build all options', () => {
return new Promise((resolve) => {
it('should build all options', () =>
new Promise(resolve => {
const options = new firebase.native.admob.VideoOptions();
options.setStartMuted(false);
@ -126,36 +129,35 @@ export default function addTests({ before, fdescribe, describe, it, firebase })
build.should.have.property('startMuted').and.be.a.equal(false);
resolve();
});
});
}));
});
describe('Interstitial', () => {
it('should return a new instance with methods', () => {
return new Promise((resolve) => {
const advert = firebase.native.admob().interstitial('ca-app-pub-3940256099942544/1033173712');
it('should return a new instance with methods', () =>
new Promise(resolve => {
const advert = firebase.native
.admob()
.interstitial('ca-app-pub-3940256099942544/1033173712');
advert.should.have.property('loadAd').and.be.a.Function();
advert.should.have.property('isLoaded').and.be.a.Function();
advert.should.have.property('show').and.be.a.Function();
advert.should.have.property('on').and.be.a.Function();
resolve();
});
});
}));
});
describe('Rewarded', () => {
it('should return a new instance with methods', () => {
return new Promise((resolve) => {
const advert = firebase.native.admob().rewarded('ca-app-pub-3940256099942544/1033173712');
it('should return a new instance with methods', () =>
new Promise(resolve => {
const advert = firebase.native
.admob()
.rewarded('ca-app-pub-3940256099942544/1033173712');
advert.should.have.property('loadAd').and.be.a.Function();
advert.should.have.property('isLoaded').and.be.a.Function();
advert.should.have.property('show').and.be.a.Function();
advert.should.have.property('on').and.be.a.Function();
resolve();
});
});
}));
});
}

View File

@ -1,64 +1,59 @@
export default function addTests({ fdescribe, describe, it, firebase }) {
export default function addTests({ describe, it, firebase }) {
describe('Analytics', () => {
it('logEvent: it should log a text event without error', () => {
return new Promise((resolve) => {
it('logEvent: it should log a text event without error', () =>
new Promise(resolve => {
firebase.native.analytics().logEvent('test_event');
resolve();
});
});
}));
it('logEvent: it should log a text event with parameters without error', () => {
return new Promise((resolve) => {
it('logEvent: it should log a text event with parameters without error', () =>
new Promise(resolve => {
firebase.native.analytics().logEvent('test_event', {
boolean: true,
number: 1,
string: 'string',
});
resolve();
});
});
}));
it('setAnalyticsCollectionEnabled: it should run without error', () => {
return new Promise((resolve) => {
it('setAnalyticsCollectionEnabled: it should run without error', () =>
new Promise(resolve => {
firebase.native.analytics().setAnalyticsCollectionEnabled(true);
resolve();
});
});
}));
it('setCurrentScreen: it should run without error', () => {
return new Promise((resolve) => {
firebase.native.analytics().setCurrentScreen('test screen', 'test class override');
it('setCurrentScreen: it should run without error', () =>
new Promise(resolve => {
firebase.native
.analytics()
.setCurrentScreen('test screen', 'test class override');
resolve();
});
});
}));
it('setMinimumSessionDuration: it should run without error', () => {
return new Promise((resolve) => {
it('setMinimumSessionDuration: it should run without error', () =>
new Promise(resolve => {
firebase.native.analytics().setMinimumSessionDuration(10000);
resolve();
});
});
}));
it('setSessionTimeoutDuration: it should run without error', () => {
return new Promise((resolve) => {
it('setSessionTimeoutDuration: it should run without error', () =>
new Promise(resolve => {
firebase.native.analytics().setSessionTimeoutDuration(1800000);
resolve();
});
});
}));
it('setUserId: it should run without error', () => {
return new Promise((resolve) => {
it('setUserId: it should run without error', () =>
new Promise(resolve => {
firebase.native.analytics().setUserId('test-id');
resolve();
});
});
}));
it('setUserProperty: it should run without error', () => {
return new Promise((resolve) => {
firebase.native.analytics().setUserProperty('test-property', 'test-value');
it('setUserProperty: it should run without error', () =>
new Promise(resolve => {
firebase.native
.analytics()
.setUserProperty('test-property', 'test-value');
resolve();
});
});
}));
});
}

File diff suppressed because it is too large Load Diff

View File

@ -15,20 +15,21 @@ function configTests({ before, describe, it, firebase }) {
});
describe('Config', () => {
it('it should fetch and activate config', () => {
return firebase.native.config().fetch(0)
.then(() => {
return firebase.native.config().activateFetched();
})
.then((activated) => {
it('it should fetch and activate config', () =>
firebase.native
.config()
.fetch(0)
.then(() => firebase.native.config().activateFetched())
.then(activated => {
activated.should.be.a.Boolean();
return Promise.resolve();
});
});
}));
it('it should get a single value by key', () => {
return firebase.native.config().getValue('foo')
.then((snapshot) => {
it('it should get a single value by key', () =>
firebase.native
.config()
.getValue('foo')
.then(snapshot => {
snapshot.should.be.a.Object();
snapshot.source.should.be.a.String();
snapshot.val.should.be.a.Function();
@ -36,12 +37,13 @@ function configTests({ before, describe, it, firebase }) {
const value = snapshot.val();
value.should.be.equalOneOf('bar', true);
return Promise.resolve();
});
});
}));
it('it should get multiple values by an array of keys', () => {
return firebase.native.config().getValues(['foo', 'bar', 'foobar', 'numvalue'])
.then((result) => {
it('it should get multiple values by an array of keys', () =>
firebase.native
.config()
.getValues(['foo', 'bar', 'foobar', 'numvalue'])
.then(result => {
result.should.be.a.Object();
result.should.have.keys('foo', 'bar', 'foobar');
const fooValue = result.foo.val();
@ -55,16 +57,16 @@ function configTests({ before, describe, it, firebase }) {
numvalueValue.should.be.equal(0);
return Promise.resolve();
});
});
}));
it('it get all keys as an array', () => {
return firebase.native.config().getKeysByPrefix()
.then((result) => {
it('it get all keys as an array', () =>
firebase.native
.config()
.getKeysByPrefix()
.then(result => {
result.should.be.Array();
return Promise.resolve();
});
});
}));
});
}

View File

@ -5,7 +5,8 @@ import RNFirebase from './../../../firebase';
const androidTestConfig = {
// firebase android sdk completely ignores client id
clientId: '305229645282-j8ij0jev9ut24odmlk9i215pas808ugn.apps.googleusercontent.com',
clientId:
'305229645282-j8ij0jev9ut24odmlk9i215pas808ugn.apps.googleusercontent.com',
appId: '1:305229645282:android:efe37851d57e1d05',
apiKey: 'AIzaSyCzbBYFyX8d6VdSu7T4s10IWYbPc-dguwM',
databaseURL: 'https://rnfirebase-b9ad4.firebaseio.com',
@ -15,7 +16,8 @@ const androidTestConfig = {
};
const iosTestConfig = {
clientId: '305229645282-22imndi01abc2p6esgtu1i1m9mqrd0ib.apps.googleusercontent.com',
clientId:
'305229645282-22imndi01abc2p6esgtu1i1m9mqrd0ib.apps.googleusercontent.com',
androidClientId: androidTestConfig.clientId,
appId: '1:305229645282:ios:7b45748cb1117d2d',
apiKey: 'AIzaSyAcdVLG5dRzA1ck_fa_xd4Z0cY7cga7S5A',
@ -27,7 +29,7 @@ const iosTestConfig = {
function rand(from = 1, to = 9999) {
const r = Math.random();
return Math.floor(r * ((to - from) + from));
return Math.floor(r * (to - from + from));
}
function coreTests({ describe, it }) {
@ -38,18 +40,32 @@ function coreTests({ describe, it }) {
});
it('natively initialized apps should have options available in js', () => {
should.equal(RNFirebase.app().options.apiKey, Platform.OS === 'ios' ? iosTestConfig.apiKey : androidTestConfig.apiKey);
should.equal(RNFirebase.app().options.appId, Platform.OS === 'ios' ? iosTestConfig.appId : androidTestConfig.appId);
should.equal(RNFirebase.app().options.databaseURL, iosTestConfig.databaseURL);
should.equal(RNFirebase.app().options.messagingSenderId, iosTestConfig.messagingSenderId);
should.equal(
RNFirebase.app().options.apiKey,
Platform.OS === 'ios' ? iosTestConfig.apiKey : androidTestConfig.apiKey
);
should.equal(
RNFirebase.app().options.appId,
Platform.OS === 'ios' ? iosTestConfig.appId : androidTestConfig.appId
);
should.equal(
RNFirebase.app().options.databaseURL,
iosTestConfig.databaseURL
);
should.equal(
RNFirebase.app().options.messagingSenderId,
iosTestConfig.messagingSenderId
);
should.equal(RNFirebase.app().options.projectId, iosTestConfig.projectId);
should.equal(RNFirebase.app().options.storageBucket, iosTestConfig.storageBucket);
should.equal(
RNFirebase.app().options.storageBucket,
iosTestConfig.storageBucket
);
return Promise.resolve();
});
it('it should resolve onReady for natively initialized apps', () => {
return RNFirebase.app().onReady();
});
it('it should resolve onReady for natively initialized apps', () =>
RNFirebase.app().onReady());
it('it should provide an array of apps', () => {
should.equal(!!RNFirebase.apps.length, true);
@ -59,12 +75,16 @@ function coreTests({ describe, it }) {
it('it should initialize dynamic apps', () => {
const name = `testscoreapp${rand()}`;
return RNFirebase
.initializeApp(Platform.OS === 'ios' ? iosTestConfig : androidTestConfig, name)
return RNFirebase.initializeApp(
Platform.OS === 'ios' ? iosTestConfig : androidTestConfig,
name
)
.onReady()
.then((newApp) => {
.then(newApp => {
newApp.name.should.equal(name.toUpperCase());
newApp.options.apiKey.should.equal((Platform.OS === 'ios' ? iosTestConfig : androidTestConfig).apiKey);
newApp.options.apiKey.should.equal(
(Platform.OS === 'ios' ? iosTestConfig : androidTestConfig).apiKey
);
// TODO add back in when android sdk support for deleting apps becomes available
// return newApp.delete();
return Promise.resolve();

View File

@ -1,38 +1,35 @@
export default function addTests({ describe, it, firebase }) {
describe('API', () => {
it('it should set collection enabled/disabled', () => {
return new Promise((resolve) => {
it('it should set collection enabled/disabled', () =>
new Promise(resolve => {
firebase.native.crash().setCrashCollectionEnabled(false);
firebase.native.crash().setCrashCollectionEnabled(true);
resolve();
});
});
}));
it('it should return whether crash reporting is enabled', () => {
return new Promise((resolve) => {
firebase.native.crash().isCrashCollectionEnabled()
.then((enabled) => {
it('it should return whether crash reporting is enabled', () =>
new Promise(resolve => {
firebase.native
.crash()
.isCrashCollectionEnabled()
.then(enabled => {
enabled.should.be.a.Boolean();
resolve();
});
});
});
}));
});
describe('Log', () => {
it('log: it should log without error', () => {
return new Promise((resolve) => {
it('log: it should log without error', () =>
new Promise(resolve => {
firebase.native.crash().log('Test log');
resolve();
});
});
}));
it('logcat: it should log without error', () => {
return new Promise((resolve) => {
it('logcat: it should log without error', () =>
new Promise(resolve => {
firebase.native.crash().logcat(0, 'LogTest', 'Test log');
resolve();
});
});
}));
});
}

View File

@ -1,52 +1,47 @@
export default function addTests({ describe, it, firebase }) {
describe('Crashlytics', () => {
it('log: it should log without error', () => {
return new Promise((resolve) => {
it('log: it should log without error', () =>
new Promise(resolve => {
firebase.native.fabric.crashlytics().log('Test log');
resolve();
});
});
}));
it('recordError: it should record an error without error', () => {
return new Promise((resolve) => {
it('recordError: it should record an error without error', () =>
new Promise(resolve => {
firebase.native.fabric.crashlytics().recordError(1234, 'Test error');
resolve();
});
});
}));
it('setBoolValue: it should set a boolean value without error', () => {
return new Promise((resolve) => {
it('setBoolValue: it should set a boolean value without error', () =>
new Promise(resolve => {
firebase.native.fabric.crashlytics().setBoolValue('boolKey', true);
resolve();
});
});
}));
it('setFloatValue: it should set a float value without error', () => {
return new Promise((resolve) => {
it('setFloatValue: it should set a float value without error', () =>
new Promise(resolve => {
firebase.native.fabric.crashlytics().setFloatValue('floatKey', 1.23);
resolve();
});
});
}));
it('setIntValue: it should set an integer value without error', () => {
return new Promise((resolve) => {
it('setIntValue: it should set an integer value without error', () =>
new Promise(resolve => {
firebase.native.fabric.crashlytics().setIntValue('intKey', 123);
resolve();
});
});
}));
it('setStringValue: it should set a string value without error', () => {
return new Promise((resolve) => {
firebase.native.fabric.crashlytics().setStringValue('stringKey', 'test');
it('setStringValue: it should set a string value without error', () =>
new Promise(resolve => {
firebase.native.fabric
.crashlytics()
.setStringValue('stringKey', 'test');
resolve();
});
});
}));
it('setUserIdentifier: it should set the user ID without error', () => {
return new Promise((resolve) => {
it('setUserIdentifier: it should set the user ID without error', () =>
new Promise(resolve => {
firebase.native.fabric.crashlytics().setUserIdentifier('1234');
resolve();
});
});
}));
});
}

View File

@ -2,7 +2,11 @@ import firebase from '../../firebase';
import TestSuite from '../../../lib/TestSuite';
import crashlyticsTests from './crashlyticsTests';
const suite = new TestSuite('Crashlytics', 'firebase.fabric.crashlytics()', firebase);
const suite = new TestSuite(
'Crashlytics',
'firebase.fabric.crashlytics()',
firebase
);
// bootstrap tests
suite.addTests(crashlyticsTests);

View File

@ -18,4 +18,3 @@ suite.addTests(refTestGroups);
suite.addTests(snapshotTests);
export default suite;

View File

@ -32,7 +32,7 @@ function childTests({ describe, it, context, firebase }) {
});
});
context('when passed a path that doesn\'t exist', () => {
context("when passed a path that doesn't exist", () => {
it('creates a reference, anyway', () => {
// Setup

View File

@ -9,7 +9,6 @@ function factoryTests({ describe, it, firebase }) {
// Test
// Assertion
(ref.key === null).should.be.true();
@ -24,7 +23,7 @@ function factoryTests({ describe, it, firebase }) {
// Test
let valueAtRef;
await ref.once('value', (snapshot) => {
await ref.once('value', snapshot => {
valueAtRef = snapshot.val();
});

View File

@ -22,9 +22,24 @@ import priorityTests from './priorityTests';
import DatabaseContents from '../../support/DatabaseContents';
const testGroups = [
issueSpecificTests, factoryTests, keyTests, parentTests, childTests, rootTests,
pushTests, onTests, onValueTests, onChildAddedTests, onceTests, updateTests,
removeTests, setTests, transactionTests, queryTests, refTests, isEqualTests,
issueSpecificTests,
factoryTests,
keyTests,
parentTests,
childTests,
rootTests,
pushTests,
onTests,
onValueTests,
onChildAddedTests,
onceTests,
updateTests,
removeTests,
setTests,
transactionTests,
queryTests,
refTests,
isEqualTests,
priorityTests,
offTests,
];
@ -42,10 +57,9 @@ function registerTestSuite(testSuite) {
await this._databaseRef.parent.child('issues').set(DatabaseContents.ISSUES);
});
testGroups.forEach((testGroup) => {
testGroups.forEach(testGroup => {
testGroup(testSuite);
});
}
module.exports = registerTestSuite;

Some files were not shown because too many files have changed in this diff Show More