diff --git a/package.json b/package.json index 392b553d..744b9d95 100644 --- a/package.json +++ b/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" } } diff --git a/src/components/AccountPage.js b/src/components/AccountPage.js index 85b12696..d0591be7 100644 --- a/src/components/AccountPage.js +++ b/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 { - {accountData.transactions.map(tr => ( - - {'-'} - {format(tr.balance)} + {accountData.transactions.map(tx => ( + + {moment(tx.time * 1e3).format('LLL')} + {format(tx.balance)} ))} diff --git a/src/components/App.js b/src/components/App.js index b4444b57..0700ac54 100644 --- a/src/components/App.js +++ b/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, +}) => ( - + diff --git a/src/components/DashboardPage.js b/src/components/DashboardPage.js index 84d52b8e..b7813210 100644 --- a/src/components/DashboardPage.js +++ b/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 { 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 ( @@ -50,7 +66,9 @@ class DashboardPage extends PureComponent { {'Hello Anonymous,'} - {'here is the summary of your 5 accounts'} + {totalAccounts > 0 + ? `here is the summary of your ${totalAccounts} accounts` + : 'no accounts'} @@ -85,40 +103,45 @@ class DashboardPage extends PureComponent { /> - - - {'Brian account'} - - - {'Virginie account'} - - - {'Ledger account'} - - - - - {'Brian account'} - - - {'Virginie account'} - + {chunk([...Object.keys(accounts), 'add-account'], 3).map((line, i) => ( - {'+ Add account'} + {line.map( + key => + key === 'add-account' ? ( + openModal('add-account')} + > + {'+ Add account'} + + ) : ( + push(`/account/${key}`)} + > + {accounts[key].name} + + ), + )} - + ))} ) } } -export default connect(mapStateToProps)(DashboardPage) +export default connect(mapStateToProps, mapDispatchToProps)(DashboardPage) diff --git a/src/components/IsUnlocked/index.js b/src/components/IsUnlocked/index.js index 0968d8bd..f1f9b429 100644 --- a/src/components/IsUnlocked/index.js +++ b/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 { ...defaultState, } + componentWillReceiveProps(nextProps) { + if (this.props.isLocked && !nextProps.isLocked) { + startSyncAccounts(nextProps.accounts) + } + + if (!this.props.isLocked && nextProps.isLocked) { + stopSyncAccounts() + } + } + handleChangeInput = (key: $Keys) => (value: $Values) => this.setState(prev => ({ inputValue: { diff --git a/src/components/SettingsPage/Display.js b/src/components/SettingsPage/Display.js new file mode 100644 index 00000000..b5de95c1 --- /dev/null +++ b/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 { + 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) => (value: $Values) => + this.setState(prev => ({ + inputValue: { + ...prev.inputValue, + [key]: value, + }, + })) + + handleSubmit = (e: SyntheticEvent) => { + 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 ( +
+ + + + this.handleChangeInput('password.state')(e.target.checked)} />{' '} with password @@ -123,4 +115,4 @@ class SettingsPage extends PureComponent { } } -export default connect(mapStateToProps, mapDispatchToProps)(SettingsPage) +export default connect(null, mapDispatchToProps)(TabProfile) diff --git a/src/components/SettingsPage/index.js b/src/components/SettingsPage/index.js index 8f3f7eb3..23863f9a 100644 --- a/src/components/SettingsPage/index.js +++ b/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 { 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 ( - {'Settings'} + {t('settings.title')}
{'Affichage'}
, + title: t('settings.tabs.display'), + render: () => , }, { key: 'money', - title: 'Monnaie', + title: t('settings.tabs.money'), render: () =>
{'Monnaie'}
, }, { key: 'material', - title: 'Matériel', + title: t('settings.tabs.material'), render: () =>
{'Matériel'}
, }, { key: 'app', - title: 'App (beta)', + title: t('settings.tabs.app'), render: () =>
{'App (beta)'}
, }, { key: 'tools', - title: 'Outils', + title: t('settings.tabs.tools'), render: () =>
{'Outils'}
, }, { key: 'blockchain', - title: 'Blockchain', + title: t('settings.tabs.blockchain'), render: () =>
{'Blockchain'}
, }, { key: 'profile', - title: 'Profil', - render: () => , + title: t('settings.tabs.profile'), + render: () => , }, ]} /> @@ -73,4 +102,4 @@ class SettingsPage extends PureComponent { } } -export default SettingsPage +export default compose(connect(mapStateToProps, mapDispatchToProps), translate())(SettingsPage) diff --git a/src/components/SideBar/Item.js b/src/components/SideBar/Item.js index 0e177da9..46407f99 100644 --- a/src/components/SideBar/Item.js +++ b/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, diff --git a/src/components/Wrapper.js b/src/components/Wrapper.js index 548a6f44..3fc15d89 100644 --- a/src/components/Wrapper.js +++ b/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 ( diff --git a/src/components/base/Label.js b/src/components/base/Label.js new file mode 100644 index 00000000..bd87fd78 --- /dev/null +++ b/src/components/base/Label.js @@ -0,0 +1,6 @@ +import styled from 'styled-components' + +export default styled.label` + display: block; + text-transform: uppercase; +` diff --git a/src/components/modals/AddAccount.js b/src/components/modals/AddAccount.js index a4ffae17..9770cfa9 100644 --- a/src/components/modals/AddAccount.js +++ b/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) => ( @@ -229,6 +224,7 @@ class AddAccountModal extends PureComponent { }) closeModal('add-account') + this.handleClose() } handleChangeInput = (key: $Keys) => (value: $Values) => diff --git a/src/i18n/en/translation.yml b/src/i18n/en/translation.yml index 7c818448..1cf9eb59 100644 --- a/src/i18n/en/translation.yml +++ b/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 diff --git a/src/i18n/fr/translation.yml b/src/i18n/fr/translation.yml new file mode 100644 index 00000000..08d6d853 --- /dev/null +++ b/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 diff --git a/src/internals/usb/devices.js b/src/internals/usb/devices.js index 82b161df..d0a57dfe 100644 --- a/src/internals/usb/devices.js +++ b/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)), }) diff --git a/src/internals/usb/wallet/accounts.js b/src/internals/usb/wallet/accounts.js index f7153446..50c8d84b 100644 --- a/src/internals/usb/wallet/accounts.js +++ b/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) diff --git a/src/main/app.js b/src/main/app.js index 58f4e7e8..6a809f83 100644 --- a/src/main/app.js +++ b/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()) }) diff --git a/src/reducers/accounts.js b/src/reducers/accounts.js index 5255d890..37177266 100644 --- a/src/reducers/accounts.js +++ b/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) { diff --git a/src/reducers/settings.js b/src/reducers/settings.js index bdc6b21d..3e4eaa5c 100644 --- a/src/reducers/settings.js +++ b/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) diff --git a/src/renderer/events.js b/src/renderer/events.js index 8159b35f..97a55617 100644 --- a/src/renderer/events.js +++ b/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 diff --git a/src/renderer/i18n.js b/src/renderer/i18n.js index 39129a61..2166d0b8 100644 --- a/src/renderer/i18n.js +++ b/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: { diff --git a/src/renderer/index.js b/src/renderer/index.js index c97b6f72..eb26a1a0 100644 --- a/src/renderer/index.js +++ b/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() +r() if (module.hot) { module.hot.accept('../components/App', () => { const NewApp = require('../components/App').default // eslint-disable-line global-require - r() + r() }) } diff --git a/src/types/common.js b/src/types/common.js index 7d8ea066..0d41c713 100644 --- a/src/types/common.js +++ b/src/types/common.js @@ -13,6 +13,7 @@ export type Devices = Array 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 diff --git a/yarn.lock b/yarn.lock index d0cd7875..56e4af44 100644 --- a/yarn.lock +++ b/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"