import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { createSelector } from 'reselect'; import Helmet from 'react-helmet'; import fontawesome from '@fortawesome/fontawesome'; import { withTranslation } from 'react-i18next'; import { fetchUser, isSignedInSelector, onlineStatusChange, isOnlineSelector, userFetchStateSelector, userSelector, usernameSelector, executeGA } from '../../redux'; import { flashMessageSelector, removeFlashMessage } from '../Flash/redux'; import { isBrowser } from '../../../utils'; import WithInstantSearch from '../search/WithInstantSearch'; import OfflineWarning from '../OfflineWarning'; import Flash from '../Flash'; import Header from '../Header'; import Footer from '../Footer'; // preload common fonts import latoLightURL from '../../../static/fonts/lato/Lato-Light.woff'; import latoRegularURL from '../../../static/fonts/lato/Lato-Regular.woff'; import latoBoldURL from '../../../static/fonts/lato/Lato-Bold.woff'; // eslint-disable-next-line max-len import robotoRegularURL from '../../../static/fonts/roboto-mono/RobotoMono-Regular.woff'; // eslint-disable-next-line max-len import robotoBoldURL from '../../../static/fonts/roboto-mono/RobotoMono-Bold.woff'; // eslint-disable-next-line max-len import robotoItalicURL from '../../../static/fonts/roboto-mono/RobotoMono-Italic.woff'; import './fonts.css'; import './global.css'; import './variables.css'; fontawesome.config = { autoAddCss: false }; const propTypes = { children: PropTypes.node.isRequired, executeGA: PropTypes.func, fetchState: PropTypes.shape({ pending: PropTypes.bool }), fetchUser: PropTypes.func.isRequired, flashMessage: PropTypes.shape({ id: PropTypes.string, type: PropTypes.string, message: PropTypes.string }), hasMessage: PropTypes.bool, isOnline: PropTypes.bool.isRequired, isSignedIn: PropTypes.bool, onlineStatusChange: PropTypes.func.isRequired, pathname: PropTypes.string.isRequired, removeFlashMessage: PropTypes.func.isRequired, showFooter: PropTypes.bool, signedInUserName: PropTypes.string, t: PropTypes.func.isRequired, theme: PropTypes.string, useTheme: PropTypes.bool, user: PropTypes.object }; const mapStateToProps = createSelector( isSignedInSelector, flashMessageSelector, isOnlineSelector, userFetchStateSelector, userSelector, usernameSelector, (isSignedIn, flashMessage, isOnline, fetchState, user) => ({ isSignedIn, flashMessage, hasMessage: !!flashMessage.message, isOnline, fetchState, theme: user.theme, user }) ); const mapDispatchToProps = dispatch => bindActionCreators( { fetchUser, removeFlashMessage, onlineStatusChange, executeGA }, dispatch ); class DefaultLayout extends Component { componentDidMount() { const { isSignedIn, fetchUser, pathname, executeGA } = this.props; if (!isSignedIn) { fetchUser(); } executeGA({ type: 'page', data: pathname }); window.addEventListener('online', this.updateOnlineStatus); window.addEventListener('offline', this.updateOnlineStatus); } componentDidUpdate(prevProps) { const { pathname, executeGA } = this.props; const { pathname: prevPathname } = prevProps; if (pathname !== prevPathname) { executeGA({ type: 'page', data: pathname }); } } componentWillUnmount() { window.removeEventListener('online', this.updateOnlineStatus); window.removeEventListener('offline', this.updateOnlineStatus); } updateOnlineStatus = () => { const { onlineStatusChange } = this.props; const isOnline = isBrowser() && 'navigator' in window ? window.navigator.onLine : null; return typeof isOnline === 'boolean' ? onlineStatusChange(isOnline) : null; }; render() { const { children, hasMessage, fetchState, flashMessage, isOnline, isSignedIn, removeFlashMessage, showFooter = true, t, theme = 'default', user, useTheme = true } = this.props; return (
{hasMessage && flashMessage ? ( ) : null} {children} {showFooter &&
); } } DefaultLayout.displayName = 'DefaultLayout'; DefaultLayout.propTypes = propTypes; export default connect( mapStateToProps, mapDispatchToProps )(withTranslation()(DefaultLayout));