aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components/dropdowns
diff options
context:
space:
mode:
authorChi Kei Chan <chikeichan@gmail.com>2017-10-19 13:58:46 +0800
committerChi Kei Chan <chikeichan@gmail.com>2017-10-19 13:58:46 +0800
commitc8c773d6416f92c520a509a9811d73f68aa00fc0 (patch)
tree495a798eeb4a5272c80d93725f5146a6fbcf85c8 /ui/app/components/dropdowns
parent5bdee96e73f65a0b369277e9c56b0afe5159e65b (diff)
parentde1da7d1b215ade650fc644adf63384a401dc240 (diff)
downloadtangerine-wallet-browser-c8c773d6416f92c520a509a9811d73f68aa00fc0.tar.gz
tangerine-wallet-browser-c8c773d6416f92c520a509a9811d73f68aa00fc0.tar.zst
tangerine-wallet-browser-c8c773d6416f92c520a509a9811d73f68aa00fc0.zip
Merge branch 'NewUI-flat' into merge
Diffstat (limited to 'ui/app/components/dropdowns')
-rw-r--r--ui/app/components/dropdowns/account-options-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/account-selection-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/components/account-dropdowns.js469
-rw-r--r--ui/app/components/dropdowns/components/dropdown.js108
-rw-r--r--ui/app/components/dropdowns/components/menu.js51
-rw-r--r--ui/app/components/dropdowns/components/network-dropdown-icon.js28
-rw-r--r--ui/app/components/dropdowns/index.js17
-rw-r--r--ui/app/components/dropdowns/network-dropdown.js316
-rw-r--r--ui/app/components/dropdowns/token-menu-dropdown.js51
9 files changed, 1098 insertions, 0 deletions
diff --git a/ui/app/components/dropdowns/account-options-dropdown.js b/ui/app/components/dropdowns/account-options-dropdown.js
new file mode 100644
index 000000000..50e793d87
--- /dev/null
+++ b/ui/app/components/dropdowns/account-options-dropdown.js
@@ -0,0 +1,29 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const AccountDropdowns = require('./components/account-dropdowns')
+
+inherits(AccountOptionsDropdown, Component)
+function AccountOptionsDropdown () {
+ Component.call(this)
+}
+
+module.exports = AccountOptionsDropdown
+
+// TODO: specify default props and proptypes
+// TODO: hook up to state, connect to redux to clean up API
+// TODO: selectedAddress is not defined... should we use selected?
+AccountOptionsDropdown.prototype.render = function () {
+ const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
+
+ return h(AccountDropdowns, {
+ enableAccountOptions: true,
+ enableAccountsSelector: false,
+ selected: selectedAddress,
+ network,
+ identities,
+ style: style || {},
+ dropdownWrapperStyle: dropdownWrapperStyle || {},
+ menuItemStyles: menuItemStyles || {},
+ }, [])
+}
diff --git a/ui/app/components/dropdowns/account-selection-dropdown.js b/ui/app/components/dropdowns/account-selection-dropdown.js
new file mode 100644
index 000000000..7a8502d18
--- /dev/null
+++ b/ui/app/components/dropdowns/account-selection-dropdown.js
@@ -0,0 +1,29 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const AccountDropdowns = require('./components/account-dropdowns')
+
+inherits(AccountSelectionDropdown, Component)
+function AccountSelectionDropdown () {
+ Component.call(this)
+}
+
+module.exports = AccountSelectionDropdown
+
+// TODO: specify default props and proptypes
+// TODO: hook up to state, connect to redux to clean up API
+// TODO: selectedAddress is not defined... should we use selected?
+AccountSelectionDropdown.prototype.render = function () {
+ const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
+
+ return h(AccountDropdowns, {
+ enableAccountOptions: false,
+ enableAccountsSelector: true,
+ selected: selectedAddress,
+ network,
+ identities,
+ style: style || {},
+ dropdownWrapperStyle: dropdownWrapperStyle || {},
+ menuItemStyles: menuItemStyles || {},
+ }, [])
+}
diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js
new file mode 100644
index 000000000..e2eed1e4b
--- /dev/null
+++ b/ui/app/components/dropdowns/components/account-dropdowns.js
@@ -0,0 +1,469 @@
+const Component = require('react').Component
+const PropTypes = require('react').PropTypes
+const h = require('react-hyperscript')
+const actions = require('../../../actions')
+const genAccountLink = require('../../../../lib/account-link.js')
+const connect = require('react-redux').connect
+const Dropdown = require('./dropdown').Dropdown
+const DropdownMenuItem = require('./dropdown').DropdownMenuItem
+const Identicon = require('../../identicon')
+const ethUtil = require('ethereumjs-util')
+const copyToClipboard = require('copy-to-clipboard')
+const { formatBalance } = require('../../../util')
+
+class AccountDropdowns extends Component {
+ constructor (props) {
+ super(props)
+ this.state = {
+ accountSelectorActive: false,
+ optionsMenuActive: false,
+ }
+ // Used for orangeaccount selector icon
+ // this.accountSelectorToggleClassName = 'accounts-selector'
+ this.accountSelectorToggleClassName = 'fa-angle-down'
+ this.optionsMenuToggleClassName = 'fa-ellipsis-h'
+ }
+
+ renderAccounts () {
+ const { identities, accounts, selected, menuItemStyles, actions, keyrings } = this.props
+
+ return Object.keys(identities).map((key, index) => {
+ const identity = identities[key]
+ const isSelected = identity.address === selected
+
+ const balanceValue = accounts[key].balance
+ const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
+ const simpleAddress = identity.address.substring(2).toLowerCase()
+
+ const keyring = keyrings.find((kr) => {
+ return kr.accounts.includes(simpleAddress) ||
+ kr.accounts.includes(identity.address)
+ })
+
+ return h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ onClick: () => {
+ this.props.actions.showAccountDetail(identity.address)
+ },
+ style: Object.assign(
+ {
+ marginTop: index === 0 ? '5px' : '',
+ fontSize: '24px',
+ width: '260px',
+ },
+ menuItemStyles,
+ ),
+ },
+ [
+ h('div.flex-row.flex-center', {}, [
+
+ h('span', {
+ style: {
+ flex: '1 1 0',
+ minWidth: '20px',
+ minHeight: '30px',
+ },
+ }, [
+ h('span', {
+ style: {
+ flex: '1 1 auto',
+ fontSize: '14px',
+ },
+ }, isSelected ? h('i.fa.fa-check') : null),
+ ]),
+
+ h(
+ Identicon,
+ {
+ address: identity.address,
+ diameter: 24,
+ style: {
+ flex: '1 1 auto',
+ marginLeft: '10px',
+ },
+ },
+ ),
+
+ h('span.flex-column', {
+ style: {
+ flex: '10 10 auto',
+ width: '175px',
+ alignItems: 'flex-start',
+ justifyContent: 'center',
+ marginLeft: '10px',
+ position: 'relative',
+ },
+ }, [
+ this.indicateIfLoose(keyring),
+ h('span.account-dropdown-name', {
+ style: {
+ fontSize: '18px',
+ maxWidth: '145px',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ },
+ }, identity.name || ''),
+
+ h('span.account-dropdown-balance', {
+ style: {
+ fontSize: '14px',
+ fontFamily: 'Avenir',
+ fontWeight: 500,
+ },
+ }, formattedBalance),
+ ]),
+
+ h('span', {
+ style: {
+ flex: '3 3 auto',
+ },
+ }, [
+ h('span.account-dropdown-edit-button', {
+ style: {
+ fontSize: '16px',
+ },
+ onClick: () => {
+ actions.showEditAccountModal(identity)
+ },
+ }, [
+ 'Edit',
+ ]),
+ ]),
+
+ ]),
+// =======
+// },
+// ),
+// this.indicateIfLoose(keyring),
+// h('span', {
+// style: {
+// marginLeft: '20px',
+// fontSize: '24px',
+// maxWidth: '145px',
+// whiteSpace: 'nowrap',
+// overflow: 'hidden',
+// textOverflow: 'ellipsis',
+// },
+// }, identity.name || ''),
+// h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
+// >>>>>>> master:ui/app/components/account-dropdowns.js
+ ]
+ )
+ })
+ }
+
+ indicateIfLoose (keyring) {
+ try { // Sometimes keyrings aren't loaded yet:
+ const type = keyring.type
+ const isLoose = type !== 'HD Key Tree'
+ return isLoose ? h('.keyring-label', 'LOOSE') : null
+ } catch (e) { return }
+ }
+
+ renderAccountSelector () {
+ const { actions, useCssTransition, innerStyle, sidebarOpen } = this.props
+ const { accountSelectorActive, menuItemStyles } = this.state
+
+ return h(
+ Dropdown,
+ {
+ useCssTransition,
+ style: {
+ marginLeft: '-185px',
+ marginTop: '50px',
+ minWidth: '180px',
+ overflowY: 'auto',
+ maxHeight: '300px',
+ width: '300px',
+ },
+ innerStyle,
+ isOpen: accountSelectorActive,
+ onClickOutside: (event) => {
+ const { classList } = event.target
+ const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName)
+ if (accountSelectorActive && isNotToggleElement) {
+ this.setState({ accountSelectorActive: false })
+ }
+ },
+ },
+ [
+ ...this.renderAccounts(),
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ style: Object.assign(
+ {},
+ menuItemStyles,
+ ),
+ onClick: () => actions.showNewAccountModal(),
+ },
+ [
+ h(
+ 'i.fa.fa-plus.fa-lg',
+ {
+ style: {
+ marginLeft: '8px',
+ },
+ }
+ ),
+ h('span', {
+ style: {
+ marginLeft: '14px',
+ fontFamily: 'DIN OT',
+ fontSize: '16px',
+ lineHeight: '23px',
+ },
+ }, 'Create Account'),
+ ],
+ ),
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {
+ if (sidebarOpen) {
+ actions.hideSidebar()
+ }
+ },
+ onClick: () => actions.showImportPage(),
+ style: Object.assign(
+ {},
+ menuItemStyles,
+ ),
+ },
+ [
+ h(
+ 'i.fa.fa-download.fa-lg',
+ {
+ style: {
+ marginLeft: '8px',
+ },
+ }
+ ),
+ h('span', {
+ style: {
+ marginLeft: '20px',
+ marginBottom: '5px',
+ fontFamily: 'DIN OT',
+ fontSize: '16px',
+ lineHeight: '23px',
+ },
+ }, 'Import Account'),
+ ]
+ ),
+ ]
+ )
+ }
+
+ renderAccountOptions () {
+ const { actions, dropdownWrapperStyle, useCssTransition } = this.props
+ const { optionsMenuActive, menuItemStyles } = this.state
+ const dropdownMenuItemStyle = {
+ fontFamily: 'DIN OT',
+ fontSize: 16,
+ lineHeight: '24px',
+ padding: '8px',
+ }
+
+ return h(
+ Dropdown,
+ {
+ useCssTransition,
+ style: Object.assign(
+ {
+ marginLeft: '-10px',
+ position: 'absolute',
+ width: '29vh', // affects both mobile and laptop views
+ },
+ dropdownWrapperStyle,
+ ),
+ isOpen: optionsMenuActive,
+ onClickOutside: () => {
+ const { classList } = event.target
+ const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
+ if (optionsMenuActive && isNotToggleElement) {
+ this.setState({ optionsMenuActive: false })
+ }
+ },
+ },
+ [
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ onClick: () => {
+ this.props.actions.showAccountDetailModal()
+ },
+ style: Object.assign(
+ dropdownMenuItemStyle,
+ menuItemStyles,
+ ),
+ },
+ 'Account Details',
+ ),
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ onClick: () => {
+ const { selected, network } = this.props
+ const url = genAccountLink(selected, network)
+ global.platform.openWindow({ url })
+ },
+ style: Object.assign(
+ dropdownMenuItemStyle,
+ menuItemStyles,
+ ),
+ },
+ 'View account on Etherscan',
+ ),
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ onClick: () => {
+ const { selected } = this.props
+ const checkSumAddress = selected && ethUtil.toChecksumAddress(selected)
+ copyToClipboard(checkSumAddress)
+ },
+ style: Object.assign(
+ dropdownMenuItemStyle,
+ menuItemStyles,
+ ),
+ },
+ 'Copy Address to clipboard',
+ ),
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ onClick: () => this.props.actions.showExportPrivateKeyModal(),
+ style: Object.assign(
+ dropdownMenuItemStyle,
+ menuItemStyles,
+ ),
+ },
+ 'Export Private Key',
+ ),
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => {},
+ onClick: () => {
+ actions.hideSidebar()
+ actions.showAddTokenPage()
+ },
+ style: Object.assign(
+ dropdownMenuItemStyle,
+ menuItemStyles,
+ ),
+ },
+ 'Add Token',
+ ),
+
+ ]
+ )
+ }
+
+ render () {
+ const { style, enableAccountsSelector, enableAccountOptions } = this.props
+ const { optionsMenuActive, accountSelectorActive } = this.state
+
+ return h(
+ 'span',
+ {
+ style: style,
+ },
+ [
+ enableAccountsSelector && h(
+ 'i.fa.fa-angle-down',
+ {
+ style: {
+ cursor: 'pointer',
+ },
+ onClick: (event) => {
+ event.stopPropagation()
+ this.setState({
+ accountSelectorActive: !accountSelectorActive,
+ optionsMenuActive: false,
+ })
+ },
+ },
+ this.renderAccountSelector(),
+ ),
+ enableAccountOptions && h(
+ 'i.fa.fa-ellipsis-h',
+ {
+ style: {
+ fontSize: '135%',
+ cursor: 'pointer',
+ },
+ onClick: (event) => {
+ event.stopPropagation()
+ this.setState({
+ accountSelectorActive: false,
+ optionsMenuActive: !optionsMenuActive,
+ })
+ },
+ },
+ this.renderAccountOptions()
+ ),
+ ]
+ )
+ }
+}
+
+AccountDropdowns.defaultProps = {
+ enableAccountsSelector: false,
+ enableAccountOptions: false,
+}
+
+AccountDropdowns.propTypes = {
+ identities: PropTypes.objectOf(PropTypes.object),
+ selected: PropTypes.string,
+ keyrings: PropTypes.array,
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ actions: {
+ hideSidebar: () => dispatch(actions.hideSidebar()),
+ showConfigPage: () => dispatch(actions.showConfigPage()),
+ showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
+ showAccountDetailModal: () => {
+ dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
+ },
+ showEditAccountModal: (identity) => {
+ dispatch(actions.showModal({
+ name: 'EDIT_ACCOUNT_NAME',
+ identity,
+ }))
+ },
+ showNewAccountModal: () => {
+ dispatch(actions.showModal({ name: 'NEW_ACCOUNT' }))
+ },
+ showExportPrivateKeyModal: () => {
+ dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
+ },
+ showAddTokenPage: () => {
+ dispatch(actions.showAddTokenPage())
+ },
+ addNewAccount: () => dispatch(actions.addNewAccount()),
+ showImportPage: () => dispatch(actions.showImportPage()),
+ showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
+ },
+ }
+}
+
+function mapStateToProps (state) {
+ return {
+ keyrings: state.metamask.keyrings,
+ sidebarOpen: state.appState.sidebarOpen,
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns)
+
diff --git a/ui/app/components/dropdowns/components/dropdown.js b/ui/app/components/dropdowns/components/dropdown.js
new file mode 100644
index 000000000..991c89cb8
--- /dev/null
+++ b/ui/app/components/dropdowns/components/dropdown.js
@@ -0,0 +1,108 @@
+const Component = require('react').Component
+const PropTypes = require('react').PropTypes
+const h = require('react-hyperscript')
+const MenuDroppo = require('../../menu-droppo')
+const extend = require('xtend')
+
+const noop = () => {}
+
+class Dropdown extends Component {
+ render () {
+ const {
+ containerClassName,
+ isOpen,
+ onClickOutside,
+ style,
+ innerStyle,
+ children,
+ useCssTransition,
+ } = this.props
+
+ const innerStyleDefaults = extend({
+ borderRadius: '4px',
+ padding: '8px 16px',
+ background: 'rgba(0, 0, 0, 0.8)',
+ boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px',
+ }, innerStyle)
+
+ return h(
+ MenuDroppo,
+ {
+ containerClassName,
+ useCssTransition,
+ isOpen,
+ zIndex: 30,
+ onClickOutside,
+ style,
+ innerStyle: innerStyleDefaults,
+ },
+ [
+ h(
+ 'style',
+ `
+ li.dropdown-menu-item:hover {
+ color:rgb(225, 225, 225);
+ background-color: rgba(255, 255, 255, 0.05);
+ border-radius: 4px;
+ }
+ li.dropdown-menu-item { color: rgb(185, 185, 185); }
+ `
+ ),
+ ...children,
+ ]
+ )
+ }
+}
+
+Dropdown.defaultProps = {
+ isOpen: false,
+ onClick: noop,
+ useCssTransition: false,
+}
+
+Dropdown.propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ onClick: PropTypes.func.isRequired,
+ children: PropTypes.node,
+ style: PropTypes.object.isRequired,
+}
+
+class DropdownMenuItem extends Component {
+ render () {
+ const { onClick, closeMenu, children, style } = this.props
+
+ return h(
+ 'li.dropdown-menu-item',
+ {
+ onClick: () => {
+ onClick()
+ closeMenu()
+ },
+ style: Object.assign({
+ listStyle: 'none',
+ padding: '8px 0px',
+ fontSize: '18px',
+ fontStyle: 'normal',
+ fontFamily: 'Montserrat Regular',
+ cursor: 'pointer',
+ display: 'flex',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ color: 'white',
+ }, style),
+ },
+ children
+ )
+ }
+}
+
+DropdownMenuItem.propTypes = {
+ closeMenu: PropTypes.func.isRequired,
+ onClick: PropTypes.func.isRequired,
+ children: PropTypes.node,
+}
+
+module.exports = {
+ Dropdown,
+ DropdownMenuItem,
+}
diff --git a/ui/app/components/dropdowns/components/menu.js b/ui/app/components/dropdowns/components/menu.js
new file mode 100644
index 000000000..f6d8a139e
--- /dev/null
+++ b/ui/app/components/dropdowns/components/menu.js
@@ -0,0 +1,51 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+
+inherits(Menu, Component)
+function Menu () { Component.call(this) }
+
+Menu.prototype.render = function () {
+ const { className = '', children, isShowing } = this.props
+ return isShowing
+ ? h('div', { className: `menu ${className}` }, children)
+ : h('noscript')
+}
+
+inherits(Item, Component)
+function Item () { Component.call(this) }
+
+Item.prototype.render = function () {
+ const {
+ icon,
+ children,
+ text,
+ className = '',
+ onClick,
+ } = this.props
+ const itemClassName = `menu__item ${className} ${onClick ? 'menu__item--clickable' : ''}`
+ const iconComponent = icon ? h('div.menu__item__icon', [icon]) : null
+ const textComponent = text ? h('div.menu__item__text', text) : null
+
+ return children
+ ? h('div', { className: itemClassName, onClick }, children)
+ : h('div.menu__item', { className: itemClassName, onClick }, [ iconComponent, textComponent ]
+ .filter(d => Boolean(d))
+ )
+}
+
+inherits(Divider, Component)
+function Divider () { Component.call(this) }
+
+Divider.prototype.render = function () {
+ return h('div.menu__divider')
+}
+
+inherits(CloseArea, Component)
+function CloseArea () { Component.call(this) }
+
+CloseArea.prototype.render = function () {
+ return h('div.menu__close-area', { onClick: this.props.onClick })
+}
+
+module.exports = { Menu, Item, Divider, CloseArea }
diff --git a/ui/app/components/dropdowns/components/network-dropdown-icon.js b/ui/app/components/dropdowns/components/network-dropdown-icon.js
new file mode 100644
index 000000000..7e94e0af5
--- /dev/null
+++ b/ui/app/components/dropdowns/components/network-dropdown-icon.js
@@ -0,0 +1,28 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+
+
+inherits(NetworkDropdownIcon, Component)
+module.exports = NetworkDropdownIcon
+
+function NetworkDropdownIcon () {
+ Component.call(this)
+}
+
+NetworkDropdownIcon.prototype.render = function () {
+ const {
+ backgroundColor,
+ isSelected,
+ innerBorder = 'none',
+ } = this.props
+
+ return h(`.menu-icon-circle${isSelected ? '--active' : ''}`, {},
+ h('div', {
+ style: {
+ background: backgroundColor,
+ border: innerBorder,
+ },
+ })
+ )
+}
diff --git a/ui/app/components/dropdowns/index.js b/ui/app/components/dropdowns/index.js
new file mode 100644
index 000000000..fa66f5000
--- /dev/null
+++ b/ui/app/components/dropdowns/index.js
@@ -0,0 +1,17 @@
+// Reusable Dropdown Components
+// TODO: Refactor into separate components
+const Dropdown = require('./components/dropdown').Dropdown
+const AccountDropdowns = require('./components/account-dropdowns')
+
+// App-Specific Instances
+const AccountSelectionDropdown = require('./account-selection-dropdown')
+const AccountOptionsDropdown = require('./account-options-dropdown')
+const NetworkDropdown = require('./network-dropdown').default
+
+module.exports = {
+ AccountSelectionDropdown,
+ AccountOptionsDropdown,
+ NetworkDropdown,
+ Dropdown,
+ AccountDropdowns,
+}
diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js
new file mode 100644
index 000000000..736019c39
--- /dev/null
+++ b/ui/app/components/dropdowns/network-dropdown.js
@@ -0,0 +1,316 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const connect = require('react-redux').connect
+const actions = require('../../actions')
+const Dropdown = require('./components/dropdown').Dropdown
+const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
+const NetworkDropdownIcon = require('./components/network-dropdown-icon')
+
+function mapStateToProps (state) {
+ return {
+ provider: state.metamask.provider,
+ frequentRpcList: state.metamask.frequentRpcList || [],
+ networkDropdownOpen: state.appState.networkDropdownOpen,
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ hideModal: () => {
+ dispatch(actions.hideModal())
+ },
+ setProviderType: (type) => {
+ dispatch(actions.setProviderType(type))
+ },
+ setDefaultRpcTarget: type => {
+ dispatch(actions.setDefaultRpcTarget(type))
+ },
+ setRpcTarget: (target) => {
+ dispatch(actions.setRpcTarget(target))
+ },
+ showConfigPage: () => {
+ dispatch(actions.showConfigPage())
+ },
+ showNetworkDropdown: () => { dispatch(actions.showNetworkDropdown()) },
+ hideNetworkDropdown: () => { dispatch(actions.hideNetworkDropdown()) },
+ }
+}
+
+
+inherits(NetworkDropdown, Component)
+function NetworkDropdown () {
+ Component.call(this)
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(NetworkDropdown)
+
+// TODO: specify default props and proptypes
+NetworkDropdown.prototype.render = function () {
+ const props = this.props
+ const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
+ const rpcList = props.frequentRpcList
+ const isOpen = this.props.networkDropdownOpen
+ const dropdownMenuItemStyle = {
+ fontFamily: 'DIN OT',
+ fontSize: '16px',
+ lineHeight: '20px',
+ padding: '12px 0',
+ }
+
+ 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.props.hideNetworkDropdown()
+ }
+ },
+ containerClassName: 'network-droppo',
+ zIndex: 11,
+ style: {
+ position: 'absolute',
+ top: '38px',
+ minWidth: '309px',
+ },
+ innerStyle: {
+ padding: '18px 8px',
+ },
+ }, [
+
+ h('div.network-dropdown-header', {}, [
+ h('div.network-dropdown-title', {}, 'Networks'),
+
+ h('div.network-dropdown-divider'),
+
+ h('div.network-dropdown-content',
+ {},
+ 'The default network for Ether transactions is Main Net.'
+ ),
+ ]),
+
+ h(
+ DropdownMenuItem,
+ {
+ key: 'main',
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ onClick: () => props.setProviderType('mainnet'),
+ style: { ...dropdownMenuItemStyle, borderColor: '#038789' },
+ },
+ [
+ providerType === 'mainnet' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h(NetworkDropdownIcon, {
+ backgroundColor: '#038789', // $blue-lagoon
+ isSelected: providerType === 'mainnet',
+ }),
+ h('span.network-name-item', {
+ style: {
+ color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b',
+ },
+ }, 'Main Ethereum Network'),
+ ]
+ ),
+
+ h(
+ DropdownMenuItem,
+ {
+ key: 'ropsten',
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ onClick: () => props.setProviderType('ropsten'),
+ style: dropdownMenuItemStyle,
+ },
+ [
+ providerType === 'ropsten' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h(NetworkDropdownIcon, {
+ backgroundColor: '#e91550', // $crimson
+ isSelected: providerType === 'ropsten',
+ }),
+ h('span.network-name-item', {
+ style: {
+ color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b',
+ },
+ }, 'Ropsten Test Network'),
+ ]
+ ),
+
+ h(
+ DropdownMenuItem,
+ {
+ key: 'kovan',
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ onClick: () => props.setProviderType('kovan'),
+ style: dropdownMenuItemStyle,
+ },
+ [
+ providerType === 'kovan' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h(NetworkDropdownIcon, {
+ backgroundColor: '#690496', // $purple
+ isSelected: providerType === 'kovan',
+ }),
+ h('span.network-name-item', {
+ style: {
+ color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b',
+ },
+ }, 'Kovan Test Network'),
+ ]
+ ),
+
+ h(
+ DropdownMenuItem,
+ {
+ key: 'rinkeby',
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ onClick: () => props.setProviderType('rinkeby'),
+ style: dropdownMenuItemStyle,
+ },
+ [
+ providerType === 'rinkeby' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h(NetworkDropdownIcon, {
+ backgroundColor: '#ebb33f', // $tulip-tree
+ isSelected: providerType === 'rinkeby',
+ }),
+ h('span.network-name-item', {
+ style: {
+ color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b',
+ },
+ }, 'Rinkeby Test Network'),
+ ]
+ ),
+
+ h(
+ DropdownMenuItem,
+ {
+ key: 'default',
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ onClick: () => props.setRpcTarget('http://localhost:8545'),
+ style: dropdownMenuItemStyle,
+ },
+ [
+ activeNetwork === 'http://localhost:8545' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h(NetworkDropdownIcon, {
+ isSelected: activeNetwork === 'http://localhost:8545',
+ innerBorder: '1px solid #9b9b9b',
+ }),
+ h('span.network-name-item', {
+ style: {
+ color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b',
+ },
+ }, 'Localhost 8545'),
+ ]
+ ),
+
+ this.renderCustomOption(props.provider),
+ this.renderCommonRpc(rpcList, props.provider),
+
+ h(
+ DropdownMenuItem,
+ {
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ onClick: () => this.props.showConfigPage(),
+ style: dropdownMenuItemStyle,
+ },
+ [
+ activeNetwork === 'custom' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h(NetworkDropdownIcon, {
+ isSelected: activeNetwork === 'custom',
+ innerBorder: '1px solid #9b9b9b',
+ }),
+ h('span.network-name-item', {
+ style: {
+ color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b',
+ },
+ }, 'Custom RPC'),
+ ]
+ ),
+
+ ])
+}
+
+
+NetworkDropdown.prototype.getNetworkName = function () {
+ const { provider } = this.props
+ const providerName = provider.type
+
+ let name
+
+ if (providerName === 'mainnet') {
+ name = 'Main Ethereum Network'
+ } else if (providerName === 'ropsten') {
+ name = 'Ropsten Test Network'
+ } else if (providerName === 'kovan') {
+ name = 'Kovan Test Network'
+ } else if (providerName === 'rinkeby') {
+ name = 'Rinkeby Test Network'
+ } else {
+ name = 'Unknown Private Network'
+ }
+
+ return name
+}
+
+NetworkDropdown.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.props.hideNetworkDropdown(),
+ onClick: () => props.setRpcTarget(rpc),
+ },
+ [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ rpc,
+ rpcTarget === rpc ? h('.check', '✓') : null,
+ ]
+ )
+ }
+ })
+}
+
+NetworkDropdown.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.setRpcTarget(rpcTarget),
+ closeMenu: () => this.props.hideNetworkDropdown(),
+ },
+ [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ label,
+ h('.check', '✓'),
+ ]
+ )
+ }
+}
diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js
new file mode 100644
index 000000000..7234a9b21
--- /dev/null
+++ b/ui/app/components/dropdowns/token-menu-dropdown.js
@@ -0,0 +1,51 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const connect = require('react-redux').connect
+const actions = require('../../actions')
+
+module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown)
+
+function mapDispatchToProps (dispatch) {
+ return {
+ showHideTokenConfirmationModal: (token) => {
+ dispatch(actions.showModal({ name: 'HIDE_TOKEN_CONFIRMATION', token }))
+ }
+ }
+}
+
+
+inherits(TokenMenuDropdown, Component)
+function TokenMenuDropdown () {
+ Component.call(this)
+
+ this.onClose = this.onClose.bind(this)
+}
+
+TokenMenuDropdown.prototype.onClose = function (e) {
+ e.stopPropagation()
+ this.props.onClose()
+}
+
+TokenMenuDropdown.prototype.render = function () {
+ const { showHideTokenConfirmationModal } = this.props
+
+ return h('div.token-menu-dropdown', {}, [
+ h('div.token-menu-dropdown__close-area', {
+ onClick: this.onClose,
+ }),
+ h('div.token-menu-dropdown__container', {}, [
+ h('div.token-menu-dropdown__options', {}, [
+
+ h('div.token-menu-dropdown__option', {
+ onClick: (e) => {
+ e.stopPropagation()
+ showHideTokenConfirmationModal(this.props.token)
+ this.props.onClose()
+ },
+ }, 'Hide Token')
+
+ ]),
+ ]),
+ ])
+}