Browse Source

Merge pull request #61 from loeck/master

Add ImportAccounts component
master
Loëck Vézien 7 years ago
committed by GitHub
parent
commit
05f5c6803e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .eslintrc
  2. 26
      package.json
  3. 2
      src/components/AccountPage.js
  4. 2
      src/components/ReceiveBox.js
  5. 26
      src/components/SelectAccount.js
  6. 2
      src/components/SideBar/index.js
  7. 93
      src/components/base/Checkbox/index.js
  8. 9
      src/components/base/Checkbox/stories.js
  9. 6
      src/components/base/Icon/index.js
  10. 36
      src/components/base/Icon/stories.js
  11. 3
      src/components/base/Modal/index.js
  12. 7
      src/components/base/Select/index.js
  13. 69
      src/components/modals/AddAccount/CreateAccount.js
  14. 122
      src/components/modals/AddAccount/ImportAccounts.js
  15. 123
      src/components/modals/AddAccount/index.js
  16. 41
      src/components/modals/Send.js
  17. 2
      src/internals/usb/wallet/accounts.js
  18. 1
      src/internals/usb/wallet/index.js
  19. 1
      src/main/app.js
  20. 8
      src/renderer/head.js
  21. 1
      src/renderer/index.js
  22. 5
      src/styles/global.js
  23. 6
      static/i18n/en/translation.yml
  24. 6
      static/i18n/fr/translation.yml
  25. 169
      yarn.lock

5
.eslintrc

@ -13,8 +13,8 @@
"rules": {
"camelcase": 0,
"import/no-extraneous-dependencies": 0,
"import/prefer-default-export": 0,
"import/no-named-as-default": 0,
"import/prefer-default-export": 0,
"jsx-a11y/anchor-is-valid": 0,
"jsx-a11y/label-has-for": 0,
"new-cap": 0,
@ -28,6 +28,7 @@
"react/forbid-prop-types": 0,
"react/jsx-curly-brace-presence": 0,
"react/jsx-filename-extension": 0,
"react/jsx-no-target-blank": 0,
"react/prefer-stateless-function": 0,
},
"settings": {
@ -35,7 +36,7 @@
"babel-module": {},
},
"flowtype": {
"onlyFilesWithFlowAnnotation": true
"onlyFilesWithFlowAnnotation": true,
}
},
}

26
package.json

@ -38,6 +38,10 @@
"webpack-sources": "1.0.1"
},
"dependencies": {
"@fortawesome/fontawesome": "^1.1.3",
"@fortawesome/fontawesome-free-regular": "^5.0.6",
"@fortawesome/fontawesome-free-solid": "^5.0.6",
"@fortawesome/react-fontawesome": "^0.0.17",
"@ledgerhq/common": "2.0.5",
"@ledgerhq/hw-app-btc": "^2.1.0",
"@ledgerhq/hw-app-eth": "^2.1.0",
@ -50,7 +54,7 @@
"bs58check": "^2.1.1",
"color": "^3.0.0",
"cross-env": "^5.1.3",
"downshift": "^1.25.0",
"downshift": "^1.26.0",
"electron-store": "^1.3.0",
"electron-updater": "^2.20.1",
"fuse.js": "^3.2.0",
@ -65,7 +69,7 @@
"raven-js": "^3.22.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-i18next": "^7.3.2",
"react-i18next": "^7.3.4",
"react-mortal": "^3.0.1",
"react-motion": "^0.5.2",
"react-redux": "^5.0.6",
@ -77,16 +81,16 @@
"redux-thunk": "^2.2.0",
"shortid": "^2.2.8",
"source-map-support": "^0.5.3",
"styled-components": "^3.0.2",
"styled-components": "^3.1.1",
"styled-system": "^1.1.1"
},
"devDependencies": {
"@storybook/addon-actions": "^3.3.10",
"@storybook/addon-knobs": "^3.3.10",
"@storybook/addon-links": "^3.3.10",
"@storybook/addon-options": "^3.3.10",
"@storybook/addons": "^3.3.10",
"@storybook/react": "^3.3.10",
"@storybook/addon-actions": "^3.3.11",
"@storybook/addon-knobs": "^3.3.11",
"@storybook/addon-links": "^3.3.11",
"@storybook/addon-options": "^3.3.11",
"@storybook/addons": "^3.3.11",
"@storybook/react": "^3.3.11",
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.1",
"babel-loader": "^7.1.2",
@ -108,14 +112,14 @@
"eslint-plugin-flowtype": "^2.42.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.6.0",
"eslint-plugin-react": "^7.6.1",
"flow-bin": "^0.64.0",
"flow-typed": "^2.2.3",
"husky": "^0.14.3",
"lint-staged": "^6.1.0",
"node-loader": "^0.6.0",
"prettier": "^1.10.2",
"react-hot-loader": "^4.0.0-beta.17",
"react-hot-loader": "^4.0.0-beta.18",
"webpack": "^3.10.0"
}
}

2
src/components/AccountPage.js

