aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Miller <danjm.com@gmail.com>2018-12-07 03:39:47 +0800
committerDan Miller <danjm.com@gmail.com>2018-12-13 01:23:37 +0800
commit20dbebae9fc96c968febc68a289976e7e147a7c8 (patch)
tree3c4e7f9d906839c66a428034a6c71e47e2f81352
parent0e5e51b8ff8a1db66d5310fa0d8b07f6480007e1 (diff)
downloadtangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.tar
tangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.tar.gz
tangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.tar.bz2
tangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.tar.lz
tangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.tar.xz
tangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.tar.zst
tangerine-wallet-browser-20dbebae9fc96c968febc68a289976e7e147a7c8.zip
Adds network loading retry / error screen.
-rw-r--r--test/unit/ui/app/actions.spec.js4
-rw-r--r--ui/app/actions.js12
-rw-r--r--ui/app/app.js4
-rw-r--r--ui/app/components/loading-network-screen/index.js2
-rw-r--r--ui/app/components/loading-network-screen/loading-network-screen.component.js145
-rw-r--r--ui/app/components/loading-network-screen/loading-network-screen.container.js41
-rw-r--r--ui/app/components/modals/loading-network-error/index.js1
-rw-r--r--ui/app/components/modals/loading-network-error/loading-network-error.component.js29
-rw-r--r--ui/app/components/modals/loading-network-error/loading-network-error.container.js4
-rw-r--r--ui/app/css/itcss/components/loading-overlay.scss21
-rw-r--r--ui/app/reducers/app.js9
11 files changed, 268 insertions, 4 deletions
diff --git a/test/unit/ui/app/actions.spec.js b/test/unit/ui/app/actions.spec.js
index df7d2ee8f..c7ac8b6cf 100644
--- a/test/unit/ui/app/actions.spec.js
+++ b/test/unit/ui/app/actions.spec.js
@@ -1079,8 +1079,10 @@ describe('Actions', () => {
describe('#setProviderType', () => {
let setProviderTypeSpy
+ let store
beforeEach(() => {
+ store = mockStore({ metamask: { provider: {} } })
setProviderTypeSpy = sinon.stub(background, 'setProviderType')
})
@@ -1089,13 +1091,11 @@ describe('Actions', () => {
})
it('', () => {
- const store = mockStore()
store.dispatch(actions.setProviderType())
assert(setProviderTypeSpy.calledOnce)
})
it('', () => {
- const store = mockStore()
const expectedActions = [
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
]
diff --git a/ui/app/actions.js b/ui/app/actions.js
index fa175177e..e1bb6dc2d 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -226,6 +226,7 @@ var actions = {
SET_RPC_TARGET: 'SET_RPC_TARGET',
SET_DEFAULT_RPC_TARGET: 'SET_DEFAULT_RPC_TARGET',
SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE',
+ SET_PREVIOUS_PROVIDER: 'SET_PREVIOUS_PROVIDER',
showConfigPage,
SHOW_ADD_TOKEN_PAGE: 'SHOW_ADD_TOKEN_PAGE',
SHOW_ADD_SUGGESTED_TOKEN_PAGE: 'SHOW_ADD_SUGGESTED_TOKEN_PAGE',
@@ -1866,13 +1867,15 @@ function createSpeedUpTransaction (txId, customGasPrice) {
//
function setProviderType (type) {
- return (dispatch) => {
+ return (dispatch, getState) => {
+ const { type: currentProviderType } = getState().metamask.provider
log.debug(`background.setProviderType`, type)
background.setProviderType(type, (err, result) => {
if (err) {
log.error(err)
return dispatch(actions.displayWarning('Had a problem changing networks!'))
}
+ dispatch(setPreviousProvider(currentProviderType))
dispatch(actions.updateProviderType(type))
dispatch(actions.setSelectedToken())
})
@@ -1887,6 +1890,13 @@ function updateProviderType (type) {
}
}
+function setPreviousProvider (type) {
+ return {
+ type: actions.SET_PREVIOUS_PROVIDER,
+ value: type,
+ }
+}
+
function setRpcTarget (newRpc, chainId, ticker = 'ETH', nickname = '') {
return (dispatch) => {
log.debug(`background.setRpcTarget: ${newRpc} ${chainId} ${ticker} ${nickname}`)
diff --git a/ui/app/app.js b/ui/app/app.js
index 4e58a6396..14b199b8e 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -32,6 +32,7 @@ const CreateAccountPage = require('./components/pages/create-account')
const NoticeScreen = require('./components/pages/notice')
const Loading = require('./components/loading-screen')
+const LoadingNetwork = require('./components/loading-network-screen').default
const NetworkDropdown = require('./components/dropdowns/network-dropdown')
const AccountMenu = require('./components/account-menu')
@@ -169,9 +170,10 @@ class App extends Component {
h(AccountMenu),
h('div.main-container-wrapper', [
- (isLoading || isLoadingNetwork) && h(Loading, {
+ isLoading && h(Loading, {
loadingMessage: loadMessage,
}),
+ !isLoading && isLoadingNetwork && h(LoadingNetwork),
// content
this.renderRoutes(),
diff --git a/ui/app/components/loading-network-screen/index.js b/ui/app/components/loading-network-screen/index.js
new file mode 100644
index 000000000..9d0612f26
--- /dev/null
+++ b/ui/app/components/loading-network-screen/index.js
@@ -0,0 +1,2 @@
+const LoadingNetworksScreen = require('./loading-network-screen.container')
+module.exports = LoadingNetworksScreen
diff --git a/ui/app/components/loading-network-screen/loading-network-screen.component.js b/ui/app/components/loading-network-screen/loading-network-screen.component.js
new file mode 100644
index 000000000..98abf6bd2
--- /dev/null
+++ b/ui/app/components/loading-network-screen/loading-network-screen.component.js
@@ -0,0 +1,145 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import Spinner from '../spinner'
+import Button from '../button'
+
+class LoadingNetworkScreen extends Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ showErrorScreen: false,
+ }
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ componentWillMount = () => {
+ this.cancelCallTimeout = setTimeout(this.cancelCall, this.props.cancelTime || 3000)
+ }
+
+ getConnectingLabel = function (loadingMessage) {
+ if (loadingMessage) {
+ return loadingMessage
+ }
+ const { provider, providerId } = this.props
+ const providerName = provider.type
+
+ let name
+
+ if (providerName === 'mainnet') {
+ name = this.context.t('connectingToMainnet')
+ } else if (providerName === 'ropsten') {
+ name = this.context.t('connectingToRopsten')
+ } else if (providerName === 'kovan') {
+ name = this.context.t('connectingToKovan')
+ } else if (providerName === 'rinkeby') {
+ name = this.context.t('connectingToRinkeby')
+ } else {
+ name = this.context.t('connectingTo', [providerId])
+ }
+
+ return name
+ }
+
+ renderMessage = () => {
+ return <span>{ this.getConnectingLabel(this.props.loadingMessage) }</span>
+ }
+
+ renderLoadingScreenContent = () => {
+ return <div className="loading-overlay__screen-content">
+ <Spinner color="#F7C06C" />
+ {this.renderMessage()}
+ </div>
+ }
+
+ renderErrorScreenContent = () => {
+ const { showNetworkDropdown, setProviderArgs, setProviderType } = this.props
+
+ return <div className="loading-overlay__error-screen">
+ <span className="loading-overlay__emoji">&#128542;</span>
+ <span>Oops! Something went wrong.</span>
+ <div className="loading-overlay__error-buttons">
+ <Button
+ type="default"
+ onClick={() => {
+ window.clearTimeout(this.cancelCallTimeout)
+ showNetworkDropdown()
+ }}
+ >
+ { 'Switch Networks' }
+ </Button>
+
+ <Button
+ type="primary"
+ onClick={() => {
+ this.setState({ showErrorScreen: false })
+ setProviderType(...setProviderArgs)
+ window.clearTimeout(this.cancelCallTimeout)
+ this.cancelCallTimeout = setTimeout(this.cancelCall, this.props.cancelTime || 3000)
+ }}
+ >
+ { 'Try Again' }
+ </Button>
+ </div>
+ </div>
+ }
+
+ cancelCall = () => {
+ const { isLoadingNetwork } = this.props
+
+ if (isLoadingNetwork) {
+ this.setState({ showErrorScreen: true })
+ }
+ }
+
+ componentDidUpdate = (prevProps) => {
+ const { provider } = this.props
+ const { provider: prevProvider } = prevProps
+ if (provider.type !== prevProvider.type) {
+ window.clearTimeout(this.cancelCallTimeout)
+ this.setState({ showErrorScreen: false })
+ this.cancelCallTimeout = setTimeout(this.cancelCall, this.props.cancelTime || 3000)
+ }
+ }
+
+ componentWillUnmount = () => {
+ window.clearTimeout(this.cancelCallTimeout)
+ }
+
+ render () {
+ const { lastSelectedProvider, setProviderType } = this.props
+
+ return (
+ <div className="loading-overlay">
+ <div
+ className="page-container__header-close"
+ onClick={() => setProviderType(lastSelectedProvider || 'ropsten')}
+ />
+ <div className="loading-overlay__container">
+ { this.state.showErrorScreen
+ ? this.renderErrorScreenContent()
+ : this.renderLoadingScreenContent()
+ }
+ </div>
+ </div>
+ )
+ }
+}
+
+LoadingNetworkScreen.propTypes = {
+ loadingMessage: PropTypes.string,
+ cancelTime: PropTypes.number,
+ provider: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ providerId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ showNetworkDropdown: PropTypes.func,
+ setProviderArgs: PropTypes.array,
+ lastSelectedProvider: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ setProviderType: PropTypes.func,
+ isLoadingNetwork: PropTypes.bool,
+
+}
+
+module.exports = LoadingNetworkScreen
diff --git a/ui/app/components/loading-network-screen/loading-network-screen.container.js b/ui/app/components/loading-network-screen/loading-network-screen.container.js
new file mode 100644
index 000000000..d0623e574
--- /dev/null
+++ b/ui/app/components/loading-network-screen/loading-network-screen.container.js
@@ -0,0 +1,41 @@
+import { connect } from 'react-redux'
+import LoadingNetworkScreen from './loading-network-screen.component'
+import actions from '../../actions'
+import { getNetworkIdentifier } from '../../selectors'
+
+const mapStateToProps = state => {
+ const {
+ loadingMessage,
+ currentView,
+ } = state.appState
+ const {
+ provider,
+ lastSelectedProvider,
+ network,
+ } = state.metamask
+ const { rpcTarget, chainId, ticker, nickname, type } = provider
+
+ const setProviderArgs = type === 'rpc'
+ ? [rpcTarget, chainId, ticker, nickname]
+ : [provider.type]
+
+ return {
+ isLoadingNetwork: network === 'loading' && currentView.name !== 'config',
+ loadingMessage,
+ lastSelectedProvider,
+ setProviderArgs,
+ provider,
+ providerId: getNetworkIdentifier(state),
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setProviderType: (type) => {
+ dispatch(actions.setProviderType(type))
+ },
+ showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
+ }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(LoadingNetworkScreen)
diff --git a/ui/app/components/modals/loading-network-error/index.js b/ui/app/components/modals/loading-network-error/index.js
new file mode 100644
index 000000000..b3737458a
--- /dev/null
+++ b/ui/app/components/modals/loading-network-error/index.js
@@ -0,0 +1 @@
+export { default } from './loading-network-error.container'
diff --git a/ui/app/components/modals/loading-network-error/loading-network-error.component.js b/ui/app/components/modals/loading-network-error/loading-network-error.component.js
new file mode 100644
index 000000000..44f71e4b2
--- /dev/null
+++ b/ui/app/components/modals/loading-network-error/loading-network-error.component.js
@@ -0,0 +1,29 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import Modal, { ModalContent } from '../../modal'
+
+const LoadingNetworkError = (props, context) => {
+ const { t } = context
+ const { hideModal } = props
+
+ return (
+ <Modal
+ onSubmit={() => hideModal()}
+ submitText={t('tryAgain')}
+ >
+ <ModalContent
+ description={'Oops! Something went wrong.'}
+ />
+ </Modal>
+ )
+}
+
+LoadingNetworkError.contextTypes = {
+ t: PropTypes.func,
+}
+
+LoadingNetworkError.propTypes = {
+ hideModal: PropTypes.func,
+}
+
+export default LoadingNetworkError
diff --git a/ui/app/components/modals/loading-network-error/loading-network-error.container.js b/ui/app/components/modals/loading-network-error/loading-network-error.container.js
new file mode 100644
index 000000000..3fcba20aa
--- /dev/null
+++ b/ui/app/components/modals/loading-network-error/loading-network-error.container.js
@@ -0,0 +1,4 @@
+import LoadingNetworkError from './loading-network-error.component'
+import withModalProps from '../../../higher-order-components/with-modal-props'
+
+export default withModalProps(LoadingNetworkError)
diff --git a/ui/app/css/itcss/components/loading-overlay.scss b/ui/app/css/itcss/components/loading-overlay.scss
index b023c8423..b5206ef29 100644
--- a/ui/app/css/itcss/components/loading-overlay.scss
+++ b/ui/app/css/itcss/components/loading-overlay.scss
@@ -26,6 +26,27 @@
font-size: 20px;
color: $manatee;
}
+
+ &__error-screen {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ height: 160px;
+ justify-content: space-evenly;
+ }
+
+ &__error-buttons {
+ display: flex;
+ flex-direction: row;
+
+ button {
+ margin: 5px;
+ }
+ }
+
+ &__emoji {
+ font-size: 32px;
+ }
}
.spinner {
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index ea25b8693..22cfe7f8d 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -76,6 +76,7 @@ function reduceApp (state, action) {
trezor: `m/44'/60'/0'/0`,
ledger: `m/44'/60'/0'/0/0`,
},
+ lastSelectedProvider: null,
}, state.appState)
switch (action.type) {
@@ -748,6 +749,14 @@ function reduceApp (state, action) {
networkNonce: action.value,
})
+ case actions.SET_PREVIOUS_PROVIDER:
+ if (action.value === 'loading') {
+ return appState
+ }
+ return extend(appState, {
+ lastSelectedProvider: action.value,
+ })
+
default:
return appState
}