Add error description and button to copy stack trace to web UI (#12033)
This commit is contained in:
		
							parent
							
								
									66fda37fd0
								
							
						
					
					
						commit
						cbaea097be
					
				| @ -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 className='error-boundary'> | ||||
|         <div> | ||||
|         <img src={illustration} alt='' /> | ||||
|           <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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user