2
0
mirror of synced 2025-01-10 22:26:02 +00:00

typeOf check the actual value - derp

This commit is contained in:
Salakar 2017-03-02 13:10:10 +00:00
parent 7e8dbcbb35
commit 79bfc7ce09
7 changed files with 741 additions and 0 deletions

26
lib/constants.js Normal file
View File

@ -0,0 +1,26 @@
import { reverseKeyValues } from './utils';
export const ConnectionResult = {
SUCCESS: 0,
SERVICE_MISSING: 1,
SERVICE_VERSION_UPDATE_REQUIRED: 2,
SERVICE_DISABLED: 3,
SIGN_IN_REQUIRED: 4,
INVALID_ACCOUNT: 5,
RESOLUTION_REQUIRED: 6,
NETWORK_ERROR: 7,
INTERNAL_ERROR: 8,
SERVICE_INVALID: 9,
DEVELOPER_ERROR: 10,
LICENSE_CHECK_FAILED: 11,
CANCELED: 13,
TIMEOUT: 14,
INTERRUPTED: 15,
API_UNAVAILABLE: 16,
SIGN_IN_FAILED: 17,
SERVICE_UPDATING: 18,
SERVICE_MISSING_PERMISSION: 19,
RESTRICTED_PROFILE: 20,
};
export const ConnectionResultReverse = reverseKeyValues(ConnectionResult);

225
lib/firebase.js Normal file
View File

@ -0,0 +1,225 @@
/**
* @providesModule Firebase
* @flow
*/
import { NativeModules, NativeEventEmitter } from 'react-native';
import Log from './utils/log';
import { promisify } from './utils';
import Singleton from './utils/singleton';
// modules
import Auth from './modules/auth';
import Storage from './modules/storage';
import Database from './modules/database';
import Messaging from './modules/messaging';
import Analytics from './modules/analytics';
let log;
const instances = { default: null };
const FirebaseModule = NativeModules.RNFirebase;
const FirebaseModuleEvt = new NativeEventEmitter(FirebaseModule);
/**
* @class Firebase
*/
export default class Firebase extends Singleton {
/**
*
* @param options
*/
constructor(options: Object = {}) {
const instance = super(options);
instance.options = Object.assign({ errorOnMissingPlayServices: true }, options);
instance._debug = instance.options.debug || false;
Log.enable(instance._debug);
log = instance._log = new Log('firebase');
log.info('Creating new firebase instance');
instance._remoteConfig = instance.options.remoteConfig || {};
delete instance.options.remoteConfig;
instance.configured = instance.options.configure || false;
instance.eventHandlers = {};
log.info('Calling configure with options', instance.options);
instance.configurePromise = instance.configure(instance.options);
instance._auth = new Auth(instance, instance.options);
if (instance.options.errorOnMissingPlayServices && !this.googleApiAvailability.isAvailable) {
throw new Error(`Google Play Services is required to run this application but no valid installation was found (Code ${this.googleApiAvailability.status}).`);
}
}
_db: ?Object;
_log: ?Object;
_auth: ?Object;
_store: ?Object;
_storage: ?Object;
_presence: ?Object;
_analytics: ?Object;
_constants: ?Object;
_messaging: ?Object;
_remoteConfig: ?Object;
/**
* Support web version of initApp.
* @param options
* @param name
* @returns {*}
*/
static initializeApp(options: Object = {}, name: string = 'default') {
if (!instances[name]) instances[name] = new Firebase(options);
return instances[name];
}
/**
*
* @param opts
* @returns {Promise.<TResult>|*|Promise.<T>}
*/
configure(opts: Object = {}) {
if (!this.configurePromise) {
const firebaseOptions = Object.assign({}, this.options, opts);
this.configurePromise = promisify('configureWithOptions', FirebaseModule)(firebaseOptions)
.then((configuredProperties) => {
log.info('Native configureWithOptions success', configuredProperties);
this.configured = true;
this.firebaseOptions = configuredProperties;
return configuredProperties;
}).catch((err) => {
log.info('Native error occurred while calling configure', err);
});
}
return this.configurePromise;
}
onReady(cb: Function) {
// TODO wut o.O
return this.configurePromise = this.configurePromise.then(cb);
}
/**
* Wrappers
* We add methods from each wrapper to this instance
* when they are needed. Not sure if this is a good
* idea or not (imperative vs. direct manipulation/proxy)
*/
auth() {
return this._auth;
}
database() {
if (!this._db) {
this._db = new Database(this);
}
return this._db;
}
analytics() {
if (!this._analytics) {
this._analytics = new Analytics(this);
}
return this._analytics;
}
// storage
storage() {
if (!this._storage) {
this._storage = new Storage(this);
}
return this._storage;
}
messaging() {
if (!this._messaging) {
this._messaging = new Messaging(this);
}
return this._messaging;
}
remoteConfig() {
if (!this._remoteConfig) {
this._remoteConfig = new RemoteConfig(this);
}
return this._remoteConfig;
}
get ServerValue(): Promise<*> {
return promisify('serverValue', FirebaseModule)();
}
get apps(): Array<string> {
return Object.keys(instances);
}
/**
* Returns androids GoogleApiAvailability status and message if available.
* @returns {GoogleApiAvailabilityType|{isAvailable: boolean, status: number}}
*/
get googleApiAvailability(): GoogleApiAvailabilityType {
// if not available then return a fake object for ios - saves doing platform specific logic.
return FirebaseModule.googleApiAvailability || { isAvailable: true, status: 0 };
}
/**
* Logger
*/
get log(): Log {
return this._log;
}
/**
* Redux store
**/
get store(): ?Object {
return this._store;
}
get constants(): Object {
if (!this._constants) {
this._constants = Object.assign({}, Storage.constants);
}
return this._constants;
}
/**
* Set the redux store helper
*/
setStore(store: Object) {
if (store) {
this.log.info('Setting the store for Firebase instance');
this._store = store;
}
}
/**
* Global event handlers for the single Firebase instance
*/
on(name: string, cb: Function, nativeModule: Object = FirebaseModuleEvt) {
if (!this.eventHandlers[name]) {
this.eventHandlers[name] = [];
}
const sub = nativeModule.addListener(name, cb);
this.eventHandlers[name].push(sub);
return sub;
}
off(name: string) {
if (this.eventHandlers[name]) {
this.eventHandlers[name]
.forEach(subscription => subscription.remove());
}
}
}

