UNPKG

@shopgate/pwa-common-commerce

Version:

Commerce library for the Shopgate Connect PWA.

215 lines (210 loc) • 6.94 kB
import "core-js/modules/es.regexp.flags.js"; import { hex2bin } from '@shopgate/pwa-common/helpers/data'; import showModal from '@shopgate/pwa-common/actions/modal/showModal'; import { getThemeSettings, historyPop, historyPush, routeWillEnter$, routeWillLeave$ } from '@shopgate/engage/core'; import { ToastProvider } from '@shopgate/pwa-common/providers'; import { getSearchRoute } from '@shopgate/pwa-common-commerce/search'; import fetchProduct from "../actions/fetchProduct"; import fetchProductDescription from "../actions/fetchProductDescription"; import fetchProductProperties from "../actions/fetchProductProperties"; import fetchProductImages from "../actions/fetchProductImages"; import fetchProductShipping from "../actions/fetchProductShipping"; import fetchProductVariants from "../actions/fetchProductVariants"; import fetchProductOptions from "../actions/fetchProductOptions"; import fetchProductMedia from "../actions/fetchProductMedia"; import { productWillEnter$, galleryWillEnter$, productReceived$, cachedProductReceived$, productRelationsReceived$, receivedVisibleProduct$, errorProductResourcesNotFound$, visibleProductNotFound$, productNotAvailable$, fetchProductsRequested$, pdpDataNeedsRefresh$ } from "../streams"; import fetchProductsById from "../actions/fetchProductsById"; import { getProductRelationsByHash } from "../selectors/relations"; import { checkoutSucceeded$ } from "../../checkout/streams"; import expireProductById from "../action-creators/expireProductById"; import { ITEM_PATTERN, NOT_AVAILABLE_EFFECTIVITY_DATES } from "../constants"; import { getProductName } from "../selectors/product"; const fetchPDPData$ = productWillEnter$.merge(pdpDataNeedsRefresh$); /** * Product subscriptions. * @param {Function} subscribe The subscribe function. */ function product(subscribe) { const processProduct$ = productReceived$.merge(cachedProductReceived$); subscribe(fetchPDPData$, ({ action, dispatch }) => { const { productId } = action.route.params; const { productId: variantId } = action.route.state; const id = variantId || hex2bin(productId); dispatch(fetchProduct(id)); dispatch(fetchProductDescription(id)); dispatch(fetchProductProperties(id)); dispatch(fetchProductImages(id)); dispatch(fetchProductShipping(id)); /** * This feature is currently in BETA testing. * It should only be used for approved BETA Client Projects */ dispatch(fetchProductMedia(id)); }); subscribe(galleryWillEnter$, ({ action, dispatch }) => { const { productId } = action.route.params; dispatch(fetchProductImages(hex2bin(productId))); dispatch(fetchProductMedia(hex2bin(productId))); }); subscribe(processProduct$, ({ action, dispatch }) => { const { id, flags = { hasVariants: false, hasOptions: false }, baseProductId, active } = action.productData; if (baseProductId) { dispatch(fetchProduct(baseProductId)); dispatch(fetchProductImages(baseProductId)); } if (active && flags.hasVariants) { dispatch(fetchProductVariants(id)); } if (active && flags.hasOptions) { dispatch(fetchProductOptions(id)); } }); const errorProductResourcesNotFoundFirst$ = receivedVisibleProduct$.filter(({ action }) => { const active = action?.productData?.active; return !!active; }).switchMap(() => errorProductResourcesNotFound$.first()); /** Refresh product data after some of resources has ENOTFOUND code */ subscribe(errorProductResourcesNotFoundFirst$, ({ action, dispatch }) => { const { productId } = action; dispatch(fetchProduct(productId, true)); }); const productNotFound$ = visibleProductNotFound$.withLatestFrom(receivedVisibleProduct$.startWith({ action: { productData: {} } })); /** Visible product is no more available */ subscribe(productNotFound$, ([{ action, dispatch }, { action: { productData } }]) => { const { productId } = action; const name = productData.id === productId ? productData.name : productId; dispatch(showModal({ confirm: null, dismiss: 'modal.ok', title: 'modal.title_error', message: 'product.no_more_found', params: { name } })); dispatch(historyPop()); dispatch(expireProductById(productId)); }); subscribe(productRelationsReceived$, ({ dispatch, getState, action }) => { const { hash } = action; const productIds = getProductRelationsByHash(hash)(getState()); dispatch(fetchProductsById(productIds)); }); /** * Expire products after checkout, fetch updated data */ subscribe(checkoutSucceeded$, ({ dispatch, action }) => { const { products } = action; const productIds = products.map(p => p.product.id); productIds.forEach(id => dispatch(expireProductById(id))); dispatch(fetchProductsById(productIds)); }); const productNotAvailableEffDates$ = productNotAvailable$.filter(({ action }) => action.reason === NOT_AVAILABLE_EFFECTIVITY_DATES); /** PDP expired effectivity dates */ const productNotAvailableEffDatesPDP$ = productNotAvailableEffDates$.withLatestFrom(routeWillEnter$).filter(([notAvailable, willEnter]) => willEnter.action.route.pattern === ITEM_PATTERN && notAvailable.action.productId === willEnter.action.route.state.productId).map(([notAvailable]) => notAvailable); subscribe(productNotAvailableEffDatesPDP$, ({ action, getState, dispatch, events }) => { const { effectivityDates: { accessExpired } = {} } = getThemeSettings('product') || {}; if (accessExpired === false) { const { productId } = action; dispatch(historyPop()); const name = getProductName(getState(), { productId }); events.emit(ToastProvider.ADD, { id: 'product.available.not_search_similar', message: 'product.available.not_search_similar', messageParams: { name }, action: () => dispatch(historyPush({ pathname: getSearchRoute(name) })) }); } }); /** Expired effectivity dates products after PLP leave */ const productNotAvailableEffDatesPLP$ = productNotAvailableEffDates$.buffer(routeWillLeave$).filter(buffered => buffered.length); subscribe(productNotAvailableEffDatesPLP$, buffered => { const [{ dispatch }] = buffered; const productIds = buffered.map(params => params.action.productId); dispatch(expireProductById(productIds, true)); }); subscribe(fetchProductsRequested$, ({ dispatch, action: { productId } }) => { dispatch(fetchProductsById(Array.isArray(productId) ? productId : [productId])); }); } export default product;