Browse Source

Add IsUnlocked, and simple application reducer

master
Loëck Vézien 7 years ago
parent
commit
a8edce5801
No known key found for this signature in database GPG Key ID: CBCDCE384E853AC4
  1. 1
      .flowconfig
  2. 2
      src/actions/accounts.js
  3. 2
      src/actions/settings.js
  4. 100
      src/components/IsUnlocked/index.js
  5. 13
      src/components/SettingsPage.js
  6. 42
      src/components/Wrapper.js
  7. 39
      src/helpers/db.js
  8. 14
      src/middlewares/db.js
  9. 32
      src/reducers/application.js
  10. 4
      src/reducers/index.js
  11. 8
      src/renderer/index.js

1
.flowconfig

@ -1,4 +1,5 @@
[ignore]
<PROJECT_ROOT>/node_modules/bcryptjs/src/bower.json
[include]

2
src/actions/accounts.js

@ -13,5 +13,5 @@ export const addAccount: AddAccount = payload => ({
type FetchAccounts = () => { type: string }
export const fetchAccounts: FetchAccounts = () => ({
type: 'FETCH_ACCOUNTS',
payload: db.accounts(),
payload: db('accounts'),
})

2
src/actions/settings.js

@ -13,5 +13,5 @@ export const saveSettings: SaveSettings = payload => ({
type FetchSettings = () => { type: string }
export const fetchSettings: FetchSettings = () => ({
type: 'FETCH_SETTINGS',
payload: db.settings(),
payload: db('settings'),
})

100
src/components/IsUnlocked/index.js

@ -0,0 +1,100 @@
// @flow
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import bcrypt from 'bcryptjs'
import type { MapStateToProps } from 'react-redux'
import type { Settings } from 'types/common'
import get from 'lodash/get'
import { setEncryptionKey } from 'helpers/db'
import { fetchAccounts } from 'actions/accounts'
import { isLocked, unlock } from 'reducers/application'
import Box from 'components/base/Box'
import Input from 'components/base/Input'
type InputValue = {
password: string,
}
type Props = {
fetchAccounts: Function,
isLocked: boolean,
render: Function,
settings: Settings,
unlock: Function,
}
type State = {
inputValue: InputValue,
}
const mapStateToProps: MapStateToProps<*, *, *> = state => ({
settings: state.settings,
isLocked: isLocked(state),
})
const mapDispatchToProps = {
fetchAccounts,
unlock,
}
class IsUnlocked extends PureComponent<Props, State> {
state = {
inputValue: {
password: '',
},
}
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) =>
this.setState(prev => ({
inputValue: {
...prev.inputValue,
[key]: value,
},
}))
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
e.preventDefault()
const { settings, unlock, fetchAccounts } = this.props
const { inputValue } = this.state
if (bcrypt.compareSync(inputValue.password, get(settings, 'password.value'))) {
setEncryptionKey('accounts', inputValue.password)
fetchAccounts()
unlock()
}
}
render() {
const { inputValue } = this.state
const { isLocked, render } = this.props
if (isLocked) {
return (
<Box sticky align="center" justify="center">
<form onSubmit={this.handleSubmit}>
<Box>
<Input
placeholder="Password"
type="password"
onChange={this.handleChangeInput('password')}
value={inputValue.password}
/>
</Box>
</form>
</Box>
)
}
return render()
}
}
export default connect(mapStateToProps, mapDispatchToProps, null, {
pure: false,
})(IsUnlocked)

13
src/components/SettingsPage.js

@ -8,6 +8,8 @@ import bcrypt from 'bcryptjs'
import get from 'lodash/get'
import set from 'lodash/set'
import { setEncryptionKey } from 'helpers/db'
import type { MapStateToProps } from 'react-redux'
import type { Settings } from 'types/common'
@ -72,11 +74,12 @@ class SettingsPage extends PureComponent<Props, State> {
},
}
if (
get(inputValue, 'password.state') === true &&
get(inputValue, 'password.value', '').trim() !== ''
) {
settings.password.value = bcrypt.hashSync(get(inputValue, 'password.value'), 8)
const password = get(inputValue, 'password', {})
if (password.state === true && password.value.trim() !== '') {
settings.password.value = bcrypt.hashSync(password.value, 8)
setEncryptionKey('accounts', password.value)
}
saveSettings(settings)

42
src/components/Wrapper.js

@ -5,8 +5,8 @@ import { ipcRenderer } from 'electron'
import { Route } from 'react-router'
import { translate } from 'react-i18next'
import Box from 'components/base/Box'
import * as modals from 'components/modals'
import Box from 'components/base/Box'
import AccountPage from 'components/AccountPage'
import DashboardPage from 'components/DashboardPage'
@ -14,6 +14,7 @@ import SettingsPage from 'components/SettingsPage'
import UpdateNotifier from 'components/UpdateNotifier'
import AppRegionDrag from 'components/AppRegionDrag'
import IsUnlocked from 'components/IsUnlocked'
import SideBar from 'components/SideBar'
import TopBar from 'components/TopBar'
@ -26,22 +27,29 @@ class Wrapper extends PureComponent<{}> {
return (
<Fragment>
<AppRegionDrag />
<UpdateNotifier />
{Object.entries(modals).map(([name, ModalComponent]: [string, any]) => (
<ModalComponent key={name} />
))}
<Box grow horizontal>
<SideBar />
<Box shrink grow bg="cream">
<TopBar />
<Route path="/" exact component={DashboardPage} />
<Route path="/settings" component={SettingsPage} />
<Route path="/account/:id" component={AccountPage} />
</Box>
</Box>
<IsUnlocked
render={() => (
<Fragment>
<UpdateNotifier />
{Object.entries(modals).map(([name, ModalComponent]: [string, any]) => (
<ModalComponent key={name} />
))}
<Box grow horizontal>
<SideBar />
<Box shrink grow bg="cream">
<TopBar />
<Route path="/" exact component={DashboardPage} />
<Route path="/settings" component={SettingsPage} />
<Route path="/account/:id" component={AccountPage} />
</Box>
</Box>
</Fragment>
)}
/>
</Fragment>
)
}

39
src/helpers/db.js

@ -1,29 +1,24 @@
import Store from 'electron-store'
export default {
accounts: (accounts, options = {}) => {
const db = new Store({
name: 'accounts',
defaults: {},
...options,
})
const encryptionKey = {}
if (accounts) {
db.store = accounts
}
const store = type =>
new Store({
name: type,
defaults: {},
encryptionKey: encryptionKey[type],
})
return db.store
},
settings: settings => {
const db = new Store({
name: 'settings',
defaults: {},
})
export function setEncryptionKey(type, value) {
encryptionKey[type] = value
}
export default (type, values) => {
const db = store(type)
if (settings) {
db.store = settings
}
if (values) {
db.store = values
}
return db.store
},
return db.store
}

14
src/middlewares/db.js

@ -1,10 +1,9 @@
import get from 'lodash/get'
/* eslint-disable consistent-return */
import db from 'helpers/db'
import { getAccounts } from 'reducers/accounts'
// eslint-disable-next-line consistent-return
export default store => next => action => {
if (!action.type.startsWith('DB:')) {
return next(action)
@ -18,13 +17,6 @@ export default store => next => action => {
const state = getState()
const { settings } = state
db.settings(settings)
const optionsAccounts = {}
if (get(settings, 'password.state') === true) {
optionsAccounts.encryptionKey = get(settings, 'password.value')
}
db.accounts(getAccounts(state), optionsAccounts)
db('settings', settings)
db('accounts', getAccounts(state))
}

32
src/reducers/application.js

@ -0,0 +1,32 @@
// @flow
import { handleActions, createAction } from 'redux-actions'
import get from 'lodash/get'
export type ApplicationState = {}
const state: ApplicationState = {}
const handlers = {
APPLICATION_SET_DATA: (state, { payload }: { payload: ApplicationState }) => ({
...state,
...payload,
}),
}
// Actions
export const unlock = createAction('APPLICATION_SET_DATA', () => ({ isLocked: false }))
export const lock = createAction('APPLICATION_SET_DATA', () => ({ isLocked: true }))
// Selectors
export const isLocked = (state: Object) =>
state.application.isLocked === undefined
? get(state.settings, 'password.state', false)
: state.application.isLocked
// Exporting reducer
export default handleActions(handlers, state)

4
src/reducers/index.js

@ -5,12 +5,14 @@ import { routerReducer as router } from 'react-router-redux'
import type { LocationShape } from 'react-router'
import application from './application'
import accounts from './accounts'
import devices from './devices'
import modals from './modals'
import settings from './settings'
import update from './update'
import type { ApplicationState } from './application'
import type { AccountsState } from './accounts'
import type { DevicesState } from './devices'
import type { ModalsState } from './modals'
@ -18,6 +20,7 @@ import type { SettingsState } from './settings'
import type { UpdateState } from './update'
export type State = {
application: ApplicationState,
accounts: AccountsState,
devices: DevicesState,
modals: ModalsState,
@ -27,6 +30,7 @@ export type State = {
}
export default combineReducers({
application,
accounts,
devices,
modals,

8
src/renderer/index.js

@ -10,6 +10,7 @@ import events from 'renderer/events'
import { fetchAccounts } from 'actions/accounts'
import { fetchSettings } from 'actions/settings'
import { isLocked } from 'reducers/application'
import App from 'components/App'
@ -22,7 +23,12 @@ const rootNode = document.getElementById('app')
events(store)
store.dispatch(fetchSettings())
store.dispatch(fetchAccounts())
const state = store.getState() || {}
if (!isLocked(state)) {
store.dispatch(fetchAccounts())
}
function r(Comp) {
if (rootNode) {

Loading…
Cancel
Save