Browse Source

Add accounts: handle cta wording and unselect all

master
meriadec 7 years ago
parent
commit
564f57ec4d
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 13
      src/components/MainSideBar.js
  2. 4
      src/components/modals/ImportAccounts/AccountRow.js
  3. 77
      src/components/modals/ImportAccounts/steps/03-step-import.js
  4. 10
      static/i18n/en/importAccounts.yml

13
src/components/MainSideBar.js

@ -17,10 +17,13 @@ import type { UpdateStatus } from 'reducers/update'
import { MODAL_RECEIVE, MODAL_SEND } from 'config/constants' import { MODAL_RECEIVE, MODAL_SEND } from 'config/constants'
import { rgba } from 'styles/helpers'
import { accountsSelector } from 'reducers/accounts' import { accountsSelector } from 'reducers/accounts'
import { openModal } from 'reducers/modals' import { openModal } from 'reducers/modals'
import { getUpdateStatus } from 'reducers/update' import { getUpdateStatus } from 'reducers/update'
import Tooltip from 'components/base/Tooltip'
import { SideBarList } from 'components/base/SideBar' import { SideBarList } from 'components/base/SideBar'
import Box, { Tabbable } from 'components/base/Box' import Box, { Tabbable } from 'components/base/Box'
import Space from 'components/base/Space' import Space from 'components/base/Space'
@ -140,9 +143,11 @@ class MainSideBar extends PureComponent<Props> {
scroll scroll
title={t('sidebar:accounts')} title={t('sidebar:accounts')}
titleRight={ titleRight={
<Tooltip render={() => t('importAccounts:title')}>
<PlusWrapper onClick={() => openModal('importAccounts')}> <PlusWrapper onClick={() => openModal('importAccounts')}>
<IconCirclePlus size={16} /> <IconCirclePlus size={16} />
</PlusWrapper> </PlusWrapper>
</Tooltip>
} }
items={accountsItems} items={accountsItems}
emptyText={t('emptyState:sidebar.text')} emptyText={t('emptyState:sidebar.text')}
@ -157,15 +162,15 @@ const PlusWrapper = styled(Tabbable).attrs({
cursor: 'pointer', cursor: 'pointer',
borderRadius: 1, borderRadius: 1,
})` })`
opacity: 0.4; color: ${p => p.theme.colors.smoke};
&:hover { &:hover {
opacity: 1; color: ${p => p.theme.colors.dark};
} }
border: 1px dashed rgba(0, 0, 0, 0); border: 1px solid transparent;
&:focus { &:focus {
border: 1px dashed rgba(0, 0, 0, 0.2);
outline: none; outline: none;
border-color: ${p => rgba(p.theme.colors.wallet, 0.3)};
} }
` `

4
src/components/modals/ImportAccounts/AccountRow.js

@ -17,7 +17,7 @@ import IconCheck from 'icons/Check'
type Props = { type Props = {
account: Account, account: Account,
isChecked: boolean, isChecked: boolean,
isDisabled: boolean, isDisabled?: boolean,
onClick: Account => void, onClick: Account => void,
onAccountUpdate: Account => void, onAccountUpdate: Account => void,
} }
@ -110,7 +110,7 @@ export default class AccountRow extends PureComponent<Props, State> {
fontSize={4} fontSize={4}
color="grey" color="grey"
/> />
<Radio isChecked={isChecked || isDisabled} /> <Radio isChecked={isChecked || !!isDisabled} />
</AccountRowContainer> </AccountRowContainer>
) )
} }

77
src/components/modals/ImportAccounts/steps/03-step-import.js

