SHARE
TWEET

Untitled

a guest Jan 24th, 2020 79 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 'use strict';
  2.  
  3. import { observable } from 'mobx';
  4. import axios from 'axios';
  5. import * as _ from 'lodash';
  6. import { formatRoute } from 'react-router-named-routes';
  7. import { reloadTop } from '../navigation';
  8. import { API } from '../routes';
  9. import * as C from '../_constants';
  10. import Utils from '../../../_utils';
  11.  
  12. import Shop from './_shop';
  13. import Usage from './_usage';
  14. import Discount from './_discount';
  15.  
  16. const PRODUCT_VARIANTS_LIFETIME = 180;
  17.  
  18. export default class Storage {
  19.   /**
  20.    * @type {{id: number, variants: {id:number,title:string,price:string}[], loaded_at: Date}[]}
  21.    * @private
  22.    */
  23.   @observable _productVariants = [];
  24.  
  25.   /**
  26.    * @type {Discount[]}
  27.    * @private
  28.    */
  29.   @observable _discounts = [];
  30.  
  31.   /**
  32.    * @type {number[]}
  33.    * @private
  34.    */
  35.   #_requested_products = [];
  36.  
  37.   /**
  38.    * @type {Shop}
  39.    */
  40.   #shop;
  41.  
  42.   /**
  43.    * @type {Usage}
  44.    */
  45.   #usage;
  46.  
  47.   /**
  48.    * @type {number}
  49.    */
  50.   #updateDiscountsTimeout = 0;
  51.  
  52.   /**
  53.    * @param {{}} shopData
  54.    * @param {{}[]} plansData
  55.    * @param {{}} initialData
  56.    */
  57.   constructor (shopData, plansData, initialData) {
  58.     this.#shop = new Shop(shopData);
  59.     this.#usage = new Usage(plansData, this.#shop);
  60.     this._discounts = initialData.discounts.map(d => new Discount(d, this.shop.timezone));
  61.     this.#loadDiscounts(true);
  62.   }
  63.  
  64.   /**
  65.    * @return {Shop}
  66.    */
  67.   get shop () {
  68.     return this.#shop;
  69.   }
  70.  
  71.   /**
  72.    * @return {Usage}
  73.    */
  74.   get usage () {
  75.     return this.#usage;
  76.   }
  77.  
  78.   /**
  79.    * @return {Discount[]}
  80.    */
  81.   discountsList (status = C.DISCOUNT_STATUS_ANY, search = '') {
  82.     return this._discounts
  83.       .filter(d => status === C.DISCOUNT_STATUS_ANY || d.status === status)
  84.       .filter(d => search === '' || d.name.toLowerCase().indexOf(search.toLowerCase()) !== -1);
  85.   }
  86.  
  87.   /**
  88.    * @param {number} discountId
  89.    * @param {boolean} enable
  90.    * @param {function} callback
  91.    */
  92.   discountEnable (discountId, enable, callback) {
  93.     let discount = this._discounts.find(d => d.id === discountId);
  94.     if (!discount) {
  95.       return;
  96.     }
  97.     axios.post(formatRoute(API.DISCOUNT_ENABLE, {id: discountId}), {enable})
  98.       .then(response => {
  99.         discount.fillWithData(response.data);
  100.         callback();
  101.       })
  102.       .catch(error => this.constructor.error(error));
  103.   }
  104.  
  105.   /**
  106.    * @param {number} discountId
  107.    */
  108.   discountDelete (discountId) {
  109.     axios.post(formatRoute(API.DISCOUNT_DELETE, {id: discountId}))
  110.       .then(response => {
  111.         this._discounts = this._discounts.filter(d => d.id !== discountId);
  112.       })
  113.       .catch(error => this.constructor.error(error));
  114.   }
  115.  
  116.   /**
  117.    * @param {number} discountId
  118.    * @param {{
  119.    *    name: string,
  120.    *    value_type:string,
  121.    *    value: number,
  122.    *    override_cents:boolean,
  123.    *    override_cents_to: number,
  124.    *    override_to_nearest:boolean,
  125.    *    applies_to:string,
  126.    *    collections: {id:number, title:string, image:string|null}[],
  127.    *    products: {id:number, title:string, image:.string|null, hasOnlyDefaultVariant:boolean, variants:number[], selected_itself:boolean}[]
  128.    *    date_start:Date,
  129.    *    date_stop: Date|null,
  130.    * }} data
  131.    * @param {function} callback
  132.    * @param {boolean} reactivation
  133.    */
  134.   discountSave (discountId, data, callback, reactivation = false) {
  135.     data = this.constructor.prepareDiscountDataToSave(data, this.shop.timezone);
  136.     data.reactivation = reactivation;
  137.     let url = discountId ? formatRoute(API.DISCOUNT_EDIT, {id: discountId}) : API.DISCOUNT_ADD;
  138.     axios.post(url, data)
  139.       .then(response => {
  140.         let discountNew = new Discount(response.data, this.shop.timezone);
  141.         if (discountId) {
  142.           let index = this._discounts.findIndex(d => d.id === discountId);
  143.           if (index !== -1) {
  144.             this._discounts.splice(index, 1, discountNew);
  145.           } else {
  146.             this._discounts.push(discountNew);
  147.           }
  148.         } else {
  149.           this._discounts.push(discountNew);
  150.         }
  151.         this.shop.progress = 2; // Get-started-task "Create a test rule" - done
  152.         callback(discountNew.id);
  153.       })
  154.       .catch(error => this.constructor.error(error));
  155.   }
  156.  
  157.   /**
  158.    * @param {{
  159.    *    applies_to:string,
  160.    *    collections: {id:number, title:string, image:string|null}[],
  161.    *    products: {id:number, title:string, image:.string|null, hasOnlyDefaultVariant:boolean, variants:number[], selected_itself:boolean}[],
  162.    *    date_start:Date,
  163.    *    date_stop: Date|null
  164.    * }} data
  165.    * @param {string} shopTimezone
  166.    * @return {*}
  167.    */
  168.   static prepareDiscountDataToSave (data, shopTimezone) {
  169.     data = _.cloneDeep(data);
  170.     data.date_start = Utils.datetimeToString(Utils.datetimeFromShopToServerTimezone(
  171.       data.date_start,
  172.       shopTimezone
  173.     ));
  174.     if (data.date_stop) {
  175.       data.date_stop = Utils.datetimeToString(Utils.datetimeFromShopToServerTimezone(
  176.         data.date_stop,
  177.         shopTimezone
  178.       ));
  179.     } else if (typeof (data.date_stop) !== 'undefined') {
  180.       delete (data.date_stop);
  181.     }
  182.     data.products = data.products.map(p => ({
  183.       id: p.id,
  184.       selected_itself: p.selected_itself,
  185.       variants: p.variants.map(v => parseInt(v))
  186.     }));
  187.     data.collections = data.collections.map(c => parseInt(c.id));
  188.     return data;
  189.   }
  190.  
  191.   /**
  192.    * @param {number} discountId
  193.    * @param {function} callback
  194.    */
  195.   discountLoadDetails (discountId, callback) {
  196.     axios.get(formatRoute(API.DISCOUNT_PRODUCTS_AND_COLLECTIONS, {id: discountId}))
  197.       .then(response => {
  198.         let discount = this._discounts.find(d => d.id === discountId);
  199.         if (!discount) {
  200.           callback({});
  201.         }
  202.         discount.collections = response.data.collections;
  203.         discount.products = response.data.products;
  204.         callback(discount);
  205.       })
  206.       .catch(error => this.constructor.error(error));
  207.   }
  208.  
  209.   /**
  210.    * @param {number[]} productIds
  211.    * @param {function} callback
  212.    */
  213.   loadProductVariants (productIds, callback = () => null) {
  214.     // Exclude already loaded (except expired ones) and already requested products
  215.     let now = new Date();
  216.     let excludeIds = [];
  217.     this._productVariants.forEach(product => {
  218.       let expired = now - product.loaded_at > PRODUCT_VARIANTS_LIFETIME * 1000;
  219.       if (!expired) {
  220.         excludeIds.push(product.id);
  221.       }
  222.     });
  223.     productIds = productIds.filter(pid => !excludeIds.includes(pid));
  224.     productIds = productIds.filter(pid => !this.#_requested_products.includes(pid));
  225.     if (!productIds.length) {
  226.       callback();
  227.       return;
  228.     }
  229.  
  230.     // Remember requested products
  231.     this.#_requested_products = this.#_requested_products.concat(productIds);
  232.  
  233.     // Do request
  234.     axios.post(API.GET_PRODUCT_VARIANTS, {products: productIds})
  235.       .then(response => {
  236.         // Apply response
  237.         response.data.forEach(item => {
  238.           let newProduct = {
  239.             id: item.id,
  240.             variants: item.variants,
  241.             loaded_at: new Date()
  242.           };
  243.           let k = this._productVariants.findIndex(p => p.id === item.id);
  244.           if (k !== -1) {
  245.             this._productVariants.splice(k, 1, newProduct);
  246.           } else {
  247.             this._productVariants.push(newProduct);
  248.           }
  249.         });
  250.         callback();
  251.  
  252.         // Clear requested products
  253.         setTimeout(
  254.           () => this.#_requested_products = this.#_requested_products.filter(pid => !productIds.includes(pid)),
  255.           1000
  256.         );
  257.       })
  258.       .catch(error => this.constructor.error(error));
  259.   }
  260.  
  261.   /**
  262.    * @param {number} productId
  263.    * @return {{id:number,title:string,price:string}[]|null}
  264.    */
  265.   getProductVariants (productId) {
  266.     let product = this._productVariants.find(p => p.id === productId);
  267.     return product ? product.variants : null;
  268.   }
  269.  
  270.   /**
  271.    * @param {boolean} delayed
  272.    */
  273.   #loadDiscounts (delayed = false) {
  274.     if (!delayed) {
  275.       axios.get(API.DISCOUNTS)
  276.         .then(response => {
  277.           this._discounts = response.data.map(d => {
  278.             return new Discount(d, this.shop.timezone);
  279.           });
  280.         })
  281.         .catch(error => this.constructor.error(error));
  282.     }
  283.     if (this.#updateDiscountsTimeout) {
  284.       clearTimeout(this.#updateDiscountsTimeout);
  285.     }
  286.     this.#updateDiscountsTimeout = setTimeout(
  287.       () => this.#loadDiscounts(),
  288.       5000
  289.     );
  290.   }
  291.  
  292.   /**
  293.    * @type {boolean}
  294.    */
  295.   static #reloading = false;
  296.  
  297.   /**
  298.    * @param {Error} error
  299.    */
  300.   static error (error) {
  301.     if (error.response && error.response.status && error.response.status === 401) {
  302.       if (!this.#reloading) {
  303.         this.#reloading = true;
  304.         console.warn('Session lost, reloading...');
  305.         reloadTop();
  306.       }
  307.       return;
  308.     }
  309.     console.error(error.message);
  310.   }
  311. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top