Browse Source

Merge pull request #50 from loeck/master

Add feature for change language in Settings page
master
Meriadec Pillet 7 years ago
committed by GitHub
parent
commit
1eeb746fbf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      package.json
  2. 9
      src/components/AccountPage.js
  3. 12
      src/components/App.js
  4. 85
      src/components/DashboardPage.js
  5. 16
      src/components/IsUnlocked/index.js
  6. 98
      src/components/SettingsPage/Display.js
  7. 30
      src/components/SettingsPage/Profile.js
  8. 55
      src/components/SettingsPage/index.js
  9. 27
      src/components/SideBar/Item.js
  10. 5
      src/components/Wrapper.js
  11. 6
      src/components/base/Label.js
  12. 8
      src/components/modals/AddAccount.js
  13. 19
      src/i18n/en/translation.yml
  14. 25
      src/i18n/fr/translation.yml
  15. 33
      src/internals/usb/devices.js
  16. 2
      src/internals/usb/wallet/accounts.js
  17. 15
      src/main/app.js
  18. 13
      src/reducers/accounts.js
  19. 6
      src/reducers/settings.js
  20. 40
      src/renderer/events.js
  21. 1
      src/renderer/i18n.js
  22. 11
      src/renderer/index.js
  23. 12
      src/types/common.js
  24. 192
      yarn.lock

21
package.json

@ -38,11 +38,11 @@
"webpack-sources": "1.0.1"
},
"dependencies": {
"@ledgerhq/common": "2.0.4",
"@ledgerhq/hw-app-btc": "^1.1.2-beta.068e2a14",
"@ledgerhq/hw-app-eth": "^1.1.2-beta.068e2a14",
"@ledgerhq/hw-transport": "^1.1.2-beta.068e2a14",
"@ledgerhq/hw-transport-node-hid": "^1.1.2-beta.068e2a14",
"@ledgerhq/common": "2.0.5",
"@ledgerhq/hw-app-btc": "^2.0.5",
"@ledgerhq/hw-app-eth": "^2.0.5",
"@ledgerhq/hw-transport": "^2.0.5",
"@ledgerhq/hw-transport-node-hid": "^2.0.6",
"axios": "^0.17.1",
"bcryptjs": "^2.4.3",
"bitcoinjs-lib": "^3.3.2",
@ -52,15 +52,16 @@
"cross-env": "^5.1.3",
"downshift": "^1.25.0",
"electron-store": "^1.3.0",
"electron-updater": "^2.19.1",
"electron-updater": "^2.20.1",
"fuse.js": "^3.2.0",
"history": "^4.7.2",
"i18next": "^10.2.2",
"i18next-node-fs-backend": "^1.0.0",
"lodash": "^4.17.4",
"moment": "^2.20.1",
"object-path": "^0.11.4",
"qrcode": "^1.2.0",
"raven": "^2.3.0",
"raven": "^2.4.0",
"raven-js": "^3.22.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
@ -97,14 +98,14 @@
"concurrently": "^3.5.1",
"dotenv": "^4.0.0",
"electron": "1.7.11",
"electron-builder": "^19.54.0",
"electron-builder": "^19.55.2",
"electron-devtools-installer": "^2.2.3",
"electron-webpack": "1.11.0",
"eslint": "^4.16.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-import-resolver-babel-module": "^4.0.0",
"eslint-plugin-flowtype": "^2.40.1",
"eslint-plugin-flowtype": "^2.41.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.5.1",
@ -114,7 +115,7 @@
"lint-staged": "^6.0.0",
"node-loader": "^0.6.0",
"prettier": "^1.10.2",
"react-hot-loader": "^4.0.0-beta.12",
"react-hot-loader": "^4.0.0-beta.17",
"webpack": "^3.10.0"
}
}

9
src/components/AccountPage.js