23
lib/flow.js Normal file
View File

@ -0,0 +1,23 @@
/* eslint-disable */
declare module 'react-native' {
// noinspection ES6ConvertVarToLetConst
declare var exports: any;
}
declare type AuthResultType = {
authenticated: boolean,
user: Object|null
};
declare type CredentialType = {
provider: string,
token: string,
secret: string
};
declare type GoogleApiAvailabilityType = {
status: number,
isAvailable: boolean,
isUserResolvableError?: boolean,
error?: string
};

79
lib/modules/base.js Normal file
View File

@ -0,0 +1,79 @@
/**
* @flow
*/
import { NativeModules, NativeEventEmitter } from 'react-native';
import Log from '../utils/log';
import EventEmitter from './../utils/eventEmitter';
const FirebaseModule = NativeModules.RNFirebase;
const FirebaseModuleEvt = new NativeEventEmitter(FirebaseModule);
const logs = {};
type FirebaseOptions = {};
// TODO cleanup
export class Base extends EventEmitter {
constructor(firebase: Object, options: FirebaseOptions = {}) {
super();
this.firebase = firebase;
this.eventHandlers = {};
this.options = Object.assign({}, firebase.options, options);
}
// Logger
get log(): Log {
if (!logs[this.namespace]) logs[this.namespace] = new Log(this.namespace, this.firebase._debug);
return logs[this.namespace];
}
/**
* app instance
**/
get app(): Object {
return this.firebase.app;
}
whenReady(promise: Promise<*>): Promise<*> {
return this.firebase.configurePromise.then(() => promise);
}
// Event handlers
// proxy to firebase instance
_on(name, cb, nativeModule) {
return new Promise((resolve) => {
// if (!this.eventHandlers[name]) {
// this.eventHandlers[name] = {};
// }
if (!nativeModule) {
nativeModule = FirebaseModuleEvt;
}
const sub = nativeModule.addListener(name, cb);
this.eventHandlers[name] = sub;
resolve(sub);
});
}
_off(name) {
return new Promise((resolve) => {
if (this.eventHandlers[name]) {
const subscription = this.eventHandlers[name];
subscription.remove(); // Remove subscription
delete this.eventHandlers[name];
resolve(subscription);
}
});
}
}
export class ReferenceBase extends Base {
constructor(firebase: Object, path: string) {
super(firebase);
this.path = path || '/';
}
get key(): string|null {
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
}
}

313
lib/utils/eventEmitter.js Normal file
View File

