function getDefaultContext( request ) { var context = Object.assign( {}, request.context, { compileDebug: config( 'env' ) === 'development' ? true : false, urls: generateStaticUrls( request ), user: false, env: CALYPSO_ENV, sanitize: sanitize, isRTL: config( 'rtl' ), isDebug: request.query.debug !== undefined ? true : false, badge: false, lang: config( 'i18n_default_locale_slug' ), jsFile: 'build', faviconURL: '//s1.wp.com/i/favicon.ico', isFluidWidth: !! config.isEnabled( 'fluid-width' ), abTestHelper: !! config.isEnabled( 'dev/test-helper' ), devDocsURL: '/devdocs', catchJsErrors: '/calypso/catch-js-errors-' + 'v2' + '.min.js', isPushEnabled: !! config.isEnabled( 'push-notifications' ) } ); context.app = { // use ipv4 address when is ipv4 mapped address clientIp: request.ip ? request.ip.replace( '::ffff:', '' ) : request.ip, isDebug: context.env === 'development' || context.isDebug, tinymceWpSkin: context.urls[ 'tinymce/skins/wordpress/wp-content.css' ], tinymceEditorCss: context.urls[ 'editor.css' ] }; if ( CALYPSO_ENV === 'wpcalypso' ) { context.badge = CALYPSO_ENV; context.devDocs = true; context.feedbackURL = 'https://github.com/Automattic/wp-calypso/issues/'; context.faviconURL = '/calypso/images/favicons/favicon-wpcalypso.ico'; } if ( CALYPSO_ENV === 'horizon' ) { context.badge = 'feedback'; context.feedbackURL = 'https://horizonfeedback.wordpress.com/'; context.faviconURL = '/calypso/images/favicons/favicon-horizon.ico'; } if ( CALYPSO_ENV === 'stage' ) { context.badge = 'staging'; context.feedbackURL = 'https://github.com/Automattic/wp-calypso/issues/'; context.faviconURL = '/calypso/images/favicons/favicon-staging.ico'; } if ( CALYPSO_ENV === 'development' ) { context.badge = 'dev'; context.devDocs = true; context.feedbackURL = 'https://github.com/Automattic/wp-calypso/issues/'; context.faviconURL = '/calypso/images/favicons/favicon-development.ico'; context.branchName = getCurrentBranchName(); context.commitChecksum = getCurrentCommitShortChecksum(); } return context; }
/** * Gets the default set of options to configure the Directly RTM widget. * It's important to keep this config in a getter function, rather than a constant * on the module scope, to prevent import-time errors errors that could crash Calypso. * * @see https://cloudup.com/cySVQ9R_O6S for all configuration options * * @returns {Object} The default configuration options */ function getDefaultOptions() { const ids = config( 'directly_rtm_widget_ids' ); const env = config( 'directly_rtm_widget_environment' ); return { id: ids[ env ], displayAskQuestion: false, }; }
translate: function() { var options, translation, sprintfArgs, errorMethod, optionsString, cacheable; options = normalizeTranslateArguments( arguments ); cacheable = ! options.components; if ( cacheable ) { optionsString = JSON.stringify( options ); translation = i18nState.translations.get( optionsString ); if ( translation ) { return translation; } } translation = getTranslationFromJed( options ); // handle any string substitution if ( options.args ) { sprintfArgs = ( Array.isArray( options.args ) ) ? options.args.slice( 0 ) : [ options.args ]; sprintfArgs.unshift( translation ); try { translation = Jed.sprintf.apply( Jed, sprintfArgs ); } catch ( error ) { if ( ! window || ! window.console ) { return; } errorMethod = ( config( 'env' ) === 'development' ) ? 'error' : 'warn'; if ( typeof error !== 'string' ) { window.console[ errorMethod ]( error ); } else { window.console[ errorMethod ]( 'i18n sprintf error:', sprintfArgs ); } } } // interpolate any components if ( options.components ) { translation = interpolateComponents( { mixedString: translation, components: options.components, throwErrors: ( config( 'env' ) !== 'production' ) } ); } // run any necessary hooks translateHooks.forEach( function( hook ) { translation = hook( translation, options ); } ); if ( cacheable ) { i18nState.translations.set( optionsString, translation ); } return translation; },
getLanguage: function( langSlug ) { let language; if ( localeRegex.test( langSlug ) || localeWithRegionRegex.test( langSlug ) ) { language = find( config( 'languages' ), { langSlug } ) || find( config( 'languages' ), { langSlug: langSlug.split( '-' )[ 0 ] } ); } return language; },
export const loginSocialUser = ( socialInfo, redirectTo ) => dispatch => { dispatch( { type: SOCIAL_LOGIN_REQUEST } ); return request .post( addLocaleToWpcomUrl( 'https://wordpress.com/wp-login.php?action=social-login-endpoint', getLocaleSlug() ) ) .withCredentials() .set( 'Content-Type', 'application/x-www-form-urlencoded' ) .accept( 'application/json' ) .send( { ...socialInfo, redirect_to: redirectTo, client_id: config( 'wpcom_signup_id' ), client_secret: config( 'wpcom_signup_key' ), } ) .then( response => { if ( get( response, 'body.data.two_step_notification_sent' ) === 'sms' ) { dispatch( { type: TWO_FACTOR_AUTHENTICATION_SEND_SMS_CODE_REQUEST_SUCCESS, notice: { message: getSMSMessageFromResponse( response ), status: 'is-success', }, twoStepNonce: get( response, 'body.data.two_step_nonce_sms' ), } ); } return remoteLoginUser( get( response, 'body.data.token_links', [] ) ).then( () => { dispatch( { type: SOCIAL_LOGIN_REQUEST_SUCCESS, data: get( response, 'body.data' ), } ); } ); } ) .catch( httpError => { const error = getErrorFromHTTPError( httpError ); error.email = get( httpError, 'response.body.data.email' ); dispatch( { type: SOCIAL_LOGIN_REQUEST_FAILURE, error, authInfo: socialInfo, data: get( httpError, 'response.body.data' ), } ); return Promise.reject( error ); } ); };
export const loginUserWithTwoFactorVerificationCode = ( twoStepCode, twoFactorAuthType ) => ( dispatch, getState ) => { dispatch( { type: TWO_FACTOR_AUTHENTICATION_LOGIN_REQUEST } ); return request .post( addLocaleToWpcomUrl( 'https://wordpress.com/wp-login.php?action=two-step-authentication-endpoint', getLocaleSlug() ) ) .withCredentials() .set( 'Content-Type', 'application/x-www-form-urlencoded' ) .accept( 'application/json' ) .send( { user_id: getTwoFactorUserId( getState() ), auth_type: twoFactorAuthType, two_step_code: replace( twoStepCode, /\s/g, '' ), two_step_nonce: getTwoFactorAuthNonce( getState(), twoFactorAuthType ), remember_me: true, client_id: config( 'wpcom_signup_id' ), client_secret: config( 'wpcom_signup_key' ), } ) .then( response => { return remoteLoginUser( get( response, 'body.data.token_links', [] ) ).then( () => { dispatch( { type: TWO_FACTOR_AUTHENTICATION_LOGIN_REQUEST_SUCCESS } ); } ); } ) .catch( httpError => { const twoStepNonce = get( httpError, 'response.body.data.two_step_nonce' ); if ( twoStepNonce ) { dispatch( { type: TWO_FACTOR_AUTHENTICATION_UPDATE_NONCE, twoStepNonce, nonceType: twoFactorAuthType, } ); } const error = getErrorFromHTTPError( httpError ); dispatch( { type: TWO_FACTOR_AUTHENTICATION_LOGIN_REQUEST_FAILURE, error, } ); return Promise.reject( error ); } ); };
export function setUpLocale( context, next ) { const { lang } = context.params; const language = getLanguage( lang ); if ( language ) { context.lang = lang; context.isRTL = Boolean( language.rtl ); } else { context.lang = config( 'i18n_default_locale_slug' ); context.isRTL = Boolean( config( 'rtl' ) ); } next(); }
export function getLanguage( localeSlug, localeVariant = null ) { // if a localeVariant is given, we should use it. Otherwise, use localeSlug const langSlug = localeVariant || localeSlug; if ( localeRegex.test( langSlug ) ) { // Find for the langSlug first. If we can't find it, split it and find its parent slug. // Please see the comment above `localeRegex` to see why we can split by - or _ and find the parent slug. return ( find( config( 'languages' ), { langSlug } ) || find( config( 'languages' ), { langSlug: langSlug.split( /[-_]/ )[ 0 ] } ) ); } return undefined; }
checkToken: function( context, next ) { const loggedOutRoutes = [ '/oauth-login', '/oauth', '/start', '/authorize', '/api/oauth/token' ], isValidSection = loggedOutRoutes.some( route => startsWith( context.path, route ) ); // Check we have an OAuth token, otherwise redirect to auth/login page if ( OAuthToken.getToken() === false && ! isValidSection ) { if ( config( 'env_id' ) === 'desktop' ) { return page( config( 'login_url' ) ); } return page( '/authorize' ); } next(); },
export function isATEnabled( site ) { // don't let this explode in SSR'd envs if ( typeof window !== 'object' ) { return false; } // Site has already been transferred if ( get( site, 'options.is_automated_transfer' ) ) { return true; } // Feature must be enabled on environment if ( ! isEnabled( 'automated-transfer' ) ) { return false; } // If it's wpcalypso, this is open if ( config( 'env_id' ) === 'wpcalypso' ) { return true; } // Site has Business plan const planSlug = get( site, 'plan.product_slug' ); if ( planSlug !== PLAN_BUSINESS ) { return false; } // Current User can manage site const canManageSite = userCan( 'manage_options', site ); if ( ! canManageSite ) { return false; } return true; }
export function isExternal( url ) { // parseURL will return hostname = null if no protocol or double-slashes // the url passed in might be of form `en.support.wordpress.com` // so for this function we'll append double-slashes to fake it // if it is a relative URL the hostname will still be empty from parseURL if ( ! startsWith( url, 'http://' ) && ! startsWith( url, 'https://' ) && ! startsWith( url, '//' ) ) { url = '//' + url; } const { hostname, path } = parseUrl( url, false, true ); // no qs needed, and slashesDenoteHost to handle protocol-relative URLs if ( ! hostname ) { return false; } if ( typeof window !== 'undefined' ) { if ( hostname === window.location.hostname ) { // even if hostname matches, the url might be outside calypso // outside calypso should be considered external // double separators are valid paths - but not handled correctly if ( path && isLegacyRoute( path.replace( '//', '/' ) ) ) { return true; } return false; } } return hostname !== config( 'hostname' ); }
app.get( '/discover', function( req, res, next ) { if ( ! req.cookies.wordpress_logged_in ) { res.redirect( config( 'discover_logged_out_redirect_url' ) ); } else { next(); } } );
/** * This lists modules that must use commonJS `require()`s * All modules listed here need to be ES5. * * @returns { object } list of externals */ function getExternals() { var externals = {}; // Don't bundle any node_modules, both to avoid a massive bundle, and problems // with modules that are incompatible with webpack bundling. fs.readdirSync( 'node_modules' ) .filter( function( module ) { return [ '.bin' ].indexOf( module ) === -1; } ) .forEach( function( module ) { externals[ module ] = 'commonjs ' + module; } ); // Don't bundle webpack.config, as it depends on absolute paths (__dirname) externals[ 'webpack.config' ] = 'commonjs webpack.config'; // Exclude hot-reloader, as webpack will try and resolve this in production builds, // and error. // TODO: use WebpackDefinePlugin for CALYPSO_ENV, so we can make conditional requires work externals[ 'bundler/hot-reloader' ] = 'commonjs bundler/hot-reloader'; // Exclude the devdocs search-index, as it's huge. externals[ 'devdocs/search-index' ] = 'commonjs devdocs/search-index'; // Exclude the devdocs components usage stats data externals[ 'devdocs/components-usage-stats.json' ] = 'commonjs devdocs/components-usage-stats.json'; // Exclude server/bundler/assets, since the files it requires don't exist until the bundler has run externals[ 'bundler/assets' ] = 'commonjs bundler/assets'; // Map React and redux to the minimized version in production if ( config( 'env' ) === 'production' ) { externals[ 'react-with-addons' ] = 'commonjs react/dist/react-with-addons.min'; externals.react = 'commonjs react/dist/react.min'; externals.redux = 'commonjs redux/dist/redux.min'; } return externals; }
export function render( element, key = JSON.stringify( element ) ) { try { const startTime = Date.now(); debug( 'cache access for key', key ); let context = markupCache.get( key ); if ( ! context ) { bumpStat( 'calypso-ssr', 'loggedout-design-cache-miss' ); debug( 'cache miss for key', key ); const renderedLayout = ReactDomServer.renderToString( element ); context = { renderedLayout }; markupCache.set( key, context ); } const rtsTimeMs = Date.now() - startTime; debug( 'Server render time (ms)', rtsTimeMs ); if ( rtsTimeMs > 100 ) { // Server renders should probably never take longer than 100ms bumpStat( 'calypso-ssr', 'over-100ms-rendertostring' ); } return context; } catch ( ex ) { if ( config( 'env' ) === 'development' ) { throw ex; } } //todo: render an error? }
getAll: function( selectedSite, siteCount ) { let siteProps = {}; const defaultProps = { environment: process.env.NODE_ENV, site_count: siteCount || 0, site_id_label: 'wpcom', client: config( 'client_slug' ), }; if ( selectedSite ) { siteProps = { // Tracks expects a blog_id property to identify the blog which is // why we use it here instead of calling the property site_id blog_id: selectedSite.ID, // Tracks expects a blog_lang property to identify the blog language which is // why we use it here instead of calling the property site_language blog_lang: selectedSite.lang, site_id_label: selectedSite.jetpack ? 'jetpack' : 'wpcom', site_plan_id: selectedSite.plan ? selectedSite.plan.product_id : null, }; } return assign( defaultProps, siteProps ); },
discover: function( context ) { var blogId = config( 'discover_blog_id' ), SiteStream = require( 'reader/site-stream' ), basePath = route.sectionify( context.path ), fullAnalyticsPageTitle = analyticsPageTitle + ' > Site > ' + blogId, feedStore = feedStreamFactory( 'site:' + blogId ), mcKey = 'discover'; titleActions.setTitle( 'Discover' ); ensureStoreLoading( feedStore, context ); trackPageLoad( basePath, fullAnalyticsPageTitle, mcKey ); stats.recordTrack( 'calypso_reader_discover_viewed' ); ReactDom.render( React.createElement( SiteStream, { key: 'site-' + blogId, store: feedStore, siteId: blogId, trackScrollPage: trackScrollPage.bind( null, basePath, fullAnalyticsPageTitle, analyticsPageTitle, mcKey ), onUpdatesShown: trackUpdatesLoaded.bind( null, mcKey ), suppressSiteNameLink: true, showBack: false } ), document.getElementById( 'primary' ) ); }
export function serverRender( req, res ) { const context = req.context; let title, metas = [], links = []; if ( context.lang !== config( 'i18n_default_locale_slug' ) ) { context.i18nLocaleScript = '//widgets.wp.com/languages/calypso/' + context.lang + '.js'; } if ( config.isEnabled( 'server-side-rendering' ) && context.layout && ! context.user ) { const key = context.renderCacheKey || JSON.stringify( context.layout ); Object.assign( context, render( context.layout, key ) ); } if ( context.store ) { title = getDocumentHeadFormattedTitle( context.store.getState() ); metas = getDocumentHeadMeta( context.store.getState() ); links = getDocumentHeadLink( context.store.getState() ); let reduxSubtrees = [ 'documentHead' ]; if ( isSectionIsomorphic( context.store.getState() ) ) { reduxSubtrees = reduxSubtrees.concat( [ 'ui', 'themes' ] ); } context.initialReduxState = pick( context.store.getState(), reduxSubtrees ); } context.head = { title, metas, links }; if ( config.isEnabled( 'desktop' ) ) { res.render( 'desktop.jade', context ); } else { res.render( 'index.jade', context ); } }
function setUpLoggedOutRoute( req, res, next ) { var context = getDefaultContext( req ), language; res.set( { 'X-Frame-Options': 'SAMEORIGIN' } ); // Set up the locale in case it has ended up in the flow param req.params = i18nUtils.setUpLocale( req.params ); language = i18nUtils.getLanguage( req.params.lang ); if ( language ) { context.lang = req.params.lang; if ( language.rtl ) { context.isRTL = true; } } if ( context.lang !== config( 'i18n_default_locale_slug' ) ) { context.i18nLocaleScript = '//widgets.wp.com/languages/calypso/' + context.lang + '.js'; } req.context = context; next(); }
User.prototype.initialize = function() { debug( 'Initializing User' ); this.fetching = false; this.initialized = false; if ( config( 'wpcom_user_bootstrap' ) ) { this.data = window.currentUser || false; // Store the current user in localStorage so that we can use it to determine // if the the logged in user has changed when initializing in the future if ( this.data ) { this.clearStoreIfChanged( this.data.ID ); store.set( 'wpcom_user', this.data ); } else { // The user is logged out this.initialized = true; } } else { this.data = store.get( 'wpcom_user' ) || false; // Make sure that the user stored in localStorage matches the logged-in user this.fetch(); } if ( this.data ) { this.initialized = true; } };
function renderNoVisibleSites( context ) { const EmptyContentComponent = require( 'components/empty-content' ); const currentUser = user.get(); const hiddenSites = currentUser.site_count - currentUser.visible_site_count; const signup_url = config( 'signup_url' ); removeSidebar( context ); renderWithReduxStore( React.createElement( EmptyContentComponent, { title: i18n.translate( 'You have %(hidden)d hidden WordPress site.', 'You have %(hidden)d hidden WordPress sites.', { count: hiddenSites, args: { hidden: hiddenSites } } ), line: i18n.translate( 'To manage it here, set it to visible.', 'To manage them here, set them to visible.', { count: hiddenSites } ), action: i18n.translate( 'Change Visibility' ), actionURL: '//dashboard.wordpress.com/wp-admin/index.php?page=my-blogs', secondaryAction: i18n.translate( 'Create New Site' ), secondaryActionURL: `${ signup_url }?ref=calypso-nosites` } ), document.getElementById( 'primary' ), context.store ); }
bumpStatWithPageView: function( group, name ) { // this function is fairly dangerous, as it bumps page views for wpcom and should only be called in very specific cases. var uriComponent = buildQuerystringNoPrefix( group, name ); // prints debug info if ( config( 'mc_analytics_enabled' ) ) { new Image().src = document.location.protocol + '//pixel.wp.com/g.gif?v=wpcom' + uriComponent + '&t=' + Math.random(); } }
function bumpStat( group, name ) { const statUrl = `http://pixel.wp.com/g.gif?v=wpcom-no-pv&x_${ group }=${ name }&t=${ Math.random() }`; if ( config( 'env' ) === 'production' ) { superagent.get( statUrl ).end(); } }
app.get( '/discover', function( req, res ) { if ( req.cookies.wordpress_logged_in ) { renderLoggedInRoute( req, res ); } else { res.redirect( config( 'discover_logged_out_redirect_url' ) ); } } );
addHotJarScript: function() { if ( ! config( 'hotjar_enabled' ) || doNotTrack() || isPiiUrl() || ! mayWeTrackCurrentUserGdpr() ) { hotjarDebug( 'Not loading HotJar script' ); return; } ( function( h, o, t, j, a, r ) { hotjarDebug( 'Loading HotJar script' ); h.hj = h.hj || function() { ( h.hj.q = h.hj.q || [] ).push( arguments ); }; h._hjSettings = { hjid: 227769, hjsv: 5 }; a = o.getElementsByTagName( 'head' )[ 0 ]; r = o.createElement( 'script' ); r.async = 1; r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv; a.appendChild( r ); } )( window, document, '//static.hotjar.com/c/hotjar-', '.js?sv=' ); },
/** * Loads locale json file to instantiate Jed instance as `i18nState`, emits 'change' event to notify view to update * @param {string} localeSlug The string that represents this particular locale */ function fetchLocale( localeSlug ) { var localeJson = '//widgets.wp.com/languages/calypso/' + localeSlug + '.json'; // If default locale, no need to retrieve translations if ( config( 'i18n_default_locale_slug' ) === localeSlug ) { i18nState.localeSlug = localeSlug; setLocale( { '': {} } ); return; } // Instantiate empty Jed object so we don't block rendering // while waiting for the locale file to load. // Will result in flash of content, but better than waiting. if ( ! i18nState.jed ) { setLocale( { '': {} } ); } request.get( localeJson ) .end( function( error, response ) { if ( error ) { console.error( 'Encountered an error loading locale file for ' + localeSlug + '. Falling back to English.' ); return; } debug( 'Received new locale file for ' + localeSlug + '.', response.body ); i18nState.localeSlug = localeSlug; setLocale( response.body ); } ); }
getLogoutUrl: function( redirect ) { var url = '/logout', userData = user.get(), subdomain = ''; // If logout_URL isn't set, then go ahead and return the logout URL // without a proper nonce as a fallback. // Note: we never want to use logout_URL in the desktop app if ( ! userData.logout_URL || config.isEnabled( 'always_use_logout_url' ) ) { // Use localized version of the homepage in the redirect if ( userData.localeSlug && userData.localeSlug !== '' && userData.localeSlug !== 'en' ) { subdomain = userData.localeSlug + '.'; } url = config( 'logout_url' ).replace( '|subdomain|', subdomain ); } else { url = userData.logout_URL; } if ( 'string' === typeof redirect ) { redirect = '&redirect_to=' + encodeURIComponent( redirect ); url += redirect; } debug( 'Logout Url: ' + url ); return url; },
function boot() { var localeSlug; init(); // When the user is bootstrapped, we also bootstrap the // locale strings if ( ! config( 'wpcom_user_bootstrap' ) ) { localeSlug = user.get().localeSlug; if ( localeSlug ) { switchLocale( localeSlug ); } } // Set the locale for the current user user.on( 'change', function() { localeSlug = user.get().localeSlug; if ( localeSlug ) { switchLocale( localeSlug ); } } ); translatorJumpstart.init(); createReduxStoreFromPersistedInitialState( reduxStoreReady ); }
user( req.get( 'Cookie' ), function( error, data ) { var end, searchParam, errorMessage; if ( error ) { if ( error.error === 'authorization_required' ) { debug( 'User public API authorization required. Redirecting to %s', redirectUrl ); res.redirect( redirectUrl ); } else { if ( error.error ) { errorMessage = error.error + ' ' + error.message; } else { errorMessage = error.message; } console.log( 'API Error: ' + errorMessage ); res.status( 500 ).render( '500.jade', context ); } return; } end = ( new Date().getTime() ) - start; debug( 'Rendering with bootstrapped user object. Fetched in %d ms', end ); context.user = data; context.isRTL = data.isRTL ? true : false; if ( data.localeSlug ) { context.lang = data.localeSlug; } if ( context.lang !== config( 'i18n_default_locale_slug' ) ) { context.i18nLocaleScript = '//widgets.wp.com/languages/calypso/' + context.lang + '.js'; } if ( req.path === '/' && req.query ) { searchParam = req.query.s || req.query.q; if ( searchParam ) { res.redirect( 'https://' + context.lang + '.search.wordpress.com/?q=' + encodeURIComponent( searchParam ) ); return; } if ( req.query.newuseremail ) { debug( 'Detected legacy email verification action. Redirecting...' ); res.redirect( 'https://wordpress.com/verify-email/?' + qs.stringify( req.query ) ); return; } if ( req.query.action === 'wpcom-invite-users' ) { debug( 'Detected legacy invite acceptance action. Redirecting...' ); res.redirect( 'https://wordpress.com/accept-invite/?' + qs.stringify( req.query ) ); return; } } req.context = context; next(); } );
// raise a console warning function warn() { if ( config( 'env' ) === 'production' ) { return; } if ( window && window.console && window.console.warn ) { window.console.warn.apply( window.console, arguments ); } }
export const logoutUser = redirectTo => ( dispatch, getState ) => { dispatch( { type: LOGOUT_REQUEST, } ); const currentUser = getCurrentUser( getState() ); const logoutNonceMatches = ( currentUser.logout_URL || '' ).match( /_wpnonce=([^&]*)/ ); const logoutNonce = logoutNonceMatches && logoutNonceMatches[ 1 ]; return request .post( addLocaleToWpcomUrl( 'https://wordpress.com/wp-login.php?action=logout-endpoint', getLocaleSlug() ) ) .withCredentials() .set( 'Content-Type', 'application/x-www-form-urlencoded' ) .accept( 'application/json' ) .send( { redirect_to: redirectTo, client_id: config( 'wpcom_signup_id' ), client_secret: config( 'wpcom_signup_key' ), logout_nonce: logoutNonce, } ) .then( response => { const data = get( response, 'body.data', {} ); dispatch( { type: LOGOUT_REQUEST_SUCCESS, data, } ); return Promise.resolve( data ); } ) .catch( httpError => { const error = getErrorFromHTTPError( httpError ); dispatch( { type: LOGOUT_REQUEST_FAILURE, error, } ); return Promise.reject( error ); } ); };