mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
The entry for imported accounts in the account menu looked wrong with the new connected site icon - there was no padding between the site icon and the 'imported' label. The entry was pretty crowded with these three symbols as well (the third being the 'x' used to remove the account). The site icon has been made the right-most icon, so that it lines up with the site icons shown for other accounts, and spacing has been added between the site icon and the 'imported' label. The 'x' used to remove accounts has been removed. Accounts can still be removed from the 'Account Options' menu on the Home screen. This seemed like the wrong place for this button to exist, as it's the only action that can be taken from that menu aside from navigation.
412 lines
11 KiB
JavaScript
412 lines
11 KiB
JavaScript
import React, { Component } from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import { debounce } from 'lodash'
|
|
import Fuse from 'fuse.js'
|
|
import InputAdornment from '@material-ui/core/InputAdornment'
|
|
|
|
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 Identicon from '../../ui/identicon'
|
|
import IconWithFallBack from '../../ui/icon-with-fallback'
|
|
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
|
import { PRIMARY } from '../../../helpers/constants/common'
|
|
import {
|
|
SETTINGS_ROUTE,
|
|
ABOUT_US_ROUTE,
|
|
NEW_ACCOUNT_ROUTE,
|
|
IMPORT_ACCOUNT_ROUTE,
|
|
CONNECT_HARDWARE_ROUTE,
|
|
DEFAULT_ROUTE,
|
|
} from '../../../helpers/constants/routes'
|
|
import TextField from '../../ui/text-field'
|
|
import SearchIcon from '../../ui/search-icon'
|
|
|
|
export default class AccountMenu extends Component {
|
|
static contextTypes = {
|
|
t: PropTypes.func,
|
|
metricsEvent: PropTypes.func,
|
|
}
|
|
|
|
static propTypes = {
|
|
shouldShowAccountsSearch: PropTypes.bool,
|
|
accounts: PropTypes.array,
|
|
history: PropTypes.object,
|
|
isAccountMenuOpen: PropTypes.bool,
|
|
keyrings: PropTypes.array,
|
|
lockMetamask: PropTypes.func,
|
|
selectedAddress: PropTypes.string,
|
|
showAccountDetail: PropTypes.func,
|
|
toggleAccountMenu: PropTypes.func,
|
|
addressConnectedDomainMap: PropTypes.object,
|
|
originOfCurrentTab: PropTypes.string,
|
|
}
|
|
|
|
accountsRef
|
|
|
|
state = {
|
|
shouldShowScrollButton: false,
|
|
searchQuery: '',
|
|
}
|
|
|
|
addressFuse = new Fuse([], {
|
|
shouldSort: false,
|
|
threshold: 0.45,
|
|
location: 0,
|
|
distance: 100,
|
|
maxPatternLength: 32,
|
|
minMatchCharLength: 1,
|
|
keys: [
|
|
{ name: 'name', weight: 0.5 },
|
|
{ name: 'address', weight: 0.5 },
|
|
],
|
|
})
|
|
|
|
componentDidUpdate (prevProps, prevState) {
|
|
const { isAccountMenuOpen: prevIsAccountMenuOpen } = prevProps
|
|
const { searchQuery: prevSearchQuery } = prevState
|
|
const { isAccountMenuOpen } = this.props
|
|
const { searchQuery } = this.state
|
|
|
|
if (!prevIsAccountMenuOpen && isAccountMenuOpen) {
|
|
this.setShouldShowScrollButton()
|
|
this.resetSearchQuery()
|
|
}
|
|
|
|
// recalculate on each search query change
|
|
// whether we can show scroll down button
|
|
if (isAccountMenuOpen && prevSearchQuery !== searchQuery) {
|
|
this.setShouldShowScrollButton()
|
|
}
|
|
}
|
|
|
|
renderAccountsSearch () {
|
|
const inputAdornment = (
|
|
<InputAdornment
|
|
position="start"
|
|
style={{
|
|
maxHeight: 'none',
|
|
marginRight: 0,
|
|
marginLeft: '8px',
|
|
}}
|
|
>
|
|
<SearchIcon />
|
|
</InputAdornment>
|
|
)
|
|
|
|
return [
|
|
<TextField
|
|
key="search-text-field"
|
|
id="search-accounts"
|
|
placeholder={this.context.t('searchAccounts')}
|
|
type="text"
|
|
value={this.state.searchQuery}
|
|
onChange={(e) => this.setSearchQuery(e.target.value)}
|
|
startAdornment={inputAdornment}
|
|
fullWidth
|
|
theme="material-white-padded"
|
|
/>,
|
|
<Divider key="search-divider" />,
|
|
]
|
|
}
|
|
|
|
renderAccounts () {
|
|
const {
|
|
accounts,
|
|
selectedAddress,
|
|
keyrings,
|
|
showAccountDetail,
|
|
addressConnectedDomainMap,
|
|
originOfCurrentTab,
|
|
} = this.props
|
|
const { searchQuery } = this.state
|
|
|
|
let filteredIdentities = accounts
|
|
if (searchQuery) {
|
|
this.addressFuse.setCollection(accounts)
|
|
filteredIdentities = this.addressFuse.search(searchQuery)
|
|
}
|
|
|
|
if (filteredIdentities.length === 0) {
|
|
return <p className="account-menu__no-accounts">{this.context.t('noAccountsFound')}</p>
|
|
}
|
|
|
|
return filteredIdentities.map((identity) => {
|
|
const isSelected = identity.address === selectedAddress
|
|
|
|
const simpleAddress = identity.address.substring(2).toLowerCase()
|
|
|
|
const keyring = keyrings.find((kr) => {
|
|
return kr.accounts.includes(simpleAddress) || kr.accounts.includes(identity.address)
|
|
})
|
|
const addressDomains = addressConnectedDomainMap[identity.address] || {}
|
|
const iconAndNameForOpenDomain = addressDomains[originOfCurrentTab]
|
|
|
|
return (
|
|
<div
|
|
className="account-menu__account menu__item--clickable"
|
|
onClick={() => {
|
|
this.context.metricsEvent({
|
|
eventOpts: {
|
|
category: 'Navigation',
|
|
action: 'Main Menu',
|
|
name: 'Switched Account',
|
|
},
|
|
})
|
|
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={identity.balance}
|
|
type={PRIMARY}
|
|
/>
|
|
</div>
|
|
{ this.renderKeyringType(keyring) }
|
|
{ iconAndNameForOpenDomain
|
|
? (
|
|
<div className="account-menu__icon-list">
|
|
<IconWithFallBack icon={iconAndNameForOpenDomain.icon} name={iconAndNameForOpenDomain.name} />
|
|
</div>
|
|
)
|
|
: null
|
|
}
|
|
</div>
|
|
)
|
|
})
|
|
}
|
|
|
|
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
|
|
default:
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div className="keyring-label allcaps">
|
|
{ label }
|
|
</div>
|
|
)
|
|
}
|
|
|
|
resetSearchQuery () {
|
|
this.setSearchQuery('')
|
|
}
|
|
|
|
setSearchQuery (searchQuery) {
|
|
this.setState({ searchQuery })
|
|
}
|
|
|
|
setShouldShowScrollButton = () => {
|
|
const { scrollTop, offsetHeight, scrollHeight } = this.accountsRef
|
|
|
|
const canScroll = scrollHeight > offsetHeight
|
|
|
|
const atAccountListBottom = scrollTop + offsetHeight >= scrollHeight
|
|
|
|
const shouldShowScrollButton = canScroll && !atAccountListBottom
|
|
|
|
this.setState({ shouldShowScrollButton })
|
|
}
|
|
|
|
onScroll = debounce(this.setShouldShowScrollButton, 25)
|
|
|
|
handleScrollDown = (e) => {
|
|
e.stopPropagation()
|
|
|
|
const { scrollHeight } = this.accountsRef
|
|
this.accountsRef.scroll({ left: 0, top: scrollHeight, behavior: 'smooth' })
|
|
|
|
this.setShouldShowScrollButton()
|
|
}
|
|
|
|
renderScrollButton () {
|
|
const { shouldShowScrollButton } = this.state
|
|
|
|
return shouldShowScrollButton && (
|
|
<div
|
|
className="account-menu__scroll-button"
|
|
onClick={this.handleScrollDown}
|
|
>
|
|
<img
|
|
src="./images/icons/down-arrow.svg"
|
|
width={28}
|
|
height={28}
|
|
alt="scroll down"
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
render () {
|
|
const { t, metricsEvent } = this.context
|
|
const {
|
|
shouldShowAccountsSearch,
|
|
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__lock-button"
|
|
onClick={() => {
|
|
lockMetamask()
|
|
history.push(DEFAULT_ROUTE)
|
|
}}
|
|
>
|
|
{ t('lock') }
|
|
</button>
|
|
</Item>
|
|
<Divider />
|
|
<div className="account-menu__accounts-container">
|
|
{shouldShowAccountsSearch ? this.renderAccountsSearch() : null}
|
|
<div
|
|
className="account-menu__accounts"
|
|
onScroll={this.onScroll}
|
|
ref={(ref) => {
|
|
this.accountsRef = ref
|
|
}}
|
|
>
|
|
{ this.renderAccounts() }
|
|
</div>
|
|
{ this.renderScrollButton() }
|
|
</div>
|
|
<Divider />
|
|
<Item
|
|
onClick={() => {
|
|
toggleAccountMenu()
|
|
metricsEvent({
|
|
eventOpts: {
|
|
category: 'Navigation',
|
|
action: 'Main Menu',
|
|
name: 'Clicked Create Account',
|
|
},
|
|
})
|
|
history.push(NEW_ACCOUNT_ROUTE)
|
|
}}
|
|
icon={(
|
|
<img
|
|
className="account-menu__item-icon"
|
|
src="images/plus-btn-white.svg"
|
|
/>
|
|
)}
|
|
text={t('createAccount')}
|
|
/>
|
|
<Item
|
|
onClick={() => {
|
|
toggleAccountMenu()
|
|
metricsEvent({
|
|
eventOpts: {
|
|
category: 'Navigation',
|
|
action: 'Main Menu',
|
|
name: 'Clicked Import Account',
|
|
},
|
|
})
|
|
history.push(IMPORT_ACCOUNT_ROUTE)
|
|
}}
|
|
icon={(
|
|
<img
|
|
className="account-menu__item-icon"
|
|
src="images/import-account.svg"
|
|
/>
|
|
)}
|
|
text={t('importAccount')}
|
|
/>
|
|
<Item
|
|
onClick={() => {
|
|
toggleAccountMenu()
|
|
metricsEvent({
|
|
eventOpts: {
|
|
category: 'Navigation',
|
|
action: 'Main Menu',
|
|
name: 'Clicked Connect Hardware',
|
|
},
|
|
})
|
|
if (getEnvironmentType() === 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(ABOUT_US_ROUTE)
|
|
}}
|
|
icon={
|
|
<img src="images/mm-info-icon.svg" />
|
|
}
|
|
text={t('infoHelp')}
|
|
/>
|
|
<Item
|
|
onClick={() => {
|
|
toggleAccountMenu()
|
|
history.push(SETTINGS_ROUTE)
|
|
this.context.metricsEvent({
|
|
eventOpts: {
|
|
category: 'Navigation',
|
|
action: 'Main Menu',
|
|
name: 'Opened Settings',
|
|
},
|
|
})
|
|
}}
|
|
icon={(
|
|
<img
|
|
className="account-menu__item-icon"
|
|
src="images/settings.svg"
|
|
/>
|
|
)}
|
|
text={t('settings')}
|
|
/>
|
|
</Menu>
|
|
)
|
|
}
|
|
}
|