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
|
// @flow
|
||||
import { shell } from 'electron' |
|
||||
import { track } from 'analytics/segment' |
import { track } from 'analytics/segment' |
||||
|
|
||||
|
let shell |
||||
|
if (!process.env.STORYBOOK_ENV) { |
||||
|
const electron = require('electron') |
||||
|
shell = electron.shell |
||||
|
} |
||||
|
|
||||
export const openURL = ( |
export const openURL = ( |
||||
url: string, |
url: string, |
||||
customEventName: string = 'OpenURL', |
customEventName: string = 'OpenURL', |
||||
extraParams: Object = {}, |
extraParams: Object = {}, |
||||
) => { |
) => { |
||||
track(customEventName, { ...extraParams, url }) |
track(customEventName, { ...extraParams, url }) |
||||
shell.openExternal(url) |
shell && shell.openExternal(url) |
||||
} |
} |
||||
|
Loading…
Reference in new issue