Browse Source

Merge branch 'master' of github.com:ledgerhq/ledger-live-desktop into pixel-push

master
Gaëtan Renaudeau 7 years ago
parent
commit
3679e51699
  1. 185
      src/components/MainSideBar.js
  2. 37
      src/components/MainSideBar/AccountListItem.js
  3. 43
      src/components/MainSideBar/AddAccountButton.js
  4. 155
      src/components/MainSideBar/index.js
  5. 23
      src/components/base/SideBar/SideBarList.js
  6. 17
      src/components/base/SideBar/SideBarListItem.js
  7. 1
      src/components/base/SideBar/index.js
  8. 21
      src/components/base/SideBar/stories.js

185
src/components/MainSideBar.js

@ -1,185 +0,0 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { withRouter } from 'react-router'
import { push } from 'react-router-redux'
import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import type { Location } from 'react-router'
import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common'
import type { UpdateStatus } from 'reducers/update'
import { MODAL_RECEIVE, MODAL_SEND } from 'config/constants'
import { rgba } from 'styles/helpers'
import { accountsSelector } from 'reducers/accounts'
import { openModal } from 'reducers/modals'
import { getUpdateStatus } from 'reducers/update'
import Tooltip from 'components/base/Tooltip'
import { SideBarList } from 'components/base/SideBar'
import Box, { Tabbable } from 'components/base/Box'
import Space from 'components/base/Space'
import FormattedVal from 'components/base/FormattedVal'
import IconManager from 'icons/Manager'
import IconPieChart from 'icons/PieChart'
import IconCirclePlus from 'icons/CirclePlus'
import IconReceive from 'icons/Receive'
import IconSend from 'icons/Send'
import IconExchange from 'icons/Exchange'
const mapStateToProps = state => ({
accounts: accountsSelector(state),
updateStatus: getUpdateStatus(state),
})
const mapDispatchToProps = {
push,
openModal,
}
type Props = {
t: T,
accounts: Account[],
location: Location,
push: string => void,
openModal: string => void,
updateStatus: UpdateStatus,
}
class MainSideBar extends PureComponent<Props> {
push(to: string) {
const { push } = this.props
const {
location: { pathname },
} = this.props
if (pathname === to) {
return
}
push(to)
}
render() {
const { t, accounts, openModal, location, updateStatus } = this.props
const { pathname } = location
const navigationItems = [
{
value: 'dashboard',
label: t('dashboard:title'),
icon: IconPieChart,
iconActiveColor: 'wallet',
onClick: () => this.push('/'),
isActive: pathname === '/',
hasNotif: updateStatus === 'downloaded',
},
{
value: 'send',
label: t('send:title'),
icon: IconSend,
iconActiveColor: 'wallet',
onClick: () => openModal(MODAL_SEND),
},
{
value: 'receive',
label: t('receive:title'),
icon: IconReceive,
iconActiveColor: 'wallet',
onClick: () => openModal(MODAL_RECEIVE),
},
{
value: 'manager',
label: t('sidebar:manager'),
icon: IconManager,
iconActiveColor: 'wallet',
onClick: () => this.push('/manager'),
isActive: pathname === '/manager',
},
{
value: 'exchange',
label: t('sidebar:exchange'),
icon: IconExchange,
iconActiveColor: 'wallet',
onClick: () => this.push('/exchange'),
isActive: pathname === '/exchange',
},
]
const accountsItems = accounts.map(account => {
const accountURL = `/account/${account.id}`
return {
value: account.id,
label: account.name,
desc: () => (
<FormattedVal
alwaysShowSign={false}
color="graphite"
unit={account.unit}
showCode
val={account.balance || 0}
/>
),
iconActiveColor: account.currency.color,
icon: getCryptoCurrencyIcon(account.currency),
onClick: () => this.push(accountURL),
isActive: pathname === accountURL,
}
})
return (
<Box bg="white" style={{ width: 230 }}>
<Space of={70} />
<SideBarList title={t('sidebar:menu')} items={navigationItems} />
<Space of={40} />
<SideBarList
scroll
title={t('sidebar:accounts')}
titleRight={
<Tooltip render={() => t('importAccounts:title')}>
<PlusWrapper onClick={() => openModal('importAccounts')}>
<IconCirclePlus size={16} />
</PlusWrapper>
</Tooltip>
}
items={accountsItems}
emptyText={t('emptyState:sidebar.text')}
/>
</Box>
)
}
}
const PlusWrapper = styled(Tabbable).attrs({
p: 1,
cursor: 'pointer',
borderRadius: 1,
})`
color: ${p => p.theme.colors.smoke};
&:hover {
color: ${p => p.theme.colors.dark};
}
border: 1px solid transparent;
&:focus {
outline: none;
border-color: ${p => rgba(p.theme.colors.wallet, 0.3)};
}
`
const decorate = compose(
withRouter,
translate(),
connect(
mapStateToProps,
mapDispatchToProps,
),
)
export default decorate(MainSideBar)

