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 { this.getConnectingLabel(this.props.loadingMessage) } + } + + renderLoadingScreenContent = () => { + return
+ + {this.renderMessage()} +
+ } + + renderErrorScreenContent = () => { + const { showNetworkDropdown, setProviderArgs, setProviderType } = this.props + + return
+ 😞 + Oops! Something went wrong. +
+ + + +
+
+ } + + 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 ( +
+
setProviderType(lastSelectedProvider || 'ropsten')} + /> +
+ { this.state.showErrorScreen + ? this.renderErrorScreenContent() + : this.renderLoadingScreenContent() + } +
+
+ ) + } +} + +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 ( + hideModal()} + submitText={t('tryAgain')} + > + + + ) +} + +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 }