'use strict'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { IntlProvider } from 'react-intl'; import { Provider, connect } from 'react-redux'; import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom'; import { ScrollContext } from 'react-router-scroll-4'; import { loadInstance } from 'soapbox/actions/instance'; import { fetchMe } from 'soapbox/actions/me'; import { getSettings } from 'soapbox/actions/settings'; import { loadSoapboxConfig } from 'soapbox/actions/soapbox'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { fetchVerificationConfig } from 'soapbox/actions/verification'; import { FE_SUBDIRECTORY } from 'soapbox/build_config'; import Helmet from 'soapbox/components/helmet'; import AuthLayout from 'soapbox/features/auth_layout'; import OnboardingWizard from 'soapbox/features/onboarding/onboarding-wizard'; import PublicLayout from 'soapbox/features/public_layout'; import NotificationsContainer from 'soapbox/features/ui/containers/notifications_container'; import WaitlistPage from 'soapbox/features/verification/waitlist_page'; import { createGlobals } from 'soapbox/globals'; import messages from 'soapbox/locales/messages'; import { makeGetAccount } from 'soapbox/selectors'; import { getFeatures } from 'soapbox/utils/features'; import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types'; import { generateThemeCss } from 'soapbox/utils/theme'; import { ONBOARDING_VERSION } from '../actions/onboarding'; import { preload } from '../actions/preload'; import ErrorBoundary from '../components/error_boundary'; import UI from '../features/ui'; import { store } from '../store'; const validLocale = locale => Object.keys(messages).includes(locale); // Configure global functions for developers createGlobals(store); // Preload happens synchronously store.dispatch(preload()); /** Load initial data from the backend */ const loadInitial = () => { return async(dispatch, getState) => { // Await for authenticated fetch await dispatch(fetchMe()); // Await for feature detection await dispatch(loadInstance()); const promises = []; promises.push(dispatch(loadSoapboxConfig())); const state = getState(); const features = getFeatures(state.instance); if (features.pepe && !state.me) { promises.push(dispatch(fetchVerificationConfig())); } await Promise.all(promises); }; }; const makeAccount = makeGetAccount(); const mapStateToProps = (state) => { const me = state.get('me'); const account = makeAccount(state, me); const settings = getSettings(state); const needsOnboarding = settings.get('onboardingVersion') < ONBOARDING_VERSION; const soapboxConfig = getSoapboxConfig(state); const locale = settings.get('locale'); const singleUserMode = soapboxConfig.get('singleUserMode') && soapboxConfig.get('singleUserModeProfile'); return { me, account, reduceMotion: settings.get('reduceMotion'), underlineLinks: settings.get('underlineLinks'), systemFont: settings.get('systemFont'), dyslexicFont: settings.get('dyslexicFont'), demetricator: settings.get('demetricator'), locale: validLocale(locale) ? locale : 'en', themeCss: generateThemeCss(soapboxConfig), brandColor: soapboxConfig.get('brandColor'), appleAppId: soapboxConfig.get('appleAppId'), themeMode: settings.get('themeMode'), singleUserMode, needsOnboarding, }; }; @connect(mapStateToProps) class SoapboxMount extends React.PureComponent { static propTypes = { me: SoapboxPropTypes.me, account: ImmutablePropTypes.record, reduceMotion: PropTypes.bool, underlineLinks: PropTypes.bool, systemFont: PropTypes.bool, needsOnboarding: PropTypes.bool, dyslexicFont: PropTypes.bool, demetricator: PropTypes.bool, locale: PropTypes.string.isRequired, themeCss: PropTypes.string, themeMode: PropTypes.string, brandColor: PropTypes.string, appleAppId: PropTypes.string, dispatch: PropTypes.func, singleUserMode: PropTypes.bool, }; state = { messages: {}, localeLoading: true, isLoaded: false, } setMessages = () => { messages[this.props.locale]().then(messages => { this.setState({ messages, localeLoading: false }); }).catch(() => {}); } maybeUpdateMessages = prevProps => { if (this.props.locale !== prevProps.locale) { this.setMessages(); } } componentDidMount() { this.setMessages(); this.props.dispatch(loadInitial()).then(() => { this.setState({ isLoaded: true }); }).catch(() => { this.setState({ isLoaded: false }); }); } componentDidUpdate(prevProps) { this.maybeUpdateMessages(prevProps); } shouldUpdateScroll(prevRouterProps, { location }) { return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prevRouterProps?.location?.state?.soapboxModalKey); } render() { const { me, account, themeCss, locale, needsOnboarding, singleUserMode } = this.props; if (me === null) return null; if (me && !account) return null; if (!this.state.isLoaded) return null; if (this.state.localeLoading) return null; const waitlisted = account && !account.getIn(['source', 'approved'], true); if (account && !waitlisted && needsOnboarding) { return ( {themeCss && } ); } const bodyClass = classNames('bg-white dark:bg-slate-900 text-base', { 'no-reduce-motion': !this.props.reduceMotion, 'underline-links': this.props.underlineLinks, 'dyslexic': this.props.dyslexicFont, 'demetricator': this.props.demetricator, }); return ( {themeCss && } {this.props.appleAppId && ( )} <> {waitlisted && } />} {!me && (singleUserMode ? : )} {!me && } ); } } const Soapbox = () => { return ( ); }; export default Soapbox;