diff --git a/js/views/reusable/LinkButton.jsx b/js/views/reusable/LinkButton.jsx
index 2c33f1b4..e08ef309 100644
--- a/js/views/reusable/LinkButton.jsx
+++ b/js/views/reusable/LinkButton.jsx
@@ -13,45 +13,52 @@ class LinkButton extends React.Component {
onClick: PropTypes.func,
color: PropTypes.string,
size: PropTypes.string,
+ target: PropTypes.string,
+ }
+
+ static defaultProps = {
+ target: '_blank',
}
triggerLink = () => {
- if (this.props.href.split(':')[0] === 'mailto') {
- window.location = this.props.href
+ const { href } = this.props
+ if (href.split(':')[0] === 'mailto') {
+ window.location = href
} else {
- window.open(this.props.href)
+ window.open(href)
}
}
render() {
+ const { href, target, size, color, label, onClick } = this.props
let wrapperStyle = null
let textStyle = null
- if (this.props.size === 'small') {
+ if (size === 'small') {
wrapperStyle =
- this.props.color === 'secondary'
+ color === 'secondary'
? [styles.wrapper, styles.wrapperSecondary, styles.wrapperSmall]
: [styles.wrapper, styles.wrapperSmall]
textStyle =
- this.props.color === 'secondary'
+ color === 'secondary'
? [styles.text, styles.textSecondary, styles.textSmall]
: [styles.text, styles.textSmall]
} else {
wrapperStyle =
- this.props.color === 'secondary'
+ color === 'secondary'
? [styles.wrapper, styles.wrapperSecondary]
: styles.wrapper
textStyle =
- this.props.color === 'secondary'
+ color === 'secondary'
? [styles.text, styles.textSecondary]
: styles.text
}
const inner = (
- {this.props.label}
+ {label}
)
- if (this.props.href && iOS.detect()) {
+ if (href && iOS.detect()) {
return (
)
}
- if (this.props.href) {
+ if (href) {
return (
{inner}
)
}
- return (
- {inner}
- )
+ return {inner}
}
}
styles = StyleSheet.create({
diff --git a/js/views/shell/Content.jsx b/js/views/shell/Content.jsx
index b3adcec2..f7e8ea25 100644
--- a/js/views/shell/Content.jsx
+++ b/js/views/shell/Content.jsx
@@ -4,6 +4,7 @@ import { View, StyleSheet } from 'react-native'
import { withRouter, Route } from 'react-router-dom'
import Switch from './Switch.jsx'
+import { ErrorBoundary } from './ErrorBoundary.jsx'
import Events from '../../stores/Events'
import Station from '../station/Station.jsx'
@@ -75,27 +76,40 @@ class Content extends React.Component {
}
onLayout={this.triggerLayout}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {
+ throw new Error('Intentional error')
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/js/views/shell/ErrorBoundary.jsx b/js/views/shell/ErrorBoundary.jsx
new file mode 100644
index 00000000..4e9b6269
--- /dev/null
+++ b/js/views/shell/ErrorBoundary.jsx
@@ -0,0 +1,74 @@
+import React, { Component } from 'react'
+import { View, Text, StyleSheet } from 'react-native'
+
+import { vars } from '../../styles.js'
+import Wrapper from './Wrapper.jsx'
+import Header from '../reusable/Header.jsx'
+import LinkButton from '../reusable/LinkButton.jsx'
+
+let styles
+
+export class ErrorBoundary extends Component {
+ constructor(props) {
+ super(props)
+ this.state = { hasError: false }
+ }
+
+ static getDerivedStateFromError() {
+ return { hasError: true }
+ }
+
+ componentDidCatch(error, errorInfo) {
+ // TODO: Log error to an error reporting service
+ console.error(error, errorInfo)
+ }
+
+ render() {
+ const { hasError } = this.state
+ if (hasError) {
+ // You can render any custom fallback UI
+ return (
+
+
+
+
+
+ There was an unexpected error. You can either try reload this
+ page, or return home.
+
+
+
+
+
+
+ )
+ }
+
+ const { children } = this.props
+ return children
+ }
+}
+
+const { padding, defaultFontSize, fontFamily } = vars
+styles = StyleSheet.create({
+ wrapper: {
+ flex: 1,
+ },
+ error: {
+ padding,
+ },
+ errorMessage: {
+ fontSize: defaultFontSize,
+ fontFamily,
+ marginBottom: padding,
+ },
+})