hw accounts list page
This commit is contained in:
parent
db9701d45c
commit
c6d30a01bc
|
@ -0,0 +1,200 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ethNetProps from 'eth-net-props'
|
||||
import { default as Select } from 'react-select'
|
||||
import Button from '../../../../ui/app/components/button'
|
||||
|
||||
class AccountList extends Component {
|
||||
constructor (props, context) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
getHdPaths () {
|
||||
return [
|
||||
{
|
||||
label: `Ledger Live`,
|
||||
value: `m/44'/60'/0'/0/0`,
|
||||
},
|
||||
{
|
||||
label: `Legacy (MEW / MyCrypto)`,
|
||||
value: `m/44'/60'/0'`,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
goToNextPage = () => {
|
||||
// If we have < 5 accounts, it's restricted by BIP-44
|
||||
if (this.props.accounts.length === 5) {
|
||||
this.props.getPage(this.props.device, 1, this.props.selectedPath)
|
||||
} else {
|
||||
this.props.onAccountRestriction()
|
||||
}
|
||||
}
|
||||
|
||||
goToPreviousPage = () => {
|
||||
this.props.getPage(this.props.device, -1, this.props.selectedPath)
|
||||
}
|
||||
|
||||
renderHdPathSelector () {
|
||||
const { onPathChange, selectedPath } = this.props
|
||||
|
||||
const options = this.getHdPaths()
|
||||
return (
|
||||
<div>
|
||||
<h3 className='hw-connect__hdPath__title'>this.context.t('selectHdPath')</h3>
|
||||
<p className='hw-connect__msg'>this.context.t('selectPathHelp')</p>
|
||||
<div className='hw-connect__hdPath'>
|
||||
<Select
|
||||
className='hw-connect__hdPath__select'
|
||||
name='hd-path-select'
|
||||
clearable={false}
|
||||
value={selectedPath}
|
||||
options
|
||||
onChange={(opt) => {
|
||||
onPathChange(opt.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
capitalizeDevice (device) {
|
||||
return device.slice(0, 1).toUpperCase() + device.slice(1)
|
||||
}
|
||||
|
||||
renderHeader () {
|
||||
const { device } = this.props
|
||||
return (
|
||||
<div className='hw-connect'>
|
||||
<h3 className='hw-connect'>
|
||||
<h3 className='hw-connect__unlock-title'>`${this.context.t('unlock')} ${this.capitalizeDevice(device)}`</h3>
|
||||
{device.toLowerCase() === 'ledger' ? this.renderHdPathSelector() : null}
|
||||
<h3 className='hw-connect__hdPath__title'>{this.context.t('selectAnAccount')}</h3>
|
||||
<p className='hw-connect__msg'>{this.context.t('selectAnAccountHelp')}</p>
|
||||
</h3>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderAccounts () {
|
||||
const rows = []
|
||||
this.props.accounts.map((a, i) => {
|
||||
rows.push(
|
||||
<div className='hw-account-list__item' key={a.address}>
|
||||
<div className='hw-account-list__item__radio'>
|
||||
<input
|
||||
type='radio'
|
||||
name='selectedAccount'
|
||||
id={`address-${i}`}
|
||||
value={a.index}
|
||||
onChange={(e) => this.props.onAccountChange(e.target.value)}
|
||||
checked={this.props.selectedAccount === a.index.toString()}
|
||||
/>
|
||||
<label className='hw-account-list__item__label' htmlFor={`address-${i}`}>
|
||||
<span className='hw-account-list__item__index'>{a.index + 1}</span>
|
||||
{`${a.address.slice(0, 4)}...${a.address.slice(-4)}`}
|
||||
<span className='hw-account-list__item__balance'>{`${a.balance}`}</span>
|
||||
</label>
|
||||
</div>
|
||||
<a
|
||||
className='hw-account-list__item__link'
|
||||
href={ethNetProps.explorerLinks.getExplorerAccountLinkFor(a.address, this.props.network)}
|
||||
target='_blank'
|
||||
title={this.context.t('etherscanView')}
|
||||
/>
|
||||
<img src='images/popout.svg' />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='hw-account-list'>{rows}</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderPagination () {
|
||||
return (
|
||||
<div className='hw-list-pagination'>
|
||||
<button
|
||||
className='hw-list-pagination__button'
|
||||
onClick={this.goToPreviousPage}
|
||||
>{`< ${this.context.t('prev')}`}</button>
|
||||
<button
|
||||
className='hw-list-pagination__button'
|
||||
onClick={this.goToNextPage}
|
||||
>{`${this.context.t('next')} >`}</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderButtons () {
|
||||
const disabled = this.props.selectedAccount === null
|
||||
const buttonProps = {}
|
||||
if (disabled) {
|
||||
buttonProps.disabled = true
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='new-account-connect-form__buttons'>
|
||||
<Button
|
||||
type='default'
|
||||
large={true}
|
||||
className='new-account-connect-form__button'
|
||||
onClick={this.props.onCancel.bind(this)}
|
||||
>{this.context.t('cancel')}</Button>
|
||||
<Button
|
||||
type='primary'
|
||||
large={true}
|
||||
className='new-account-connect-form__button unlock'
|
||||
disabled={disabled}
|
||||
onClick={this.props.onUnlockAccount.bind(this, this.props.device)}
|
||||
>{this.context.t('unlock')}</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderForgetDevice () {
|
||||
return (
|
||||
<div className='hw-forget-device-container'>
|
||||
<a onClick={this.props.onForgetDevice.bind(this, this.props.device)}>{this.context.t('forgetDevice')}</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className='new-account-connect-form.account-list'>
|
||||
{this.renderHeader()}
|
||||
{this.renderAccounts()}
|
||||
{this.renderPagination()}
|
||||
{this.renderButtons()}
|
||||
{this.renderForgetDevice()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
AccountList.propTypes = {
|
||||
onPathChange: PropTypes.func.isRequired,
|
||||
selectedPath: PropTypes.string.isRequired,
|
||||
device: PropTypes.string.isRequired,
|
||||
accounts: PropTypes.array.isRequired,
|
||||
onAccountChange: PropTypes.func.isRequired,
|
||||
onForgetDevice: PropTypes.func.isRequired,
|
||||
getPage: PropTypes.func.isRequired,
|
||||
network: PropTypes.string,
|
||||
selectedAccount: PropTypes.string,
|
||||
history: PropTypes.object,
|
||||
onUnlockAccount: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
onAccountRestriction: PropTypes.func,
|
||||
}
|
||||
|
||||
AccountList.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = AccountList
|
|
@ -0,0 +1,135 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Button from '../../../../ui/app/components/button'
|
||||
|
||||
class ConnectScreen extends Component {
|
||||
constructor (props, context) {
|
||||
super(props)
|
||||
this.state = {
|
||||
selectedDevice: null,
|
||||
}
|
||||
}
|
||||
|
||||
connect = () => {
|
||||
if (this.state.selectedDevice) {
|
||||
this.props.connectToHardwareWallet(this.state.selectedDevice)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
renderConnectToTrezorButton () {
|
||||
return (
|
||||
<button
|
||||
className={`hw-connect__btn${this.state.selectedDevice === 'trezor' ? ' selected' : ''}`}
|
||||
onClick={_ => this.setState({selectedDevice: 'trezor'})}
|
||||
>
|
||||
<img className="hw-connect__btn__img" src="images/trezor-logo.svg"/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
renderConnectToLedgerButton () {
|
||||
return (
|
||||
<button
|
||||
className={`hw-connect__btn${this.state.selectedDevice === 'ledger' ? ' selected' : ''}`}
|
||||
onClick={_ => this.setState({selectedDevice: 'ledger'})}
|
||||
>
|
||||
<img className="hw-connect__btn__img" src="images/ledger-logo.svg"/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
renderButtons () {
|
||||
return (
|
||||
<div>
|
||||
<div className="hw-connect__btn-wrapper">
|
||||
{this.renderConnectToLedgerButton()}
|
||||
{this.renderConnectToTrezorButton()}
|
||||
</div>
|
||||
<button
|
||||
className={`hw-connect__connect-btn${!this.state.selectedDevice ? ' disabled' : ''}`}
|
||||
onClick={this.connect}
|
||||
>Connect</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderUnsupportedBrowser () {
|
||||
return (
|
||||
<div className="new-account-connect-form unsupported-browser">
|
||||
<div className="hw-connect">
|
||||
<h3 className="hw-connect__title">Your Browser is not supported...</h3>
|
||||
<p className="hw-connect__msg">You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet.</p>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
large={true}
|
||||
onClick={() => global.platform.openWindow({
|
||||
url: 'https://google.com/chrome',
|
||||
})}
|
||||
>Download Google Chrome</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderHeader () {
|
||||
return (
|
||||
<div className="hw-connect__header">
|
||||
<p className="hw-connect__header__msg">{`Select a hardware wallet you'd like to use with MetaMask`}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
getAffiliateLinks () {
|
||||
const links = {
|
||||
trezor: `<a class='hw-connect__get-hw__link' href='https://shop.trezor.io/?a=metamask' target='_blank'>Trezor</a>`,
|
||||
ledger: `<a class='hw-connect__get-hw__link' href='https://www.ledger.com/products/ledger-nano-s?r=17c4991a03fa&tracker=MY_TRACKER' target='_blank'>Ledger</a>`,
|
||||
}
|
||||
|
||||
const text = 'Order a Trezor or Ledger and keep your funds in cold storage'
|
||||
const response = text.replace('Trezor', links.trezor).replace('Ledger', links.ledger)
|
||||
|
||||
return (
|
||||
<div className="hw-connect__get-hw__msg" dangerouslySetInnerHTML={{ __html: response }} />
|
||||
)
|
||||
}
|
||||
|
||||
renderTrezorAffiliateLink () {
|
||||
return (
|
||||
<div className="hw-connect__get-hw">
|
||||
<p className="hw-connect__get-hw__msg">Don’t have a hardware wallet?</p>
|
||||
{this.getAffiliateLinks()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
scrollToTutorial = (e) => {
|
||||
if (this.referenceNode) this.referenceNode.scrollIntoView({behavior: 'smooth'})
|
||||
}
|
||||
|
||||
renderConnectScreen () {
|
||||
return (
|
||||
<div className="new-account-connect-form">
|
||||
{this.renderHeader()}
|
||||
{this.renderButtons()}
|
||||
{this.renderTrezorAffiliateLink()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.props.browserSupported) {
|
||||
return this.renderConnectScreen()
|
||||
}
|
||||
return this.renderUnsupportedBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
ConnectScreen.propTypes = {
|
||||
connectToHardwareWallet: PropTypes.func.isRequired,
|
||||
browserSupported: PropTypes.bool.isRequired,
|
||||
}
|
||||
|
||||
module.exports = ConnectScreen
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import actions from '../../../../ui/app/actions'
|
||||
import ConnectScreen from './connect-screen'
|
||||
import AccountList from './account-list'
|
||||
import { DEFAULT_ROUTE } from '../../../../ui/app/routes'
|
||||
import { formatBalance } from '../../../../ui/app/util'
|
||||
import { getPlatform } from '../../../../app/scripts/lib/util'
|
||||
import { PLATFORM_FIREFOX } from '../../../../app/scripts/lib/enums'
|
||||
|
||||
class ConnectHardwareForm extends Component {
|
||||
constructor (props, context) {
|
||||
super(props)
|
||||
this.state = {
|
||||
error: null,
|
||||
selectedAccount: null,
|
||||
accounts: [],
|
||||
browserSupported: true,
|
||||
unlocked: false,
|
||||
device: null,
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { accounts } = nextProps
|
||||
const newAccounts = this.state.accounts.map(a => {
|
||||
const normalizedAddress = a.address.toLowerCase()
|
||||
const balanceValue = accounts[normalizedAddress] && accounts[normalizedAddress].balance || null
|
||||
a.balance = balanceValue ? formatBalance(balanceValue, 6) : '...'
|
||||
return a
|
||||
})
|
||||
this.setState({accounts: newAccounts})
|
||||
}
|
||||
|
||||
|
||||
componentDidMount () {
|
||||
this.checkIfUnlocked()
|
||||
}
|
||||
|
||||
async checkIfUnlocked () {
|
||||
['trezor', 'ledger'].forEach(async device => {
|
||||
const unlocked = await this.props.checkHardwareStatus(device, this.props.defaultHdPaths[device])
|
||||
if (unlocked) {
|
||||
this.setState({unlocked: true})
|
||||
this.getPage(device, 0, this.props.defaultHdPaths[device])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connectToHardwareWallet = (device) => {
|
||||
// None of the hardware wallets are supported
|
||||
// At least for now
|
||||
if (getPlatform() === PLATFORM_FIREFOX) {
|
||||
this.setState({ browserSupported: false, error: null})
|
||||
return null
|
||||
}
|
||||
|
||||
if (this.state.accounts.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Default values
|
||||
this.getPage(device, 0, this.props.defaultHdPaths[device])
|
||||
}
|
||||
|
||||
onPathChange = (path) => {
|
||||
this.props.setHardwareWalletDefaultHdPath({device: this.state.device, path})
|
||||
this.getPage(this.state.device, 0, path)
|
||||
}
|
||||
|
||||
onAccountChange = (account) => {
|
||||
this.setState({selectedAccount: account.toString(), error: null})
|
||||
}
|
||||
|
||||
onAccountRestriction = () => {
|
||||
this.setState({error: 'You need to make use your last account before you can add a new one.' })
|
||||
}
|
||||
|
||||
showTemporaryAlert () {
|
||||
this.props.showAlert('Hardware wallet connected')
|
||||
// Autohide the alert after 5 seconds
|
||||
setTimeout(_ => {
|
||||
this.props.hideAlert()
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
getPage = (device, page, hdPath) => {
|
||||
this.props
|
||||
.connectHardware(device, page, hdPath)
|
||||
.then(accounts => {
|
||||
if (accounts.length) {
|
||||
|
||||
// If we just loaded the accounts for the first time
|
||||
// (device previously locked) show the global alert
|
||||
if (this.state.accounts.length === 0 && !this.state.unlocked) {
|
||||
this.showTemporaryAlert()
|
||||
}
|
||||
|
||||
const newState = { unlocked: true, device, error: null }
|
||||
// Default to the first account
|
||||
if (this.state.selectedAccount === null) {
|
||||
accounts.forEach((a, i) => {
|
||||
if (a.address.toLowerCase() === this.props.address) {
|
||||
newState.selectedAccount = a.index.toString()
|
||||
}
|
||||
})
|
||||
// If the page doesn't contain the selected account, let's deselect it
|
||||
} else if (!accounts.filter(a => a.index.toString() === this.state.selectedAccount).length) {
|
||||
newState.selectedAccount = null
|
||||
}
|
||||
|
||||
|
||||
// Map accounts with balances
|
||||
newState.accounts = accounts.map(account => {
|
||||
const normalizedAddress = account.address.toLowerCase()
|
||||
const balanceValue = this.props.accounts[normalizedAddress] && this.props.accounts[normalizedAddress].balance || null
|
||||
account.balance = balanceValue ? formatBalance(balanceValue, 6) : '...'
|
||||
return account
|
||||
})
|
||||
|
||||
this.setState(newState)
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 'Window blocked') {
|
||||
this.setState({ browserSupported: false, error: null})
|
||||
} else if (e !== 'Window closed') {
|
||||
this.setState({ error: e.toString() })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onForgetDevice = (device) => {
|
||||
this.props.forgetDevice(device)
|
||||
.then(_ => {
|
||||
this.setState({
|
||||
error: null,
|
||||
selectedAccount: null,
|
||||
accounts: [],
|
||||
unlocked: false,
|
||||
})
|
||||
}).catch(e => {
|
||||
this.setState({ error: e.toString() })
|
||||
})
|
||||
}
|
||||
|
||||
onUnlockAccount = (device) => {
|
||||
|
||||
if (this.state.selectedAccount === null) {
|
||||
this.setState({ error: 'You need to select an account!' })
|
||||
}
|
||||
|
||||
this.props.unlockHardwareWalletAccount(this.state.selectedAccount, device)
|
||||
.then(_ => {
|
||||
this.props.history.push(DEFAULT_ROUTE)
|
||||
}).catch(e => {
|
||||
this.setState({ error: e.toString() })
|
||||
})
|
||||
}
|
||||
|
||||
onCancel = () => {
|
||||
this.props.history.push(DEFAULT_ROUTE)
|
||||
}
|
||||
|
||||
renderError () {
|
||||
return this.state.error
|
||||
? <span className="error" style={{ display: 'block', textAlign: 'center' }}>{this.state.error}</span>
|
||||
: null
|
||||
}
|
||||
|
||||
renderContent () {
|
||||
if (!this.state.accounts.length) {
|
||||
return (
|
||||
<ConnectScreen
|
||||
connectToHardwareWallet={this.connectToHardwareWallet}
|
||||
browserSupported={this.state.browserSupported}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<AccountList
|
||||
onPathChange={this.onPathChange}
|
||||
selectedPath={this.props.defaultHdPaths[this.state.device]}
|
||||
device={this.state.device}
|
||||
accounts={this.state.accounts}
|
||||
selectedAccount={this.state.selectedAccount}
|
||||
onAccountChange={this.onAccountChange}
|
||||
network={this.props.network}
|
||||
getPage={this.getPage}
|
||||
history={this.props.history}
|
||||
onUnlockAccount={this.onUnlockAccount}
|
||||
onForgetDevice={this.onForgetDevice}
|
||||
onCancel={this.onCancel}
|
||||
onAccountRestriction={this.onAccountRestriction}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div style={{width: '100%'}}>
|
||||
<div className="section-title flex-row flex-center">
|
||||
<i className="fa fa-arrow-left fa-lg cursor-pointer"
|
||||
onClick={() => this.props.goHome() }
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: '30px',
|
||||
}}/>
|
||||
<h2>Connect to hardware wallet</h2>
|
||||
</div>
|
||||
<div style={{overflowY: 'auto', height: '482px'}}>
|
||||
<div style={{padding: '0 30px'}}>
|
||||
{this.renderError()}
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ConnectHardwareForm.propTypes = {
|
||||
hideModal: PropTypes.func,
|
||||
showImportPage: PropTypes.func,
|
||||
showConnectPage: PropTypes.func,
|
||||
connectHardware: PropTypes.func,
|
||||
checkHardwareStatus: PropTypes.func,
|
||||
forgetDevice: PropTypes.func,
|
||||
showAlert: PropTypes.func,
|
||||
hideAlert: PropTypes.func,
|
||||
unlockHardwareWalletAccount: PropTypes.func,
|
||||
setHardwareWalletDefaultHdPath: PropTypes.func,
|
||||
goHome: PropTypes.func,
|
||||
numberOfExistingAccounts: PropTypes.number,
|
||||
history: PropTypes.object,
|
||||
t: PropTypes.func,
|
||||
network: PropTypes.string,
|
||||
accounts: PropTypes.object,
|
||||
address: PropTypes.string,
|
||||
defaultHdPaths: PropTypes.object,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const {
|
||||
metamask: { network, selectedAddress, identities = {}, accounts = [] },
|
||||
} = state
|
||||
const numberOfExistingAccounts = Object.keys(identities).length
|
||||
const {
|
||||
appState: { defaultHdPaths },
|
||||
} = state
|
||||
|
||||
return {
|
||||
network,
|
||||
accounts,
|
||||
address: selectedAddress,
|
||||
numberOfExistingAccounts,
|
||||
defaultHdPaths,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
goHome: () => {
|
||||
dispatch(actions.goHome())
|
||||
},
|
||||
setHardwareWalletDefaultHdPath: ({device, path}) => {
|
||||
return dispatch(actions.setHardwareWalletDefaultHdPath({device, path}))
|
||||
},
|
||||
connectHardware: (deviceName, page, hdPath) => {
|
||||
return dispatch(actions.connectHardware(deviceName, page, hdPath))
|
||||
},
|
||||
checkHardwareStatus: (deviceName, hdPath) => {
|
||||
return dispatch(actions.checkHardwareStatus(deviceName, hdPath))
|
||||
},
|
||||
forgetDevice: (deviceName) => {
|
||||
return dispatch(actions.forgetDevice(deviceName))
|
||||
},
|
||||
unlockHardwareWalletAccount: (index, deviceName, hdPath) => {
|
||||
return dispatch(actions.unlockHardwareWalletAccount(index, deviceName, hdPath))
|
||||
},
|
||||
showImportPage: () => dispatch(actions.showImportPage()),
|
||||
showConnectPage: () => dispatch(actions.showConnectPage()),
|
||||
showAlert: (msg) => dispatch(actions.showAlert(msg)),
|
||||
hideAlert: () => dispatch(actions.hideAlert()),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConnectHardwareForm)
|
|
@ -0,0 +1,262 @@
|
|||
.hw-tutorial {
|
||||
width: 375px;
|
||||
border-top: 1px solid #D2D8DD;
|
||||
border-bottom: 1px solid #D2D8DD;
|
||||
overflow: visible;
|
||||
display: block;
|
||||
padding: 15px 30px;
|
||||
}
|
||||
|
||||
.hw-connect {
|
||||
width: 100%;
|
||||
}
|
||||
.hw-connect__header__title {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 22px;
|
||||
}
|
||||
.hw-connect__header__msg {
|
||||
font-size: 14px;
|
||||
color: #9b9b9b;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.hw-connect__btn-wrapper {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
}
|
||||
.hw-connect__connect-btn {
|
||||
color: #fff;
|
||||
border: none;
|
||||
width: 100%;
|
||||
min-height: 54px;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 20px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hw-connect__connect-btn.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: .5;
|
||||
}
|
||||
.hw-connect__btn {
|
||||
background: #fbfbfb;
|
||||
border: 1px solid #e5e5e5;
|
||||
height: 100px;
|
||||
width: 150px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.hw-connect__btn__img {
|
||||
width: 95px;
|
||||
}
|
||||
.hw-connect__btn.selected {
|
||||
border: 2px solid #60DB97;
|
||||
width: 149px;
|
||||
}
|
||||
.hw-connect__btn:first-child {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.hw-connect__btn:last-child {
|
||||
|
||||
}
|
||||
.hw-connect__hdPath {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.hw-connect__hdPath__title {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.hw-connect__hdPath__select {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
.hw-connect__learn-more {
|
||||
margin-top: 15px;
|
||||
font-size: 14px;
|
||||
color: #5B5D67;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hw-connect__learn-more__arrow {
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
display: block;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
margin: 0px auto 10px;
|
||||
}
|
||||
.hw-connect__title {
|
||||
padding-top: 10px;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
}
|
||||
.hw-connect__unlock-title {
|
||||
padding-top: 10px;
|
||||
font-weight: 400;
|
||||
font-size: 22px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.hw-connect__msg {
|
||||
font-size: 14px;
|
||||
color: #9b9b9b;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.hw-connect__link {
|
||||
color: #2f9ae0;
|
||||
}
|
||||
.hw-connect__footer__title {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 12px;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
.hw-connect__footer__msg {
|
||||
font-size: 14px;
|
||||
color: #9b9b9b;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 27px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-left: 20px;
|
||||
}
|
||||
.hw-connect__footer__link {
|
||||
color: #2f9ae0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.hw-connect__get-hw {
|
||||
width: 100%;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.hw-connect__get-hw__msg {
|
||||
font-size: 14px;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
.hw-connect__get-hw__link {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #60DB97;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hw-connect__step-asset {
|
||||
margin: 0px auto 20px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hw-account-list {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-flow: column;
|
||||
width: 100%;
|
||||
}
|
||||
.hw-account-list__title_wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
}
|
||||
.hw-account-list__title {
|
||||
margin-bottom: 23px;
|
||||
align-self: flex-start;
|
||||
color: #5d5d5d;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
.hw-account-list__device {
|
||||
margin-bottom: 23px;
|
||||
align-self: flex-end;
|
||||
color: #5d5d5d;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
font-weight: normal;
|
||||
display: flex;
|
||||
}
|
||||
.hw-account-list__item {
|
||||
font-size: 15px;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.hw-account-list__item:nth-of-type(even) {
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
.hw-account-list__item:nth-of-type(odd) {
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.hw-account-list__item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.hw-account-list__item__index {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
}
|
||||
.hw-account-list__item__radio {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
.hw-account-list__item__radio input {
|
||||
padding: 10px;
|
||||
margin-top: 13px;
|
||||
}
|
||||
.hw-account-list__item__label {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.hw-account-list__item__balance {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.hw-account-list__item__link {
|
||||
display: flex;
|
||||
margin-top: 13px;
|
||||
}
|
||||
.hw-account-list__item__link img {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
.hw-list-pagination {
|
||||
display: flex;
|
||||
align-self: flex-end;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.hw-list-pagination__button {
|
||||
height: 19px;
|
||||
display: flex;
|
||||
color: #33a4e7;
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
border: none;
|
||||
min-width: 46px;
|
||||
margin-right: 0px;
|
||||
margin-left: 16px;
|
||||
padding: 0px;
|
||||
text-transform: uppercase;
|
||||
font-family: Roboto;
|
||||
}
|
Loading…
Reference in New Issue