37
src/components/MainSideBar/AccountListItem.js

@ -0,0 +1,37 @@
// @flow
import React, { PureComponent } from 'react'
import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import type { Account } from '@ledgerhq/live-common/lib/types'
import FormattedVal from 'components/base/FormattedVal'
import { SideBarListItem } from 'components/base/SideBar'
export default class AccountListItem extends PureComponent<{
account: Account,
push: string => void,
isActive: boolean,
}> {
render() {
const { account, push, isActive } = this.props
const accountURL = `/account/${account.id}`
const item = {
label: account.name,
desc: () => (
<FormattedVal
alwaysShowSign={false}
color="graphite"
unit={account.unit}
showCode
val={account.balance || 0}
/>
),
iconActiveColor: account.currency.color,
icon: getCryptoCurrencyIcon(account.currency),
onClick: () => push(accountURL),
isActive,
}
return <SideBarListItem {...item} />
}
}

43
src/components/MainSideBar/AddAccountButton.js

@ -0,0 +1,43 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { Tabbable } from 'components/base/Box'
import Tooltip from 'components/base/Tooltip'
import IconCirclePlus from 'icons/CirclePlus'
import { rgba } from 'styles/helpers'
const PlusWrapper = styled(Tabbable).attrs({
p: 1,
cursor: 'pointer',
borderRadius: 1,
})`
color: ${p => p.theme.colors.smoke};
&:hover {
color: ${p => p.theme.colors.dark};
}
border: 1px solid transparent;
&:focus {
outline: none;
border-color: ${p => rgba(p.theme.colors.wallet, 0.3)};
}
`
export default class AddAccountButton extends PureComponent<{
onClick: () => void,
tooltipText: string,
}> {
render() {
const { onClick, tooltipText } = this.props
return (
<Tooltip render={() => tooltipText}>
<PlusWrapper onClick={onClick}>
<IconCirclePlus size={16} />
</PlusWrapper>
</Tooltip>
)
}
}

155
src/components/MainSideBar/index.js