@ -50,13 +50,15 @@ class StepImport extends PureComponent<StepProps> {
this.scanSubscription = bridge.scanAccountsOnDevice(currency, devicePath, { this.scanSubscription = bridge.scanAccountsOnDevice(currency, devicePath, {
next: account => { next: account => {
const { scannedAccounts, checkedAccountsIds } = this.props const { scannedAccounts, checkedAccountsIds, existingAccounts } = this.props
const hasAlreadyBeenScanned = !!scannedAccounts.find(a => account.id === a.id) const hasAlreadyBeenScanned = !!scannedAccounts.find(a => account.id === a.id)
const hasAlreadyBeenImported = !!existingAccounts.find(a => account.id === a.id)
const isNewAccount = account.operations.length === 0
if (!hasAlreadyBeenScanned) { if (!hasAlreadyBeenScanned) {
setState({ setState({
scannedAccounts: [...scannedAccounts, account], scannedAccounts: [...scannedAccounts, account],
checkedAccountsIds: checkedAccountsIds:
account.operations.length > 0 !hasAlreadyBeenImported && !isNewAccount
? uniq([...checkedAccountsIds, account.id]) ? uniq([...checkedAccountsIds, account.id])
: checkedAccountsIds, : checkedAccountsIds,
}) })
@ -111,15 +113,17 @@ class StepImport extends PureComponent<StepProps> {
}) })
} }
handleToggleSelectAll = () => { handleSelectAll = () => {
const { scannedAccounts, setState } = this.props const { scannedAccounts, setState } = this.props
setState({ setState({
checkedAccountsIds: scannedAccounts.filter(a => a.operations.length > 0).map(a => a.id), checkedAccountsIds: scannedAccounts.filter(a => a.operations.length > 0).map(a => a.id),
}) })
} }
handleUnselectAll = () => this.props.setState({ checkedAccountsIds: [] })
render() { render() {
const { scanStatus, err, scannedAccounts, checkedAccountsIds, existingAccounts } = this.props const { scanStatus, err, scannedAccounts, checkedAccountsIds, existingAccounts, t } = this.props
const importableAccounts = scannedAccounts.filter(acc => { const importableAccounts = scannedAccounts.filter(acc => {
if (acc.operations.length <= 0) { if (acc.operations.length <= 0) {
@ -135,11 +139,18 @@ class StepImport extends PureComponent<StepProps> {
return existingAccounts.find(a => a.id === acc.id) === undefined return existingAccounts.find(a => a.id === acc.id) === undefined
}) })
const isAllSelected = scannedAccounts.filter(acc => acc.operations.length > 0).every(acc => {
const isChecked = !!checkedAccountsIds.find(id => acc.id === id)
const isImported = !!existingAccounts.find(a => acc.id === a.id)
return isChecked || isImported
})
return ( return (
<Box> <Box>
{err && <Box shrink>{err.message}</Box>} {err && <Box shrink>{err.message}</Box>}
<Box flow={5}> <Box flow={5}>
{(!!importableAccounts.length || scanStatus === 'scanning') && (
<Box> <Box>
{!!importableAccounts.length && ( {!!importableAccounts.length && (
<Box horizontal mb={3} align="center"> <Box horizontal mb={3} align="center">
@ -149,10 +160,18 @@ class StepImport extends PureComponent<StepProps> {
fontSize={2} fontSize={2}
style={{ textTransform: 'uppercase' }} style={{ textTransform: 'uppercase' }}
> >
{`Account(s) to import (${importableAccounts.length})`} {t('importAccounts:accountToImportSubtitle', {
count: importableAccounts.length,
})}
</Box> </Box>
<FakeLink ml="auto" onClick={this.handleToggleSelectAll} fontSize={3}> <FakeLink
{'Select all'} ml="auto"
onClick={isAllSelected ? this.handleUnselectAll : this.handleSelectAll}
fontSize={3}
>
{isAllSelected
? t('importAccounts:unselectAll')
: t('importAccounts:selectAll')}
</FakeLink> </FakeLink>
</Box> </Box>
)} )}
@ -181,6 +200,8 @@ class StepImport extends PureComponent<StepProps> {
)} )}
</Box> </Box>
</Box> </Box>
)}
{creatableAccounts.length > 0 && ( {creatableAccounts.length > 0 && (
<Box> <Box>
<Box horizontal mb={3} align="center"> <Box horizontal mb={3} align="center">
@ -190,7 +211,7 @@ class StepImport extends PureComponent<StepProps> {
fontSize={2} fontSize={2}
style={{ textTransform: 'uppercase' }} style={{ textTransform: 'uppercase' }}
> >
{'Create account'} {t('importAccounts:createNewAccount')}
</Box> </Box>
</Box> </Box>
<AccountRow <AccountRow
@ -210,7 +231,7 @@ class StepImport extends PureComponent<StepProps> {
<Button small outline onClick={this.handleRetry}> <Button small outline onClick={this.handleRetry}>
<Box horizontal flow={2} align="center"> <Box horizontal flow={2} align="center">
<IconExchange size={13} /> <IconExchange size={13} />
<span>{'retry sync'}</span> <span>{t('importAccounts:retrySync')}</span>
</Box> </Box>
</Button> </Button>
)} )}
@ -233,12 +254,44 @@ export const LoadingRow = styled(Box).attrs({
border: 1px dashed ${p => p.theme.colors.fog}; border: 1px dashed ${p => p.theme.colors.fog};
` `
export const StepImportFooter = ({ scanStatus, onClickImport, checkedAccountsIds }: StepProps) => ( export const StepImportFooter = ({
scanStatus,
onClickImport,
checkedAccountsIds,
scannedAccounts,
t,
}: StepProps) => {
const willCreateAccount = checkedAccountsIds.some(id => {
const account = scannedAccounts.find(a => a.id === id)
return account && account.operations.length === 0
})
const willImportAccounts = checkedAccountsIds.some(id => {
const account = scannedAccounts.find(a => a.id === id)
return account && account.operations.length > 0
})
const importedAccountsCount = checkedAccountsIds.filter(id => {
const account = scannedAccounts.find(acc => acc.id === id)
return account && account.operations.length > 0
}).length
const ctaWording =
willCreateAccount && willImportAccounts
? `${t('importAccounts:cta.create')} / ${t('importAccounts:cta.import', {
count: importedAccountsCount,
})}`
: willCreateAccount
? t('importAccounts:cta.create')
: t('importAccounts:cta.import', { count: importedAccountsCount })
return (
<Button <Button
primary primary
disabled={scanStatus !== 'finished' || checkedAccountsIds.length === 0} disabled={scanStatus !== 'finished' || checkedAccountsIds.length === 0}
onClick={() => onClickImport()} onClick={() => onClickImport()}
> >
{'Import accounts'} {ctaWording}
</Button> </Button>
) )
}

10
static/i18n/en/importAccounts.yml

@ -4,3 +4,13 @@ breadcrumb:
connectDevice: Connect device connectDevice: Connect device
import: Import import: Import
finish: End finish: End
accountToImportSubtitle: Account to import
accountToImportSubtitle_plural: 'Accounts to import ({{count}})'
selectAll: Select all
unselectAll: Unselect all
createNewAccount: Create new account
retrySync: Retry sync
cta:
create: 'Create account'
import: 'Import account'
import_plural: 'Import accounts'

Loading…
Cancel
Save