aboutsummaryrefslogtreecommitdiffstats
path: root/old-ui
diff options
context:
space:
mode:
authorThomas Huang <thomas.b.huang@gmail.com>2018-08-02 01:40:31 +0800
committerThomas Huang <thomas.b.huang@gmail.com>2018-08-02 01:40:31 +0800
commit024ebe07e0c61e7185e84499a2f19885ac52e4a9 (patch)
tree88ed997d61c225573b7d75c357f5c11b6840a3ea /old-ui
parent5b3927fe5b5243a89e5fd31ad069da9ea5c987e9 (diff)
parent4f02726fd9a2b7509dfd00eb4b23d9fc81eb5dcd (diff)
downloadtangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.tar
tangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.tar.gz
tangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.tar.bz2
tangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.tar.lz
tangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.tar.xz
tangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.tar.zst
tangerine-wallet-browser-024ebe07e0c61e7185e84499a2f19885ac52e4a9.zip
Merge branch 'develop' into network-remove-provider-engine-tests
Diffstat (limited to 'old-ui')
-rw-r--r--old-ui/app/account-qr.js86
-rw-r--r--old-ui/app/app.js461
-rw-r--r--old-ui/app/components/app-bar.js432
-rw-r--r--old-ui/app/components/qr-code.js79
-rw-r--r--old-ui/app/components/shapeshift-form.js4
-rw-r--r--old-ui/app/components/transaction-list-item.js11
-rw-r--r--old-ui/app/css/index.css128
-rw-r--r--old-ui/app/new-ui-annoucement.js85
8 files changed, 771 insertions, 515 deletions
diff --git a/old-ui/app/account-qr.js b/old-ui/app/account-qr.js
new file mode 100644
index 000000000..b41cc5112
--- /dev/null
+++ b/old-ui/app/account-qr.js
@@ -0,0 +1,86 @@
+const PropTypes = require('prop-types')
+const {PureComponent} = require('react')
+const h = require('react-hyperscript')
+const {qrcode: qrCode} = require('qrcode-npm')
+const {connect} = require('react-redux')
+const {isHexPrefixed} = require('ethereumjs-util')
+const actions = require('../../ui/app/actions')
+const CopyButton = require('./components/copyButton')
+
+class AccountQrScreen extends PureComponent {
+ static defaultProps = {
+ warning: null,
+ }
+
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ buyView: PropTypes.any.isRequired,
+ Qr: PropTypes.object.isRequired,
+ selectedAddress: PropTypes.string.isRequired,
+ warning: PropTypes.node,
+ }
+
+ render () {
+ const {dispatch, Qr, selectedAddress, warning} = this.props
+ const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
+ const qrImage = qrCode(4, 'M')
+
+ qrImage.addData(address)
+ qrImage.make()
+
+ return h('div.flex-column.full-width', {
+ style: {
+ alignItems: 'center',
+ boxSizing: 'border-box',
+ padding: '50px',
+ },
+ }, [
+ h('div.flex-row.full-width', {
+ style: {
+ alignItems: 'flex-start',
+ },
+ }, [
+ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
+ onClick () {
+ dispatch(actions.backToAccountDetail(selectedAddress))
+ },
+ }),
+ ]),
+ h('div.qr-header', Qr.message),
+ warning && h('span.error.flex-center', {
+ style: {
+ textAlign: 'center',
+ width: '229px',
+ height: '82px',
+ },
+ }, [
+ this.props.warning,
+ ]),
+ h('div#qr-container.flex-column', {
+ style: {
+ marginTop: '25px',
+ marginBottom: '15px',
+ },
+ dangerouslySetInnerHTML: {
+ __html: qrImage.createTableTag(4),
+ },
+ }),
+ h('div.flex-row.full-width', [
+ h('h3.ellip-address.grow-tenx', Qr.data),
+ h(CopyButton, {
+ value: Qr.data,
+ }),
+ ]),
+ ])
+ }
+}
+
+function mapStateToProps (state) {
+ return {
+ Qr: state.appState.Qr,
+ buyView: state.appState.buyView,
+ warning: state.appState.warning,
+ }
+}
+
+module.exports = connect(mapStateToProps)(AccountQrScreen)
diff --git a/old-ui/app/app.js b/old-ui/app/app.js
index 0637e3b5b..d3e9e823b 100644
--- a/old-ui/app/app.js
+++ b/old-ui/app/app.js
@@ -14,6 +14,7 @@ const NewKeyChainScreen = require('./new-keychain')
const UnlockScreen = require('./unlock')
// accounts
const AccountDetailScreen = require('./account-detail')
+const AccountQrScreen = require('./account-qr')
const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx')
// notice
@@ -24,17 +25,13 @@ const ConfigScreen = require('./config')
const AddTokenScreen = require('./add-token')
const Import = require('./accounts/import')
const InfoScreen = require('./info')
+const NewUiAnnouncement = require('./new-ui-annoucement')
+const AppBar = require('./components/app-bar')
const Loading = require('./components/loading')
-const SandwichExpando = require('sandwich-expando')
-const Dropdown = require('./components/dropdown').Dropdown
-const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
-const NetworkIndicator = require('./components/network')
const BuyView = require('./components/buy-button-subview')
-const QrView = require('./components/qr-code')
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
-const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
module.exports = connect(mapStateToProps)(App)
@@ -86,13 +83,29 @@ function mapStateToProps (state) {
}
App.prototype.render = function () {
- var props = this.props
- const { isLoading, loadingMessage, transForward, network } = props
- const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
- const loadMessage = loadingMessage || isLoadingNetwork ?
- `Connecting to ${this.getNetworkName()}` : null
+ const {
+ currentView,
+ dispatch,
+ isLoading,
+ loadingMessage,
+ transForward,
+ network,
+ featureFlags,
+ } = this.props
+ const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
+ const loadMessage = loadingMessage || isLoadingNetwork
+ ? `Connecting to ${this.getNetworkName()}`
+ : null
log.debug('Main ui render function')
+ if (!featureFlags.skipAnnounceBetaUI) {
+ return (
+ h(NewUiAnnouncement, {
+ dispatch,
+ })
+ )
+ }
+
return (
h('.flex-column.full-height', {
style: {
@@ -102,12 +115,9 @@ App.prototype.render = function () {
alignItems: 'center',
},
}, [
-
- // app bar
- this.renderAppBar(),
- this.renderNetworkDropdown(),
- this.renderDropdown(),
-
+ h(AppBar, {
+ ...this.props,
+ }),
this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
// panel content
@@ -121,299 +131,6 @@ App.prototype.render = function () {
])
)
}
-
-App.prototype.renderAppBar = function () {
- if (window.METAMASK_UI_TYPE === 'notification') {
- return null
- }
-
- const props = this.props
- const state = this.state || {}
- const isNetworkMenuOpen = state.isNetworkMenuOpen || false
- 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', {
- height: '38px',
- }, [
-
- h('.app-header.flex-row.flex-space-between', {
- style: {
- alignItems: 'center',
- visibility: props.isUnlocked ? 'visible' : 'none',
- background: props.isUnlocked ? 'white' : 'none',
- height: '38px',
- position: 'relative',
- zIndex: 12,
- },
- }, [
-
- h('div.left-menu-section', {
- style: {
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- },
- }, [
-
- // mini logo
- h('img', {
- height: 24,
- width: 24,
- src: './images/icon-128.png',
- }),
-
- h(NetworkIndicator, {
- network: this.props.network,
- provider: this.props.provider,
- onClick: (event) => {
- event.preventDefault()
- event.stopPropagation()
- this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
- },
- }),
-
- ]),
-
- props.isUnlocked && h('div', {
- style: {
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- },
- }, [
-
- props.isUnlocked && h(AccountDropdowns, {
- style: {},
- enableAccountsSelector: true,
- identities: this.props.identities,
- selected: this.props.selectedAddress,
- network: this.props.network,
- keyrings: this.props.keyrings,
- }, []),
-
- // hamburger
- props.isUnlocked && h(SandwichExpando, {
- className: 'sandwich-expando',
- width: 16,
- barHeight: 2,
- padding: 0,
- isOpen: state.isMainMenuOpen,
- color: 'rgb(247,146,30)',
- onClick: () => {
- this.setState({
- isMainMenuOpen: !state.isMainMenuOpen,
- })
- },
- }),
- ]),
- ]),
- ])
- )
-}
-
-App.prototype.renderNetworkDropdown = function () {
- const props = this.props
- const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
- const rpcList = props.frequentRpcList
- const state = this.state || {}
- const isOpen = state.isNetworkMenuOpen
-
- return h(Dropdown, {
- useCssTransition: true,
- isOpen,
- onClickOutside: (event) => {
- const { classList } = event.target
- const isNotToggleElement = [
- classList.contains('menu-icon'),
- classList.contains('network-name'),
- classList.contains('network-indicator'),
- ].filter(bool => bool).length === 0
- // classes from three constituent nodes of the toggle element
-
- if (isNotToggleElement) {
- this.setState({ isNetworkMenuOpen: false })
- }
- },
- zIndex: 11,
- style: {
- position: 'absolute',
- left: '2px',
- top: '36px',
- },
- innerStyle: {
- padding: '2px 16px 2px 0px',
- },
- }, [
-
- h(
- DropdownMenuItem,
- {
- key: 'main',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('mainnet')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.diamond'),
- 'Main Ethereum Network',
- providerType === 'mainnet' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'ropsten',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('ropsten')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.red-dot'),
- 'Ropsten Test Network',
- providerType === 'ropsten' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'kovan',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('kovan')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.hollow-diamond'),
- 'Kovan Test Network',
- providerType === 'kovan' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'rinkeby',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('rinkeby')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.golden-square'),
- 'Rinkeby Test Network',
- providerType === 'rinkeby' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'default',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('localhost')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- 'Localhost 8545',
- activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null,
- ]
- ),
-
- this.renderCustomOption(props.provider),
- this.renderCommonRpc(rpcList, props.provider),
-
- h(
- DropdownMenuItem,
- {
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => this.props.dispatch(actions.showConfigPage()),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- 'Custom RPC',
- activeNetwork === 'custom' ? h('.check', '✓') : null,
- ]
- ),
-
- ])
-}
-
-App.prototype.renderDropdown = function () {
- const state = this.state || {}
- const isOpen = state.isMainMenuOpen
-
- return h(Dropdown, {
- useCssTransition: true,
- isOpen: isOpen,
- zIndex: 11,
- onClickOutside: (event) => {
- const classList = event.target.classList
- const parentClassList = event.target.parentElement.classList
-
- const isToggleElement = classList.contains('sandwich-expando') ||
- parentClassList.contains('sandwich-expando')
-
- if (isOpen && !isToggleElement) {
- this.setState({ isMainMenuOpen: false })
- }
- },
- style: {
- position: 'absolute',
- right: '2px',
- top: '38px',
- },
- innerStyle: {},
- }, [
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => { this.props.dispatch(actions.showConfigPage()) },
- }, 'Settings'),
-
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => { this.props.dispatch(actions.lockMetamask()) },
- }, 'Log Out'),
-
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => { this.props.dispatch(actions.showInfoPage()) },
- }, 'Info/Help'),
-
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => {
- this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
- },
- }, 'Try Beta!'),
- ])
-}
-
App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
const { isMascara } = this.props
@@ -425,25 +142,6 @@ App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork,
})
}
-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
@@ -465,22 +163,6 @@ App.prototype.renderPrimary = function () {
key: 'NoticeScreen',
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
}),
-
- !props.isInitialized && h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: () => {
- global.platform.openExtensionInBrowser()
- props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
- },
- style: {
- fontSize: '0.8em',
- color: '#aeaeae',
- textDecoration: 'underline',
- marginTop: '32px',
- },
- }, 'Try Beta Version'),
- ]),
-
])
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
log.debug('rendering notice screen for lost accounts view.')
@@ -580,31 +262,10 @@ App.prototype.renderPrimary = function () {
case 'qr':
log.debug('rendering show qr screen')
- return h('div', {
- style: {
- position: 'absolute',
- height: '100%',
- top: '0px',
- left: '0px',
- },
- }, [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
- onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAddress)),
- style: {
- marginLeft: '10px',
- marginTop: '50px',
- },
- }),
- h('div', {
- style: {
- position: 'absolute',
- left: '44px',
- width: '285px',
- },
- }, [
- h(QrView, {key: 'qr'}),
- ]),
- ])
+ return h(AccountQrScreen, {
+ key: 'account-qr',
+ selectedAddress: props.selectedAddress,
+ })
default:
log.debug('rendering default, account detail screen')
@@ -623,41 +284,6 @@ App.prototype.toggleMetamaskActive = function () {
this.props.dispatch(actions.lockMetamask(false))
}
}
-
-App.prototype.renderCustomOption = function (provider) {
- const { rpcTarget, type } = provider
- const props = this.props
-
- if (type !== 'rpc') return null
-
- // Concatenate long URLs
- let label = rpcTarget
- if (rpcTarget.length > 31) {
- label = label.substr(0, 34) + '...'
- }
-
- switch (rpcTarget) {
-
- case 'http://localhost:8545':
- return null
-
- default:
- return h(
- DropdownMenuItem,
- {
- key: rpcTarget,
- onClick: () => props.dispatch(actions.setRpcTarget(rpcTarget)),
- closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- label,
- h('.check', '✓'),
- ]
- )
- }
-}
-
App.prototype.getNetworkName = function () {
const { provider } = this.props
const providerName = provider.type
@@ -678,28 +304,3 @@ App.prototype.getNetworkName = function () {
return name
}
-
-App.prototype.renderCommonRpc = function (rpcList, provider) {
- const props = this.props
- const rpcTarget = provider.rpcTarget
-
- return rpcList.map((rpc) => {
- if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) {
- return null
- } else {
- return h(
- DropdownMenuItem,
- {
- key: `common${rpc}`,
- closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- onClick: () => props.dispatch(actions.setRpcTarget(rpc)),
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- rpc,
- rpcTarget === rpc ? h('.check', '✓') : null,
- ]
- )
- }
- })
-}
diff --git a/old-ui/app/components/app-bar.js b/old-ui/app/components/app-bar.js
new file mode 100644
index 000000000..8ab647efd
--- /dev/null
+++ b/old-ui/app/components/app-bar.js
@@ -0,0 +1,432 @@
+const PropTypes = require('prop-types')
+const {Component} = require('react')
+const h = require('react-hyperscript')
+const actions = require('../../../ui/app/actions')
+const SandwichExpando = require('sandwich-expando')
+const {Dropdown} = require('./dropdown')
+const {DropdownMenuItem} = require('./dropdown')
+const NetworkIndicator = require('./network')
+const {AccountDropdowns} = require('./account-dropdowns')
+
+const LOCALHOST_RPC_URL = 'http://localhost:8545'
+
+module.exports = class AppBar extends Component {
+ static defaultProps = {
+ selectedAddress: undefined,
+ }
+
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ frequentRpcList: PropTypes.array.isRequired,
+ isMascara: PropTypes.bool.isRequired,
+ isOnboarding: PropTypes.bool.isRequired,
+ identities: PropTypes.any.isRequired,
+ selectedAddress: PropTypes.string,
+ isUnlocked: PropTypes.bool.isRequired,
+ network: PropTypes.any.isRequired,
+ keyrings: PropTypes.any.isRequired,
+ provider: PropTypes.any.isRequired,
+ }
+
+ static renderSpace () {
+ return (
+ h('span', {
+ dangerouslySetInnerHTML: {
+ __html: '&nbsp;',
+ },
+ })
+ )
+ }
+
+ state = {
+ isNetworkMenuOpen: false,
+ }
+
+ renderAppBar () {
+ 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('div.app-bar', [
+ this.renderAppBarNewUiNotice(),
+ this.renderAppBarAppHeader(),
+ ])
+ )
+ }
+
+ renderAppBarNewUiNotice () {
+ const {dispatch} = this.props
+
+ return (
+ h('div.app-bar__new-ui-banner', {
+ style: {
+ height: '28px',
+ zIndex: 12,
+ },
+ }, [
+ 'Try the New MetaMask',
+ AppBar.renderSpace(),
+ h('span.banner__link', {
+ async onClick () {
+ await dispatch(actions.setFeatureFlag('betaUI', true))
+ global.platform.openExtensionInBrowser()
+ },
+ }, [
+ 'Now',
+ ]),
+ AppBar.renderSpace(),
+ 'or',
+ AppBar.renderSpace(),
+ h('span.banner__link', {
+ onClick () {
+ global.platform.openWindow({
+ url: 'https://medium.com/metamask/74dba32cc7f7',
+ })
+ },
+ }, [
+ 'Learn More',
+ ]),
+ ])
+ )
+ }
+
+ renderAppBarAppHeader () {
+ const {
+ identities,
+ selectedAddress,
+ isUnlocked,
+ network,
+ keyrings,
+ provider,
+ } = this.props
+ const {
+ isNetworkMenuOpen,
+ isMainMenuOpen,
+ } = this.state
+
+ return (
+ h('.full-width', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '38px',
+ },
+ }, [
+ h('.app-header.flex-row.flex-space-between', {
+ style: {
+ alignItems: 'center',
+ visibility: isUnlocked ? 'visible' : 'none',
+ background: isUnlocked ? 'white' : 'none',
+ height: '38px',
+ position: 'relative',
+ zIndex: 12,
+ },
+ }, [
+ h('div.left-menu-section', {
+ style: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ }, [
+ // mini logo
+ h('img', {
+ height: 24,
+ width: 24,
+ src: './images/icon-128.png',
+ }),
+ h(NetworkIndicator, {
+ network: network,
+ provider: provider,
+ onClick: (event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
+ },
+ }),
+ ]),
+ isUnlocked && h('div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ }, [
+ h(AccountDropdowns, {
+ style: {},
+ enableAccountsSelector: true,
+ identities: identities,
+ selected: selectedAddress,
+ network,
+ keyrings,
+ }, []),
+ h(SandwichExpando, {
+ className: 'sandwich-expando',
+ width: 16,
+ barHeight: 2,
+ padding: 0,
+ isOpen: isMainMenuOpen,
+ color: 'rgb(247,146,30)',
+ onClick: () => {
+ this.setState({
+ isMainMenuOpen: !isMainMenuOpen,
+ })
+ },
+ }),
+ ]),
+ ]),
+ ])
+ )
+ }
+
+ renderNetworkDropdown () {
+ const {
+ dispatch,
+ frequentRpcList: rpcList,
+ provider,
+ } = this.props
+ const {
+ type: providerType,
+ rpcTarget: activeNetwork,
+ } = provider
+ const isOpen = this.state.isNetworkMenuOpen
+
+ return h(Dropdown, {
+ useCssTransition: true,
+ isOpen,
+ onClickOutside: (event) => {
+ const { classList } = event.target
+ const isNotToggleElement = [
+ classList.contains('menu-icon'),
+ classList.contains('network-name'),
+ classList.contains('network-indicator'),
+ ].filter(bool => bool).length === 0
+ // classes from three constituent nodes of the toggle element
+
+ if (isNotToggleElement) {
+ this.setState({ isNetworkMenuOpen: false })
+ }
+ },
+ zIndex: 11,
+ style: {
+ position: 'absolute',
+ left: '2px',
+ top: '64px',
+ },
+ innerStyle: {
+ padding: '2px 16px 2px 0px',
+ },
+ }, [
+ h(DropdownMenuItem, {
+ key: 'main',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('mainnet')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.diamond'),
+ 'Main Ethereum Network',
+ providerType === 'mainnet'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'ropsten',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('ropsten')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.red-dot'),
+ 'Ropsten Test Network',
+ providerType === 'ropsten'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'kovan',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('kovan')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.hollow-diamond'),
+ 'Kovan Test Network',
+ providerType === 'kovan'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'rinkeby',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('rinkeby')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.golden-square'),
+ 'Rinkeby Test Network',
+ providerType === 'rinkeby'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'default',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('localhost')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ 'Localhost 8545',
+ activeNetwork === LOCALHOST_RPC_URL
+ ? h('.check', '✓')
+ : null,
+ ]),
+
+ this.renderCustomOption(provider),
+ this.renderCommonRpc(rpcList, provider),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.showConfigPage()),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ 'Custom RPC',
+ activeNetwork === 'custom'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ ])
+ }
+
+ renderCustomOption ({ rpcTarget, type }) {
+ const {dispatch} = this.props
+
+ if (type !== 'rpc') {
+ return null
+ }
+
+ // Concatenate long URLs
+ let label = rpcTarget
+ if (rpcTarget.length > 31) {
+ label = label.substr(0, 34) + '...'
+ }
+
+ switch (rpcTarget) {
+ case LOCALHOST_RPC_URL:
+ return null
+ default:
+ return h(DropdownMenuItem, {
+ key: rpcTarget,
+ onClick: () => dispatch(actions.setRpcTarget(rpcTarget)),
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ label,
+ h('.check', '✓'),
+ ])
+ }
+ }
+
+ renderCommonRpc (rpcList, {rpcTarget}) {
+ const {dispatch} = this.props
+
+ return rpcList.map((rpc) => {
+ if ((rpc === LOCALHOST_RPC_URL) || (rpc === rpcTarget)) {
+ return null
+ } else {
+ return h(DropdownMenuItem, {
+ key: `common${rpc}`,
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ onClick: () => dispatch(actions.setRpcTarget(rpc)),
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ rpc,
+ rpcTarget === rpc
+ ? h('.check', '✓')
+ : null,
+ ])
+ }
+ })
+ }
+
+ renderDropdown () {
+ const {dispatch} = this.props
+ const isOpen = this.state.isMainMenuOpen
+
+ return h(Dropdown, {
+ useCssTransition: true,
+ isOpen: isOpen,
+ zIndex: 11,
+ onClickOutside: (event) => {
+ const classList = event.target.classList
+ const parentClassList = event.target.parentElement.classList
+
+ const isToggleElement = classList.contains('sandwich-expando') ||
+ parentClassList.contains('sandwich-expando')
+
+ if (isOpen && !isToggleElement) {
+ this.setState({ isMainMenuOpen: false })
+ }
+ },
+ style: {
+ position: 'absolute',
+ right: '2px',
+ top: '66px',
+ },
+ innerStyle: {},
+ }, [
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => { dispatch(actions.showConfigPage()) },
+ }, 'Settings'),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => { dispatch(actions.lockMetamask()) },
+ }, 'Log Out'),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => { dispatch(actions.showInfoPage()) },
+ }, 'Info/Help'),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => {
+ dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
+ },
+ }, 'Try Beta!'),
+ ])
+ }
+
+ render () {
+ return h('div.full-width', [
+ this.renderAppBar(),
+ this.renderNetworkDropdown(),
+ this.renderDropdown(),
+ ])
+ }
+}
diff --git a/old-ui/app/components/qr-code.js b/old-ui/app/components/qr-code.js
deleted file mode 100644
index 06b9aed9b..000000000
--- a/old-ui/app/components/qr-code.js
+++ /dev/null
@@ -1,79 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const qrCode = require('qrcode-npm').qrcode
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
-const isHexPrefixed = require('ethereumjs-util').isHexPrefixed
-const CopyButton = require('./copyButton')
-
-module.exports = connect(mapStateToProps)(QrCodeView)
-
-function mapStateToProps (state) {
- return {
- Qr: state.appState.Qr,
- buyView: state.appState.buyView,
- warning: state.appState.warning,
- }
-}
-
-inherits(QrCodeView, Component)
-
-function QrCodeView () {
- Component.call(this)
-}
-
-QrCodeView.prototype.render = function () {
- const props = this.props
- const Qr = props.Qr
- const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
- const qrImage = qrCode(4, 'M')
- qrImage.addData(address)
- qrImage.make()
- return h('.main-container.flex-column', {
- key: 'qr',
- style: {
- justifyContent: 'center',
- paddingBottom: '45px',
- paddingLeft: '45px',
- paddingRight: '45px',
- alignItems: 'center',
- },
- }, [
- Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('.qr-header', Qr.message),
-
- this.props.warning ? this.props.warning && h('span.error.flex-center', {
- style: {
- textAlign: 'center',
- width: '229px',
- height: '82px',
- },
- },
- this.props.warning) : null,
-
- h('#qr-container.flex-column', {
- style: {
- marginTop: '25px',
- marginBottom: '15px',
- },
- dangerouslySetInnerHTML: {
- __html: qrImage.createTableTag(4),
- },
- }),
- h('.flex-row', [
- h('h3.ellip-address', {
- style: {
- width: '247px',
- },
- }, Qr.data),
- h(CopyButton, {
- value: Qr.data,
- }),
- ]),
- ])
-}
-
-QrCodeView.prototype.renderMultiMessage = function () {
- var Qr = this.props.Qr
- var multiMessage = Qr.message.map((message) => h('.qr-message', message))
- return multiMessage
-}
diff --git a/old-ui/app/components/shapeshift-form.js b/old-ui/app/components/shapeshift-form.js
index 97068db0a..14de309ab 100644
--- a/old-ui/app/components/shapeshift-form.js
+++ b/old-ui/app/components/shapeshift-form.js
@@ -3,7 +3,6 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../../ui/app/actions')
-const Qr = require('./qr-code')
const isValidAddress = require('../util').isValidAddress
module.exports = connect(mapStateToProps)(ShapeshiftForm)
@@ -11,7 +10,6 @@ function mapStateToProps (state) {
return {
warning: state.appState.warning,
isSubLoading: state.appState.isSubLoading,
- qrRequested: state.appState.qrRequested,
}
}
@@ -23,7 +21,7 @@ function ShapeshiftForm () {
}
ShapeshiftForm.prototype.render = function () {
- return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
+ return this.renderMain()
}
ShapeshiftForm.prototype.renderMain = function () {
diff --git a/old-ui/app/components/transaction-list-item.js b/old-ui/app/components/transaction-list-item.js
index e9280419a..f479ce666 100644
--- a/old-ui/app/components/transaction-list-item.js
+++ b/old-ui/app/components/transaction-list-item.js
@@ -36,14 +36,23 @@ TransactionListItem.prototype.showRetryButton = function () {
return false
}
+ let currentTxIsLatest = false
const currentNonce = txParams.nonce
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
+ const currentSubmittedTxs = transactions.filter(tx => tx.status === 'submitted')
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
lastSubmittedTxWithCurrentNonce.id === transaction.id
+ if (currentSubmittedTxs.length > 0) {
+ const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
+ if (tx1.submittedTime < tx2.submittedTime) return tx1
+ return tx2
+ })
+ currentTxIsLatest = lastTx.id === transaction.id
+ }
- return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
+ return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000 && currentTxIsLatest
}
TransactionListItem.prototype.render = function () {
diff --git a/old-ui/app/css/index.css b/old-ui/app/css/index.css
index 7af713336..d209b4754 100644
--- a/old-ui/app/css/index.css
+++ b/old-ui/app/css/index.css
@@ -720,7 +720,131 @@ div.message-container > div:first-child {
transform: scale(1.1);
}
-//Notification Modal
+.new-ui-announcement {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background: white;
+ color: #4D4D4D;
+ font-family: Roboto, Arial, sans-serif;
+ padding: 1.5rem;
+}
+
+.new-ui-announcement__announcement-header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ padding-bottom: 1rem;
+}
+
+.new-ui-announcement__announcement-header a.close {
+ cursor: pointer;
+ font-size: 32px;
+ line-height: 17px;
+}
+
+.new-ui-announcement__announcement-header a.close:hover {
+ color: inherit;
+}
+
+.new-ui-announcement__announcement-header h1 {
+ color: #33A4E7;
+ text-transform: uppercase;
+ font-size: 18px;
+ font-weight: 400;
+}
+
+.new-ui-announcement__body {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ font-size: 10.5pt;
+ font-weight: 300;
+}
+
+.new-ui-announcement__body h1 {
+ font-size: 22px;
+ font-weight: 600;
+ padding-bottom: 1rem;
+}
+
+.new-ui-announcement__body a {
+ color: #33A4E7;
+}
+
+.new-ui-announcement__body .updates-list {
+ padding: .5rem 1rem;
+}
+
+.new-ui-announcement__body .updates-list h2 {
+ font-weight: 600;
+}
+
+.new-ui-announcement__body .updates-list ul {
+ list-style: disc inside;
+}
+
+.new-ui-announcement__footer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.new-ui-announcement__footer h1 {
+ font-family: inherit;
+ font-weight: 600;
+}
+
+.new-ui-announcement__footer button:hover {
+ transform: none;
+}
+
+.new-ui-announcement__footer button.positive {
+ padding: 1rem;
+ margin: 1rem;
+ background: #33A4E7;
+ color: white;
+ text-transform: uppercase;
+ box-shadow: none;
+ border-radius: 5px;
+ font-family: inherit;
+ font-size: 13px;
+ font-weight: 400;
+ width: 90%;
+}
+
+.new-ui-announcement__footer button.negative {
+ margin: 0;
+ padding: 0;
+ background: white;
+ color: #33A4E7;
+ font-family: inherit;
+ font-size: 13px;
+ font-weight: 400;
+ box-shadow: none;
+}
+
+.app-bar {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.app-bar__new-ui-banner {
+ background: #33A4E7;
+ color: white;
+ font-size: 12px;
+ line-height: 12px;
+ padding: 8px;
+ font-family: Roboto, Arial, sans-serif;
+ font-weight: 400;
+ width: 100%;
+}
+
+.banner__link {
+ cursor: pointer;
+ text-decoration: underline;
+}
.notification-modal-wrapper {
display: flex;
@@ -812,4 +936,4 @@ div.message-container > div:first-child {
.notification-modal__link {
color: #2f9ae0;
-} \ No newline at end of file
+}
diff --git a/old-ui/app/new-ui-annoucement.js b/old-ui/app/new-ui-annoucement.js
new file mode 100644
index 000000000..59b126279
--- /dev/null
+++ b/old-ui/app/new-ui-annoucement.js
@@ -0,0 +1,85 @@
+const PropTypes = require('prop-types')
+const {PureComponent} = require('react')
+const h = require('react-hyperscript')
+const actions = require('../../ui/app/actions')
+
+module.exports = class NewUiAnnouncement extends PureComponent {
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ };
+
+ close = async () => {
+ await this.props.dispatch(actions.setFeatureFlag('skipAnnounceBetaUI', true))
+ }
+
+ switchToNewUi = async () => {
+ const flag = 'betaUI'
+ const enabled = true
+ await this.props.dispatch(actions.setFeatureFlag(
+ flag,
+ enabled,
+ ))
+ await this.close()
+ global.platform.openExtensionInBrowser()
+ }
+
+ render () {
+ return (
+ h('div.new-ui-announcement', [
+ h('section.new-ui-announcement__announcement-header', [
+ h('h1', 'Announcement'),
+ h('a.close', {
+ onClick: this.close,
+ }, '×'),
+ ]),
+ h('section.new-ui-announcement__body', [
+ h('h1', 'A New Version of MetaMask'),
+ h('p', [
+ "We're excited to announce a brand-new version of MetaMask with enhanced features and functionality.",
+ ]),
+ h('div.updates-list', [
+ h('h2', 'Updates include'),
+ h('ul', [
+ h('li', 'New user interface'),
+ h('li', 'Full-screen mode'),
+ h('li', 'Better token support'),
+ h('li', 'Better gas controls'),
+ h('li', 'Advanced features for developers'),
+ h('li', 'New confirmation screens'),
+ h('li', 'And more!'),
+ ]),
+ ]),
+ h('p', [
+ 'You can still use the current version of MetaMask. The new version is still in beta, ' +
+ 'however we encourage you to try it out as we transition into this exciting new update.',
+ h('span', {
+ dangerouslySetInnerHTML: {
+ __html: '&nbsp;',
+ },
+ }),
+ h('a', {
+ href: 'https://medium.com/metamask/74dba32cc7f7',
+ onClick ({target}) {
+ const url = target.href
+ global.platform.openWindow({
+ url,
+ })
+ },
+ }, [
+ 'Learn more.',
+ ]),
+ ]),
+ ]),
+ h('section.new-ui-announcement__footer', [
+ h('h1', 'Ready to try the new MetaMask?'),
+ h('button.positive', {
+ onClick: this.switchToNewUi,
+ }, 'Try it now'),
+ h('button.negative', {
+ onClick: this.close,
+ }, 'No thanks, maybe later'),
+ ]),
+ ])
+ )
+ }
+}