Browse Source

Factorize AddAccount code and create AccountsList

master
meriadec 7 years ago
parent
commit
ed831d7e79
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 2
      src/components/base/AccountsList/AccountRow.js
  2. 98
      src/components/base/AccountsList/index.js
  3. 30
      src/components/base/AccountsList/stories.js
  4. 2
      src/components/base/Box/Box.js
  5. 2
      src/components/base/Box/Tabbable.js
  6. 3
      src/components/modals/AddAccounts/index.js
  7. 180
      src/components/modals/AddAccounts/steps/03-step-import.js
  8. 7
      src/config/global-tab.js
  9. 3
      src/styles/theme.js

2
src/components/modals/AddAccounts/AccountRow.js → src/components/base/AccountsList/AccountRow.js

@ -110,7 +110,7 @@ export default class AccountRow extends PureComponent<Props, State> {
fontSize={4}
color="grey"
/>
<Radio isChecked={isChecked || !!isDisabled} />
<Radio disabled isChecked={isChecked || !!isDisabled} />
</AccountRowContainer>
)
}

98
src/components/base/AccountsList/index.js

@ -0,0 +1,98 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import { translate } from 'react-i18next'
import type { Account } from '@ledgerhq/live-common/lib/types'
import Box from 'components/base/Box'
import FakeLink from 'components/base/FakeLink'
import Spinner from 'components/base/Spinner'
import type { T } from 'types/common'
import AccountRow from './AccountRow'
const AccountsList = ({
accounts,
checkedIds,
onToggleAccount,
onUpdateAccount,
onSelectAll,
onUnselectAll,
isLoading,
title,
emptyText,
t,
}: {
accounts: Account[],
checkedIds: string[],
onToggleAccount: Account => void,
onUpdateAccount: Account => void,
onSelectAll: () => void,
onUnselectAll: () => void,
isLoading?: boolean,
title?: string,
emptyText?: string,
t: T,
}) => {
const withToggleAll = !!onSelectAll && !!onUnselectAll && accounts.length > 1
const isAllSelected = accounts.every(acc => !!checkedIds.find(id => acc.id === id))
return (
<Box flow={3}>
{(title || withToggleAll) && (
<Box horizontal align="center">
{title && (
<Box ff="Open Sans|Bold" color="dark" fontSize={2} textTransform="uppercase">
{title}
</Box>
)}
{withToggleAll && (
<FakeLink
ml="auto"
onClick={isAllSelected ? onUnselectAll : onSelectAll}
fontSize={3}
style={{ lineHeight: '10px' }}
>
{isAllSelected ? t('app:addAccounts.unselectAll') : t('app:addAccounts.selectAll')}
</FakeLink>
)}
</Box>
)}
{accounts.length || isLoading ? (
<Box flow={2}>
{accounts.map(account => (
<AccountRow
key={account.id}
account={account}
isChecked={checkedIds.find(id => id === account.id) !== undefined}
onClick={onToggleAccount}
onAccountUpdate={onUpdateAccount}
/>
))}
{isLoading && (
<LoadingRow>
<Spinner color="grey" size={16} />
</LoadingRow>
)}
</Box>
) : emptyText && !isLoading ? (
<Box ff="Open Sans|Regular" fontSize={3}>
{emptyText}
</Box>
) : null}
</Box>
)
}
const LoadingRow = styled(Box).attrs({
horizontal: true,
borderRadius: 1,
px: 3,
align: 'center',
justify: 'center',
})`
height: 48px;
border: 1px dashed ${p => p.theme.colors.grey};
`
export default translate()(AccountsList)

30
src/components/base/AccountsList/stories.js

@ -0,0 +1,30 @@
// @flow
import React from 'react'
import { genAccount } from '@ledgerhq/live-common/lib/mock/account'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import { boolean, text } from '@storybook/addon-knobs'
import AccountsList from 'components/base/AccountsList'
const stories = storiesOf('Components/base', module)
const ACCOUNTS = [genAccount('a'), genAccount('b'), genAccount('c')]
const CHECKED_IDS = []
stories.add('AccountsList', () => (
<div style={{ maxWidth: 450 }}>
<AccountsList
title={text('title', 'this is an account list')}
accounts={ACCOUNTS}
checkedIds={CHECKED_IDS}
onToggleAccount={action('onToggleAccount')}
onUpdateAccount={action('onUpdateAccount')}
onSelectAll={action('onSelectAll')}
onUnselectAll={action('onUnselectAll')}
isLoading={boolean('isLoading', false)}
/>
</div>
))

2
src/components/base/Box/Box.js

