| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 | "use strict";var _CoreManager = _interopRequireDefault(require("./CoreManager"));var _ParseObject = _interopRequireDefault(require("./ParseObject"));var _ParseQuery = _interopRequireDefault(require("./ParseQuery"));var _Storage = _interopRequireDefault(require("./Storage"));function _interopRequireDefault(obj) {  return obj && obj.__esModule ? obj : {    default: obj  };}/** * https://github.com/francimedia/parse-js-local-storage * * @flow *//*:: import type { SaveOptions } from './ParseObject';*//*:: import type { RequestOptions } from './RESTController';*//*:: type QueueObject = {  queueId: string,  action: string,  object: ParseObject,  serverOptions: SaveOptions | RequestOptions,  id: string,  className: string,  hash: string,  createdAt: Date,};*//*:: type Queue = Array<QueueObject>;*/const QUEUE_KEY = 'Parse/Eventually/Queue';let queueCache = [];let dirtyCache = true;let polling = undefined;/** * Provides utility functions to queue objects that will be * saved to the server at a later date. * * @class Parse.EventuallyQueue * @static */const EventuallyQueue = {  /**   * Add object to queue with save operation.   *   * @function save   * @name Parse.EventuallyQueue.save   * @param {ParseObject} object Parse.Object to be saved eventually   * @param {object} [serverOptions] See {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#save Parse.Object.save} options.   * @returns {Promise} A promise that is fulfilled if object is added to queue.   * @static   * @see Parse.Object#saveEventually   */  save(object /*: ParseObject*/, serverOptions /*: SaveOptions*/ = {}) /*: Promise*/{    return this.enqueue('save', object, serverOptions);  },  /**   * Add object to queue with save operation.   *   * @function destroy   * @name Parse.EventuallyQueue.destroy   * @param {ParseObject} object Parse.Object to be destroyed eventually   * @param {object} [serverOptions] See {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#destroy Parse.Object.destroy} options   * @returns {Promise} A promise that is fulfilled if object is added to queue.   * @static   * @see Parse.Object#destroyEventually   */  destroy(object /*: ParseObject*/, serverOptions /*: RequestOptions*/ = {}) /*: Promise*/{    return this.enqueue('destroy', object, serverOptions);  },  /**   * Generate unique identifier to avoid duplicates and maintain previous state.   *   * @param {string} action save / destroy   * @param {object} object Parse.Object to be queued   * @returns {string}   * @static   * @ignore   */  generateQueueId(action /*: string*/, object /*: ParseObject*/) /*: string*/{    object._getId();    const {      className,      id,      _localId    } = object;    const uniqueId = object.get('hash') || _localId;    return [action, className, id, uniqueId].join('_');  },  /**   * Build queue object and add to queue.   *   * @param {string} action save / destroy   * @param {object} object Parse.Object to be queued   * @param {object} [serverOptions]   * @returns {Promise} A promise that is fulfilled if object is added to queue.   * @static   * @ignore   */  async enqueue(action /*: string*/, object /*: ParseObject*/, serverOptions /*: SaveOptions | RequestOptions*/) /*: Promise*/{    const queueData = await this.getQueue();    const queueId = this.generateQueueId(action, object);    let index = this.queueItemExists(queueData, queueId);    if (index > -1) {      // Add cached values to new object if they don't exist      for (const prop in queueData[index].object) {        if (typeof object.get(prop) === 'undefined') {          object.set(prop, queueData[index].object[prop]);        }      }    } else {      index = queueData.length;    }    queueData[index] = {      queueId,      action,      object: object.toJSON(),      serverOptions,      id: object.id,      className: object.className,      hash: object.get('hash'),      createdAt: new Date()    };    return this.setQueue(queueData);  },  store(data) {    return _Storage.default.setItemAsync(QUEUE_KEY, JSON.stringify(data));  },  load() {    return _Storage.default.getItemAsync(QUEUE_KEY);  },  /**   * Sets the in-memory queue from local storage and returns.   *   * @function getQueue   * @name Parse.EventuallyQueue.getQueue   * @returns {Promise<Array>}   * @static   */  async getQueue() /*: Promise<Array>*/{    if (dirtyCache) {      queueCache = JSON.parse((await this.load()) || '[]');      dirtyCache = false;    }    return queueCache;  },  /**   * Saves the queue to local storage   *   * @param {Queue} queue Queue containing Parse.Object data.   * @returns {Promise} A promise that is fulfilled when queue is stored.   * @static   * @ignore   */  setQueue(queue /*: Queue*/) /*: Promise<void>*/{    queueCache = queue;    return this.store(queueCache);  },  /**   * Removes Parse.Object data from queue.   *   * @param {string} queueId Unique identifier for Parse.Object data.   * @returns {Promise} A promise that is fulfilled when queue is stored.   * @static   * @ignore   */  async remove(queueId /*: string*/) /*: Promise<void>*/{    const queueData = await this.getQueue();    const index = this.queueItemExists(queueData, queueId);    if (index > -1) {      queueData.splice(index, 1);      await this.setQueue(queueData);    }  },  /**   * Removes all objects from queue.   *   * @function clear   * @name Parse.EventuallyQueue.clear   * @returns {Promise} A promise that is fulfilled when queue is cleared.   * @static   */  clear() /*: Promise*/{    queueCache = [];    return this.store([]);  },  /**   * Return the index of a queueId in the queue. Returns -1 if not found.   *   * @param {Queue} queue Queue containing Parse.Object data.   * @param {string} queueId Unique identifier for Parse.Object data.   * @returns {number}   * @static   * @ignore   */  queueItemExists(queue /*: Queue*/, queueId /*: string*/) /*: number*/{    return queue.findIndex(data => data.queueId === queueId);  },  /**   * Return the number of objects in the queue.   *   * @function length   * @name Parse.EventuallyQueue.length   * @returns {number}   * @static   */  async length() /*: number*/{    const queueData = await this.getQueue();    return queueData.length;  },  /**   * Sends the queue to the server.   *   * @function sendQueue   * @name Parse.EventuallyQueue.sendQueue   * @returns {Promise<boolean>} Returns true if queue was sent successfully.   * @static   */  async sendQueue() /*: Promise<boolean>*/{    const queue = await this.getQueue();    const queueData = [...queue];    if (queueData.length === 0) {      return false;    }    for (let i = 0; i < queueData.length; i += 1) {      const queueObject = queueData[i];      const {        id,        hash,        className      } = queueObject;      const ObjectType = _ParseObject.default.extend(className);      if (id) {        await this.process.byId(ObjectType, queueObject);      } else if (hash) {        await this.process.byHash(ObjectType, queueObject);      } else {        await this.process.create(ObjectType, queueObject);      }    }    return true;  },  /**   * Build queue object and add to queue.   *   * @param {ParseObject} object Parse.Object to be processed   * @param {QueueObject} queueObject Parse.Object data from the queue   * @returns {Promise} A promise that is fulfilled when operation is performed.   * @static   * @ignore   */  async sendQueueCallback(object /*: ParseObject*/, queueObject /*: QueueObject*/) /*: Promise<void>*/{    if (!object) {      return this.remove(queueObject.queueId);    }    switch (queueObject.action) {      case 'save':        // Queued update was overwritten by other request. Do not save        if (typeof object.updatedAt !== 'undefined' && object.updatedAt > new Date(queueObject.object.createdAt)) {          return this.remove(queueObject.queueId);        }        try {          await object.save(queueObject.object, queueObject.serverOptions);          await this.remove(queueObject.queueId);        } catch (e) {          if (e.message !== 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {            await this.remove(queueObject.queueId);          }        }        break;      case 'destroy':        try {          await object.destroy(queueObject.serverOptions);          await this.remove(queueObject.queueId);        } catch (e) {          if (e.message !== 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {            await this.remove(queueObject.queueId);          }        }        break;    }  },  /**   * Start polling server for network connection.   * Will send queue if connection is established.   *   * @function poll   * @name Parse.EventuallyQueue.poll   * @param [ms] Milliseconds to ping the server. Default 2000ms   * @static   */  poll(ms /*: number*/ = 2000) {    if (polling) {      return;    }    polling = setInterval(() => {      const RESTController = _CoreManager.default.getRESTController();      RESTController.request('GET', 'health').then(({        status      }) => {        if (status === 'ok') {          this.stopPoll();          return this.sendQueue();        }      }).catch(e => e);    }, ms);  },  /**   * Turns off polling.   *   * @function stopPoll   * @name Parse.EventuallyQueue.stopPoll   * @static   */  stopPoll() {    clearInterval(polling);    polling = undefined;  },  /**   * Return true if pinging the server.   *   * @function isPolling   * @name Parse.EventuallyQueue.isPolling   * @returns {boolean}   * @static   */  isPolling() /*: boolean*/{    return !!polling;  },  _setPolling(flag /*: boolean*/) {    polling = flag;  },  process: {    create(ObjectType, queueObject) {      const object = new ObjectType();      return EventuallyQueue.sendQueueCallback(object, queueObject);    },    async byId(ObjectType, queueObject) {      const {        sessionToken      } = queueObject.serverOptions;      const query = new _ParseQuery.default(ObjectType);      query.equalTo('objectId', queueObject.id);      const results = await query.find({        sessionToken      });      return EventuallyQueue.sendQueueCallback(results[0], queueObject);    },    async byHash(ObjectType, queueObject) {      const {        sessionToken      } = queueObject.serverOptions;      const query = new _ParseQuery.default(ObjectType);      query.equalTo('hash', queueObject.hash);      const results = await query.find({        sessionToken      });      if (results.length > 0) {        return EventuallyQueue.sendQueueCallback(results[0], queueObject);      }      return EventuallyQueue.process.create(ObjectType, queueObject);    }  }};module.exports = EventuallyQueue;
 |