diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2019-10-01 20:48:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-01 20:48:49 +0200 |
commit | cbaea097be807f8a6a28dbddb85c657ec3e2848c (patch) | |
tree | 5e01804f007e7b19fe60dbefc99a18ce4524234b /app | |
parent | 66fda37fd04de989d12f3f4c565ba5bfc6ee189d (diff) |
Add error description and button to copy stack trace to web UI (#12033)
Diffstat (limited to 'app')
-rw-r--r-- | app/javascript/mastodon/components/error_boundary.js | 40 | ||||
-rw-r--r-- | app/javascript/mastodon/locales/en.json | 2 | ||||
-rw-r--r-- | app/javascript/styles/mastodon/basics.scss | 74 |
3 files changed, 108 insertions, 8 deletions
diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.js index d1ca5bf75..82543e118 100644 --- a/app/javascript/mastodon/components/error_boundary.js +++ b/app/javascript/mastodon/components/error_boundary.js @@ -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> ); } diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index fc769a18f..6ed6b8bcb 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -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}", diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 1f3ef7da2..2b10b5ad3 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -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; + } + } +} |