@ -18,6 +18,7 @@ import fontFamily from 'styles/styled/fontFamily'
export const styledTextAlign = style({ prop: 'textAlign', cssProperty: 'textAlign' })
export const styledCursor = style({ prop: 'cursor', cssProperty: 'cursor' })
export const styledTextTransform = style({ prop: 'textTransform', cssProperty: 'textTransform' })
export default styled.div`
${alignItems};
@ -32,6 +33,7 @@ export default styled.div`
${space};
${styledTextAlign};
${styledCursor};
${styledTextTransform};
display: flex;
flex-shrink: ${p => (p.noShrink === true ? '0' : p.shrink === true ? '1' : '')};

2
src/components/base/Box/Tabbable.js

@ -3,7 +3,7 @@
import React, { Component } from 'react'
import styled from 'styled-components'
import { isGlobalTabEnabled } from 'renderer/init'
import { isGlobalTabEnabled } from 'config/global-tab'
import { rgba } from 'styles/helpers'
import Box from './Box'

3
src/components/modals/AddAccounts/index.js

@ -130,9 +130,10 @@ class AddAccounts extends PureComponent<Props, State> {
})
transitionTo = stepId => {
const { currency } = this.state
let nextState = { stepId }
if (stepId === 'chooseCurrency') {
nextState = { ...INITIAL_STATE }
nextState = { ...INITIAL_STATE, currency }
}
this.setState(nextState)
}

180
src/components/modals/AddAccounts/steps/03-step-import.js

