UNPKG

react-native-firebase-compiled

Version:

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, Functions, Messaging (FCM), Remote Config, Sto

243 lines (184 loc) 7.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _events = require("../../utils/events"); var _native = require("../../utils/native"); var _Transaction = _interopRequireDefault(require("./Transaction")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } let transactionId = 0; /** * Uses the push id generator to create a transaction id * @returns {number} * @private */ const generateTransactionId = () => transactionId++; /** * @class TransactionHandler */ class TransactionHandler { constructor(firestore) { _defineProperty(this, "_firestore", void 0); _defineProperty(this, "_pending", void 0); this._pending = {}; this._firestore = firestore; _events.SharedEventEmitter.addListener((0, _events.getAppEventName)(this._firestore, 'firestore_transaction_event'), this._handleTransactionEvent.bind(this)); } /** * ------------- * INTERNAL API * ------------- */ /** * Add a new transaction and start it natively. * @param updateFunction */ _add(updateFunction) { const id = generateTransactionId(); // $FlowExpectedError: Transaction has to be populated const meta = { id, updateFunction, stack: new Error().stack.split('\n').slice(2).join('\n') }; this._pending[id] = { meta, transaction: new _Transaction.default(this._firestore, meta) }; // deferred promise return new Promise((resolve, reject) => { (0, _native.getNativeModule)(this._firestore).transactionBegin(id); meta.resolve = r => { resolve(r); this._remove(id); }; meta.reject = e => { reject(e); this._remove(id); }; }); } /** * Destroys a local instance of a transaction meta * * @param id * @private */ _remove(id) { (0, _native.getNativeModule)(this._firestore).transactionDispose(id); delete this._pending[id]; } /** * ------------- * EVENTS * ------------- */ /** * Handles incoming native transaction events and distributes to correct * internal handler by event.type * * @param event * @returns {*} * @private */ _handleTransactionEvent(event) { // eslint-disable-next-line default-case switch (event.type) { case 'update': this._handleUpdate(event); break; case 'error': this._handleError(event); break; case 'complete': this._handleComplete(event); break; } } /** * Handles incoming native transaction update events * * @param event * @private */ _handleUpdate(event) { var _this = this; return _asyncToGenerator(function* () { const id = event.id; // abort if no longer exists js side if (!_this._pending[id]) return _this._remove(id); const _this$_pending$id = _this._pending[id], meta = _this$_pending$id.meta, transaction = _this$_pending$id.transaction; const updateFunction = meta.updateFunction, reject = meta.reject; // clear any saved state from previous transaction runs transaction._prepare(); let finalError; let updateFailed; let pendingResult; // run the users custom update functionality try { const possiblePromise = updateFunction(transaction); // validate user has returned a promise in their update function // TODO must it actually return a promise? Can't find any usages of it without one... if (!possiblePromise || !possiblePromise.then) { finalError = new Error('Update function for `firestore.runTransaction(updateFunction)` must return a Promise.'); } else { pendingResult = yield possiblePromise; } } catch (exception) { // exception can still be falsey if user `Promise.reject();` 's with no args // so we track the exception with a updateFailed boolean to ensure no fall-through updateFailed = true; finalError = exception; } // reject the final promise and remove from native // update is failed when either the users updateFunction // throws an error or rejects a promise if (updateFailed || finalError) { // $FlowExpectedError: Reject will always be present return reject(finalError); } // capture the resolved result as we'll need this // to resolve the runTransaction() promise when // native emits that the transaction is final transaction._pendingResult = pendingResult; // send the buffered update/set/delete commands for native to process return (0, _native.getNativeModule)(_this._firestore).transactionApplyBuffer(id, transaction._commandBuffer); })(); } /** * Handles incoming native transaction error events * * @param event * @private */ _handleError(event) { const id = event.id, error = event.error; const meta = this._pending[id].meta; if (meta && error) { const code = error.code, message = error.message; // build a JS error and replace its stack // with the captured one at start of transaction // so it's actually relevant to the user const errorWithStack = new Error(message); // $FlowExpectedError: code is needed for Firebase errors errorWithStack.code = code; // $FlowExpectedError: stack should be a stack trace errorWithStack.stack = `Error: ${message}\n${meta.stack}`; // $FlowExpectedError: Reject will always be present meta.reject(errorWithStack); } } /** * Handles incoming native transaction complete events * * @param event * @private */ _handleComplete(event) { const id = event.id; const _this$_pending$id2 = this._pending[id], meta = _this$_pending$id2.meta, transaction = _this$_pending$id2.transaction; if (meta) { const pendingResult = transaction._pendingResult; // $FlowExpectedError: Resolve will always be present meta.resolve(pendingResult); } } } exports.default = TransactionHandler;