Refactor ErrorBoundary, let it destroy the ServiceWorker

revert-5af0e40a
Alex Gleason 2022-04-19 16:30:10 -05:00
rodzic 0c98157fb3
commit 45018a6fb0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
2 zmienionych plików z 74 dodań i 45 usunięć

Wyświetl plik

@ -1,43 +1,58 @@
import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { NODE_ENV } from 'soapbox/build_config'; import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import 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 { captureException } from 'soapbox/monitoring'; import { captureException } from 'soapbox/monitoring';
import sourceCode from 'soapbox/utils/code'; import sourceCode from 'soapbox/utils/code';
import { getSoapboxConfig } from '../actions/soapbox'; import type { RootState } from 'soapbox/store';
const mapStateToProps = (state) => { const goHome = () => location.href = '/';
const soapboxConfig = getSoapboxConfig(state);
/** Unregister the ServiceWorker */
// https://stackoverflow.com/a/49771828/8811886
const unregisterSw = async() => {
if (!navigator.serviceWorker) return;
const registrations = await navigator.serviceWorker.getRegistrations();
const unregisterAll = registrations.map(r => r.unregister());
await Promise.all(unregisterAll);
};
const mapStateToProps = (state: RootState) => {
const { links, logo } = getSoapboxConfig(state);
return { return {
siteTitle: state.instance.title, siteTitle: state.instance.title,
helpLink: soapboxConfig.getIn(['links', 'help']), logo,
supportLink: soapboxConfig.getIn(['links', 'support']), links,
statusLink: soapboxConfig.getIn(['links', 'status']),
}; };
}; };
@connect(mapStateToProps) type Props = ReturnType<typeof mapStateToProps>;
class ErrorBoundary extends React.PureComponent {
static propTypes = { type State = {
children: PropTypes.node, hasError: boolean,
siteTitle: PropTypes.string, error: any,
supportLink: PropTypes.string, componentStack: any,
helpLink: PropTypes.string, browser?: Bowser.Parser.Parser,
statusLink: PropTypes.string, }
};
state = { class ErrorBoundary extends React.PureComponent<Props, State> {
state: State = {
hasError: false, hasError: false,
error: undefined,
componentStack: undefined, componentStack: undefined,
browser: undefined,
} }
componentDidCatch(error, info) { textarea: HTMLTextAreaElement | null = null;
componentDidCatch(error: any, info: any): void {
captureException(error); captureException(error);
this.setState({ this.setState({
@ -55,11 +70,11 @@ class ErrorBoundary extends React.PureComponent {
.catch(() => {}); .catch(() => {});
} }
setTextareaRef = c => { setTextareaRef: React.RefCallback<HTMLTextAreaElement> = c => {
this.textarea = c; this.textarea = c;
} }
handleCopy = e => { handleCopy: React.MouseEventHandler = () => {
if (!this.textarea) return; if (!this.textarea) return;
this.textarea.select(); this.textarea.select();
@ -68,25 +83,30 @@ class ErrorBoundary extends React.PureComponent {
document.execCommand('copy'); document.execCommand('copy');
} }
getErrorText = () => { getErrorText = (): string => {
const { error, componentStack } = this.state; const { error, componentStack } = this.state;
return error + componentStack; return error + componentStack;
} }
clearCookies = e => { clearCookies: React.MouseEventHandler = (e) => {
localStorage.clear(); localStorage.clear();
sessionStorage.clear(); sessionStorage.clear();
if ('serviceWorker' in navigator) {
e.preventDefault();
unregisterSw().then(goHome).catch(goHome);
}
} }
render() { render() {
const { browser, hasError } = this.state; const { browser, hasError } = this.state;
const { children, siteTitle, helpLink, statusLink, supportLink } = this.props; const { children, siteTitle, logo, links } = this.props;
if (!hasError) { if (!hasError) {
return children; return children;
} }
const isProduction = NODE_ENV === 'production'; const isProduction = BuildConfig.NODE_ENV === 'production';
const errorText = this.getErrorText(); const errorText = this.getErrorText();
@ -95,7 +115,11 @@ class ErrorBoundary extends React.PureComponent {
<main className='flex-grow flex flex-col justify-center max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'> <main className='flex-grow flex flex-col justify-center max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'>
<div className='flex-shrink-0 flex justify-center'> <div className='flex-shrink-0 flex justify-center'>
<a href='/' className='inline-flex'> <a href='/' className='inline-flex'>
<img className='h-12 w-12' src='/instance/images/app-icon.png' alt={siteTitle} /> {logo ? (
<img className='h-12 w-12' src={logo} alt={siteTitle} />
) : (
<SvgIcon className='h-12 w-12' src={require('@tabler/icons/icons/home.svg')} alt={siteTitle} />
)}
</a> </a>
</div> </div>
@ -105,14 +129,18 @@ class ErrorBoundary extends React.PureComponent {
<FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' /> <FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' />
</h1> </h1>
<p className='text-lg text-gray-500'> <p className='text-lg text-gray-500'>
We're sorry for the interruption. If the problem persists, please reach out to our support team. You <FormattedMessage
may also try to <a href='/' onClick={this.clearCookies} className='text-gray-700 hover:underline'> id='alert.unexpected.body'
<FormattedMessage defaultMessage="We're sorry for the interruption. If the problem persists, please reach out to our support team. You may also try to {clearCookies} (this will log you out)."
id='alert.unexpected.clear_cookies' values={{ clearCookies: (
defaultMessage='clear cookies and browser data' <a href='/' onClick={this.clearCookies} className='text-gray-700 hover:underline'>
/> <FormattedMessage
</a> id='alert.unexpected.clear_cookies'
{' ' }(this will log you out). defaultMessage='clear cookies and browser data'
/>
</a>
) }}
/>
</p> </p>
<Text theme='muted'> <Text theme='muted'>
@ -144,7 +172,7 @@ class ErrorBoundary extends React.PureComponent {
{browser && ( {browser && (
<Stack> <Stack>
<Text weight='semibold'>Browser</Text> <Text weight='semibold'><FormattedMessage id='alert.unexpected.browser' defaultMessage='Browser' /></Text>
<Text theme='muted'>{browser.getBrowserName()} {browser.getBrowserVersion()}</Text> <Text theme='muted'>{browser.getBrowserName()} {browser.getBrowserVersion()}</Text>
</Stack> </Stack>
)} )}
@ -155,28 +183,28 @@ class ErrorBoundary extends React.PureComponent {
<footer className='flex-shrink-0 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'> <footer className='flex-shrink-0 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'>
<nav className='flex justify-center space-x-4'> <nav className='flex justify-center space-x-4'>
{statusLink && ( {links.get('status') && (
<> <>
<a href={statusLink} className='text-sm font-medium text-gray-500 hover:text-gray-600'> <a href={links.get('status')} className='text-sm font-medium text-gray-500 hover:text-gray-600'>
Status <FormattedMessage id='alert.unexpected.links.status' defaultMessage='Status' />
</a> </a>
</> </>
)} )}
{helpLink && ( {links.get('help') && (
<> <>
<span className='inline-block border-l border-gray-300' aria-hidden='true' /> <span className='inline-block border-l border-gray-300' aria-hidden='true' />
<a href={helpLink} className='text-sm font-medium text-gray-500 hover:text-gray-600'> <a href={links.get('help')} className='text-sm font-medium text-gray-500 hover:text-gray-600'>
Help Center <FormattedMessage id='alert.unexpected.links.help' defaultMessage='Help Center' />
</a> </a>
</> </>
)} )}
{supportLink && ( {links.get('support') && (
<> <>
<span className='inline-block border-l border-gray-300' aria-hidden='true' /> <span className='inline-block border-l border-gray-300' aria-hidden='true' />
<a href={supportLink} className='text-sm font-medium text-gray-500 hover:text-gray-600'> <a href={links.get('support')} className='text-sm font-medium text-gray-500 hover:text-gray-600'>
Support <FormattedMessage id='alert.unexpected.links.support' defaultMessage='Support' />
</a> </a>
</> </>
)} )}
@ -188,4 +216,4 @@ class ErrorBoundary extends React.PureComponent {
} }
export default ErrorBoundary; export default connect(mapStateToProps)(ErrorBoundary as any);

Wyświetl plik

@ -114,6 +114,7 @@ export const SoapboxConfigRecord = ImmutableRecord({
singleUserMode: false, singleUserMode: false,
singleUserModeProfile: '', singleUserModeProfile: '',
linkFooterMessage: '', linkFooterMessage: '',
links: ImmutableMap<string, string>(),
}, 'SoapboxConfig'); }, 'SoapboxConfig');
type SoapboxConfigMap = ImmutableMap<string, any>; type SoapboxConfigMap = ImmutableMap<string, any>;