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 React from 'react'; | ||||||
| import PropTypes from 'prop-types'; | 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 { | export default class ErrorBoundary extends React.PureComponent { | ||||||
| 
 | 
 | ||||||
| @ -12,26 +13,53 @@ export default class ErrorBoundary extends React.PureComponent { | |||||||
|     hasError: false, |     hasError: false, | ||||||
|     stackTrace: undefined, |     stackTrace: undefined, | ||||||
|     componentStack: undefined, |     componentStack: undefined, | ||||||
|   } |   }; | ||||||
| 
 | 
 | ||||||
|   componentDidCatch(error, info) { |   componentDidCatch (error, info) { | ||||||
|     this.setState({ |     this.setState({ | ||||||
|       hasError: true, |       hasError: true, | ||||||
|       stackTrace: error.stack, |       stackTrace: error.stack, | ||||||
|       componentStack: info && info.componentStack, |       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() { |   render() { | ||||||
|     const { hasError } = this.state; |     const { hasError, copied } = this.state; | ||||||
| 
 | 
 | ||||||
|     if (!hasError) { |     if (!hasError) { | ||||||
|       return this.props.children; |       return this.props.children; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div> |       <div className='error-boundary'> | ||||||
|         <img src={illustration} alt='' /> |         <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> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -160,7 +160,7 @@ | |||||||
|   "getting_started.heading": "Getting started", |   "getting_started.heading": "Getting started", | ||||||
|   "getting_started.invite": "Invite people", |   "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.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", |   "getting_started.terms": "Terms of service", | ||||||
|   "hashtag.column_header.tag_mode.all": "and {additional}", |   "hashtag.column_header.tag_mode.all": "and {additional}", | ||||||
|   "hashtag.column_header.tag_mode.any": "or {additional}", |   "hashtag.column_header.tag_mode.any": "or {additional}", | ||||||
|  | |||||||
| @ -135,13 +135,18 @@ button { | |||||||
| 
 | 
 | ||||||
| .app-holder { | .app-holder { | ||||||
|   &, |   &, | ||||||
|   & > div { |   & > div, | ||||||
|  |   & > noscript { | ||||||
|     display: flex; |     display: flex; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     outline: 0 !important; |     outline: 0 !important; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   & > noscript { | ||||||
|  |     height: 100vh; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .layout-single-column .app-holder { | .layout-single-column .app-holder { | ||||||
| @ -157,3 +162,70 @@ button { | |||||||
|     height: 100%; |     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