1/* eslint-disable import/no-cycle */ 2import { getConfigValue, CARTID_STORE } from '../configs.js'; 3 4class Store { 5 constructor(key = Store.CART_STORE) { 6 this.subscribers = []; 7 this.key = key; 8 this.type = 'guest'; 9 this.cartId = Store.getCartIdFromLocalStorage(); 10 this.newCartItem = null; 11 } 12 13 static CART_STORE = 'COMMERCE_CART_CACHE'; 14 15 static COOKIE_SESSION = 'COMMERCE_SESSION'; 16 17 static COOKIE_CART_ID = 'COMMERCE_CART_ID'; 18 19 static COOKIE_EXPIRATION_DAYS = 30; 20 21 static DEFAULT_CART = { 22 cart: {}, 23 id: null, 24 items_qty: 0, 25 totals: {}, 26 }; 27 28 static getCartIdFromLocalStorage() { 29 const cartIdField = window.localStorage.getItem(CARTID_STORE); 30 if (!cartIdField) { 31 return null; 32 } 33 try { 34 const parsed = JSON.parse(cartIdField); 35 if (Store.isExpired(parsed.expiryTime)) { 36 Store.deleteExpiredCart(parsed.value); 37 return null; 38 } 39 return parsed.value.replaceAll('"', ''); 40 } catch (err) { 41 console.error('Could not parse cartId', err); 42 return null; 43 } 44 } 45 46 static async saveCartIdInLocalStorage(cartId) { 47 window.localStorage.setItem(CARTID_STORE, JSON.stringify({ 48 value: `"${cartId}"`, 49 timeStored: Date.now(), 50 expiryTime: await Store.getExpiryTimestamp(), 51 })); 52 } 53 54 static getCookie(key) { 55 return document.cookie 56 .split(';') 57 .map((c) => c.trim()) 58 .filter((cookie) => cookie.startsWith(`${key}=`))
59 .map((cookie) => decodeURIComponent(cookie.split('=')[1]))[0] || null; 60 } 61 62 static setCookie(key, value) { 63 const expires = new Date(Date.now() + Store.COOKIE_EXPIRATION_DAYS * 864e5).toUTCString(); 64 document.cookie = `${key}=${encodeURIComponent(value)}; expires=${expires}; path=/`; 65 } 66 67 async setCartId(cartId) { 68 this.cartId = cartId; 69 await Store.saveCartIdInLocalStorage(cartId); 70 this.setCart({ 71 ...this.getCart(), 72 id: cartId, 73 }); 74 } 75 76 getCartId() { 77 return this.cartId; 78 } 79 80 setCart(cart) { 81 // Only store cart with proper id 82 if (!cart.id) { 83 return; 84 } 85 86 window.localStorage.setItem(`${this.key}_${this.cartId}`, JSON.stringify(cart)); 87 88 this.subscribers.forEach((callback) => { 89 callback(cart); 90 }); 91 } 92 93 getCart() { 94 this.cartId = Store.getCartIdFromLocalStorage(); 95 if (!this.cartId) { 96 return Store.DEFAULT_CART; 97 } 98 try { 99 const parsed = JSON.parse(window.localStorage.getItem(`${this.key}_${this.cartId}`)) || Store.DEFAULT_CART; 100 if (Store.isExpired(parsed.expiryTime)) { 101 Store.deleteExpiredCart(parsed.value); 102 return Store.DEFAULT_CART; 103 } 104 return parsed; 105 } catch (err) { 106 console.error('Failed to parse cart from localStore. Resetting it.'); 107 window.localStorage.removeItem(`${this.key}_${this.cartId}`); 108 } 109 return Store.DEFAULT_CART; 110 } 111 112 resetCart() { 113 window.localStorage.removeItem(`${this.key}_${this.cartId}`); 114 this.cartId = null; 115 } 116 117 subscribe(callback) { 118 this.subscribers.push(callback); 119 callback(this.getCart()); 120 } 121 122 setNewCartItem(data) { 123 this.newCartItem = data; 124 } 125
126 getNewCartItem() { 127 return this.newCartItem; 128 } 129 130 static async getExpiryTimestamp() { 131 const guestCartExpiryDays = await getConfigValue('guest-cart-expiry-days') || Store.COOKIE_EXPIRATION_DAYS; 132 const now = new Date(); 133 const expiryDate = new Date(now.getTime() + guestCartExpiryDays * 864e5); 134 return expiryDate.getTime(); 135 } 136 137 static isExpired(expiryTime) { 138 const now = new Date(); 139 return now.getTime() > expiryTime; 140 } 141 142 static deleteExpiredCart(cartIdAsString) { 143 window.localStorage.removeItem(CARTID_STORE); 144 // remove starting and trailing quotes from the cartId 145 const cartId = cartIdAsString?.replaceAll('"', ''); 146 window.localStorage.removeItem(`${Store.CART_STORE}_${cartId}`); 147 } 148} 149 150export const store = new Store(); 151 152export const cartApi = { 153 addToCart: async (sku, options, quantity, source = 'product-detail') => { 154 const { addToCart, createCart } = await import('./cart.js'); 155 // const { showCart } = await import('./Minicart.js'); // TODO: add as part of mini cart story 156 if (!store.getCartId()) { 157 console.debug('Cannot add item to cart, need to create a new cart first.'); 158 await createCart(); 159 } 160 const response = await addToCart(sku, options, quantity, source); 161 // showCart(); // TODO: add as part of mini cart story 162 return response; 163 }, 164 toggleCart: async () => { 165 const { toggle } = await import('./Minicart.js'); 166 toggle(); 167 }, 168 cartItemsQuantity: { 169 watch: (callback) => { 170 store.subscribe((cart) => { 171 const isCodSurchargeApplied = cart?.cart?.extension_attributes?.surcharge?.is_applied; 172 const codSurchargeAmt = cart?.cart?.extension_attributes?.surcharge?.amount; 173 const baseGrandTotal = cart?.totals?.base_grand_total; 174 const grandTotal = isCodSurchargeApplied && codSurchargeAmt 175 ? baseGrandTotal - codSurchargeAmt 176 : baseGrandTotal; 177 const deliveryAmountFee = cart?.totals?.shipping_incl_tax; 178 const grandTotalWithoutCharge = grandTotal - deliveryAmountFee; 179 const itemsLength = cart?.cart?.items?.length || 0; 180 // Workaround for CT sample products. 181 if (itemsLength === 0 && grandTotalWithoutCharge === 0.01) { 182 callback(itemsLength, 0, cart?.totals?.base_currency_code || 'AED'); 183 } else { 184 callback(cart.cart?.items_qty || 0, grandTotalWithoutCharge || 0, cart?.totals?.base_currency_code || 'AED'); 185 } 186 }); 187 }, 188 }, 189};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.