@shopgate/pwa-common
Version:
Common library for the Shopgate Connect PWA.
51 lines • 10 kB
JavaScript
function _typeof(obj){if(typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"){_typeof=function _typeof(obj){return typeof obj;};}else{_typeof=function _typeof(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj;};}return _typeof(obj);}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}function _createClass(Constructor,protoProps,staticProps){if(protoProps)_defineProperties(Constructor.prototype,protoProps);if(staticProps)_defineProperties(Constructor,staticProps);return Constructor;}function _callSuper(_this,derived,args){function isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{return!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}));}catch(e){return false;}}derived=_getPrototypeOf(derived);return _possibleConstructorReturn(_this,isNativeReflectConstruct()?Reflect.construct(derived,args||[],_getPrototypeOf(_this).constructor):derived.apply(_this,args));}function _possibleConstructorReturn(self,call){if(call&&(_typeof(call)==="object"||typeof call==="function")){return call;}return _assertThisInitialized(self);}function _assertThisInitialized(self){if(self===void 0){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return self;}function _getPrototypeOf(o){_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function _getPrototypeOf(o){return o.__proto__||Object.getPrototypeOf(o);};return _getPrototypeOf(o);}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function");}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,writable:true,configurable:true}});if(superClass)_setPrototypeOf(subClass,superClass);}function _setPrototypeOf(o,p){_setPrototypeOf=Object.setPrototypeOf||function _setPrototypeOf(o,p){o.__proto__=p;return o;};return _setPrototypeOf(o,p);}function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}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);}import React from'react';import PropTypes from'prop-types';import{router,stack as routeStack,onDidPush,onDidPop,onDidReplace,onDidReset,onUpdate,ACTION_POP}from'@virtuous/conductor';import Route from'@virtuous/conductor/Route';import{RouterContext,Router as OrigRouter}from'@virtuous/react-conductor';import{UIEvents}from'@shopgate/pwa-core';import{hasSGJavaScriptBridge}from'@shopgate/pwa-core/helpers';import{hasWebBridge}from'@shopgate/engage/core';import{sanitizeLink}from"../../subscriptions/helpers/handleLinks";import authRoutes from"../../collections/AuthRoutes";import{EVENT_USER_INITIALIZED}from"../../constants/user";import connect from"./connector";/**
* Adds additional history listeners to compensate bugs and improve the behaviour within
* browser environments.
*/var createBrowserListeners=function createBrowserListeners(){var history=router.history;// Remove the original listener from the router.
router.historyListener();// Add new one which injects an intermediate function which intercepts history events
router.historyListener=history.listen(function(location,action){var locationPathname=location.pathname,search=location.search,hash=location.hash,state=location.state;// Create a pathname which fulfills the router expectations
var pathname="".concat(locationPathname).concat(search).concat(hash);if(action===ACTION_POP&&router.routeIndex===0){/**
* Within browser environments we need to handle situations where users come back to old
* Engage history entries from previous sessions, Without special handling the page wouldn't
* render correct content. So we replace the current visible page with content from the route.
*/router.replace({pathname:pathname,state:state});return;}/**
* Conductor 2.5.0 contains a bug within the handler for native history events. It ignores that
* the search and hash parameters are stored within separate properties of the history location
* object. The routing methods expect those parameters as part of the pathname.
*/router.handleNativeEvent(_extends({},location,{pathname:pathname}),action);}).bind(router);};/**
* The Router component.
*/var Router=/*#__PURE__*/function(_React$Component){/**
* @param {Object} props The component props.
*/function Router(props){var _this2;_classCallCheck(this,Router);_this2=_callSuper(this,Router,[props]);/**
* Updates the user initialized component state
*/_defineProperty(_this2,"setUserInitialized",function(){_this2.setState({userInitialized:true});});/**
* Determines the current location from the browser history
* @returns {string}
*/_defineProperty(_this2,"getHistoryLocation",function(){var _router$history$locat=router.history.location,hash=_router$history$locat.hash,pathname=_router$history$locat.pathname,search=_router$history$locat.search;return sanitizeLink("".concat(pathname).concat(search).concat(hash));});/**
* Determines if the current route is a protected route
* @returns {null|string}
*/_defineProperty(_this2,"getRouteProtector",function(){return authRoutes.getProtector(_this2.getHistoryLocation());});/**
* @param {Object} data Data for the update method
*/_defineProperty(_this2,"update",function(data){var prev=data.prev,next=data.next;if(data===null||data===void 0?void 0:data.id){/**
* The only change right now compared to the original component. When invoked for "onUpdate"
* only the updated route is passed instead of an object with prev and next.
*/_this2.setState({updated:Date.now()});return;}_this2.setState({prev:prev?prev.id:null,next:next.id,updated:Date.now()});});if(typeof props.history==='function'){router.constructor(props.history);}_this2.state={prev:null,next:null,updated:null,userInitialized:false,initialRouteProtected:!!_this2.getRouteProtector()};UIEvents.addListener(EVENT_USER_INITIALIZED,_this2.setUserInitialized);onDidPush(_this2.update);onDidPop(_this2.update);onDidReplace(_this2.update);onDidReset(_this2.update);onUpdate(_this2.update);if(hasWebBridge()||hasSGJavaScriptBridge()){createBrowserListeners();}return _this2;}/**
* @param {Object} nextProps The next component props.
* @param {Object} nextState The next component state.
* @returns {boolean}
*/_inherits(Router,_React$Component);return _createClass(Router,[{key:"shouldComponentUpdate",value:function shouldComponentUpdate(nextProps,nextState){var isUserLoggedIn=this.props.isUserLoggedIn;var _this$state=this.state,updated=_this$state.updated,userInitialized=_this$state.userInitialized;return updated!==nextState.updated||userInitialized!==nextState.userInitialized||isUserLoggedIn!==nextProps.isUserLoggedIn;}/**
* Replaces the initial route with a protector if necessary
* @param {Object} nextProps The next component props
* @param {Object} nextState The next components state
*/},{key:"UNSAFE_componentWillUpdate",value:function UNSAFE_componentWillUpdate(nextProps,nextState){var isUserLoggedIn=nextProps.isUserLoggedIn;var userInitialized=nextState.userInitialized,initialRouteProtected=nextState.initialRouteProtected;if(initialRouteProtected&&userInitialized&&!isUserLoggedIn){var protector=this.getRouteProtector();var location=this.getHistoryLocation();// Get the initial route from the route stack which was created by the router on init
var initialRoute=routeStack.getByIndex(0);var id=initialRoute.id;// Prepare the redirect state for the protector route
var state={redirect:{location:location}};var routeReplacement=new Route({id:id,state:state,pathname:protector});// Replace the auto-generated route with the replacement
routeStack.update(id,routeReplacement);// Update the browser url with the protector route
router.history.replace({pathname:protector,state:_extends({},state,{route:{id:id}})});this.setState({initialRouteProtected:false});}}/**
* Removes the listener for the EVENT_USER_INITIALIZED event
*/},{key:"componentWillUnmount",value:function componentWillUnmount(){UIEvents.removeListener(EVENT_USER_INITIALIZED,this.setUserInitialized);}},{key:"render",value:/**
* @returns {JSX}
*/function render(){var children=this.props.children;var _this$state2=this.state,prev=_this$state2.prev,next=_this$state2.next,initialRouteProtected=_this$state2.initialRouteProtected,userInitialized=_this$state2.userInitialized;if(initialRouteProtected&&!userInitialized){/**
* When the initial route is a protected route, the first rendering of the Router needs to
* be postponed till we know the final login state of the user.
*/return null;}var stack=Array.from(routeStack.getAll());return React.createElement(RouterContext.Provider,{value:{prev:prev,next:next,stack:stack}},children);}}]);}(React.Component);_defineProperty(Router,"defaultProps",{history:null,isUserLoggedIn:false});_defineProperty(Router,"Push",OrigRouter.Push);_defineProperty(Router,"Pop",OrigRouter.Pop);_defineProperty(Router,"Replace",OrigRouter.Replace);_defineProperty(Router,"Reset",OrigRouter.Reset);_defineProperty(Router,"ResetTo",OrigRouter.ResetTo);export default connect(Router);