kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'chunk' into 'main'
Improve chunking, reduce entrypoint to 200kb See merge request soapbox-pub/soapbox!2780environments/review-main-yi2y9f/deployments/4100
commit
690c678e80
|
@ -1,258 +0,0 @@
|
||||||
import { QueryClientProvider } from '@tanstack/react-query';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import React, { useState, useEffect, Suspense } from 'react';
|
|
||||||
import { Toaster } from 'react-hot-toast';
|
|
||||||
import { IntlProvider } from 'react-intl';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
|
|
||||||
import { CompatRouter } from 'react-router-dom-v5-compat';
|
|
||||||
// @ts-ignore: it doesn't have types
|
|
||||||
import { ScrollContext } from 'react-router-scroll-4';
|
|
||||||
|
|
||||||
import { loadInstance } from 'soapbox/actions/instance';
|
|
||||||
import { fetchMe } from 'soapbox/actions/me';
|
|
||||||
import { loadSoapboxConfig } from 'soapbox/actions/soapbox';
|
|
||||||
import * as BuildConfig from 'soapbox/build-config';
|
|
||||||
import GdprBanner from 'soapbox/components/gdpr-banner';
|
|
||||||
import Helmet from 'soapbox/components/helmet';
|
|
||||||
import LoadingScreen from 'soapbox/components/loading-screen';
|
|
||||||
import { StatProvider } from 'soapbox/contexts/stat-context';
|
|
||||||
import EmbeddedStatus from 'soapbox/features/embedded-status';
|
|
||||||
import {
|
|
||||||
ModalContainer,
|
|
||||||
OnboardingWizard,
|
|
||||||
} from 'soapbox/features/ui/util/async-components';
|
|
||||||
import { createGlobals } from 'soapbox/globals';
|
|
||||||
import {
|
|
||||||
useAppSelector,
|
|
||||||
useAppDispatch,
|
|
||||||
useOwnAccount,
|
|
||||||
useSentry,
|
|
||||||
useSettings,
|
|
||||||
useSoapboxConfig,
|
|
||||||
useTheme,
|
|
||||||
useLocale,
|
|
||||||
} from 'soapbox/hooks';
|
|
||||||
import MESSAGES from 'soapbox/messages';
|
|
||||||
import { normalizeSoapboxConfig } from 'soapbox/normalizers';
|
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
|
||||||
import { useCachedLocationHandler } from 'soapbox/utils/redirect';
|
|
||||||
import { generateThemeCss } from 'soapbox/utils/theme';
|
|
||||||
|
|
||||||
import { checkOnboardingStatus } from '../actions/onboarding';
|
|
||||||
import { preload } from '../actions/preload';
|
|
||||||
import ErrorBoundary from '../components/error-boundary';
|
|
||||||
import UI from '../features/ui';
|
|
||||||
import { store } from '../store';
|
|
||||||
|
|
||||||
// Configure global functions for developers
|
|
||||||
createGlobals(store);
|
|
||||||
|
|
||||||
// Preload happens synchronously
|
|
||||||
store.dispatch(preload() as any);
|
|
||||||
|
|
||||||
// This happens synchronously
|
|
||||||
store.dispatch(checkOnboardingStatus() as any);
|
|
||||||
|
|
||||||
/** Load initial data from the backend */
|
|
||||||
const loadInitial = () => {
|
|
||||||
// @ts-ignore
|
|
||||||
return async(dispatch, getState) => {
|
|
||||||
// Await for authenticated fetch
|
|
||||||
await dispatch(fetchMe());
|
|
||||||
// Await for feature detection
|
|
||||||
await dispatch(loadInstance());
|
|
||||||
// Await for configuration
|
|
||||||
await dispatch(loadSoapboxConfig());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Highest level node with the Redux store. */
|
|
||||||
const SoapboxMount = () => {
|
|
||||||
useCachedLocationHandler();
|
|
||||||
|
|
||||||
const me = useAppSelector(state => state.me);
|
|
||||||
const { account } = useOwnAccount();
|
|
||||||
const soapboxConfig = useSoapboxConfig();
|
|
||||||
|
|
||||||
const needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding);
|
|
||||||
const showOnboarding = account && needsOnboarding;
|
|
||||||
const { redirectRootNoLogin } = soapboxConfig;
|
|
||||||
|
|
||||||
// @ts-ignore: I don't actually know what these should be, lol
|
|
||||||
const shouldUpdateScroll = (prevRouterProps, { location }) => {
|
|
||||||
return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prevRouterProps?.location?.state?.soapboxModalKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Render the onboarding flow. */
|
|
||||||
const renderOnboarding = () => (
|
|
||||||
<Suspense fallback={<LoadingScreen />}>
|
|
||||||
<OnboardingWizard />
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Render the auth layout or UI. */
|
|
||||||
const renderSwitch = () => (
|
|
||||||
<Switch>
|
|
||||||
{(!me && redirectRootNoLogin) && (
|
|
||||||
<Redirect exact from='/' to={redirectRootNoLogin} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Route path='/' component={UI} />
|
|
||||||
</Switch>
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Render the onboarding flow or UI. */
|
|
||||||
const renderBody = () => {
|
|
||||||
if (showOnboarding) {
|
|
||||||
return renderOnboarding();
|
|
||||||
} else {
|
|
||||||
return renderSwitch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ErrorBoundary>
|
|
||||||
<BrowserRouter basename={BuildConfig.FE_SUBDIRECTORY}>
|
|
||||||
<CompatRouter>
|
|
||||||
<ScrollContext shouldUpdateScroll={shouldUpdateScroll}>
|
|
||||||
<Switch>
|
|
||||||
<Route
|
|
||||||
path='/embed/:statusId'
|
|
||||||
render={(props) => <EmbeddedStatus params={props.match.params} />}
|
|
||||||
/>
|
|
||||||
<Redirect from='/@:username/:statusId/embed' to='/embed/:statusId' />
|
|
||||||
|
|
||||||
<Route>
|
|
||||||
{renderBody()}
|
|
||||||
|
|
||||||
<ModalContainer />
|
|
||||||
<GdprBanner />
|
|
||||||
|
|
||||||
<div id='toaster'>
|
|
||||||
<Toaster
|
|
||||||
position='top-right'
|
|
||||||
containerClassName='top-10'
|
|
||||||
containerStyle={{ top: 75 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Route>
|
|
||||||
</Switch>
|
|
||||||
</ScrollContext>
|
|
||||||
</CompatRouter>
|
|
||||||
</BrowserRouter>
|
|
||||||
</ErrorBoundary>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ISoapboxLoad {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initial data loader. */
|
|
||||||
const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const me = useAppSelector(state => state.me);
|
|
||||||
const { account } = useOwnAccount();
|
|
||||||
const swUpdating = useAppSelector(state => state.meta.swUpdating);
|
|
||||||
const { locale } = useLocale();
|
|
||||||
|
|
||||||
const [messages, setMessages] = useState<Record<string, string>>({});
|
|
||||||
const [localeLoading, setLocaleLoading] = useState(true);
|
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
|
||||||
|
|
||||||
/** Whether to display a loading indicator. */
|
|
||||||
const showLoading = [
|
|
||||||
me === null,
|
|
||||||
me && !account,
|
|
||||||
!isLoaded,
|
|
||||||
localeLoading,
|
|
||||||
swUpdating,
|
|
||||||
].some(Boolean);
|
|
||||||
|
|
||||||
// Load the user's locale
|
|
||||||
useEffect(() => {
|
|
||||||
MESSAGES[locale]().then(messages => {
|
|
||||||
setMessages(messages);
|
|
||||||
setLocaleLoading(false);
|
|
||||||
}).catch(() => { });
|
|
||||||
}, [locale]);
|
|
||||||
|
|
||||||
// Load initial data from the API
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(loadInitial()).then(() => {
|
|
||||||
setIsLoaded(true);
|
|
||||||
}).catch(() => {
|
|
||||||
setIsLoaded(true);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// intl is part of loading.
|
|
||||||
// It's important nothing in here depends on intl.
|
|
||||||
if (showLoading) {
|
|
||||||
return <LoadingScreen />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IntlProvider locale={locale} messages={messages}>
|
|
||||||
{children}
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ISoapboxHead {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Injects metadata into site head with Helmet. */
|
|
||||||
const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
|
|
||||||
const { locale, direction } = useLocale();
|
|
||||||
const settings = useSettings();
|
|
||||||
const soapboxConfig = useSoapboxConfig();
|
|
||||||
|
|
||||||
const demo = !!settings.get('demo');
|
|
||||||
const darkMode = useTheme() === 'dark';
|
|
||||||
const themeCss = generateThemeCss(demo ? normalizeSoapboxConfig({ brandColor: '#0482d8' }) : soapboxConfig);
|
|
||||||
|
|
||||||
const bodyClass = clsx('h-full bg-white text-base dark:bg-gray-800', {
|
|
||||||
'no-reduce-motion': !settings.get('reduceMotion'),
|
|
||||||
'underline-links': settings.get('underlineLinks'),
|
|
||||||
'demetricator': settings.get('demetricator'),
|
|
||||||
});
|
|
||||||
|
|
||||||
useSentry(soapboxConfig.sentryDsn);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Helmet>
|
|
||||||
<html lang={locale} className={clsx('h-full', { dark: darkMode })} />
|
|
||||||
<body className={bodyClass} dir={direction} />
|
|
||||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
|
||||||
{darkMode && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
|
||||||
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
|
||||||
</Helmet>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The root React node of the application. */
|
|
||||||
const Soapbox: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<Provider store={store}>
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<StatProvider>
|
|
||||||
<SoapboxHead>
|
|
||||||
<SoapboxLoad>
|
|
||||||
<SoapboxMount />
|
|
||||||
</SoapboxLoad>
|
|
||||||
</SoapboxHead>
|
|
||||||
</StatProvider>
|
|
||||||
</QueryClientProvider>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Soapbox;
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||||
import { escape } from 'lodash';
|
import escape from 'lodash/escape';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
import { debounce } from 'lodash';
|
import debounce from 'lodash/debounce';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
useSentry,
|
||||||
|
useSettings,
|
||||||
|
useSoapboxConfig,
|
||||||
|
useTheme,
|
||||||
|
useLocale,
|
||||||
|
} from 'soapbox/hooks';
|
||||||
|
import { normalizeSoapboxConfig } from 'soapbox/normalizers';
|
||||||
|
import { generateThemeCss } from 'soapbox/utils/theme';
|
||||||
|
|
||||||
|
const Helmet = React.lazy(() => import('soapbox/components/helmet'));
|
||||||
|
|
||||||
|
interface ISoapboxHead {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Injects metadata into site head with Helmet. */
|
||||||
|
const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
|
||||||
|
const { locale, direction } = useLocale();
|
||||||
|
const settings = useSettings();
|
||||||
|
const soapboxConfig = useSoapboxConfig();
|
||||||
|
|
||||||
|
const demo = !!settings.get('demo');
|
||||||
|
const darkMode = useTheme() === 'dark';
|
||||||
|
const themeCss = generateThemeCss(demo ? normalizeSoapboxConfig({ brandColor: '#0482d8' }) : soapboxConfig);
|
||||||
|
|
||||||
|
const bodyClass = clsx('h-full bg-white text-base dark:bg-gray-800', {
|
||||||
|
'no-reduce-motion': !settings.get('reduceMotion'),
|
||||||
|
'underline-links': settings.get('underlineLinks'),
|
||||||
|
'demetricator': settings.get('demetricator'),
|
||||||
|
});
|
||||||
|
|
||||||
|
useSentry(soapboxConfig.sentryDsn);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Helmet>
|
||||||
|
<html lang={locale} className={clsx('h-full', { dark: darkMode })} />
|
||||||
|
<body className={bodyClass} dir={direction} />
|
||||||
|
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||||
|
{darkMode && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
||||||
|
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SoapboxHead;
|
|
@ -0,0 +1,85 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
|
|
||||||
|
import { loadInstance } from 'soapbox/actions/instance';
|
||||||
|
import { fetchMe } from 'soapbox/actions/me';
|
||||||
|
import { loadSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
|
import LoadingScreen from 'soapbox/components/loading-screen';
|
||||||
|
import {
|
||||||
|
useAppSelector,
|
||||||
|
useAppDispatch,
|
||||||
|
useOwnAccount,
|
||||||
|
useLocale,
|
||||||
|
} from 'soapbox/hooks';
|
||||||
|
import MESSAGES from 'soapbox/messages';
|
||||||
|
|
||||||
|
/** Load initial data from the backend */
|
||||||
|
const loadInitial = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
return async(dispatch, getState) => {
|
||||||
|
// Await for authenticated fetch
|
||||||
|
await dispatch(fetchMe());
|
||||||
|
// Await for feature detection
|
||||||
|
await dispatch(loadInstance());
|
||||||
|
// Await for configuration
|
||||||
|
await dispatch(loadSoapboxConfig());
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ISoapboxLoad {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initial data loader. */
|
||||||
|
const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const me = useAppSelector(state => state.me);
|
||||||
|
const { account } = useOwnAccount();
|
||||||
|
const swUpdating = useAppSelector(state => state.meta.swUpdating);
|
||||||
|
const { locale } = useLocale();
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState<Record<string, string>>({});
|
||||||
|
const [localeLoading, setLocaleLoading] = useState(true);
|
||||||
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
|
|
||||||
|
/** Whether to display a loading indicator. */
|
||||||
|
const showLoading = [
|
||||||
|
me === null,
|
||||||
|
me && !account,
|
||||||
|
!isLoaded,
|
||||||
|
localeLoading,
|
||||||
|
swUpdating,
|
||||||
|
].some(Boolean);
|
||||||
|
|
||||||
|
// Load the user's locale
|
||||||
|
useEffect(() => {
|
||||||
|
MESSAGES[locale]().then(messages => {
|
||||||
|
setMessages(messages);
|
||||||
|
setLocaleLoading(false);
|
||||||
|
}).catch(() => { });
|
||||||
|
}, [locale]);
|
||||||
|
|
||||||
|
// Load initial data from the API
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(loadInitial()).then(() => {
|
||||||
|
setIsLoaded(true);
|
||||||
|
}).catch(() => {
|
||||||
|
setIsLoaded(true);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// intl is part of loading.
|
||||||
|
// It's important nothing in here depends on intl.
|
||||||
|
if (showLoading) {
|
||||||
|
return <LoadingScreen />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
|
{children}
|
||||||
|
</IntlProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SoapboxLoad;
|
|
@ -0,0 +1,105 @@
|
||||||
|
import React, { Suspense } from 'react';
|
||||||
|
import { Toaster } from 'react-hot-toast';
|
||||||
|
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
|
||||||
|
import { CompatRouter } from 'react-router-dom-v5-compat';
|
||||||
|
// @ts-ignore: it doesn't have types
|
||||||
|
import { ScrollContext } from 'react-router-scroll-4';
|
||||||
|
|
||||||
|
import * as BuildConfig from 'soapbox/build-config';
|
||||||
|
import LoadingScreen from 'soapbox/components/loading-screen';
|
||||||
|
import {
|
||||||
|
ModalContainer,
|
||||||
|
OnboardingWizard,
|
||||||
|
} from 'soapbox/features/ui/util/async-components';
|
||||||
|
import {
|
||||||
|
useAppSelector,
|
||||||
|
useOwnAccount,
|
||||||
|
useSoapboxConfig,
|
||||||
|
} from 'soapbox/hooks';
|
||||||
|
import { useCachedLocationHandler } from 'soapbox/utils/redirect';
|
||||||
|
|
||||||
|
import ErrorBoundary from '../components/error-boundary';
|
||||||
|
|
||||||
|
const GdprBanner = React.lazy(() => import('soapbox/components/gdpr-banner'));
|
||||||
|
const EmbeddedStatus = React.lazy(() => import('soapbox/features/embedded-status'));
|
||||||
|
const UI = React.lazy(() => import('soapbox/features/ui'));
|
||||||
|
|
||||||
|
/** Highest level node with the Redux store. */
|
||||||
|
const SoapboxMount = () => {
|
||||||
|
useCachedLocationHandler();
|
||||||
|
|
||||||
|
const me = useAppSelector(state => state.me);
|
||||||
|
const { account } = useOwnAccount();
|
||||||
|
const soapboxConfig = useSoapboxConfig();
|
||||||
|
|
||||||
|
const needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding);
|
||||||
|
const showOnboarding = account && needsOnboarding;
|
||||||
|
const { redirectRootNoLogin } = soapboxConfig;
|
||||||
|
|
||||||
|
// @ts-ignore: I don't actually know what these should be, lol
|
||||||
|
const shouldUpdateScroll = (prevRouterProps, { location }) => {
|
||||||
|
return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prevRouterProps?.location?.state?.soapboxModalKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Render the onboarding flow. */
|
||||||
|
const renderOnboarding = () => (
|
||||||
|
<Suspense fallback={<LoadingScreen />}>
|
||||||
|
<OnboardingWizard />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Render the auth layout or UI. */
|
||||||
|
const renderSwitch = () => (
|
||||||
|
<Switch>
|
||||||
|
{(!me && redirectRootNoLogin) && (
|
||||||
|
<Redirect exact from='/' to={redirectRootNoLogin} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Route path='/' component={UI} />
|
||||||
|
</Switch>
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Render the onboarding flow or UI. */
|
||||||
|
const renderBody = () => {
|
||||||
|
if (showOnboarding) {
|
||||||
|
return renderOnboarding();
|
||||||
|
} else {
|
||||||
|
return renderSwitch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<BrowserRouter basename={BuildConfig.FE_SUBDIRECTORY}>
|
||||||
|
<CompatRouter>
|
||||||
|
<ScrollContext shouldUpdateScroll={shouldUpdateScroll}>
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
path='/embed/:statusId'
|
||||||
|
render={(props) => <EmbeddedStatus params={props.match.params} />}
|
||||||
|
/>
|
||||||
|
<Redirect from='/@:username/:statusId/embed' to='/embed/:statusId' />
|
||||||
|
|
||||||
|
<Route>
|
||||||
|
{renderBody()}
|
||||||
|
|
||||||
|
<ModalContainer />
|
||||||
|
<GdprBanner />
|
||||||
|
|
||||||
|
<div id='toaster'>
|
||||||
|
<Toaster
|
||||||
|
position='top-right'
|
||||||
|
containerClassName='top-10'
|
||||||
|
containerStyle={{ top: 75 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</ScrollContext>
|
||||||
|
</CompatRouter>
|
||||||
|
</BrowserRouter>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SoapboxMount;
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
|
import { StatProvider } from 'soapbox/contexts/stat-context';
|
||||||
|
import { createGlobals } from 'soapbox/globals';
|
||||||
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
|
|
||||||
|
import { checkOnboardingStatus } from '../actions/onboarding';
|
||||||
|
import { preload } from '../actions/preload';
|
||||||
|
import { store } from '../store';
|
||||||
|
|
||||||
|
const SoapboxHead = React.lazy(() => import('./soapbox-head'));
|
||||||
|
const SoapboxLoad = React.lazy(() => import('./soapbox-load'));
|
||||||
|
const SoapboxMount = React.lazy(() => import('./soapbox-mount'));
|
||||||
|
|
||||||
|
// Configure global functions for developers
|
||||||
|
createGlobals(store);
|
||||||
|
|
||||||
|
// Preload happens synchronously
|
||||||
|
store.dispatch(preload() as any);
|
||||||
|
|
||||||
|
// This happens synchronously
|
||||||
|
store.dispatch(checkOnboardingStatus() as any);
|
||||||
|
|
||||||
|
/** The root React node of the application. */
|
||||||
|
const Soapbox: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<StatProvider>
|
||||||
|
<SoapboxHead>
|
||||||
|
<SoapboxLoad>
|
||||||
|
<SoapboxMount />
|
||||||
|
</SoapboxLoad>
|
||||||
|
</SoapboxHead>
|
||||||
|
</StatProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Soapbox;
|
|
@ -22,10 +22,11 @@ import './styles/application.scss';
|
||||||
import './styles/tailwind.css';
|
import './styles/tailwind.css';
|
||||||
|
|
||||||
import './precheck';
|
import './precheck';
|
||||||
import { default as Soapbox } from './containers/soapbox';
|
|
||||||
import ready from './ready';
|
import ready from './ready';
|
||||||
import { registerSW } from './utils/sw';
|
import { registerSW } from './utils/sw';
|
||||||
|
|
||||||
|
const Soapbox = React.lazy(() => import('./init/soapbox'));
|
||||||
|
|
||||||
if (BuildConfig.NODE_ENV === 'production') {
|
if (BuildConfig.NODE_ENV === 'production') {
|
||||||
printConsoleWarning();
|
printConsoleWarning();
|
||||||
registerSW('/sw.js');
|
registerSW('/sw.js');
|
||||||
|
|
Ładowanie…
Reference in New Issue