UNPKG

@eastsideco/escshopify

Version:

WIP JS library for Shopify, containing a variety of useful functionality.

340 lines (294 loc) 8.65 kB
import Evee from 'evee'; import Queue from 'promise-queue'; import deepEqual from 'deep-equal'; import _ from 'lodash'; import config from 'config'; import log from 'log'; import ajaxApi from 'http/shopifyAJAXAPI'; const TAG = 'Cart'; /** * @typedef {Object} ShopifyCartLineItem * @property {Number} id * @property {Object|null} properties * @property {Number} quantity * @property {Number} variant_id * @property {String} key * @property {String} title * @property {Number} price * @property {Number} original_price * @property {Number} discounted_price * @property {Number} original_line_price * @property {Number} line_price * @property {Number} total_discount * @property {Object[]} discounts * @property {String|null} sku * @property {Number} grams * @property {String} vendor * @property {Boolean} taxable * @property {Number} product_id * @property {Boolean} gift_card * @property {String} url * @property {String|null} image * @property {String} handle * @property {Boolean} requires_shipping * @property {String|null} product_type * @property {String} product_title * @property {String|null} product_description * @property {String} variant_title * @property {String[]} variant_options */ /** * @typedef {Object} ShopifyCart * @property {String} token * @property {String} note * @property {Object} attributes * @property {Number} total_price * @property {Number} total_weight * @property {ShopifyCartLineItem[]} items * @property {Boolean} requires_shipping */ /** * Shopify Cart entity. * @extends {evee} */ export default class Cart extends Evee { /** * Construct a new instance of the cart entity. */ constructor() { super(); /** @type {promise-queue} */ this._queue = new Queue(1, Infinity); this._attributes = {}; this._ready = false; /** @type {Object[]} */ this._items = []; this._attributes = {}; } /** * Initialize the cart entity with the current state of the visitor's cart. * @param {ShopifyCart} cartData - Shopify Cart object. */ initialize(cartData) { this._loadFromShopifyCart(cartData); this._emitUpdate('init', null); if (this.items.length == 0) { this._emitClear([]); } log.send(log.DEBUG, TAG, 'Loaded.'); } /** * Alias to getItems(); * @type {ShopifyCartLineItem[]} */ get items() { return this.getItems(); } /** * Returns items in the cart (in the normal Shopify line item format. * @returns {ShopifyCartLineItem[]} */ getItems() { return this._items; } /** * Alias to getTotalPrice(); * @type {Number} */ get total_price() { return this.getTotalPrice(); } /** * Alias to getTotalPrice(); * @type {Number} */ get totalPrice() { return this.getTotalPrice(); } /** * Calculates the total value of the cart. * @returns {Number} */ getTotalPrice() { return _.reduce(this.items, (total, item) => { return total + item.line_price; }, 0) } /** * Reloads the cart state via AJAX. * @returns {Promise<src/entities/Cart.js~Cart, Error>} */ async reload() { var cartData = await ajaxApi.get('/cart.js'); this._loadFromShopifyCart(cartData.data); return this; } /** * Alias to getAttributes() * @type {Object} */ get attributes() { return this.getAttributes(); } /** * Returns cart attributes. * @returns {Object} */ getAttributes() { return this._attributes; } /** * Sets all attributes on the cart. This will override/remove existing attributes. * @param {Object} attributes - New attributes for the cart. * @returns {Promise<src/entities/Cart.js~Cart, Error>} */ setAttributes(attributes) { log.sendObject(log.DEBUG, TAG, 'Setting attributes...', { attributes }); return this._queue.add(async () => { var cart = await ajaxApi.post({attributes}); this._loadFromShopifyCart(cart.data); this._emitUpdate('attribute-updated', null); }); } getAttribute(key) { return this._attributes[key]; } setAttribute(key, value) { log.sendObject(log.DEBUG, TAG, 'Setting attribute...', { key, value }); return this._queue.add(async () => { var newAttributes = Object.assign({}, this._attributes); newAttributes[key] = value; var cart = await ajaxApi.post({attributes: newAttributes}); this._loadFromShopifyCart(cart.data); this._emitUpdate('attribute-updated', null); }); } /** * Add a new item to the cart. * @param {Number|String} id - Variant ID to add to cart. * @param {Number} quantity - Quantity to add. * @param {Object} properties - Line item attributes. * @returns {Promise<ShopifyCartLineItem, Error>} */ addItem(id, quantity, properties) { log.sendObject(log.DEBUG, TAG, 'Queuing addItem...', { id, quantity, properties }); return this._queue.add(async () => { var res = await ajaxApi.post('/cart/add.js', { id, quantity, properties }); var data = res.data; log.sendObject(log.DEBUG, TAG, 'addItem resonse', data); if (data.id) { var item = this._loadItem(data); this._emitAdd(item); this._emitUpdate('add', item); return item; } throw new Error(data); }); } updateItem(lineNumber, quantity, properties) { return this._queue.add(() => { return new Promise((acc, rej) => { }); }); } removeItem(lineNumber) { return this._queue.add(() => { return new Promise((acc, rej) => { }); }); } updateItemById(id, quantity, properties) { return this._queue.add(() => { return new Promise((acc, rej) => { }); }); } updateItemById(id, quantity, properties) { return this._queue.add(() => { return new Promise((acc, rej) => { }); }); } updateItemQuantitiesById(updates) { return this._queue.add(() => { return new Promise((acc, rej) => { }); }); } removeItemById(id) { return this._queue.add(() => { return new Promise((acc, rej) => { }); }); } /** * Clear the cart. * @returns {Promise<src/entities/Cart.js~Cart, Error>} */ clear() { return this._queue.add(async () => { var cartData = await ajaxApi.post('/cart/clear.js'); var oldItems = this.items; this._loadFromShopifyCart(cartData); this._emitClear(oldItems); }); } _loadItem(itemData) { for (var item of this.items) { if (item.id == itemData.id && deepEqual(item.properties, itemData.properties)) { item.quantity = itemData.quantity; return item; } } this.items.push(itemData); return itemData; } _emitUpdate(operation, item) { var data ={ items: this.items, operation, item }; log.sendObject(log.DEBUG, TAG, 'Event: update', data); this.emit('update', data); } _emitClear(oldItems) { var data ={ oldItems }; log.sendObject(log.DEBUG, TAG, 'Event: clear', data); this.emit('clear', data); } _emitAdd(item) { var data ={ items: this.items, item }; log.sendObject(log.DEBUG, TAG, 'Event: add', data); this.emit('add', data); } _emitUpdateItem(item) { var data ={ items: this.items, item }; log.sendObject(log.DEBUG, TAG, 'Event: updae-item', data); this.emit('update-item', data); } _emitRemove(item) { var data = { items: this.items, item }; log.sendObject(log.DEBUG, TAG, 'Event: remove', data); this.emit('clear', data); } _loadFromShopifyCart(cart) { this._items = cart.items || []; } }