Merge branch 'next-container' into 'next'
Next: convert Soapbox container to TSX+FC See merge request soapbox-pub/soapbox-fe!1248
This commit is contained in:
commit
b531115c02
|
@ -256,7 +256,6 @@ module.exports = {
|
||||||
'promise/catch-or-return': 'error',
|
'promise/catch-or-return': 'error',
|
||||||
|
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
'react-hooks/exhaustive-deps': 'warn',
|
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import BuildConfig from 'soapbox/build_config';
|
import * as BuildConfig from 'soapbox/build_config';
|
||||||
import { Text, Stack } from 'soapbox/components/ui';
|
import { Text, Stack } from 'soapbox/components/ui';
|
||||||
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
||||||
import { captureException } from 'soapbox/monitoring';
|
import { captureException } from 'soapbox/monitoring';
|
||||||
|
|
|
@ -1,276 +0,0 @@
|
||||||
'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 { NODE_ENV } 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 (
|
|
||||||
<IntlProvider locale={locale} messages={this.state.messages}>
|
|
||||||
<Helmet>
|
|
||||||
<html lang='en' className={classNames({ dark: this.props.themeMode === 'dark' })} />
|
|
||||||
<body className={bodyClass} />
|
|
||||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
|
||||||
<meta name='theme-color' content={this.props.brandColor} />
|
|
||||||
</Helmet>
|
|
||||||
|
|
||||||
<ErrorBoundary>
|
|
||||||
<BrowserRouter basename={FE_SUBDIRECTORY}>
|
|
||||||
<OnboardingWizard />
|
|
||||||
<NotificationsContainer />
|
|
||||||
</BrowserRouter>
|
|
||||||
</ErrorBoundary>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<IntlProvider locale={locale} messages={this.state.messages}>
|
|
||||||
<Helmet>
|
|
||||||
<html lang='en' className={classNames({ dark: this.props.themeMode === 'dark' })} />
|
|
||||||
<body className={bodyClass} />
|
|
||||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
|
||||||
<meta name='theme-color' content={this.props.brandColor} />
|
|
||||||
|
|
||||||
{this.props.appleAppId && (
|
|
||||||
<meta name='apple-itunes-app' content={`app-id=${this.props.appleAppId}`} />
|
|
||||||
)}
|
|
||||||
</Helmet>
|
|
||||||
|
|
||||||
<ErrorBoundary>
|
|
||||||
<BrowserRouter basename={FE_SUBDIRECTORY}>
|
|
||||||
<>
|
|
||||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
|
||||||
<Switch>
|
|
||||||
<Redirect from='/v1/verify_email/:token' to='/auth/verify/email/:token' />
|
|
||||||
|
|
||||||
{waitlisted && <Route render={(props) => <WaitlistPage {...props} account={account} />} />}
|
|
||||||
|
|
||||||
{!me && (singleUserMode
|
|
||||||
? <Redirect exact from='/' to={`/${singleUserMode}`} />
|
|
||||||
: <Route exact path='/' component={PublicLayout} />)}
|
|
||||||
|
|
||||||
{!me && <Route exact path='/' component={PublicLayout} />}
|
|
||||||
<Route exact path='/about/:slug?' component={PublicLayout} />
|
|
||||||
<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} />
|
|
||||||
|
|
||||||
<Route path='/' component={UI} />
|
|
||||||
</Switch>
|
|
||||||
</ScrollContext>
|
|
||||||
</>
|
|
||||||
</BrowserRouter>
|
|
||||||
</ErrorBoundary>
|
|
||||||
</IntlProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Soapbox extends React.PureComponent {
|
|
||||||
|
|
||||||
printConsoleWarning = () => {
|
|
||||||
/* eslint-disable no-console */
|
|
||||||
console.log('%cStop!', [
|
|
||||||
'color: #ff0000',
|
|
||||||
'display: block',
|
|
||||||
'font-family: system-ui, -apple-system, BlinkMacSystemFont, Ubuntu, "Helvetica Neue", sans-serif',
|
|
||||||
'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',
|
|
||||||
'font-family: system-ui, -apple-system, BlinkMacSystemFont, Ubuntu, "Helvetica Neue", sans-serif',
|
|
||||||
'font-size: 18px',
|
|
||||||
'padding: 4px 0 16px',
|
|
||||||
].join(';'));
|
|
||||||
/* eslint-enable no-console */
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (NODE_ENV === 'production') {
|
|
||||||
this.printConsoleWarning();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Provider store={store}>
|
|
||||||
<SoapboxMount />
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
|
||||||
|
// @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 { fetchVerificationConfig } from 'soapbox/actions/verification';
|
||||||
|
import * as BuildConfig 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 { useAppSelector, useAppDispatch, useOwnAccount, useSoapboxConfig, useSettings } from 'soapbox/hooks';
|
||||||
|
import MESSAGES from 'soapbox/locales/messages';
|
||||||
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
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';
|
||||||
|
|
||||||
|
/** Ensure the given locale exists in our codebase */
|
||||||
|
const validLocale = (locale: string): boolean => Object.keys(MESSAGES).includes(locale);
|
||||||
|
|
||||||
|
// Configure global functions for developers
|
||||||
|
createGlobals(store);
|
||||||
|
|
||||||
|
// Preload happens synchronously
|
||||||
|
store.dispatch(preload() 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());
|
||||||
|
|
||||||
|
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 SoapboxMount = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const me = useAppSelector(state => state.me);
|
||||||
|
const account = useOwnAccount();
|
||||||
|
const settings = useSettings();
|
||||||
|
const soapboxConfig = useSoapboxConfig();
|
||||||
|
|
||||||
|
const locale = validLocale(settings.get('locale')) ? settings.get('locale') : 'en';
|
||||||
|
|
||||||
|
const needsOnboarding = settings.get('onboardingVersion') < ONBOARDING_VERSION;
|
||||||
|
const singleUserMode = soapboxConfig.singleUserMode && soapboxConfig.singleUserModeProfile;
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState<Record<string, string>>({});
|
||||||
|
const [localeLoading, setLocaleLoading] = useState(true);
|
||||||
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
|
|
||||||
|
const themeCss = generateThemeCss(soapboxConfig);
|
||||||
|
|
||||||
|
// 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(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// @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);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (me === null) return null;
|
||||||
|
if (me && !account) return null;
|
||||||
|
if (!isLoaded) return null;
|
||||||
|
if (localeLoading) return null;
|
||||||
|
|
||||||
|
const waitlisted = account && !account.source.get('approved', true);
|
||||||
|
|
||||||
|
const bodyClass = classNames('bg-white dark:bg-slate-900 text-base', {
|
||||||
|
'no-reduce-motion': !settings.get('reduceMotion'),
|
||||||
|
'underline-links': settings.get('underlineLinks'),
|
||||||
|
'dyslexic': settings.get('dyslexicFont'),
|
||||||
|
'demetricator': settings.get('demetricator'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (account && !waitlisted && needsOnboarding) {
|
||||||
|
return (
|
||||||
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
|
<Helmet>
|
||||||
|
<html lang={locale} className={classNames({ dark: settings.get('themeMode') === 'dark' })} />
|
||||||
|
<body className={bodyClass} />
|
||||||
|
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||||
|
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
<ErrorBoundary>
|
||||||
|
<BrowserRouter basename={BuildConfig.FE_SUBDIRECTORY}>
|
||||||
|
<OnboardingWizard />
|
||||||
|
<NotificationsContainer />
|
||||||
|
</BrowserRouter>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</IntlProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
|
<Helmet>
|
||||||
|
<html lang={locale} className={classNames({ dark: settings.get('themeMode') === 'dark' })} />
|
||||||
|
<body className={bodyClass} />
|
||||||
|
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||||
|
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
||||||
|
|
||||||
|
{soapboxConfig.appleAppId && (
|
||||||
|
<meta name='apple-itunes-app' content={`app-id=${soapboxConfig.appleAppId}`} />
|
||||||
|
)}
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
<ErrorBoundary>
|
||||||
|
<BrowserRouter basename={BuildConfig.FE_SUBDIRECTORY}>
|
||||||
|
<>
|
||||||
|
<ScrollContext shouldUpdateScroll={shouldUpdateScroll}>
|
||||||
|
<Switch>
|
||||||
|
<Redirect from='/v1/verify_email/:token' to='/auth/verify/email/:token' />
|
||||||
|
|
||||||
|
{waitlisted && <Route render={(props) => <WaitlistPage {...props} account={account} />} />}
|
||||||
|
|
||||||
|
{!me && (singleUserMode
|
||||||
|
? <Redirect exact from='/' to={`/${singleUserMode}`} />
|
||||||
|
: <Route exact path='/' component={PublicLayout} />)}
|
||||||
|
|
||||||
|
{!me && <Route exact path='/' component={PublicLayout} />}
|
||||||
|
<Route exact path='/about/:slug?' component={PublicLayout} />
|
||||||
|
<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} />
|
||||||
|
|
||||||
|
<Route path='/' component={UI} />
|
||||||
|
</Switch>
|
||||||
|
</ScrollContext>
|
||||||
|
</>
|
||||||
|
</BrowserRouter>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</IntlProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Soapbox = () => {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<SoapboxMount />
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Soapbox;
|
|
@ -16,7 +16,7 @@ const Logout: React.FC = () => {
|
||||||
dispatch(logOut(intl) as any)
|
dispatch(logOut(intl) as any)
|
||||||
.then(() => setDone(true))
|
.then(() => setDone(true))
|
||||||
.catch(console.warn);
|
.catch(console.warn);
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
return <Redirect to='/' />;
|
return <Redirect to='/' />;
|
||||||
|
|
|
@ -36,14 +36,14 @@ const ExternalLoginForm: React.FC = () => {
|
||||||
if (code) {
|
if (code) {
|
||||||
dispatch(loginWithCode(code));
|
dispatch(loginWithCode(code));
|
||||||
}
|
}
|
||||||
});
|
}, [code]);
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit} data-testid='external-login'>
|
||||||
<FormGroup labelText={intl.formatMessage(messages.instanceLabel)}>
|
<FormGroup labelText={intl.formatMessage(messages.instanceLabel)}>
|
||||||
<Input
|
<Input
|
||||||
aria-label={intl.formatMessage(messages.instancePlaceholder)}
|
aria-label={intl.formatMessage(messages.instancePlaceholder)}
|
||||||
|
|
|
@ -66,7 +66,7 @@ const LandingPage = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className='mt-16 sm:mt-24'>
|
<main className='mt-16 sm:mt-24' data-testid='homepage'>
|
||||||
<div className='mx-auto max-w-7xl'>
|
<div className='mx-auto max-w-7xl'>
|
||||||
<div className='lg:grid lg:grid-cols-12 lg:gap-8 py-12'>
|
<div className='lg:grid lg:grid-cols-12 lg:gap-8 py-12'>
|
||||||
<div className='px-4 sm:px-6 sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left lg:flex'>
|
<div className='px-4 sm:px-6 sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left lg:flex'>
|
||||||
|
|
|
@ -67,7 +67,7 @@ const OnboardingWizard = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div data-testid='onboarding-wizard'>
|
||||||
<div className='fixed h-screen w-full bg-gradient-to-tr from-primary-50 via-white to-cyan-50' />
|
<div className='fixed h-screen w-full bg-gradient-to-tr from-primary-50 via-white to-cyan-50' />
|
||||||
|
|
||||||
<main className='h-screen flex flex-col'>
|
<main className='h-screen flex flex-col'>
|
||||||
|
|
|
@ -20,7 +20,7 @@ const Pulse: React.FC = () => {
|
||||||
setAnimationData(json);
|
setAnimationData(json);
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
if (animationData) {
|
if (animationData) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -52,5 +52,5 @@ export default function SitePreview({ soapbox }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SitePreview.propTypes = {
|
SitePreview.propTypes = {
|
||||||
soapbox: ImmutablePropTypes.map.isRequired,
|
soapbox: ImmutablePropTypes.record.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,8 +18,8 @@ const PromoPanel: React.FC = () => {
|
||||||
<Widget title={siteTitle}>
|
<Widget title={siteTitle}>
|
||||||
<Stack space={2}>
|
<Stack space={2}>
|
||||||
{promoItems.map((item, i) => (
|
{promoItems.map((item, i) => (
|
||||||
<Text>
|
<Text key={i}>
|
||||||
<a className='flex items-center' href={item.url} target='_blank' key={i}>
|
<a className='flex items-center' href={item.url} target='_blank'>
|
||||||
<Icon id={item.icon} className='flex-none text-lg mr-2' fixedWidth />
|
<Icon id={item.icon} className='flex-none text-lg mr-2' fixedWidth />
|
||||||
{item.textLocales.get(locale) || item.text}
|
{item.textLocales.get(locale) || item.text}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -188,7 +188,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
onLayoutChange: PropTypes.func.isRequired,
|
onLayoutChange: PropTypes.func.isRequired,
|
||||||
soapbox: ImmutablePropTypes.map.isRequired,
|
soapbox: ImmutablePropTypes.record.isRequired,
|
||||||
features: PropTypes.object.isRequired,
|
features: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ class UI extends React.PureComponent {
|
||||||
streamingUrl: PropTypes.string,
|
streamingUrl: PropTypes.string,
|
||||||
account: PropTypes.object,
|
account: PropTypes.object,
|
||||||
features: PropTypes.object.isRequired,
|
features: PropTypes.object.isRequired,
|
||||||
soapbox: ImmutablePropTypes.map.isRequired,
|
soapbox: ImmutablePropTypes.record.isRequired,
|
||||||
vapidKey: PropTypes.string,
|
vapidKey: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,12 @@ import { __clear as clearApiMocks } from '../__mocks__/api';
|
||||||
jest.mock('soapbox/api');
|
jest.mock('soapbox/api');
|
||||||
afterEach(() => clearApiMocks());
|
afterEach(() => clearApiMocks());
|
||||||
|
|
||||||
|
// Mock IndexedDB
|
||||||
|
// https://dev.to/andyhaskell/testing-your-indexeddb-code-with-jest-2o17
|
||||||
|
require('fake-indexeddb/auto');
|
||||||
|
|
||||||
// Mock external dependencies
|
// Mock external dependencies
|
||||||
jest.mock('uuid', () => ({ v4: jest.fn(() => 1) }));
|
jest.mock('uuid', () => ({ v4: jest.fn(() => '1') }));
|
||||||
|
|
||||||
const intersectionObserverMock = () => ({ observe: () => null, disconnect: () => null });
|
const intersectionObserverMock = () => ({ observe: () => null, disconnect: () => null });
|
||||||
window.IntersectionObserver = jest.fn().mockImplementation(intersectionObserverMock);
|
window.IntersectionObserver = jest.fn().mockImplementation(intersectionObserverMock);
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
// Import custom messages
|
type MessageJson = Record<string, string>;
|
||||||
const importCustom = locale => {
|
type MessageModule = { default: MessageJson };
|
||||||
|
|
||||||
|
/** Import custom messages */
|
||||||
|
const importCustom = (locale: string): Promise<MessageModule> => {
|
||||||
return import(/* webpackChunkName: "locale_[request]" */`custom/locales/${locale}.json`)
|
return import(/* webpackChunkName: "locale_[request]" */`custom/locales/${locale}.json`)
|
||||||
.catch(error => ({ default: {} }));
|
.catch(() => ({ default: {} }));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Import git-checked messages
|
/** Import git-checked messages */
|
||||||
const importMessages = locale => {
|
const importMessages = (locale: string): Promise<MessageModule> => {
|
||||||
return import(/* webpackChunkName: "locale_[request]" */`./${locale}.json`);
|
return import(/* webpackChunkName: "locale_[request]" */`./${locale}.json`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Override custom messages
|
/** Override custom messages */
|
||||||
const importMessagesWithCustom = locale => {
|
const importMessagesWithCustom = (locale: string): Promise<MessageJson> => {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
importMessages(locale),
|
importMessages(locale),
|
||||||
importCustom(locale),
|
importCustom(locale),
|
||||||
|
@ -23,7 +26,7 @@ const importMessagesWithCustom = locale => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const locales = [
|
const locales: string[] = [
|
||||||
'ar',
|
'ar',
|
||||||
'ast',
|
'ast',
|
||||||
'bg',
|
'bg',
|
||||||
|
@ -89,10 +92,10 @@ const locales = [
|
||||||
'zh-TW',
|
'zh-TW',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Build the export
|
/** Soapbox locales map */
|
||||||
const messages = locales.reduce((acc, locale) => {
|
const messages = locales.reduce((acc, locale) => {
|
||||||
acc[locale] = () => importMessagesWithCustom(locale);
|
acc[locale] = () => importMessagesWithCustom(locale);
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {} as Record<string, () => Promise<MessageJson>>);
|
||||||
|
|
||||||
export default messages;
|
export default messages;
|
|
@ -6,6 +6,7 @@ import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import * as BuildConfig from 'soapbox/build_config';
|
import * as BuildConfig from 'soapbox/build_config';
|
||||||
|
import { printConsoleWarning } from 'soapbox/utils/console';
|
||||||
|
|
||||||
import { default as Soapbox } from './containers/soapbox';
|
import { default as Soapbox } from './containers/soapbox';
|
||||||
import * as monitoring from './monitoring';
|
import * as monitoring from './monitoring';
|
||||||
|
@ -18,6 +19,11 @@ function main() {
|
||||||
// Sentry
|
// Sentry
|
||||||
monitoring.start();
|
monitoring.start();
|
||||||
|
|
||||||
|
// Print console warning
|
||||||
|
if (BuildConfig.NODE_ENV === 'production') {
|
||||||
|
printConsoleWarning();
|
||||||
|
}
|
||||||
|
|
||||||
ready(() => {
|
ready(() => {
|
||||||
const mountNode = document.getElementById('soapbox') as HTMLElement;
|
const mountNode = document.getElementById('soapbox') as HTMLElement;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/** Print a warning to users not to copy-paste into the console */
|
||||||
|
const printConsoleWarning = () => {
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
console.log('%cStop!', [
|
||||||
|
'color: #ff0000',
|
||||||
|
'display: block',
|
||||||
|
'font-family: system-ui, -apple-system, BlinkMacSystemFont, Ubuntu, "Helvetica Neue", sans-serif',
|
||||||
|
'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',
|
||||||
|
'font-family: system-ui, -apple-system, BlinkMacSystemFont, Ubuntu, "Helvetica Neue", sans-serif',
|
||||||
|
'font-size: 18px',
|
||||||
|
'padding: 4px 0 16px',
|
||||||
|
].join(';'));
|
||||||
|
/* eslint-enable no-console */
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
printConsoleWarning,
|
||||||
|
};
|
|
@ -211,6 +211,7 @@
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
"eslint-plugin-react": "^7.25.1",
|
"eslint-plugin-react": "^7.25.1",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
|
"fake-indexeddb": "^3.1.7",
|
||||||
"husky": "^7.0.2",
|
"husky": "^7.0.2",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"lint-staged": ">=10",
|
"lint-staged": ">=10",
|
||||||
|
|
55
yarn.lock
55
yarn.lock
|
@ -3084,6 +3084,11 @@ balanced-match@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
|
||||||
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
|
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
|
||||||
|
|
||||||
|
base64-arraybuffer-es6@^0.7.0:
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86"
|
||||||
|
integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==
|
||||||
|
|
||||||
batch@0.6.1:
|
batch@0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
|
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
|
||||||
|
@ -3708,6 +3713,11 @@ core-js@^3.1.3, core-js@^3.15.2:
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.0.tgz#9af3f4a6df9ba3428a3fb1b171f1503b3f40cc49"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.0.tgz#9af3f4a6df9ba3428a3fb1b171f1503b3f40cc49"
|
||||||
integrity sha512-WJeQqq6jOYgVgg4NrXKL0KLQhi0CT4ZOCvFL+3CQ5o7I6J8HkT5wd53EadMfqTDp1so/MT1J+w2ujhWcCJtN7w==
|
integrity sha512-WJeQqq6jOYgVgg4NrXKL0KLQhi0CT4ZOCvFL+3CQ5o7I6J8HkT5wd53EadMfqTDp1so/MT1J+w2ujhWcCJtN7w==
|
||||||
|
|
||||||
|
core-js@^3.4:
|
||||||
|
version "3.22.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.2.tgz#3ea0a245b0895fa39d1faa15fe75d91ade504a01"
|
||||||
|
integrity sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA==
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||||
|
@ -4296,6 +4306,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
||||||
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
||||||
|
|
||||||
|
domexception@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
|
||||||
|
integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
|
||||||
|
dependencies:
|
||||||
|
webidl-conversions "^4.0.2"
|
||||||
|
|
||||||
domexception@^2.0.1:
|
domexception@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
|
resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
|
||||||
|
@ -4927,6 +4944,13 @@ extend@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||||
|
|
||||||
|
fake-indexeddb@^3.1.7:
|
||||||
|
version "3.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-3.1.7.tgz#d9efbeade113c15efbe862e4598a4b0a1797ed9f"
|
||||||
|
integrity sha512-CUGeCzCOVjmeKi2C0pcvSh6NDU6uQIaS+7YyR++tO/atJJujkBYVhDvfePdz/U8bD33BMVWirsr1MKczfAqbjA==
|
||||||
|
dependencies:
|
||||||
|
realistic-structured-clone "^2.0.1"
|
||||||
|
|
||||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||||
|
@ -9049,6 +9073,16 @@ readdirp@~3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
|
realistic-structured-clone@^2.0.1:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.4.tgz#7eb4c2319fc3cb72f4c8d3c9e888b11647894b50"
|
||||||
|
integrity sha512-lItAdBIFHUSe6fgztHPtmmWqKUgs+qhcYLi3wTRUl4OTB3Vb8aBVSjGfQZUvkmJCKoX3K9Wf7kyLp/F/208+7A==
|
||||||
|
dependencies:
|
||||||
|
core-js "^3.4"
|
||||||
|
domexception "^1.0.1"
|
||||||
|
typeson "^6.1.0"
|
||||||
|
typeson-registry "^1.0.0-alpha.20"
|
||||||
|
|
||||||
rechoir@^0.7.0:
|
rechoir@^0.7.0:
|
||||||
version "0.7.1"
|
version "0.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
||||||
|
@ -10440,6 +10474,20 @@ typescript@^4.4.4:
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||||
|
|
||||||
|
typeson-registry@^1.0.0-alpha.20:
|
||||||
|
version "1.0.0-alpha.39"
|
||||||
|
resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.39.tgz#9e0f5aabd5eebfcffd65a796487541196f4b1211"
|
||||||
|
integrity sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer-es6 "^0.7.0"
|
||||||
|
typeson "^6.0.0"
|
||||||
|
whatwg-url "^8.4.0"
|
||||||
|
|
||||||
|
typeson@^6.0.0, typeson@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/typeson/-/typeson-6.1.0.tgz#5b2a53705a5f58ff4d6f82f965917cabd0d7448b"
|
||||||
|
integrity sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA==
|
||||||
|
|
||||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||||
|
@ -10730,6 +10778,11 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
|
|
||||||
|
webidl-conversions@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||||
|
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
||||||
|
|
||||||
webidl-conversions@^5.0.0:
|
webidl-conversions@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
|
||||||
|
@ -10914,7 +10967,7 @@ whatwg-mimetype@^2.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
||||||
|
|
||||||
whatwg-url@^8.0.0, whatwg-url@^8.5.0:
|
whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0:
|
||||||
version "8.7.0"
|
version "8.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
|
||||||
integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==
|
integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==
|
||||||
|
|
Loading…
Reference in New Issue