Add error description and button to copy stack trace to web UI (#12033)
This commit is contained in:
		
							parent
							
								
									66fda37fd0
								
							
						
					
					
						commit
						cbaea097be
					
				
					 3 changed files with 108 additions and 8 deletions
				
			
		| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import illustration from '../../images/elephant_ui_disappointed.svg';
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
import { version, source_url } from 'mastodon/initial_state';
 | 
			
		||||
 | 
			
		||||
export default class ErrorBoundary extends React.PureComponent {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,26 +13,53 @@ export default class ErrorBoundary extends React.PureComponent {
 | 
			
		|||
    hasError: false,
 | 
			
		||||
    stackTrace: undefined,
 | 
			
		||||
    componentStack: undefined,
 | 
			
		||||
  }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  componentDidCatch(error, info) {
 | 
			
		||||
  componentDidCatch (error, info) {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      hasError: true,
 | 
			
		||||
      stackTrace: error.stack,
 | 
			
		||||
      componentStack: info && info.componentStack,
 | 
			
		||||
      copied: false,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleCopyStackTrace = () => {
 | 
			
		||||
    const { stackTrace } = this.state;
 | 
			
		||||
    const textarea = document.createElement('textarea');
 | 
			
		||||
 | 
			
		||||
    textarea.textContent    = stackTrace;
 | 
			
		||||
    textarea.style.position = 'fixed';
 | 
			
		||||
 | 
			
		||||
    document.body.appendChild(textarea);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      textarea.select();
 | 
			
		||||
      document.execCommand('copy');
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
 | 
			
		||||
    } finally {
 | 
			
		||||
      document.body.removeChild(textarea);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.setState({ copied: true });
 | 
			
		||||
    setTimeout(() => this.setState({ copied: false }), 700);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { hasError } = this.state;
 | 
			
		||||
    const { hasError, copied } = this.state;
 | 
			
		||||
 | 
			
		||||
    if (!hasError) {
 | 
			
		||||
      return this.props.children;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <img src={illustration} alt='' />
 | 
			
		||||
      <div className='error-boundary'>
 | 
			
		||||
        <div>
 | 
			
		||||
          <p className='error-boundary__error'><FormattedMessage id='error.unexpected_crash.explanation' defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.' /></p>
 | 
			
		||||
          <p><FormattedMessage id='error.unexpected_crash.next_steps' defaultMessage='Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' /></p>
 | 
			
		||||
          <p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied && 'copied'}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -160,7 +160,7 @@
 | 
			
		|||
  "getting_started.heading": "Getting started",
 | 
			
		||||
  "getting_started.invite": "Invite people",
 | 
			
		||||
  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
 | 
			
		||||
  "getting_started.security": "Security",
 | 
			
		||||
  "getting_started.security": "Account settings",
 | 
			
		||||
  "getting_started.terms": "Terms of service",
 | 
			
		||||
  "hashtag.column_header.tag_mode.all": "and {additional}",
 | 
			
		||||
  "hashtag.column_header.tag_mode.any": "or {additional}",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -135,13 +135,18 @@ button {
 | 
			
		|||
 | 
			
		||||
.app-holder {
 | 
			
		||||
  &,
 | 
			
		||||
  & > div {
 | 
			
		||||
  & > div,
 | 
			
		||||
  & > noscript {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    outline: 0 !important;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  & > noscript {
 | 
			
		||||
    height: 100vh;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.layout-single-column .app-holder {
 | 
			
		||||
| 
						 | 
				
			
			@ -157,3 +162,70 @@ button {
 | 
			
		|||
    height: 100%;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.error-boundary,
 | 
			
		||||
.app-holder noscript {
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  font-weight: 400;
 | 
			
		||||
  line-height: 1.7;
 | 
			
		||||
  color: lighten($error-red, 4%);
 | 
			
		||||
  text-align: center;
 | 
			
		||||
 | 
			
		||||
  & > div {
 | 
			
		||||
    max-width: 500px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  p {
 | 
			
		||||
    margin-bottom: .85em;
 | 
			
		||||
 | 
			
		||||
    &:last-child {
 | 
			
		||||
      margin-bottom: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  a {
 | 
			
		||||
    color: $highlight-text-color;
 | 
			
		||||
 | 
			
		||||
    &:hover,
 | 
			
		||||
    &:focus,
 | 
			
		||||
    &:active {
 | 
			
		||||
      text-decoration: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__footer {
 | 
			
		||||
    color: $dark-text-color;
 | 
			
		||||
    font-size: 13px;
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      color: $dark-text-color;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  button {
 | 
			
		||||
    display: inline;
 | 
			
		||||
    border: 0;
 | 
			
		||||
    background: transparent;
 | 
			
		||||
    color: $dark-text-color;
 | 
			
		||||
    font: inherit;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    line-height: inherit;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    outline: 0;
 | 
			
		||||
    transition: color 300ms linear;
 | 
			
		||||
    text-decoration: underline;
 | 
			
		||||
 | 
			
		||||
    &:hover,
 | 
			
		||||
    &:focus,
 | 
			
		||||
    &:active {
 | 
			
		||||
      text-decoration: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.copied {
 | 
			
		||||
      color: $valid-value-color;
 | 
			
		||||
      transition: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue