UNPKG

@shopgate/pwa-common-commerce

Version:

Commerce library for the Shopgate Connect PWA.

48 lines • 14.7 kB
import _regeneratorRuntime from"@babel/runtime/regenerator";var _excluded=["coupon"];function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value;}catch(error){reject(error);return;}if(info.done){resolve(value);}else{Promise.resolve(value).then(_next,_throw);}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise(function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value);}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err);}_next(undefined);});};}function _objectWithoutProperties(source,excluded){if(source==null)return{};var target=_objectWithoutPropertiesLoose(source,excluded);var key,i;if(Object.getOwnPropertySymbols){var sourceSymbolKeys=Object.getOwnPropertySymbols(source);for(i=0;i<sourceSymbolKeys.length;i++){key=sourceSymbolKeys[i];if(excluded.indexOf(key)>=0)continue;if(!Object.prototype.propertyIsEnumerable.call(source,key))continue;target[key]=source[key];}}return target;}function _objectWithoutPropertiesLoose(source,excluded){if(source==null)return{};var target={};var sourceKeys=Object.keys(source);var key,i;for(i=0;i<sourceKeys.length;i++){key=sourceKeys[i];if(excluded.indexOf(key)>=0)continue;target[key]=source[key];}return target;}function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}import'rxjs/add/operator/debounceTime';import'rxjs/add/observable/of';import{Observable}from'rxjs/Observable';import event from'@shopgate/pwa-core/classes/Event';import pipelineDependencies from'@shopgate/pwa-core/classes/PipelineDependencies';import{redirects}from'@shopgate/pwa-common/collections';import{userDidUpdate$}from'@shopgate/pwa-common/streams/user';import{appWillStart$,appDidStart$}from'@shopgate/pwa-common/streams/app';import{parseObjectToQueryString}from'@shopgate/pwa-common/helpers/router';import fetchRegisterUrl from'@shopgate/pwa-common/actions/user/fetchRegisterUrl';import{LoadingProvider}from'@shopgate/pwa-common/providers';import{fetchProductsById,getProductRoute,hasProductVariety,getProduct}from'@shopgate/pwa-common-commerce/product';import{historyReplace}from'@shopgate/pwa-common/actions/router';import{checkoutSucceeded$}from'@shopgate/pwa-common-commerce/checkout';import{DIRECT_SHIP,ROPIS,BOPIS,getPreferredLocation,getPreferredFulfillmentMethod}from'@shopgate/engage/locations';import{makeGetEnabledFulfillmentMethods}from'@shopgate/engage/core/config';import{errorBehavior}from'@shopgate/engage/core';import{hasNewServices}from'@shopgate/engage/core/helpers';import*as pipelines from"../constants/Pipelines";import addCouponsToCart from"../actions/addCouponsToCart";import addProductsToCart from"../actions/addProductsToCart";import updateProductsInCart from"../actions/updateProductsInCart";import fetchCart from"../actions/fetchCart";import{cartDidEnter$,cartRequesting$,cartReceived$,productsAdded$,productsModified$,productsUpdated$,productsDeleted$,couponsAdded$,couponsUpdated$,couponsDeleted$,routeWithCouponWillEnter$,remoteCartDidUpdate$,cartUpdateFailed$,routeAddProductNavigate$,setFulfillmentSlot$,cartWillEnterIdle$}from"../streams";import setCartProductPendingCount from"../action-creators/setCartProductPendingCount";import{CART_PATH,DEEPLINK_CART_ADD_COUPON_PATTERN,DEEPLINK_CART_ADD_PRODUCT_PATTERN,CART_ITEM_TYPE_PRODUCT}from"../constants";import{getCartProducts}from"../selectors";/** * Cart subscriptions. * @param {Function} subscribe The subscribe function. */export default function cart(subscribe){/** * Gets triggered when ever the local cart is out of * sync with the remote cart from the server. */var cartNeedsSync$=userDidUpdate$.merge(remoteCartDidUpdate$,checkoutSucceeded$,cartWillEnterIdle$);/** * Gets triggered when the app is started or the cart route is entered. */var cartDidEnterOrAppDidStart$=cartDidEnter$.merge(appDidStart$);var cartBusy$=cartRequesting$.merge(couponsAdded$,couponsDeleted$,productsAdded$,productsModified$,productsDeleted$);var cartIdle$=cartReceived$.merge(couponsUpdated$,productsUpdated$);/** * Gets triggered when the app will start. */subscribe(appWillStart$,function(){// Use the redirect system to handle coupons from deep links. redirects.set(DEEPLINK_CART_ADD_COUPON_PATTERN,function(_ref){var dispatch=_ref.dispatch,action=_ref.action;var _action$params$pathna=action.params.pathname.split('/'),_action$params$pathna2=_slicedToArray(_action$params$pathna,3),coupon=_action$params$pathna2[2];if(coupon){dispatch(addCouponsToCart([coupon.split('?')[0]],false));}return null;});// This will be handled in 2 deferred subscriptions redirects.set(DEEPLINK_CART_ADD_PRODUCT_PATTERN,function(){return null;});});/** * Gets triggered when the app starts. */subscribe(appDidStart$,function(_ref2){var dispatch=_ref2.dispatch;pipelineDependencies.set(pipelines.SHOPGATE_CART_GET_CART,[pipelines.SHOPGATE_CART_ADD_PRODUCTS,pipelines.SHOPGATE_CART_UPDATE_PRODUCTS,pipelines.SHOPGATE_CART_DELETE_PRODUCTS,pipelines.SHOPGATE_CART_ADD_COUPONS,pipelines.SHOPGATE_CART_DELETE_COUPONS]);// Push (deeplink) with coupon concurrent to get cart on app start pipelineDependencies.set(pipelines.SHOPGATE_CART_ADD_COUPONS,[pipelines.SHOPGATE_CART_ADD_PRODUCTS,pipelines.SHOPGATE_CART_GET_CART]);pipelineDependencies.set(pipelines.SHOPGATE_CART_DELETE_COUPONS,[pipelines.SHOPGATE_CART_ADD_PRODUCTS,pipelines.SHOPGATE_CART_GET_CART]);/** * Reload the cart whenever the WebView becomes visible. * This is needed, for example, when the cart is modified from another inAppBrowser tab like a * web-checkout and the user closes the said tab before reaching the success page. */event.addCallback('viewWillAppear',function(){dispatch(fetchCart());});// Reset the productPendingCount on app start to avoid a wrong value in the cart badge. dispatch(setCartProductPendingCount(0));});subscribe(cartNeedsSync$,function(_ref3){var dispatch=_ref3.dispatch;dispatch(fetchCart());});subscribe(cartBusy$,function(){LoadingProvider.setLoading(CART_PATH);});subscribe(setFulfillmentSlot$,function(_ref4){var _action$fulfillmentSl;var dispatch=_ref4.dispatch,getState=_ref4.getState,action=_ref4.action;// When the fulfillment slot is set we need to update all line items. var products=getCartProducts(getState());if(!products.length||!(action===null||action===void 0?void 0:(_action$fulfillmentSl=action.fulfillmentSlot)===null||_action$fulfillmentSl===void 0?void 0:_action$fulfillmentSl.id)){return;}products=products.filter(function(product){var _product$fulfillment;return[ROPIS,BOPIS].includes(product===null||product===void 0?void 0:(_product$fulfillment=product.fulfillment)===null||_product$fulfillment===void 0?void 0:_product$fulfillment.method);}).map(function(product){return{cartItemId:product.id,fulfillment:{slotId:action.fulfillmentSlot.id}};});if(!products.length){return;}// Update the slot id for ROPE products. dispatch(updateProductsInCart(products));});subscribe(cartIdle$,function(_ref5){var dispatch=_ref5.dispatch,getState=_ref5.getState;/** Fetch missing products of a cart */var productIds=getCartProducts(getState()).map(function(cartItem){return cartItem.product.id;});if(productIds.length){dispatch(fetchProductsById(productIds));}LoadingProvider.resetLoading(CART_PATH);});subscribe(cartDidEnterOrAppDidStart$,function(_ref6){var dispatch=_ref6.dispatch;dispatch(fetchRegisterUrl())["catch"](function(e){return e;});});subscribe(cartUpdateFailed$,function(_ref7){var dispatch=_ref7.dispatch,action=_ref7.action;/** * @type {PipelineErrorElement[]} errors */var _action$errors=action.errors,errors=_action$errors===void 0?[]:_action$errors,_action$couponsIds=action.couponsIds,couponsIds=_action$couponsIds===void 0?[]:_action$couponsIds;if(Array.isArray(errors)&&errors.length){// Supports only one error, because none of the pipelines is ever called with multiple items. // Multiple errors would cause the this to overlay multiple modals on top of each other. var _errors$2=errors[0],message=_errors$2.message,handled=_errors$2.handled,code=_errors$2.code,additionalParams=_errors$2.additionalParams,translated=_errors$2.translated,context=_errors$2.context;// Some errors are already handled automatically before if(handled){return;}errorBehavior.modal()({dispatch:dispatch,error:{code:code,context:context,meta:_extends({message:message,additionalParams:additionalParams,translated:translated},Array.isArray(couponsIds)&&couponsIds.length>0?{input:{couponsIds:couponsIds}}:null)}});}});/** * Gets triggered when a route with a coupon within its GET parameters will enter. */subscribe(routeWithCouponWillEnter$,function(_ref8){var dispatch=_ref8.dispatch,action=_ref8.action;var coupon=action.route.query.coupon;if(coupon){dispatch(addCouponsToCart([coupon],false));var _action$route2=action.route,query=_action$route2.query,pathname=_action$route2.pathname;var c=query.coupon,rest=_objectWithoutProperties(query,_excluded);dispatch(historyReplace({pathname:"".concat(pathname).concat(parseObjectToQueryString(rest))}));}});var getFulfillmentMethods=makeGetEnabledFulfillmentMethods();/** * Deeplink to add product to cart, eg /cart_add_product/123s */subscribe(routeAddProductNavigate$,/*#__PURE__*/function(){var _ref10=_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref9){var dispatch,action,getState,state,redirectToPDP,fulfillment,product,preferredLocation,preferredFulfillmentMethod,shopFulfillmentMethods,activeLocation,activeFulfillmentMethod,availableFulfillmentMethods,_availableFulfillment;return _regeneratorRuntime.wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:dispatch=_ref9.dispatch,action=_ref9.action,getState=_ref9.getState;state=getState();redirectToPDP=false;fulfillment=null;if(hasProductVariety(state,action)){// Redirect to PDP when the product has variants redirectToPDP=true;}else if(hasNewServices()){product=getProduct(state,action);preferredLocation=getPreferredLocation(state);preferredFulfillmentMethod=getPreferredFulfillmentMethod(state);shopFulfillmentMethods=getFulfillmentMethods(state);if(preferredFulfillmentMethod!==DIRECT_SHIP){activeLocation=null;if(preferredLocation){activeLocation=preferredLocation;}// Get fulfillment method that is both active for location and product. activeFulfillmentMethod=preferredFulfillmentMethod;availableFulfillmentMethods=(shopFulfillmentMethods===null||shopFulfillmentMethods===void 0?void 0:shopFulfillmentMethods.filter(function(s){return((product===null||product===void 0?void 0:product.fulfillmentMethods)||[]).includes(s);}))||[];if(activeLocation&&!activeFulfillmentMethod&&availableFulfillmentMethods.length===1){_availableFulfillment=_slicedToArray(availableFulfillmentMethods,1);activeFulfillmentMethod=_availableFulfillment[0];}if(!(product===null||product===void 0?void 0:product.fulfillmentMethods.includes(activeFulfillmentMethod))||false){activeFulfillmentMethod=null;}if(!activeFulfillmentMethod||!activeLocation){redirectToPDP=true;}else{fulfillment={method:preferredFulfillmentMethod,location:{code:preferredLocation.code,name:preferredLocation.name||''}};}}}// NO product or variety if(redirectToPDP){// Redirect to item page to select options/variant dispatch(historyReplace({pathname:getProductRoute(action.productId)}));}else{dispatch(addProductsToCart([_extends({productId:action.productId,quantity:1},fulfillment?{fulfillment:fulfillment}:{})]));dispatch(historyReplace({pathname:CART_PATH}));}case 6:case"end":return _context.stop();}},_callee);}));return function(_x){return _ref10.apply(this,arguments);};}());/** * Deffer coupon adding to a cart, until we have the product from the push message inside cart. * * Logic was implemented for PWA 6 in ticket no PWA-1809. */var addCouponDeferred$=routeAddProductNavigate$// Only if coupon is given .filter(function(_ref11){var _ref11$action$couponC=_ref11.action.couponCode,couponCode=_ref11$action$couponC===void 0?'':_ref11$action$couponC;return!!couponCode;}).filter(function(){return!hasNewServices();}).withLatestFrom(cartReceived$).switchMap(function(_ref12){var _ref13=_slicedToArray(_ref12,2),navigate=_ref13[0],cartReceived=_ref13[1];if(cartReceived.action.cart.cartItems.find(function(i){return i.type===CART_ITEM_TYPE_PRODUCT;})){// We have items in cart, add coupon immediately return Observable.of(navigate);}// Wait until first product in cart to add a coupon return cartReceived$.filter(function(cartReceivedNext){return cartReceivedNext.action.cart.cartItems.find(function(i){return i.type===CART_ITEM_TYPE_PRODUCT;});}).first();},function(_ref14){var _ref15=_slicedToArray(_ref14,1),navigate=_ref15[0];return navigate;});/** * With the new services, the coupon is added immediately. */var addCouponImmediately$=routeAddProductNavigate$// Only if coupon is given .filter(function(_ref16){var _ref16$action$couponC=_ref16.action.couponCode,couponCode=_ref16$action$couponC===void 0?'':_ref16$action$couponC;return!!couponCode;}).filter(function(){return hasNewServices();});var addCoupon$=addCouponDeferred$.merge(addCouponImmediately$);/** * Deeplink to add product and coupon to cart, eg /cart_add_product/123/COUPON */subscribe(addCoupon$,/*#__PURE__*/function(){var _ref18=_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref17){var dispatch,action,couponCode;return _regeneratorRuntime.wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:dispatch=_ref17.dispatch,action=_ref17.action;couponCode=action.couponCode;if(couponCode){dispatch(addCouponsToCart([couponCode],false));}case 3:case"end":return _context2.stop();}},_callee2);}));return function(_x2){return _ref18.apply(this,arguments);};}());}