@ -0,0 +1,313 @@
// TODO - this is just a raw copy of eventEmitter3 - until i can implement a lightweight version
'use strict';
var has = Object.prototype.hasOwnProperty
, prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @api private
*/
function Events() {}
//
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null);
//
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {Mixed} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @api private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @api public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @api public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = []
, events
, name;
if (this._eventsCount === 0) return names;
for (name in (events = this._events)) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {String|Symbol} event The event name.
* @param {Boolean} exists Only check if there are listeners.
* @returns {Array|Boolean}
* @api public
*/
EventEmitter.prototype.listeners = function listeners(event, exists) {
var evt = prefix ? prefix + event : event
, available = this._events[evt];
if (exists) return !!available;
if (!available) return [];
if (available.fn) return [available.fn];
for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {
ee[i] = available[i].fn;
}
return ee;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {String|Symbol} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @api public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt]
, len = arguments.length
, args
, i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1: return listeners.fn.call(listeners.context), true;
case 2: return listeners.fn.call(listeners.context, a1), true;
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len -1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length
, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1: listeners[i].fn.call(listeners[i].context); break;
case 2: listeners[i].fn.call(listeners[i].context, a1); break;
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
default:
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {String|Symbol} event The event name.
* @param {Function} fn The listener function.
* @param {Mixed} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @api public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
var listener = new EE(fn, context || this)
, evt = prefix ? prefix + event : event;
if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;
else if (!this._events[evt].fn) this._events[evt].push(listener);
else this._events[evt] = [this._events[evt], listener];
return this;
};
/**
* Add a one-time listener for a given event.
*
* @param {String|Symbol} event The event name.
* @param {Function} fn The listener function.
* @param {Mixed} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @api public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
var listener = new EE(fn, context || this, true)
, evt = prefix ? prefix + event : event;
if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;
else if (!this._events[evt].fn) this._events[evt].push(listener);
else this._events[evt] = [this._events[evt], listener];
return this;
};
/**
* Remove the listeners of a given event.
*
* @param {String|Symbol} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {Mixed} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @api public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
if (--this._eventsCount === 0) this._events = new Events();
else delete this._events[evt];
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (
listeners.fn === fn
&& (!once || listeners.once)
&& (!context || listeners.context === context)
) {
if (--this._eventsCount === 0) this._events = new Events();
else delete this._events[evt];
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (
listeners[i].fn !== fn
|| (once && !listeners[i].once)
|| (context && listeners[i].context !== context)
) {
events.push(listeners[i]);
}
}
//
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
else if (--this._eventsCount === 0) this._events = new Events();
else delete this._events[evt];
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {String|Symbol} [event] The event name.
* @returns {EventEmitter} `this`.
* @api public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) {
if (--this._eventsCount === 0) this._events = new Events();
else delete this._events[evt];
}
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
};
//
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
//
// This function doesn't apply anymore.
//
EventEmitter.prototype.setMaxListeners = function setMaxListeners() {
return this;
};
//
// Expose the prefix.
//
EventEmitter.prefixed = prefix;
//
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter;
//
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}

40
lib/utils/log.js Normal file
View File

@ -0,0 +1,40 @@
import { windowOrGlobal } from './';
((base) => {
window = base || window;
if (!window.localStorage) window.localStorage = {};
})(windowOrGlobal);
export default class Log {
constructor(namespace) {
this._namespace = namespace || 'RNFirebase';
require('bows').config({ padLength: 20 });
this.loggers = {};
}
get warn() {
return this._createOrGetLogger('warn');
}
get info() {
return this._createOrGetLogger('info');
}
get error() {
return this._createOrGetLogger('error');
}
get debug() {
return this._createOrGetLogger('debug');
}
static enable(booleanOrStringDebug) {
window.localStorage.debug = booleanOrStringDebug;
window.localStorage.debugColors = !!window.localStorage.debug;
}
_createOrGetLogger(level) {
if (!this.loggers[level]) this.loggers[level] = require('bows')(this._namespace, `[${level}]`);
return this.loggers[level];
}
}

35
lib/utils/singleton.js Normal file
View File

@ -0,0 +1,35 @@
const Symbol = require('es6-symbol');
class Singleton {
constructor() {
const Class = this.constructor;
if (!Class[this.singleton]) {
Class[this.singleton] = this;
}
return Class[this.singleton];
}
static get instance() {
if (!this[this.singleton]) {
this[this.singleton] = new this();
}
return this[this.singleton];
}
static set instance(instance) {
this[this.singleton] = instance;
return this[this.singleton];
}
static get singleton() {
return Symbol(this.namespace);
}
static reset() {
delete this[this.singleton];
}
}
export default Singleton;