@ -39,7 +39,7 @@ class AccountPage extends PureComponent<Props> {
<Box horizontal flow={3}>
<Box grow>
<Card title="Balance" style={{ height: 435 }} align="center" justify="center">
<Text fontSize={6}>{formatBTC(accountData.balance)}</Text>
<Text fontSize={5}>{formatBTC(accountData.balance)}</Text>
</Card>
</Box>

2
src/components/ReceiveBox.js

@ -65,7 +65,7 @@ const ReceiveBox = ({ address }: Props) => (
<span>{'Print'}</span>
</Action>
<Action>
<Icon name="share-square-o" />
<Icon name="share-square" />
<span>{'Share'}</span>
</Action>
</Box>

26
src/components/SelectAccount.js

@ -2,22 +2,16 @@
import React from 'react'
import { connect } from 'react-redux'
import values from 'lodash/values'
import type { MapStateToProps } from 'react-redux'
import { getAccounts } from 'reducers/accounts'
import Select from 'components/base/Select'
import Text from 'components/base/Text'
import type { Account } from 'types/common'
function renderItem(accounts) {
return item => <span>{(accounts.find(a => a.id === item) || {}).name}</span>
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
accounts: values(getAccounts(state)),
accounts: Object.entries(getAccounts(state)).map(([, account]: [string, any]) => account),
})
type Props = {
@ -28,18 +22,18 @@ type Props = {
const SelectAccount = ({ accounts, value, onChange }: Props) => (
<Select
itemToString={item => item}
value={value}
renderSelected={item => item.name}
renderItem={item => (
<div key={item.id}>
{item.name} - {item.data.balance}
</div>
)}
keyProp="id"
items={accounts}
placeholder="Choose an account"
items={accounts.map(a => a.id)}
onChange={onChange}
renderItem={renderItem(accounts)}
renderSelected={renderItem(accounts)}
renderHighlight={(text, key) => (
<Text key={key} fontWeight="bold">
{text}
</Text>
)}
/>
)
export default connect(mapStateToProps)(SelectAccount)

2
src/components/SideBar/index.js

@ -70,7 +70,7 @@ class SideBar extends PureComponent<Props> {
<Box flow={2}>
<CapsSubtitle>{t('sidebar.menu')}</CapsSubtitle>
<div>
<Item icon="bar-chart" linkTo="/">
<Item icon="chart-bar" linkTo="/">
{t('dashboard.title')}
</Item>
<Item icon="upload" modal="send">

93
src/components/base/Checkbox/index.js

@ -0,0 +1,93 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import Box from 'components/base/Box'
import Icon from 'components/base/Icon'
const Base = styled(Box).attrs({
align: 'center',
justify: 'center',
relative: true,
})`
background-color: ${p => (p.checked ? p.theme.colors.blue : p.theme.colors.white)};
box-shadow: 0 0 0 ${p => (p.checked ? 4 : 1)}px
${p => (p.checked ? p.theme.colors.cream : p.theme.colors.argile)};
border-radius: 50%;
font-size: 7px;
height: 19px;
width: 19px;
transition: all ease-in-out 0.1s;
input[type='checkbox'] {
bottom: 0;
cursor: pointer;
height: 100%;
left: 0;
opacity: 0;
position: absolute;
right: 0;
top: 0;
width: 100%;
z-index: 10;
}
> span {
position: relative;
top: 1px;
opacity: ${p => (p.checked ? 1 : 0)};
transition: all ease-in-out 0.1s;
}
`
type Props = {
checked?: boolean,
onChange?: Function,
}
type State = {
checked: boolean,
}
class Checkbox extends PureComponent<Props, State> {
state = {
checked: this.props.checked || false,
}
componentWillReceiveProps(nextProps: Props) {
if (nextProps.checked) {
this.setState({
checked: nextProps.checked,
})
}
}
handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
const { onChange } = this.props
const { checked } = e.target
this.setState({
checked,
})
if (onChange) {
onChange(checked)
}
}
render() {
const { checked } = this.state
const { onChange, ...props } = this.props
return (
<Base {...props}>
<input type="checkbox" checked={checked} onChange={this.handleChange} />
<Icon color="white" name="check" />
</Base>
)
}
}
export default Checkbox

9
src/components/base/Checkbox/stories.js

@ -0,0 +1,9 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { boolean } from '@storybook/addon-knobs'
import Checkbox from 'components/base/Checkbox'
const stories = storiesOf('Checkbox', module)
stories.add('basic', () => <Checkbox checked={boolean('checked', false)} />)

6
src/components/base/Icon.js → src/components/base/Icon/index.js

@ -3,15 +3,15 @@
import React from 'react'
import styled from 'styled-components'
import { fontSize, color } from 'styled-system'
import FontAwesomeIcon from '@fortawesome/react-fontawesome'
const Container = styled.div`
const Container = styled.span`
${fontSize};
${color};
line-height: 1;
`
export default ({ name, ...props }: { name: string }) => (
<Container {...props}>
<i className={`fa fa-${name}`} />
<FontAwesomeIcon icon={name} />
</Container>
)

36
src/components/base/Icon/stories.js

@ -0,0 +1,36 @@
// @flow
import React from 'react'
import { storiesOf } from '@storybook/react'
import { text, number } from '@storybook/addon-knobs'
import Icon from 'components/base/Icon'
const stories = storiesOf('Icon', module)
const Wrapper = ({ children }: { children: any }) => (
<div>
<div style={{ opacity: 0.2 }}>
(Change the icon value with{' '}
<a
href="https://fontawesome.com/icons?d=gallery&s=regular,solid&m=free"
target="_blank"
style={{ cursor: 'pointer' }}
>
FontAwesome
</a>{' '}
icon)
</div>
{children}
</div>
)
stories.add('basic', () => (
<Wrapper>
<Icon
color={text('color', 'black')}
fontSize={number('fontSize', 5)}
name={text('icon', 'check')}
/>
</Wrapper>
))

3
src/components/base/Modal/index.js

@ -50,7 +50,7 @@ const Container = styled(Box).attrs({
pointerEvents: p.isVisible ? 'auto' : 'none',
}),
})`
overflow: hidden;
overflow: scroll;
position: fixed;
z-index: 20;
`
@ -69,6 +69,7 @@ const Wrapper = styled(Box).attrs({
bg: 'transparent',
flow: 20,
mt: 100,
mb: 100,
style: p => ({
opacity: p.op,
transform: `translate3d(0, ${p.offset}px, 0)`,

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

@ -91,7 +91,7 @@ const FloatingTriangles = styled(Box).attrs({
class Select extends PureComponent<Props> {
static defaultProps = {
itemToString: (item: Object) => item.name,
itemToString: (item: Object) => item && item.name,
keyProp: undefined,
}
@ -103,10 +103,7 @@ class Select extends PureComponent<Props> {
<Dropdown>
{items.length ? (
items.map((item, i) => (
<ItemWrapper
key={keyProp ? item[keyProp] : item.key || item}
{...getItemProps({ item })}
>
<ItemWrapper key={keyProp ? item[keyProp] : item.key} {...getItemProps({ item })}>
<Item highlighted={i === highlightedIndex}>
{renderItem ? renderItem(item) : <span>{item.name_highlight || item.name}</span>}
</Item>

69
src/components/modals/AddAccount/CreateAccount.js

@ -0,0 +1,69 @@
// @flow
import React, { PureComponent } from 'react'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import Input from 'components/base/Input'
import Label from 'components/base/Label'
type Props = {
account: Object,
onAddAccount: Function,
}
type State = {
accountName: string,
}
class CreateAccount extends PureComponent<Props, State> {
state = {
accountName: '',
}
handleCreateAccount = (e: SyntheticEvent<HTMLFormElement>) => {
e.preventDefault()
const { accountName } = this.state
const { onAddAccount, account } = this.props
if (accountName.trim() === '') {
return
}
onAddAccount({
...account,
name: accountName,
})
}
handleChangeInput = (value: string) =>
this.setState({
accountName: value,
})
render() {
const { accountName } = this.state
return (
<Box>
<Box>Create Account</Box>
<form onSubmit={this.handleCreateAccount}>
<Box flow={3}>
<Box flow={1}>
<Label>Account name</Label>
<Input value={accountName} onChange={this.handleChangeInput} />
</Box>
<Box horizontal justify="flex-end">
<Button primary type="submit">
Create
</Button>
</Box>
</Box>
</form>
</Box>
)
}
}
export default CreateAccount

122
src/components/modals/AddAccount/ImportAccounts.js

@ -0,0 +1,122 @@
// @flow
import React, { PureComponent } from 'react'
import { translate } from 'react-i18next'
import type { T } from 'types/common'
import { formatBTC } from 'helpers/format'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import Checkbox from 'components/base/Checkbox'
import Input from 'components/base/Input'
type Props = {
t: T,
accounts: Array<Object>,
onImportAccounts: Function,
}
type State = {
accountsSelected: Array<string>,
accountsName: Object,
}
class ImportAccounts extends PureComponent<Props, State> {
state = {
accountsSelected: [],
accountsName: this.props.accounts.reduce((result, value, index) => {
result[value.id] = {
placeholder: this.props.t(`addAccount.import.placeholder`, {
index: index + 1,
}),
}
return result
}, {}),
}
handleSelectAccount = (id: string, selected: boolean) => () =>
this.setState(prev => ({
accountsSelected: selected
? prev.accountsSelected.filter(v => v !== id)
: [...prev.accountsSelected, id],
}))
handleChangeInput = (id: string) => (value: string) =>
this.setState(prev => ({
accountsName: {
...prev.accountsName,
[id]: {
...prev.accountsName[id],
value,
},
},
}))
handleImportAccounts = (e: SyntheticEvent<HTMLFormElement>) => {
e.preventDefault()
const { accounts, onImportAccounts } = this.props
const { accountsSelected, accountsName } = this.state
const importAccounts = accountsSelected.map(id => ({
...accounts.find(a => a.id === id),
name: accountsName[id].value || accountsName[id].placeholder,
}))
onImportAccounts(importAccounts)
}
render() {
const { accounts } = this.props
const { accountsSelected, accountsName } = this.state
const canImportAccounts = accountsSelected.length > 0
return (
<Box>
<Box>Import Accounts</Box>
<form onSubmit={this.handleImportAccounts}>
<Box flow={3}>
{accounts.map(account => {
const selected = accountsSelected.includes(account.id)
const accountName = accountsName[account.id]
return (
<Box key={account.id} horizontal flow={10}>
<Box>
<Checkbox
checked={selected}
onChange={this.handleSelectAccount(account.id, selected)}
/>
</Box>
<Box grow>
<Box>
<Input
type="text"
disabled={!selected}
placeholder={accountName.placeholder}
value={accountName.value || ''}
onChange={this.handleChangeInput(account.id)}
/>
</Box>
<Box>Balance: {formatBTC(account.balance)}</Box>
<Box>Transactions: {account.transactions.length}</Box>
</Box>
</Box>
)
})}
<Box horizontal justify="flex-end">
<Button primary disabled={!canImportAccounts} type="submit">
Import
</Button>
</Box>
</Box>
</form>
</Box>
)
}
}
export default translate()(ImportAccounts)

123
src/components/modals/AddAccount.js → src/components/modals/AddAccount/index.js

@ -17,13 +17,15 @@ import { addAccount } from 'actions/accounts'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import Button from 'components/base/Button'
import Input from 'components/base/Input'
import Label from 'components/base/Label'
import Modal, { ModalBody } from 'components/base/Modal'
import Select from 'components/base/Select'
import CreateAccount from './CreateAccount'
import ImportAccounts from './ImportAccounts'
const Steps = {
createAccount: (props: Object) => (
chooseWallet: (props: Object) => (
<form onSubmit={props.onSubmit}>
<Box flow={3}>
<Box flow={1}>
@ -40,10 +42,6 @@ const Steps = {
]}
/>
</Box>
<Box flow={1}>
<Label>Account name</Label>
<Input onChange={props.onChangeInput('accountName')} value={props.value.accountName} />
</Box>
<Box horizontal justify="flex-end">
<Button primary type="submit">
Add account
@ -53,65 +51,45 @@ const Steps = {
</form>
),
connectDevice: (props: Object) => (
<div>
<div>Connect your Ledger: {props.connected ? 'ok' : 'ko'}</div>
<div>Start {props.wallet.toUpperCase()} App on your Ledger: ko</div>
</div>
<Box>
<Box>Connect your Ledger: {props.connected ? 'ok' : 'ko'}</Box>
<Box>Start {props.wallet.toUpperCase()} App on your Ledger: ko</Box>
</Box>
),
inProgress: (props: Object) => (
<div>
<Box>
In progress.
{props.progress !== null && (
<div>
<Box>
Account: {props.progress.account} / Transactions: {props.progress.transactions}
</div>
</Box>
)}
</div>
</Box>
),
listAccounts: (props: Object) => {
const accounts = []
let newAccount = null
Object.entries(props.accounts).forEach(([, account]: [string, any]) => {
const hasTransactions = account.transactions.length > 0
if (hasTransactions) {
accounts.push(account)
} else {
newAccount = account
}
})
const accounts = Object.entries(props.accounts).map(([, account]: [string, any]) => account)
const emptyAccounts = accounts.filter(account => account.transactions.length === 0)
const existingAccounts = accounts.filter(account => account.transactions.length > 0)
const canCreateAccount = props.canCreateAccount && emptyAccounts.length === 1
const newAccount = emptyAccounts[0]
return (
<div>
{accounts.map(account => (
<div key={account.id} style={{ marginBottom: 10 }}>
<div>Balance: {account.balance}</div>
<div>Transactions: {account.transactions.length}</div>
<div>
<Button onClick={props.onAddAccount(account)}>Import</Button>
</div>
</div>
))}
{props.canCreateAccount && newAccount !== null ? (
<div>
<Button onClick={props.onAddAccount(newAccount)}>Create new account</Button>
</div>
<Box flow={10}>
<ImportAccounts {...props} accounts={existingAccounts} />
{canCreateAccount ? (
<CreateAccount {...props} account={newAccount} />
) : (
<div>You cannot create new account</div>
<Box>{`You can't create new account`}</Box>
)}
</div>
</Box>
)
},
}
type InputValue = {
accountName: string,
wallet: string,
}
type Step = 'createAccount' | 'connectDevice' | 'inProgress' | 'listAccounts'
type Step = 'chooseWallet' | 'connectDevice' | 'inProgress' | 'listAccounts'
type Props = {
accounts: Accounts,
@ -140,12 +118,11 @@ const mapDispatchToProps = {
const defaultState = {
inputValue: {
accountName: '',
wallet: '',
},
accounts: {},
progress: null,
step: 'createAccount',
step: 'chooseWallet',
}
class AddAccountModal extends PureComponent<Props, State> {
@ -157,6 +134,19 @@ class AddAccountModal extends PureComponent<Props, State> {
ipcRenderer.on('msg', this.handleWalletRequest)
}
componentWillReceiveProps(nextProps) {
if (nextProps.accounts) {
this.setState(prev => ({
accounts: Object.keys(prev.accounts).reduce((result, value) => {
if (!nextProps.accounts[value]) {
result[value] = prev.accounts[value]
}
return result
}, {}),
}))
}
}
componentDidUpdate() {
const { step } = this.state
const { currentDevice } = this.props
@ -195,7 +185,7 @@ class AddAccountModal extends PureComponent<Props, State> {
const props = (predicate, props) => (predicate ? props : {})
return {
...props(step === 'createAccount', {
...props(step === 'chooseWallet', {
value: inputValue,
onSubmit: this.handleSubmit,
onChangeInput: this.handleChangeInput,
@ -211,6 +201,7 @@ class AddAccountModal extends PureComponent<Props, State> {
accounts,
canCreateAccount,
onAddAccount: this.handleAddAccount,
onImportAccounts: this.handleImportAccounts,
}),
}
}
@ -235,23 +226,9 @@ class AddAccountModal extends PureComponent<Props, State> {
}
}
handleAddAccount = account => () => {
const { inputValue } = this.state
const { addAccount, closeModal } = this.props
const { id, ...data } = account
addAccount({
id,
name: inputValue.accountName,
type: inputValue.wallet,
data,
})
closeModal('add-account')
handleAddAccount = account => this.addAccount(account)
this.handleClose()
}
handleImportAccounts = accounts => accounts.forEach(account => this.addAccount(account))
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>
this.setState(prev => ({
@ -266,7 +243,7 @@ class AddAccountModal extends PureComponent<Props, State> {
const { inputValue } = this.state
if (inputValue.accountName.trim() === '' || inputValue.wallet.trim() === '') {
if (inputValue.wallet.trim() === '') {
return
}
@ -282,6 +259,18 @@ class AddAccountModal extends PureComponent<Props, State> {
})
}
addAccount = ({ id, name, ...data }) => {
const { inputValue } = this.state
const { addAccount } = this.props
addAccount({
id,
name,
type: inputValue.wallet,
data,
})
}
_timeout = undefined
render() {
@ -292,7 +281,7 @@ class AddAccountModal extends PureComponent<Props, State> {
return (
<Modal
name="add-account"
preventBackdropClick={step !== 'createAccount'}
preventBackdropClick={step !== 'chooseWallet'}
onClose={this.handleClose}
render={({ onClose }) => (
<ModalBody onClose={onClose} flow={3}>

41
src/components/modals/Send.js

@ -2,17 +2,11 @@
import React, { Fragment, PureComponent } from 'react'
import styled from 'styled-components'
import { connect } from 'react-redux'
import type { MapStateToProps } from 'react-redux'
import type { Accounts } from 'types/common'
import { getAccounts } from 'reducers/accounts'
import Button from 'components/base/Button'
import Input from 'components/base/Input'
import Modal, { ModalBody } from 'components/base/Modal'
import Select from 'components/base/Select'
import SelectAccount from 'components/SelectAccount'
const Label = styled.label`
display: block;
@ -26,7 +20,7 @@ const Steps = {
e.preventDefault()
if (
props.value.account.trim() === '' ||
!props.value.account ||
props.value.address.trim() === '' ||
props.value.amount.trim() === ''
) {
@ -39,14 +33,7 @@ const Steps = {
<div>amount</div>
<div>
<Label>Account to debit</Label>
<Select
onChange={item => props.onChangeInput('account')(item.key)}
renderSelected={item => item.name}
items={Object.entries(props.accounts).map(([id, account]: [string, any]) => ({
key: id,
name: account.name,
}))}
/>
<SelectAccount onChange={props.onChangeInput('account')} value={props.value.account} />
</div>
<div>
<Label>Recipient address</Label>
@ -64,59 +51,49 @@ const Steps = {
<div>summary</div>
<div>{props.value.amount}</div>
<div>to {props.value.address}</div>
<div>from {props.account.name}</div>
<div>from {props.value.account.name}</div>
</div>
),
}
type InputValue = {
account: string,
account: any,
address: string,
amount: string,
}
type Step = 'amount' | 'summary'
type Props = {
accounts: Accounts,
}
type State = {
inputValue: InputValue,
step: Step,
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
accounts: getAccounts(state),
})
const defaultState = {
inputValue: {
account: '',
account: undefined,
address: '',
amount: '',
},
step: 'amount',
}
class Send extends PureComponent<Props, State> {
class Send extends PureComponent<{}, State> {
state = {
...defaultState,
}
getStepProps() {
const { accounts } = this.props
const { inputValue, step } = this.state
const props = (predicate, props) => (predicate ? props : {})
return {
...props(step === 'amount', {
accounts,
onChangeInput: this.handleChangeInput,
value: inputValue,
}),
...props(step === 'summary', {
account: accounts[inputValue.account],
value: inputValue,
}),
onChangeStep: this.handleChangeStep,
@ -131,7 +108,7 @@ class Send extends PureComponent<Props, State> {
},
}))
handleChangeStep = step =>
handleChangeStep = (step: Step) =>
this.setState({
step,
})
@ -163,4 +140,4 @@ class Send extends PureComponent<Props, State> {
}
}
export default connect(mapStateToProps)(Send)
export default Send

2
src/internals/usb/wallet/accounts.js

@ -122,7 +122,7 @@ export default async ({
const hasTransactions = account.transactions.length > 0
accounts[currentAccount] = {
accounts[xpub58] = {
id: xpub58,
...account,
}

1
src/internals/usb/wallet/index.js

@ -31,6 +31,7 @@ export default (sendEvent: Function) => ({
currentAccounts,
onProgress: progress => sendEvent('wallet.getAccounts.progress', progress, { kill: false }),
})
sendEvent('wallet.getAccounts.success', data)
} catch (err) {
sendEvent('wallet.getAccounts.fail', err.stack || err)

1
src/main/app.js

@ -17,6 +17,7 @@ function createMainWindow() {
vibrancy: 'ultra-dark', // https://github.com/electron/electron/issues/10521
}
: {}),
center: true,
show: true,
height: MIN_HEIGHT,
width: MIN_WIDTH,

8
src/renderer/head.js

@ -1,8 +0,0 @@
const list = ['https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css']
list.forEach(href => {
const tag = document.createElement('link')
tag.setAttribute('rel', 'stylesheet')
tag.setAttribute('href', href)
document.head.appendChild(tag)
})

1
src/renderer/index.js

@ -17,7 +17,6 @@ import { getLanguage } from 'reducers/settings'
import App from 'components/App'
import 'styles/global'
import 'renderer/head'
if (__PROD__ && __SENTRY_URL__) {
Raven.config(__SENTRY_URL__, { allowSecretKey: true }).install()

5
src/styles/global.js

@ -1,8 +1,11 @@
// @flow
/* eslint-disable no-unused-expressions */
import { injectGlobal } from 'styled-components'
import '@fortawesome/fontawesome-free-solid'
import '@fortawesome/fontawesome-free-regular'
/* eslint-disable no-unused-expressions */
injectGlobal`
* {
box-sizing: border-box;

6
static/i18n/en/translation.yml

@ -1,9 +1,6 @@
common:
ok: Okay
cancel: Cancel
connectedDevices: You have {{count}} device connected
connectedDevices_0: You don't have device connected
connectedDevices_plural: You have {{count}} devices connected
language:
en: English
@ -26,6 +23,9 @@ receive:
addAccount:
title: Add account
import:
placeholder: Account {{index}}
settings:
title: Settings

6
static/i18n/fr/translation.yml

@ -1,9 +1,6 @@
common:
ok: Okay
cancel: Annuler
connectedDevices: You have {{count}} device connected
connectedDevices_0: You don't have device connected
connectedDevices_plural: You have {{count}} devices connected
language:
en: Anglais
@ -26,6 +23,9 @@ receive:
addAccount:
title: Ajouter un compte
import:
placeholder: Compte {{index}}
settings:
title: Réglages

169
yarn.lock

@ -78,6 +78,34 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@fortawesome/fontawesome-common-types@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.1.2.tgz#d6aa075058f0c984d6e2ebcbc0052c1f7f9bea72"
"@fortawesome/fontawesome-free-regular@^5.0.6":
version "5.0.6"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free-regular/-/fontawesome-free-regular-5.0.6.tgz#fafc624025a247c1a1bbb5080b9902a490cd79f5"
dependencies:
"@fortawesome/fontawesome-common-types" "^0.1.2"
"@fortawesome/fontawesome-free-solid@^5.0.6":
version "5.0.6"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free-solid/-/fontawesome-free-solid-5.0.6.tgz#6bb5acdd081b03ab25b7684f7089b37ed3d20740"
dependencies:
"@fortawesome/fontawesome-common-types" "^0.1.2"
"@fortawesome/fontawesome@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome/-/fontawesome-1.1.3.tgz#7a4844345cbc7fb238f5f1788af73bd302ee9b80"
dependencies:
"@fortawesome/fontawesome-common-types" "^0.1.2"
"@fortawesome/react-fontawesome@^0.0.17":
version "0.0.17"
resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.0.17.tgz#69abd135523187044f533cadc5458f829d43961f"
dependencies:
humps "^2.0.1"
"@ledgerhq/common@2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@ledgerhq/common/-/common-2.0.5.tgz#5f5eca4ac907914d700540c20b3d733cd7a25a93"
@ -118,9 +146,9 @@
dependencies:
events "^1.1.1"
"@storybook/addon-actions@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.3.10.tgz#f3e4b538d8260364c55a3ba1e301a2fab9d8d3f2"
"@storybook/addon-actions@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.3.11.tgz#158a64f01c97fcf6922e7a370c6519d216544bcd"
dependencies:
deep-equal "^1.0.1"
global "^4.3.2"
@ -129,9 +157,9 @@
react-inspector "^2.2.2"
uuid "^3.1.0"
"@storybook/addon-knobs@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-3.3.10.tgz#25f32cd3d32a1667dc9d8ae484ddfd6223fcffef"
"@storybook/addon-knobs@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-3.3.11.tgz#ab53a627f9517d922e1a1f6b874bd76ae2fc3b08"
dependencies:
babel-runtime "^6.26.0"
deep-equal "^1.0.1"
@ -145,41 +173,41 @@
react-textarea-autosize "^5.2.1"
util-deprecate "^1.0.2"
"@storybook/addon-links@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-3.3.10.tgz#4e6c1a0b0bf5b18101bc5001b858b33202ae8209"
"@storybook/addon-links@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-3.3.11.tgz#7bc57baddd1502153ee94cf11fcb88d49131b211"
dependencies:
"@storybook/components" "^3.3.10"
"@storybook/components" "^3.3.11"
global "^4.3.2"
prop-types "^15.5.10"
"@storybook/addon-options@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/addon-options/-/addon-options-3.3.10.tgz#536796b6223616a4a8b3c2851c7efbe66036bc8a"
"@storybook/addon-options@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-options/-/addon-options-3.3.11.tgz#2e4a5fd9b4a5875104aed232dff0f7f257a9611b"
"@storybook/addons@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.3.10.tgz#8753007d872013d2376ba71b14396eef3159673b"
"@storybook/addons@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.3.11.tgz#7f85136d6da785160658aee512fd3cac99780f42"
"@storybook/channel-postmessage@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-3.3.10.tgz#4f22b5a665d3c95eb61cf41bbb06872009ace7b5"
"@storybook/channel-postmessage@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-3.3.11.tgz#a379f96f7819ba3752bb471ebf90ad07c3fc28ea"
dependencies:
"@storybook/channels" "^3.3.10"
"@storybook/channels" "^3.3.11"
global "^4.3.2"
json-stringify-safe "^5.0.1"
"@storybook/channels@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-3.3.10.tgz#0b15d47c2ea0cb1c7b735955d74e9d3ca99cdc42"
"@storybook/channels@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-3.3.11.tgz#569f1c7c364aeb076df78eb829c58f9c9f0a3936"
"@storybook/client-logger@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-3.3.10.tgz#6f8b85c3dfad229794fee88f930df59b163ee144"
"@storybook/client-logger@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-3.3.11.tgz#35c851dbed2067201189847c7aa92f8d567a4d61"
"@storybook/components@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-3.3.10.tgz#f213a129ed49de33cdaf116da2c2b662b8eb3ea0"
"@storybook/components@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-3.3.11.tgz#cb2a48b52e7cb45408172f4462f4730ca6970e78"
dependencies:
glamor "^2.20.40"
glamorous "^4.11.2"
@ -193,9 +221,9 @@
"@storybook/react-simple-di" "^1.2.1"
babel-runtime "6.x.x"
"@storybook/node-logger@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-3.3.10.tgz#d9c09a622713ec4726cdd292e798aa98c0503c15"
"@storybook/node-logger@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-3.3.11.tgz#e459cbf8da75e2671a08de4f6dfe32b556b20af6"
dependencies:
chalk "^2.3.0"
npmlog "^4.1.2"
@ -225,17 +253,17 @@
dependencies:
babel-runtime "^6.5.0"
"@storybook/react@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/react/-/react-3.3.10.tgz#a55f8f804f3f01d76f1b7e8675e818ee4c107324"
dependencies:
"@storybook/addon-actions" "^3.3.10"
"@storybook/addon-links" "^3.3.10"
"@storybook/addons" "^3.3.10"
"@storybook/channel-postmessage" "^3.3.10"
"@storybook/client-logger" "^3.3.10"
"@storybook/node-logger" "^3.3.10"
"@storybook/ui" "^3.3.10"
"@storybook/react@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/react/-/react-3.3.11.tgz#5438d40aa095dd7b0c2f4e8e51a83fd5151df0c1"
dependencies:
"@storybook/addon-actions" "^3.3.11"
"@storybook/addon-links" "^3.3.11"
"@storybook/addons" "^3.3.11"
"@storybook/channel-postmessage" "^3.3.11"
"@storybook/client-logger" "^3.3.11"
"@storybook/node-logger" "^3.3.11"
"@storybook/ui" "^3.3.11"
airbnb-js-shims "^1.4.0"
autoprefixer "^7.2.3"
babel-loader "^7.1.2"
@ -287,11 +315,11 @@
webpack-dev-middleware "^1.12.2"
webpack-hot-middleware "^2.21.0"
"@storybook/ui@^3.3.10":
version "3.3.10"
resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-3.3.10.tgz#99a83b988b01cde1df61b87a58227a50ed196dd1"
"@storybook/ui@^3.3.11":
version "3.3.11"
resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-3.3.11.tgz#df500b97739da484d51d6a1bcb52ce3866ad2148"
dependencies:
"@storybook/components" "^3.3.10"
"@storybook/components" "^3.3.11"
"@storybook/mantra-core" "^1.7.2"
"@storybook/react-komposer" "^2.0.3"
babel-runtime "^6.26.0"
@ -3126,9 +3154,9 @@ dotenv@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
downshift@^1.25.0:
version "1.25.0"
resolved "https://registry.yarnpkg.com/downshift/-/downshift-1.25.0.tgz#7f6e2dda4aa5ddbb2932401bd61e7b741e92c02e"
downshift@^1.26.0:
version "1.26.0"
resolved "https://registry.yarnpkg.com/downshift/-/downshift-1.26.0.tgz#1ee954fef129861f977c6b9effe93fb78e7dc363"
duplexer3@^0.1.4:
version "0.1.4"
@ -3602,9 +3630,9 @@ eslint-plugin-jsx-a11y@^6.0.3:
emoji-regex "^6.1.0"
jsx-ast-utils "^2.0.0"
eslint-plugin-react@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.6.0.tgz#351651188c74c5b2fecc2717e3936b7207baa728"
eslint-plugin-react@^7.6.1:
version "7.6.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.6.1.tgz#5d0e908be599f0c02fbf4eef0c7ed6f29dff7633"
dependencies:
doctrine "^2.0.2"
has "^1.0.1"
@ -4758,6 +4786,10 @@ https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
humps@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa"
husky@^0.14.3:
version "0.14.3"
resolved "https://registry.yarnpkg.com/husky/-/husky-0.14.3.tgz#c69ed74e2d2779769a17ba8399b54ce0b63c12c3"
@ -7307,14 +7339,14 @@ react-fuzzy@^0.5.1:
fuse.js "^3.0.1"
prop-types "^15.5.9"
react-hot-loader@^4.0.0-beta.17:
version "4.0.0-beta.17"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.17.tgz#484ff64221fc456698d6a24cd2a107b66f024eaa"
react-hot-loader@^4.0.0-beta.18:
version "4.0.0-beta.18"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.18.tgz#5a3d1b5bd813633380b88c0c660019dbf638975d"
dependencies:
fast-levenshtein "^2.0.6"
global "^4.3.0"
hoist-non-react-statics "^2.3.1"
react-stand-in "^4.0.0-beta.17"
react-stand-in "^4.0.0-beta.18"
react-html-attributes@^1.3.0:
version "1.4.1"
@ -7322,9 +7354,9 @@ react-html-attributes@^1.3.0:
dependencies:
html-element-attributes "^1.0.0"
react-i18next@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-7.3.2.tgz#5036dc0371808bd8afe0c9a4a738ebd39721e33b"
react-i18next@^7.3.4:
version "7.3.4"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-7.3.4.tgz#d5932d47ac7f0d723eecef492ea97b7232673706"
dependencies:
hoist-non-react-statics "2.3.1"
html-parse-stringify2 "2.0.1"
@ -7434,9 +7466,9 @@ react-split-pane@^0.1.74:
prop-types "^15.5.10"
react-style-proptype "^3.0.0"
react-stand-in@^4.0.0-beta.17:
version "4.0.0-beta.17"
resolved "https://registry.yarnpkg.com/react-stand-in/-/react-stand-in-4.0.0-beta.17.tgz#c65216f5109ae9db2a961812a96ea5a5e416184b"
react-stand-in@^4.0.0-beta.18:
version "4.0.0-beta.18"
resolved "https://registry.yarnpkg.com/react-stand-in/-/react-stand-in-4.0.0-beta.18.tgz#67d83309ae5d95526a2d1124beaa7ab093085cb2"
dependencies:
shallowequal "^1.0.2"
@ -8533,9 +8565,9 @@ style-loader@^0.19.0, style-loader@^0.19.1:
loader-utils "^1.0.2"
schema-utils "^0.3.0"
styled-components@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.0.2.tgz#dbcd66ee84d444ee4332a7f74027e8a675191593"
styled-components@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.1.1.tgz#7896819070b2663c6519f428017d6ca7e2a3c7cd"
dependencies:
buffer "^5.0.3"
css-to-react-native "^2.0.3"
@ -8544,6 +8576,7 @@ styled-components@^3.0.2:
is-plain-object "^2.0.1"
prop-types "^15.5.4"
stylis "^3.4.0"
stylis-rule-sheet "^0.0.7"
supports-color "^3.2.3"
styled-system@^1.1.1:
@ -8552,6 +8585,10 @@ styled-system@^1.1.1:
dependencies:
prop-types "^15.6.0"
stylis-rule-sheet@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.7.tgz#5c51dc879141a61821c2094ba91d2cbcf2469c6c"
stylis@^3.4.0:
version "3.4.8"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.4.8.tgz#94380babbcd4c75726215794ca985b38ec96d1a3"

Loading…
Cancel
Save