diff --git a/package.json b/package.json index 6f711fb6..a65b74fe 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "bs58check": "^2.1.1", "color": "^3.0.0", "cross-env": "^5.1.4", - "d3": "^5.0.0", + "d3": "^5.0.1", "debug": "^3.1.0", "downshift": "^1.31.7", "electron-store": "^1.3.0", @@ -74,8 +74,8 @@ "qs": "^6.5.1", "raven": "^2.5.0", "raven-js": "^3.24.1", - "react": "^16.3.1", - "react-dom": "^16.3.1", + "react": "^16.3.2", + "react-dom": "^16.3.2", "react-flip-ticker": "^0.3.0", "react-i18next": "^7.6.0", "react-mortal": "^3.2.0", @@ -92,7 +92,7 @@ "smooth-scrollbar": "^8.2.7", "source-map": "0.7.2", "source-map-support": "^0.5.4", - "styled-components": "^3.2.5", + "styled-components": "^3.2.6", "styled-system": "^2.2.1", "tippy.js": "^2.5.2", "ws": "^5.1.1", @@ -117,7 +117,7 @@ "babel-loader": "^8.0.0-beta.2", "babel-plugin-module-resolver": "^3.1.1", "babel-plugin-styled-components": "^1.5.0", - "chalk": "^2.3.1", + "chalk": "^2.4.0", "chance": "^1.0.13", "concurrently": "^3.5.1", "dotenv": "^5.0.1", @@ -142,9 +142,9 @@ "js-yaml": "^3.10.0", "lint-staged": "^7.0.4", "node-loader": "^0.6.0", - "prettier": "^1.12.0", + "prettier": "^1.12.1", "react-hot-loader": "^4.0.1", - "react-test-renderer": "^16.3.1", + "react-test-renderer": "^16.3.2", "webpack": "^4.5.0", "webpack-bundle-analyzer": "^2.11.1", "webpack-cli": "^2.0.14", diff --git a/src/components/IsUnlocked.js b/src/components/IsUnlocked.js index c70b5cea..113f55e7 100644 --- a/src/components/IsUnlocked.js +++ b/src/components/IsUnlocked.js @@ -25,7 +25,7 @@ import { isLocked, unlock } from 'reducers/application' import { getCounterValueCode } from 'reducers/settings' import Box from 'components/base/Box' -import Input from 'components/base/Input' +import InputPassword from 'components/base/InputPassword' type InputValue = { password: string, @@ -141,7 +141,7 @@ class IsUnlocked extends Component {
- (this._input = n)} placeholder={t('common:password')} diff --git a/src/components/SettingsPage/PasswordModal.js b/src/components/SettingsPage/PasswordModal.js index 1e653e81..a5921584 100644 --- a/src/components/SettingsPage/PasswordModal.js +++ b/src/components/SettingsPage/PasswordModal.js @@ -8,7 +8,7 @@ import { unlock } from 'reducers/application' import Box from 'components/base/Box' import Button from 'components/base/Button' -import Input from 'components/base/Input' +import InputPassword from 'components/base/InputPassword' import Label from 'components/base/Label' import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal' @@ -30,15 +30,15 @@ type Props = { type State = { currentPassword: string, newPassword: string, - repeatPassword: string, +} + +const INITIAL_STATE = { + currentPassword: '', + newPassword: '', } class PasswordModal extends PureComponent { - state = { - currentPassword: '', - newPassword: '', - repeatPassword: '', - } + state = INITIAL_STATE handleSave = (e: SyntheticEvent) => { if (e) { @@ -47,32 +47,35 @@ class PasswordModal extends PureComponent { if (!this.isValid()) { return } - const { currentPassword, newPassword, repeatPassword } = this.state + const { currentPassword, newPassword } = this.state const { isPasswordEnabled, currentPasswordHash, onChangePassword } = this.props if (isPasswordEnabled) { - const calculatedPasswordHash = bcrypt.hashSync(currentPassword, 8) - if (calculatedPasswordHash !== currentPasswordHash) { + if (!bcrypt.compareSync(currentPassword, currentPasswordHash)) { return } onChangePassword(newPassword) - } else if (newPassword === repeatPassword) { + } else { onChangePassword(newPassword) } } handleInputChange = key => value => this.setState({ [key]: value }) + handleReset = () => this.setState(INITIAL_STATE) + isValid = () => { - const { newPassword, repeatPassword } = this.state - return newPassword && newPassword === repeatPassword + const { newPassword } = this.state + return newPassword } render() { const { t, isPasswordEnabled, onClose, ...props } = this.props + const { currentPassword, newPassword } = this.state const isValid = this.isValid() return ( ( @@ -90,35 +93,28 @@ class PasswordModal extends PureComponent { - )} - - + {t('settings:profile.passwordModalNewPasswordInput')} + + )} + - - - - diff --git a/src/components/SettingsPage/index.js b/src/components/SettingsPage/index.js index a4f45b67..3c8bd4b6 100644 --- a/src/components/SettingsPage/index.js +++ b/src/components/SettingsPage/index.js @@ -43,7 +43,7 @@ type State = { class SettingsPage extends PureComponent { state = { - tab: 2, + tab: 0, } _items = [] diff --git a/src/components/SettingsPage/sections/Profile.js b/src/components/SettingsPage/sections/Profile.js index c636cbbe..6352b023 100644 --- a/src/components/SettingsPage/sections/Profile.js +++ b/src/components/SettingsPage/sections/Profile.js @@ -7,6 +7,8 @@ import bcrypt from 'bcryptjs' import type { Settings, T } from 'types/common' +import debounce from 'lodash/debounce' + import { unlock } from 'reducers/application' import db, { setEncryptionKey } from 'helpers/db' @@ -32,11 +34,13 @@ const mapDispatchToProps = { type Props = { t: T, settings: Settings, + unlock: Function, saveSettings: Function, } type State = { isHardResetModalOpened: boolean, + isPasswordModalOpened: boolean, username: string, } @@ -44,14 +48,29 @@ class TabProfile extends PureComponent { state = { username: this.props.settings.username, isHardResetModalOpened: false, + isPasswordModalOpened: false, + } + + setPassword = hash => { + const { saveSettings, unlock } = this.props + setEncryptionKey('accounts', hash) + saveSettings({ + password: { + isEnabled: hash !== undefined, + value: hash, + }, + }) + unlock() } + debounceSaveUsername = debounce( + v => this.props.saveSettings({ username: v.trim() || 'Anonymous' }), + 250, + ) + handleChangeUsername = username => { - const { saveSettings } = this.props this.setState({ username }) - window.requestIdleCallback(() => { - saveSettings({ username: username.trim() || 'Anonymous' }) - }) + this.debounceSaveUsername(username) } handleOpenHardResetModal = () => this.setState({ isHardResetModalOpened: true }) @@ -69,23 +88,16 @@ class TabProfile extends PureComponent { if (isChecked) { this.handleOpenPasswordModal() } else { - // console.log(`decrypting data`) + this.setPassword(undefined) } } handleChangePassword = (password: ?string) => { - const { saveSettings, unlock } = this.props - const hash = bcrypt.hashSync(password, 8) - setEncryptionKey('accounts', password) - window.requestIdleCallback(() => { - saveSettings({ - password: { - isEnabled: hash !== undefined, - value: hash, - }, - }) - unlock() - }) + if (password) { + const hash = bcrypt.hashSync(password, 8) + this.setPassword(hash) + this.handleClosePasswordModal() + } } render() { @@ -110,7 +122,11 @@ class TabProfile extends PureComponent { - {isPasswordEnabled && } + {isPasswordEnabled && ( + + )} @@ -140,7 +156,7 @@ class TabProfile extends PureComponent { (p.isChecked ? 'wallet' : 'fog'), + bg: p => (p.isChecked ? 'wallet' : 'lightFog'), horizontal: true, align: 'center', })` backround: red; width: 50px; - height: 24px; - border-radius: 16px; + height: 26px; + border-radius: 13px; transition: 250ms linear background-color; cursor: pointer; &:focus { @@ -28,9 +28,9 @@ const Ball = styled.div` height: 20px; border-radius: 50%; background: white; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.15); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2); transition: 250ms ease-in-out transform; - transform: translate3d(${p => (p.isChecked ? '28px' : '2px')}, 0, 0); + transform: translate3d(${p => (p.isChecked ? '27px' : '3px')}, 0, 0); ` type Props = { diff --git a/src/components/base/InputPassword/index.js b/src/components/base/InputPassword/index.js index 9072f69c..692afd43 100644 --- a/src/components/base/InputPassword/index.js +++ b/src/components/base/InputPassword/index.js @@ -1,6 +1,6 @@ // @flow -import React, { PureComponent } from 'react' +import React, { Fragment, PureComponent } from 'react' import styled from 'styled-components' import { translate } from 'react-i18next' import zxcvbn from 'zxcvbn' @@ -50,6 +50,7 @@ type Props = { onChange: Function, t: T, value: string, + withStrength: boolean, } class InputPassword extends PureComponent { @@ -84,7 +85,7 @@ class InputPassword extends PureComponent { } render() { - const { t, value, maxLength } = this.props + const { t, value, maxLength, withStrength } = this.props const { passwordStrength, inputType } = this.state const hasValue = value.trim() !== '' @@ -102,19 +103,23 @@ class InputPassword extends PureComponent { } /> - - {[0, 1, 2, 3, 4].map(v => ( - = v} - /> - ))} - - {hasValue && ( - - {t(`password:warning_${passwordStrength}`)} - + {withStrength && ( + + + {[0, 1, 2, 3, 4].map(v => ( + = v} + /> + ))} + + {hasValue && ( + + {t(`password:warning_${passwordStrength}`)} + + )} + )} ) diff --git a/src/reducers/accounts.js b/src/reducers/accounts.js index 2a6bcb38..49c9633a 100644 --- a/src/reducers/accounts.js +++ b/src/reducers/accounts.js @@ -94,10 +94,10 @@ export function canCreateAccount(state: State): boolean { // such a simple thing, let's put any, right? I don't care. export function serializeAccounts(accounts: any): Account[] { // ensure that accounts are always wrapped in data key - if (accounts.length && !accounts[0].data) { + if (accounts && accounts.length && !accounts[0].data) { accounts = accounts.map(account => ({ data: account })) } - return accounts.map(accountModel.decode) + return accounts ? accounts.map(accountModel.decode) : [] } export function deserializeAccounts(accounts: Account[]) { diff --git a/yarn.lock b/yarn.lock index eb9f1709..ffd73253 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3362,6 +3362,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3 escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" @@ -4321,9 +4329,9 @@ d3-zoom@1: d3-selection "1" d3-transition "1" -d3@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/d3/-/d3-5.0.0.tgz#b373ce8ccc953cbe09cef4c1e7e2223b42441df9" +d3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/d3/-/d3-5.0.1.tgz#c74743964deff2eedf2a10e6119fbeb77825af7a" dependencies: d3-array "1" d3-axis "1" @@ -8013,6 +8021,9 @@ ledger-test-library@KhalilBellakrid/ledger-test-library-nodejs#7d37482: dependencies: axios "^0.17.1" bindings "^1.3.0" + electron "^1.8.2" + electron-builder "^20.0.4" + electron-rebuild "^1.7.3" nan "^2.6.2" prebuild-install "^2.2.2" @@ -9792,9 +9803,9 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.0.tgz#d26fc5894b9230de97629b39cae225b503724ce8" +prettier@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" prettier@^1.5.3: version "1.11.1" @@ -10132,9 +10143,9 @@ react-docgen@^3.0.0-beta11: node-dir "^0.1.10" recast "^0.12.6" -react-dom@^16.3.1: - version "16.3.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.1.tgz#6a3c90a4fb62f915bdbcf6204422d93a7d4ca573" +react-dom@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -10205,6 +10216,10 @@ react-is@^16.3.1: version "16.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.1.tgz#ee66e6d8283224a83b3030e110056798488359ba" +react-is@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" + react-modal@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.3.2.tgz#b13da9490653a7c76bc0e9600323eb1079c620e7" @@ -10308,14 +10323,14 @@ react-style-proptype@^3.0.0: dependencies: prop-types "^15.5.4" -react-test-renderer@^16.3.1: - version "16.3.1" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.1.tgz#d9257936d8535bd40f57f3d5a84e7b0452fb17f2" +react-test-renderer@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.2.tgz#3d1ed74fda8db42521fdf03328e933312214749a" dependencies: fbjs "^0.8.16" object-assign "^4.1.1" prop-types "^15.6.0" - react-is "^16.3.1" + react-is "^16.3.2" react-textarea-autosize@^5.2.1: version "5.2.1" @@ -10353,9 +10368,9 @@ react@^16.0.0, react@^16.2.0: object-assign "^4.1.1" prop-types "^15.6.0" -react@^16.3.1: - version "16.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.3.1.tgz#4a2da433d471251c69b6033ada30e2ed1202cfd8" +react@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -11704,9 +11719,9 @@ style-loader@^0.20.3: loader-utils "^1.1.0" schema-utils "^0.4.5" -styled-components@^3.2.5: - version "3.2.5" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.2.5.tgz#b5d5d7d618ab240ff10602b5ca5886b8db3d0a0d" +styled-components@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.2.6.tgz#99e6e75a746bdedd295a17e03dd1493055a1cc3b" dependencies: buffer "^5.0.3" css-to-react-native "^2.0.3" @@ -11714,6 +11729,7 @@ styled-components@^3.2.5: hoist-non-react-statics "^2.5.0" is-plain-object "^2.0.1" prop-types "^15.5.4" + react-is "^16.3.1" stylis "^3.5.0" stylis-rule-sheet "^0.0.10" supports-color "^3.2.3"