2020-03-27 13:59:38 -07:00
'use strict' ;
2022-01-10 14:17:52 -08:00
import classNames from 'classnames' ;
2020-03-27 13:59:38 -07:00
import PropTypes from 'prop-types' ;
2022-01-10 14:17:52 -08:00
import React from 'react' ;
2020-06-07 15:30:47 -07:00
import ImmutablePropTypes from 'react-immutable-proptypes' ;
2022-01-10 14:17:52 -08:00
import { IntlProvider } from 'react-intl' ;
import { Provider , connect } from 'react-redux' ;
2022-02-25 07:08:05 -08:00
import { BrowserRouter , Switch , Redirect , Route } from 'react-router-dom' ;
2020-03-27 13:59:38 -07:00
import { ScrollContext } from 'react-router-scroll-4' ;
2022-01-10 14:25:06 -08:00
2021-10-20 11:18:55 -07:00
import { loadInstance } from 'soapbox/actions/instance' ;
2020-05-28 15:52:07 -07:00
import { fetchMe } from 'soapbox/actions/me' ;
import { getSettings } from 'soapbox/actions/settings' ;
2022-01-10 14:17:52 -08:00
import { loadSoapboxConfig } from 'soapbox/actions/soapbox' ;
2020-08-23 13:56:18 -07:00
import { getSoapboxConfig } from 'soapbox/actions/soapbox' ;
2022-03-21 11:09:01 -07:00
import { fetchVerificationConfig } from 'soapbox/actions/verification' ;
2021-09-05 11:21:39 -07:00
import { FE _SUBDIRECTORY } from 'soapbox/build_config' ;
2022-03-21 11:09:01 -07:00
import { NODE _ENV } from 'soapbox/build_config' ;
2022-01-10 14:17:52 -08:00
import Helmet from 'soapbox/components/helmet' ;
2022-03-21 11:09:01 -07:00
import AuthLayout from 'soapbox/features/auth_layout' ;
2022-01-10 14:17:52 -08:00
import PublicLayout from 'soapbox/features/public_layout' ;
2022-03-21 11:09:01 -07:00
import WaitlistPage from 'soapbox/features/verification/waitlist_page' ;
2021-11-02 09:47:26 -07:00
import { createGlobals } from 'soapbox/globals' ;
2022-01-10 14:17:52 -08:00
import messages from 'soapbox/locales/messages' ;
2022-03-21 11:09:01 -07:00
import { makeGetAccount } from 'soapbox/selectors' ;
2022-01-10 14:17:52 -08:00
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types' ;
2022-03-23 13:31:19 -07:00
import { generateThemeCss } from 'soapbox/utils/theme' ;
2022-01-10 14:25:06 -08:00
2022-01-10 14:17:52 -08:00
import { INTRODUCTION _VERSION } from '../actions/onboarding' ;
2022-01-10 14:01:24 -08:00
import { preload } from '../actions/preload' ;
2022-01-10 14:17:52 -08:00
import ErrorBoundary from '../components/error_boundary' ;
2022-03-21 11:09:01 -07:00
// import Introduction from '../features/introduction';
2022-01-10 14:01:24 -08:00
import UI from '../features/ui' ;
2022-03-14 16:01:09 -07:00
import { store } from '../store' ;
2020-03-27 13:59:38 -07:00
2020-06-04 19:48:45 -07:00
const validLocale = locale => Object . keys ( messages ) . includes ( locale ) ;
2022-01-31 08:49:51 -08:00
// Delay rendering until instance has loaded or failed (for feature detection)
const isInstanceLoaded = state => {
2022-01-31 09:23:39 -08:00
const v = state . getIn ( [ 'instance' , 'version' ] , '0.0.0' ) ;
2022-01-31 08:49:51 -08:00
const fetchFailed = state . getIn ( [ 'meta' , 'instance_fetch_failed' ] , false ) ;
return v !== '0.0.0' || fetchFailed ;
} ;
2021-11-02 09:47:26 -07:00
// Configure global functions for developers
createGlobals ( store ) ;
2020-08-24 13:23:05 -07:00
store . dispatch ( preload ( ) ) ;
2021-09-03 05:42:27 -07:00
store . dispatch ( fetchMe ( ) )
2022-03-21 11:09:01 -07:00
. then ( account => {
2021-09-03 05:42:27 -07:00
// Postpone for authenticated fetch
2021-10-20 11:18:55 -07:00
store . dispatch ( loadInstance ( ) ) ;
2021-11-15 14:56:33 -08:00
store . dispatch ( loadSoapboxConfig ( ) ) ;
2022-03-21 11:09:01 -07:00
if ( ! account ) {
store . dispatch ( fetchVerificationConfig ( ) ) ;
}
2021-09-03 05:42:27 -07:00
} )
2022-03-21 11:09:01 -07:00
. catch ( console . error ) ;
const makeAccount = makeGetAccount ( ) ;
2020-03-27 13:59:38 -07:00
const mapStateToProps = ( state ) => {
2020-04-01 19:20:47 -07:00
const me = state . get ( 'me' ) ;
2022-03-21 11:09:01 -07:00
const account = makeAccount ( state , me ) ;
2020-03-27 13:59:38 -07:00
const showIntroduction = account ? state . getIn ( [ 'settings' , 'introductionVersion' ] , 0 ) < INTRODUCTION _VERSION : false ;
2020-04-28 11:49:39 -07:00
const settings = getSettings ( state ) ;
2020-08-23 13:56:18 -07:00
const soapboxConfig = getSoapboxConfig ( state ) ;
2020-06-04 19:48:45 -07:00
const locale = settings . get ( 'locale' ) ;
2020-03-27 13:59:38 -07:00
2022-02-25 07:08:05 -08:00
const singleUserMode = soapboxConfig . get ( 'singleUserMode' ) && soapboxConfig . get ( 'singleUserModeProfile' ) ;
2020-03-27 13:59:38 -07:00
return {
showIntroduction ,
2020-04-08 16:38:22 -07:00
me ,
2022-03-21 11:09:01 -07:00
account ,
2022-01-31 08:49:51 -08:00
instanceLoaded : isInstanceLoaded ( state ) ,
2020-04-28 11:49:39 -07:00
reduceMotion : settings . get ( 'reduceMotion' ) ,
2021-07-28 07:03:14 -07:00
underlineLinks : settings . get ( 'underlineLinks' ) ,
2020-04-28 11:49:39 -07:00
systemFont : settings . get ( 'systemFont' ) ,
dyslexicFont : settings . get ( 'dyslexicFont' ) ,
demetricator : settings . get ( 'demetricator' ) ,
2020-06-04 19:48:45 -07:00
locale : validLocale ( locale ) ? locale : 'en' ,
2022-03-23 13:31:19 -07:00
themeCss : generateThemeCss ( soapboxConfig ) ,
2021-08-14 19:49:09 -07:00
brandColor : soapboxConfig . get ( 'brandColor' ) ,
2020-06-02 10:16:26 -07:00
themeMode : settings . get ( 'themeMode' ) ,
2022-02-25 07:08:05 -08:00
singleUserMode ,
2020-04-14 11:44:40 -07:00
} ;
} ;
2020-03-27 13:59:38 -07:00
@ connect ( mapStateToProps )
2020-05-28 15:52:07 -07:00
class SoapboxMount extends React . PureComponent {
2020-03-27 13:59:38 -07:00
static propTypes = {
showIntroduction : PropTypes . bool ,
2020-04-27 11:56:26 -07:00
me : SoapboxPropTypes . me ,
2022-03-23 10:14:42 -07:00
account : ImmutablePropTypes . record ,
2022-01-31 08:49:51 -08:00
instanceLoaded : PropTypes . bool ,
2020-04-21 13:16:33 -07:00
reduceMotion : PropTypes . bool ,
2021-07-28 07:03:14 -07:00
underlineLinks : PropTypes . bool ,
2020-04-21 13:05:49 -07:00
systemFont : PropTypes . bool ,
2020-04-21 13:10:45 -07:00
dyslexicFont : PropTypes . bool ,
2020-04-21 13:14:06 -07:00
demetricator : PropTypes . bool ,
2020-04-28 13:25:10 -07:00
locale : PropTypes . string . isRequired ,
2022-03-07 15:19:54 -08:00
themeCss : PropTypes . string ,
2020-06-02 10:16:26 -07:00
themeMode : PropTypes . string ,
2021-08-14 19:49:09 -07:00
brandColor : PropTypes . string ,
2020-05-30 17:05:01 -07:00
dispatch : PropTypes . func ,
2022-03-21 11:09:01 -07:00
singleUserMode : PropTypes . bool ,
2020-03-27 13:59:38 -07:00
} ;
2020-06-04 19:22:58 -07:00
state = {
messages : { } ,
localeLoading : true ,
}
setMessages = ( ) => {
messages [ this . props . locale ] ( ) . then ( messages => {
this . setState ( { messages , localeLoading : false } ) ;
} ) . catch ( ( ) => { } ) ;
}
maybeUpdateMessages = prevProps => {
if ( this . props . locale !== prevProps . locale ) {
this . setMessages ( ) ;
2021-08-03 12:22:51 -07:00
}
2020-06-04 19:22:58 -07:00
}
componentDidMount ( ) {
this . setMessages ( ) ;
}
componentDidUpdate ( prevProps ) {
this . maybeUpdateMessages ( prevProps ) ;
}
2022-01-06 08:45:10 -08:00
shouldUpdateScroll ( prevRouterProps , { location } ) {
return ! ( location . state ? . soapboxModalKey && location . state ? . soapboxModalKey !== prevRouterProps ? . location ? . state ? . soapboxModalKey ) ;
2021-08-28 06:17:28 -07:00
}
2020-04-14 14:47:35 -07:00
render ( ) {
2022-03-07 15:19:54 -08:00
const { me , account , instanceLoaded , themeCss , locale , singleUserMode } = this . props ;
2020-04-14 13:45:38 -07:00
if ( me === null ) return null ;
2022-03-21 11:09:01 -07:00
if ( me && ! account ) return null ;
2022-01-31 08:49:51 -08:00
if ( ! instanceLoaded ) return null ;
2020-06-04 19:22:58 -07:00
if ( this . state . localeLoading ) return null ;
2020-04-08 16:38:22 -07:00
2022-03-21 11:09:01 -07:00
const waitlisted = account && ! account . getIn ( [ 'source' , 'approved' ] , true ) ;
2020-03-27 13:59:38 -07:00
// Disabling introduction for launch
// const { showIntroduction } = this.props;
//
// if (showIntroduction) {
// return <Introduction />;
// }
2022-03-21 11:09:01 -07:00
const bodyClass = classNames ( 'bg-white dark:bg-slate-900 text-base' , {
2020-06-02 13:28:15 -07:00
'no-reduce-motion' : ! this . props . reduceMotion ,
2021-07-28 07:03:14 -07:00
'underline-links' : this . props . underlineLinks ,
2020-06-02 13:28:15 -07:00
'dyslexic' : this . props . dyslexicFont ,
'demetricator' : this . props . demetricator ,
2020-04-21 13:05:49 -07:00
} ) ;
2020-03-27 13:59:38 -07:00
return (
2020-06-04 19:22:58 -07:00
< IntlProvider locale = { locale } messages = { this . state . messages } >
2022-03-24 06:17:32 -07:00
< Helmet >
< html lang = 'en' className = { classNames ( { dark : this . props . themeMode === 'dark' } ) } / >
< body className = { bodyClass } / >
{ themeCss && < style id = 'theme' type = 'text/css' > { ` :root{ ${ themeCss } } ` } < / s t y l e > }
< meta name = 'theme-color' content = { this . props . brandColor } / >
< / H e l m e t >
2021-04-22 11:57:47 -07:00
< ErrorBoundary >
2021-09-05 11:21:39 -07:00
< BrowserRouter basename = { FE _SUBDIRECTORY } >
2021-10-12 14:22:44 -07:00
< >
< ScrollContext shouldUpdateScroll = { this . shouldUpdateScroll } >
< Switch >
2022-03-21 11:09:01 -07:00
< Redirect from = '/v1/verify_email/:token' to = '/auth/verify/email/:token' / >
{ waitlisted && < Route render = { ( props ) => < WaitlistPage { ... props } account = { account } / > } / > }
2022-02-25 07:08:05 -08:00
{ ! me && ( singleUserMode
? < Redirect exact from = '/' to = { ` / ${ singleUserMode } ` } / >
: < Route exact path = '/' component = { PublicLayout } / > ) }
2022-03-21 11:09:01 -07:00
{ ! me && < Route exact path = '/' component = { PublicLayout } / > }
2021-10-12 14:22:44 -07:00
< Route exact path = '/about/:slug?' component = { PublicLayout } / >
2022-03-21 11:09:01 -07:00
< Route exact path = '/beta/:slug?' component = { PublicLayout } / >
< Route exact path = '/mobile/:slug?' component = { PublicLayout } / >
< Route exact path = '/login' component = { AuthLayout } / >
< Route path = '/auth/verify' component = { AuthLayout } / >
< Route path = '/reset-password' component = { AuthLayout } / >
< Route path = '/edit-password' component = { AuthLayout } / >
< Redirect from = '/auth/reset_password' to = '/reset-password' / >
< Redirect from = '/auth/edit_password' to = '/edit-password' / >
< Redirect from = '/auth/sign_in' to = '/login' / >
2021-10-12 14:22:44 -07:00
< Route path = '/' component = { UI } / >
< / S w i t c h >
< / S c r o l l C o n t e x t >
< / >
2020-04-28 13:25:10 -07:00
< / B r o w s e r R o u t e r >
2021-04-22 11:57:47 -07:00
< / E r r o r B o u n d a r y >
2020-04-28 13:25:10 -07:00
< / I n t l P r o v i d e r >
2020-03-27 13:59:38 -07:00
) ;
}
}
2020-05-28 15:52:07 -07:00
export default class Soapbox extends React . PureComponent {
2020-03-27 13:59:38 -07:00
2021-03-24 11:37:41 -07:00
printConsoleWarning = ( ) => {
/* eslint-disable no-console */
console . log ( '%cStop!' , [
'color: #ff0000' ,
'display: block' ,
2022-03-21 11:09:01 -07:00
'font-family: system-ui, -apple-system, BlinkMacSystemFont, Ubuntu, "Helvetica Neue", sans-serif' ,
2021-03-24 11:37:41 -07:00
'font-size: 50px' ,
'font-weight: 800' ,
'padding: 4px 0' ,
] . join ( ';' ) ) ;
console . log ( '%cThis is a browser feature intended for developers. If someone told you to copy-paste something here it is a scam and will give them access to your account.' , [
'color: #111111' ,
'display: block' ,
2022-03-21 11:09:01 -07:00
'font-family: system-ui, -apple-system, BlinkMacSystemFont, Ubuntu, "Helvetica Neue", sans-serif' ,
2021-03-24 11:37:41 -07:00
'font-size: 18px' ,
'padding: 4px 0 16px' ,
] . join ( ';' ) ) ;
/* eslint-enable no-console */
}
componentDidMount ( ) {
2022-03-21 11:09:01 -07:00
if ( NODE _ENV === 'production' ) {
this . printConsoleWarning ( ) ;
}
2021-03-24 11:37:41 -07:00
}
2020-04-14 14:47:35 -07:00
render ( ) {
2020-03-27 13:59:38 -07:00
return (
2020-04-28 13:25:10 -07:00
< Provider store = { store } >
2021-04-22 11:57:47 -07:00
< SoapboxMount / >
2020-04-28 13:25:10 -07:00
< / P r o v i d e r >
2020-03-27 13:59:38 -07:00
) ;
}
}