Browse Source

Merge branch 'master' into release-notes

master
Thibaut 7 years ago
committed by GitHub
parent
commit
8f00c30fb8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      package.json
  2. 46
      src/api/Ethereum.js
  3. 6
      src/api/Fees.js
  4. 22
      src/api/network.js
  5. 6
      src/components/AccountPage/index.js
  6. 4
      src/components/BalanceSummary/index.js
  7. 2
      src/components/DashboardPage/AccountCard.js
  8. 6
      src/components/DashboardPage/index.js
  9. 185
      src/components/MainSideBar.js
  10. 37
      src/components/MainSideBar/AccountListItem.js
  11. 43
      src/components/MainSideBar/AddAccountButton.js
  12. 155
      src/components/MainSideBar/index.js
  13. 30
      src/components/OperationsList/index.js
  14. 6
      src/components/OperationsList/stories.js
  15. 8
      src/components/SelectCurrency/index.js
  16. 2
      src/components/base/Chart/helpers.js
  17. 6
      src/components/base/Modal/ModalBody.js
  18. 7
      src/components/base/Select/index.js
  19. 23
      src/components/base/SideBar/SideBarList.js
  20. 17
      src/components/base/SideBar/SideBarListItem.js
  21. 1
      src/components/base/SideBar/index.js
  22. 21
      src/components/base/SideBar/stories.js
  23. 2
      src/components/modals/OperationDetails.js
  24. 3
      src/config/constants.js
  25. 3
      src/helpers/derivations.js
  26. 5
      src/helpers/promise.js
  27. 3
      src/helpers/withLibcore.js
  28. 2
      src/main/app.js
  29. 2
      static/i18n/en/sidebar.yml
  30. 12
      static/i18n/fr/importAccounts.yml
  31. 3
      static/i18n/fr/releaseNotes.yml
  32. 4
      static/i18n/fr/settings.yml
  33. 2
      static/i18n/fr/sidebar.yml
  34. 6
      yarn.lock

4
package.json

