UNPKG

@shopify/hydrogen-react

Version:

React components, hooks, and utilities for creating custom Shopify storefronts

399 lines (398 loc) • 14.5 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const jsxRuntime = require("react/jsx-runtime"); const React = require("react"); const useCartAPIStateMachine = require("./useCartAPIStateMachine.js"); const cartConstants = require("./cart-constants.js"); const cartQueries = require("./cart-queries.js"); const ShopifyProvider = require("./ShopifyProvider.js"); const CartContext = React.createContext(null); function useCart() { const context = React.useContext(CartContext); if (!context) { throw new Error("Expected a Cart Context, but no Cart Context was found"); } return context; } function CartProvider({ children, numCartLines, onCreate, onLineAdd, onLineRemove, onLineUpdate, onNoteUpdate, onBuyerIdentityUpdate, onAttributesUpdate, onDiscountCodesUpdate, onCreateComplete, onLineAddComplete, onLineRemoveComplete, onLineUpdateComplete, onNoteUpdateComplete, onBuyerIdentityUpdateComplete, onAttributesUpdateComplete, onDiscountCodesUpdateComplete, data: cart, cartFragment = cartQueries.defaultCartFragment, customerAccessToken, countryCode, languageCode }) { var _a, _b, _c, _d, _e, _f, _g; const shop = ShopifyProvider.useShop(); if (!shop) throw new Error( "<CartProvider> needs to be a descendant of <ShopifyProvider>" ); countryCode = (countryCode ?? shop.countryIsoCode ?? "US").toUpperCase(); languageCode = (languageCode ?? shop.languageIsoCode ?? "EN").toUpperCase(); if (countryCode) countryCode = countryCode.toUpperCase(); const [prevCountryCode, setPrevCountryCode] = React.useState(countryCode); const [prevCustomerAccessToken, setPrevCustomerAccessToken] = React.useState(customerAccessToken); const customerOverridesCountryCode = React.useRef(false); if (prevCountryCode !== countryCode || prevCustomerAccessToken !== customerAccessToken) { setPrevCountryCode(countryCode); setPrevCustomerAccessToken(customerAccessToken); customerOverridesCountryCode.current = false; } const [cartState, cartSend] = useCartAPIStateMachine.useCartAPIStateMachine({ numCartLines, data: cart, cartFragment, countryCode, languageCode, onCartActionEntry(_, event) { try { switch (event.type) { case "CART_CREATE": return onCreate == null ? void 0 : onCreate(); case "CARTLINE_ADD": return onLineAdd == null ? void 0 : onLineAdd(); case "CARTLINE_REMOVE": return onLineRemove == null ? void 0 : onLineRemove(); case "CARTLINE_UPDATE": return onLineUpdate == null ? void 0 : onLineUpdate(); case "NOTE_UPDATE": return onNoteUpdate == null ? void 0 : onNoteUpdate(); case "BUYER_IDENTITY_UPDATE": return onBuyerIdentityUpdate == null ? void 0 : onBuyerIdentityUpdate(); case "CART_ATTRIBUTES_UPDATE": return onAttributesUpdate == null ? void 0 : onAttributesUpdate(); case "DISCOUNT_CODES_UPDATE": return onDiscountCodesUpdate == null ? void 0 : onDiscountCodesUpdate(); } } catch (error) { console.error("Cart entry action failed", error); } }, onCartActionOptimisticUI(context, event) { var _a2, _b2, _c2, _d2; if (!context.cart) return { ...context }; switch (event.type) { case "CARTLINE_REMOVE": return { ...context, cart: { ...context.cart, lines: (_b2 = (_a2 = context == null ? void 0 : context.cart) == null ? void 0 : _a2.lines) == null ? void 0 : _b2.filter( (line) => (line == null ? void 0 : line.id) && !event.payload.lines.includes(line == null ? void 0 : line.id) ) } }; case "CARTLINE_UPDATE": return { ...context, cart: { ...context.cart, lines: (_d2 = (_c2 = context == null ? void 0 : context.cart) == null ? void 0 : _c2.lines) == null ? void 0 : _d2.map((line) => { const updatedLine = event.payload.lines.find( ({ id }) => id === (line == null ? void 0 : line.id) ); if (updatedLine && updatedLine.quantity) { return { ...line, quantity: updatedLine.quantity }; } return line; }) } }; } return { ...context }; }, onCartActionComplete(context, event) { const cartActionEvent = event.payload.cartActionEvent; try { switch (event.type) { case "RESOLVE": switch (cartActionEvent.type) { case "CART_CREATE": return onCreateComplete == null ? void 0 : onCreateComplete(); case "CARTLINE_ADD": return onLineAddComplete == null ? void 0 : onLineAddComplete(); case "CARTLINE_REMOVE": return onLineRemoveComplete == null ? void 0 : onLineRemoveComplete(); case "CARTLINE_UPDATE": return onLineUpdateComplete == null ? void 0 : onLineUpdateComplete(); case "NOTE_UPDATE": return onNoteUpdateComplete == null ? void 0 : onNoteUpdateComplete(); case "BUYER_IDENTITY_UPDATE": if (countryCodeNotUpdated(context, cartActionEvent)) { customerOverridesCountryCode.current = true; } return onBuyerIdentityUpdateComplete == null ? void 0 : onBuyerIdentityUpdateComplete(); case "CART_ATTRIBUTES_UPDATE": return onAttributesUpdateComplete == null ? void 0 : onAttributesUpdateComplete(); case "DISCOUNT_CODES_UPDATE": return onDiscountCodesUpdateComplete == null ? void 0 : onDiscountCodesUpdateComplete(); } } } catch (error) { console.error("onCartActionComplete failed", error); } } }); const cartReady = React.useRef(false); const [isCartReady, setIsCartReady] = React.useState(false); const cartCompleted = cartState.matches("cartCompleted"); const countryChanged = (cartState.value === "idle" || cartState.value === "error" || cartState.value === "cartCompleted") && countryCode !== ((_c = (_b = (_a = cartState == null ? void 0 : cartState.context) == null ? void 0 : _a.cart) == null ? void 0 : _b.buyerIdentity) == null ? void 0 : _c.countryCode) && !cartState.context.errors; const fetchingFromStorage = React.useRef(false); React.useEffect(() => { if (!cartReady.current && !fetchingFromStorage.current) { if (!cart && storageAvailable("localStorage")) { fetchingFromStorage.current = true; try { const cartId = window.localStorage.getItem(cartConstants.CART_ID_STORAGE_KEY); if (cartId) { cartSend({ type: "CART_FETCH", payload: { cartId } }); } } catch (error) { console.warn("error fetching cartId"); console.warn(error); } } cartReady.current = true; setIsCartReady(true); } }, [cart, cartReady, cartSend]); React.useEffect(() => { if (!countryChanged || customerOverridesCountryCode.current) return; cartSend({ type: "BUYER_IDENTITY_UPDATE", payload: { buyerIdentity: { countryCode, customerAccessToken } } }); }, [ countryCode, customerAccessToken, countryChanged, customerOverridesCountryCode, cartSend ]); const onCartReadySend = React.useCallback( (cartEvent) => { if (!cartReady.current) { return console.warn("Cart isn't ready yet"); } cartSend(cartEvent); }, [cartSend] ); React.useEffect(() => { var _a2, _b2, _c2; if (((_b2 = (_a2 = cartState == null ? void 0 : cartState.context) == null ? void 0 : _a2.cart) == null ? void 0 : _b2.id) && storageAvailable("localStorage")) { try { window.localStorage.setItem( cartConstants.CART_ID_STORAGE_KEY, (_c2 = cartState.context.cart) == null ? void 0 : _c2.id ); } catch (error) { console.warn("Failed to save cartId to localStorage", error); } } }, [(_e = (_d = cartState == null ? void 0 : cartState.context) == null ? void 0 : _d.cart) == null ? void 0 : _e.id]); React.useEffect(() => { if (cartCompleted && storageAvailable("localStorage")) { try { window.localStorage.removeItem(cartConstants.CART_ID_STORAGE_KEY); } catch (error) { console.warn("Failed to delete cartId from localStorage", error); } } }, [cartCompleted]); const cartCreate = React.useCallback( (cartInput) => { var _a2, _b2; if (countryCode && !((_a2 = cartInput.buyerIdentity) == null ? void 0 : _a2.countryCode)) { if (cartInput.buyerIdentity == null) { cartInput.buyerIdentity = {}; } cartInput.buyerIdentity.countryCode = countryCode; } if (customerAccessToken && !((_b2 = cartInput.buyerIdentity) == null ? void 0 : _b2.customerAccessToken)) { if (cartInput.buyerIdentity == null) { cartInput.buyerIdentity = {}; } cartInput.buyerIdentity.customerAccessToken = customerAccessToken; } onCartReadySend({ type: "CART_CREATE", payload: cartInput }); }, [countryCode, customerAccessToken, onCartReadySend] ); const cartDisplayState = useDelayedStateUntilHydration(cartState); const cartContextValue = React.useMemo(() => { var _a2, _b2, _c2, _d2; return { ...((_a2 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _a2.cart) ?? { lines: [], attributes: [] }, status: transposeStatus(cartDisplayState.value), error: (_b2 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _b2.errors, totalQuantity: ((_d2 = (_c2 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _c2.cart) == null ? void 0 : _d2.totalQuantity) ?? 0, cartCreate, cartReady: isCartReady, linesAdd(lines) { var _a3, _b3; if ((_b3 = (_a3 = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _a3.cart) == null ? void 0 : _b3.id) { onCartReadySend({ type: "CARTLINE_ADD", payload: { lines } }); } else { cartCreate({ lines }); } }, linesRemove(lines) { onCartReadySend({ type: "CARTLINE_REMOVE", payload: { lines } }); }, linesUpdate(lines) { onCartReadySend({ type: "CARTLINE_UPDATE", payload: { lines } }); }, noteUpdate(note) { onCartReadySend({ type: "NOTE_UPDATE", payload: { note } }); }, buyerIdentityUpdate(buyerIdentity) { onCartReadySend({ type: "BUYER_IDENTITY_UPDATE", payload: { buyerIdentity } }); }, cartAttributesUpdate(attributes) { onCartReadySend({ type: "CART_ATTRIBUTES_UPDATE", payload: { attributes } }); }, discountCodesUpdate(discountCodes) { onCartReadySend({ type: "DISCOUNT_CODES_UPDATE", payload: { discountCodes } }); }, cartFragment }; }, [ cartCreate, isCartReady, (_f = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _f.cart, (_g = cartDisplayState == null ? void 0 : cartDisplayState.context) == null ? void 0 : _g.errors, cartDisplayState.value, cartFragment, onCartReadySend ]); return /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, { value: cartContextValue, children }); } function transposeStatus(status) { switch (status) { case "uninitialized": case "initializationError": return "uninitialized"; case "idle": case "cartCompleted": case "error": return "idle"; case "cartFetching": return "fetching"; case "cartCreating": return "creating"; case "cartLineAdding": case "cartLineRemoving": case "cartLineUpdating": case "noteUpdating": case "buyerIdentityUpdating": case "cartAttributesUpdating": case "discountCodesUpdating": return "updating"; } } function useDelayedStateUntilHydration(state) { const [isPending, startTransition] = React.useTransition(); const [delayedState, setDelayedState] = React.useState(state); const firstTimePending = React.useRef(false); if (isPending) { firstTimePending.current = true; } const firstTimePendingFinished = React.useRef(false); if (!isPending && firstTimePending.current) { firstTimePendingFinished.current = true; } React.useEffect(() => { startTransition(() => { if (!firstTimePendingFinished.current) { setDelayedState(state); } }); }, [state]); const displayState = firstTimePendingFinished.current ? state : delayedState; return displayState; } function storageAvailable(type) { let storage; try { storage = window[type]; const x = "__storage_test__"; storage.setItem(x, x); storage.removeItem(x); return true; } catch (e) { return !!(e instanceof DOMException && // everything except Firefox (e.code === 22 || // Firefox e.code === 1014 || // test name field too, because code might not be present // everything except Firefox e.name === "QuotaExceededError" || // Firefox e.name === "NS_ERROR_DOM_QUOTA_REACHED") && // acknowledge QuotaExceededError only if there's something already stored storage && storage.length !== 0); } } function countryCodeNotUpdated(context, event) { var _a, _b; return !!(event.payload.buyerIdentity.countryCode && ((_b = (_a = context.cart) == null ? void 0 : _a.buyerIdentity) == null ? void 0 : _b.countryCode) !== event.payload.buyerIdentity.countryCode); } exports.CartContext = CartContext; exports.CartProvider = CartProvider; exports.storageAvailable = storageAvailable; exports.useCart = useCart; //# sourceMappingURL=CartProvider.js.map