aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components/account-menu/account-menu.component.js
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components/account-menu/account-menu.component.js')
-rw-r--r--ui/app/components/account-menu/account-menu.component.js301
1 files changed, 301 insertions, 0 deletions
diff --git a/ui/app/components/account-menu/account-menu.component.js b/ui/app/components/account-menu/account-menu.component.js
new file mode 100644
index 000000000..b2fec647a
--- /dev/null
+++ b/ui/app/components/account-menu/account-menu.component.js
@@ -0,0 +1,301 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import debounce from 'lodash.debounce'
+import { Menu, Item, Divider, CloseArea } from '../dropdowns/components/menu'
+import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums'
+import { getEnvironmentType } from '../../../../app/scripts/lib/util'
+import Tooltip from '../tooltip'
+import Identicon from '../identicon'
+import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
+import { PRIMARY } from '../../constants/common'
+import {
+ SETTINGS_ROUTE,
+ INFO_ROUTE,
+ NEW_ACCOUNT_ROUTE,
+ IMPORT_ACCOUNT_ROUTE,
+ CONNECT_HARDWARE_ROUTE,
+ DEFAULT_ROUTE,
+} from '../../routes'
+
+export default class AccountMenu extends PureComponent {
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ static propTypes = {
+ accounts: PropTypes.object,
+ history: PropTypes.object,
+ identities: PropTypes.object,
+ isAccountMenuOpen: PropTypes.bool,
+ keyrings: PropTypes.array,
+ lockMetamask: PropTypes.func,
+ selectedAddress: PropTypes.string,
+ showAccountDetail: PropTypes.func,
+ showRemoveAccountConfirmationModal: PropTypes.func,
+ toggleAccountMenu: PropTypes.func,
+ }
+
+ state = {
+ atAccountListBottom: false,
+ }
+
+ componentDidUpdate (prevProps) {
+ const { prevIsAccountMenuOpen } = prevProps
+ const { isAccountMenuOpen } = this.props
+
+ if (!prevIsAccountMenuOpen && isAccountMenuOpen) {
+ this.setAtAccountListBottom()
+ }
+ }
+
+ renderAccounts () {
+ const {
+ identities,
+ accounts,
+ selectedAddress,
+ keyrings,
+ showAccountDetail,
+ } = this.props
+
+ const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
+
+ return accountOrder.filter(address => !!identities[address]).map(address => {
+ const identity = identities[address]
+ const isSelected = identity.address === selectedAddress
+
+ const balanceValue = accounts[address] ? accounts[address].balance : ''
+ const simpleAddress = identity.address.substring(2).toLowerCase()
+
+ const keyring = keyrings.find(kr => {
+ return kr.accounts.includes(simpleAddress) || kr.accounts.includes(identity.address)
+ })
+
+ return (
+ <div
+ className="account-menu__account menu__item--clickable"
+ onClick={() => showAccountDetail(identity.address)}
+ key={identity.address}
+ >
+ <div className="account-menu__check-mark">
+ { isSelected && <div className="account-menu__check-mark-icon" /> }
+ </div>
+ <Identicon
+ address={identity.address}
+ diameter={24}
+ />
+ <div className="account-menu__account-info">
+ <div className="account-menu__name">
+ { identity.name || '' }
+ </div>
+ <UserPreferencedCurrencyDisplay
+ className="account-menu__balance"
+ value={balanceValue}
+ type={PRIMARY}
+ />
+ </div>
+ { this.renderKeyringType(keyring) }
+ { this.renderRemoveAccount(keyring, identity) }
+ </div>
+ )
+ })
+ }
+
+ renderRemoveAccount (keyring, identity) {
+ const { t } = this.context
+ // Any account that's not from the HD wallet Keyring can be removed
+ const { type } = keyring
+ const isRemovable = type !== 'HD Key Tree'
+
+ return isRemovable && (
+ <Tooltip
+ title={t('removeAccount')}
+ position="bottom"
+ >
+ <a
+ className="remove-account-icon"
+ onClick={e => this.removeAccount(e, identity)}
+ />
+ </Tooltip>
+ )
+ }
+
+ removeAccount (e, identity) {
+ e.preventDefault()
+ e.stopPropagation()
+ const { showRemoveAccountConfirmationModal } = this.props
+ showRemoveAccountConfirmationModal(identity)
+ }
+
+ renderKeyringType (keyring) {
+ const { t } = this.context
+
+ // Sometimes keyrings aren't loaded yet
+ if (!keyring) {
+ return null
+ }
+
+ const { type } = keyring
+ let label
+
+ switch (type) {
+ case 'Trezor Hardware':
+ case 'Ledger Hardware':
+ label = t('hardware')
+ break
+ case 'Simple Key Pair':
+ label = t('imported')
+ break
+ }
+
+ return label && (
+ <div className="keyring-label allcaps">
+ { label }
+ </div>
+ )
+ }
+
+ setAtAccountListBottom = () => {
+ const target = document.querySelector('.account-menu__accounts')
+ const { scrollTop, offsetHeight, scrollHeight } = target
+ const atAccountListBottom = scrollTop + offsetHeight >= scrollHeight
+ this.setState({ atAccountListBottom })
+ }
+
+ onScroll = debounce(this.setAtAccountListBottom, 25)
+
+ handleScrollDown = e => {
+ e.stopPropagation()
+ const target = document.querySelector('.account-menu__accounts')
+ const { scrollHeight } = target
+ target.scroll({ left: 0, top: scrollHeight, behavior: 'smooth' })
+ this.setAtAccountListBottom()
+ }
+
+ renderScrollButton () {
+ const { accounts } = this.props
+ const { atAccountListBottom } = this.state
+
+ return !atAccountListBottom && Object.keys(accounts).length > 3 && (
+ <div
+ className="account-menu__scroll-button"
+ onClick={this.handleScrollDown}
+ >
+ <img
+ src="./images/icons/down-arrow.svg"
+ width={28}
+ height={28}
+ />
+ </div>
+ )
+ }
+
+ render () {
+ const { t } = this.context
+ const {
+ isAccountMenuOpen,
+ toggleAccountMenu,
+ lockMetamask,
+ history,
+ } = this.props
+
+ return (
+ <Menu
+ className="account-menu"
+ isShowing={isAccountMenuOpen}
+ >
+ <CloseArea onClick={toggleAccountMenu} />
+ <Item className="account-menu__header">
+ { t('myAccounts') }
+ <button
+ className="account-menu__logout-button"
+ onClick={() => {
+ lockMetamask()
+ history.push(DEFAULT_ROUTE)
+ }}
+ >
+ { t('logout') }
+ </button>
+ </Item>
+ <Divider />
+ <div className="account-menu__accounts-container">
+ <div
+ className="account-menu__accounts"
+ onScroll={this.onScroll}
+ >
+ { this.renderAccounts() }
+ </div>
+ { this.renderScrollButton() }
+ </div>
+ <Divider />
+ <Item
+ onClick={() => {
+ toggleAccountMenu()
+ history.push(NEW_ACCOUNT_ROUTE)
+ }}
+ icon={
+ <img
+ className="account-menu__item-icon"
+ src="images/plus-btn-white.svg"
+ />
+ }
+ text={t('createAccount')}
+ />
+ <Item
+ onClick={() => {
+ toggleAccountMenu()
+ history.push(IMPORT_ACCOUNT_ROUTE)
+ }}
+ icon={
+ <img
+ className="account-menu__item-icon"
+ src="images/import-account.svg"
+ />
+ }
+ text={t('importAccount')}
+ />
+ <Item
+ onClick={() => {
+ toggleAccountMenu()
+
+ if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
+ global.platform.openExtensionInBrowser(CONNECT_HARDWARE_ROUTE)
+ } else {
+ history.push(CONNECT_HARDWARE_ROUTE)
+ }
+ }}
+ icon={
+ <img
+ className="account-menu__item-icon"
+ src="images/connect-icon.svg"
+ />
+ }
+ text={t('connectHardwareWallet')}
+ />
+ <Divider />
+ <Item
+ onClick={() => {
+ toggleAccountMenu()
+ history.push(INFO_ROUTE)
+ }}
+ icon={
+ <img src="images/mm-info-icon.svg" />
+ }
+ text={t('infoHelp')}
+ />
+ <Item
+ onClick={() => {
+ toggleAccountMenu()
+ history.push(SETTINGS_ROUTE)
+ }}
+ icon={
+ <img
+ className="account-menu__item-icon"
+ src="images/settings.svg"
+ />
+ }
+ text={t('settings')}
+ />
+ </Menu>
+ )
+ }
+}