@ -3,7 +3,7 @@
"productName": "Ledger Live",
"description": "Ledger Live - Desktop",
"repository": "https://github.com/LedgerHQ/ledger-live-desktop",
"version": "0.1.0-alpha.7",
"version": "0.1.0-alpha.8",
"author": "Ledger",
"license": "MIT",
"scripts": {
@ -41,7 +41,7 @@
"@ledgerhq/hw-app-xrp": "^4.13.0",
"@ledgerhq/hw-transport": "^4.13.0",
"@ledgerhq/hw-transport-node-hid": "^4.13.0",
"@ledgerhq/ledger-core": "1.4.5",
"@ledgerhq/ledger-core": "1.6.0",
"@ledgerhq/live-common": "2.29.0",
"async": "^2.6.1",
"axios": "^0.18.0",

46
src/api/Ethereum.js

@ -1,8 +1,7 @@
// @flow
import axios from 'axios'
import { retry } from 'helpers/promise'
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { blockchainBaseURL, userFriendlyError } from './Ledger'
import network from './network'
import { blockchainBaseURL } from './Ledger'
export type Block = { height: number } // TODO more fields actually
export type Tx = {
@ -47,37 +46,40 @@ export const apiForCurrency = (currency: CryptoCurrency): API => {
}
return {
async getTransactions(address, blockHash) {
const { data } = await userFriendlyError(
retry(
() =>
axios.get(`${baseURL}/addresses/${address}/transactions`, {
params: { blockHash, noToken: 1 },
}),
{ maxRetry: 3 },
),
)
const { data } = await network({
method: 'GET',
url: `${baseURL}/addresses/${address}/transactions`,
params: { blockHash, noToken: 1 },
})
return data
},
async getCurrentBlock() {
const { data } = await userFriendlyError(
retry(() => axios.get(`${baseURL}/blocks/current`), { maxRetry: 3 }),
)
const { data } = await network({
method: 'GET',
url: `${baseURL}/blocks/current`,
})
return data
},
async getAccountNonce(address) {
const { data } = await userFriendlyError(
retry(() => axios.get(`${baseURL}/addresses/${address}/nonce`), { maxRetry: 3 }),
)
const { data } = await network({
method: 'GET',
url: `${baseURL}/addresses/${address}/nonce`,
})
return data[0].nonce
},
async broadcastTransaction(tx) {
const { data } = await userFriendlyError(axios.post(`${baseURL}/transactions/send`, { tx }))
const { data } = await network({
method: 'POST',
url: `${baseURL}/transactions/send`,
data: { tx },
})
return data.result
},
async getAccountBalance(address) {
const { data } = await userFriendlyError(
retry(() => axios.get(`${baseURL}/addresses/${address}/balance`), { maxRetry: 3 }),
)
const { data } = await network({
method: 'GET',
url: `${baseURL}/addresses/${address}/balance`,
})
return data[0].balance
},
}

6
src/api/Fees.js

@ -1,8 +1,8 @@
// @flow
import invariant from 'invariant'
import axios from 'axios'
import type { Currency } from '@ledgerhq/live-common/lib/types'
import { blockchainBaseURL, userFriendlyError } from './Ledger'
import { blockchainBaseURL } from './Ledger'
import network from './network'
export type Fees = {
[_: string]: number,
@ -11,7 +11,7 @@ export type Fees = {
export const getEstimatedFees = async (currency: Currency): Promise<Fees> => {
const baseURL = blockchainBaseURL(currency)
invariant(baseURL, `Fees for ${currency.id} are not supported`)
const { data, status } = await userFriendlyError(axios.get(`${baseURL}/fees`))
const { data, status } = await network({ method: 'GET', url: `${baseURL}/fees` })
if (data) {
return data
}

22
src/api/network.js

@ -0,0 +1,22 @@
// @flow
import axios from 'axios'
import { GET_CALLS_RETRY, GET_CALLS_TIMEOUT } from 'config/constants'
import { userFriendlyError } from 'api/Ledger'
import { retry } from 'helpers/promise'
const doRequest = axios // TODO later introduce a way to run it in renderer based on a env, we will diverge this implementation
export default (arg: Object) => {
let promise
if (arg.method === 'GET') {
if (!('timeout' in arg)) {
arg.timeout = GET_CALLS_TIMEOUT
}
promise = retry(() => doRequest(arg), {
maxRetry: GET_CALLS_RETRY,
})
} else {
promise = doRequest(arg)
}
return userFriendlyError(promise)
}

6
src/components/AccountPage/index.js

@ -74,8 +74,8 @@ type State = {
class AccountPage extends PureComponent<Props, State> {
state = {
selectedTime: 'week',
daysCount: 7,
selectedTime: 'month',
daysCount: 30,
}
handleChangeSelectedTime = item =>
@ -180,7 +180,7 @@ class AccountPage extends PureComponent<Props, State> {
)}
/>
</Box>
<OperationsList canShowMore account={account} title={t('account:lastOperations')} />
<OperationsList account={account} title={t('account:lastOperations')} />
</Fragment>
) : (
<EmptyStateAccount account={account} />

4
src/components/BalanceSummary/index.js

@ -35,7 +35,7 @@ const BalanceSummary = ({
}: Props) => {
const account = accounts.length === 1 ? accounts[0] : undefined
return (
<Card p={0} py={6}>
<Card p={0} py={5}>
<CalculateBalance accounts={accounts} daysCount={daysCount}>
{({ isAvailable, balanceHistory, balanceStart, balanceEnd }) =>
!isAvailable ? null : (
@ -57,7 +57,7 @@ const BalanceSummary = ({
unit={account ? account.unit : null}
color={chartColor}
data={balanceHistory}
height={250}
height={200}
currency={counterValue}
tickXScale={selectedTime}
renderTickY={val => formatShort(counterValue.units[0], val)}

2
src/components/DashboardPage/AccountCard.js

@ -41,7 +41,7 @@ class AccountCard extends PureComponent<{
</Box>
<Box>
<Box style={{ textTransform: 'uppercase' }} fontSize={0} color="graphite">
{account.unit.code}
{account.currency.name}
</Box>
<Box fontSize={4} color="dark">
{account.name}

6
src/components/DashboardPage/index.js

@ -56,8 +56,9 @@ type State = {
class DashboardPage extends PureComponent<Props, State> {
state = {
selectedTime: 'week',
daysCount: 7,
// save to user preference?
selectedTime: 'month',
daysCount: 30,
}
onAccountClick = account => this.props.push(`/account/${account.id}`)
@ -167,7 +168,6 @@ class DashboardPage extends PureComponent<Props, State> {
</Box>
{displayOperations && (
<OperationsList
canShowMore
onAccountClick={this.onAccountClick}
accounts={accounts}
title={t('dashboard:recentActivity')}

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', { count: accounts.length })}
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)

30
src/components/OperationsList/index.js

@ -52,8 +52,7 @@ const mapDispatchToProps = {
type Props = {
account: Account,
accounts: Account[],
canShowMore: boolean,
openModal: Function,
openModal: (string, Object) => *,
t: T,
withAccount?: boolean,
title?: string,
@ -66,10 +65,18 @@ type State = {
const initialState = {
nbToShow: 20,
}
const footerPlaceholder = (
<Box p={4} align="center">
<Text ff="Open Sans" fontSize={3}>
No more operations
</Text>
</Box>
)
export class OperationsList extends PureComponent<Props, State> {
static defaultProps = {
withAccount: false,
canShowMore: false,
}
state = initialState
@ -86,7 +93,7 @@ export class OperationsList extends PureComponent<Props, State> {
}
render() {
const { account, accounts, canShowMore, t, title, withAccount } = this.props
const { account, accounts, t, title, withAccount } = this.props
const { nbToShow } = this.state
if (!account && !accounts) {
@ -130,13 +137,14 @@ export class OperationsList extends PureComponent<Props, State> {
</Card>
</Box>
))}
{canShowMore &&
!groupedOperations.completed && (
<ShowMore onClick={this.fetchMoreOperations}>
<span>{t('operationsList:showMore')}</span>
<IconAngleDown size={12} />
</ShowMore>
)}
{!groupedOperations.completed ? (
<ShowMore onClick={this.fetchMoreOperations}>
<span>{t('operationsList:showMore')}</span>
<IconAngleDown size={12} />
</ShowMore>
) : (
footerPlaceholder
)}
</Box>
</Defer>
)

6
src/components/OperationsList/stories.js

@ -15,10 +15,6 @@ const account2 = genAccount('account2')
stories.add('OperationsList', () => (
<Box bg="lightGrey" p={6} m={-4}>
<OperationsList
accounts={[account1, account2]}
canShowMore={boolean('canShowMore')}
withAccount={boolean('withAccount')}
/>
<OperationsList accounts={[account1, account2]} withAccount={boolean('withAccount')} />
</Box>
))

8
src/components/SelectCurrency/index.js

@ -14,7 +14,7 @@ import Select from 'components/base/Select'
import Box from 'components/base/Box'
type OwnProps = {
onChange: Option => void,
onChange: (?Option) => void,
currencies?: CryptoCurrency[],
value?: CryptoCurrency,
placeholder: string,
@ -30,7 +30,9 @@ const mapStateToProps = (state, props: OwnProps) => ({
})
const SelectCurrency = ({ onChange, value, t, placeholder, currencies, ...props }: Props) => {
const options = currencies ? currencies.map(c => ({ ...c, value: c.id, label: c.name })) : []
const options = currencies
? currencies.map(c => ({ ...c, value: c.id, label: c.name, currency: c }))
: []
return (
<Select
value={value}
@ -38,7 +40,7 @@ const SelectCurrency = ({ onChange, value, t, placeholder, currencies, ...props
renderValue={renderOption}
options={options}
placeholder={placeholder || t('common:selectCurrency')}
onChange={onChange}
onChange={item => onChange(item ? item.currency : null)}
{...props}
/>
)

2
src/components/base/Chart/helpers.js

@ -25,7 +25,7 @@ export function generateMargins(hideAxis) {
top: hideAxis ? 5 : 10,
bottom: hideAxis ? 5 : 30,
right: hideAxis ? 5 : 40,
left: hideAxis ? 5 : 80,
left: hideAxis ? 5 : 70,
}
// FIXME: Forced to "use" margins here to prevent babel/uglify to believe

6
src/components/base/Modal/ModalBody.js

@ -55,13 +55,13 @@ class ModalBody extends PureComponent<Props, State> {
}
const CloseContainer = styled(Box).attrs({
p: 4,
p: 2,
color: 'fog',
})`
cursor: pointer;
position: absolute;
top: 0;
right: 0;
top: 25px;
right: 10px;
z-index: 1;
&:hover {

7
src/components/base/Select/index.js

@ -11,7 +11,7 @@ type Props = {
// required
value: ?Option,
options: Option[],
onChange: Option => void,
onChange: (?Option) => void,
// custom renders
renderOption: Option => Node,
@ -40,6 +40,9 @@ class Select extends Component<Props> {
if (action === 'select-option') {
onChange(value)
}
if (action === 'pop-value') {
onChange(null)
}
}
render() {
@ -73,10 +76,10 @@ class Select extends Component<Props> {
isClearable={isClearable}
isSearchable={isSearchable}
blurInputOnSelect={false}
onChange={this.handleChange}
backspaceRemovesValue
menuShouldBlockScroll
{...props}
onChange={this.handleChange}
/>
)
}

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>
))

2
src/components/modals/OperationDetails.js

@ -160,7 +160,7 @@ const OperationDetails = connect(mapStateToProps)((props: Props) => {
<Line>
<ColLeft>Fees</ColLeft>
<ColRight>
<FormattedVal unit={unit} showCode val={fee} />
<FormattedVal unit={unit} showCode val={fee} color="dark" />
</ColRight>
</Line>
<B />

3
src/config/constants.js

@ -6,6 +6,9 @@ const intFromEnv = (key: string, def: number) => {
return def
}
export const GET_CALLS_TIMEOUT = intFromEnv('GET_CALLS_TIMEOUT', 30 * 1000)
export const GET_CALLS_RETRY = intFromEnv('GET_CALLS_RETRY', 2)
export const SYNC_MAX_CONCURRENT = intFromEnv('LEDGER_SYNC_MAX_CONCURRENT', 2)
export const SYNC_BOOT_DELAY = 2 * 1000
export const SYNC_ALL_INTERVAL = 60 * 1000

3
src/helpers/derivations.js

@ -11,9 +11,12 @@ const ethLegacyMEW: Derivation = ({ x }) => `44'/60'/0'/${x}`
const etcLegacyMEW: Derivation = ({ x }) => `44'/60'/160720'/${x}`
const rippleLegacy: Derivation = ({ x }) => `44'/144'/0'/${x}'`
const legacyDerivations = {
ethereum: [ethLegacyMEW],
ethereum_classic: [etcLegacyMEW],
ripple: [rippleLegacy],
}
export const standardDerivation: Derivation = ({ currency, segwit, x }) => {

5
src/helpers/promise.js

@ -1,7 +1,8 @@
// @flow
import logger from 'logger'
// small utilities for Promises
import logger from 'logger'
export const delay = (ms: number): Promise<void> => new Promise(f => setTimeout(f, ms))
const defaults = {
@ -21,7 +22,7 @@ export function retry<A>(f: () => Promise<A>, options?: $Shape<typeof defaults>)
}
// In case of failure, wait the interval, retry the action
return result.catch(e => {
logger.warn('Promise#retry', e)
logger.warn('retry failed', e.message)
return delay(interval).then(() => rec(remainingTry - 1, interval * intervalMultiplicator))
})
}

3
src/helpers/withLibcore.js

@ -1,9 +1,12 @@
// @flow
import invariant from 'invariant'
import network from 'api/network'
const core = require('@ledgerhq/ledger-core')
core.setHttpQueryImplementation(network)
let walletPoolInstance: ?Object = null
// TODO: `core` and `NJSWalletPool` should be typed

2
src/main/app.js

@ -65,7 +65,7 @@ const defaultWindowOptions = {
}
function createMainWindow() {
const MIN_HEIGHT = 768
const MIN_HEIGHT = 720
const MIN_WIDTH = 1024
const savedDimensions = db.getIn('settings', 'window.MainWindow.dimensions', {})

2
static/i18n/en/sidebar.yml

@ -1,4 +1,4 @@
menu: Menu
accounts: Accounts
accounts: Accounts ({{count}})
manager: Manager
exchange: Exchange

12
static/i18n/fr/importAccounts.yml

@ -1,7 +1,17 @@
---
title: Import accounts
title: Add accounts
breadcrumb:
informations: Informations
connectDevice: Connect device
import: Import
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'

3
static/i18n/fr/releaseNotes.yml

@ -0,0 +1,3 @@
---
title: Release notes
version: Version {{versionNb}}

4
static/i18n/fr/settings.yml

@ -58,6 +58,10 @@ softResetModal:
title: Clean application cache
subTitle: Are you sure houston?
desc: Lorem ipsum dolor sit amet
removeAccountModal:
title: Delete this account
subTitle: Are you sure houston?
desc: Lorem ipsum dolor sit amet
exportLogs:
title: Export Logs
desc: Export Logs

2
static/i18n/fr/sidebar.yml

@ -1,5 +1,5 @@
---
menu: Menu
accounts: Comptes
accounts: Accounts ({{count}})
manager: Manager
exchange: Exchange

6
yarn.lock

@ -1502,9 +1502,9 @@
dependencies:
events "^2.0.0"
"@ledgerhq/ledger-core@1.4.5":
version "1.4.5"
resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-1.4.5.tgz#d3cf00ac07307d419b09e4cc1de8a8764ef16338"
"@ledgerhq/ledger-core@1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-1.6.0.tgz#885184cfc14598487894de2b9893c4a08798b074"
dependencies:
"@ledgerhq/hw-app-btc" "^4.7.3"
"@ledgerhq/hw-transport-node-hid" "^4.7.6"

Loading…
Cancel
Save