Browse Source

Add add account feature

master
Loëck Vézien 7 years ago
parent
commit
335cf42128
No known key found for this signature in database GPG Key ID: CBCDCE384E853AC4
  1. 5
      .eslintrc
  2. 17
      package.json
  3. 19
      src/actions/accounts.js
  4. 9
      src/actions/wallets.js
  5. 66
      src/components/AccountPage.js
  6. 7
      src/components/SideBar/Item.js
  7. 72
      src/components/SideBar/index.js
  8. 13
      src/components/Wrapper.js
  9. 22
      src/components/base/Input/index.js
  10. 10
      src/components/base/Modal.js
  11. 224
      src/components/modals/AddAccount.js
  12. 0
      src/components/modals/Receive.js
  13. 0
      src/components/modals/Send.js
  14. 3
      src/components/modals/index.js
  15. 2
      src/globals.js
  16. 7
      src/helpers/db.js
  17. 2
      src/main/app.js
  18. 36
      src/main/bridge.js
  19. 17
      src/middlewares/db.js
  20. 30
      src/reducers/accounts.js
  21. 4
      src/reducers/devices.js
  22. 10
      src/reducers/index.js
  23. 20
      src/reducers/wallets.js
  24. 4
      src/renderer/createStore.js
  25. 20
      src/renderer/events.js
  26. 4
      src/renderer/index.js
  27. 8
      src/types/common.js
  28. 9
      webpack/define.js
  29. 4
      webpack/internals.config.js
  30. 11
      webpack/plugins.js
  31. 4
      webpack/renderer.config.js
  32. 199
      yarn.lock

5
.eslintrc

@ -1,6 +1,6 @@
{
"parser": "babel-eslint",
"extends": ["airbnb", "prettier", "prettier/react"],
"extends": ["airbnb", "plugin:flowtype/recommended", "prettier", "prettier/react"],
"plugins": ["flowtype"],
"globals": {
"__ENV__": false,
@ -27,5 +27,8 @@
"import/resolver": {
"babel-module": {},
},
"flowtype": {
"onlyFilesWithFlowAnnotation": true
}
},
}

17
package.json

@ -20,7 +20,11 @@
"storybook": "start-storybook -p 4444"
},
"lint-staged": {
"*.js": ["eslint --fix", "prettier --write", "git add"]
"*.js": [
"eslint --fix",
"prettier --write",
"git add"
]
},
"electronWebpack": {
"renderer": {
@ -33,7 +37,7 @@
"@ledgerhq/hw-transport": "^1.1.2-beta.068e2a14",
"@ledgerhq/hw-transport-node-hid": "^1.1.2-beta.068e2a14",
"color": "^2.0.1",
"electron-devtools-installer": "^2.2.3",
"electron-store": "^1.3.0",
"electron-updater": "^2.18.2",
"history": "^4.7.2",
"i18next": "^10.2.2",
@ -52,8 +56,7 @@
"redux-thunk": "^2.2.0",
"source-map-support": "^0.5.0",
"styled-components": "^2.2.4",
"styled-system": "^1.1.1",
"webpack": "^3.10.0"
"styled-system": "^1.1.1"
},
"devDependencies": {
"@storybook/addon-actions": "^3.3.9",
@ -72,7 +75,8 @@
"babel-preset-stage-0": "^6.24.1",
"concurrently": "^3.5.1",
"electron": "1.7.10",
"electron-builder": "^19.53.7",
"electron-builder": "^19.54.0",
"electron-devtools-installer": "^2.2.3",
"electron-webpack": "1.11.0",
"eslint": "^4.13.1",
"eslint-config-airbnb": "^16.1.0",
@ -88,6 +92,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.12",
"webpack": "^3.10.0"
}
}

19
src/actions/accounts.js

@ -0,0 +1,19 @@
// @flow
/* eslint-disable import/prefer-default-export */
import db from 'helpers/db'
import type { Account } from 'types/common'
export type AddAccount = Account => { type: string, payload: Account }
export const addAccount: AddAccount = payload => ({
type: 'DB:ADD_ACCOUNT',
payload,
})
type FetchAccounts = () => { type: string }
export const fetchAccounts: FetchAccounts = () => ({
type: 'FETCH_ACCOUNTS',
payload: db.get('accounts', []),
})

9
src/actions/wallets.js

@ -1,9 +0,0 @@
// @flow
/* eslint-disable import/prefer-default-export */
export type SetCurrentWalletType = (Object | null) => { type: string, payload: Object | null }
export const setCurrentWallet: SetCurrentWalletType = payload => ({
type: 'SET_CURRENT_WALLET',
payload,
})

66
src/components/AccountPage.js