@ -0,0 +1,155 @@
// @flow
import React, { PureComponent } from 'react'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { withRouter } from 'react-router'
import { push } from 'react-router-redux'
import type { Location } from 'react-router'
import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common'
import type { UpdateStatus } from 'reducers/update'
import { MODAL_RECEIVE, MODAL_SEND } from 'config/constants'
import { accountsSelector } from 'reducers/accounts'
import { openModal } from 'reducers/modals'
import { getUpdateStatus } from 'reducers/update'
import { SideBarList, SideBarListItem } from 'components/base/SideBar'
import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll'
import Space from 'components/base/Space'
import IconManager from 'icons/Manager'
import IconPieChart from 'icons/PieChart'
import IconReceive from 'icons/Receive'
import IconSend from 'icons/Send'
import IconExchange from 'icons/Exchange'
import AccountListItem from './AccountListItem'
import AddAccountButton from './AddAccountButton'
const mapStateToProps = state => ({
accounts: accountsSelector(state),
updateStatus: getUpdateStatus(state),
})
const mapDispatchToProps = {
push,
openModal,
}
type Props = {
t: T,
accounts: Account[],
location: Location,
push: string => void,
openModal: string => void,
updateStatus: UpdateStatus,
}
class MainSideBar extends PureComponent<Props> {
push = (to: string) => {
const { push } = this.props
const {
location: { pathname },
} = this.props
if (pathname === to) {
return
}
push(to)
}
handleClickDashboard = () => this.push('/')
handleOpenSendModal = () => this.props.openModal(MODAL_SEND)
handleOpenReceiveModal = () => this.props.openModal(MODAL_RECEIVE)
handleClickManager = () => this.push('/manager')
handleClickExchange = () => this.push('/exchange')
handleOpenImportModal = () => this.props.openModal('importAccounts')
render() {
const { t, accounts, location, updateStatus } = this.props
const { pathname } = location
const addAccountButton = (
<AddAccountButton
tooltipText={t('importAccounts:title')}
onClick={this.handleOpenImportModal}
/>
)
return (
<Box relative bg="white" style={{ width: 230 }}>
<GrowScroll>
<Space of={70} />
<SideBarList title={t('sidebar:menu')}>
<SideBarListItem
label={t('dashboard:title')}
icon={IconPieChart}
iconActiveColor={'wallet'}
onClick={this.handleClickDashboard}
isActive={pathname === '/'}
hasNotif={updateStatus === 'downloaded'}
/>
<SideBarListItem
label={t('send:title')}
icon={IconSend}
iconActiveColor={'wallet'}
onClick={this.handleOpenSendModal}
/>
<SideBarListItem
label={t('receive:title')}
icon={IconReceive}
iconActiveColor={'wallet'}
onClick={this.handleOpenReceiveModal}
/>
<SideBarListItem
label={t('sidebar:manager')}
icon={IconManager}
iconActiveColor={'wallet'}
onClick={this.handleClickManager}
isActive={pathname === '/manager'}
/>
<SideBarListItem
label={t('sidebar:exchange')}
icon={IconExchange}
iconActiveColor={'wallet'}
onClick={this.handleClickExchange}
isActive={pathname === '/exchange'}
/>
</SideBarList>
<Space of={40} />
<SideBarList
title={t('sidebar:accounts')}
titleRight={addAccountButton}
emptyText={t('emptyState:sidebar.text')}
>
{accounts.map(account => (
<AccountListItem
key={account.id}
account={account}
push={this.push}
isActive={pathname === `/account/${account.id}`}
/>
))}
</SideBarList>
<Space of={15} />
</GrowScroll>
</Box>
)
}
}
const decorate = compose(
withRouter,
translate(),
connect(
mapStateToProps,
mapDispatchToProps,
),
)
export default decorate(MainSideBar)

23
src/components/base/SideBar/SideBarList.js

