Gaëtan Renaudeau
7 years ago
19 changed files with 480 additions and 288 deletions
@ -1,103 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import styled from 'styled-components' |
|||
|
|||
import type { Account, Currency } from '@ledgerhq/live-common/lib/types' |
|||
|
|||
import Chart from 'components/base/Chart' |
|||
import Bar from 'components/base/Bar' |
|||
import Box, { Card } from 'components/base/Box' |
|||
import CalculateBalance from 'components/CalculateBalance' |
|||
import FormattedVal from 'components/base/FormattedVal' |
|||
import Ellipsis from 'components/base/Ellipsis' |
|||
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' |
|||
import DeltaChange from '../DeltaChange' |
|||
|
|||
const Wrapper = styled(Card).attrs({ |
|||
p: 4, |
|||
flex: 1, |
|||
})` |
|||
cursor: ${p => (p.onClick ? 'pointer' : 'default')}; |
|||
` |
|||
|
|||
class AccountCard extends PureComponent<{ |
|||
counterValue: Currency, |
|||
account: Account, |
|||
onClick?: Account => void, |
|||
daysCount: number, |
|||
}> { |
|||
render() { |
|||
const { counterValue, account, onClick, daysCount, ...props } = this.props |
|||
return ( |
|||
<Wrapper onClick={onClick ? () => onClick(account) : null} {...props}> |
|||
<Box flow={4}> |
|||
<Box horizontal ff="Open Sans|SemiBold" flow={3} alignItems="center"> |
|||
<Box |
|||
alignItems="center" |
|||
justifyContent="center" |
|||
style={{ color: account.currency.color }} |
|||
> |
|||
<CryptoCurrencyIcon currency={account.currency} size={20} /> |
|||
</Box> |
|||
<Box grow> |
|||
<Box style={{ textTransform: 'uppercase' }} fontSize={0} color="graphite"> |
|||
{account.currency.name} |
|||
</Box> |
|||
<Ellipsis fontSize={4} color="dark"> |
|||
{account.name} |
|||
</Ellipsis> |
|||
</Box> |
|||
</Box> |
|||
<Bar size={1} color="fog" /> |
|||
<Box justifyContent="center"> |
|||
<FormattedVal |
|||
alwaysShowSign={false} |
|||
color="dark" |
|||
unit={account.unit} |
|||
showCode |
|||
val={account.balance} |
|||
/> |
|||
</Box> |
|||
</Box> |
|||
<CalculateBalance counterValue={counterValue} accounts={[account]} daysCount={daysCount}> |
|||
{({ isAvailable, balanceHistory, balanceStart, balanceEnd }) => ( |
|||
<Box flow={4}> |
|||
<Box flow={2} horizontal> |
|||
<Box justifyContent="center"> |
|||
{isAvailable ? ( |
|||
<FormattedVal |
|||
animateTicker |
|||
unit={counterValue.units[0]} |
|||
val={balanceEnd} |
|||
alwaysShowSign={false} |
|||
showCode |
|||
fontSize={3} |
|||
color="graphite" |
|||
/> |
|||
) : null} |
|||
</Box> |
|||
<Box grow justifyContent="center"> |
|||
{isAvailable && !balanceStart.isZero() ? ( |
|||
<DeltaChange from={balanceStart} to={balanceEnd} alwaysShowSign fontSize={3} /> |
|||
) : null} |
|||
</Box> |
|||
</Box> |
|||
<Chart |
|||
data={balanceHistory} |
|||
color={account.currency.color} |
|||
height={52} |
|||
hideAxis |
|||
isInteractive={false} |
|||
id={`account-chart-${account.id}`} |
|||
unit={account.unit} |
|||
/> |
|||
</Box> |
|||
)} |
|||
</CalculateBalance> |
|||
</Wrapper> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default AccountCard |
@ -0,0 +1,33 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' |
|||
import Box from 'components/base/Box' |
|||
import Ellipsis from 'components/base/Ellipsis' |
|||
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' |
|||
|
|||
class AccountCardHeader extends PureComponent<{ |
|||
currency: CryptoCurrency, |
|||
accountName: string, |
|||
}> { |
|||
render() { |
|||
const { currency, accountName } = this.props |
|||
return ( |
|||
<Box horizontal ff="Open Sans|SemiBold" flow={3} alignItems="center"> |
|||
<Box alignItems="center" justifyContent="center" style={{ color: currency.color }}> |
|||
<CryptoCurrencyIcon currency={currency} size={20} /> |
|||
</Box> |
|||
<Box grow> |
|||
<Box style={{ textTransform: 'uppercase' }} fontSize={0} color="graphite"> |
|||
{currency.name} |
|||
</Box> |
|||
<Ellipsis fontSize={4} color="dark"> |
|||
{accountName} |
|||
</Ellipsis> |
|||
</Box> |
|||
</Box> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default AccountCardHeader |
@ -0,0 +1,94 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import styled from 'styled-components' |
|||
|
|||
import type { Account, Currency } from '@ledgerhq/live-common/lib/types' |
|||
|
|||
import Chart from 'components/base/Chart' |
|||
import Bar from 'components/base/Bar' |
|||
import Box, { Card } from 'components/base/Box' |
|||
import CalculateBalance from 'components/CalculateBalance' |
|||
import FormattedVal from 'components/base/FormattedVal' |
|||
import DeltaChange from 'components/DeltaChange' |
|||
import AccountCardHeader from './Header' |
|||
|
|||
const Wrapper = styled(Card).attrs({ |
|||
p: 4, |
|||
flex: 1, |
|||
})` |
|||
cursor: ${p => (p.onClick ? 'pointer' : 'default')}; |
|||
` |
|||
|
|||
class AccountCard extends PureComponent<{ |
|||
counterValue: Currency, |
|||
account: Account, |
|||
onClick: Account => void, |
|||
daysCount: number, |
|||
}> { |
|||
renderBody = ({ isAvailable, balanceHistory, balanceStart, balanceEnd }: *) => { |
|||
const { counterValue, account } = this.props |
|||
return ( |
|||
<Box flow={4}> |
|||
<Box flow={2} horizontal> |
|||
<Box justifyContent="center"> |
|||
{isAvailable ? ( |
|||
<FormattedVal |
|||
animateTicker |
|||
unit={counterValue.units[0]} |
|||
val={balanceEnd} |
|||
alwaysShowSign={false} |
|||
showCode |
|||
fontSize={3} |
|||
color="graphite" |
|||
/> |
|||
) : null} |
|||
</Box> |
|||
<Box grow justifyContent="center"> |
|||
{isAvailable && !balanceStart.isZero() ? ( |
|||
<DeltaChange from={balanceStart} to={balanceEnd} alwaysShowSign fontSize={3} /> |
|||
) : null} |
|||
</Box> |
|||
</Box> |
|||
<Chart |
|||
data={balanceHistory} |
|||
color={account.currency.color} |
|||
height={52} |
|||
hideAxis |
|||
isInteractive={false} |
|||
id={`account-chart-${account.id}`} |
|||
unit={account.unit} |
|||
/> |
|||
</Box> |
|||
) |
|||
} |
|||
onClick = () => { |
|||
const { account, onClick } = this.props |
|||
onClick(account) |
|||
} |
|||
render() { |
|||
const { counterValue, account, onClick, daysCount, ...props } = this.props |
|||
return ( |
|||
<Wrapper onClick={this.onClick} {...props}> |
|||
<Box flow={4}> |
|||
<AccountCardHeader accountName={account.name} currency={account.currency} /> |
|||
<Bar size={1} color="fog" /> |
|||
<Box justifyContent="center"> |
|||
<FormattedVal |
|||
alwaysShowSign={false} |
|||
color="dark" |
|||
unit={account.unit} |
|||
showCode |
|||
val={account.balance} |
|||
/> |
|||
</Box> |
|||
</Box> |
|||
<CalculateBalance counterValue={counterValue} accounts={[account]} daysCount={daysCount}> |
|||
{this.renderBody} |
|||
</CalculateBalance> |
|||
</Wrapper> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default AccountCard |
@ -0,0 +1,66 @@ |
|||
// @flow
|
|||
|
|||
import React, { Component } from 'react' |
|||
import type { Account, Currency } from '@ledgerhq/live-common/lib/types' |
|||
|
|||
import Box from 'components/base/Box' |
|||
import AccountCard from './AccountCard' |
|||
import AccountCardListHeader from './AccountCardListHeader' |
|||
import AccountCardPlaceholder from './AccountCardPlaceholder' |
|||
|
|||
type Props = { |
|||
accounts: Account[], |
|||
onAccountClick: Account => void, |
|||
counterValue: Currency, |
|||
daysCount: number, |
|||
} |
|||
|
|||
class AccountCardList extends Component<Props> { |
|||
render() { |
|||
const { accounts, counterValue, daysCount, onAccountClick } = this.props |
|||
|
|||
return ( |
|||
<Box flow={4}> |
|||
<AccountCardListHeader accountsLength={accounts.length} /> |
|||
<Box |
|||
horizontal |
|||
flexWrap="wrap" |
|||
justifyContent="flex-start" |
|||
alignItems="center" |
|||
style={{ margin: '0 -16px' }} |
|||
> |
|||
{accounts |
|||
.map(account => ({ |
|||
key: account.id, |
|||
account, |
|||
})) |
|||
.concat( |
|||
Array(3 - (accounts.length % 3)) |
|||
.fill(null) |
|||
.map((_, i) => ({ |
|||
key: `placeholder_${i}`, |
|||
withPlaceholder: i === 0, |
|||
})), |
|||
) |
|||
.map(item => ( |
|||
<Box key={item.key} flex="33%" p={16}> |
|||
{item.account ? ( |
|||
<AccountCard |
|||
key={item.account.id} |
|||
counterValue={counterValue} |
|||
account={item.account} |
|||
daysCount={daysCount} |
|||
onClick={onAccountClick} |
|||
/> |
|||
) : item.withPlaceholder ? ( |
|||
<AccountCardPlaceholder /> |
|||
) : null} |
|||
</Box> |
|||
))} |
|||
</Box> |
|||
</Box> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default AccountCardList |
@ -0,0 +1,33 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import { translate } from 'react-i18next' |
|||
import type { T } from 'types/common' |
|||
|
|||
import Box from 'components/base/Box' |
|||
import Text from 'components/base/Text' |
|||
import AccountsOrder from './AccountsOrder' |
|||
|
|||
type Props = { |
|||
t: T, |
|||
accountsLength: number, |
|||
} |
|||
|
|||
class AccountCardListHeader extends PureComponent<Props> { |
|||
render() { |
|||
const { accountsLength, t } = this.props |
|||
|
|||
return ( |
|||
<Box horizontal alignItems="flex-end"> |
|||
<Text color="dark" ff="Museo Sans" fontSize={6}> |
|||
{t('app:dashboard.accounts.title', { count: accountsLength })} |
|||
</Text> |
|||
<Box ml="auto" horizontal flow={1}> |
|||
<AccountsOrder /> |
|||
</Box> |
|||
</Box> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default translate()(AccountCardListHeader) |
@ -0,0 +1,64 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import { connect } from 'react-redux' |
|||
import { translate } from 'react-i18next' |
|||
import styled from 'styled-components' |
|||
|
|||
import { openModal } from 'reducers/modals' |
|||
import { MODAL_ADD_ACCOUNTS } from 'config/constants' |
|||
import type { T } from 'types/common' |
|||
import { i } from 'helpers/staticPath' |
|||
import Box from 'components/base/Box' |
|||
import Button from 'components/base/Button' |
|||
|
|||
const Wrapper = styled(Box).attrs({ |
|||
p: 4, |
|||
flex: 1, |
|||
alignItems: 'center', |
|||
})` |
|||
border: 1px dashed ${p => p.theme.colors.fog}; |
|||
border-radius: 4px; |
|||
height: 215px; |
|||
` |
|||
|
|||
class AccountCardPlaceholder extends PureComponent<{ |
|||
t: T, |
|||
openModal: string => void, |
|||
}> { |
|||
onAddAccounts = () => this.props.openModal(MODAL_ADD_ACCOUNTS) |
|||
|
|||
render() { |
|||
const { t } = this.props |
|||
return ( |
|||
<Wrapper> |
|||
<Box mt={2}> |
|||
<img alt="" src={i('empty-account-tile.svg')} /> |
|||
</Box> |
|||
<Box |
|||
ff="Open Sans" |
|||
fontSize={3} |
|||
color="grey" |
|||
pb={2} |
|||
mt={3} |
|||
textAlign="center" |
|||
style={{ maxWidth: 150 }} |
|||
> |
|||
{t('app:dashboard.emptyAccountTile.desc')} |
|||
</Box> |
|||
<Button primary onClick={this.onAddAccounts}> |
|||
{t('app:dashboard.emptyAccountTile.createAccount')} |
|||
</Button> |
|||
</Wrapper> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default translate()( |
|||
connect( |
|||
null, |
|||
{ |
|||
openModal, |
|||
}, |
|||
)(AccountCardPlaceholder), |
|||
) |
@ -0,0 +1,31 @@ |
|||
// @flow
|
|||
import React, { PureComponent } from 'react' |
|||
import { translate } from 'react-i18next' |
|||
import type { T } from 'types/common' |
|||
|
|||
import Text from 'components/base/Text' |
|||
|
|||
const getCurrentGreetings = () => { |
|||
const localTimeHour = new Date().getHours() |
|||
const afternoon_breakpoint = 12 |
|||
const evening_breakpoint = 17 |
|||
if (localTimeHour >= afternoon_breakpoint && localTimeHour < evening_breakpoint) { |
|||
return 'app:dashboard.greeting.afternoon' |
|||
} else if (localTimeHour >= evening_breakpoint) { |
|||
return 'app:dashboard.greeting.evening' |
|||
} |
|||
return 'app:dashboard.greeting.morning' |
|||
} |
|||
|
|||
class CurrentGettings extends PureComponent<{ t: T }> { |
|||
render() { |
|||
const { t } = this.props |
|||
return ( |
|||
<Text color="dark" ff="Museo Sans" fontSize={7}> |
|||
{t(getCurrentGreetings())} |
|||
</Text> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default translate()(CurrentGettings) |
@ -0,0 +1,22 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import { translate } from 'react-i18next' |
|||
import type { T } from 'types/common' |
|||
import Text from 'components/base/Text' |
|||
|
|||
class SummaryDesc extends PureComponent<{ |
|||
t: T, |
|||
totalAccounts: number, |
|||
}> { |
|||
render() { |
|||
const { totalAccounts, t } = this.props |
|||
return ( |
|||
<Text color="grey" fontSize={5} ff="Museo Sans|Light"> |
|||
{t('app:dashboard.summary', { count: totalAccounts })} |
|||
</Text> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default translate()(SummaryDesc) |
@ -1,12 +1,17 @@ |
|||
// @flow
|
|||
import { shell } from 'electron' |
|||
import { track } from 'analytics/segment' |
|||
|
|||
let shell |
|||
if (!process.env.STORYBOOK_ENV) { |
|||
const electron = require('electron') |
|||
shell = electron.shell |
|||
} |
|||
|
|||
export const openURL = ( |
|||
url: string, |
|||
customEventName: string = 'OpenURL', |
|||
extraParams: Object = {}, |
|||
) => { |
|||
track(customEventName, { ...extraParams, url }) |
|||
shell.openExternal(url) |
|||
shell && shell.openExternal(url) |
|||
} |
|||
|
Loading…
Reference in new issue