@ -1,73 +1,13 @@
// @flow
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import type { MapStateToProps } from 'react-redux'
import type { Device } from 'types/common'
import { getCurrentDevice } from 'reducers/devices'
import { sendEvent } from 'renderer/events'
import Box from 'components/base/Box'
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
currentDevice: getCurrentDevice(state),
currentWallet: state.wallets.currentWallet,
})
type Props = {
currentDevice: Device | null,
currentWallet: Object | null,
}
type State = {
address: Object | null,
}
class AccountPage extends PureComponent<Props, State> {
state = {
address: null,
}
componentDidMount() {
this.getWalletInfos()
}
componentWillReceiveProps(nextProps) {
const { currentWallet } = nextProps
if (currentWallet !== null && !currentWallet.err) {
this.setState({
address: currentWallet.data.bitcoinAddress,
})
} else {
this._timeout = setTimeout(() => this.getWalletInfos(), 2e3)
}
}
componentWillUnmount() {
clearTimeout(this._timeout)
}
getWalletInfos() {
const { currentDevice } = this.props
if (currentDevice !== null) {
sendEvent('usb', 'wallet.infos.request', {
path: currentDevice.path,
wallet: 'btc',
})
}
}
_timeout = undefined
class AccountPage extends PureComponent<{}> {
render() {
const { address } = this.state
return <Box>{address === null ? 'Select Bitcoin App on your Ledger' : address}</Box>
return <Box>{'account page'}</Box>
}
}
export default connect(mapStateToProps)(AccountPage)
export default AccountPage

7
src/components/SideBar/Item.js

@ -71,10 +71,13 @@ function Item({
isModalOpened,
}: Props) {
const { pathname } = location
const active = pathname === linkTo || isModalOpened
return (
<Container
onClick={linkTo ? () => push(linkTo) : modal ? () => openModal(modal) : void 0}
active={pathname === linkTo || isModalOpened}
onClick={
linkTo ? (active ? undefined : () => push(linkTo)) : modal ? () => openModal(modal) : void 0
}
active={active}
>
<IconWrapper mr={2}>{icon || null}</IconWrapper>
<div>

72
src/components/SideBar/index.js

@ -1,7 +1,14 @@
// @flow
import React, { PureComponent } from 'react'
import React, { PureComponent, Fragment } 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 { openModal } from 'reducers/modals'
import { getAccounts } from 'reducers/accounts'
import { rgba } from 'styles/helpers'
@ -13,6 +20,7 @@ const CapsSubtitle = styled(Box).attrs({
fontSize: 0,
color: 'shark',
})`
cursor: default;
text-transform: uppercase;
font-weight: bold;
`
@ -25,41 +33,67 @@ const Container = styled(Box).attrs({
width: 250px;
`
class SideBar extends PureComponent<{}> {
const BtnAddAccount = styled(Box).attrs({
align: 'center',
color: 'steel',
})`
border-radius: 5px;
border: 1px dashed ${p => p.theme.colors.steel};
cursor: pointer;
margin: 30px 30px 0 30px;
padding: 5px;
`
type Props = {
accounts: Accounts,
openModal: Function,
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
accounts: getAccounts(state),
})
const mapDispatchToProps = {
openModal,
}
class SideBar extends PureComponent<Props> {
render() {
const { accounts, openModal } = this.props
return (
<Container>
<GrowScroll flow={4} py={4}>
<Box flow={2}>
<CapsSubtitle>{'Menu'}</CapsSubtitle>
<div>
<Fragment>
<Item linkTo="/">{'Dashboard'}</Item>
<Item modal="send">{'Send'}</Item>
<Item modal="receive">{'Receive'}</Item>
<Item linkTo="/settings">{'Settings'}</Item>
</div>
</Fragment>
</Box>
<Box flow={2}>
<CapsSubtitle>{'Accounts'}</CapsSubtitle>
<div>
<Item linkTo="/account/brian" desc="BTC 3.78605936">
{'Brian Account'}
</Item>
<Item linkTo="/account/virginie" desc="ETH 0.05944">
{'Virginie Account'}
</Item>
<Item linkTo="/account/ledger" desc="DOGE 2.2658">
{'Ledger Account'}
</Item>
<Item linkTo="/account/nicolas" desc="BTC 0.00015486">
{'Nicolas Account'}
</Item>
</div>
<Fragment>
{accounts.map((account, i) => (
<Item
linkTo="/account/brian"
desc={`${account.type.toUpperCase()} 3.78605936`}
key={i} // eslint-disable-line react/no-array-index-key
>
{account.name}
</Item>
))}
</Fragment>
</Box>
<BtnAddAccount onClick={() => openModal('add-account')}>{'Add account'}</BtnAddAccount>
</GrowScroll>
</Container>
)
}
}
export default SideBar
export default connect(mapStateToProps, mapDispatchToProps, null, {
pure: false,
})(SideBar)

13
src/components/Wrapper.js

@ -6,12 +6,11 @@ import { Route } from 'react-router'
import { translate } from 'react-i18next'
import Box from 'components/base/Box'
import * as modals from 'components/modals'
import AccountPage from 'components/AccountPage'
import DashboardPage from 'components/DashboardPage'
import SettingsPage from 'components/SettingsPage'
import AccountPage from 'components/AccountPage'
import SendModal from 'components/SendModal'
import ReceiveModal from 'components/ReceiveModal'
import UpdateNotifier from 'components/UpdateNotifier'
import AppRegionDrag from 'components/AppRegionDrag'
@ -27,12 +26,12 @@ class Wrapper extends PureComponent<{}> {
return (
<Fragment>
<AppRegionDrag />
<SendModal />
<ReceiveModal />
<UpdateNotifier />
{Object.entries(modals).map(([name, ModalComponent]: [string, any]) => (
<ModalComponent key={name} />
))}
<Box grow horizontal>
<SideBar />

22
src/components/base/Input/index.js

@ -1,6 +1,10 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
export default styled.input`
const Base = styled.input`
padding: 10px 15px;
border: 1px solid ${p => p.theme.colors.mouse};
border-radius: 3px;
@ -18,3 +22,19 @@ export default styled.input`
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 2px;
}
`
type Props = {
onChange: Function,
}
export default class Input extends PureComponent<Props> {
handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
const { onChange } = this.props
onChange(e.target.value)
}
render() {
return <Base {...this.props} onChange={this.handleChange} />
}
}

10
src/components/base/Modal.js

@ -6,6 +6,7 @@ import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import Mortal from 'react-mortal'
import styled from 'styled-components'
import noop from 'lodash/noop'
import { closeModal, isModalOpened } from 'reducers/modals'
@ -23,8 +24,13 @@ const mapStateToProps = (state, { name, isOpened }) => ({
isOpened: isOpened || (name && isModalOpened(state, name)),
})
const mapDispatchToProps = (dispatch, { name }) => ({
onClose: name ? () => dispatch(closeModal(name)) : undefined,
const mapDispatchToProps = (dispatch, { name, onClose = noop }) => ({
onClose: name
? () => {
dispatch(closeModal(name))
onClose()
}
: onClose,
})
const Container = styled.div.attrs({

224
src/components/modals/AddAccount.js

@ -0,0 +1,224 @@
// @flow
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { connect } from 'react-redux'
import type { MapStateToProps } from 'react-redux'
import type { Device } from 'types/common'
import { sendSyncEvent } from 'renderer/events'
import { getCurrentDevice } from 'reducers/devices'
import { closeModal } from 'reducers/modals'
import { addAccount } from 'actions/accounts'
import Button from 'components/base/Button'
import Input from 'components/base/Input'
import Modal from 'components/base/Modal'
const Label = styled.label`
display: block;
text-transform: uppercase;
`
const Steps = {
createAccount: (props: Object) => (
<form onSubmit={props.onSubmit}>
<div>
<Label>Currency</Label>
<select
onChange={e => props.onChangeInput('wallet')(e.target.value)}
value={props.value.wallet}
>
<option value="">---</option>
<option value="btc">Bitcoin</option>
</select>
</div>
<div>
<Label>Account name</Label>
<Input onChange={props.onChangeInput('accountName')} value={props.value.accountName} />
</div>
<Button type="submit">Create account</Button>
</form>
),
connectDevice: () => <div>Connect your Ledger</div>,
startWallet: (props: Object) => <div>Select {props.wallet.toUpperCase()} App on your Ledger</div>,
confirmation: (props: Object) => (
<div>
Add {props.wallet.toUpperCase()} - {props.accountName} - {props.walletAddress} ?
<Button onClick={props.onConfirm}>Yes!</Button>
</div>
),
}
type InputValue = {
accountName: string,
wallet: string,
}
type Step = 'createAccount' | 'connectDevice' | 'startWallet' | 'confirmation'
type Props = {
addAccount: Function,
closeModal: Function,
currentDevice: Device | null,
}
type State = {
inputValue: InputValue,
step: Step,
walletAddress: string,
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
currentDevice: getCurrentDevice(state),
})
const mapDispatchToProps = {
addAccount,
closeModal,
}
const defaultState = {
inputValue: {
accountName: '',
wallet: '',
},
walletAddress: '',
step: 'createAccount',
}
class AddAccountModal extends PureComponent<Props, State> {
state = {
...defaultState,
}
componentWillReceiveProps(nextProps) {
const { currentDevice } = nextProps
if (this.state.step !== 'createAccount') {
this.setState({
step: currentDevice !== null ? 'startWallet' : 'connectDevice',
})
}
}
componentDidUpdate() {
const { step } = this.state
const { currentDevice } = this.props
if (step === 'startWallet' && currentDevice !== null) {
this.getWalletInfos()
} else {
clearTimeout(this._timeout)
}
}
getWalletInfos() {
const { inputValue } = this.state
const { currentDevice } = this.props
if (currentDevice === null) {
return
}
const { data: { data }, type } = sendSyncEvent('usb', 'wallet.infos.request', {
path: currentDevice.path,
wallet: inputValue.wallet,
})
if (type === 'wallet.infos.fail') {
this._timeout = setTimeout(() => this.getWalletInfos(), 1e3)
}
if (type === 'wallet.infos.success') {
this.setState({
walletAddress: data.bitcoinAddress,
step: 'confirmation',
})
}
}
getStepProps() {
const { inputValue, walletAddress, step } = this.state
const props = (predicate, props) => (predicate ? props : {})
return {
...props(step === 'createAccount', {
value: inputValue,
onSubmit: this.handleSubmit,
onChangeInput: this.handleChangeInput,
}),
...props(step === 'startWallet', {
wallet: inputValue.wallet,
}),
...props(step === 'confirmation', {
accountName: inputValue.accountName,
onConfirm: this.handleAddAccount,
wallet: inputValue.wallet,
walletAddress,
}),
}
}
handleAddAccount = () => {
const { inputValue, walletAddress } = this.state
const { addAccount, closeModal } = this.props
const account = {
name: inputValue.accountName,
type: inputValue.wallet,
address: walletAddress,
}
addAccount(account)
closeModal('add-account')
}
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>
this.setState(prev => ({
inputValue: {
...prev.inputValue,
[key]: value,
},
}))
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
e.preventDefault()
const { currentDevice } = this.props
const { inputValue } = this.state
if (inputValue.accountName.trim() === '' || inputValue.wallet.trim() === '') {
return
}
this.setState({
step: currentDevice === null ? 'connectDevice' : 'startWallet',
})
}
handleClose = () => {
clearTimeout(this._timeout)
this.setState({
...defaultState,
})
}
_timeout = undefined
render() {
const { step } = this.state
const Step = Steps[step]
return (
<Modal name="add-account" onClose={this.handleClose}>
<Step {...this.getStepProps()} />
</Modal>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddAccountModal)

0
src/components/ReceiveModal.js → src/components/modals/Receive.js

0
src/components/SendModal.js → src/components/modals/Send.js

3
src/components/modals/index.js

@ -0,0 +1,3 @@
export AddAccount from './AddAccount'
export Receive from './Receive'
export Send from './Send'

2
src/globals.js

@ -2,6 +2,6 @@
const { NODE_ENV } = process.env
global.__ENV__ = NODE_ENV
global.__ENV__ = NODE_ENV === 'production' ? NODE_ENV : 'development'
global.__DEV__ = global.__ENV__ === 'development'
global.__PROD__ = !global.__DEV__

7
src/helpers/db.js

@ -0,0 +1,7 @@
import Store from 'electron-store'
const store = new Store({
// encryptionKey: 'toto',
})
export default store

2
src/main/app.js

@ -11,7 +11,7 @@ function createMainWindow() {
? {
frame: false,
titleBarStyle: 'hiddenInset',
vibrancy: 'ultra-dark',
vibrancy: 'ultra-dark', // https://github.com/electron/electron/issues/10521
}
: {}),
show: false,

36
src/main/bridge.js

@ -7,21 +7,31 @@ import { resolve } from 'path'
import setupAutoUpdater from './autoUpdate'
// Forwards every usb message to usb process
ipcMain.on('usb', (event: any, payload) => {
const { type, data } = payload
function onChannelUsb(callType) {
return (event: any, payload) => {
const { type, data } = payload
const compute = fork(resolve(__dirname, `${__DEV__ ? '../../' : './'}dist/internals/usb`))
const compute = fork(resolve(__dirname, `${__DEV__ ? '../../' : './'}dist/internals/usb`))
compute.send({ type, data })
compute.on('message', payload => {
const { type, data, options = {} } = payload
event.sender.send('msg', { type, data })
if (options.kill) {
compute.kill()
}
})
})
compute.send({ type, data })
compute.on('message', payload => {
const { type, data, options = {} } = payload
if (callType === 'async') {
event.sender.send('msg', { type, data })
}
if (callType === 'sync') {
event.returnValue = { type, data }
}
if (options.kill) {
compute.kill()
}
})
}
}
// Forwards every usb message to usb process
ipcMain.on('usb', onChannelUsb('async'))
ipcMain.on('usb:sync', onChannelUsb('sync'))
const handlers = {
updater: {

17
src/middlewares/db.js

@ -0,0 +1,17 @@
import db from 'helpers/db'
// eslint-disable-next-line consistent-return
export default store => next => action => {
if (!action.type.startsWith('DB:')) {
return next(action)
}
const { dispatch, getState } = store
const [, type] = action.type.split(':')
dispatch({ type, payload: action.payload })
const { accounts } = getState()
db.set('accounts', accounts.accounts)
}

30
src/reducers/accounts.js

@ -0,0 +1,30 @@
// @flow
import { handleActions } from 'redux-actions'
import type { Account, Accounts } from 'types/common'
export type AccountsState = {
accounts: Accounts,
}
const state: AccountsState = {
accounts: [],
}
const handlers: Object = {
ADD_ACCOUNT: (state: AccountsState, { payload: account }: { payload: Account }) => ({
...state,
accounts: [...state.accounts, account],
}),
FETCH_ACCOUNTS: (state: AccountsState, { payload: accounts }: { payload: Accounts }) => ({
...state,
accounts,
}),
}
export function getAccounts(state: { accounts: AccountsState }) {
return state.accounts.accounts
}
export default handleActions(handlers, state)

4
src/reducers/devices.js

@ -48,11 +48,11 @@ const handlers: Object = {
}),
}
export function getCurrentDevice(state: Object) {
export function getCurrentDevice(state: { devices: DevicesState }) {
return state.devices.currentDevice
}
export function getDevices(state: Object) {
export function getDevices(state: { devices: DevicesState }) {
return state.devices.devices
}

10
src/reducers/index.js

@ -5,28 +5,28 @@ import { routerReducer as router } from 'react-router-redux'
import type { LocationShape } from 'react-router'
import accounts from './accounts'
import devices from './devices'
import modals from './modals'
import update from './update'
import wallets from './wallets'
import type { AccountsState } from './accounts'
import type { DevicesState } from './devices'
import type { ModalsState } from './modals'
import type { UpdateState } from './update'
import type { WalletsState } from './wallets'
export type State = {
router: LocationShape,
accounts: AccountsState,
devices: DevicesState,
modals: ModalsState,
router: LocationShape,
update: UpdateState,
wallets: WalletsState,
}
export default combineReducers({
accounts,
devices,
modals,
router,
update,
wallets,
})

20
src/reducers/wallets.js

@ -1,20 +0,0 @@
// @flow
import { handleActions } from 'redux-actions'
export type WalletsState = {
currentWallet: Object | null,
}
const state: WalletsState = {
currentWallet: null,
}
const handlers: Object = {
SET_CURRENT_WALLET: (state: Object, { payload: currentWallet }: { payload: WalletsState }) => ({
...state,
currentWallet,
}),
}
export default handleActions(handlers, state)

4
src/renderer/createStore.js

@ -6,10 +6,12 @@ import { createStore, applyMiddleware, compose } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
import db from 'middlewares/db'
import reducers from 'reducers'
export default (history: RouterHistory) => {
const middlewares = [routerMiddleware(history), thunk]
const middlewares = [routerMiddleware(history), thunk, db]
const enhancers = compose(
applyMiddleware(...middlewares),
window.devToolsExtension ? window.devToolsExtension() : f => f, // eslint-disable-line

20
src/renderer/events.js

@ -4,12 +4,11 @@ import { ipcRenderer } from 'electron'
import objectPath from 'object-path'
import { updateDevices, addDevice, removeDevice } from 'actions/devices'
import { setCurrentWallet } from 'actions/wallets'
import { setUpdateStatus } from 'reducers/update'
type MsgPayload = {
type: string,
data: *,
data: any,
}
// wait a bit before launching update check
@ -22,6 +21,13 @@ export function sendEvent(channel: string, msgType: string, data: any) {
})
}
export function sendSyncEvent(channel: string, msgType: string, data: any): any {
return ipcRenderer.sendSync(`${channel}:sync`, {
type: msgType,
data,
})
}
export default (store: Object) => {
const handlers = {
devices: {
@ -33,12 +39,6 @@ export default (store: Object) => {
add: device => store.dispatch(addDevice(device)),
remove: device => store.dispatch(removeDevice(device)),
},
wallet: {
infos: {
success: ({ wallet, data }) => store.dispatch(setCurrentWallet({ wallet, data })),
fail: ({ wallet, err }) => store.dispatch(setCurrentWallet({ wallet, err })),
},
},
updater: {
checking: () => store.dispatch(setUpdateStatus('checking')),
updateAvailable: info => store.dispatch(setUpdateStatus('available', info)),
@ -49,14 +49,12 @@ export default (store: Object) => {
},
}
ipcRenderer.on('msg', (e: *, payload: MsgPayload) => {
ipcRenderer.on('msg', (event: any, payload: MsgPayload) => {
const { type, data } = payload
const handler = objectPath.get(handlers, type)
if (!handler) {
return
}
handler(data)
})

4
src/renderer/index.js

@ -8,6 +8,8 @@ import createHistory from 'history/createHashHistory'
import createStore from 'renderer/createStore'
import events from 'renderer/events'
import { fetchAccounts } from 'actions/accounts'
import App from 'components/App'
import 'styles/global'
@ -18,6 +20,8 @@ const rootNode = document.getElementById('app')
events(store)
store.dispatch(fetchAccounts())
function r(Comp) {
if (rootNode) {
render(<AppContainer>{Comp}</AppContainer>, rootNode)

8
src/types/common.js

@ -8,4 +8,12 @@ export type Device = {
export type Devices = Array<Device>
export type Account = {
name: string,
type: string,
address: string,
}
export type Accounts = Array<Account>
export type T = (string, ?Object) => string

9
webpack/define.js

@ -1,9 +0,0 @@
const webpack = require('webpack')
require('../src/globals')
module.exports = new webpack.DefinePlugin({
__DEV__,
__PROD__,
'process.env.NODE_ENV': __PROD__ ? '"production"' : '"development"',
})

4
webpack/internals.config.js

@ -2,7 +2,7 @@ const path = require('path')
const fs = require('fs')
const webpackMain = require('electron-webpack/webpack.main.config')
const define = require('./define')
const plugins = require('./plugins')
const dirs = p =>
fs
@ -35,5 +35,5 @@ module.exports = webpackMain().then(config => ({
module: config.module,
plugins: [define, ...config.plugins],
plugins: [...plugins, ...config.plugins],
}))

11
webpack/plugins.js

@ -0,0 +1,11 @@
const webpack = require('webpack')
require('../src/globals')
module.exports = [
new webpack.DefinePlugin({
__DEV__,
__PROD__,
'process.env.NODE_ENV': `"${__ENV__}"`,
}),
]

4
webpack/renderer.config.js

@ -1,7 +1,7 @@
const define = require('./define')
const plugins = require('./plugins')
const config = {
plugins: [define],
plugins,
devServer: {
historyApiFallback: true,
},

199
yarn.lock

@ -380,7 +380,7 @@ ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5, ajv@^5.2.3, ajv@^5.3.0, ajv@^5.5.2:
ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5, ajv@^5.2.3, ajv@^5.3.0, ajv@^5.5.0, ajv@^5.5.2:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
@ -1882,14 +1882,7 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
caniuse-db "^1.0.30000639"
electron-to-chromium "^1.2.7"
browserslist@^2.1.2:
version "2.11.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.2.tgz#76ad768b97a689512fcd9724a8b9d76cdffb18fd"
dependencies:
caniuse-lite "^1.0.30000791"
electron-to-chromium "^1.3.30"
browserslist@^2.11.1:
browserslist@^2.1.2, browserslist@^2.11.1:
version "2.11.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.3.tgz#fe36167aed1bbcde4827ebfe71347a2cc70b99b2"
dependencies:
@ -1923,7 +1916,7 @@ 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, builder-util-runtime@^4.0.2, builder-util-runtime@~4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-4.0.2.tgz#673f1a0f2e275e6f80a16ce57225589a003c9a52"
dependencies:
@ -1932,18 +1925,9 @@ builder-util-runtime@4.0.2, builder-util-runtime@^4.0.2:
fs-extra-p "^4.5.0"
sax "^1.2.4"
builder-util-runtime@^4.0.1, builder-util-runtime@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-4.0.1.tgz#d8423190a21e8c7cec185d589cb0cb888cc8e731"
dependencies:
bluebird-lst "^1.0.5"
debug "^3.1.0"
fs-extra-p "^4.5.0"
sax "^1.2.4"
builder-util@4.1.6, builder-util@^4.1.6:
version "4.1.6"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-4.1.6.tgz#369313429c3feebb75bb60808382397195e18a30"
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"
dependencies:
"7zip-bin" "^2.3.4"
bluebird-lst "^1.0.5"
@ -1955,28 +1939,8 @@ builder-util@4.1.6, builder-util@^4.1.6:
is-ci "^1.1.0"
js-yaml "^3.10.0"
lazy-val "^1.0.3"
semver "^5.4.1"
source-map-support "^0.5.0"
stat-mode "^0.2.2"
temp-file "^3.1.1"
tunnel-agent "^0.6.0"
builder-util@^4.1.0:
version "4.1.5"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-4.1.5.tgz#924a7ea13368d8d3f9626d22de14a18ef170ff21"
dependencies:
"7zip-bin" "^2.3.4"
bluebird-lst "^1.0.5"
builder-util-runtime "^4.0.1"
chalk "^2.3.0"
debug "^3.1.0"
fs-extra-p "^4.5.0"
ini "^1.3.5"
is-ci "^1.1.0"
js-yaml "^3.10.0"
lazy-val "^1.0.3"
semver "^5.4.1"
source-map-support "^0.5.0"
semver "^5.5.0"
source-map-support "^0.5.1"
stat-mode "^0.2.2"
temp-file "^3.1.1"
tunnel-agent "^0.6.0"
@ -2075,8 +2039,8 @@ caniuse-api@^1.5.2:
lodash.uniq "^4.5.0"
caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000792"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000792.tgz#a7dac6dc9f5181b675fd69e5cb06fefb523157f8"
version "1.0.30000793"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000793.tgz#3c00c66e423a7a1907c7dd96769a78b2afa8a72e"
caniuse-lite@^1.0.30000791, caniuse-lite@^1.0.30000792:
version "1.0.30000792"
@ -2440,6 +2404,16 @@ concurrently@^3.5.1:
supports-color "^3.2.3"
tree-kill "^1.1.0"
conf@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/conf/-/conf-1.4.0.tgz#1ea66c9d7a9b601674a5bb9d2b8dc3c726625e67"
dependencies:
dot-prop "^4.1.0"
env-paths "^1.0.0"
make-dir "^1.0.0"
pkg-up "^2.0.0"
write-file-atomic "^2.3.0"
configstore@^3.0.0, configstore@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90"
@ -2924,12 +2898,12 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
dmg-builder@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-3.1.0.tgz#11b8ec781b64813116b7ddc9175d673d59e1ad02"
dmg-builder@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-3.1.1.tgz#6e363919235d509df582c143ad5aa90b1ec0a994"
dependencies:
bluebird-lst "^1.0.5"
builder-util "^4.1.0"
builder-util "^4.1.7"
fs-extra-p "^4.5.0"
iconv-lite "^0.4.19"
js-yaml "^3.10.0"
@ -3064,22 +3038,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.53.7:
version "19.53.7"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-19.53.7.tgz#6c994f4ef6c0d8042b88449ad5c6481104a9d0c6"
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"
dependencies:
"7zip-bin" "^2.3.4"
asar-integrity "0.2.4"
async-exit-hook "^2.0.1"
bluebird-lst "^1.0.5"
builder-util "4.1.6"
builder-util "4.1.7"
builder-util-runtime "4.0.2"
chromium-pickle-js "^0.2.0"
debug "^3.1.0"
dmg-builder "3.1.0"
dmg-builder "3.1.1"
ejs "^2.5.7"
electron-osx-sign "0.4.7"
electron-publish "19.53.7"
electron-osx-sign "0.4.8"
electron-publish "19.54.0"
fs-extra-p "^4.5.0"
hosted-git-info "^2.5.0"
is-ci "^1.1.0"
@ -3089,25 +3063,25 @@ electron-builder-lib@19.53.7:
minimatch "^3.0.4"
normalize-package-data "^2.4.0"
plist "^2.1.0"
read-config-file "2.0.1"
read-config-file "2.1.1"
sanitize-filename "^1.6.1"
semver "^5.4.1"
semver "^5.5.0"
temp-file "^3.1.1"
electron-builder@^19.53.7:
version "19.53.7"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-19.53.7.tgz#dae5d8d68b6016446a59b279acc1769a3bc555bc"
electron-builder@^19.54.0:
version "19.54.0"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-19.54.0.tgz#b70b6876f8b9e09a53824bcad43172126c5e2543"
dependencies:
bluebird-lst "^1.0.5"
builder-util "4.1.6"
builder-util "4.1.7"
builder-util-runtime "4.0.2"
chalk "^2.3.0"
electron-builder-lib "19.53.7"
electron-builder-lib "19.54.0"
electron-download-tf "4.3.4"
fs-extra-p "^4.5.0"
is-ci "^1.1.0"
lazy-val "^1.0.3"
read-config-file "2.0.1"
read-config-file "2.1.1"
sanitize-filename "^1.6.1"
update-notifier "^2.3.0"
yargs "^10.1.1"
@ -3153,9 +3127,9 @@ electron-is-dev@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
electron-osx-sign@0.4.7:
version "0.4.7"
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.7.tgz#1d75647a82748eacd48bea70616ec83ffade3ee5"
electron-osx-sign@0.4.8:
version "0.4.8"
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.8.tgz#f0b9fadded9e1e54ec35fa89877b5c6c34c7bc40"
dependencies:
bluebird "^3.5.0"
compare-version "^0.1.2"
@ -3164,12 +3138,12 @@ electron-osx-sign@0.4.7:
minimist "^1.2.0"
plist "^2.1.0"
electron-publish@19.53.7:
version "19.53.7"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-19.53.7.tgz#b52fb70d91fc65825a444c7dd9678761a4720695"
electron-publish@19.54.0:
version "19.54.0"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-19.54.0.tgz#f7521550ce869f54b1c0e88d98620d4be56567e4"
dependencies:
bluebird-lst "^1.0.5"
builder-util "^4.1.6"
builder-util "^4.1.7"
builder-util-runtime "^4.0.2"
chalk "^2.3.0"
fs-extra-p "^4.5.0"
@ -3179,6 +3153,12 @@ electron-releases@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/electron-releases/-/electron-releases-2.1.0.tgz#c5614bf811f176ce3c836e368a0625782341fd4e"
electron-store@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-1.3.0.tgz#ee488a28a61fb982fd35b658fb9cb6331eb201f8"
dependencies:
conf "^1.3.0"
electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30:
version "1.3.30"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.30.tgz#9666f532a64586651fc56a72513692e820d06a80"
@ -3343,17 +3323,17 @@ es-to-primitive@^1.1.1:
is-symbol "^1.0.1"
es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
version "0.10.37"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.37.tgz#0ee741d148b80069ba27d020393756af257defc3"
version "0.10.38"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.38.tgz#fa7d40d65bbc9bb8a67e1d3f9cc656a00530eed3"
dependencies:
es6-iterator "~2.0.1"
es6-iterator "~2.0.3"
es6-symbol "~3.1.1"
es5-shim@^4.5.9:
version "4.5.10"
resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.10.tgz#b7e17ef4df2a145b821f1497b50c25cf94026205"
es6-iterator@^2.0.1, es6-iterator@~2.0.1:
es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
dependencies:
@ -4212,8 +4192,8 @@ glamor@^2.20.40:
through "^2.3.8"
glamorous@^4.11.2:
version "4.11.2"
resolved "https://registry.yarnpkg.com/glamorous/-/glamorous-4.11.2.tgz#ce144c6a53e247ddf0896ad6faddebf78c49d864"
version "4.11.3"
resolved "https://registry.yarnpkg.com/glamorous/-/glamorous-4.11.3.tgz#873ddc4c85842c33ce2a24bffe1d1f64bdbf94fa"
dependencies:
brcast "^3.0.0"
fast-memoize "^2.2.7"
@ -5165,8 +5145,8 @@ jest-validate@^21.1.0:
pretty-format "^21.2.1"
js-base64@^2.1.9:
version "2.4.0"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.0.tgz#9e566fee624751a1d720c966cd6226d29d4025aa"
version "2.4.1"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.1.tgz#e02813181cd53002888e918935467acb2910e596"
js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2"
@ -7072,8 +7052,8 @@ raw-body@2.3.2:
unpipe "1.0.0"
rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1:
version "1.2.3"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.3.tgz#51575a900f8dd68381c710b4712c2154c3e2035b"
version "1.2.4"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.4.tgz#a0f606caae2a3b862bbd0ef85482c0125b315fa3"
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
@ -7130,13 +7110,13 @@ react-fuzzy@^0.5.1:
prop-types "^15.5.9"
react-hot-loader@^4.0.0-beta.12:
version "4.0.0-beta.14"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.14.tgz#9575065aadda9c53ef455e757b8b36c1fd14e5d6"
version "4.0.0-beta.15"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.0.0-beta.15.tgz#d32d23bad2f2f1f7d084e5a28bebca127066c5bc"
dependencies:
fast-levenshtein "^2.0.6"
global "^4.3.0"
hoist-non-react-statics "^2.3.1"
react-stand-in "^4.0.0-beta.14"
react-stand-in "^4.0.0-beta.15"
redbox-react "^1.3.6"
source-map "^0.6.1"
@ -7172,8 +7152,8 @@ react-inspector@^2.2.2:
is-dom "^1.0.9"
react-modal@^3.1.10:
version "3.1.10"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.1.10.tgz#8898b5cc4ebba78adbb8dea4c55a69818aa682cc"
version "3.1.11"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.1.11.tgz#95c8223fcee7013258ad2d149c38c9f870c89958"
dependencies:
exenv "^1.2.0"
prop-types "^15.5.10"
@ -7258,9 +7238,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.14:
version "4.0.0-beta.14"
resolved "https://registry.yarnpkg.com/react-stand-in/-/react-stand-in-4.0.0-beta.14.tgz#0a06a94b44bc4ca1d06575414acf400585d84e35"
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"
dependencies:
shallowequal "^1.0.2"
@ -7312,12 +7292,12 @@ reactcss@^1.2.0:
dependencies:
lodash "^4.0.1"
read-config-file@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-2.0.1.tgz#4f6f536508ed8863c50c3a2cfd1dbd82ba961b82"
read-config-file@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-2.1.1.tgz#bd6c2b93e97a82a35f71a3c9eb43161e16692054"
dependencies:
ajv "^5.5.2"
ajv-keywords "^2.1.1"
ajv "^5.5.0"
ajv-keywords "^2.1.0"
bluebird-lst "^1.0.5"
dotenv "^4.0.0"
dotenv-expand "^4.0.1"
@ -7814,9 +7794,9 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
send@0.16.1:
version "0.16.1"
@ -8077,9 +8057,9 @@ source-map-support@^0.4.15:
dependencies:
source-map "^0.5.6"
source-map-support@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.0.tgz#2018a7ad2bdf8faf2691e5fddab26bed5a2bacab"
source-map-support@^0.5.0, source-map-support@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.1.tgz#72291517d1fd0cb9542cee6c27520884b5da1a07"
dependencies:
source-map "^0.6.0"
@ -8091,7 +8071,7 @@ 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:
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:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@ -8887,8 +8867,8 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
version "3.2.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
validate-npm-package-license@^3.0.1:
version "3.0.1"
@ -9021,14 +9001,7 @@ webpack-merge@^4.1.0:
dependencies:
lodash "^4.17.4"
webpack-sources@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf"
dependencies:
source-list-map "^2.0.0"
source-map "~0.5.3"
webpack-sources@^1.1.0:
webpack-sources@^1.0.1, webpack-sources@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"
dependencies:
@ -9141,7 +9114,7 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
write-file-atomic@^2.0.0:
write-file-atomic@^2.0.0, write-file-atomic@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
dependencies:

Loading…
Cancel
Save