UNPKG

@automattic/shopping-cart

Version:
146 lines (135 loc) 4.63 kB
import debugFactory from 'debug'; import { convertTempResponseCartToResponseCart } from './cart-functions'; import { getEmptyResponseCart } from './empty-carts'; import type { ShoppingCartManager, ShoppingCartState, SavedActionPromise, ShoppingCartManagerState, ShoppingCartManagerGetState, SubscribeCallback, UnsubscribeFunction, SubscriptionManager, ActionPromises, ShoppingCartManagerActions, CartKey, } from './types'; const debug = debugFactory( 'shopping-cart:managers' ); export function getShoppingCartManagerState( state: ShoppingCartState ): ShoppingCartManagerState { const { cacheStatus, queuedActions, couponStatus, loadingErrorType, loadingError, lastValidResponseCart, } = state; const isLoading = cacheStatus === 'fresh' || cacheStatus === 'fresh-pending'; const isPendingUpdate = queuedActions.length > 0 || cacheStatus !== 'valid'; const loadingErrorForManager = cacheStatus === 'error' ? loadingError : null; debug( 'manager isLoading', isLoading, 'isPendingUpdate', isPendingUpdate, 'loadingError', loadingErrorForManager ); return { isLoading, loadingError: loadingErrorForManager, loadingErrorType, isPendingUpdate, couponStatus, responseCart: lastValidResponseCart, }; } export function createSubscriptionManager( cartKey: CartKey | undefined ): SubscriptionManager { let subscribedClients: SubscribeCallback[] = []; const subscribe = ( callback: SubscribeCallback ): UnsubscribeFunction => { debug( `adding subscriber for cartKey ${ cartKey }` ); subscribedClients.push( callback ); return () => { debug( `removing subscriber for cartKey ${ cartKey }` ); subscribedClients = subscribedClients.filter( ( prevCallback ) => prevCallback !== callback ); }; }; const notifySubscribers = () => { debug( `notifying ${ subscribedClients.length } subscribers for cartKey ${ cartKey }` ); subscribedClients.forEach( ( clientCallback ) => { try { clientCallback(); } catch ( err ) { // eslint-disable-next-line no-console console.error( 'An error ocurred while notifying a subscriber of a ShoppingCartManager change', ( err as Error ).message ); throw err; } } ); debug( `completed notification of subscribers for cartKey ${ cartKey }` ); }; return { subscribe, notifySubscribers }; } /** * Create an object that manages the Promises returned by cart actions. * * When an action is requested, we create a Promise for that action dispatcher * to return and store it in this object. Later when we know if the action * resulted in a success or a failure, the resolve or reject methods are called * on this object; those methods then resolve or reject each Promise that was * waiting for a response. */ export function createActionPromisesManager(): ActionPromises { let actionPromises: SavedActionPromise[] = []; return { resolve( tempResponseCart ) { if ( actionPromises.length > 0 ) { debug( `resolving ${ actionPromises.length } action promises` ); const responseCart = convertTempResponseCartToResponseCart( tempResponseCart ); actionPromises.forEach( ( actionPromise ) => actionPromise.resolve( responseCart ) ); actionPromises = []; } }, reject( error ) { if ( actionPromises.length > 0 ) { debug( `rejecting ${ actionPromises.length } action promises` ); actionPromises.forEach( ( actionPromise ) => actionPromise.reject( error ) ); actionPromises = []; } }, add( actionPromise: SavedActionPromise ) { actionPromises.push( actionPromise ); }, }; } const emptyCart = getEmptyResponseCart(); const noopCartAction = (): ReturnType< ShoppingCartManagerActions[ 'addProductsToCart' ] > => Promise.reject( 'Cart actions cannot be taken without a cart key.' ); const noopState: ShoppingCartManagerState = { isLoading: true, loadingError: undefined, loadingErrorType: undefined, isPendingUpdate: true, couponStatus: 'fresh', responseCart: emptyCart, }; const noopGetState: ShoppingCartManagerGetState = () => noopState; const noopActions: ShoppingCartManagerActions = { addProductsToCart: noopCartAction, removeProductFromCart: noopCartAction, applyCoupon: noopCartAction, removeCoupon: noopCartAction, updateLocation: noopCartAction, replaceProductInCart: noopCartAction, replaceProductsInCart: noopCartAction, reloadFromServer: () => Promise.resolve( emptyCart ), clearMessages: () => Promise.resolve( emptyCart ), }; export const noopManager: ShoppingCartManager = { actions: noopActions, getState: noopGetState, subscribe: () => () => null, fetchInitialCart: () => Promise.resolve( emptyCart ), };