From bcaf9a711d795753502d5bdae281ac037e3e9d5f Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Thu, 8 Oct 2020 15:58:26 +0100 Subject: [PATCH 1/7] error boundary init --- app/components/Views/Root/index.js | 10 +++++++++- package.json | 1 + yarn.lock | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/components/Views/Root/index.js b/app/components/Views/Root/index.js index 97df4fda47b..017ebc06002 100644 --- a/app/components/Views/Root/index.js +++ b/app/components/Views/Root/index.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import propTypes from 'prop-types'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/lib/integration/react'; +import ErrorBoundary from 'react-native-error-boundary'; import { store, persistor } from '../../../store/'; import SplashScreen from 'react-native-splash-screen'; @@ -9,6 +10,7 @@ import SplashScreen from 'react-native-splash-screen'; import App from '../../Nav/App'; import SecureKeychain from '../../../core/SecureKeychain'; import EntryScriptWeb3 from '../../../core/EntryScriptWeb3'; +import Logger from '../../../util/Logger'; /** * Top level of the component hierarchy @@ -23,6 +25,10 @@ export default class Root extends PureComponent { foxCode: 'null' }; + errorHandler = (error, stackTrace) => { + Logger.error(error, stackTrace); + }; + constructor(props) { super(props); SecureKeychain.init(props.foxCode); @@ -34,7 +40,9 @@ export default class Root extends PureComponent { render = () => ( - + + + ); diff --git a/package.json b/package.json index 58ee1ea6b58..73e4ded6bf7 100644 --- a/package.json +++ b/package.json @@ -143,6 +143,7 @@ "react-native-device-info": "3.1.4", "react-native-elevated-view": "0.0.6", "react-native-emoji": "1.3.1", + "react-native-error-boundary": "^1.1.3", "react-native-fade-in-image": "1.4.1", "react-native-flash-message": "0.1.11", "react-native-fs": "^2.16.6", diff --git a/yarn.lock b/yarn.lock index 6e5bbe439af..bf96fd9112b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10344,6 +10344,11 @@ react-native-emoji@1.3.1: dependencies: node-emoji "1.8.1" +react-native-error-boundary@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/react-native-error-boundary/-/react-native-error-boundary-1.1.3.tgz#2df82e8a8fafcb0d779c059dc0c926f6cecfbe0b" + integrity sha512-L4jPTia6Gchop0BZ7uhp2pQe1I5pa5rIt5RAHqCl2ySvWxJyM/QgTJV/NkH6VOnVZ77cAiK6BOAU+YcMz77R+A== + react-native-fade-in-image@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/react-native-fade-in-image/-/react-native-fade-in-image-1.4.1.tgz#90f6de57e3bc7f41324a921087896afbe491d430" From b96fbdb6bf7ac5b098c15c2aad5f8720a7e85d07 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Fri, 9 Oct 2020 14:46:48 +0100 Subject: [PATCH 2/7] Create our error screen --- app/components/UI/ErrorBoundary/index.js | 203 ++++++++++++++++++ .../Views/RevealPrivateCredential/index.js | 44 ++-- app/components/Views/Root/index.js | 4 +- app/images/metamask-error.png | Bin 0 -> 5231 bytes locales/en.json | 17 ++ 5 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 app/components/UI/ErrorBoundary/index.js create mode 100644 app/images/metamask-error.png diff --git a/app/components/UI/ErrorBoundary/index.js b/app/components/UI/ErrorBoundary/index.js new file mode 100644 index 00000000000..90071671efc --- /dev/null +++ b/app/components/UI/ErrorBoundary/index.js @@ -0,0 +1,203 @@ +import React, { Component } from 'react'; +import { SafeAreaView, Text, TouchableOpacity, View, StyleSheet, Image, Linking, Alert } from 'react-native'; +import PropTypes from 'prop-types'; +import RevealPrivateCredential from '../../Views/RevealPrivateCredential'; +import Logger from '../../../util/Logger'; +import { colors, fontStyles } from '../../../styles/common'; +import { ScrollView } from 'react-native-gesture-handler'; +import Clipboard from '@react-native-community/clipboard'; +import { strings } from '../../../../locales/i18n'; + +// eslint-disable-next-line import/no-commonjs +const metamaskErrorImage = require('../../../images/metamask-error.png'); + +const styles = StyleSheet.create({ + container: { + flex: 1 + }, + content: { + paddingHorizontal: 24, + flex: 1 + }, + header: { + alignItems: 'center' + }, + errorImage: { + width: 50, + height: 50, + marginTop: 24 + }, + title: { + color: colors.black, + fontSize: 24, + lineHeight: 34, + ...fontStyles.bold + }, + subtitle: { + fontSize: 14, + lineHeight: 20, + color: colors.grey500, + marginTop: 8, + ...fontStyles.normal + }, + errorContainer: { + backgroundColor: colors.red000, + borderRadius: 8, + marginTop: 24 + }, + error: { + color: colors.black, + padding: 8, + fontSize: 14, + lineHeight: 20, + ...fontStyles.normal + }, + button: { + marginTop: 24, + backgroundColor: colors.blue, + borderRadius: 50, + padding: 16 + }, + buttonText: { + color: colors.white, + textAlign: 'center', + ...fontStyles.normal, + fontWeight: '500' + }, + textContainer: { + marginTop: 24 + }, + text: { + color: colors.black, + fontSize: 14, + ...fontStyles.normal, + lineHeight: 20 + }, + link: { + color: colors.blue + } +}); + +const Fallback = props => ( + + + + + {strings('error_screen.title')} + {strings('error_screen.subtitle')} + + + {props.errorMessage} + + + {strings('error_screen.try_again_button')} + + + + {strings('error_screen.funds_safe')} + {'\n'} + {'\n'} + {strings('error_screen.submit_ticket_1')} + {'\n'} + {'\n'} + {strings('error_screen.submit_ticket_2')} + {'\n'} + + {strings('error_screen.submit_ticket_3')}{' '} + + {strings('error_screen.submit_ticket_4')} + {' '} + {strings('error_screen.submit_ticket_5')} + + {'\n'} + + {strings('error_screen.submit_ticket_6')}{' '} + + {strings('error_screen.submit_ticket_7')} + {' '} + {strings('error_screen.submit_ticket_8')} + + {'\n'} + {'\n'} + + {strings('error_screen.save_seedphrase_1')}{' '} + + {strings('error_screen.save_seedphrase_2')} + {' '} + {strings('error_screen.save_seedphrase_3')} + + + + + +); + +Fallback.propTypes = { + errorMessage: PropTypes.string, + resetError: PropTypes.func, + showExportSeedphrase: PropTypes.func, + copyErrorToClipboard: PropTypes.func, + openTicket: PropTypes.func +}; + +class ErrorBoundary extends Component { + state = { error: null }; + + static propTypes = { + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]), + view: PropTypes.string.isRequired + }; + + static getDerivedStateFromError(error) { + return { error }; + } + + componentDidCatch(error, errorInfo) { + Logger.error(error, { ...errorInfo, view: this.props.view }); + console.log(error, { view: this.props.view, ...errorInfo }); + } + + resetError = () => { + this.setState({ error: null }); + }; + + showExportSeedphrase = () => { + this.setState({ backupSeedphrase: true }); + }; + + cancelExportSeedphrase = () => { + this.setState({ backupSeedphrase: false }); + }; + + getErrorMessage = () => `View: ${this.props.view}\n${this.state.error.toString()}`; + + copyErrorToClipboard = async () => { + await Clipboard.setString(this.getErrorMessage()); + Alert.alert('Copied to clipboard', '', [{ text: 'OK' }], { + cancelable: true + }); + }; + + openTicket = () => { + const url = 'https://metamask.zendesk.com/hc/en-us/requests/new'; + Linking.openURL(url); + }; + + render() { + return this.state.backupSeedphrase ? ( + + ) : this.state.error ? ( + + ) : ( + this.props.children + ); + } +} + +export default ErrorBoundary; diff --git a/app/components/Views/RevealPrivateCredential/index.js b/app/components/Views/RevealPrivateCredential/index.js index 24ea18caa26..428c10a2a90 100644 --- a/app/components/Views/RevealPrivateCredential/index.js +++ b/app/components/Views/RevealPrivateCredential/index.js @@ -146,7 +146,6 @@ class RevealPrivateCredential extends PureComponent { strings(`reveal_credential.${navigation.getParam('privateCredentialName', '')}_title`), navigation ); - static propTypes = { /** /* navigation object required to push new views @@ -163,7 +162,15 @@ class RevealPrivateCredential extends PureComponent { /** * Boolean that determines if the user has set a password before */ - passwordSet: PropTypes.bool + passwordSet: PropTypes.bool, + /** + * String that determines whether to show the seedphrase or private key export screen + */ + privateCredentialName: PropTypes.string, + /** + * Cancel function to be called when cancel button is clicked. If not provided, we go to previous screen on cancel + */ + cancel: PropTypes.func }; async componentDidMount() { @@ -193,20 +200,17 @@ class RevealPrivateCredential extends PureComponent { }; cancel = () => { + if (this.props.cancel) return this.props.cancel(); const { navigation } = this.props; navigation.pop(); }; async tryUnlockWithPassword(password) { const { KeyringController } = Engine.context; - const { - selectedAddress, - navigation: { - state: { - params: { privateCredentialName } - } - } - } = this.props; + const { selectedAddress } = this.props; + + const privateCredentialName = + this.props.privateCredentialName || this.props.navigation.state.params.privateCredentialName; try { if (privateCredentialName === 'seed_phrase') { @@ -241,13 +245,9 @@ class RevealPrivateCredential extends PureComponent { copyPrivateCredentialToClipboard = async () => { const { privateCredential } = this.state; - const { - navigation: { - state: { - params: { privateCredentialName } - } - } - } = this.props; + const privateCredentialName = + this.props.privateCredentialName || this.props.navigation.state.params.privateCredentialName; + await Clipboard.setString(privateCredential); this.props.showAlert({ isVisible: true, @@ -272,13 +272,9 @@ class RevealPrivateCredential extends PureComponent { render = () => { const { unlocked, privateCredential } = this.state; - const { - navigation: { - state: { - params: { privateCredentialName } - } - } - } = this.props; + const privateCredentialName = + this.props.privateCredentialName || this.props.navigation.state.params.privateCredentialName; + return ( ( - + diff --git a/app/images/metamask-error.png b/app/images/metamask-error.png new file mode 100644 index 0000000000000000000000000000000000000000..31eac5519492678704e632330154f9c9881d6b8c GIT binary patch literal 5231 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 literal 0 HcmV?d00001 diff --git a/locales/en.json b/locales/en.json index c152051e4df..96d56c36b51 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1325,5 +1325,22 @@ "text": "Don’t risk losing your funds. Protect your wallet by saving your seed phrase in a place you trust.", "text_bold": "It’s the only way to recover your wallet if you get locked out of the app or get a new device.", "action": "Learn more" + }, + "error_screen": { + "title": "We're sorry", + "subtitle": "Information can't be shown due to an error.", + "try_again_button": "Try again", + "funds_safe": "Don’t worry, your wallet and it’s funds are safe.", + "submit_ticket_1": "If trying again doesn’t work, please submit a support ticket by following these steps:", + "submit_ticket_2": "1. Take a screenshot of this screen.", + "submit_ticket_3": "2. Copy the", + "submit_ticket_4": "error message", + "submit_ticket_5": "to clipboard.", + "submit_ticket_6": "3.", + "submit_ticket_7": "Submit a ticket", + "submit_ticket_8": "to our ticketing system and include the screenshot and add the error message to the description field.", + "save_seedphrase_1": "If this error persists,", + "save_seedphrase_2": "save your seed phrase", + "save_seedphrase_3": "and re-install the app." } } From 31674c67afb6a5bc9db33d668a26f423ac8d9b59 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Fri, 9 Oct 2020 15:13:19 +0100 Subject: [PATCH 3/7] Move error boundary screen to views and remove unecessary library --- app/components/{UI => Views}/ErrorBoundary/index.js | 2 +- app/components/Views/Root/index.js | 2 +- package.json | 1 - yarn.lock | 5 ----- 4 files changed, 2 insertions(+), 8 deletions(-) rename app/components/{UI => Views}/ErrorBoundary/index.js (98%) diff --git a/app/components/UI/ErrorBoundary/index.js b/app/components/Views/ErrorBoundary/index.js similarity index 98% rename from app/components/UI/ErrorBoundary/index.js rename to app/components/Views/ErrorBoundary/index.js index 90071671efc..cd31f958ac1 100644 --- a/app/components/UI/ErrorBoundary/index.js +++ b/app/components/Views/ErrorBoundary/index.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { SafeAreaView, Text, TouchableOpacity, View, StyleSheet, Image, Linking, Alert } from 'react-native'; import PropTypes from 'prop-types'; -import RevealPrivateCredential from '../../Views/RevealPrivateCredential'; +import RevealPrivateCredential from '../RevealPrivateCredential'; import Logger from '../../../util/Logger'; import { colors, fontStyles } from '../../../styles/common'; import { ScrollView } from 'react-native-gesture-handler'; diff --git a/app/components/Views/Root/index.js b/app/components/Views/Root/index.js index 10d110810e9..f20dbd4d001 100644 --- a/app/components/Views/Root/index.js +++ b/app/components/Views/Root/index.js @@ -10,7 +10,7 @@ import App from '../../Nav/App'; import SecureKeychain from '../../../core/SecureKeychain'; import EntryScriptWeb3 from '../../../core/EntryScriptWeb3'; import Logger from '../../../util/Logger'; -import ErrorBoundary from '../../UI/ErrorBoundary'; +import ErrorBoundary from '../ErrorBoundary'; /** * Top level of the component hierarchy diff --git a/package.json b/package.json index 73e4ded6bf7..58ee1ea6b58 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,6 @@ "react-native-device-info": "3.1.4", "react-native-elevated-view": "0.0.6", "react-native-emoji": "1.3.1", - "react-native-error-boundary": "^1.1.3", "react-native-fade-in-image": "1.4.1", "react-native-flash-message": "0.1.11", "react-native-fs": "^2.16.6", diff --git a/yarn.lock b/yarn.lock index bf96fd9112b..6e5bbe439af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10344,11 +10344,6 @@ react-native-emoji@1.3.1: dependencies: node-emoji "1.8.1" -react-native-error-boundary@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/react-native-error-boundary/-/react-native-error-boundary-1.1.3.tgz#2df82e8a8fafcb0d779c059dc0c926f6cecfbe0b" - integrity sha512-L4jPTia6Gchop0BZ7uhp2pQe1I5pa5rIt5RAHqCl2ySvWxJyM/QgTJV/NkH6VOnVZ77cAiK6BOAU+YcMz77R+A== - react-native-fade-in-image@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/react-native-fade-in-image/-/react-native-fade-in-image-1.4.1.tgz#90f6de57e3bc7f41324a921087896afbe491d430" From b907945cfae758972b02e61295f7cb5d90842cab Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Fri, 9 Oct 2020 15:31:08 +0100 Subject: [PATCH 4/7] Put the error boundary on more views --- app/components/Views/ActivityView/index.js | 19 +- .../__snapshots__/index.test.js.snap | 568 +++++++++--------- app/components/Views/BrowserTab/index.js | 73 +-- app/components/Views/Login/index.js | 133 ++-- .../Root/__snapshots__/index.test.js.snap | 11 +- app/components/Views/Wallet/index.js | 21 +- 6 files changed, 425 insertions(+), 400 deletions(-) diff --git a/app/components/Views/ActivityView/index.js b/app/components/Views/ActivityView/index.js index 40472acbda2..d4fdd786816 100644 --- a/app/components/Views/ActivityView/index.js +++ b/app/components/Views/ActivityView/index.js @@ -11,6 +11,7 @@ import TransactionsView from '../TransactionsView'; import TabBar from '../../Base/TabBar'; import { strings } from '../../../../locales/i18n'; import FiatOrdersView from '../FiatOrdersView'; +import ErrorBoundary from '../ErrorBoundary'; const styles = StyleSheet.create({ wrapper: { @@ -30,12 +31,18 @@ function ActivityView({ hasOrders, ...props }) { ); return ( - - - - {hasOrders && } - - + + + + + {hasOrders && } + + + ); } diff --git a/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap b/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap index b54bf94b161..1471bab6acd 100644 --- a/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap +++ b/app/components/Views/BrowserTab/__snapshots__/index.test.js.snap @@ -1,321 +1,325 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Browser should render correctly 1`] = ` - - - - - - - - + /> - + + + - + + - + - Cancel - - - - - - + Cancel + + + + + + - + + + - - - + + + - - - + + `; diff --git a/app/components/Views/BrowserTab/index.js b/app/components/Views/BrowserTab/index.js index 2a36e8a1c5d..cb29237c74e 100644 --- a/app/components/Views/BrowserTab/index.js +++ b/app/components/Views/BrowserTab/index.js @@ -60,6 +60,7 @@ import { ethErrors } from 'eth-json-rpc-errors'; import EntryScriptWeb3 from '../../../core/EntryScriptWeb3'; import { getVersion } from 'react-native-device-info'; +import ErrorBoundary from '../ErrorBoundary'; const { HOMEPAGE_URL, USER_AGENT, NOTIFICATION_NAMES } = AppConstants; const HOMEPAGE_HOST = 'home.metamask.io'; @@ -1681,42 +1682,44 @@ export const BrowserTab = props => { * Main render */ return ( - - - {!!entryScriptWeb3 && firstUrlLoaded && ( - null} />} - source={{ uri: initialUrl }} - injectedJavaScriptBeforeContentLoaded={entryScriptWeb3} - style={styles.webview} - onLoadStart={onLoadStart} - onLoadEnd={onLoadEnd} - onLoadProgress={onLoadProgress} - onMessage={onMessage} - onError={onError} - onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} - userAgent={USER_AGENT} - sendCookies - javascriptEnabled - allowsInlineMediaPlayback - useWebkit - testID={'browser-webview'} - /> - )} + + + + {!!entryScriptWeb3 && firstUrlLoaded && ( + null} />} + source={{ uri: initialUrl }} + injectedJavaScriptBeforeContentLoaded={entryScriptWeb3} + style={styles.webview} + onLoadStart={onLoadStart} + onLoadEnd={onLoadEnd} + onLoadProgress={onLoadProgress} + onMessage={onMessage} + onError={onError} + onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} + userAgent={USER_AGENT} + sendCookies + javascriptEnabled + allowsInlineMediaPlayback + useWebkit + testID={'browser-webview'} + /> + )} + + {renderProgressBar()} + {isTabActive() && renderPhishingModal()} + {isTabActive() && renderUrlModal()} + {isTabActive() && renderApprovalModal()} + {isTabActive() && renderWatchAssetModal()} + {isTabActive() && renderOptions()} + {isTabActive() && renderBottomBar()} + {isTabActive() && renderOnboardingWizard()} - {renderProgressBar()} - {isTabActive() && renderPhishingModal()} - {isTabActive() && renderUrlModal()} - {isTabActive() && renderApprovalModal()} - {isTabActive() && renderWatchAssetModal()} - {isTabActive() && renderOptions()} - {isTabActive() && renderBottomBar()} - {isTabActive() && renderOnboardingWizard()} - + ); }; diff --git a/app/components/Views/Login/index.js b/app/components/Views/Login/index.js index 279d75d6252..6cdc3006ccc 100644 --- a/app/components/Views/Login/index.js +++ b/app/components/Views/Login/index.js @@ -30,6 +30,7 @@ import { ORIGINAL } from '../../../constants/storage'; import { passwordRequirementsMet } from '../../../util/password'; +import ErrorBoundary from '../ErrorBoundary'; const styles = StyleSheet.create({ mainWrapper: { @@ -335,78 +336,80 @@ class Login extends PureComponent { }; render = () => ( - - - - - {Device.isAndroid() ? ( - - ) : ( - - )} - - {strings('login.title')} - - {strings('login.password')} - ( - - - - + + + + ); } diff --git a/app/components/Views/Root/__snapshots__/index.test.js.snap b/app/components/Views/Root/__snapshots__/index.test.js.snap index fa0ebe4d776..45adee4dd1d 100644 --- a/app/components/Views/Root/__snapshots__/index.test.js.snap +++ b/app/components/Views/Root/__snapshots__/index.test.js.snap @@ -28,9 +28,14 @@ exports[`Root should render correctly 1`] = ` } } > - + + + `; diff --git a/app/components/Views/Wallet/index.js b/app/components/Views/Wallet/index.js index 6c36aa4afde..0b94f0f6ac7 100644 --- a/app/components/Views/Wallet/index.js +++ b/app/components/Views/Wallet/index.js @@ -18,6 +18,7 @@ import { getTicker } from '../../../util/transactions'; import OnboardingWizard from '../../UI/OnboardingWizard'; import { showTransactionNotification, hideTransactionNotification } from '../../../actions/notification'; import DeeplinkManager from '../../../core/DeeplinkManager'; +import ErrorBoundary from '../ErrorBoundary'; const styles = StyleSheet.create({ wrapper: { @@ -238,15 +239,17 @@ class Wallet extends PureComponent { }; render = () => ( - - } - > - {this.props.selectedAddress ? this.renderContent() : this.renderLoader()} - - {this.renderOnboardingWizard()} - + + + } + > + {this.props.selectedAddress ? this.renderContent() : this.renderLoader()} + + {this.renderOnboardingWizard()} + + ); } From 8ae8d2def184529816f2e133251b299aa7447122 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Fri, 9 Oct 2020 15:42:50 +0100 Subject: [PATCH 5/7] remove console log --- app/components/Views/ErrorBoundary/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/components/Views/ErrorBoundary/index.js b/app/components/Views/ErrorBoundary/index.js index cd31f958ac1..4f9e8b2a3bc 100644 --- a/app/components/Views/ErrorBoundary/index.js +++ b/app/components/Views/ErrorBoundary/index.js @@ -153,8 +153,7 @@ class ErrorBoundary extends Component { } componentDidCatch(error, errorInfo) { - Logger.error(error, { ...errorInfo, view: this.props.view }); - console.log(error, { view: this.props.view, ...errorInfo }); + Logger.error(error, { View: this.props.view, ...errorInfo }); } resetError = () => { From da4a31baa1e485419da2ad85d67f8a94f67ab0ec Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Fri, 9 Oct 2020 15:58:28 +0100 Subject: [PATCH 6/7] Add alert to strings --- app/components/Views/ErrorBoundary/index.js | 2 +- locales/en.json | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/components/Views/ErrorBoundary/index.js b/app/components/Views/ErrorBoundary/index.js index 4f9e8b2a3bc..e5ae1cf3774 100644 --- a/app/components/Views/ErrorBoundary/index.js +++ b/app/components/Views/ErrorBoundary/index.js @@ -172,7 +172,7 @@ class ErrorBoundary extends Component { copyErrorToClipboard = async () => { await Clipboard.setString(this.getErrorMessage()); - Alert.alert('Copied to clipboard', '', [{ text: 'OK' }], { + Alert.alert(strings('error_screen.copied_clipboard'), '', [{ text: strings('error_screen.ok') }], { cancelable: true }); }; diff --git a/locales/en.json b/locales/en.json index 96d56c36b51..2a46dc757c9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1341,6 +1341,8 @@ "submit_ticket_8": "to our ticketing system and include the screenshot and add the error message to the description field.", "save_seedphrase_1": "If this error persists,", "save_seedphrase_2": "save your seed phrase", - "save_seedphrase_3": "and re-install the app." + "save_seedphrase_3": "and re-install the app.", + "copied_clipboard": "Copied to clipboard", + "ok": "OK" } } From 7ec7a4e92884d532dfd935fcff2a4bc418d41b24 Mon Sep 17 00:00:00 2001 From: andrepimenta Date: Fri, 9 Oct 2020 18:17:27 +0100 Subject: [PATCH 7/7] update design --- app/components/Views/ErrorBoundary/index.js | 86 +++++++++++++-------- locales/en.json | 22 +++--- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/app/components/Views/ErrorBoundary/index.js b/app/components/Views/ErrorBoundary/index.js index e5ae1cf3774..890da5cc18c 100644 --- a/app/components/Views/ErrorBoundary/index.js +++ b/app/components/Views/ErrorBoundary/index.js @@ -7,6 +7,7 @@ import { colors, fontStyles } from '../../../styles/common'; import { ScrollView } from 'react-native-gesture-handler'; import Clipboard from '@react-native-community/clipboard'; import { strings } from '../../../../locales/i18n'; +import Icon from 'react-native-vector-icons/FontAwesome'; // eslint-disable-next-line import/no-commonjs const metamaskErrorImage = require('../../../images/metamask-error.png'); @@ -38,6 +39,7 @@ const styles = StyleSheet.create({ lineHeight: 20, color: colors.grey500, marginTop: 8, + textAlign: 'center', ...fontStyles.normal }, errorContainer: { @@ -54,12 +56,14 @@ const styles = StyleSheet.create({ }, button: { marginTop: 24, - backgroundColor: colors.blue, + borderColor: colors.blue, + borderWidth: 1, borderRadius: 50, - padding: 16 + padding: 12, + paddingHorizontal: 34 }, buttonText: { - color: colors.white, + color: colors.blue, textAlign: 'center', ...fontStyles.normal, fontWeight: '500' @@ -70,11 +74,19 @@ const styles = StyleSheet.create({ text: { color: colors.black, fontSize: 14, - ...fontStyles.normal, - lineHeight: 20 + lineHeight: 20, + ...fontStyles.normal }, link: { color: colors.blue + }, + reportTextContainer: { + paddingLeft: 14, + marginTop: 16, + marginBottom: 24 + }, + reportStep: { + marginTop: 14 } }); @@ -89,43 +101,51 @@ const Fallback = props => ( {props.errorMessage} - - {strings('error_screen.try_again_button')} - + + + + + {' '} + {strings('error_screen.try_again_button')} + + + - {strings('error_screen.funds_safe')} - {'\n'} - {'\n'} {strings('error_screen.submit_ticket_1')} - {'\n'} - {'\n'} - {strings('error_screen.submit_ticket_2')} - {'\n'} - - {strings('error_screen.submit_ticket_3')}{' '} + + + + + {' '} + {strings('error_screen.submit_ticket_2')} + + + + + {' '} - {strings('error_screen.submit_ticket_4')} + {strings('error_screen.submit_ticket_3')} {' '} - {strings('error_screen.submit_ticket_5')} + {strings('error_screen.submit_ticket_4')} - {'\n'} - - {strings('error_screen.submit_ticket_6')}{' '} + + + + {' '} + {strings('error_screen.submit_ticket_5')}{' '} - {strings('error_screen.submit_ticket_7')} - {' '} - {strings('error_screen.submit_ticket_8')} - - {'\n'} - {'\n'} - - {strings('error_screen.save_seedphrase_1')}{' '} - - {strings('error_screen.save_seedphrase_2')} + {strings('error_screen.submit_ticket_6')} {' '} - {strings('error_screen.save_seedphrase_3')} + {strings('error_screen.submit_ticket_7')} + + + {strings('error_screen.save_seedphrase_1')}{' '} + + {strings('error_screen.save_seedphrase_2')} + {' '} + {strings('error_screen.save_seedphrase_3')} diff --git a/locales/en.json b/locales/en.json index 2a46dc757c9..c2640b4a886 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1327,21 +1327,19 @@ "action": "Learn more" }, "error_screen": { - "title": "We're sorry", - "subtitle": "Information can't be shown due to an error.", + "title": "An error occured", + "subtitle": "Your information can't be shown. Don’t worry, your wallet and funds are safe.", "try_again_button": "Try again", - "funds_safe": "Don’t worry, your wallet and it’s funds are safe.", - "submit_ticket_1": "If trying again doesn’t work, please submit a support ticket by following these steps:", - "submit_ticket_2": "1. Take a screenshot of this screen.", - "submit_ticket_3": "2. Copy the", - "submit_ticket_4": "error message", - "submit_ticket_5": "to clipboard.", - "submit_ticket_6": "3.", - "submit_ticket_7": "Submit a ticket", - "submit_ticket_8": "to our ticketing system and include the screenshot and add the error message to the description field.", + "submit_ticket_1": "Please report this issue so we can fix it:", + "submit_ticket_2": "Take a screenshot of this screen.", + "submit_ticket_3": "Copy", + "submit_ticket_4": "the error message to clipboard.", + "submit_ticket_5": "Submit a ticket", + "submit_ticket_6": "here.", + "submit_ticket_7": "Please include the error message and the screenshot.", "save_seedphrase_1": "If this error persists,", "save_seedphrase_2": "save your seed phrase", - "save_seedphrase_3": "and re-install the app.", + "save_seedphrase_3": "& re-install the app. Note: you can NOT restore your wallet without your seed phrase.", "copied_clipboard": "Copied to clipboard", "ok": "OK" }