aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/app/actions.js177
-rw-r--r--ui/app/app.js1179
-rw-r--r--ui/app/components/account-menu/index.js51
-rw-r--r--ui/app/components/pages/add-token.js (renamed from ui/app/add-token.js)22
-rw-r--r--ui/app/components/pages/authenticated.js34
-rw-r--r--ui/app/components/pages/create-account/import-account/index.js (renamed from ui/app/accounts/import/index.js)2
-rw-r--r--ui/app/components/pages/create-account/import-account/json.js (renamed from ui/app/accounts/import/json.js)19
-rw-r--r--ui/app/components/pages/create-account/import-account/private-key.js (renamed from ui/app/accounts/import/private-key.js)23
-rw-r--r--ui/app/components/pages/create-account/import-account/seed.js (renamed from ui/app/accounts/import/seed.js)2
-rw-r--r--ui/app/components/pages/create-account/index.js81
-rw-r--r--ui/app/components/pages/create-account/new-account.js (renamed from ui/app/accounts/new-account/create-form.js)32
-rw-r--r--ui/app/components/pages/initialized.js25
-rw-r--r--ui/app/components/pages/keychains/restore-vault.js170
-rw-r--r--ui/app/components/pages/keychains/reveal-seed.js195
-rw-r--r--ui/app/components/pages/metamask-route.js28
-rw-r--r--ui/app/components/pages/notice.js203
-rw-r--r--ui/app/components/pages/settings/index.js59
-rw-r--r--ui/app/components/pages/settings/info.js109
-rw-r--r--ui/app/components/pages/settings/settings.js (renamed from ui/app/settings.js)164
-rw-r--r--ui/app/components/pages/signature-request.js284
-rw-r--r--ui/app/components/pages/unlock.js175
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js18
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js15
-rw-r--r--ui/app/components/send/send-v2-container.js8
-rw-r--r--ui/app/components/tab-bar.js26
-rw-r--r--ui/app/components/tx-list.js12
-rw-r--r--ui/app/components/tx-view.js14
-rw-r--r--ui/app/components/wallet-view.js15
-rw-r--r--ui/app/conf-tx.js48
-rw-r--r--ui/app/css/index.scss6
-rw-r--r--ui/app/css/itcss/components/index.scss2
-rw-r--r--ui/app/css/itcss/components/new-account.scss5
-rw-r--r--ui/app/css/itcss/components/pages/index.scss1
-rw-r--r--ui/app/css/itcss/components/pages/unlock.scss9
-rw-r--r--ui/app/css/itcss/components/sections.scss6
-rw-r--r--ui/app/first-time/init-menu.js335
-rw-r--r--ui/app/main-container.js4
-rw-r--r--ui/app/root.js33
-rw-r--r--ui/app/routes.js39
-rw-r--r--ui/app/select-app.js10
-rw-r--r--ui/app/send-v2.js12
-rw-r--r--ui/app/welcome-screen.js5
42 files changed, 2641 insertions, 1016 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 58240054d..4550237d5 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -347,7 +347,7 @@ function transitionBackward () {
}
function confirmSeedWords () {
- return (dispatch) => {
+ return dispatch => {
dispatch(actions.showLoadingIndication())
log.debug(`background.clearSeedWordCache`)
return new Promise((resolve, reject) => {
@@ -355,7 +355,7 @@ function confirmSeedWords () {
dispatch(actions.hideLoadingIndication())
if (err) {
dispatch(actions.displayWarning(err.message))
- reject(err)
+ return reject(err)
}
log.info('Seed word cache cleared. ' + account)
@@ -571,35 +571,47 @@ function signMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- log.debug(`actions calling background.signMessage`)
- background.signMessage(msgData, (err, newState) => {
- log.debug('signMessage called back')
- dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.hideLoadingIndication())
+ return new Promise((resolve, reject) => {
+ log.debug(`actions calling background.signMessage`)
+ background.signMessage(msgData, (err, newState) => {
+ log.debug('signMessage called back')
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.hideLoadingIndication())
- if (err) log.error(err)
- if (err) return dispatch(actions.displayWarning(err.message))
+ if (err) {
+ log.error(err)
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
- dispatch(actions.completedTx(msgData.metamaskId))
+ dispatch(actions.completedTx(msgData.metamaskId))
+ return resolve(msgData)
+ })
})
}
}
function signPersonalMsg (msgData) {
log.debug('action - signPersonalMsg')
- return (dispatch) => {
+ return dispatch => {
dispatch(actions.showLoadingIndication())
- log.debug(`actions calling background.signPersonalMessage`)
- background.signPersonalMessage(msgData, (err, newState) => {
- log.debug('signPersonalMessage called back')
- dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.hideLoadingIndication())
+ return new Promise((resolve, reject) => {
+ log.debug(`actions calling background.signPersonalMessage`)
+ background.signPersonalMessage(msgData, (err, newState) => {
+ log.debug('signPersonalMessage called back')
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.hideLoadingIndication())
- if (err) log.error(err)
- if (err) return dispatch(actions.displayWarning(err.message))
+ if (err) {
+ log.error(err)
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
- dispatch(actions.completedTx(msgData.metamaskId))
+ dispatch(actions.completedTx(msgData.metamaskId))
+ return resolve(msgData)
+ })
})
}
}
@@ -609,16 +621,22 @@ function signTypedMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- log.debug(`actions calling background.signTypedMessage`)
- background.signTypedMessage(msgData, (err, newState) => {
- log.debug('signTypedMessage called back')
- dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.hideLoadingIndication())
+ return new Promise((resolve, reject) => {
+ log.debug(`actions calling background.signTypedMessage`)
+ background.signTypedMessage(msgData, (err, newState) => {
+ log.debug('signTypedMessage called back')
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.hideLoadingIndication())
- if (err) log.error(err)
- if (err) return dispatch(actions.displayWarning(err.message))
+ if (err) {
+ log.error(err)
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
- dispatch(actions.completedTx(msgData.metamaskId))
+ dispatch(actions.completedTx(msgData.metamaskId))
+ return resolve(msgData)
+ })
})
}
}
@@ -798,17 +816,24 @@ function updateTransaction (txData) {
function updateAndApproveTx (txData) {
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
return (dispatch) => {
- log.debug(`actions calling background.updateAndApproveTx.`)
- background.updateAndApproveTransaction(txData, (err) => {
- dispatch(actions.hideLoadingIndication())
- dispatch(actions.updateTransactionParams(txData.id, txData.txParams))
- dispatch(actions.clearSend())
- if (err) {
- dispatch(actions.txError(err))
- dispatch(actions.goHome())
- return log.error(err.message)
- }
- dispatch(actions.completedTx(txData.id))
+ log.debug(`actions calling background.updateAndApproveTx`)
+
+ return new Promise((resolve, reject) => {
+ background.updateAndApproveTransaction(txData, err => {
+ dispatch(actions.hideLoadingIndication())
+ dispatch(actions.updateTransactionParams(txData.id, txData.txParams))
+ dispatch(actions.clearSend())
+
+ if (err) {
+ dispatch(actions.txError(err))
+ dispatch(actions.goHome())
+ log.error(err.message)
+ reject(err)
+ }
+
+ dispatch(actions.completedTx(txData.id))
+ resolve(txData)
+ })
})
}
}
@@ -836,29 +861,77 @@ function txError (err) {
}
function cancelMsg (msgData) {
- log.debug(`background.cancelMessage`)
- background.cancelMessage(msgData.id)
- return actions.completedTx(msgData.id)
+ return dispatch => {
+ dispatch(actions.showLoadingIndication())
+
+ return new Promise((resolve, reject) => {
+ log.debug(`background.cancelMessage`)
+ background.cancelMessage(msgData.id, (err, newState) => {
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.hideLoadingIndication())
+
+ if (err) {
+ return reject(err)
+ }
+
+ dispatch(actions.completedTx(msgData.id))
+ return resolve(msgData)
+ })
+ })
+ }
}
function cancelPersonalMsg (msgData) {
- const id = msgData.id
- background.cancelPersonalMessage(id)
- return actions.completedTx(id)
+ return dispatch => {
+ dispatch(actions.showLoadingIndication())
+
+ return new Promise((resolve, reject) => {
+ const id = msgData.id
+ background.cancelPersonalMessage(id, (err, newState) => {
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.hideLoadingIndication())
+
+ if (err) {
+ return reject(err)
+ }
+
+ dispatch(actions.completedTx(id))
+ return resolve(msgData)
+ })
+ })
+ }
}
function cancelTypedMsg (msgData) {
- const id = msgData.id
- background.cancelTypedMessage(id)
- return actions.completedTx(id)
+ return dispatch => {
+ dispatch(actions.showLoadingIndication())
+
+ return new Promise((resolve, reject) => {
+ const id = msgData.id
+ background.cancelTypedMessage(id, (err, newState) => {
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch(actions.hideLoadingIndication())
+
+ if (err) {
+ return reject(err)
+ }
+
+ dispatch(actions.completedTx(id))
+ return resolve(msgData)
+ })
+ })
+ }
}
function cancelTx (txData) {
- return (dispatch) => {
+ return dispatch => {
log.debug(`background.cancelTransaction`)
- background.cancelTransaction(txData.id, () => {
- dispatch(actions.clearSend())
- dispatch(actions.completedTx(txData.id))
+ return new Promise((resolve, reject) => {
+ background.cancelTransaction(txData.id, () => {
+ dispatch(actions.clearSend())
+ dispatch(actions.completedTx(txData.id))
+ resolve(txData)
+ })
})
}
}
@@ -1772,7 +1845,7 @@ function forceUpdateMetamaskState (dispatch) {
}
dispatch(actions.updateMetamaskState(newState))
- resolve()
+ resolve(newState)
})
})
}
diff --git a/ui/app/app.js b/ui/app/app.js
index d23238bab..719830fda 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -1,42 +1,47 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
+const { Component } = require('react')
+const PropTypes = require('prop-types')
const connect = require('./metamask-connect')
+const { Route, Switch, Redirect, withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const h = require('react-hyperscript')
const actions = require('./actions')
const classnames = require('classnames')
-// mascara
-const MascaraFirstTime = require('../../mascara/src/app/first-time').default
-const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
// init
-const OldUIInitializeMenuScreen = require('./first-time/init-menu')
-const InitializeMenuScreen = MascaraFirstTime
-const NewKeyChainScreen = require('./new-keychain')
+const InitializeScreen = require('../../mascara/src/app/first-time').default
const WelcomeScreen = require('./welcome-screen').default
+const NewKeyChainScreen = require('./new-keychain')
+// mascara
+const MascaraCreatePassword = require('../../mascara/src/app/first-time/create-password-screen').default
+const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default
+const MascaraNoticeScreen = require('../../mascara/src/app/first-time/notice-screen').default
+const MascaraSeedScreen = require('../../mascara/src/app/first-time/seed-screen').default
+const MascaraConfirmSeedScreen = require('../../mascara/src/app/first-time/confirm-seed-screen').default
// accounts
const MainContainer = require('./main-container')
const SendTransactionScreen2 = require('./components/send/send-v2-container')
const ConfirmTxScreen = require('./conf-tx')
-// notice
-const NoticeScreen = require('./components/notice')
-const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
// slideout menu
const WalletView = require('./components/wallet-view')
// other views
-const Settings = require('./settings')
-const AddTokenScreen = require('./add-token')
-const Import = require('./accounts/import')
-const NewAccount = require('./accounts/new-account')
+const Authenticated = require('./components/pages/authenticated')
+const Initialized = require('./components/pages/initialized')
+const Settings = require('./components/pages/settings')
+const UnlockPage = require('./components/pages/unlock')
+const RestoreVaultPage = require('./components/pages/keychains/restore-vault')
+const RevealSeedPage = require('./components/pages/keychains/reveal-seed')
+const AddTokenPage = require('./components/pages/add-token')
+const CreateAccountPage = require('./components/pages/create-account')
+const NoticeScreen = require('./components/pages/notice')
+const SignatureRequestPage = require('./components/pages/signature-request')
+
const Loading = require('./components/loading')
const NetworkIndicator = require('./components/network')
const Identicon = require('./components/identicon')
const BuyView = require('./components/buy-button-subview')
-const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
-const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
-const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const NetworkDropdown = require('./components/dropdowns/network-dropdown')
const AccountMenu = require('./components/account-menu')
@@ -45,548 +50,716 @@ const QrView = require('./components/qr-code')
// Global Modals
const Modal = require('./components/modals/index').Modal
-module.exports = connect(mapStateToProps, mapDispatchToProps)(App)
-
-inherits(App, Component)
-function App () { Component.call(this) }
-
-function mapStateToProps (state) {
- const {
- identities,
- accounts,
- address,
- keyrings,
- isInitialized,
- noActiveNotices,
- seedWords,
- } = state.metamask
- const selected = address || Object.keys(accounts)[0]
+// Routes
+const {
+ DEFAULT_ROUTE,
+ UNLOCK_ROUTE,
+ SETTINGS_ROUTE,
+ REVEAL_SEED_ROUTE,
+ CONFIRM_SEED_ROUTE,
+ RESTORE_VAULT_ROUTE,
+ ADD_TOKEN_ROUTE,
+ NEW_ACCOUNT_ROUTE,
+ SEND_ROUTE,
+ CONFIRM_TRANSACTION_ROUTE,
+ INITIALIZE_ROUTE,
+ NOTICE_ROUTE,
+ SIGNATURE_REQUEST_ROUTE,
+ WELCOME_ROUTE,
+} = require('./routes')
+
+class App extends Component {
+ constructor (props) {
+ super(props)
+
+ this.renderPrimary = this.renderPrimary.bind(this)
+ }
- return {
- // state from plugin
- networkDropdownOpen: state.appState.networkDropdownOpen,
- sidebarOpen: state.appState.sidebarOpen,
- isLoading: state.appState.isLoading,
- loadingMessage: state.appState.loadingMessage,
- noActiveNotices: state.metamask.noActiveNotices,
- isInitialized: state.metamask.isInitialized,
- isUnlocked: state.metamask.isUnlocked,
- selectedAddress: state.metamask.selectedAddress,
- currentView: state.appState.currentView,
- activeAddress: state.appState.activeAddress,
- transForward: state.appState.transForward,
- isMascara: state.metamask.isMascara,
- isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
- isPopup: state.metamask.isPopup,
- seedWords: state.metamask.seedWords,
- unapprovedTxs: state.metamask.unapprovedTxs,
- unapprovedMsgs: state.metamask.unapprovedMsgs,
- menuOpen: state.appState.menuOpen,
- network: state.metamask.network,
- provider: state.metamask.provider,
- forgottenPassword: state.metamask.forgottenPassword,
- lastUnreadNotice: state.metamask.lastUnreadNotice,
- lostAccounts: state.metamask.lostAccounts,
- frequentRpcList: state.metamask.frequentRpcList || [],
- currentCurrency: state.metamask.currentCurrency,
- isMouseUser: state.appState.isMouseUser,
- betaUI: state.metamask.featureFlags.betaUI,
- isRevealingSeedWords: state.metamask.isRevealingSeedWords,
- Qr: state.appState.Qr,
- welcomeScreenSeen: state.metamask.welcomeScreenSeen,
+ componentWillMount () {
+ const { currentCurrency, setCurrentCurrencyToUSD } = this.props
- // state needed to get account dropdown temporarily rendering from app bar
- identities,
- selected,
- keyrings,
+ if (!currentCurrency) {
+ setCurrentCurrencyToUSD()
+ }
}
-}
-function mapDispatchToProps (dispatch, ownProps) {
- return {
- dispatch,
- hideSidebar: () => dispatch(actions.hideSidebar()),
- showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
- hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
- setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
- toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
- setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
- }
-}
+ renderRoutes () {
+ const exact = true
-App.prototype.componentWillMount = function () {
- if (!this.props.currentCurrency) {
- this.props.setCurrentCurrencyToUSD()
+ return (
+ h(Switch, [
+ h(Route, {
+ path: WELCOME_ROUTE,
+ exact,
+ component: WelcomeScreen,
+ }),
+ h(Route, {
+ path: INITIALIZE_ROUTE,
+ component: InitializeScreen,
+ }),
+ h(Initialized, {
+ path: REVEAL_SEED_ROUTE,
+ exact,
+ component: RevealSeedPage,
+ mascaraComponent: MascaraSeedScreen,
+ }),
+ h(Initialized, {
+ path: CONFIRM_SEED_ROUTE,
+ exact,
+ mascaraComponent: MascaraConfirmSeedScreen,
+ }),
+ h(Initialized, { path: UNLOCK_ROUTE, exact, component: UnlockPage }),
+ h(Initialized, { path: SETTINGS_ROUTE, component: Settings }),
+ h(Initialized, { path: RESTORE_VAULT_ROUTE, exact, component: RestoreVaultPage }),
+ h(Initialized, {
+ path: NOTICE_ROUTE,
+ exact,
+ component: MascaraNoticeScreen,
+ }),
+ h(Authenticated, { path: CONFIRM_TRANSACTION_ROUTE, exact, component: ConfirmTxScreen }),
+ h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }),
+ h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }),
+ h(Authenticated, { path: NEW_ACCOUNT_ROUTE, component: CreateAccountPage }),
+ h(Authenticated, { path: SIGNATURE_REQUEST_ROUTE, exact, component: SignatureRequestPage }),
+ h(Authenticated, { path: DEFAULT_ROUTE, exact, component: this.renderPrimary }),
+ ])
+ )
}
-}
-App.prototype.render = function () {
- var props = this.props
- const {
- isLoading,
- loadingMessage,
- network,
- isMouseUser,
- setMouseUserState,
- } = props
- const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
- const loadMessage = loadingMessage || isLoadingNetwork ?
- this.getConnectingLabel() : null
- log.debug('Main ui render function')
-
- return (
- h('.flex-column.full-height', {
- className: classnames({ 'mouse-user-styles': isMouseUser }),
- style: {
- overflowX: 'hidden',
- position: 'relative',
- alignItems: 'center',
- },
- tabIndex: '0',
- onClick: () => setMouseUserState(true),
- onKeyDown: (e) => {
- if (e.keyCode === 9) {
- setMouseUserState(false)
- }
- },
- }, [
+ render () {
+ const {
+ isLoading,
+ loadingMessage,
+ network,
+ isMouseUser,
+ provider,
+ frequentRpcList,
+ currentView,
+ setMouseUserState,
+ } = this.props
+ const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
+ const loadMessage = loadingMessage || isLoadingNetwork ?
+ this.getConnectingLabel() : null
+ log.debug('Main ui render function')
+
+ return (
+ h('.flex-column.full-height', {
+ className: classnames({ 'mouse-user-styles': isMouseUser }),
+ style: {
+ overflowX: 'hidden',
+ position: 'relative',
+ alignItems: 'center',
+ },
+ tabIndex: '0',
+ onClick: () => setMouseUserState(true),
+ onKeyDown: (e) => {
+ if (e.keyCode === 9) {
+ setMouseUserState(false)
+ }
+ },
+ }, [
- // global modal
- h(Modal, {}, []),
+ // global modal
+ h(Modal, {}, []),
- // app bar
- this.renderAppBar(),
+ // app bar
+ this.renderAppBar(),
- // sidebar
- this.renderSidebar(),
+ // sidebar
+ this.renderSidebar(),
- // network dropdown
- h(NetworkDropdown, {
- provider: this.props.provider,
- frequentRpcList: this.props.frequentRpcList,
- }, []),
+ // network dropdown
+ h(NetworkDropdown, {
+ provider,
+ frequentRpcList,
+ }, []),
- h(AccountMenu),
+ h(AccountMenu),
- (isLoading || isLoadingNetwork) && h(Loading, {
- loadingMessage: loadMessage,
- }),
+ (isLoading || isLoadingNetwork) && h(Loading, {
+ loadingMessage: loadMessage,
+ }),
- // this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
+ // this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
- // content
- this.renderPrimary(),
- ])
- )
-}
-
-App.prototype.renderGlobalModal = function () {
- return h(Modal, {
- ref: 'modalRef',
- }, [
- // h(BuyOptions, {}, []),
- ])
-}
+ // content
+ this.renderRoutes(),
+ ])
+ )
+ }
-App.prototype.renderSidebar = function () {
-
- return h('div', {
- }, [
- h('style', `
- .sidebar-enter {
- transition: transform 300ms ease-in-out;
- transform: translateX(-100%);
- }
- .sidebar-enter.sidebar-enter-active {
- transition: transform 300ms ease-in-out;
- transform: translateX(0%);
- }
- .sidebar-leave {
- transition: transform 200ms ease-out;
- transform: translateX(0%);
- }
- .sidebar-leave.sidebar-leave-active {
- transition: transform 200ms ease-out;
- transform: translateX(-100%);
- }
- `),
-
- h(ReactCSSTransitionGroup, {
- transitionName: 'sidebar',
- transitionEnterTimeout: 300,
- transitionLeaveTimeout: 200,
+ renderGlobalModal () {
+ return h(Modal, {
+ ref: 'modalRef',
}, [
- // A second instance of Walletview is used for non-mobile viewports
- this.props.sidebarOpen ? h(WalletView, {
- responsiveDisplayClassname: '.sidebar',
- style: {},
- }) : undefined,
-
- ]),
-
- // overlay
- // TODO: add onClick for overlay to close sidebar
- this.props.sidebarOpen ? h('div.sidebar-overlay', {
- style: {},
- onClick: () => {
- this.props.hideSidebar()
- },
- }, []) : undefined,
- ])
-}
-
-App.prototype.renderAppBar = function () {
- const {
- isUnlocked,
- network,
- provider,
- networkDropdownOpen,
- showNetworkDropdown,
- hideNetworkDropdown,
- currentView,
- isInitialized,
- betaUI,
- isPopup,
- welcomeScreenSeen,
- } = this.props
-
- if (window.METAMASK_UI_TYPE === 'notification') {
- return null
+ // h(BuyOptions, {}, []),
+ ])
}
- const props = this.props
- const {isMascara, isOnboarding} = props
-
- // Do not render header if user is in mascara onboarding
- if (isMascara && isOnboarding) {
- return null
- }
+ renderSidebar () {
+ return h('div', [
+ h('style', `
+ .sidebar-enter {
+ transition: transform 300ms ease-in-out;
+ transform: translateX(-100%);
+ }
+ .sidebar-enter.sidebar-enter-active {
+ transition: transform 300ms ease-in-out;
+ transform: translateX(0%);
+ }
+ .sidebar-leave {
+ transition: transform 200ms ease-out;
+ transform: translateX(0%);
+ }
+ .sidebar-leave.sidebar-leave-active {
+ transition: transform 200ms ease-out;
+ transform: translateX(-100%);
+ }
+ `),
- // Do not render header if user is in mascara buy ether
- if (isMascara && props.currentView.name === 'buyEth') {
- return null
- }
+ h(ReactCSSTransitionGroup, {
+ transitionName: 'sidebar',
+ transitionEnterTimeout: 300,
+ transitionLeaveTimeout: 200,
+ }, [
+ // A second instance of Walletview is used for non-mobile viewports
+ this.props.sidebarOpen ? h(WalletView, {
+ responsiveDisplayClassname: '.sidebar',
+ style: {},
+ }) : undefined,
- return (
+ ]),
- h('.full-width', {
- style: {},
- }, [
+ // overlay
+ // TODO: add onClick for overlay to close sidebar
+ this.props.sidebarOpen ? h('div.sidebar-overlay', {
+ style: {},
+ onClick: () => {
+ this.props.hideSidebar()
+ },
+ }, []) : undefined,
+ ])
+ }
- (isInitialized || welcomeScreenSeen || isPopup || !betaUI) && h('.app-header.flex-row.flex-space-between', {
- className: classnames({
- 'app-header--initialized': !isOnboarding,
- }),
+ renderAppBar () {
+ const {
+ isUnlocked,
+ network,
+ provider,
+ networkDropdownOpen,
+ showNetworkDropdown,
+ hideNetworkDropdown,
+ currentView,
+ isInitialized,
+ welcomeScreenSeen,
+ isPopup,
+ betaUI,
+ } = this.props
+
+ if (window.METAMASK_UI_TYPE === 'notification') {
+ return null
+ }
+
+ const props = this.props
+ const {isMascara, isOnboarding} = props
+
+ // Do not render header if user is in mascara onboarding
+ if (isMascara && isOnboarding) {
+ return null
+ }
+
+ // Do not render header if user is in mascara buy ether
+ if (isMascara && props.currentView.name === 'buyEth') {
+ return null
+ }
+
+ return (
+
+ h('.full-width', {
+ style: {},
}, [
- h('div.app-header-contents', {}, [
- h('div.left-menu-wrapper', {
- onClick: () => {
- props.dispatch(actions.backToAccountDetail(props.activeAddress))
- },
- }, [
- // mini logo
- h('img.metafox-icon', {
- height: 42,
- width: 42,
- src: '/images/metamask-fox.svg',
- }),
- // metamask name
- h('.flex-row', [
- h('h1', this.props.t('appName')),
- h('div.beta-label', this.props.t('beta')),
- ]),
- ]),
-
- betaUI && isInitialized && h('div.header__right-actions', [
- h('div.network-component-wrapper', {
- style: {},
+ (isInitialized || welcomeScreenSeen || isPopup || !betaUI) && h('.app-header.flex-row.flex-space-between', {
+ className: classnames({
+ 'app-header--initialized': !isOnboarding,
+ }),
+ }, [
+ h('div.app-header-contents', {}, [
+ h('div.left-menu-wrapper', {
+ onClick: () => props.history.push(DEFAULT_ROUTE),
}, [
- // Network Indicator
- h(NetworkIndicator, {
- network,
- provider,
- disabled: currentView.name === 'confTx',
- onClick: (event) => {
- event.preventDefault()
- event.stopPropagation()
- return networkDropdownOpen === false
- ? showNetworkDropdown()
- : hideNetworkDropdown()
- },
+ // mini logo
+ h('img.metafox-icon', {
+ height: 42,
+ width: 42,
+ src: '/images/metamask-fox.svg',
}),
+ // metamask name
+ h('.flex-row', [
+ h('h1', this.props.t('appName')),
+ h('div.beta-label', this.props.t('beta')),
+ ]),
+
]),
- isUnlocked && h('div.account-menu__icon', { onClick: this.props.toggleAccountMenu }, [
- h(Identicon, {
- address: this.props.selectedAddress,
- diameter: 32,
- }),
+ betaUI && isInitialized && h('div.header__right-actions', [
+ h('div.network-component-wrapper', {
+ style: {},
+ }, [
+ // Network Indicator
+ h(NetworkIndicator, {
+ network,
+ provider,
+ disabled: currentView.name === 'confTx',
+ onClick: (event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ return networkDropdownOpen === false
+ ? showNetworkDropdown()
+ : hideNetworkDropdown()
+ },
+ }),
+
+ ]),
+
+ isUnlocked && h('div.account-menu__icon', { onClick: this.props.toggleAccountMenu }, [
+ h(Identicon, {
+ address: this.props.selectedAddress,
+ diameter: 32,
+ }),
+ ]),
]),
]),
]),
- ]),
- !isInitialized && !isPopup && betaUI && h('.alpha-warning__container', {}, [
- h('h2', {
- className: classnames({
- 'alpha-warning': welcomeScreenSeen,
- 'alpha-warning-welcome-screen': !welcomeScreenSeen,
- }),
- }, 'Please be aware that this version is still under development'),
- ]),
-
- ])
- )
-}
-
-App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
- const { isMascara } = this.props
-
- return isMascara
- ? null
- : h(Loading, {
- isLoading: isLoading || isLoadingNetwork,
- loadingMessage: loadMessage,
- })
-}
-
-App.prototype.renderBackButton = function (style, justArrow = false) {
- var props = this.props
- return (
- h('.flex-row', {
- key: 'leftArrow',
- style: style,
- onClick: () => props.dispatch(actions.goBackToInitView()),
- }, [
- h('i.fa.fa-arrow-left.cursor-pointer'),
- justArrow ? null : h('div.cursor-pointer', {
- style: {
- marginLeft: '3px',
- },
- onClick: () => props.dispatch(actions.goBackToInitView()),
- }, 'BACK'),
- ])
- )
-}
-
-App.prototype.renderPrimary = function () {
- log.debug('rendering primary')
- var props = this.props
- const {
- isMascara,
- isOnboarding,
- betaUI,
- isRevealingSeedWords,
- welcomeScreenSeen,
- Qr,
- isInitialized,
- isUnlocked,
- } = props
- const isMascaraOnboarding = isMascara && isOnboarding
- const isBetaUIOnboarding = betaUI && isOnboarding
-
- if (!welcomeScreenSeen && betaUI && !isInitialized && !isUnlocked) {
- return h(WelcomeScreen)
- }
-
- if (isMascaraOnboarding || isBetaUIOnboarding) {
- return h(MascaraFirstTime)
- }
-
- // notices
- if (!props.noActiveNotices && !betaUI) {
- log.debug('rendering notice screen for unread notices.')
- return h(NoticeScreen, {
- notice: props.lastUnreadNotice,
- key: 'NoticeScreen',
- onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
- })
- } else if (props.lostAccounts && props.lostAccounts.length > 0) {
- log.debug('rendering notice screen for lost accounts view.')
- return h(NoticeScreen, {
- notice: generateLostAccountsNotice(props.lostAccounts),
- key: 'LostAccountsNotice',
- onConfirm: () => props.dispatch(actions.markAccountsFound()),
- })
- }
+ !isInitialized && !isPopup && betaUI && h('.alpha-warning__container', {}, [
+ h('h2', {
+ className: classnames({
+ 'alpha-warning': welcomeScreenSeen,
+ 'alpha-warning-welcome-screen': !welcomeScreenSeen,
+ }),
+ }, 'Please be aware that this version is still under development'),
+ ]),
- if (props.isInitialized && props.forgottenPassword) {
- log.debug('rendering restore vault screen')
- return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
- } else if (!props.isInitialized && !props.isUnlocked && !isRevealingSeedWords) {
- log.debug('rendering menu screen')
- return !betaUI
- ? h(OldUIInitializeMenuScreen, {key: 'menuScreenInit'})
- : h(InitializeMenuScreen, {key: 'menuScreenInit'})
+ ])
+ )
}
- // show unlock screen
- if (!props.isUnlocked) {
- return h(MainContainer, {
- currentViewName: props.currentView.name,
- isUnlocked: props.isUnlocked,
- })
- }
+ renderLoadingIndicator ({ isLoading, isLoadingNetwork, loadMessage }) {
+ const { isMascara } = this.props
- // show seed words screen
- if (props.seedWords) {
- log.debug('rendering seed words')
- return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
+ return isMascara
+ ? null
+ : h(Loading, {
+ isLoading: isLoading || isLoadingNetwork,
+ loadingMessage: loadMessage,
+ })
}
- // show current view
- switch (props.currentView.name) {
-
- case 'accountDetail':
- log.debug('rendering main container')
- return h(MainContainer, {key: 'account-detail'})
-
- case 'sendTransaction':
- log.debug('rendering send tx screen')
-
- // Going to leave this here until we are ready to delete SendTransactionScreen v1
- // const SendComponentToRender = checkFeatureToggle('send-v2')
- // ? SendTransactionScreen2
- // : SendTransactionScreen
-
- return h(SendTransactionScreen2, {key: 'send-transaction'})
-
- case 'sendToken':
- log.debug('rendering send token screen')
-
- // Going to leave this here until we are ready to delete SendTransactionScreen v1
- // const SendTokenComponentToRender = checkFeatureToggle('send-v2')
- // ? SendTransactionScreen2
- // : SendTokenScreen
-
- return h(SendTransactionScreen2, {key: 'sendToken'})
-
- case 'newKeychain':
- log.debug('rendering new keychain screen')
- return h(NewKeyChainScreen, {key: 'new-keychain'})
+ renderBackButton (style, justArrow = false) {
+ const { dispatch } = this.props
- case 'confTx':
- log.debug('rendering confirm tx screen')
- return h(ConfirmTxScreen, {key: 'confirm-tx'})
-
- case 'add-token':
- log.debug('rendering add-token screen from unlock screen.')
- return h(AddTokenScreen, {key: 'add-token'})
-
- case 'config':
- log.debug('rendering config screen')
- return h(Settings, {key: 'config'})
-
- case 'import-menu':
- log.debug('rendering import screen')
- return h(Import, {key: 'import-menu'})
-
- case 'new-account-page':
- log.debug('rendering new account screen')
- return h(NewAccount, {key: 'new-account'})
-
- case 'reveal-seed-conf':
- log.debug('rendering reveal seed confirmation screen')
- return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
-
- case 'info':
- log.debug('rendering info screen')
- return h(Settings, {key: 'info', tab: 'info'})
-
- case 'buyEth':
- log.debug('rendering buy ether screen')
- return h(BuyView, {key: 'buyEthView'})
-
- case 'onboardingBuyEth':
- log.debug('rendering onboarding buy ether screen')
- return h(MascaraBuyEtherScreen, {key: 'buyEthView'})
-
- case 'qr':
- log.debug('rendering show qr screen')
- return h('div', {
- style: {
- position: 'absolute',
- height: '100%',
- top: '0px',
- left: '0px',
- },
+ return (
+ h('.flex-row', {
+ key: 'leftArrow',
+ style: style,
+ onClick: () => dispatch(actions.goBackToInitView()),
}, [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
- onClick: () => props.dispatch(actions.backToAccountDetail(props.activeAddress)),
+ h('i.fa.fa-arrow-left.cursor-pointer'),
+ justArrow ? null : h('div.cursor-pointer', {
style: {
- marginLeft: '10px',
- marginTop: '50px',
+ marginLeft: '3px',
},
- }),
- h('div', {
+ onClick: () => dispatch(actions.goBackToInitView()),
+ }, 'BACK'),
+ ])
+ )
+ }
+
+ renderPrimary () {
+ log.debug('rendering primary')
+ const {
+ noActiveNotices,
+ lostAccounts,
+ forgottenPassword,
+ currentView,
+ activeAddress,
+ unapprovedTxs = {},
+ seedWords,
+ unapprovedMsgCount = 0,
+ unapprovedPersonalMsgCount = 0,
+ unapprovedTypedMessagesCount = 0,
+ } = this.props
+
+ // seed words
+ if (seedWords) {
+ log.debug('rendering seed words')
+ return h(Redirect, {
+ to: {
+ pathname: REVEAL_SEED_ROUTE,
+ },
+ })
+ }
+
+ if (forgottenPassword) {
+ log.debug('rendering restore vault screen')
+ return h(Redirect, {
+ to: {
+ pathname: RESTORE_VAULT_ROUTE,
+ },
+ })
+ }
+
+ // notices
+ if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) {
+ return h(Redirect, {
+ to: {
+ pathname: NOTICE_ROUTE,
+ },
+ })
+ }
+
+ // unapprovedTxs
+ if (Object.keys(unapprovedTxs).length) {
+ return h(Redirect, {
+ to: {
+ pathname: CONFIRM_TRANSACTION_ROUTE,
+ },
+ })
+ }
+
+ // unapproved messages
+ if (unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) {
+ return h(Redirect, {
+ to: {
+ pathname: SIGNATURE_REQUEST_ROUTE,
+ },
+ })
+ }
+
+ // if (!props.noActiveNotices) {
+ // log.debug('rendering notice screen for unread notices.')
+ // return h(NoticeScreen, {
+ // notice: props.lastUnreadNotice,
+ // key: 'NoticeScreen',
+ // onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
+ // })
+ // } else if (props.lostAccounts && props.lostAccounts.length > 0) {
+ // log.debug('rendering notice screen for lost accounts view.')
+ // return h(NoticeScreen, {
+ // notice: generateLostAccountsNotice(props.lostAccounts),
+ // key: 'LostAccountsNotice',
+ // onConfirm: () => props.dispatch(actions.markAccountsFound()),
+ // })
+ // }
+
+ // if (props.seedWords) {
+ // log.debug('rendering seed words')
+ // return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
+ // }
+
+ // show initialize screen
+ // if (!isInitialized || forgottenPassword) {
+ // // show current view
+ // log.debug('rendering an initialize screen')
+ // // switch (props.currentView.name) {
+
+ // // case 'restoreVault':
+ // // log.debug('rendering restore vault screen')
+ // // return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
+
+ // // default:
+ // // log.debug('rendering menu screen')
+ // // return h(InitializeScreen, {key: 'menuScreenInit'})
+ // // }
+ // }
+
+ // // show unlock screen
+ // if (!props.isUnlocked) {
+ // return h(MainContainer, {
+ // currentViewName: props.currentView.name,
+ // isUnlocked: props.isUnlocked,
+ // })
+ // }
+
+ // show current view
+ switch (currentView.name) {
+
+ case 'accountDetail':
+ log.debug('rendering main container')
+ return h(MainContainer, {key: 'account-detail'})
+
+ // case 'sendTransaction':
+ // log.debug('rendering send tx screen')
+
+ // // Going to leave this here until we are ready to delete SendTransactionScreen v1
+ // // const SendComponentToRender = checkFeatureToggle('send-v2')
+ // // ? SendTransactionScreen2
+ // // : SendTransactionScreen
+
+ // return h(SendTransactionScreen2, {key: 'send-transaction'})
+
+ // case 'sendToken':
+ // log.debug('rendering send token screen')
+
+ // // Going to leave this here until we are ready to delete SendTransactionScreen v1
+ // // const SendTokenComponentToRender = checkFeatureToggle('send-v2')
+ // // ? SendTransactionScreen2
+ // // : SendTokenScreen
+
+ // return h(SendTransactionScreen2, {key: 'sendToken'})
+
+ case 'newKeychain':
+ log.debug('rendering new keychain screen')
+ return h(NewKeyChainScreen, {key: 'new-keychain'})
+
+ // case 'confTx':
+ // log.debug('rendering confirm tx screen')
+ // return h(Redirect, {
+ // to: {
+ // pathname: CONFIRM_TRANSACTION_ROUTE,
+ // },
+ // })
+ // return h(ConfirmTxScreen, {key: 'confirm-tx'})
+
+ // case 'add-token':
+ // log.debug('rendering add-token screen from unlock screen.')
+ // return h(AddTokenScreen, {key: 'add-token'})
+
+ // case 'config':
+ // log.debug('rendering config screen')
+ // return h(Settings, {key: 'config'})
+
+ // case 'import-menu':
+ // log.debug('rendering import screen')
+ // return h(Import, {key: 'import-menu'})
+
+ // case 'reveal-seed-conf':
+ // log.debug('rendering reveal seed confirmation screen')
+ // return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
+
+ // case 'info':
+ // log.debug('rendering info screen')
+ // return h(Settings, {key: 'info', tab: 'info'})
+
+ case 'buyEth':
+ log.debug('rendering buy ether screen')
+ return h(BuyView, {key: 'buyEthView'})
+
+ case 'onboardingBuyEth':
+ log.debug('rendering onboarding buy ether screen')
+ return h(MascaraBuyEtherScreen, {key: 'buyEthView'})
+
+ case 'qr':
+ log.debug('rendering show qr screen')
+ return h('div', {
style: {
position: 'absolute',
- left: '44px',
- width: '285px',
+ height: '100%',
+ top: '0px',
+ left: '0px',
},
}, [
- h(QrView, {key: 'qr', Qr}),
- ]),
- ])
+ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
+ onClick: () => this.props.dispatch(actions.backToAccountDetail(activeAddress)),
+ style: {
+ marginLeft: '10px',
+ marginTop: '50px',
+ },
+ }),
+ h('div', {
+ style: {
+ position: 'absolute',
+ left: '44px',
+ width: '285px',
+ },
+ }, [
+ h(QrView, {key: 'qr'}),
+ ]),
+ ])
- default:
- log.debug('rendering default, account detail screen')
- return h(MainContainer, {key: 'account-detail'})
+ default:
+ log.debug('rendering default, account detail screen')
+ return h(MainContainer, {key: 'account-detail'})
+ }
}
-}
-App.prototype.toggleMetamaskActive = function () {
- if (!this.props.isUnlocked) {
- // currently inactive: redirect to password box
- var passwordBox = document.querySelector('input[type=password]')
- if (!passwordBox) return
- passwordBox.focus()
- } else {
- // currently active: deactivate
- this.props.dispatch(actions.lockMetamask(false))
+ toggleMetamaskActive () {
+ if (!this.props.isUnlocked) {
+ // currently inactive: redirect to password box
+ var passwordBox = document.querySelector('input[type=password]')
+ if (!passwordBox) return
+ passwordBox.focus()
+ } else {
+ // currently active: deactivate
+ this.props.dispatch(actions.lockMetamask(false))
+ }
}
-}
-App.prototype.getConnectingLabel = function () {
- const { provider } = this.props
- const providerName = provider.type
-
- let name
-
- if (providerName === 'mainnet') {
- name = this.props.t('connectingToMainnet')
- } else if (providerName === 'ropsten') {
- name = this.props.t('connectingToRopsten')
- } else if (providerName === 'kovan') {
- name = this.props.t('connectingToRopsten')
- } else if (providerName === 'rinkeby') {
- name = this.props.t('connectingToRinkeby')
- } else {
- name = this.props.t('connectingToUnknown')
+ getConnectingLabel = function () {
+ const { provider } = this.props
+ const providerName = provider.type
+
+ let name
+
+ if (providerName === 'mainnet') {
+ name = this.props.t('connectingToMainnet')
+ } else if (providerName === 'ropsten') {
+ name = this.props.t('connectingToRopsten')
+ } else if (providerName === 'kovan') {
+ name = this.props.t('connectingToRopsten')
+ } else if (providerName === 'rinkeby') {
+ name = this.props.t('connectingToRinkeby')
+ } else {
+ name = this.props.t('connectingToUnknown')
+ }
+
+ return name
}
- return name
+ getNetworkName () {
+ const { provider } = this.props
+ const providerName = provider.type
+
+ let name
+
+ if (providerName === 'mainnet') {
+ name = this.props.t('mainnet')
+ } else if (providerName === 'ropsten') {
+ name = this.props.t('ropsten')
+ } else if (providerName === 'kovan') {
+ name = this.props.t('kovan')
+ } else if (providerName === 'rinkeby') {
+ name = this.props.t('rinkeby')
+ } else {
+ name = this.props.t('unknownNetwork')
+ }
+
+ return name
+ }
}
-App.prototype.getNetworkName = function () {
- const { provider } = this.props
- const providerName = provider.type
-
- let name
-
- if (providerName === 'mainnet') {
- name = this.props.t('mainnet')
- } else if (providerName === 'ropsten') {
- name = this.props.t('ropsten')
- } else if (providerName === 'kovan') {
- name = this.props.t('kovan')
- } else if (providerName === 'rinkeby') {
- name = this.props.t('rinkeby')
- } else {
- name = this.props.t('unknownNetwork')
+App.propTypes = {
+ currentCurrency: PropTypes.string,
+ setCurrentCurrencyToUSD: PropTypes.func,
+ isLoading: PropTypes.bool,
+ loadingMessage: PropTypes.string,
+ network: PropTypes.string,
+ provider: PropTypes.object,
+ frequentRpcList: PropTypes.array,
+ currentView: PropTypes.object,
+ sidebarOpen: PropTypes.bool,
+ hideSidebar: PropTypes.func,
+ isMascara: PropTypes.bool,
+ isOnboarding: PropTypes.bool,
+ isUnlocked: PropTypes.bool,
+ networkDropdownOpen: PropTypes.bool,
+ showNetworkDropdown: PropTypes.func,
+ hideNetworkDropdown: PropTypes.func,
+ history: PropTypes.object,
+ dispatch: PropTypes.func,
+ toggleAccountMenu: PropTypes.func,
+ selectedAddress: PropTypes.string,
+ noActiveNotices: PropTypes.bool,
+ lostAccounts: PropTypes.array,
+ isInitialized: PropTypes.bool,
+ forgottenPassword: PropTypes.bool,
+ activeAddress: PropTypes.string,
+ unapprovedTxs: PropTypes.object,
+ seedWords: PropTypes.string,
+ unapprovedMsgCount: PropTypes.number,
+ unapprovedPersonalMsgCount: PropTypes.number,
+ unapprovedTypedMessagesCount: PropTypes.number,
+ welcomeScreenSeen: PropTypes.bool,
+ isPopup: PropTypes.bool,
+ betaUI: PropTypes.bool,
+ isMouseUser: PropTypes.bool,
+ setMouseUserState: PropTypes.func,
+}
+
+function mapStateToProps (state) {
+ const { appState, metamask } = state
+ const {
+ networkDropdownOpen,
+ sidebarOpen,
+ isLoading,
+ loadingMessage,
+ } = appState
+
+ const {
+ identities,
+ accounts,
+ address,
+ keyrings,
+ isInitialized,
+ noActiveNotices,
+ seedWords,
+ unapprovedTxs,
+ lastUnreadNotice,
+ lostAccounts,
+ unapprovedMsgCount,
+ unapprovedPersonalMsgCount,
+ unapprovedTypedMessagesCount,
+ } = metamask
+ const selected = address || Object.keys(accounts)[0]
+
+ return {
+ // state from plugin
+ networkDropdownOpen,
+ sidebarOpen,
+ isLoading,
+ loadingMessage,
+ noActiveNotices,
+ isInitialized,
+ isUnlocked: state.metamask.isUnlocked,
+ selectedAddress: state.metamask.selectedAddress,
+ currentView: state.appState.currentView,
+ activeAddress: state.appState.activeAddress,
+ transForward: state.appState.transForward,
+ isMascara: state.metamask.isMascara,
+ isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
+ isPopup: state.metamask.isPopup,
+ seedWords: state.metamask.seedWords,
+ unapprovedTxs,
+ unapprovedMsgs: state.metamask.unapprovedMsgs,
+ unapprovedMsgCount,
+ unapprovedPersonalMsgCount,
+ unapprovedTypedMessagesCount,
+ menuOpen: state.appState.menuOpen,
+ network: state.metamask.network,
+ provider: state.metamask.provider,
+ forgottenPassword: state.appState.forgottenPassword,
+ lastUnreadNotice,
+ lostAccounts,
+ frequentRpcList: state.metamask.frequentRpcList || [],
+ currentCurrency: state.metamask.currentCurrency,
+ isMouseUser: state.appState.isMouseUser,
+ betaUI: state.metamask.featureFlags.betaUI,
+ isRevealingSeedWords: state.metamask.isRevealingSeedWords,
+ Qr: state.appState.Qr,
+ welcomeScreenSeen: state.metamask.welcomeScreenSeen,
+
+ // state needed to get account dropdown temporarily rendering from app bar
+ identities,
+ selected,
+ keyrings,
}
+}
- return name
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ dispatch,
+ hideSidebar: () => dispatch(actions.hideSidebar()),
+ showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
+ hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
+ setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
+ toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
+ setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
+ }
}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(App)
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index a9120f9db..7927b80d5 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -1,13 +1,25 @@
const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('../../metamask-connect')
+const { compose } = require('recompose')
+const { withRouter } = require('react-router-dom')
const h = require('react-hyperscript')
const actions = require('../../actions')
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu')
const Identicon = require('../identicon')
const { formatBalance } = require('../../util')
-
-module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountMenu)
+const {
+ SETTINGS_ROUTE,
+ INFO_ROUTE,
+ NEW_ACCOUNT_ROUTE,
+ IMPORT_ACCOUNT_ROUTE,
+ DEFAULT_ROUTE,
+} = require('../../routes')
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(AccountMenu)
inherits(AccountMenu, Component)
function AccountMenu () { Component.call(this) }
@@ -19,7 +31,6 @@ function mapStateToProps (state) {
keyrings: state.metamask.keyrings,
identities: state.metamask.identities,
accounts: state.metamask.accounts,
-
}
}
@@ -42,11 +53,6 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.hideSidebar())
dispatch(actions.toggleAccountMenu())
},
- showNewAccountPage: (formToSelect) => {
- dispatch(actions.showNewAccountPage(formToSelect))
- dispatch(actions.hideSidebar())
- dispatch(actions.toggleAccountMenu())
- },
showInfoPage: () => {
dispatch(actions.showInfoPage())
dispatch(actions.hideSidebar())
@@ -59,10 +65,8 @@ AccountMenu.prototype.render = function () {
const {
isAccountMenuOpen,
toggleAccountMenu,
- showNewAccountPage,
lockMetamask,
- showConfigPage,
- showInfoPage,
+ history,
} = this.props
return h(Menu, { className: 'account-menu', isShowing: isAccountMenuOpen }, [
@@ -72,30 +76,45 @@ AccountMenu.prototype.render = function () {
}, [
this.props.t('myAccounts'),
h('button.account-menu__logout-button', {
- onClick: lockMetamask,
+ onClick: () => {
+ lockMetamask()
+ history.push(DEFAULT_ROUTE)
+ },
}, this.props.t('logout')),
]),
h(Divider),
h('div.account-menu__accounts', this.renderAccounts()),
h(Divider),
h(Item, {
- onClick: () => showNewAccountPage('CREATE'),
+ onClick: () => {
+ toggleAccountMenu()
+ history.push(NEW_ACCOUNT_ROUTE)
+ },
icon: h('img.account-menu__item-icon', { src: 'images/plus-btn-white.svg' }),
text: this.props.t('createAccount'),
}),
h(Item, {
- onClick: () => showNewAccountPage('IMPORT'),
+ onClick: () => {
+ toggleAccountMenu()
+ history.push(IMPORT_ACCOUNT_ROUTE)
+ },
icon: h('img.account-menu__item-icon', { src: 'images/import-account.svg' }),
text: this.props.t('importAccount'),
}),
h(Divider),
h(Item, {
- onClick: showInfoPage,
+ onClick: () => {
+ toggleAccountMenu()
+ history.push(INFO_ROUTE)
+ },
icon: h('img', { src: 'images/mm-info-icon.svg' }),
text: this.props.t('infoHelp'),
}),
h(Item, {
- onClick: showConfigPage,
+ onClick: () => {
+ toggleAccountMenu()
+ history.push(SETTINGS_ROUTE)
+ },
icon: h('img.account-menu__item-icon', { src: 'images/settings.svg' }),
text: this.props.t('settings'),
}),
diff --git a/ui/app/add-token.js b/ui/app/components/pages/add-token.js
index 472f3b736..b33373c19 100644
--- a/ui/app/add-token.js
+++ b/ui/app/components/pages/add-token.js
@@ -6,8 +6,8 @@ const connect = require('./metamask-connect')
const R = require('ramda')
const Fuse = require('fuse.js')
const contractMap = require('eth-contract-metadata')
-const TokenBalance = require('./components/token-balance')
-const Identicon = require('./components/identicon')
+const TokenBalance = require('../../components/token-balance')
+const Identicon = require('../../components/identicon')
const contractList = Object.entries(contractMap)
.map(([ _, tokenData]) => tokenData)
.filter(tokenData => Boolean(tokenData.erc20))
@@ -23,9 +23,10 @@ const fuse = new Fuse(contractList, {
{ name: 'symbol', weight: 0.5 },
],
})
-const actions = require('./actions')
+const actions = require('../../actions')
const ethUtil = require('ethereumjs-util')
-const { tokenInfoGetter } = require('./token-util')
+const { tokenInfoGetter } = require('../../token-util')
+const { DEFAULT_ROUTE } = require('../../routes')
const emptyAddr = '0x0000000000000000000000000000000000000000'
@@ -41,7 +42,6 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
- goHome: () => dispatch(actions.goHome()),
addTokens: tokens => dispatch(actions.addTokens(tokens)),
}
}
@@ -289,7 +289,7 @@ AddTokenScreen.prototype.renderConfirmation = function () {
selectedTokens,
} = this.state
- const { addTokens, goHome } = this.props
+ const { addTokens, history } = this.props
const customToken = {
address,
@@ -329,7 +329,7 @@ AddTokenScreen.prototype.renderConfirmation = function () {
onClick: () => this.setState({ isShowingConfirmation: false }),
}, this.props.t('back')),
h('button.btn-primary--lg', {
- onClick: () => addTokens(tokens).then(goHome),
+ onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
}, this.props.t('addTokens')),
]),
])
@@ -373,12 +373,12 @@ AddTokenScreen.prototype.render = function () {
isShowingConfirmation,
displayedTab,
} = this.state
- const { goHome } = this.props
+ const { history } = this.props
return h('div.add-token', [
h('div.add-token__header', [
h('div.add-token__header__cancel', {
- onClick: () => goHome(),
+ onClick: () => history.goBack(),
}, [
h('i.fa.fa-angle-left.fa-lg'),
h('span', this.props.t('cancel')),
@@ -404,14 +404,14 @@ AddTokenScreen.prototype.render = function () {
]),
]),
-//
+
isShowingConfirmation
? this.renderConfirmation()
: this.renderTabs(),
!isShowingConfirmation && h('div.add-token__buttons', [
h('button.btn-secondary--lg.add-token__cancel-button', {
- onClick: goHome,
+ onClick: () => history.goBack(),
}, this.props.t('cancel')),
h('button.btn-primary--lg.add-token__confirm-button', {
onClick: this.onNext,
diff --git a/ui/app/components/pages/authenticated.js b/ui/app/components/pages/authenticated.js
new file mode 100644
index 000000000..1f6b0be49
--- /dev/null
+++ b/ui/app/components/pages/authenticated.js
@@ -0,0 +1,34 @@
+const { connect } = require('react-redux')
+const PropTypes = require('prop-types')
+const { Redirect } = require('react-router-dom')
+const h = require('react-hyperscript')
+const MetamaskRoute = require('./metamask-route')
+const { UNLOCK_ROUTE, INITIALIZE_ROUTE } = require('../../routes')
+
+const Authenticated = props => {
+ const { isUnlocked, isInitialized } = props
+
+ switch (true) {
+ case isUnlocked && isInitialized:
+ return h(MetamaskRoute, { ...props })
+ case !isInitialized:
+ return h(Redirect, { to: { pathname: INITIALIZE_ROUTE } })
+ default:
+ return h(Redirect, { to: { pathname: UNLOCK_ROUTE } })
+ }
+}
+
+Authenticated.propTypes = {
+ isUnlocked: PropTypes.bool,
+ isInitialized: PropTypes.bool,
+}
+
+const mapStateToProps = state => {
+ const { metamask: { isUnlocked, isInitialized } } = state
+ return {
+ isUnlocked,
+ isInitialized,
+ }
+}
+
+module.exports = connect(mapStateToProps)(Authenticated)
diff --git a/ui/app/accounts/import/index.js b/ui/app/components/pages/create-account/import-account/index.js
index b09b50ef7..26353b670 100644
--- a/ui/app/accounts/import/index.js
+++ b/ui/app/components/pages/create-account/import-account/index.js
@@ -1,7 +1,7 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('../../metamask-connect')
+const connect = require('../../../../metamask-connect')
import Select from 'react-select'
// Subviews
diff --git a/ui/app/accounts/import/json.js b/ui/app/components/pages/create-account/import-account/json.js
index 02b64858d..2e858a8f0 100644
--- a/ui/app/accounts/import/json.js
+++ b/ui/app/components/pages/create-account/import-account/json.js
@@ -1,11 +1,12 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const connect = require('../../metamask-connect')
-const actions = require('../../actions')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
+const connect = require('../../../../metamask-connect')
+const actions = require('../../../../actions')
const FileInput = require('react-simple-file-input').default
-
-
+const { DEFAULT_ROUTE } = require('../../../../routes')
const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
class JsonImportSubview extends Component {
@@ -51,7 +52,7 @@ class JsonImportSubview extends Component {
h('div.new-account-create-form__buttons', {}, [
h('button.btn-secondary.new-account-create-form__button', {
- onClick: () => this.props.goHome(),
+ onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, [
this.props.t('cancel'),
]),
@@ -112,7 +113,8 @@ JsonImportSubview.propTypes = {
goHome: PropTypes.func,
displayWarning: PropTypes.func,
importNewJsonAccount: PropTypes.func,
- t: PropTypes.func,
+ history: PropTypes.object,
+ t: PropTypes.func,
}
const mapStateToProps = state => {
@@ -129,4 +131,7 @@ const mapDispatchToProps = dispatch => {
}
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(JsonImportSubview)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(JsonImportSubview)
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js
index bdf581b01..17a32c10f 100644
--- a/ui/app/accounts/import/private-key.js
+++ b/ui/app/components/pages/create-account/import-account/private-key.js
@@ -1,10 +1,16 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('../../metamask-connect')
-const actions = require('../../actions')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
+const connect = require('../../../../metamask-connect')
+const actions = require('../../../../actions')
+const { DEFAULT_ROUTE } = require('../../../../routes')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(PrivateKeyImportView)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(PrivateKeyImportView)
function mapStateToProps (state) {
return {
@@ -14,9 +20,8 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
- goHome: () => dispatch(actions.goHome()),
importNewAccount: (strategy, [ privateKey ]) => {
- dispatch(actions.importNewAccount(strategy, [ privateKey ]))
+ return dispatch(actions.importNewAccount(strategy, [ privateKey ]))
},
displayWarning: () => dispatch(actions.displayWarning(null)),
}
@@ -28,7 +33,7 @@ function PrivateKeyImportView () {
}
PrivateKeyImportView.prototype.render = function () {
- const { error, goHome } = this.props
+ const { error } = this.props
return (
h('div.new-account-import-form__private-key', [
@@ -48,7 +53,7 @@ PrivateKeyImportView.prototype.render = function () {
h('div.new-account-import-form__buttons', {}, [
h('button.btn-secondary--lg.new-account-create-form__button', {
- onClick: () => goHome(),
+ onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, [
this.props.t('cancel'),
]),
@@ -76,6 +81,8 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
PrivateKeyImportView.prototype.createNewKeychain = function () {
const input = document.getElementById('private-key-box')
const privateKey = input.value
+ const { importNewAccount, history } = this.props
- this.props.importNewAccount('Private Key', [ privateKey ])
+ importNewAccount('Private Key', [ privateKey ])
+ .then(() => history.push(DEFAULT_ROUTE))
}
diff --git a/ui/app/accounts/import/seed.js b/ui/app/components/pages/create-account/import-account/seed.js
index 5479a6be9..15dd98c73 100644
--- a/ui/app/accounts/import/seed.js
+++ b/ui/app/components/pages/create-account/import-account/seed.js
@@ -1,7 +1,7 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('../../metamask-connect')
+const connect = require('../../../../metamask-connect')
module.exports = connect(mapStateToProps)(SeedImportSubview)
diff --git a/ui/app/components/pages/create-account/index.js b/ui/app/components/pages/create-account/index.js
new file mode 100644
index 000000000..0962477d8
--- /dev/null
+++ b/ui/app/components/pages/create-account/index.js
@@ -0,0 +1,81 @@
+const Component = require('react').Component
+const { Switch, Route, matchPath } = require('react-router-dom')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+const actions = require('../../../actions')
+const { getCurrentViewContext } = require('../../../selectors')
+const classnames = require('classnames')
+const NewAccountCreateForm = require('./new-account')
+const NewAccountImportForm = require('./import-account')
+const { NEW_ACCOUNT_ROUTE, IMPORT_ACCOUNT_ROUTE } = require('../../../routes')
+
+class CreateAccountPage extends Component {
+ renderTabs () {
+ const { history, location } = this.props
+
+ return h('div.new-account__tabs', [
+ h('div.new-account__tabs__tab', {
+ className: classnames('new-account__tabs__tab', {
+ 'new-account__tabs__selected': matchPath(location.pathname, {
+ path: NEW_ACCOUNT_ROUTE, exact: true,
+ }),
+ }),
+ onClick: () => history.push(NEW_ACCOUNT_ROUTE),
+ }, 'Create'),
+
+ h('div.new-account__tabs__tab', {
+ className: classnames('new-account__tabs__tab', {
+ 'new-account__tabs__selected': matchPath(location.pathname, {
+ path: IMPORT_ACCOUNT_ROUTE, exact: true,
+ }),
+ }),
+ onClick: () => history.push(IMPORT_ACCOUNT_ROUTE),
+ }, 'Import'),
+ ])
+ }
+
+ render () {
+ return h('div.new-account', {}, [
+ h('div.new-account__header', [
+ h('div.new-account__title', 'New Account'),
+ this.renderTabs(),
+ ]),
+ h('div.new-account__form', [
+ h(Switch, [
+ h(Route, {
+ exact: true,
+ path: NEW_ACCOUNT_ROUTE,
+ component: NewAccountCreateForm,
+ }),
+ h(Route, {
+ exact: true,
+ path: IMPORT_ACCOUNT_ROUTE,
+ component: NewAccountImportForm,
+ }),
+ ]),
+ ]),
+ ])
+ }
+}
+
+CreateAccountPage.propTypes = {
+ location: PropTypes.object,
+ history: PropTypes.object,
+}
+
+const mapStateToProps = state => ({
+ displayedForm: getCurrentViewContext(state),
+})
+
+const mapDispatchToProps = dispatch => ({
+ displayForm: form => dispatch(actions.setNewAccountForm(form)),
+ showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
+ showExportPrivateKeyModal: () => {
+ dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
+ },
+ hideModal: () => dispatch(actions.hideModal()),
+ saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)),
+})
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(CreateAccountPage)
diff --git a/ui/app/accounts/new-account/create-form.js b/ui/app/components/pages/create-account/new-account.js
index 69fbf1e35..dfe2a2ab1 100644
--- a/ui/app/accounts/new-account/create-form.js
+++ b/ui/app/components/pages/create-account/new-account.js
@@ -1,8 +1,9 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const connect = require('../../metamask-connect')
-const actions = require('../../actions')
+const connect = require('../../../metamask-connect')
+const actions = require('../../../actions')
+const { DEFAULT_ROUTE } = require('../../../routes')
class NewAccountCreateForm extends Component {
constructor (props) {
@@ -19,7 +20,7 @@ class NewAccountCreateForm extends Component {
render () {
const { newAccountName, defaultAccountName } = this.state
-
+ const { history, createAccount } = this.props
return h('div.new-account-create-form', [
@@ -38,13 +39,16 @@ class NewAccountCreateForm extends Component {
h('div.new-account-create-form__buttons', {}, [
h('button.btn-secondary--lg.new-account-create-form__button', {
- onClick: () => this.props.goHome(),
+ onClick: () => history.push(DEFAULT_ROUTE),
}, [
this.props.t('cancel'),
]),
h('button.btn-primary--lg.new-account-create-form__button', {
- onClick: () => this.props.createAccount(newAccountName || defaultAccountName),
+ onClick: () => {
+ createAccount(newAccountName || defaultAccountName)
+ .then(() => history.push(DEFAULT_ROUTE))
+ },
}, [
this.props.t('create'),
]),
@@ -59,8 +63,8 @@ NewAccountCreateForm.propTypes = {
hideModal: PropTypes.func,
showImportPage: PropTypes.func,
createAccount: PropTypes.func,
- goHome: PropTypes.func,
numberOfExistingAccounts: PropTypes.number,
+ history: PropTypes.object,
t: PropTypes.func,
}
@@ -77,23 +81,17 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => {
return {
- toCoinbase: (address) => {
- dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
- },
- hideModal: () => {
- dispatch(actions.hideModal())
- },
- createAccount: (newAccountName) => {
- dispatch(actions.addNewAccount())
- .then((newAccountAddress) => {
+ toCoinbase: address => dispatch(actions.buyEth({ network: '1', address, amount: 0 })),
+ hideModal: () => dispatch(actions.hideModal()),
+ createAccount: newAccountName => {
+ return dispatch(actions.addNewAccount())
+ .then(newAccountAddress => {
if (newAccountName) {
dispatch(actions.saveAccountLabel(newAccountAddress, newAccountName))
}
- dispatch(actions.goHome())
})
},
showImportPage: () => dispatch(actions.showImportPage()),
- goHome: () => dispatch(actions.goHome()),
}
}
diff --git a/ui/app/components/pages/initialized.js b/ui/app/components/pages/initialized.js
new file mode 100644
index 000000000..3adf67b28
--- /dev/null
+++ b/ui/app/components/pages/initialized.js
@@ -0,0 +1,25 @@
+const { connect } = require('react-redux')
+const PropTypes = require('prop-types')
+const { Redirect } = require('react-router-dom')
+const h = require('react-hyperscript')
+const { INITIALIZE_ROUTE } = require('../../routes')
+const MetamaskRoute = require('./metamask-route')
+
+const Initialized = props => {
+ return props.isInitialized
+ ? h(MetamaskRoute, { ...props })
+ : h(Redirect, { to: { pathname: INITIALIZE_ROUTE } })
+}
+
+Initialized.propTypes = {
+ isInitialized: PropTypes.bool,
+}
+
+const mapStateToProps = state => {
+ const { metamask: { isInitialized } } = state
+ return {
+ isInitialized,
+ }
+}
+
+module.exports = connect(mapStateToProps)(Initialized)
diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js
new file mode 100644
index 000000000..749da9758
--- /dev/null
+++ b/ui/app/components/pages/keychains/restore-vault.js
@@ -0,0 +1,170 @@
+const { withRouter } = require('react-router-dom')
+const PropTypes = require('prop-types')
+const { compose } = require('recompose')
+const PersistentForm = require('../../../../lib/persistent-form')
+const { connect } = require('react-redux')
+const h = require('react-hyperscript')
+const { createNewVaultAndRestore } = require('../../../actions')
+const { DEFAULT_ROUTE } = require('../../../routes')
+
+class RestoreVaultPage extends PersistentForm {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ error: null,
+ }
+ }
+
+ createOnEnter (event) {
+ if (event.key === 'Enter') {
+ this.createNewVaultAndRestore()
+ }
+ }
+
+ createNewVaultAndRestore () {
+ this.setState({ error: null })
+
+ // check password
+ var passwordBox = document.getElementById('password-box')
+ var password = passwordBox.value
+ var passwordConfirmBox = document.getElementById('password-box-confirm')
+ var passwordConfirm = passwordConfirmBox.value
+
+ if (password.length < 8) {
+ this.setState({ error: 'Password not long enough' })
+ return
+ }
+
+ if (password !== passwordConfirm) {
+ this.setState({ error: 'Passwords don\'t match' })
+ return
+ }
+
+ // check seed
+ var seedBox = document.querySelector('textarea.twelve-word-phrase')
+ var seed = seedBox.value.trim()
+ if (seed.split(' ').length !== 12) {
+ this.setState({ error: 'Seed phrases are 12 words long' })
+ return
+ }
+
+ // submit
+ this.props.createNewVaultAndRestore(password, seed)
+ .then(() => history.push(DEFAULT_ROUTE))
+ .catch(({ message }) => {
+ this.setState({ error: message })
+ log.error(message)
+ })
+ }
+
+ render () {
+ const { error } = this.state
+ const { history } = this.props
+ this.persistentFormParentId = 'restore-vault-form'
+
+ return (
+ h('.initialize-screen.flex-column.flex-center.flex-grow', [
+
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ padding: 6,
+ },
+ }, [
+ 'Restore Vault',
+ ]),
+
+ // wallet seed entry
+ h('h3', 'Wallet Seed'),
+ h('textarea.twelve-word-phrase.letter-spacey', {
+ dataset: {
+ persistentFormId: 'wallet-seed',
+ },
+ placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
+ }),
+
+ // password
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'New Password (min 8 chars)',
+ dataset: {
+ persistentFormId: 'password',
+ },
+ style: {
+ width: 260,
+ marginTop: 12,
+ },
+ }),
+
+ // confirm password
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box-confirm',
+ placeholder: 'Confirm Password',
+ onKeyPress: this.createOnEnter.bind(this),
+ dataset: {
+ persistentFormId: 'password-confirmation',
+ },
+ style: {
+ width: 260,
+ marginTop: 16,
+ },
+ }),
+
+ error && (
+ h('span.error.in-progress-notification', error)
+ ),
+
+ // submit
+ h('.flex-row.flex-space-between', {
+ style: {
+ marginTop: 30,
+ width: '50%',
+ },
+ }, [
+
+ // cancel
+ h('button.primary', { onClick: () => history.goBack() }, 'CANCEL'),
+
+ // submit
+ h('button.primary', {
+ onClick: this.createNewVaultAndRestore.bind(this),
+ }, 'OK'),
+
+ ]),
+ ])
+ )
+ }
+}
+
+RestoreVaultPage.propTypes = {
+ history: PropTypes.object,
+}
+
+const mapStateToProps = state => {
+ const { appState: { warning, forgottenPassword } } = state
+
+ return {
+ warning,
+ forgottenPassword,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ createNewVaultAndRestore: (password, seed) => {
+ return dispatch(createNewVaultAndRestore(password, seed))
+ },
+ }
+}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(RestoreVaultPage)
diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js
new file mode 100644
index 000000000..029eb7d8e
--- /dev/null
+++ b/ui/app/components/pages/keychains/reveal-seed.js
@@ -0,0 +1,195 @@
+const { Component } = require('react')
+const { connect } = require('react-redux')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const { exportAsFile } = require('../../../util')
+const { requestRevealSeed, confirmSeedWords } = require('../../../actions')
+const { DEFAULT_ROUTE } = require('../../../routes')
+
+class RevealSeedPage extends Component {
+ componentDidMount () {
+ const passwordBox = document.getElementById('password-box')
+ if (passwordBox) {
+ passwordBox.focus()
+ }
+ }
+
+ checkConfirmation (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.revealSeedWords()
+ }
+ }
+
+ revealSeedWords () {
+ const password = document.getElementById('password-box').value
+ this.props.requestRevealSeed(password)
+ }
+
+ renderSeed () {
+ const { seedWords, confirmSeedWords, history } = this.props
+
+ return (
+ h('.initialize-screen.flex-column.flex-center.flex-grow', [
+
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginTop: 36,
+ marginBottom: 8,
+ width: '100%',
+ fontSize: '20px',
+ padding: 6,
+ },
+ }, [
+ 'Vault Created',
+ ]),
+
+ h('div', {
+ style: {
+ fontSize: '1em',
+ marginTop: '10px',
+ textAlign: 'center',
+ },
+ }, [
+ h('span.error', 'These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret.'),
+ ]),
+
+ h('textarea.twelve-word-phrase', {
+ readOnly: true,
+ value: seedWords,
+ }),
+
+ h('button.primary', {
+ onClick: () => confirmSeedWords().then(() => history.push(DEFAULT_ROUTE)),
+ style: {
+ margin: '24px',
+ fontSize: '0.9em',
+ marginBottom: '10px',
+ },
+ }, 'I\'ve copied it somewhere safe'),
+
+ h('button.primary', {
+ onClick: () => exportAsFile(`MetaMask Seed Words`, seedWords),
+ style: {
+ margin: '10px',
+ fontSize: '0.9em',
+ },
+ }, 'Save Seed Words As File'),
+ ])
+ )
+ }
+
+ renderConfirmation () {
+ const { history, warning, inProgress } = this.props
+
+ return (
+ h('.initialize-screen.flex-column.flex-center.flex-grow', {
+ style: { maxWidth: '420px' },
+ }, [
+
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ padding: 6,
+ },
+ }, [
+ 'Reveal Seed Words',
+ ]),
+
+ h('.div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ padding: '20px',
+ justifyContent: 'center',
+ },
+ }, [
+
+ h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'),
+
+ // confirmation
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'Enter your password to confirm',
+ onKeyPress: this.checkConfirmation.bind(this),
+ style: {
+ width: 260,
+ marginTop: '12px',
+ },
+ }),
+
+ h('.flex-row.flex-start', {
+ style: {
+ marginTop: 30,
+ width: '50%',
+ },
+ }, [
+ // cancel
+ h('button.primary', {
+ onClick: () => history.goBack(),
+ }, 'CANCEL'),
+
+ // submit
+ h('button.primary', {
+ style: { marginLeft: '10px' },
+ onClick: this.revealSeedWords.bind(this),
+ }, 'OK'),
+
+ ]),
+
+ warning && (
+ h('span.error', {
+ style: {
+ margin: '20px',
+ },
+ }, warning.split('-'))
+ ),
+
+ inProgress && (
+ h('span.in-progress-notification', 'Generating Seed...')
+ ),
+ ]),
+ ])
+ )
+ }
+
+ render () {
+ return this.props.seedWords
+ ? this.renderSeed()
+ : this.renderConfirmation()
+ }
+}
+
+RevealSeedPage.propTypes = {
+ requestRevealSeed: PropTypes.func,
+ confirmSeedWords: PropTypes.func,
+ seedWords: PropTypes.string,
+ inProgress: PropTypes.bool,
+ history: PropTypes.object,
+ warning: PropTypes.string,
+}
+
+const mapStateToProps = state => {
+ const { appState: { warning }, metamask: { seedWords } } = state
+
+ return {
+ warning,
+ seedWords,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ requestRevealSeed: password => dispatch(requestRevealSeed(password)),
+ confirmSeedWords: () => dispatch(confirmSeedWords()),
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(RevealSeedPage)
diff --git a/ui/app/components/pages/metamask-route.js b/ui/app/components/pages/metamask-route.js
new file mode 100644
index 000000000..23c5b5199
--- /dev/null
+++ b/ui/app/components/pages/metamask-route.js
@@ -0,0 +1,28 @@
+const { connect } = require('react-redux')
+const PropTypes = require('prop-types')
+const { Route } = require('react-router-dom')
+const h = require('react-hyperscript')
+
+const MetamaskRoute = ({ component, mascaraComponent, isMascara, ...props }) => {
+ return (
+ h(Route, {
+ ...props,
+ component: isMascara && mascaraComponent ? mascaraComponent : component,
+ })
+ )
+}
+
+MetamaskRoute.propTypes = {
+ component: PropTypes.func,
+ mascaraComponent: PropTypes.func,
+ isMascara: PropTypes.bool,
+}
+
+const mapStateToProps = state => {
+ const { metamask: { isMascara } } = state
+ return {
+ isMascara,
+ }
+}
+
+module.exports = connect(mapStateToProps)(MetamaskRoute)
diff --git a/ui/app/components/pages/notice.js b/ui/app/components/pages/notice.js
new file mode 100644
index 000000000..2329a9147
--- /dev/null
+++ b/ui/app/components/pages/notice.js
@@ -0,0 +1,203 @@
+const { Component } = require('react')
+const h = require('react-hyperscript')
+const { connect } = require('react-redux')
+const PropTypes = require('prop-types')
+const ReactMarkdown = require('react-markdown')
+const linker = require('extension-link-enabler')
+const generateLostAccountsNotice = require('../../../lib/lost-accounts-notice')
+const findDOMNode = require('react-dom').findDOMNode
+const actions = require('../../actions')
+const { DEFAULT_ROUTE } = require('../../routes')
+
+class Notice extends Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ disclaimerDisabled: true,
+ }
+ }
+
+ componentWillMount () {
+ if (!this.props.notice) {
+ this.props.history.push(DEFAULT_ROUTE)
+ }
+ }
+
+ componentDidMount () {
+ // eslint-disable-next-line react/no-find-dom-node
+ var node = findDOMNode(this)
+ linker.setupListener(node)
+ if (document.getElementsByClassName('notice-box')[0].clientHeight < 310) {
+ this.setState({ disclaimerDisabled: false })
+ }
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (!nextProps.notice) {
+ this.props.history.push(DEFAULT_ROUTE)
+ }
+ }
+
+ componentWillUnmount () {
+ // eslint-disable-next-line react/no-find-dom-node
+ var node = findDOMNode(this)
+ linker.teardownListener(node)
+ }
+
+ handleAccept () {
+ this.setState({ disclaimerDisabled: true })
+ this.props.onConfirm()
+ }
+
+ render () {
+ const { notice = {} } = this.props
+ const { title, date, body } = notice
+ const { disclaimerDisabled } = this.state
+
+ return (
+ h('.flex-column.flex-center.flex-grow', {
+ style: {
+ width: '100%',
+ },
+ }, [
+ h('h3.flex-center.text-transform-uppercase.terms-header', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ width: '100%',
+ fontSize: '20px',
+ textAlign: 'center',
+ padding: 6,
+ },
+ }, [
+ title,
+ ]),
+
+ h('h5.flex-center.text-transform-uppercase.terms-header', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ textAlign: 'center',
+ padding: 6,
+ },
+ }, [
+ date,
+ ]),
+
+ h('style', `
+
+ .markdown {
+ overflow-x: hidden;
+ }
+
+ .markdown h1, .markdown h2, .markdown h3 {
+ margin: 10px 0;
+ font-weight: bold;
+ }
+
+ .markdown strong {
+ font-weight: bold;
+ }
+ .markdown em {
+ font-style: italic;
+ }
+
+ .markdown p {
+ margin: 10px 0;
+ }
+
+ .markdown a {
+ color: #df6b0e;
+ }
+
+ `),
+
+ h('div.markdown', {
+ onScroll: (e) => {
+ var object = e.currentTarget
+ if (object.offsetHeight + object.scrollTop + 100 >= object.scrollHeight) {
+ this.setState({ disclaimerDisabled: false })
+ }
+ },
+ style: {
+ background: 'rgb(235, 235, 235)',
+ height: '310px',
+ padding: '6px',
+ width: '90%',
+ overflowY: 'scroll',
+ scroll: 'auto',
+ },
+ }, [
+ h(ReactMarkdown, {
+ className: 'notice-box',
+ source: body,
+ skipHtml: true,
+ }),
+ ]),
+
+ h('button.primary', {
+ disabled: disclaimerDisabled,
+ onClick: () => this.handleAccept(),
+ style: {
+ marginTop: '18px',
+ },
+ }, 'Accept'),
+ ])
+ )
+ }
+
+}
+
+const mapStateToProps = state => {
+ const { metamask } = state
+ const { noActiveNotices, lastUnreadNotice, lostAccounts } = metamask
+
+ return {
+ noActiveNotices,
+ lastUnreadNotice,
+ lostAccounts,
+ }
+}
+
+Notice.propTypes = {
+ notice: PropTypes.object,
+ onConfirm: PropTypes.func,
+ history: PropTypes.object,
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ markNoticeRead: lastUnreadNotice => dispatch(actions.markNoticeRead(lastUnreadNotice)),
+ markAccountsFound: () => dispatch(actions.markAccountsFound()),
+ }
+}
+
+const mergeProps = (stateProps, dispatchProps, ownProps) => {
+ const { noActiveNotices, lastUnreadNotice, lostAccounts } = stateProps
+ const { markNoticeRead, markAccountsFound } = dispatchProps
+
+ let notice
+ let onConfirm
+
+ if (!noActiveNotices) {
+ notice = lastUnreadNotice
+ onConfirm = () => markNoticeRead(lastUnreadNotice)
+ } else if (lostAccounts && lostAccounts.length > 0) {
+ notice = generateLostAccountsNotice(lostAccounts)
+ onConfirm = () => markAccountsFound()
+ }
+
+ return {
+ ...stateProps,
+ ...dispatchProps,
+ ...ownProps,
+ notice,
+ onConfirm,
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Notice)
diff --git a/ui/app/components/pages/settings/index.js b/ui/app/components/pages/settings/index.js
new file mode 100644
index 000000000..384ae4b41
--- /dev/null
+++ b/ui/app/components/pages/settings/index.js
@@ -0,0 +1,59 @@
+const { Component } = require('react')
+const { Switch, Route, matchPath } = require('react-router-dom')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const TabBar = require('../../tab-bar')
+const Settings = require('./settings')
+const Info = require('./info')
+const { DEFAULT_ROUTE, SETTINGS_ROUTE, INFO_ROUTE } = require('../../../routes')
+
+class Config extends Component {
+ renderTabs () {
+ const { history, location } = this.props
+
+ return h('div.settings__tabs', [
+ h(TabBar, {
+ tabs: [
+ { content: 'Settings', key: SETTINGS_ROUTE },
+ { content: 'Info', key: INFO_ROUTE },
+ ],
+ isActive: key => matchPath(location.pathname, { path: key, exact: true }),
+ onSelect: key => history.push(key),
+ }),
+ ])
+ }
+
+ render () {
+ const { history } = this.props
+
+ return (
+ h('.main-container.settings', {}, [
+ h('.settings__header', [
+ h('div.settings__close-button', {
+ onClick: () => history.push(DEFAULT_ROUTE),
+ }),
+ this.renderTabs(),
+ ]),
+ h(Switch, [
+ h(Route, {
+ exact: true,
+ path: INFO_ROUTE,
+ component: Info,
+ }),
+ h(Route, {
+ exact: true,
+ path: SETTINGS_ROUTE,
+ component: Settings,
+ }),
+ ]),
+ ])
+ )
+ }
+}
+
+Config.propTypes = {
+ location: PropTypes.object,
+ history: PropTypes.object,
+}
+
+module.exports = Config
diff --git a/ui/app/components/pages/settings/info.js b/ui/app/components/pages/settings/info.js
new file mode 100644
index 000000000..adb2fdfcd
--- /dev/null
+++ b/ui/app/components/pages/settings/info.js
@@ -0,0 +1,109 @@
+const { Component } = require('react')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const connect = require('../../../metamask-connect')
+
+class Info extends Component {
+ renderLogo () {
+ return (
+ h('div.settings__info-logo-wrapper', [
+ h('img.settings__info-logo', { src: 'images/info-logo.png' }),
+ ])
+ )
+ }
+
+ renderInfoLinks () {
+ return (
+ h('div.settings__content-item.settings__content-item--without-height', [
+ h('div.settings__info-link-header', this.props.t('links')),
+ h('div.settings__info-link-item', [
+ h('a', {
+ href: 'https://metamask.io/privacy.html',
+ target: '_blank',
+ }, [
+ h('span.settings__info-link', this.props.t('privacyMsg')),
+ ]),
+ ]),
+ h('div.settings__info-link-item', [
+ h('a', {
+ href: 'https://metamask.io/terms.html',
+ target: '_blank',
+ }, [
+ h('span.settings__info-link', this.props.t('terms')),
+ ]),
+ ]),
+ h('div.settings__info-link-item', [
+ h('a', {
+ href: 'https://metamask.io/attributions.html',
+ target: '_blank',
+ }, [
+ h('span.settings__info-link', this.props.t('attributions')),
+ ]),
+ ]),
+ h('hr.settings__info-separator'),
+ h('div.settings__info-link-item', [
+ h('a', {
+ href: 'https://support.metamask.io',
+ target: '_blank',
+ }, [
+ h('span.settings__info-link', this.props.t('supportCenter')),
+ ]),
+ ]),
+ h('div.settings__info-link-item', [
+ h('a', {
+ href: 'https://metamask.io/',
+ target: '_blank',
+ }, [
+ h('span.settings__info-link', this.props.t('visitWebSite')),
+ ]),
+ ]),
+ h('div.settings__info-link-item', [
+ h('a', {
+ target: '_blank',
+ href: 'mailto:help@metamask.io?subject=Feedback',
+ }, [
+ h('span.settings__info-link', this.props.t('emailUs')),
+ ]),
+ ]),
+ ])
+ )
+ }
+
+ render () {
+ return (
+ h('div.settings__content', [
+ h('div.settings__content-row', [
+ h('div.settings__content-item.settings__content-item--without-height', [
+ this.renderLogo(),
+ h('div.settings__info-item', [
+ h('div.settings__info-version-header', 'MetaMask Version'),
+ h('div.settings__info-version-number', '4.0.0'),
+ ]),
+ h('div.settings__info-item', [
+ h(
+ 'div.settings__info-about',
+ this.props.t('builtInCalifornia')
+ ),
+ ]),
+ ]),
+ this.renderInfoLinks(),
+ ]),
+ ])
+ )
+ }
+}
+
+Info.propTypes = {
+ tab: PropTypes.string,
+ metamask: PropTypes.object,
+ setCurrentCurrency: PropTypes.func,
+ setRpcTarget: PropTypes.func,
+ displayWarning: PropTypes.func,
+ revealSeedConfirmation: PropTypes.func,
+ warning: PropTypes.string,
+ location: PropTypes.object,
+ history: PropTypes.object,
+ t: PropTypes.func,
+}
+
+module.exports = connect()(Info)
diff --git a/ui/app/settings.js b/ui/app/components/pages/settings/settings.js
index 8764dcafe..7b3704c4d 100644
--- a/ui/app/settings.js
+++ b/ui/app/components/pages/settings/settings.js
@@ -1,16 +1,18 @@
const { Component } = require('react')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const connect = require('./metamask-connect')
-const actions = require('./actions')
-const infuraCurrencies = require('./infura-conversion.json')
+const connect = require('../../../metamask-connect')
+const actions = require('../../../actions')
+const infuraCurrencies = require('../../../infura-conversion.json')
const validUrl = require('valid-url')
-const { exportAsFile } = require('./util')
-const TabBar = require('./components/tab-bar')
-const SimpleDropdown = require('./components/dropdowns/simple-dropdown')
+const { exportAsFile } = require('../../../util')
+const SimpleDropdown = require('../../dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button')
-const locales = require('../../app/_locales/index.json')
-const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
+const { REVEAL_SEED_ROUTE } = require('../../../routes')
+const locales = require('../../../../../app/_locales/index.json')
+const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/config').enums
const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
@@ -40,30 +42,11 @@ class Settings extends Component {
constructor (props) {
super(props)
- const { tab } = props
- const activeTab = tab === 'info' ? 'info' : 'settings'
-
this.state = {
- activeTab,
newRpc: '',
}
}
- renderTabs () {
- const { activeTab } = this.state
-
- return h('div.settings__tabs', [
- h(TabBar, {
- tabs: [
- { content: this.props.t('settings'), key: 'settings' },
- { content: this.props.t('info'), key: 'info' },
- ],
- defaultTab: activeTab,
- tabSelected: key => this.setState({ activeTab: key }),
- }),
- ])
- }
-
renderBlockieOptIn () {
const { metamask: { useBlockie }, setUseBlockie } = this.props
@@ -252,7 +235,7 @@ class Settings extends Component {
}
renderSeedWords () {
- const { revealSeedConfirmation } = this.props
+ const { history } = this.props
return (
h('div.settings__content-row', [
@@ -260,9 +243,9 @@ class Settings extends Component {
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('button.btn-primary--lg.settings__button--red', {
- onClick (event) {
+ onClick: event => {
event.preventDefault()
- revealSeedConfirmation()
+ history.push(REVEAL_SEED_ROUTE)
},
}, this.props.t('revealSeedWords')),
]),
@@ -309,7 +292,7 @@ class Settings extends Component {
])
}
- renderSettingsContent () {
+ render () {
const { warning, isMascara } = this.props
return (
@@ -327,120 +310,9 @@ class Settings extends Component {
])
)
}
-
- renderLogo () {
- return (
- h('div.settings__info-logo-wrapper', [
- h('img.settings__info-logo', { src: 'images/info-logo.png' }),
- ])
- )
- }
-
- renderInfoLinks () {
- return (
- h('div.settings__content-item.settings__content-item--without-height', [
- h('div.settings__info-link-header', this.props.t('links')),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/privacy.html',
- target: '_blank',
- }, [
- h('span.settings__info-link', this.props.t('privacyMsg')),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/terms.html',
- target: '_blank',
- }, [
- h('span.settings__info-link', this.props.t('terms')),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/attributions.html',
- target: '_blank',
- }, [
- h('span.settings__info-link', this.props.t('attributions')),
- ]),
- ]),
- h('hr.settings__info-separator'),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://support.metamask.io',
- target: '_blank',
- }, [
- h('span.settings__info-link', this.props.t('supportCenter')),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/',
- target: '_blank',
- }, [
- h('span.settings__info-link', this.props.t('visitWebSite')),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- target: '_blank',
- href: 'mailto:help@metamask.io?subject=Feedback',
- }, [
- h('span.settings__info-link', this.props.t('emailUs')),
- ]),
- ]),
- ])
- )
- }
-
- renderInfoContent () {
- const version = global.platform.getVersion()
-
- return (
- h('div.settings__content', [
- h('div.settings__content-row', [
- h('div.settings__content-item.settings__content-item--without-height', [
- this.renderLogo(),
- h('div.settings__info-item', [
- h('div.settings__info-version-header', 'MetaMask Version'),
- h('div.settings__info-version-number', `${version}`),
- ]),
- h('div.settings__info-item', [
- h(
- 'div.settings__info-about',
- this.props.t('builtInCalifornia')
- ),
- ]),
- ]),
- this.renderInfoLinks(),
- ]),
- ])
- )
- }
-
- render () {
- const { goHome } = this.props
- const { activeTab } = this.state
-
- return (
- h('.main-container.settings', {}, [
- h('.settings__header', [
- h('div.settings__close-button', {
- onClick: goHome,
- }),
- this.renderTabs(),
- ]),
-
- activeTab === 'settings'
- ? this.renderSettingsContent()
- : this.renderInfoContent(),
- ])
- )
- }
}
Settings.propTypes = {
- tab: PropTypes.string,
metamask: PropTypes.object,
setUseBlockie: PropTypes.func,
setCurrentCurrency: PropTypes.func,
@@ -450,7 +322,7 @@ Settings.propTypes = {
setFeatureFlagToBeta: PropTypes.func,
showResetAccountConfirmationModal: PropTypes.func,
warning: PropTypes.string,
- goHome: PropTypes.func,
+ history: PropTypes.object,
isMascara: PropTypes.bool,
updateCurrentLocale: PropTypes.func,
currentLocale: PropTypes.string,
@@ -468,7 +340,6 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => {
return {
- goHome: () => dispatch(actions.goHome()),
setCurrentCurrency: currency => dispatch(actions.setCurrentCurrency(currency)),
setRpcTarget: newRpc => dispatch(actions.setRpcTarget(newRpc)),
displayWarning: warning => dispatch(actions.displayWarning(warning)),
@@ -485,4 +356,7 @@ const mapDispatchToProps = dispatch => {
}
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(Settings)
diff --git a/ui/app/components/pages/signature-request.js b/ui/app/components/pages/signature-request.js
new file mode 100644
index 000000000..e9271fce1
--- /dev/null
+++ b/ui/app/components/pages/signature-request.js
@@ -0,0 +1,284 @@
+const { Component } = require('react')
+const h = require('react-hyperscript')
+const PropTypes = require('prop-types')
+const Identicon = require('../identicon')
+const { connect } = require('react-redux')
+const ethUtil = require('ethereumjs-util')
+const classnames = require('classnames')
+
+const AccountDropdownMini = require('../dropdowns/account-dropdown-mini')
+
+const t = require('../../../i18n')
+const { conversionUtil } = require('../../conversion-util')
+const { DEFAULT_ROUTE } = require('../../routes')
+
+const {
+ getSelectedAccount,
+ getCurrentAccountWithSendEtherInfo,
+ getSelectedAddress,
+ accountsWithSendEtherInfoSelector,
+ conversionRateSelector,
+} = require('../../selectors.js')
+
+class SignatureRequest extends Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ selectedAccount: props.selectedAccount,
+ accountDropdownOpen: false,
+ }
+ }
+
+ componentWillMount () {
+ const {
+ unapprovedMsgCount = 0,
+ unapprovedPersonalMsgCount = 0,
+ unapprovedTypedMessagesCount = 0,
+ } = this.props
+
+ if (unapprovedMsgCount + unapprovedPersonalMsgCount + unapprovedTypedMessagesCount < 1) {
+ this.props.history.push(DEFAULT_ROUTE)
+ }
+ }
+
+ renderHeader () {
+ return h('div.request-signature__header', [
+
+ h('div.request-signature__header-background'),
+
+ h('div.request-signature__header__text', t('sigRequest')),
+
+ h('div.request-signature__header__tip-container', [
+ h('div.request-signature__header__tip'),
+ ]),
+
+ ])
+ }
+
+ renderAccountDropdown () {
+ const {
+ selectedAccount,
+ accountDropdownOpen,
+ } = this.state
+
+ const { accounts } = this.props
+
+ return h('div.request-signature__account', [
+
+ h('div.request-signature__account-text', [t('account') + ':']),
+
+ h(AccountDropdownMini, {
+ selectedAccount,
+ accounts,
+ onSelect: selectedAccount => this.setState({ selectedAccount }),
+ dropdownOpen: accountDropdownOpen,
+ openDropdown: () => this.setState({ accountDropdownOpen: true }),
+ closeDropdown: () => this.setState({ accountDropdownOpen: false }),
+ }),
+
+ ])
+ }
+
+ renderBalance () {
+ const { balance, conversionRate } = this.props
+
+ const balanceInEther = conversionUtil(balance, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ fromDenomination: 'WEI',
+ numberOfDecimals: 6,
+ conversionRate,
+ })
+
+ return h('div.request-signature__balance', [
+
+ h('div.request-signature__balance-text', [t('balance')]),
+
+ h('div.request-signature__balance-value', `${balanceInEther} ETH`),
+
+ ])
+ }
+
+ renderAccountInfo () {
+ return h('div.request-signature__account-info', [
+
+ this.renderAccountDropdown(),
+
+ this.renderRequestIcon(),
+
+ this.renderBalance(),
+
+ ])
+ }
+
+ renderRequestIcon () {
+ const { requesterAddress } = this.props
+
+ return h('div.request-signature__request-icon', [
+ h(Identicon, {
+ diameter: 40,
+ address: requesterAddress,
+ }),
+ ])
+ }
+
+ renderRequestInfo () {
+ return h('div.request-signature__request-info', [
+
+ h('div.request-signature__headline', [
+ t('yourSigRequested'),
+ ]),
+
+ ])
+ }
+
+ msgHexToText (hex) {
+ try {
+ const stripped = ethUtil.stripHexPrefix(hex)
+ const buff = Buffer.from(stripped, 'hex')
+ return buff.toString('utf8')
+ } catch (e) {
+ return hex
+ }
+ }
+
+ renderBody () {
+ let rows
+ let notice = t('youSign') + ':'
+
+ const { txData } = this.props
+ const { type, msgParams: { data } } = txData
+
+ if (type === 'personal_sign') {
+ rows = [{ name: t('message'), value: this.msgHexToText(data) }]
+ } else if (type === 'eth_signTypedData') {
+ rows = data
+ } else if (type === 'eth_sign') {
+ rows = [{ name: t('message'), value: data }]
+ notice = t('signNotice')
+ }
+
+ return h('div.request-signature__body', {}, [
+
+ this.renderAccountInfo(),
+
+ this.renderRequestInfo(),
+
+ h('div.request-signature__notice', {
+ className: classnames({
+ 'request-signature__notice': type === 'personal_sign' || type === 'eth_signTypedData',
+ 'request-signature__warning': type === 'eth_sign',
+ }),
+ }, [notice]),
+
+ h('div.request-signature__rows', [
+
+ ...rows.map(({ name, value }) => {
+ return h('div.request-signature__row', [
+ h('div.request-signature__row-title', [`${name}:`]),
+ h('div.request-signature__row-value', value),
+ ])
+ }),
+
+ ]),
+
+ ])
+ }
+
+ renderFooter () {
+ const {
+ txData = {},
+ signPersonalMessage,
+ signTypedMessage,
+ cancelPersonalMessage,
+ cancelTypedMessage,
+ signMessage,
+ cancelMessage,
+ history,
+ } = this.props
+
+ const { type } = txData
+
+ let cancel = () => Promise.resolve()
+ let sign = () => Promise.resolve()
+ const { msgParams: params = {}, id } = txData
+ params.id = id
+ params.metamaskId = id
+
+ switch (type) {
+ case 'personal_sign':
+ cancel = () => cancelPersonalMessage(params)
+ sign = () => signPersonalMessage(params)
+ break
+ case 'eth_signTypedData':
+ cancel = () => cancelTypedMessage(params)
+ sign = () => signTypedMessage(params)
+ break
+ case 'eth_sign':
+ cancel = () => cancelMessage(params)
+ sign = () => signMessage(params)
+ break
+ }
+
+ return h('div.request-signature__footer', [
+ h('button.btn-secondary--lg.request-signature__footer__cancel-button', {
+ onClick: () => {
+ cancel().then(() => history.push(DEFAULT_ROUTE))
+ },
+ }, t('cancel')),
+ h('button.btn-primary--lg', {
+ onClick: () => {
+ sign().then(() => history.push(DEFAULT_ROUTE))
+ },
+ }, t('sign')),
+ ])
+ }
+
+ render () {
+ return (
+ h('div.request-signature__container', [
+
+ this.renderHeader(),
+
+ this.renderBody(),
+
+ this.renderFooter(),
+
+ ])
+ )
+ }
+}
+
+SignatureRequest.propTypes = {
+ txData: PropTypes.object,
+ signPersonalMessage: PropTypes.func,
+ cancelPersonalMessage: PropTypes.func,
+ signTypedMessage: PropTypes.func,
+ cancelTypedMessage: PropTypes.func,
+ signMessage: PropTypes.func,
+ cancelMessage: PropTypes.func,
+ requesterAddress: PropTypes.string,
+ accounts: PropTypes.array,
+ conversionRate: PropTypes.number,
+ balance: PropTypes.string,
+ selectedAccount: PropTypes.object,
+ history: PropTypes.object,
+ unapprovedMsgCount: PropTypes.number,
+ unapprovedPersonalMsgCount: PropTypes.number,
+ unapprovedTypedMessagesCount: PropTypes.number,
+}
+
+const mapStateToProps = state => {
+ return {
+ balance: getSelectedAccount(state).balance,
+ selectedAccount: getCurrentAccountWithSendEtherInfo(state),
+ selectedAddress: getSelectedAddress(state),
+ requester: null,
+ requesterAddress: null,
+ accounts: accountsWithSendEtherInfoSelector(state),
+ conversionRate: conversionRateSelector(state),
+ }
+}
+
+module.exports = connect(mapStateToProps)(SignatureRequest)
diff --git a/ui/app/components/pages/unlock.js b/ui/app/components/pages/unlock.js
new file mode 100644
index 000000000..3d7a9091c
--- /dev/null
+++ b/ui/app/components/pages/unlock.js
@@ -0,0 +1,175 @@
+const { Component } = require('react')
+const PropTypes = require('prop-types')
+const { connect } = require('react-redux')
+const h = require('react-hyperscript')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
+const { tryUnlockMetamask, forgotPassword, markPasswordForgotten } = require('../../actions')
+const getCaretCoordinates = require('textarea-caret')
+const EventEmitter = require('events').EventEmitter
+const Mascot = require('../mascot')
+const { DEFAULT_ROUTE } = require('../../routes')
+
+class UnlockScreen extends Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ error: null,
+ }
+
+ this.animationEventEmitter = new EventEmitter()
+ }
+
+ componentWillMount () {
+ const { isUnlocked, history } = this.props
+
+ if (isUnlocked) {
+ history.push(DEFAULT_ROUTE)
+ }
+ }
+
+ componentDidMount () {
+ const passwordBox = document.getElementById('password-box')
+
+ if (passwordBox) {
+ passwordBox.focus()
+ }
+ }
+
+ tryUnlockMetamask (password) {
+ const { tryUnlockMetamask, history } = this.props
+ tryUnlockMetamask(password)
+ .then(() => history.push(DEFAULT_ROUTE))
+ .catch(({ message }) => this.setState({ error: message }))
+ }
+
+ onSubmit (event) {
+ const input = document.getElementById('password-box')
+ const password = input.value
+ this.tryUnlockMetamask(password)
+ }
+
+ onKeyPress (event) {
+ if (event.key === 'Enter') {
+ this.submitPassword(event)
+ }
+ }
+
+ submitPassword (event) {
+ var element = event.target
+ var password = element.value
+ // reset input
+ element.value = ''
+ this.tryUnlockMetamask(password)
+ }
+
+ inputChanged (event) {
+ // tell mascot to look at page action
+ var element = event.target
+ var boundingRect = element.getBoundingClientRect()
+ var coordinates = getCaretCoordinates(element, element.selectionEnd)
+ this.animationEventEmitter.emit('point', {
+ x: boundingRect.left + coordinates.left - element.scrollLeft,
+ y: boundingRect.top + coordinates.top - element.scrollTop,
+ })
+ }
+
+ render () {
+ const { error } = this.state
+ const { markPasswordForgotten } = this.props
+
+ return (
+ h('.unlock-page.main-container', [
+ h('.flex-column', {
+ style: {
+ width: 'inherit',
+ },
+ }, [
+ h('.unlock-screen.flex-column.flex-center.flex-grow', [
+
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ }),
+
+ h('h1', {
+ style: {
+ fontSize: '1.4em',
+ textTransform: 'uppercase',
+ color: '#7F8082',
+ },
+ }, 'MetaMask'),
+
+ h('input.large-input', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'enter password',
+ style: {
+ background: 'white',
+ },
+ onKeyPress: this.onKeyPress.bind(this),
+ onInput: this.inputChanged.bind(this),
+ }),
+
+ h('.error', {
+ style: {
+ display: error ? 'block' : 'none',
+ padding: '0 20px',
+ textAlign: 'center',
+ },
+ }, error),
+
+ h('button.primary.cursor-pointer', {
+ onClick: this.onSubmit.bind(this),
+ style: {
+ margin: 10,
+ },
+ }, 'Unlock'),
+
+ h('.flex-row.flex-center.flex-grow', [
+ h('p.pointer', {
+ onClick: () => {
+ markPasswordForgotten()
+ global.platform.openExtensionInBrowser()
+ },
+ style: {
+ fontSize: '0.8em',
+ color: 'rgb(247, 134, 28)',
+ textDecoration: 'underline',
+ },
+ }, 'Restore from seed phrase'),
+ ]),
+ ]),
+ ]),
+ ])
+ )
+ }
+}
+
+UnlockScreen.propTypes = {
+ forgotPassword: PropTypes.func,
+ tryUnlockMetamask: PropTypes.func,
+ markPasswordForgotten: PropTypes.func,
+ history: PropTypes.object,
+ isUnlocked: PropTypes.bool,
+}
+
+const mapStateToProps = state => {
+ const { metamask: { isUnlocked } } = state
+ return {
+ isUnlocked,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ forgotPassword: () => dispatch(forgotPassword()),
+ tryUnlockMetamask: password => dispatch(tryUnlockMetamask(password)),
+ markPasswordForgotten: () => dispatch(markPasswordForgotten()),
+ }
+}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(UnlockScreen)
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 255f0e8a2..4ba0f55dc 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -1,5 +1,7 @@
const Component = require('react').Component
const connect = require('../../metamask-connect')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const actions = require('../../actions')
@@ -17,8 +19,12 @@ const SenderToRecipient = require('../sender-to-recipient')
const NetworkDisplay = require('../network-display')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
+const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendEther)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(ConfirmSendEther)
function mapStateToProps (state) {
const {
@@ -59,7 +65,6 @@ function mapDispatchToProps (dispatch) {
errors: { to: null, amount: null },
editingTransactionId: id,
}))
- dispatch(actions.showSendPage())
},
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
@@ -209,6 +214,12 @@ ConfirmSendEther.prototype.getData = function () {
}
}
+ConfirmSendEther.prototype.editTransaction = function (txMeta) {
+ const { editTransaction, history } = this.props
+ editTransaction(txMeta)
+ history.push(SEND_ROUTE)
+}
+
ConfirmSendEther.prototype.render = function () {
const {
editTransaction,
@@ -457,6 +468,7 @@ ConfirmSendEther.prototype.cancel = function (event, txMeta) {
const { cancelTransaction } = this.props
cancelTransaction(txMeta)
+ .then(() => this.props.history.push(DEFAULT_ROUTE))
}
ConfirmSendEther.prototype.checkValidity = function () {
@@ -522,4 +534,4 @@ ConfirmSendEther.prototype.bnMultiplyByFraction = function (targetBN, numerator,
const numBN = new BN(numerator)
const denomBN = new BN(denominator)
return targetBN.mul(numBN).div(denomBN)
-} \ No newline at end of file
+}
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index 3e66705ae..9e30d9e3b 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -1,5 +1,7 @@
const Component = require('react').Component
const connect = require('../../metamask-connect')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const tokenAbi = require('human-standard-token-abi')
@@ -27,8 +29,12 @@ const {
getSelectedAddress,
getSelectedTokenContract,
} = require('../../selectors')
+const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmSendToken)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(ConfirmSendToken)
function mapStateToProps (state, ownProps) {
const { token: { symbol }, txData } = ownProps
@@ -133,6 +139,12 @@ function ConfirmSendToken () {
this.onSubmit = this.onSubmit.bind(this)
}
+ConfirmSendToken.prototype.editTransaction = function (txMeta) {
+ const { editTransaction, history } = this.props
+ editTransaction(txMeta)
+ history.push(SEND_ROUTE)
+}
+
ConfirmSendToken.prototype.componentWillMount = function () {
const { tokenContract, selectedAddress } = this.props
tokenContract && tokenContract
@@ -466,6 +478,7 @@ ConfirmSendToken.prototype.cancel = function (event, txMeta) {
const { cancelTransaction } = this.props
cancelTransaction(txMeta)
+ .then(() => this.props.history.push(DEFAULT_ROUTE))
}
ConfirmSendToken.prototype.checkValidity = function () {
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index 5b903a96e..e2c1dd49b 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -2,6 +2,8 @@ const connect = require('../../metamask-connect')
const actions = require('../../actions')
const abi = require('ethereumjs-abi')
const SendEther = require('../../send-v2')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const {
accountsWithSendEtherInfoSelector,
@@ -16,7 +18,10 @@ const {
getSelectedTokenContract,
} = require('../../selectors')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(SendEther)
function mapStateToProps (state) {
const fromAccounts = accountsWithSendEtherInfoSelector(state)
@@ -79,7 +84,6 @@ function mapDispatchToProps (dispatch) {
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
- goHome: () => dispatch(actions.goHome()),
clearSend: () => dispatch(actions.clearSend()),
setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)),
}
diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js
index a80640116..0016a09c1 100644
--- a/ui/app/components/tab-bar.js
+++ b/ui/app/components/tab-bar.js
@@ -4,31 +4,17 @@ const PropTypes = require('prop-types')
const classnames = require('classnames')
class TabBar extends Component {
- constructor (props) {
- super(props)
- const { defaultTab, tabs } = props
-
- this.state = {
- subview: defaultTab || tabs[0].key,
- }
- }
-
render () {
- const { tabs = [], tabSelected } = this.props
- const { subview } = this.state
+ const { tabs = [], onSelect, isActive } = this.props
return (
h('.tab-bar', {}, [
- tabs.map((tab) => {
- const { key, content } = tab
+ tabs.map(({ key, content }) => {
return h('div', {
className: classnames('tab-bar__tab pointer', {
- 'tab-bar__tab--active': subview === key,
+ 'tab-bar__tab--active': isActive(key, content),
}),
- onClick: () => {
- this.setState({ subview: key })
- tabSelected(key)
- },
+ onClick: () => onSelect(key),
key,
}, content)
}),
@@ -39,9 +25,9 @@ class TabBar extends Component {
}
TabBar.propTypes = {
- defaultTab: PropTypes.string,
+ isActive: PropTypes.func.isRequired,
tabs: PropTypes.array,
- tabSelected: PropTypes.func,
+ onSelect: PropTypes.func,
}
module.exports = TabBar
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index 5f09d887e..ca683f764 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -10,8 +10,14 @@ const { formatDate } = require('../util')
const { showConfTxPage } = require('../actions')
const classnames = require('classnames')
const { tokenInfoGetter } = require('../token-util')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
+const { CONFIRM_TRANSACTION_ROUTE } = require('../routes')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(TxList)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(TxList)
function mapStateToProps (state) {
return {
@@ -90,7 +96,7 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
transactionNetworkId,
transactionSubmittedTime,
} = props
- const { showConfTxPage } = this.props
+ const { history } = this.props
const opts = {
key: transactionId || transactionHash,
@@ -109,7 +115,7 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) {
- opts.onClick = () => showConfTxPage({ id: transactionId })
+ opts.onClick = () => history.push(CONFIRM_TRANSACTION_ROUTE)
opts.transactionStatus = this.props.t('notStarted')
} else if (transactionHash) {
opts.onClick = () => this.view(transactionHash, transactionNetworkId)
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 8b0cc890a..a5f737d82 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -3,14 +3,20 @@ const connect = require('../metamask-connect')
const h = require('react-hyperscript')
const ethUtil = require('ethereumjs-util')
const inherits = require('util').inherits
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const actions = require('../actions')
const selectors = require('../selectors')
+const { SEND_ROUTE } = require('../routes')
const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list')
const Identicon = require('./identicon')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(TxView)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(TxView)
function mapStateToProps (state) {
const sidebarOpen = state.appState.sidebarOpen
@@ -63,7 +69,7 @@ TxView.prototype.renderHeroBalance = function () {
}
TxView.prototype.renderButtons = function () {
- const {selectedToken, showModal, showSendPage, showSendTokenPage } = this.props
+ const {selectedToken, showModal, history } = this.props
return !selectedToken
? (
@@ -78,14 +84,14 @@ TxView.prototype.renderButtons = function () {
style: {
marginLeft: '0.8em',
},
- onClick: showSendPage,
+ onClick: () => history.push(SEND_ROUTE),
}, this.props.t('send')),
])
)
: (
h('div.flex-row.flex-center.hero-balance-buttons', [
h('button.btn-primary.hero-balance-button', {
- onClick: showSendTokenPage,
+ onClick: () => history.push(SEND_ROUTE),
}, this.props.t('send')),
])
)
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 5f1ed7b60..c0042614a 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -1,6 +1,8 @@
const Component = require('react').Component
const connect = require('../metamask-connect')
const h = require('react-hyperscript')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const inherits = require('util').inherits
const classnames = require('classnames')
const Identicon = require('./identicon')
@@ -11,8 +13,12 @@ const actions = require('../actions')
const BalanceComponent = require('./balance-component')
const TokenList = require('./token-list')
const selectors = require('../selectors')
+const { ADD_TOKEN_ROUTE } = require('../routes')
-module.exports = connect(mapStateToProps, mapDispatchToProps)(WalletView)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(WalletView)
function mapStateToProps (state) {
@@ -91,7 +97,7 @@ WalletView.prototype.render = function () {
keyrings,
showAccountDetailModal,
hideSidebar,
- showAddTokenPage,
+ history,
} = this.props
// temporary logs + fake extra wallets
// console.log('walletview, selectedAccount:', selectedAccount)
@@ -168,10 +174,7 @@ WalletView.prototype.render = function () {
h(TokenList),
h('button.btn-primary.wallet-view__add-token-button', {
- onClick: () => {
- showAddTokenPage()
- hideSidebar()
- },
+ onClick: () => history.push(ADD_TOKEN_ROUTE),
}, this.props.t('addToken')),
])
}
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 03848f490..57ae89dc6 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -2,28 +2,30 @@ const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('./metamask-connect')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
const actions = require('./actions')
const txHelper = require('../lib/tx-helper')
const PendingTx = require('./components/pending-tx')
-const SignatureRequest = require('./components/signature-request')
// const PendingMsg = require('./components/pending-msg')
// const PendingPersonalMsg = require('./components/pending-personal-msg')
// const PendingTypedMsg = require('./components/pending-typed-msg')
const Loading = require('./components/loading')
+const { DEFAULT_ROUTE } = require('./routes')
-// const contentDivider = h('div', {
-// style: {
-// marginLeft: '16px',
-// marginRight: '16px',
-// height:'1px',
-// background:'#E7E7E7',
-// },
-// })
-
-module.exports = connect(mapStateToProps)(ConfirmTxScreen)
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps)
+)(ConfirmTxScreen)
function mapStateToProps (state) {
+ const { metamask } = state
+ const {
+ unapprovedMsgCount,
+ unapprovedPersonalMsgCount,
+ } = metamask
+
return {
identities: state.metamask.identities,
accounts: state.metamask.accounts,
@@ -40,6 +42,9 @@ function mapStateToProps (state) {
currentCurrency: state.metamask.currentCurrency,
blockGasLimit: state.metamask.currentBlockGasLimit,
computedBalances: state.metamask.computedBalances,
+ unapprovedMsgCount,
+ unapprovedPersonalMsgCount,
+ send: state.metamask.send,
selectedAddressTxList: state.metamask.selectedAddressTxList,
}
}
@@ -62,7 +67,7 @@ ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) {
const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network)
if (prevTx.status === 'dropped' && unconfTxList.length === 0) {
- this.goHome({})
+ this.props.history.push(DEFAULT_ROUTE)
}
}
@@ -131,27 +136,13 @@ ConfirmTxScreen.prototype.render = function () {
function currentTxView (opts) {
log.info('rendering current tx view')
const { txData } = opts
- const { txParams, msgParams } = txData
+ const { txParams } = txData
if (txParams) {
log.debug('txParams detected, rendering pending tx')
return h(PendingTx, opts)
- } else if (msgParams) {
- log.debug('msgParams detected, rendering pending msg')
-
- return h(SignatureRequest, opts)
-
- // if (type === 'eth_sign') {
- // log.debug('rendering eth_sign message')
- // return h(PendingMsg, opts)
- // } else if (type === 'personal_sign') {
- // log.debug('rendering personal_sign message')
- // return h(PendingPersonalMsg, opts)
- // } else if (type === 'eth_signTypedData') {
- // log.debug('rendering eth_signTypedData message')
- // return h(PendingTypedMsg, opts)
- // }
}
+
return h(Loading)
}
@@ -163,6 +154,7 @@ ConfirmTxScreen.prototype.buyEth = function (address, event) {
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
this.stopPropagation(event)
this.props.dispatch(actions.updateAndApproveTx(txData))
+ .then(() => this.props.history.push(DEFAULT_ROUTE))
}
ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) {
diff --git a/ui/app/css/index.scss b/ui/app/css/index.scss
index 445c819ff..c068028f8 100644
--- a/ui/app/css/index.scss
+++ b/ui/app/css/index.scss
@@ -6,9 +6,15 @@
*/
@import './itcss/settings/index.scss';
+
@import './itcss/tools/index.scss';
+
@import './itcss/generic/index.scss';
+
@import './itcss/base/index.scss';
+
@import './itcss/objects/index.scss';
+
@import './itcss/components/index.scss';
+
@import './itcss/trumps/index.scss';
diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss
index ffd43ecbf..959eb9d15 100644
--- a/ui/app/css/itcss/components/index.scss
+++ b/ui/app/css/itcss/components/index.scss
@@ -52,6 +52,8 @@
@import './editable-label.scss';
+@import './pages/index.scss';
+
@import './new-account.scss';
@import './tooltip.scss';
diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss
index aa7fed956..293579058 100644
--- a/ui/app/css/itcss/components/new-account.scss
+++ b/ui/app/css/itcss/components/new-account.scss
@@ -35,13 +35,14 @@
font-size: 18px;
line-height: 24px;
text-align: center;
+ cursor: pointer;
}
&__tab:first-of-type {
margin-right: 20px;
}
- &__unselected:hover {
+ &__tab:hover {
color: $black;
border-bottom: none;
}
@@ -49,9 +50,9 @@
&__selected {
color: $curious-blue;
border-bottom: 3px solid $curious-blue;
+ cursor: initial;
}
}
-
}
.new-account-import-disclaimer {
diff --git a/ui/app/css/itcss/components/pages/index.scss b/ui/app/css/itcss/components/pages/index.scss
new file mode 100644
index 000000000..82446fd7a
--- /dev/null
+++ b/ui/app/css/itcss/components/pages/index.scss
@@ -0,0 +1 @@
+@import './unlock.scss';
diff --git a/ui/app/css/itcss/components/pages/unlock.scss b/ui/app/css/itcss/components/pages/unlock.scss
new file mode 100644
index 000000000..5d438377b
--- /dev/null
+++ b/ui/app/css/itcss/components/pages/unlock.scss
@@ -0,0 +1,9 @@
+.unlock-page {
+ box-shadow: none;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: rgb(247, 247, 247);
+ width: 100%;
+}
diff --git a/ui/app/css/itcss/components/sections.scss b/ui/app/css/itcss/components/sections.scss
index 388aea175..ace46bd8a 100644
--- a/ui/app/css/itcss/components/sections.scss
+++ b/ui/app/css/itcss/components/sections.scss
@@ -17,6 +17,12 @@ textarea.twelve-word-phrase {
resize: none;
}
+.initialize-screen {
+ width: 100%;
+ z-index: $main-container-z-index;
+ background: #f7f7f7;
+}
+
.initialize-screen hr {
width: 60px;
margin: 12px;
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index c0c2d3ed0..014bc5906 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -1,206 +1,221 @@
-const inherits = require('util').inherits
-const EventEmitter = require('events').EventEmitter
-const Component = require('react').Component
+const { EventEmitter } = require('events')
+const { Component } = require('react')
const connect = require('../metamask-connect')
const h = require('react-hyperscript')
+const PropTypes = require('prop-types')
const Mascot = require('../components/mascot')
const actions = require('../actions')
const Tooltip = require('../components/tooltip')
const getCaretCoordinates = require('textarea-caret')
+const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes')
const environmentType = require('../../../app/scripts/lib/environment-type')
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
-let isSubmitting = false
+class InitializeMenuScreen extends Component {
+ constructor (props) {
+ super(props)
-module.exports = connect(mapStateToProps)(InitializeMenuScreen)
-
-inherits(InitializeMenuScreen, Component)
-function InitializeMenuScreen () {
- Component.call(this)
- this.animationEventEmitter = new EventEmitter()
-}
-
-function mapStateToProps (state) {
- return {
- // state from plugin
- currentView: state.appState.currentView,
- warning: state.appState.warning,
+ this.animationEventEmitter = new EventEmitter()
+ this.state = {
+ warning: null,
+ }
}
-}
-
-InitializeMenuScreen.prototype.render = function () {
- var state = this.props
-
- switch (state.currentView.name) {
-
- default:
- return this.renderMenu(state)
+ componentWillMount () {
+ const { isInitialized, isUnlocked, history } = this.props
+ if (isInitialized || isUnlocked) {
+ history.push(DEFAULT_ROUTE)
+ }
}
-}
-// InitializeMenuScreen.prototype.componentDidMount = function(){
-// document.getElementById('password-box').focus()
-// }
-
-InitializeMenuScreen.prototype.renderMenu = function (state) {
- return (
-
- h('.initialize-screen.flex-column.flex-center.flex-grow', [
+ componentDidMount () {
+ document.getElementById('password-box').focus()
+ }
- h(Mascot, {
- animationEventEmitter: this.animationEventEmitter,
- }),
+ render () {
+ const { warning } = this.state
- h('h1', {
- style: {
- fontSize: '1.3em',
- textTransform: 'uppercase',
- color: '#7F8082',
- marginBottom: 10,
- },
- }, this.props.t('appName')),
+ return (
+ h('.initialize-screen.flex-column.flex-center', [
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ }),
- h('div', [
- h('h3', {
+ h('h1', {
style: {
- fontSize: '0.8em',
+ fontSize: '1.3em',
+ textTransform: 'uppercase',
color: '#7F8082',
- display: 'inline',
+ marginBottom: 10,
},
- }, this.props.t('encryptNewDen')),
+ }, this.props.t('appName')),
- h(Tooltip, {
- title: this.props.t('denExplainer'),
- }, [
- h('i.fa.fa-question-circle.pointer', {
+ h('div', [
+ h('h3', {
style: {
- fontSize: '18px',
- position: 'relative',
- color: 'rgb(247, 134, 28)',
- top: '2px',
- marginLeft: '4px',
+ fontSize: '0.8em',
+ color: '#7F8082',
+ display: 'inline',
},
- }),
+ }, this.props.t('encryptNewDen')),
+
+ h(Tooltip, {
+ title: this.props.t('denExplainer'),
+ }, [
+ h('i.fa.fa-question-circle.pointer', {
+ style: {
+ fontSize: '18px',
+ position: 'relative',
+ color: 'rgb(247, 134, 28)',
+ top: '2px',
+ marginLeft: '4px',
+ },
+ }),
+ ]),
]),
- ]),
-
- h('span.in-progress-notification', state.warning),
-
- // password
- h('input.large-input.letter-spacey', {
- type: 'password',
- id: 'password-box',
- placeholder: this.props.t('newPassword'),
- onInput: this.inputChanged.bind(this),
- style: {
- width: 260,
- marginTop: 12,
- },
- }),
-
- // confirm password
- h('input.large-input.letter-spacey', {
- type: 'password',
- id: 'password-box-confirm',
- placeholder: this.props.t('confirmPassword'),
- onKeyPress: this.createVaultOnEnter.bind(this),
- onInput: this.inputChanged.bind(this),
- style: {
- width: 260,
- marginTop: 16,
- },
- }),
-
-
- h('button.primary', {
- onClick: this.createNewVaultAndKeychain.bind(this),
- style: {
- margin: 12,
- },
- }, this.props.t('createDen')),
-
- h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: this.showRestoreVault.bind(this),
+
+ h('span.error.in-progress-notification', warning),
+
+ // password
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: this.props.t('newPassword'),
+ onInput: this.inputChanged.bind(this),
style: {
- fontSize: '0.8em',
- color: 'rgb(247, 134, 28)',
- textDecoration: 'underline',
+ width: 260,
+ marginTop: 12,
},
- }, this.props.t('importDen')),
- ]),
+ }),
+
+ // confirm password
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box-confirm',
+ placeholder: this.props.t('confirmPassword'),
+ onKeyPress: this.createVaultOnEnter.bind(this),
+ onInput: this.inputChanged.bind(this),
+ style: {
+ width: 260,
+ marginTop: 16,
+ },
+ }),
+
- h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: this.showOldUI.bind(this),
+ h('button.primary', {
+ onClick: this.createNewVaultAndKeychain.bind(this),
style: {
- fontSize: '0.8em',
- color: '#aeaeae',
- textDecoration: 'underline',
- marginTop: '32px',
+ margin: 12,
},
- }, 'Use classic interface'),
- ]),
+ }, this.props.t('createDen')),
- ])
- )
-}
+ h('.flex-row.flex-center.flex-grow', [
+ h('p.pointer', {
+ onClick: () => this.showRestoreVault(),
+ style: {
+ fontSize: '0.8em',
+ color: 'rgb(247, 134, 28)',
+ textDecoration: 'underline',
+ },
+ }, this.props.t('importDen')),
+ ]),
-InitializeMenuScreen.prototype.createVaultOnEnter = function (event) {
- if (event.key === 'Enter') {
- event.preventDefault()
- this.createNewVaultAndKeychain()
+ h('.flex-row.flex-center.flex-grow', [
+ h('p.pointer', {
+ onClick: this.showOldUI.bind(this),
+ style: {
+ fontSize: '0.8em',
+ color: '#aeaeae',
+ textDecoration: 'underline',
+ marginTop: '32px',
+ },
+ }, 'Use classic interface'),
+ ]),
+
+ ])
+ )
}
-}
-InitializeMenuScreen.prototype.componentDidMount = function () {
- document.getElementById('password-box').focus()
-}
+ createVaultOnEnter (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.createNewVaultAndKeychain()
+ }
+ }
+
+ createNewVaultAndKeychain () {
+ const { history } = this.props
+ var passwordBox = document.getElementById('password-box')
+ var password = passwordBox.value
+ var passwordConfirmBox = document.getElementById('password-box-confirm')
+ var passwordConfirm = passwordConfirmBox.value
+
+ this.setState({ warning: null })
-InitializeMenuScreen.prototype.showRestoreVault = function () {
- this.props.dispatch(actions.markPasswordForgotten())
- if (environmentType() === 'popup') {
- global.platform.openExtensionInBrowser()
+ if (password.length < 8) {
+ this.setState({ warning: this.props.t('passwordShort') })
+ return
+ }
+
+ if (password !== passwordConfirm) {
+ this.setState({ warning: this.props.t('passwordMismatch') })
+ return
+ }
+
+ this.props.createNewVaultAndKeychain(password)
+ .then(() => history.push(DEFAULT_ROUTE))
}
-}
-InitializeMenuScreen.prototype.showOldUI = function () {
- this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
- .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
-}
+ inputChanged (event) {
+ // tell mascot to look at page action
+ var element = event.target
+ var boundingRect = element.getBoundingClientRect()
+ var coordinates = getCaretCoordinates(element, element.selectionEnd)
+ this.animationEventEmitter.emit('point', {
+ x: boundingRect.left + coordinates.left - element.scrollLeft,
+ y: boundingRect.top + coordinates.top - element.scrollTop,
+ })
+ }
-InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
- var passwordBox = document.getElementById('password-box')
- var password = passwordBox.value
- var passwordConfirmBox = document.getElementById('password-box-confirm')
- var passwordConfirm = passwordConfirmBox.value
+ showRestoreVault () {
+ this.props.markPasswordForgotten()
+ if (environmentType() === 'popup') {
+ global.platform.openExtensionInBrowser()
+ }
- if (password.length < 8) {
- this.warning = this.props.t('passwordShort')
- this.props.dispatch(actions.displayWarning(this.warning))
- return
+ this.props.history.push(RESTORE_VAULT_ROUTE)
}
- if (password !== passwordConfirm) {
- this.warning = this.props.t('passwordMismatch')
- this.props.dispatch(actions.displayWarning(this.warning))
- return
+
+ showOldUI () {
+ this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
+ .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
}
+}
+
+InitializeMenuScreen.propTypes = {
+ history: PropTypes.object,
+ isInitialized: PropTypes.bool,
+ isUnlocked: PropTypes.bool,
+ createNewVaultAndKeychain: PropTypes.func,
+ markPasswordForgotten: PropTypes.func,
+ dispatch: PropTypes.func,
+}
- if (!isSubmitting) {
- isSubmitting = true
- this.props.dispatch(actions.createNewVaultAndKeychain(password))
+const mapStateToProps = state => {
+ const { metamask: { isInitialized, isUnlocked } } = state
+
+ return {
+ isInitialized,
+ isUnlocked,
}
}
-InitializeMenuScreen.prototype.inputChanged = function (event) {
- // tell mascot to look at page action
- var element = event.target
- var boundingRect = element.getBoundingClientRect()
- var coordinates = getCaretCoordinates(element, element.selectionEnd)
- this.animationEventEmitter.emit('point', {
- x: boundingRect.left + coordinates.left - element.scrollLeft,
- y: boundingRect.top + coordinates.top - element.scrollTop,
- })
+const mapDispatchToProps = dispatch => {
+ return {
+ createNewVaultAndKeychain: password => dispatch(actions.createNewVaultAndKeychain(password)),
+ markPasswordForgotten: () => dispatch(actions.markPasswordForgotten()),
+ }
}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(InitializeMenuScreen)
diff --git a/ui/app/main-container.js b/ui/app/main-container.js
index eed4bd164..6dd4ff151 100644
--- a/ui/app/main-container.js
+++ b/ui/app/main-container.js
@@ -2,8 +2,8 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountAndTransactionDetails = require('./account-and-transaction-details')
-const Settings = require('./settings')
-const UnlockScreen = require('./unlock')
+const Settings = require('./components/pages/settings')
+const UnlockScreen = require('./components/pages/unlock')
module.exports = MainContainer
diff --git a/ui/app/root.js b/ui/app/root.js
index 21d6d1829..09deae1b1 100644
--- a/ui/app/root.js
+++ b/ui/app/root.js
@@ -1,22 +1,23 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const Provider = require('react-redux').Provider
+const { Component } = require('react')
+const PropTypes = require('prop-types')
+const { Provider } = require('react-redux')
const h = require('react-hyperscript')
const SelectedApp = require('./select-app')
-module.exports = Root
-
-inherits(Root, Component)
-function Root () { Component.call(this) }
-
-Root.prototype.render = function () {
- return (
+class Root extends Component {
+ render () {
+ const { store } = this.props
- h(Provider, {
- store: this.props.store,
- }, [
- h(SelectedApp),
- ])
+ return (
+ h(Provider, { store }, [
+ h(SelectedApp),
+ ])
+ )
+ }
+}
- )
+Root.propTypes = {
+ store: PropTypes.object,
}
+
+module.exports = Root
diff --git a/ui/app/routes.js b/ui/app/routes.js
new file mode 100644
index 000000000..aadf91cf5
--- /dev/null
+++ b/ui/app/routes.js
@@ -0,0 +1,39 @@
+const DEFAULT_ROUTE = '/'
+const UNLOCK_ROUTE = '/unlock'
+const SETTINGS_ROUTE = '/settings'
+const INFO_ROUTE = '/settings/info'
+const REVEAL_SEED_ROUTE = '/seed'
+const CONFIRM_SEED_ROUTE = '/confirm-seed'
+const RESTORE_VAULT_ROUTE = '/restore-vault'
+const ADD_TOKEN_ROUTE = '/add-token'
+const NEW_ACCOUNT_ROUTE = '/new-account'
+const IMPORT_ACCOUNT_ROUTE = '/new-account/import'
+const SEND_ROUTE = '/send'
+const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction'
+const NOTICE_ROUTE = '/notice'
+const SIGNATURE_REQUEST_ROUTE = '/signature-request'
+const WELCOME_ROUTE = '/welcome'
+const INITIALIZE_ROUTE = '/initialize'
+const INITIALIZE_IMPORT_ACCOUNT_ROUTE = '/initialize/import-account'
+const INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE = '/initialize/import-with-seed-phrase'
+
+module.exports = {
+ DEFAULT_ROUTE,
+ UNLOCK_ROUTE,
+ SETTINGS_ROUTE,
+ INFO_ROUTE,
+ REVEAL_SEED_ROUTE,
+ CONFIRM_SEED_ROUTE,
+ RESTORE_VAULT_ROUTE,
+ ADD_TOKEN_ROUTE,
+ NEW_ACCOUNT_ROUTE,
+ IMPORT_ACCOUNT_ROUTE,
+ SEND_ROUTE,
+ CONFIRM_TRANSACTION_ROUTE,
+ NOTICE_ROUTE,
+ SIGNATURE_REQUEST_ROUTE,
+ WELCOME_ROUTE,
+ INITIALIZE_ROUTE,
+ INITIALIZE_IMPORT_ACCOUNT_ROUTE,
+ INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
+}
diff --git a/ui/app/select-app.js b/ui/app/select-app.js
index fca3decce..31fded715 100644
--- a/ui/app/select-app.js
+++ b/ui/app/select-app.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('./metamask-connect')
const h = require('react-hyperscript')
+const { HashRouter } = require('react-router-dom')
const App = require('./app')
const OldApp = require('../../old-ui/app/app')
const { autoAddToBetaUI } = require('./selectors')
@@ -62,7 +63,12 @@ SelectedApp.prototype.render = function () {
// const Selected = betaUI || isMascara || firstTime ? App : OldApp
const { betaUI, isMascara } = this.props
- const Selected = betaUI || isMascara ? App : OldApp
- return h(Selected)
+ return betaUI || isMascara
+ ? h(HashRouter, {
+ hashType: 'noslash',
+ }, [
+ h(App),
+ ])
+ : h(OldApp)
}
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index 52b8bb273..011d99d31 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -28,6 +28,7 @@ const {
isTokenBalanceSufficient,
} = require('./components/send/send-utils')
const { isValidAddress } = require('./util')
+const { CONFIRM_TRANSACTION_ROUTE } = require('./routes')
module.exports = SendTransactionScreen
@@ -184,7 +185,7 @@ SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
}
SendTransactionScreen.prototype.renderHeader = function () {
- const { selectedToken, clearSend, goHome } = this.props
+ const { selectedToken, clearSend, history } = this.props
return h('div.page-container__header', [
@@ -195,7 +196,7 @@ SendTransactionScreen.prototype.renderHeader = function () {
h('div.page-container__header-close', {
onClick: () => {
clearSend()
- goHome()
+ history.goBack()
},
}),
@@ -500,12 +501,12 @@ SendTransactionScreen.prototype.renderForm = function () {
SendTransactionScreen.prototype.renderFooter = function () {
const {
- goHome,
clearSend,
gasTotal,
tokenBalance,
selectedToken,
errors: { amount: amountError, to: toError },
+ history,
} = this.props
const missingTokenBalance = selectedToken && !tokenBalance
@@ -515,7 +516,7 @@ SendTransactionScreen.prototype.renderFooter = function () {
h('button.btn-secondary--lg.page-container__footer-button', {
onClick: () => {
clearSend()
- goHome()
+ history.goBack()
},
}, this.props.t('cancel')),
h('button.btn-primary--lg.page-container__footer-button', {
@@ -621,7 +622,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
if (editingTransactionId) {
const editedTx = this.getEditedTx()
-
updateTx(editedTx)
} else {
@@ -641,4 +641,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
? signTokenTx(selectedToken.address, to, amount, txParams)
: signTx(txParams)
}
+
+ this.props.history.push(CONFIRM_TRANSACTION_ROUTE)
}
diff --git a/ui/app/welcome-screen.js b/ui/app/welcome-screen.js
index cdbb6dba8..f32491b0d 100644
--- a/ui/app/welcome-screen.js
+++ b/ui/app/welcome-screen.js
@@ -5,19 +5,22 @@ import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {closeWelcomeScreen} from './actions'
import Mascot from './components/mascot'
+import { INITIALIZE_ROUTE } from './routes'
class WelcomeScreen extends Component {
static propTypes = {
closeWelcomeScreen: PropTypes.func.isRequired,
+ history: PropTypes.object,
}
- constructor(props) {
+ constructor (props) {
super(props)
this.animationEventEmitter = new EventEmitter()
}
initiateAccountCreation = () => {
this.props.closeWelcomeScreen()
+ this.props.history.push(INITIALIZE_ROUTE)
}
render () {