UNPKG

react-native-firebase-for-netmera

Version:
306 lines (245 loc) 7.64 kB
import INTERNALS from '../../../utils/internals'; import { SharedEventEmitter } from '../../../utils/events'; import { generatePushID, isFunction, isAndroid, isIOS, isString, nativeToJSError } from '../../../utils'; import { getNativeModule } from '../../../utils/native'; export default class PhoneAuthListener { /** * * @param auth * @param phoneNumber * @param timeout * @param forceResend */ constructor(auth, phoneNumber, timeout, forceResend) { this._auth = auth; this._reject = null; this._resolve = null; this._promise = null; this._credential = null; this._timeout = timeout || 20; // 20 secs this._forceResending = forceResend || false; this._phoneAuthRequestKey = generatePushID(); // internal events this._internalEvents = { codeSent: `phone:auth:${this._phoneAuthRequestKey}:onCodeSent`, verificationFailed: `phone:auth:${this._phoneAuthRequestKey}:onVerificationFailed`, verificationComplete: `phone:auth:${this._phoneAuthRequestKey}:onVerificationComplete`, codeAutoRetrievalTimeout: `phone:auth:${this._phoneAuthRequestKey}:onCodeAutoRetrievalTimeout` }; // user observer events this._publicEvents = { // error cb error: `phone:auth:${this._phoneAuthRequestKey}:error`, // observer event: `phone:auth:${this._phoneAuthRequestKey}:event`, // success cb success: `phone:auth:${this._phoneAuthRequestKey}:success` }; // setup internal event listeners this._subscribeToEvents(); // start verification flow natively if (isAndroid) { getNativeModule(this._auth).verifyPhoneNumber(phoneNumber, this._phoneAuthRequestKey, this._timeout, this._forceResending); } if (isIOS) { getNativeModule(this._auth).verifyPhoneNumber(phoneNumber, this._phoneAuthRequestKey); } } /** * Subscribes to all EE events on this._internalEvents * @private */ _subscribeToEvents() { const events = Object.keys(this._internalEvents); for (let i = 0, len = events.length; i < len; i++) { const type = events[i]; SharedEventEmitter.once(this._internalEvents[type], // $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323 this[`_${type}Handler`].bind(this)); } } /** * Subscribe a users listener cb to the snapshot events. * @param observer * @private */ _addUserObserver(observer) { SharedEventEmitter.addListener(this._publicEvents.event, observer); } /** * Send a snapshot event to users event observer. * @param snapshot PhoneAuthSnapshot * @private */ _emitToObservers(snapshot) { SharedEventEmitter.emit(this._publicEvents.event, snapshot); } /** * Send a error snapshot event to any subscribed errorCb's * @param snapshot * @private */ _emitToErrorCb(snapshot) { const { error } = snapshot; if (this._reject) this._reject(error); SharedEventEmitter.emit(this._publicEvents.error, error); } /** * Send a success snapshot event to any subscribed completeCb's * @param snapshot * @private */ _emitToSuccessCb(snapshot) { if (this._resolve) this._resolve(snapshot); SharedEventEmitter.emit(this._publicEvents.success, snapshot); } /** * Removes all listeners for this phone auth instance * @private */ _removeAllListeners() { setTimeout(() => { // move to next event loop - not sure if needed // internal listeners Object.values(this._internalEvents).forEach(event => { SharedEventEmitter.removeAllListeners(event); }); // user observer listeners Object.values(this._publicEvents).forEach(publicEvent => { SharedEventEmitter.removeAllListeners(publicEvent); }); }, 0); } /** * Create a new internal deferred promise, if not already created * @private */ _promiseDeferred() { if (!this._promise) { this._promise = new Promise((resolve, reject) => { this._resolve = result => { this._resolve = null; return resolve(result); }; this._reject = possibleError => { this._reject = null; return reject(possibleError); }; }); } } /* -------------------------- --- INTERNAL EVENT HANDLERS ---------------------------- */ /** * Internal code sent event handler * @private * @param credential */ _codeSentHandler(credential) { const snapshot = { verificationId: credential.verificationId, code: null, error: null, state: 'sent' }; this._emitToObservers(snapshot); if (isIOS) { this._emitToSuccessCb(snapshot); } if (isAndroid) {// android can auto retrieve so we don't emit to successCb immediately, // if auto retrieve times out then that will emit to successCb } } /** * Internal code auto retrieve timeout event handler * @private * @param credential */ _codeAutoRetrievalTimeoutHandler(credential) { const snapshot = { verificationId: credential.verificationId, code: null, error: null, state: 'timeout' }; this._emitToObservers(snapshot); this._emitToSuccessCb(snapshot); } /** * Internal verification complete event handler * @param credential * @private */ _verificationCompleteHandler(credential) { const snapshot = { verificationId: credential.verificationId, code: credential.code || null, error: null, state: 'verified' }; this._emitToObservers(snapshot); this._emitToSuccessCb(snapshot); this._removeAllListeners(); } /** * Internal verification failed event handler * @param state * @private */ _verificationFailedHandler(state) { const snapshot = { verificationId: state.verificationId, code: null, error: null, state: 'error' }; const { code, message, nativeErrorMessage } = state.error; snapshot.error = nativeToJSError(code, message, { nativeErrorMessage }); this._emitToObservers(snapshot); this._emitToErrorCb(snapshot); this._removeAllListeners(); } /* ------------- -- PUBLIC API --------------*/ on(event, observer, errorCb, successCb) { if (!isString(event)) { throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('event', 'string', 'on')); } if (event !== 'state_changed') { throw new Error(INTERNALS.STRINGS.ERROR_ARG_INVALID_VALUE('event', 'state_changed', event)); } if (!isFunction(observer)) { throw new Error(INTERNALS.STRINGS.ERROR_MISSING_ARG_NAMED('observer', 'function', 'on')); } this._addUserObserver(observer); if (isFunction(errorCb)) { SharedEventEmitter.once(this._publicEvents.error, errorCb); } if (isFunction(successCb)) { SharedEventEmitter.once(this._publicEvents.success, successCb); } return this; } /** * Promise .then proxy * @param fn */ then(fn) { this._promiseDeferred(); // $FlowFixMe: Unsure how to annotate `bind` here if (this._promise) return this._promise.then.bind(this._promise)(fn); return undefined; // will never get here - just to keep flow happy } /** * Promise .catch proxy * @param fn */ catch(fn) { this._promiseDeferred(); // $FlowFixMe: Unsure how to annotate `bind` here if (this._promise) return this._promise.catch.bind(this._promise)(fn); return undefined; // will never get here - just to keep flow happy } }