@ -1,44 +1,155 @@
import React from 'react' ;
import PropTypes from 'prop-types' ;
import { defineMessages, injectIntl } from 'react-intl' ;
import { injectIntl, FormattedMessage } from 'react-intl' ;
import Column from 'mastodon/components/column' ;
import ColumnHeader from 'mastodon/components/column_header' ;
import IconButton from 'mastodon/components/icon_button' ;
import Button from 'mastodon/components/button' ;
import { Helmet } from 'react-helmet' ;
import { Link } from 'react-router-dom' ;
import classNames from 'classnames' ;
import { autoPlayGif } from 'mastodon/initial_state' ;
const messages = defineMessages ( {
title : { id : 'bundle_column_error.title' , defaultMessage : 'Network error' } ,
body : { id : 'bundle_column_error.body' , defaultMessage : 'Something went wrong while loading this component.' } ,
retry : { id : 'bundle_column_error.retry' , defaultMessage : 'Try again' } ,
} ) ;
class GIF extends React . PureComponent {
static propTypes = {
src : PropTypes . string . isRequired ,
staticSrc : PropTypes . string . isRequired ,
className : PropTypes . string ,
animate : PropTypes . bool ,
} ;
static defaultProps = {
animate : autoPlayGif ,
} ;
state = {
hovering : false ,
} ;
handleMouseEnter = ( ) => {
const { animate } = this . props ;
if ( ! animate ) {
this . setState ( { hovering : true } ) ;
}
}
handleMouseLeave = ( ) => {
const { animate } = this . props ;
if ( ! animate ) {
this . setState ( { hovering : false } ) ;
}
}
render ( ) {
const { src , staticSrc , className , animate } = this . props ;
const { hovering } = this . state ;
return (
< img
className = { className }
src = { ( hovering || animate ) ? src : staticSrc }
alt = ''
role = 'presentation'
onMouseEnter = { this . handleMouseEnter }
onMouseLeave = { this . handleMouseLeave }
/ >
) ;
}
}
class CopyButton extends React . PureComponent {
static propTypes = {
children : PropTypes . node . isRequired ,
value : PropTypes . string . isRequired ,
} ;
state = {
copied : false ,
} ;
handleClick = ( ) => {
const { value } = this . props ;
navigator . clipboard . writeText ( value ) ;
this . setState ( { copied : true } ) ;
this . timeout = setTimeout ( ( ) => this . setState ( { copied : false } ) , 700 ) ;
}
componentWillUnmount ( ) {
if ( this . timeout ) clearTimeout ( this . timeout ) ;
}
render ( ) {
const { children } = this . props ;
const { copied } = this . state ;
return (
< Button onClick = { this . handleClick } className = { copied ? 'copied' : 'copyable' } > { copied ? < FormattedMessage id = 'copypaste.copied' defaultMessage = 'Copied' / > : children } < / B u t t o n >
) ;
}
}
export default @ injectIntl
class BundleColumnError extends React . PureComponent {
static propTypes = {
onRetry : PropTypes . func . isRequired ,
errorType : PropTypes . oneOf ( [ 'routing' , 'network' , 'error' ] ) ,
onRetry : PropTypes . func ,
intl : PropTypes . object . isRequired ,
multiColumn : PropTypes . bool ,
}
stacktrace : PropTypes . string ,
} ;
static defaultProps = {
errorType : 'routing' ,
} ;
handleRetry = ( ) => {
this . props . onRetry ( ) ;
const { onRetry } = this . props ;
if ( onRetry ) {
onRetry ( ) ;
}
}
render ( ) {
const { multiColumn , intl : { formatMessage } } = this . props ;
const { errorType, multiColumn , stacktrace } = this . props ;
return (
< Column bindToDocument = { ! multiColumn } label = { formatMessage ( messages . title ) } >
< ColumnHeader
icon = 'exclamation-circle'
title = { formatMessage ( messages . title ) }
showBackButton
multiColumn = { multiColumn }
/ >
let title , body ;
switch ( errorType ) {
case 'routing' :
title = < FormattedMessage id = 'bundle_column_error.routing.title' defaultMessage = '404' / > ;
body = < FormattedMessage id = 'bundle_column_error.routing.body' defaultMessage = 'The requested page could not be found. Are you sure the URL in the address bar is correct?' / > ;
break ;
case 'network' :
title = < FormattedMessage id = 'bundle_column_error.network.title' defaultMessage = 'Network error' / > ;
body = < FormattedMessage id = 'bundle_column_error.network.body' defaultMessage = 'There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.' / > ;
break ;
case 'error' :
title = < FormattedMessage id = 'bundle_column_error.error.title' defaultMessage = 'Oh, no!' / > ;
body = < FormattedMessage id = 'bundle_column_error.error.body' defaultMessage = 'The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.' / > ;
break ;
}
return (
< Column bindToDocument = { ! multiColumn } >
< div className = 'error-column' >
< IconButton title = { formatMessage ( messages . retry ) } icon = 'refresh' onClick = { this . handleRetry } size = { 64 } / >
{ formatMessage ( messages . body ) }
< GIF src = '/oops.gif' staticSrc = '/oops.png' className = 'error-column__image' / >
< div className = 'error-column__message' >
< h1 > { title } < / h 1 >
< p > { body } < / p >
< div className = 'error-column__message__actions' >
{ errorType === 'network' && < Button onClick = { this . handleRetry } > < FormattedMessage id = 'bundle_column_error.retry' defaultMessage = 'Try again' / > < / B u t t o n > }
{ errorType === 'error' && < CopyButton value = { stacktrace } > < FormattedMessage id = 'bundle_column_error.copy_stacktrace' defaultMessage = 'Copy error report' / > < / C o p y B u t t o n > }
< Link to = '/' className = { classNames ( 'button' , { 'button-tertiary' : errorType !== 'routing' } ) } > < FormattedMessage id = 'bundle_column_error.return' defaultMessage = 'Go back home' / > < / L i n k >
< / d i v >
< / d i v >
< / d i v >
< Helmet >
@ -49,5 +160,3 @@ class BundleColumnError extends React.PureComponent {
}
}
export default injectIntl ( BundleColumnError ) ;