@ -1,7 +1,6 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import React, { PureComponent, Fragment } from 'react'
import type { Account } from '@ledgerhq/live-common/lib/types'
import uniq from 'lodash/uniq'
@ -9,12 +8,9 @@ import { getBridgeForCurrency } from 'bridge'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import Spinner from 'components/base/Spinner'
import FakeLink from 'components/base/FakeLink'
import AccountsList from 'components/base/AccountsList'
import IconExchange from 'icons/Exchange'
import AccountRow from '../AccountRow'
import type { StepProps } from '../index'
class StepImport extends PureComponent<StepProps> {
@ -101,7 +97,7 @@ class StepImport extends PureComponent<StepProps> {
}
}
handleAccountUpdate = (updatedAccount: Account) => {
handleUpdateAccount = (updatedAccount: Account) => {
const { scannedAccounts, setState } = this.props
setState({
scannedAccounts: scannedAccounts.map(account => {
@ -123,7 +119,15 @@ class StepImport extends PureComponent<StepProps> {
handleUnselectAll = () => this.props.setState({ checkedAccountsIds: [] })
render() {
const { scanStatus, err, scannedAccounts, checkedAccountsIds, existingAccounts, t } = this.props
const {
scanStatus,
currency,
err,
scannedAccounts,
checkedAccountsIds,
existingAccounts,
t,
} = this.props
const importableAccounts = scannedAccounts.filter(acc => {
if (acc.operations.length <= 0) {
@ -139,124 +143,63 @@ class StepImport extends PureComponent<StepProps> {
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
const importableAccountsListTitle = t('app:addAccounts.accountToImportSubtitle', {
count: importableAccounts.length,
})
return (
<Box>
{err && <Box shrink>{err.message}</Box>}
const importableAccountsEmpty = `We didnt find any ${
currency ? ` ${currency.name}}` : ''
} account to import.`
return (
<Fragment>
<Box flow={5}>
{(!!importableAccounts.length || scanStatus === 'scanning') && (
<Box>
{!!importableAccounts.length && (
<Box horizontal mb={3} align="center">
<Box
ff="Open Sans|Bold"
color="dark"
fontSize={2}
style={{ textTransform: 'uppercase' }}
>
{t('app:addAccounts.accountToImportSubtitle', {
count: importableAccounts.length,
})}
</Box>
<FakeLink
ml="auto"
onClick={isAllSelected ? this.handleUnselectAll : this.handleSelectAll}
fontSize={3}
>
{isAllSelected
? t('app:addAccounts.unselectAll')
: t('app:addAccounts.selectAll')}
</FakeLink>
</Box>
)}
<Box flow={2}>
{importableAccounts.map(account => {
const isChecked = checkedAccountsIds.find(id => id === account.id) !== undefined
const existingAccount = existingAccounts.find(a => a.id === account.id)
const isDisabled = existingAccount !== undefined
return (
<AccountRow
key={account.id}
account={existingAccount || account}
isChecked={isChecked}
isDisabled={isDisabled}
onClick={this.handleToggleAccount}
onAccountUpdate={this.handleAccountUpdate}
/>
)
})}
{scanStatus === 'scanning' && (
<LoadingRow>
<Spinner color="grey" size={16} />
</LoadingRow>
)}
</Box>
</Box>
)}
{creatableAccounts.length > 0 && (
<Box>
<Box horizontal mb={3} align="center">
<Box
ff="Open Sans|Bold"
color="dark"
fontSize={2}
style={{ textTransform: 'uppercase' }}
>
{t('app:addAccounts.createNewAccount')}
</Box>
</Box>
<AccountRow
account={creatableAccounts[0]}
isChecked={
checkedAccountsIds.find(id => id === creatableAccounts[0].id) !== undefined
}
onClick={this.handleToggleAccount}
onAccountUpdate={this.handleAccountUpdate}
/>
</Box>
)}
<AccountsList
title={importableAccountsListTitle}
emptyText={importableAccountsEmpty}
accounts={importableAccounts}
checkedIds={checkedAccountsIds}
onToggleAccount={this.handleToggleAccount}
onUpdateAccount={this.handleUpdateAccount}
onSelectAll={this.handleSelectAll}
onUnselectAll={this.handleUnselectAll}
isLoading={scanStatus === 'scanning'}
/>
<AccountsList
title={t('app:addAccounts.createNewAccount')}
emptyText={
'You cannot create a new account because your last account has no operations'
}
accounts={creatableAccounts}
checkedIds={checkedAccountsIds}
onToggleAccount={this.handleToggleAccount}
onUpdateAccount={this.handleUpdateAccount}
isLoading={scanStatus === 'scanning'}
/>
</Box>
<Box horizontal mt={2}>
{['error'].includes(scanStatus) && (
{err && (
<Box shrink>
{err.message}
<Button small outline onClick={this.handleRetry}>
<Box horizontal flow={2} align="center">
<IconExchange size={13} />
<span>{t('app:addAccounts.retrySync')}</span>
</Box>
</Button>
)}
</Box>
</Box>
</Box>
)}
</Fragment>
)
}
}
export default StepImport
export const LoadingRow = styled(Box).attrs({
horizontal: true,
borderRadius: 1,
px: 3,
align: 'center',
justify: 'center',
})`
height: 48px;
border: 1px dashed ${p => p.theme.colors.fog};
`
export const StepImportFooter = ({
scanStatus,
onClickAdd,
onCloseModal,
checkedAccountsIds,
scannedAccounts,
t,
@ -277,20 +220,23 @@ export const StepImportFooter = ({
}).length
const ctaWording =
willCreateAccount && willAddAccounts
? `${t('app:addAccounts.cta.create')} / ${t('app:addAccounts.cta.import', {
count: addedAccountsCount,
})}`
: willCreateAccount
? t('app:addAccounts.cta.create')
: t('app:addAccounts.cta.import', { count: addedAccountsCount })
scanStatus === 'scanning'
? t('app:common.sync.syncing')
: willCreateAccount && willAddAccounts
? `${t('app:addAccounts.cta.create')} / ${t('app:addAccounts.cta.import', {
count: addedAccountsCount,
})}`
: willCreateAccount
? t('app:addAccounts.cta.create')
: willAddAccounts
? t('app:addAccounts.cta.import', { count: addedAccountsCount })
: t('app:common.close')
const willClose = !willCreateAccount && !willAddAccounts
const onClick = willClose ? onCloseModal : onClickAdd
return (
<Button
primary
disabled={scanStatus !== 'finished' || checkedAccountsIds.length === 0}
onClick={() => onClickAdd()}
>
<Button primary disabled={scanStatus !== 'finished'} onClick={onClick}>
{ctaWording}
</Button>
)

7
src/config/global-tab.js

@ -0,0 +1,7 @@
// Github like focus style:
// - focus states are not visible by default
// - first time user hit tab, enable global tab to see focus states
let IS_GLOBAL_TAB_ENABLED = false
export const isGlobalTabEnabled = () => IS_GLOBAL_TAB_ENABLED
export const enableGlobalTab = () => (IS_GLOBAL_TAB_ENABLED = true)

3
src/styles/theme.js

@ -1,7 +1,5 @@
// @flow
import { rgba } from 'styles/helpers'
export const space = [0, 5, 10, 15, 20, 30, 40, 50, 70]
export const fontSizes = [8, 9, 10, 12, 13, 16, 18, 22, 32]
export const radii = [0, 4]
@ -95,7 +93,6 @@ export default {
topBarHeight: 58,
sideBarWidth: 230,
},
focusBoxShadow: `${rgba(colors.wallet, 0.2)} 0 2px 5px`,
radii,
fontFamilies,
fontSizes,

Loading…
Cancel
Save