@ -3,6 +3,7 @@
import React, { PureComponent, Fragment } from 'react'
import { connect } from 'react-redux'
import styled from 'styled-components'
import moment from 'moment'
import type { MapStateToProps } from 'react-redux'
import type { Account, AccountData } from 'types/common'
@ -93,10 +94,10 @@ class AccountPage extends PureComponent<Props> {
</Box>
</Box>
<Card title="Last operations">
{accountData.transactions.map(tr => (
<Box horizontal key={tr.hash}>
<Box grow>{'-'}</Box>
<Box>{format(tr.balance)}</Box>
{accountData.transactions.map(tx => (
<Box horizontal key={tx.hash}>
<Box grow>{moment(tx.time * 1e3).format('LLL')}</Box>
<Box>{format(tx.balance)}</Box>
</Box>
))}
</Card>

12
src/components/App.js

@ -12,9 +12,17 @@ import i18n from 'renderer/i18n'
import Wrapper from 'components/Wrapper'
export default ({ store, history }: { store: Object, history: Object }) => (
export default ({
store,
history,
language,
}: {
store: Object,
history: Object,
language: string,
}) => (
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<I18nextProvider i18n={i18n} initialLanguage={language}>
<ThemeProvider theme={theme}>
<ConnectedRouter history={history}>
<Wrapper />

85
src/components/DashboardPage.js

@ -2,12 +2,16 @@
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import chunk from 'lodash/chunk'
import { push } from 'react-router-redux'
import type { MapStateToProps } from 'react-redux'
import type { Accounts } from 'types/common'
import { format } from 'helpers/btc'
import { getTotalBalance } from 'reducers/accounts'
import { openModal } from 'reducers/modals'
import { getTotalBalance, getAccounts } from 'reducers/accounts'
import Box, { Card } from 'components/base/Box'
import Text from 'components/base/Text'
@ -15,10 +19,19 @@ import Select from 'components/base/Select'
import Tabs from 'components/base/Tabs'
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
accounts: getAccounts(state),
totalBalance: getTotalBalance(state),
})
const mapDispatchToProps = {
push,
openModal,
}
type Props = {
accounts: Accounts,
push: Function,
openModal: Function,
totalBalance: number,
}
@ -40,8 +53,11 @@ class DashboardPage extends PureComponent<Props, State> {
handleChangeTab = tab => this.setState({ tab })
render() {
const { totalBalance } = this.props
const { totalBalance, openModal, push, accounts } = this.props
const { tab } = this.state
const totalAccounts = Object.keys(accounts).length
return (
<Box flow={4}>
<Box horizontal align="flex-end">
@ -50,7 +66,9 @@ class DashboardPage extends PureComponent<Props, State> {
{'Hello Anonymous,'}
</Text>
<Text color="grey" fontSize={3}>
{'here is the summary of your 5 accounts'}
{totalAccounts > 0
? `here is the summary of your ${totalAccounts} accounts`
: 'no accounts'}
</Text>
</Box>
<Box ml="auto">
@ -85,40 +103,45 @@ class DashboardPage extends PureComponent<Props, State> {
/>
</Card>
<Box flow={3}>
<Box horizontal flow={3}>
<Card flex={1} style={{ height: 200 }}>
{'Brian account'}
</Card>
<Card flex={1} style={{ height: 200 }}>
{'Virginie account'}
</Card>
<Card flex={1} style={{ height: 200 }}>
{'Ledger account'}
</Card>
</Box>
<Box horizontal flow={3}>
<Card flex={1} style={{ height: 200 }}>
{'Brian account'}
</Card>
<Card flex={1} style={{ height: 200 }}>
{'Virginie account'}
</Card>
{chunk([...Object.keys(accounts), 'add-account'], 3).map((line, i) => (
<Box
p={3}
flex={1}
borderWidth={2}
align="center"
justify="center"
borderColor="mouse"
style={{ borderStyle: 'dashed' }}
key={i} // eslint-disable-line react/no-array-index-key
horizontal
flow={3}
>
{'+ Add account'}
{line.map(
key =>
key === 'add-account' ? (
<Box
key={key}
p={3}
flex={1}
borderWidth={2}
align="center"
justify="center"
borderColor="mouse"
style={{ borderStyle: 'dashed', cursor: 'pointer' }}
onClick={() => openModal('add-account')}
>
{'+ Add account'}
</Box>
) : (
<Card
key={key}
flex={1}
style={{ cursor: 'pointer', height: 200 }}
onClick={() => push(`/account/${key}`)}
>
{accounts[key].name}
</Card>
),
)}
</Box>
</Box>
))}
</Box>
</Box>
)
}
}
export default connect(mapStateToProps)(DashboardPage)
export default connect(mapStateToProps, mapDispatchToProps)(DashboardPage)

16
src/components/IsUnlocked/index.js

@ -5,13 +5,15 @@ import { connect } from 'react-redux'
import bcrypt from 'bcryptjs'
import type { MapStateToProps } from 'react-redux'
import type { Settings } from 'types/common'
import type { Settings, Accounts } from 'types/common'
import get from 'lodash/get'
import { startSyncAccounts, stopSyncAccounts } from 'renderer/events'
import { setEncryptionKey } from 'helpers/db'
import { fetchAccounts } from 'actions/accounts'
import { getAccounts } from 'reducers/accounts'
import { isLocked, unlock } from 'reducers/application'
import Box from 'components/base/Box'
@ -22,6 +24,7 @@ type InputValue = {
}
type Props = {
accounts: Accounts,
fetchAccounts: Function,
isLocked: boolean,
render: Function,
@ -33,6 +36,7 @@ type State = {
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
accounts: getAccounts(state),
settings: state.settings,
isLocked: isLocked(state),
})
@ -53,6 +57,16 @@ class IsUnlocked extends PureComponent<Props, State> {
...defaultState,
}
componentWillReceiveProps(nextProps) {
if (this.props.isLocked && !nextProps.isLocked) {
startSyncAccounts(nextProps.accounts)
}
if (!this.props.isLocked && nextProps.isLocked) {
stopSyncAccounts()
}
}
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>
this.setState(prev => ({
inputValue: {

98
src/components/SettingsPage/Display.js

@ -0,0 +1,98 @@
// @flow
import React, { PureComponent } from 'react'
import { translate } from 'react-i18next'
import type { SettingsDisplay, T } from 'types/common'
import Box, { Card } from 'components/base/Box'
import Button from 'components/base/Button'
import Label from 'components/base/Label'
import Select from 'components/base/Select'
type InputValue = SettingsDisplay
type Props = {
t: T,
i18n: Object,
settings: SettingsDisplay,
onSaveSettings: Function,
}
type State = {
inputValue: InputValue,
}
class TabProfile extends PureComponent<Props, State> {
state = {
inputValue: {
language: this.props.settings.language,
},
}
getLanguages() {
const { t } = this.props
return [
{
key: 'en',
name: t('language.en'),
},
{
key: 'fr',
name: t('language.fr'),
},
]
}
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>
this.setState(prev => ({
inputValue: {
...prev.inputValue,
[key]: value,
},
}))
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
e.preventDefault()
const { onSaveSettings, i18n } = this.props
const { inputValue } = this.state
const settings = {
language: inputValue.language,
}
i18n.changeLanguage(settings.language)
onSaveSettings(settings)
}
render() {
const { t } = this.props
const { inputValue } = this.state
const languages = this.getLanguages()
const currentLanguage = languages.find(l => l.key === inputValue.language)
return (
<form onSubmit={this.handleSubmit}>
<Card>
<Box>
<Label>{t('settings.display.language')}</Label>
<Select
onChange={item => this.handleChangeInput('language')(item.key)}
renderSelected={item => item.name}
value={currentLanguage}
items={languages}
/>
</Box>
<Box>
<Button type="submit">Save</Button>
</Box>
</Card>
</form>
)
}
}
export default translate()(TabProfile)

30
src/components/SettingsPage/Profile.js

@ -10,10 +10,8 @@ import set from 'lodash/set'
import { setEncryptionKey } from 'helpers/db'
import type { MapStateToProps } from 'react-redux'
import type { Settings } from 'types/common'
import type { SettingsProfile } from 'types/common'
import { saveSettings } from 'actions/settings'
import { unlock } from 'reducers/application'
import Box, { Card } from 'components/base/Box'
@ -25,30 +23,24 @@ const Label = styled.label`
text-transform: uppercase;
`
type InputValue = Settings
type InputValue = SettingsProfile
type Props = {
settings: Settings,
saveSettings: Function,
settings: SettingsProfile,
onSaveSettings: Function,
unlock: Function,
}
type State = {
inputValue: InputValue,
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
settings: state.settings,
})
const mapDispatchToProps = {
saveSettings,
unlock,
}
class SettingsPage extends PureComponent<Props, State> {
class TabProfile extends PureComponent<Props, State> {
state = {
inputValue: {
...this.props.settings,
password: {
...this.props.settings.password,
value: undefined,
@ -56,7 +48,7 @@ class SettingsPage extends PureComponent<Props, State> {
},
}
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>
handleChangeInput = (key: string) => (value: $Values<InputValue>) =>
this.setState(prev => ({
inputValue: {
...set(prev.inputValue, key, value),
@ -66,7 +58,7 @@ class SettingsPage extends PureComponent<Props, State> {
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
e.preventDefault()
const { saveSettings, unlock } = this.props
const { onSaveSettings, unlock } = this.props
const { inputValue } = this.state
const settings = {
@ -81,14 +73,14 @@ class SettingsPage extends PureComponent<Props, State> {
if (password.state === true && password.value.trim() !== '') {
settings.password.value = bcrypt.hashSync(password.value, 8)
setEncryptionKey('accounts', password.value)
} else {
setEncryptionKey('accounts', undefined)
}
unlock()
saveSettings(settings)
onSaveSettings(settings)
}
render() {
@ -99,7 +91,7 @@ class SettingsPage extends PureComponent<Props, State> {
<Box horizontal>
<input
type="checkbox"
checked={get(inputValue, 'password.state')}
checked={get(inputValue, 'password.state', false)}
onChange={e => this.handleChangeInput('password.state')(e.target.checked)}
/>{' '}
with password
@ -123,4 +115,4 @@ class SettingsPage extends PureComponent<Props, State> {
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SettingsPage)
export default connect(null, mapDispatchToProps)(TabProfile)

55
src/components/SettingsPage/index.js

@ -1,14 +1,35 @@
// @flow
import React, { PureComponent } from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { translate } from 'react-i18next'
import type { MapStateToProps } from 'react-redux'
import type { Settings, T } from 'types/common'
import { saveSettings } from 'actions/settings'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import Tabs from 'components/base/Tabs'
import TabDisplay from './Display'
import TabProfile from './Profile'
type Props = {}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
settings: state.settings,
})
const mapDispatchToProps = {
saveSettings,
}
type Props = {
t: T,
settings: Settings,
saveSettings: Function,
}
type State = {
tab: number,
@ -16,55 +37,63 @@ type State = {
class SettingsPage extends PureComponent<Props, State> {
state = {
tab: 6,
tab: 0,
}
handleChangeTab = (tab: number) => this.setState({ tab })
handleSaveSettings = settings => this.props.saveSettings(settings)
render() {
const { settings, t } = this.props
const { tab } = this.state
const props = {
settings,
onSaveSettings: this.handleSaveSettings,
}
return (
<Box flow={4}>
<Text fontSize={5}>{'Settings'}</Text>
<Text fontSize={5}>{t('settings.title')}</Text>
<Tabs
index={tab}
onTabClick={this.handleChangeTab}
items={[
{
key: 'display',
title: 'Affichage',
render: () => <div>{'Affichage'}</div>,
title: t('settings.tabs.display'),
render: () => <TabDisplay {...props} />,
},
{
key: 'money',
title: 'Monnaie',
title: t('settings.tabs.money'),
render: () => <div>{'Monnaie'}</div>,
},
{
key: 'material',
title: 'Matériel',
title: t('settings.tabs.material'),
render: () => <div>{'Matériel'}</div>,
},
{
key: 'app',
title: 'App (beta)',
title: t('settings.tabs.app'),
render: () => <div>{'App (beta)'}</div>,
},
{
key: 'tools',
title: 'Outils',
title: t('settings.tabs.tools'),
render: () => <div>{'Outils'}</div>,
},
{
key: 'blockchain',
title: 'Blockchain',
title: t('settings.tabs.blockchain'),
render: () => <div>{'Blockchain'}</div>,
},
{
key: 'profile',
title: 'Profil',
render: () => <TabProfile />,
title: t('settings.tabs.profile'),
render: () => <TabProfile {...props} />,
},
]}
/>
@ -73,4 +102,4 @@ class SettingsPage extends PureComponent<Props, State> {
}
}
export default SettingsPage
export default compose(connect(mapStateToProps, mapDispatchToProps), translate())(SettingsPage)

27
src/components/SideBar/Item.js

@ -9,25 +9,14 @@ import { connect } from 'react-redux'
import { openModal, isModalOpened } from 'reducers/modals'
import type { MapStateToProps } from 'react-redux'
import type { Element } from 'react'
import type { Location } from 'react-router'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
type Props = {
children: string,
linkTo?: string | null,
modal?: string | null,
desc?: string | null,
icon?: Element<*> | null,
location: Location,
isModalOpened: boolean,
push: Function,
openModal: Function,
}
const mapStateToProps = (state, { modal }: any) => ({
const mapStateToProps: MapStateToProps<*, *, *> = (state, { modal }: any) => ({
// connect router here only to make components re-render
// see https://github.com/ReactTraining/react-router/issues/4671
router: state.router,
@ -63,6 +52,18 @@ const IconWrapper = styled(Box)`
border: 2px solid ${p => (p.isActive ? p.theme.colors.blue : 'rgba(255, 255, 255, 0.1)')};
`
type Props = {
children: string,
linkTo?: string | null,
modal?: string | null,
desc?: string | null,
icon?: Element<*> | null,
location: Location,
isModalOpened: boolean,
push: Function,
openModal: Function,
}
function Item({
children,
desc,

5
src/components/Wrapper.js

@ -1,7 +1,6 @@
// @flow
import React, { Fragment, Component } from 'react'
import { ipcRenderer } from 'electron'
import { Route } from 'react-router'
import { translate } from 'react-i18next'
@ -19,10 +18,6 @@ import SideBar from 'components/SideBar'
import TopBar from 'components/TopBar'
class Wrapper extends Component<{}> {
componentDidMount() {
ipcRenderer.send('renderer-ready')
}
render() {
return (
<Fragment>

6
src/components/base/Label.js

@ -0,0 +1,6 @@
import styled from 'styled-components'
export default styled.label`
display: block;
text-transform: uppercase;
`

8
src/components/modals/AddAccount.js

@ -1,7 +1,6 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { connect } from 'react-redux'
import { ipcRenderer } from 'electron'
@ -17,14 +16,10 @@ import { addAccount } from 'actions/accounts'
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'
const Label = styled.label`
display: block;
text-transform: uppercase;
`
const Steps = {
createAccount: (props: Object) => (
<form onSubmit={props.onSubmit}>
@ -229,6 +224,7 @@ class AddAccountModal extends PureComponent<Props, State> {
})
closeModal('add-account')
this.handleClose()
}
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>

19
src/i18n/en/translation.yml

@ -4,3 +4,22 @@ common:
connectedDevices: You have {{count}} device connected
connectedDevices_0: You don't have device connected
connectedDevices_plural: You have {{count}} devices connected
language:
en: English
fr: French
settings:
title: Settings
tabs:
display: Display
money: Money
material: Material
app: App (beta)
tools: Tools
blockchain: Blockchain
profile: Profile
display:
language: Language

25
src/i18n/fr/translation.yml

@ -0,0 +1,25 @@
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
fr: Français
settings:
title: Réglages
tabs:
display: Affichage
money: Monnaie
material: Matériel
app: App (béta)
tools: Outils
blockchain: Blockchain
profile: Profil
display:
language: Langage

33
src/internals/usb/devices.js

@ -1,28 +1,19 @@
// @flow
import listenDevices from '@ledgerhq/hw-transport-node-hid/lib/listenDevices'
import getDevices from '@ledgerhq/hw-transport-node-hid/lib/getDevices'
const isLedgerDevice = device =>
(device.vendorId === 0x2581 && device.productId === 0x3b7c) || device.vendorId === 0x2c97
let isListenDevices = false
import CommNodeHid from '@ledgerhq/hw-transport-node-hid'
export default (send: Function) => ({
listen: () => {
if (isListenDevices) {
return
}
isListenDevices = true
const handleChangeDevice = eventName => device =>
isLedgerDevice(device) && send(eventName, device, { kill: false })
listenDevices.start()
listenDevices.events.on('add', handleChangeDevice('device.add'))
listenDevices.events.on('remove', handleChangeDevice('device.remove'))
CommNodeHid.listen({
next: e => {
if (e.type === 'add') {
send('device.add', e.device, { kill: false })
}
if (e.type === 'remove') {
send('device.remove', e.device, { kill: false })
}
},
})
},
all: () => send('devices.update', getDevices().filter(isLedgerDevice)),
})

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

@ -72,7 +72,7 @@ export default async ({
v.toString(16).padStart(4, 0),
)
await transport.exchange(`e014000005${p2pkh}${p2sh}${fam.substr(-2)}`, [0x9000])
await transport.exchange(Buffer.from(`e014000005${p2pkh}${p2sh}${fam.substr(-2)}`), [0x9000])
const getPublicKey = path => btc.getWalletPublicKey(path)

15
src/main/app.js

@ -1,12 +1,15 @@
// @flow
import { app, ipcMain, BrowserWindow } from 'electron'
import { app, BrowserWindow } from 'electron'
process.setMaxListeners(100)
// necessary to prevent win from being garbage collected
let mainWindow
const MIN_HEIGHT = 768
const MIN_WIDTH = 1024
function createMainWindow() {
const windowOptions = {
...(process.platform === 'darwin'
@ -17,6 +20,10 @@ function createMainWindow() {
}
: {}),
show: true,
height: MIN_HEIGHT,
width: MIN_WIDTH,
minHeight: MIN_HEIGHT,
minWidth: MIN_WIDTH,
}
const window = new BrowserWindow(windowOptions)
@ -35,6 +42,10 @@ function createMainWindow() {
mainWindow = null
})
window.once('ready-to-show', () => {
window.show()
})
window.webContents.on('devtools-opened', () => {
window.focus()
setImmediate(() => {
@ -74,6 +85,4 @@ app.on('ready', async () => {
}
mainWindow = createMainWindow()
ipcMain.on('renderer-ready', () => mainWindow && mainWindow.show())
})

13
src/reducers/accounts.js

@ -11,12 +11,12 @@ export type AccountsState = Accounts
const state: AccountsState = {}
function setAccount(account: Account) {
function getAccount(account: Account) {
return {
...account,
data: {
...account.data,
transactions: get(account.data, 'transactions', []).reverse(),
transactions: get(account.data, 'transactions', []).sort((a, b) => b.time - a.time),
},
}
}
@ -24,7 +24,7 @@ function setAccount(account: Account) {
const handlers: Object = {
ADD_ACCOUNT: (state: AccountsState, { payload: account }: { payload: Account }) => ({
...state,
[account.id]: setAccount(account),
[account.id]: getAccount(account),
}),
FETCH_ACCOUNTS: (state: AccountsState, { payload: accounts }: { payload: Accounts }) => accounts,
SET_ACCOUNT_DATA: (
@ -48,7 +48,7 @@ const handlers: Object = {
return {
...state,
[accountID]: setAccount(account),
[accountID]: getAccount(account),
}
},
}
@ -67,7 +67,10 @@ export function getTotalBalance(state: { accounts: AccountsState }) {
}
export function getAccounts(state: { accounts: AccountsState }) {
return state.accounts
return Object.keys(state.accounts).reduce((result, key) => {
result[key] = getAccount(state.accounts[key])
return result
}, {})
}
export function getAccountById(state: { accounts: AccountsState }, id: string) {

6
src/reducers/settings.js

@ -11,10 +11,14 @@ export type SettingsState = Object
const state: SettingsState = {}
const handlers: Object = {
SAVE_SETTINGS: (state: SettingsState, { payload: settings }: { payload: Settings }) => settings,
SAVE_SETTINGS: (state: SettingsState, { payload: settings }: { payload: Settings }) => ({
...state,
...settings,
}),
FETCH_SETTINGS: (state: SettingsState, { payload: settings }: { payload: Settings }) => settings,
}
export const hasPassword = (state: Object) => get(state.settings, 'password.state', false)
export const getLanguage = (state: Object) => get(state.settings, 'language', 'en')
export default handleActions(handlers, state)

40
src/renderer/events.js

@ -2,6 +2,9 @@
import { ipcRenderer } from 'electron'
import objectPath from 'object-path'
import get from 'lodash/get'
import type { Accounts } from 'types/common'
import { updateDevices, addDevice, removeDevice } from 'actions/devices'
import { syncAccount } from 'actions/accounts'
@ -17,6 +20,9 @@ type MsgPayload = {
const CHECK_UPDATE_TIMEOUT = 3e3
const SYNC_ACCOUNT_TIMEOUT = 5e3
let syncAccounts = true
let syncTimeout
export function sendEvent(channel: string, msgType: string, data: any) {
ipcRenderer.send(channel, {
type: msgType,
@ -31,24 +37,33 @@ export function sendSyncEvent(channel: string, msgType: string, data: any): any
})
}
function startSyncAccounts(store) {
const accounts = getAccounts(store.getState())
export function startSyncAccounts(accounts: Accounts) {
syncAccounts = true
sendEvent('accounts', 'sync.all', {
accounts: Object.entries(accounts).map(([id, account]: [string, any]) => ({
id,
currentIndex: account.data.currentIndex,
currentIndex: get(account, 'data.currentIndex', 0),
})),
})
}
export default (store: Object) => {
export function stopSyncAccounts() {
syncAccounts = false
clearTimeout(syncTimeout)
}
export default ({ store, locked }: { store: Object, locked: boolean }) => {
const handlers = {
accounts: {
sync: {
success: accounts => {
accounts.forEach(account => store.dispatch(syncAccount(account)))
setTimeout(() => startSyncAccounts(store), SYNC_ACCOUNT_TIMEOUT)
if (syncAccounts) {
accounts.forEach(account => store.dispatch(syncAccount(account)))
syncTimeout = setTimeout(() => {
const newAccounts = getAccounts(store.getState())
startSyncAccounts(newAccounts)
}, SYNC_ACCOUNT_TIMEOUT)
}
},
},
},
@ -80,14 +95,15 @@ export default (store: Object) => {
handler(data)
})
// First time, we get all devices
sendEvent('usb', 'devices.all')
// Start detection when we plug/unplug devices
sendEvent('usb', 'devices.listen')
// Start accounts sync
startSyncAccounts(store)
if (!locked) {
const accounts = getAccounts(store.getState())
// Start accounts sync
startSyncAccounts(accounts)
}
if (__PROD__) {
// Start check of eventual updates

1
src/renderer/i18n.js

@ -5,7 +5,6 @@ import path from 'path'
import Backend from 'i18next-node-fs-backend'
i18n.use(Backend).init({
lng: 'en',
fallbackLng: 'en',
debug: false,
backend: {

11
src/renderer/index.js

@ -12,6 +12,7 @@ import events from 'renderer/events'
import { fetchAccounts } from 'actions/accounts'
import { fetchSettings } from 'actions/settings'
import { isLocked } from 'reducers/application'
import { getLanguage } from 'reducers/settings'
import App from 'components/App'
@ -29,12 +30,14 @@ const rootNode = document.getElementById('app')
store.dispatch(fetchSettings())
const state = store.getState() || {}
const language = getLanguage(state)
const locked = isLocked(state)
if (!isLocked(state)) {
if (!locked) {
store.dispatch(fetchAccounts())
}
events(store)
events({ store, locked })
function r(Comp) {
if (rootNode) {
@ -42,11 +45,11 @@ function r(Comp) {
}
}
r(<App store={store} history={history} />)
r(<App store={store} history={history} language={language} />)
if (module.hot) {
module.hot.accept('../components/App', () => {
const NewApp = require('../components/App').default // eslint-disable-line global-require
r(<NewApp store={store} history={history} />)
r(<NewApp store={store} history={history} language={language} />)
})
}

12
src/types/common.js

@ -13,6 +13,7 @@ export type Devices = Array<Device>
export type Transaction = {
balance: number,
hash: string,
time: number,
}
// -------------------- Accounts
@ -35,6 +36,15 @@ export type Accounts = { [_: string]: Account }
// -------------------- Settings
export type Settings = Object
export type SettingsProfile = {
password: {
state: boolean,
value: string,
},
}
export type SettingsDisplay = {
language: string,
}
export type Settings = SettingsProfile & SettingsDisplay
export type T = (string, ?Object) => string

192
yarn.lock

@ -2,25 +2,25 @@
# yarn lockfile v1
"7zip-bin-linux@^1.1.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/7zip-bin-linux/-/7zip-bin-linux-1.2.0.tgz#c0ddfb640b255e14bd6730c26af45b2669c0193c"
"7zip-bin-linux@~1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/7zip-bin-linux/-/7zip-bin-linux-1.3.1.tgz#4856db1ab1bf5b6ee8444f93f5a8ad71446d00d5"
"7zip-bin-mac@^1.0.1":
"7zip-bin-mac@~1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/7zip-bin-mac/-/7zip-bin-mac-1.0.1.tgz#3e68778bbf0926adc68159427074505d47555c02"
"7zip-bin-win@^2.1.1":
"7zip-bin-win@~2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/7zip-bin-win/-/7zip-bin-win-2.1.1.tgz#8acfc28bb34e53a9476b46ae85a97418e6035c20"
"7zip-bin@^2.3.4":
version "2.3.4"
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-2.3.4.tgz#0861a3c99793dd794f4dd6175ec4ddfa6af8bc9d"
"7zip-bin@^2.4.1":
version "2.4.1"
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-2.4.1.tgz#88cf99736d35b104dab1d430c4edd1d51e58aade"
optionalDependencies:
"7zip-bin-linux" "^1.1.0"
"7zip-bin-mac" "^1.0.1"
"7zip-bin-win" "^2.1.1"
"7zip-bin-linux" "~1.3.1"
"7zip-bin-mac" "~1.0.1"
"7zip-bin-win" "~2.1.1"
"7zip@0.0.6":
version "0.0.6"
@ -78,9 +78,9 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@ledgerhq/common@2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@ledgerhq/common/-/common-2.0.4.tgz#d1679bb2636bce6cdc624d9691fe38c316ae0d7e"
"@ledgerhq/common@2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@ledgerhq/common/-/common-2.0.5.tgz#5f5eca4ac907914d700540c20b3d733cd7a25a93"
dependencies:
fbjs "^0.8.16"
invariant "^2.2.2"
@ -93,28 +93,28 @@
redux "^3.7.2"
redux-thunk "^2.2.0"
"@ledgerhq/hw-app-btc@^1.1.2-beta.068e2a14":
version "1.1.2-beta.068e2a14"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-1.1.2-beta.068e2a14.tgz#0e028ac16dd96808d13a846ee5adc649ba1f3a1b"
"@ledgerhq/hw-app-btc@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-2.0.5.tgz#44dabd18c4dcc68127869d384b06f0601c4a7a0e"
dependencies:
"@ledgerhq/hw-transport" "^1.1.2-beta.068e2a14"
"@ledgerhq/hw-transport" "^2.0.5"
"@ledgerhq/hw-app-eth@^1.1.2-beta.068e2a14":
version "1.1.2-beta.068e2a14"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-1.1.2-beta.068e2a14.tgz#6b9cf888dc109ccadbfd38f6249e973c08366d2a"
"@ledgerhq/hw-app-eth@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-2.0.5.tgz#844d938d7985f498058e7bd5cd48bc86b510d5ec"
dependencies:
"@ledgerhq/hw-transport" "^1.1.2-beta.068e2a14"
"@ledgerhq/hw-transport" "^2.0.5"
"@ledgerhq/hw-transport-node-hid@^1.1.2-beta.068e2a14":
version "1.1.2-beta.068e2a14"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-1.1.2-beta.068e2a14.tgz#dae63fcfbab570363fc89b93e4e78010c09bfe54"
"@ledgerhq/hw-transport-node-hid@^2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-2.0.6.tgz#29e76b05156218ccd1fe4c78c887829ac9598f98"
dependencies:
"@ledgerhq/hw-transport" "^1.1.2-beta.068e2a14"
"@ledgerhq/hw-transport" "^2.0.5"
node-hid "^0.7.2"
"@ledgerhq/hw-transport@^1.1.2-beta.068e2a14":
version "1.1.2-beta.068e2a14"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-1.1.2-beta.068e2a14.tgz#66c878040bcdff514983e8908ee4645093e2a8a5"
"@ledgerhq/hw-transport@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-2.0.5.tgz#5070a0e8dfb22f365b08dcf10fb03a8bf44fa5bf"
dependencies:
events "^1.1.1"
invariant "^2.2.0"
@ -2015,22 +2015,22 @@ buffers@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
builder-util-runtime@4.0.2, builder-util-runtime@^4.0.2, builder-util-runtime@~4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-4.0.2.tgz#673f1a0f2e275e6f80a16ce57225589a003c9a52"
builder-util-runtime@4.0.3, builder-util-runtime@^4.0.3, builder-util-runtime@~4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-4.0.3.tgz#c9f1959598e3fb534cdbe9ce4160e985af11a0fe"
dependencies:
bluebird-lst "^1.0.5"
debug "^3.1.0"
fs-extra-p "^4.5.0"
sax "^1.2.4"
builder-util@4.1.7, builder-util@^4.1.7:
version "4.1.7"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-4.1.7.tgz#41f165ff6b3c8fde18ef4076e41a35a17c055a9d"
builder-util@4.2.1, builder-util@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-4.2.1.tgz#ca9f0ddb5af1da5fe432129f7c6cbd447b552016"
dependencies:
"7zip-bin" "^2.3.4"
"7zip-bin" "^2.4.1"
bluebird-lst "^1.0.5"
builder-util-runtime "^4.0.2"
builder-util-runtime "^4.0.3"
chalk "^2.3.0"
debug "^3.1.0"
fs-extra-p "^4.5.0"
@ -3014,12 +3014,12 @@ dijkstrajs@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.1.tgz#d3cd81221e3ea40742cfcde556d4e99e98ddc71b"
dmg-builder@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-3.1.1.tgz#6e363919235d509df582c143ad5aa90b1ec0a994"
dmg-builder@3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-3.1.3.tgz#aa296f4be369e7ff013e67923adc70258bc0a510"
dependencies:
bluebird-lst "^1.0.5"
builder-util "^4.1.7"
builder-util "^4.2.1"
fs-extra-p "^4.5.0"
iconv-lite "^0.4.19"
js-yaml "^3.10.0"
@ -3165,22 +3165,22 @@ ejs@^2.5.7:
version "2.5.7"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
electron-builder-lib@19.54.0:
version "19.54.0"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-19.54.0.tgz#0029a5c98563d817d1d90721773e11eb84d680ca"
electron-builder-lib@19.55.2:
version "19.55.2"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-19.55.2.tgz#9808495613cd947a0d1abf83b962c16da6402f9c"
dependencies:
"7zip-bin" "^2.3.4"
"7zip-bin" "^2.4.1"
asar-integrity "0.2.4"
async-exit-hook "^2.0.1"
bluebird-lst "^1.0.5"
builder-util "4.1.7"
builder-util-runtime "4.0.2"
builder-util "4.2.1"
builder-util-runtime "4.0.3"
chromium-pickle-js "^0.2.0"
debug "^3.1.0"
dmg-builder "3.1.1"
dmg-builder "3.1.3"
ejs "^2.5.7"
electron-osx-sign "0.4.8"
electron-publish "19.54.0"
electron-publish "19.55.2"
fs-extra-p "^4.5.0"
hosted-git-info "^2.5.0"
is-ci "^1.1.0"
@ -3195,15 +3195,15 @@ electron-builder-lib@19.54.0:
semver "^5.5.0"
temp-file "^3.1.1"
electron-builder@^19.54.0:
version "19.54.0"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-19.54.0.tgz#b70b6876f8b9e09a53824bcad43172126c5e2543"
electron-builder@^19.55.2:
version "19.55.2"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-19.55.2.tgz#af709037a97e85fe55cf87e87255bf96333750a3"
dependencies:
bluebird-lst "^1.0.5"
builder-util "4.1.7"
builder-util-runtime "4.0.2"
builder-util "4.2.1"
builder-util-runtime "4.0.3"
chalk "^2.3.0"
electron-builder-lib "19.54.0"
electron-builder-lib "19.55.2"
electron-download-tf "4.3.4"
fs-extra-p "^4.5.0"
is-ci "^1.1.0"
@ -3265,13 +3265,13 @@ electron-osx-sign@0.4.8:
minimist "^1.2.0"
plist "^2.1.0"
electron-publish@19.54.0:
version "19.54.0"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-19.54.0.tgz#f7521550ce869f54b1c0e88d98620d4be56567e4"
electron-publish@19.55.2:
version "19.55.2"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-19.55.2.tgz#773b6d13bc11312095848c08b3287f98c91ccc7e"
dependencies:
bluebird-lst "^1.0.5"
builder-util "^4.1.7"
builder-util-runtime "^4.0.2"
builder-util "^4.2.1"
builder-util-runtime "^4.0.3"
chalk "^2.3.0"
fs-extra-p "^4.5.0"
mime "^2.2.0"
@ -3292,19 +3292,19 @@ electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30:
dependencies:
electron-releases "^2.1.0"
electron-updater@^2.19.1:
version "2.19.1"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-2.19.1.tgz#2265136b7ef15f7cb06e4032eaaf64382f642437"
electron-updater@^2.20.1:
version "2.20.1"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-2.20.1.tgz#3d2714a3e472fbf198f6053daf8fd12209101aa2"
dependencies:
bluebird-lst "^1.0.5"
builder-util-runtime "~4.0.2"
builder-util-runtime "~4.0.3"
electron-is-dev "^0.3.0"
fs-extra-p "^4.5.0"
js-yaml "^3.10.0"
lazy-val "^1.0.3"
lodash.isequal "^4.5.0"
semver "^5.4.1"
source-map-support "^0.5.0"
semver "^5.5.0"
source-map-support "^0.5.2"
electron-webpack-js@~1.1.0:
version "1.1.0"
@ -3425,12 +3425,6 @@ error-ex@^1.2.0, error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
error-stack-parser@^1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-1.3.6.tgz#e0e73b93e417138d1cd7c0b746b1a4a14854c292"
dependencies:
stackframe "^0.3.1"
es-abstract@^1.10.0, es-abstract@^1.4.3, es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0, es-abstract@^1.9.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
@ -3576,9 +3570,9 @@ eslint-module-utils@^2.1.1:
debug "^2.6.8"
pkg-dir "^1.0.0"
eslint-plugin-flowtype@^2.40.1:
version "2.41.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.41.0.tgz#fd5221c60ba917c059d7ef69686a99cca09fd871"
eslint-plugin-flowtype@^2.41.1:
version "2.41.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.41.1.tgz#0996e1ea1d501dfc945802453a304ae9e8098b78"
dependencies:
lodash "^4.15.0"
@ -5790,7 +5784,7 @@ md5.js@^1.3.4:
hash-base "^3.0.0"
inherits "^2.0.1"
md5@^2.1.0:
md5@^2.1.0, md5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
dependencies:
@ -7236,12 +7230,13 @@ raven-js@^3.22.1:
version "3.22.1"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
raven@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/raven/-/raven-2.3.0.tgz#96f15346bdaa433b3b6d47130804506155833d69"
raven@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raven/-/raven-2.4.0.tgz#49b7d5f838e5893f31dd72f82d05a35e42203f60"
dependencies:
cookie "0.3.1"
lsmod "1.0.0"
md5 "^2.2.1"
stack-trace "0.0.9"
timed-out "4.0.1"
uuid "3.0.0"
@ -7313,16 +7308,14 @@ react-fuzzy@^0.5.1:
fuse.js "^3.0.1"
prop-types "^15.5.9"
react-hot-loader@^4.0.0-beta.12:
version "4.0.0-beta.15"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.15.tgz#d32d23bad2f2f1f7d084e5a28bebca127066c5bc"
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"
dependencies:
fast-levenshtein "^2.0.6"
global "^4.3.0"
hoist-non-react-statics "^2.3.1"
react-stand-in "^4.0.0-beta.15"
redbox-react "^1.3.6"
source-map "^0.6.1"
react-stand-in "^4.0.0-beta.17"
react-html-attributes@^1.3.0:
version "1.4.1"
@ -7442,9 +7435,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.15:
version "4.0.0-beta.15"
resolved "https://registry.yarnpkg.com/react-stand-in/-/react-stand-in-4.0.0-beta.15.tgz#8c97cb1e6207c86c4deb04913fc5e23e04bdcc13"
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"
dependencies:
shallowequal "^1.0.2"
@ -7618,15 +7611,6 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"
redbox-react@^1.3.6:
version "1.5.0"
resolved "https://registry.yarnpkg.com/redbox-react/-/redbox-react-1.5.0.tgz#04dab11557d26651bf3562a67c22ace56c5d3967"
dependencies:
error-stack-parser "^1.3.6"
object-assign "^4.0.1"
prop-types "^15.5.4"
sourcemapped-stacktrace "^1.1.6"
redent@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
@ -8290,10 +8274,6 @@ source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
source-map@0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@ -8302,12 +8282,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
sourcemapped-stacktrace@^1.1.6:
version "1.1.8"
resolved "https://registry.yarnpkg.com/sourcemapped-stacktrace/-/sourcemapped-stacktrace-1.1.8.tgz#6b7a3f1a6fb15f6d40e701e23ce404553480d688"
dependencies:
source-map "0.5.6"
spawn-command@^0.0.2-1:
version "0.0.2-1"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
@ -8387,10 +8361,6 @@ stack-trace@0.0.9:
version "0.0.9"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695"
stackframe@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4"
staged-git-files@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35"

Loading…
Cancel
Save