@ -1,28 +1,23 @@
// @flow
import React, { PureComponent, Fragment } from 'react'
import React, { Component, Fragment } from 'react'
import styled from 'styled-components'
import GrowScroll from 'components/base/GrowScroll'
import Box from 'components/base/Box'
import Space from 'components/base/Space'
import SideBarListItem from './SideBarListItem'
import type { Item } from './SideBarListItem'
type Props = {
items: Item[],
children: any,
title?: Node | string,
activeValue?: string,
scroll?: boolean,
titleRight?: any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
emptyText?: string,
}
class SideBarList extends PureComponent<Props> {
class SideBarList extends Component<Props> {
render() {
const { items, title, activeValue, scroll, titleRight, emptyText, ...props } = this.props
const { children, title, scroll, titleRight, emptyText, ...props } = this.props
const ListWrapper = scroll ? GrowScroll : Box
return (
<Fragment>
@ -35,15 +30,9 @@ class SideBarList extends PureComponent<Props> {
<Space of={20} />
</Fragment>
)}
{items.length > 0 ? (
{children ? (
<ListWrapper flow={2} px={3} fontSize={3} {...props}>
{items.map(item => {
const itemProps = {
item,
isActive: item.isActive || (!!activeValue && activeValue === item.value),
}
return <SideBarListItem key={item.value} {...itemProps} />
})}
{children}
</ListWrapper>
) : emptyText ? (
<Box px={4} ff="Open Sans|Regular" fontSize={3} color="grey">

17
src/components/base/SideBar/SideBarListItem.js

@ -7,7 +7,6 @@ import Box, { Tabbable } from 'components/base/Box'
import { rgba } from 'styles/helpers'
export type Item = {
value: string,
label: string | (Props => React$Element<any>),
desc?: Props => any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
icon?: any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
@ -18,20 +17,22 @@ export type Item = {
}
export type Props = {
item: Item,
isActive: boolean,
label: string | (Props => React$Element<any>),
desc?: Props => any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
icon?: any, // TODO: type should be more precise, but, eh ¯\_(ツ)_/¯
iconActiveColor: ?string,
hasNotif?: boolean,
isActive?: boolean,
onClick?: void => void,
isActive?: boolean,
}
class SideBarListItem extends PureComponent<Props> {
render() {
const {
item: { icon: Icon, label, desc, iconActiveColor, hasNotif, onClick, value },
isActive,
} = this.props
const { icon: Icon, label, desc, iconActiveColor, hasNotif, onClick, isActive } = this.props
return (
<Container
data-role="side-bar-item"
data-roledata={value}
isActive={isActive}
iconActiveColor={iconActiveColor}
onClick={onClick}

1
src/components/base/SideBar/index.js

@ -1,3 +1,4 @@
// @flow
export { default as SideBarList } from './SideBarList'
export { default as SideBarListItem } from './SideBarListItem'

21
src/components/base/SideBar/stories.js

@ -2,9 +2,8 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { select } from '@storybook/addon-knobs'
import { SideBarList } from 'components/base/SideBar'
import { SideBarList, SideBarListItem } from 'components/base/SideBar'
import Box from 'components/base/Box'
import IconAccountSettings from 'icons/AccountSettings'
@ -17,26 +16,27 @@ const stories = storiesOf('Components/base/SideBar', module)
const SIDEBAR_ITEMS = [
{
value: 'first',
key: 'first',
label: 'First',
icon: IconAccountSettings,
iconActiveColor: '#ffae35',
},
{
value: 'second',
key: 'second',
label: 'Second',
icon: IconPrint,
iconActiveColor: '#0ebdcd',
isActive: true,
},
{
value: 'third',
key: 'third',
label: 'Third very very very very long text very long text very long',
icon: IconControls,
iconActiveColor: '#27a2db',
hasNotif: true,
},
{
value: 'fourth',
key: 'fourth',
label: () => (
<Box>
{'custom'}
@ -47,7 +47,7 @@ const SIDEBAR_ITEMS = [
iconActiveColor: '#3ca569',
},
{
value: 'fifth',
key: 'fifth',
label: 'Fifth',
icon: IconExclamationCircle,
iconActiveColor: '#0e76aa',
@ -55,8 +55,7 @@ const SIDEBAR_ITEMS = [
]
stories.add('SideBarList', () => (
<SideBarList
items={SIDEBAR_ITEMS}
activeValue={select('activeValue', [...SIDEBAR_ITEMS.map(i => i.value), null], 'third')}
/>
<SideBarList>
{SIDEBAR_ITEMS.map(item => <SideBarListItem key={item.key} {...item} />)}
</SideBarList>
))

Loading…
Cancel
Save