Browse Source

Merge pull request #503 from NastiaS/passwordBranch

Password branch
master
Meriadec Pillet 7 years ago
committed by GitHub
parent
commit
da80beae76
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      src/components/IsUnlocked.js
  2. 46
      src/components/Onboarding/index.js
  3. 34
      src/components/Onboarding/steps/Analytics.js
  4. 247
      src/components/Onboarding/steps/SetPassword.js
  5. 21
      src/components/SettingsPage/DisablePasswordModal.js
  6. 89
      src/components/SettingsPage/PasswordForm.js
  7. 136
      src/components/SettingsPage/PasswordModal.js
  8. 7
      src/components/base/Input/index.js
  9. 6
      src/components/modals/AccountSettingRenderBody.js
  10. 19
      src/reducers/onboarding.js
  11. 1
      static/i18n/en/common.yml
  12. 8
      static/i18n/en/onboarding.yml
  13. 22
      static/i18n/en/password.yml
  14. 13
      static/i18n/en/settings.yml

19
src/components/IsUnlocked.js

@ -11,8 +11,6 @@ import type { SettingsState as Settings } from 'reducers/settings'
import type { T } from 'types/common' import type { T } from 'types/common'
import IconLockScreen from 'icons/LockScreen' import IconLockScreen from 'icons/LockScreen'
import { ErrorMessageInput } from 'components/base/Input'
import get from 'lodash/get' import get from 'lodash/get'
import { setEncryptionKey } from 'helpers/db' import { setEncryptionKey } from 'helpers/db'
@ -119,21 +117,13 @@ class IsUnlocked extends Component<Props, State> {
} }
} }
handleFocusInput = () => {
if (this._input && this._input !== null) {
this._input.focus()
}
}
_input: ?HTMLInputElement
render() { render() {
const { inputValue, incorrectPassword } = this.state const { inputValue, incorrectPassword } = this.state
const { isLocked, t } = this.props const { isLocked, t } = this.props
if (isLocked) { if (isLocked) {
return ( return (
<Box sticky alignItems="center" justifyContent="center" onClick={this.handleFocusInput}> <Box sticky alignItems="center" justifyContent="center">
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<Box align="center"> <Box align="center">
<IconLockScreen size={136} /> <IconLockScreen size={136} />
@ -146,17 +136,12 @@ class IsUnlocked extends Component<Props, State> {
<Box style={{ minWidth: 230 }}> <Box style={{ minWidth: 230 }}>
<InputPassword <InputPassword
autoFocus autoFocus
innerRef={(n: any) => (this._input = n)}
placeholder={t('common:lockScreen.inputPlaceholder')} placeholder={t('common:lockScreen.inputPlaceholder')}
type="password" type="password"
onChange={this.handleChangeInput('password')} onChange={this.handleChangeInput('password')}
value={inputValue.password} value={inputValue.password}
error={incorrectPassword && t('password:errorMessageIncorrectPassword')}
/> />
{incorrectPassword && (
<ErrorMessageInput>
{t('password:errorMessageIncorrectPassword')}
</ErrorMessageInput>
)}
</Box> </Box>
</Box> </Box>
</form> </form>

46
src/components/Onboarding/index.js

@ -8,6 +8,7 @@ import styled from 'styled-components'
import type { T } from 'types/common' import type { T } from 'types/common'
import type { OnboardingState } from 'reducers/onboarding' import type { OnboardingState } from 'reducers/onboarding'
import type { SettingsState } from 'reducers/settings'
import { saveSettings } from 'actions/settings' import { saveSettings } from 'actions/settings'
import { import {
@ -20,7 +21,7 @@ import {
} from 'reducers/onboarding' } from 'reducers/onboarding'
import { getCurrentDevice } from 'reducers/devices' import { getCurrentDevice } from 'reducers/devices'
// import { unlock } from 'reducers/application' import { unlock } from 'reducers/application'
import Box from 'components/base/Box' import Box from 'components/base/Box'
@ -31,8 +32,7 @@ import SelectDevice from './steps/SelectDevice'
import SelectPIN from './steps/SelectPIN/index' import SelectPIN from './steps/SelectPIN/index'
import WriteSeed from './steps/WriteSeed/index' import WriteSeed from './steps/WriteSeed/index'
import GenuineCheck from './steps/GenuineCheck' import GenuineCheck from './steps/GenuineCheck'
// UNTIL IS NEEDED SET PASSWORD IS COMMENTED OUT import SetPassword from './steps/SetPassword'
// import SetPassword from './steps/SetPassword'
import Analytics from './steps/Analytics' import Analytics from './steps/Analytics'
import Finish from './steps/Finish' import Finish from './steps/Finish'
@ -42,7 +42,7 @@ const STEPS = {
selectPIN: SelectPIN, selectPIN: SelectPIN,
writeSeed: WriteSeed, writeSeed: WriteSeed,
genuineCheck: GenuineCheck, genuineCheck: GenuineCheck,
// setPassword: SetPassword, setPassword: SetPassword,
analytics: Analytics, analytics: Analytics,
finish: Finish, finish: Finish,
start: Start, start: Start,
@ -51,6 +51,7 @@ const STEPS = {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
hasCompletedOnboarding: state.settings.hasCompletedOnboarding, hasCompletedOnboarding: state.settings.hasCompletedOnboarding,
onboarding: state.onboarding, onboarding: state.onboarding,
settings: state.settings,
getCurrentDevice: getCurrentDevice(state), getCurrentDevice: getCurrentDevice(state),
}) })
@ -59,7 +60,7 @@ const mapDispatchToProps = {
nextStep, nextStep,
prevStep, prevStep,
jumpStep, jumpStep,
// unlock, unlock,
} }
type Props = { type Props = {
@ -67,6 +68,7 @@ type Props = {
hasCompletedOnboarding: boolean, hasCompletedOnboarding: boolean,
saveSettings: Function, saveSettings: Function,
onboarding: OnboardingState, onboarding: OnboardingState,
settings: SettingsState,
prevStep: Function, prevStep: Function,
nextStep: Function, nextStep: Function,
jumpStep: Function, jumpStep: Function,
@ -77,12 +79,13 @@ type Props = {
export type StepProps = { export type StepProps = {
t: T, t: T,
onboarding: OnboardingState, onboarding: OnboardingState,
settings: SettingsState,
prevStep: Function, prevStep: Function,
nextStep: Function, nextStep: Function,
jumpStep: Function, jumpStep: Function,
finish: Function, finish: Function,
saveSettings: Function, saveSettings: Function,
// savePassword: Function, savePassword: Function,
getDeviceInfo: Function, getDeviceInfo: Function,
updateGenuineCheck: Function, updateGenuineCheck: Function,
isLedgerNano: Function, isLedgerNano: Function,
@ -92,18 +95,26 @@ export type StepProps = {
class Onboarding extends PureComponent<Props> { class Onboarding extends PureComponent<Props> {
getDeviceInfo = () => this.props.getCurrentDevice getDeviceInfo = () => this.props.getCurrentDevice
finish = () => this.props.saveSettings({ hasCompletedOnboarding: true }) finish = () => this.props.saveSettings({ hasCompletedOnboarding: true })
// savePassword = hash => { savePassword = hash => {
// this.props.saveSettings({ this.props.saveSettings({
// password: { password: {
// isEnabled: hash !== undefined, isEnabled: hash !== undefined,
// value: hash, value: hash,
// }, },
// }) })
// this.props.unlock() this.props.unlock()
// } }
render() { render() {
const { hasCompletedOnboarding, onboarding, prevStep, nextStep, jumpStep, t } = this.props const {
hasCompletedOnboarding,
onboarding,
prevStep,
nextStep,
jumpStep,
settings,
t,
} = this.props
if (hasCompletedOnboarding) { if (hasCompletedOnboarding) {
return null return null
} }
@ -119,6 +130,7 @@ class Onboarding extends PureComponent<Props> {
const stepProps: StepProps = { const stepProps: StepProps = {
t, t,
onboarding, onboarding,
settings,
updateGenuineCheck, updateGenuineCheck,
isLedgerNano, isLedgerNano,
flowType, flowType,
@ -126,7 +138,7 @@ class Onboarding extends PureComponent<Props> {
nextStep, nextStep,
jumpStep, jumpStep,
finish: this.finish, finish: this.finish,
// savePassword: this.savePassword, savePassword: this.savePassword,
getDeviceInfo: this.getDeviceInfo, getDeviceInfo: this.getDeviceInfo,
saveSettings, saveSettings,
} }

34
src/components/Onboarding/steps/Analytics.js

@ -2,8 +2,6 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { connect } from 'react-redux'
import { saveSettings } from 'actions/settings'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import CheckBox from 'components/base/CheckBox' import CheckBox from 'components/base/CheckBox'
@ -12,19 +10,21 @@ import OnboardingFooter from '../OnboardingFooter'
import type { StepProps } from '..' import type { StepProps } from '..'
const mapDispatchToProps = { saveSettings }
type State = { type State = {
analyticsToggle: boolean, analyticsToggle: boolean,
termsConditionsToggle: boolean, termsConditionsToggle: boolean,
sentryLogsToggle: boolean, sentryLogsToggle: boolean,
} }
const INITIAL_STATE = {
analyticsToggle: false,
termsConditionsToggle: false,
sentryLogsToggle: false,
}
class Analytics extends PureComponent<StepProps, State> { class Analytics extends PureComponent<StepProps, State> {
state = { state = INITIAL_STATE
analyticsToggle: false,
termsConditionsToggle: false,
sentryLogsToggle: false,
}
handleSentryLogsToggle = (isChecked: boolean) => { handleSentryLogsToggle = (isChecked: boolean) => {
this.setState({ sentryLogsToggle: !this.state.sentryLogsToggle }) this.setState({ sentryLogsToggle: !this.state.sentryLogsToggle })
this.props.saveSettings({ this.props.saveSettings({
@ -40,8 +40,15 @@ class Analytics extends PureComponent<StepProps, State> {
handleTermsToggle = () => { handleTermsToggle = () => {
this.setState({ termsConditionsToggle: !this.state.termsConditionsToggle }) this.setState({ termsConditionsToggle: !this.state.termsConditionsToggle })
} }
handleNavBack = () => {
const { savePassword, prevStep } = this.props
savePassword(undefined)
prevStep()
}
render() { render() {
const { nextStep, prevStep, t } = this.props const { nextStep, t } = this.props
const { analyticsToggle, termsConditionsToggle, sentryLogsToggle } = this.state const { analyticsToggle, termsConditionsToggle, sentryLogsToggle } = this.state
return ( return (
@ -85,7 +92,7 @@ class Analytics extends PureComponent<StepProps, State> {
flow={2} flow={2}
t={t} t={t}
nextStep={nextStep} nextStep={nextStep}
prevStep={prevStep} prevStep={this.handleNavBack}
isContinueDisabled={!termsConditionsToggle} isContinueDisabled={!termsConditionsToggle}
/> />
</Box> </Box>
@ -93,10 +100,7 @@ class Analytics extends PureComponent<StepProps, State> {
} }
} }
export default connect( export default Analytics
null,
mapDispatchToProps,
)(Analytics)
export const AnalyticsText = styled(Box).attrs({ export const AnalyticsText = styled(Box).attrs({
ff: 'Open Sans|Regular', ff: 'Open Sans|Regular',

247
src/components/Onboarding/steps/SetPassword.js

@ -1,93 +1,154 @@
// UNTIL IS NEEDED SET PASSWORD STEP IS COMMENTED OUT // @flow
// // @flow import React, { PureComponent, Fragment } from 'react'
// import bcrypt from 'bcryptjs'
// import React, { PureComponent } from 'react' import { colors, radii } from 'styles/theme'
// import bcrypt from 'bcryptjs' import styled from 'styled-components'
//
// import { setEncryptionKey } from 'helpers/db' import { setEncryptionKey } from 'helpers/db'
//
// import Box from 'components/base/Box' import Box from 'components/base/Box'
// import Button from 'components/base/Button' import Button from 'components/base/Button'
//
// import IconSetPassword from 'icons/onboarding/SetPassword' import IconChevronRight from 'icons/ChevronRight'
// import PasswordModal from 'components/SettingsPage/PasswordModal'
// import OnboardingFooter from '../OnboardingFooter' import PasswordForm from '../../SettingsPage/PasswordForm'
// import type { StepProps } from '..'
// import type { StepProps } from '..'
// import { Title, Description, DisclaimerBox } from '../helperComponents'
// import { Title, Description } from '../helperComponents'
// type State = {
// type State = { currentPassword: string,
// isPasswordModalOpened: boolean, newPassword: string,
// isPasswordEnabled: boolean, confirmPassword: string,
// } incorrectPassword: boolean,
// }
// class SetPassword extends PureComponent<StepProps, State> {
// state = { const INITIAL_STATE = {
// isPasswordModalOpened: false, currentPassword: '',
// isPasswordEnabled: false, newPassword: '',
// } confirmPassword: '',
// incorrectPassword: false,
// handleOpenPasswordModal = () => { }
// this.setState({ isPasswordModalOpened: true })
// } class SetPassword extends PureComponent<StepProps, State> {
// handleClosePasswordModal = () => { state = INITIAL_STATE
// this.setState({ isPasswordModalOpened: false })
// } handleSave = (e: SyntheticEvent<HTMLFormElement>) => {
// handleChangePassword = (password: string) => { if (e) {
// window.requestIdleCallback(() => { e.preventDefault()
// setEncryptionKey('accounts', password) }
// const hash = password ? bcrypt.hashSync(password, 8) : undefined if (!this.isValid()) {
// this.props.savePassword(hash) return
// }) }
// } const { newPassword } = this.state
// const { nextStep, savePassword } = this.props
// handleInputChange = (key: string) => (value: string) => {
// this.setState({ [key]: value }) setEncryptionKey('accounts', newPassword)
// } const hash = newPassword ? bcrypt.hashSync(newPassword, 8) : undefined
// savePassword(hash)
// render() { this.handleReset()
// const { nextStep, prevStep, t } = this.props nextStep()
// const { isPasswordModalOpened, isPasswordEnabled } = this.state }
// return (
// <Box sticky pt={150}> handleInputChange = (key: string) => (value: string) => {
// <Box grow alignItems="center"> if (this.state.incorrectPassword) {
// <Title>{t('onboarding:setPassword.title')}</Title> this.setState({ incorrectPassword: false })
// <Description>{t('onboarding:setPassword.desc')}</Description> }
// <IconSetPassword /> this.setState({ [key]: value })
// <Box style={{ paddingTop: 35 }}> }
// <Button small primary onClick={() => this.handleOpenPasswordModal()}>
// Set Password handleReset = () => this.setState(INITIAL_STATE)
// </Button>
// </Box> isValid = () => {
// {/* we might not be able to re-use what we have currently without modifications const { newPassword, confirmPassword } = this.state
// the title and descriptions are not dynamic, we might need deffirent size as well */} return newPassword === confirmPassword
// {isPasswordModalOpened && ( }
// <PasswordModal
// t={t} render() {
// isOpened={isPasswordModalOpened} const { nextStep, prevStep, t, settings } = this.props
// onClose={this.handleClosePasswordModal} const { newPassword, currentPassword, incorrectPassword, confirmPassword } = this.state
// onChangePassword={this.handleChangePassword}
// isPasswordEnabled={isPasswordEnabled} const isPasswordEnabled = settings.password.isEnabled === true
// currentPasswordHash=""
// /> const disclaimerNotes = [
// )} {
// <Box onClick={() => nextStep()} style={{ padding: 15 }}> key: 'note1',
// <Button>Skip this step</Button> icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
// </Box> desc: t('onboarding:setPassword.disclaimer.note1'),
// </Box> },
// <OnboardingFooter {
// horizontal key: 'note2',
// align="center" icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
// flow={2} desc: t('onboarding:setPassword.disclaimer.note2'),
// t={t} },
// nextStep={nextStep} {
// prevStep={prevStep} key: 'note3',
// /> icon: <IconChevronRight size={12} style={{ color: colors.smoke }} />,
// </Box> desc: t('onboarding:setPassword.disclaimer.note3'),
// ) },
// } ]
// }
// return (
// export default SetPassword <Box sticky pt={50}>
<Box grow alignItems="center" justify="center">
<Fragment>
<Box mb={3} alignItems="center">
<Title>{t('onboarding:setPassword.title')}</Title>
<Description style={{ maxWidth: 620 }}>
{t('onboarding:setPassword.desc')}
</Description>
</Box>
<Box align="center" mt={2}>
<PasswordForm
onSubmit={this.handleSave}
isPasswordEnabled={isPasswordEnabled}
newPassword={newPassword}
currentPassword={currentPassword}
confirmPassword={confirmPassword}
incorrectPassword={incorrectPassword}
isValid={this.isValid}
onChange={this.handleInputChange}
t={t}
/>
<DisclaimerBox mt={7} disclaimerNotes={disclaimerNotes} />
</Box>
</Fragment>
</Box>
<CustomFooter>
<Button padded outlineGrey onClick={() => prevStep()}>
{t('common:back')}
</Button>
<Box horizontal ml="auto">
<Button padded disabled={false} onClick={() => nextStep()} mx={2}>
{t('common:skipThisStep')}
</Button>
<Button
padded
onClick={this.handleSave}
disabled={!this.isValid() || !newPassword.length || !confirmPassword.length}
primary
>
{t('common:continue')}
</Button>
</Box>
</CustomFooter>
</Box>
)
}
}
export default SetPassword
const CustomFooter = styled(Box).attrs({
px: 5,
py: 3,
horizontal: true,
align: 'center',
})`
border-top: 1px solid ${p => p.theme.colors.lightFog};
border-bottom-left-radius: ${radii[1]}px;
border-bottom-right-radius: ${radii[1]}px;
`

21
src/components/SettingsPage/DisablePasswordModal.js

@ -7,7 +7,6 @@ import Box from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import InputPassword from 'components/base/InputPassword' import InputPassword from 'components/base/InputPassword'
import Label from 'components/base/Label' import Label from 'components/base/Label'
import { ErrorMessageInput } from 'components/base/Input'
import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal' import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal'
import type { T } from 'types/common' import type { T } from 'types/common'
@ -30,6 +29,7 @@ const INITIAL_STATE = {
incorrectPassword: false, incorrectPassword: false,
} }
// TODO: combine with the refactored password form
class DisablePasswordModal extends PureComponent<Props, State> { class DisablePasswordModal extends PureComponent<Props, State> {
state = INITIAL_STATE state = INITIAL_STATE
@ -71,44 +71,41 @@ class DisablePasswordModal extends PureComponent<Props, State> {
render={({ onClose }) => ( render={({ onClose }) => (
<form onSubmit={this.disablePassword}> <form onSubmit={this.disablePassword}>
<ModalBody onClose={onClose}> <ModalBody onClose={onClose}>
<ModalTitle>{t('settings:profile.disablePasswordModalTitle')}</ModalTitle> <ModalTitle>{t('password:disablePassword.title')}</ModalTitle>
<ModalContent> <ModalContent>
<Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center" px={4}> <Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center" px={4}>
{t('settings:profile.disablePasswordModalDesc')} {t('password:disablePassword.desc')}
<Box px={7} mt={4} flow={3}> <Box px={7} mt={4} flow={3}>
{isPasswordEnabled && ( {isPasswordEnabled && (
<Box flow={1}> <Box flow={1}>
<Label htmlFor="password"> <Label htmlFor="password">
{t('settings:profile.disablePasswordModalInput')} {t('password:inputFields.currentPassword.label')}
</Label> </Label>
<InputPassword <InputPassword
autoFocus autoFocus
type="password" type="password"
placeholder={t('settings:profile.disablePasswordModalInput')} placeholder={t('password:inputFields.currentPassword.placeholder')}
id="password" id="password"
onChange={this.handleInputChange('currentPassword')} onChange={this.handleInputChange('currentPassword')}
value={currentPassword} value={currentPassword}
error={incorrectPassword && t('password:errorMessageIncorrectPassword')}
/> />
{incorrectPassword && (
<ErrorMessageInput>
{t('password:errorMessageIncorrectPassword')}
</ErrorMessageInput>
)}
</Box> </Box>
)} )}
</Box> </Box>
</Box> </Box>
</ModalContent> </ModalContent>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}> <ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<Button type="button" onClick={onClose}> <Button type="button" padded onClick={onClose}>
{t('common:cancel')} {t('common:cancel')}
</Button> </Button>
<Button <Button
primary primary
padded
onClick={this.disablePassword} onClick={this.disablePassword}
disabled={!currentPassword && !incorrectPassword} disabled={!currentPassword && !incorrectPassword}
> >
{t('settings:profile.disablePasswordModalSave')} {t('common:save')}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalBody> </ModalBody>

89
src/components/SettingsPage/PasswordForm.js

@ -0,0 +1,89 @@
// @flow
import React, { PureComponent } from 'react'
import Box from 'components/base/Box'
import InputPassword from 'components/base/InputPassword'
import Label from 'components/base/Label'
import type { T } from 'types/common'
type Props = {
t: T,
isPasswordEnabled: boolean,
currentPassword: string,
newPassword: string,
confirmPassword: string,
incorrectPassword: boolean,
onSubmit: Function,
isValid: () => boolean,
onChange: Function,
}
class PasswordForm extends PureComponent<Props> {
render() {
const {
t,
isPasswordEnabled,
currentPassword,
newPassword,
incorrectPassword,
confirmPassword,
isValid,
onChange,
onSubmit,
} = this.props
// TODO: adjust design to separate 3 fields
return (
<form onSubmit={onSubmit}>
<Box px={7} mt={4} flow={3}>
{isPasswordEnabled && (
<Box flow={1} mb={5}>
<Label htmlFor="currentPassword">
{t('password:inputFields.currentPassword.label')}
</Label>
<InputPassword
autoFocus
placeholder={t('password:inputFields.currentPassword.placeholder')}
id="currentPassword"
onChange={onChange('currentPassword')}
value={currentPassword}
error={incorrectPassword && t('password:errorMessageIncorrectPassword')}
/>
</Box>
)}
<Box flow={1}>
<Label htmlFor="newPassword">{t('password:inputFields.newPassword.label')}</Label>
<InputPassword
style={{ mt: 4, width: 240 }}
autoFocus={!isPasswordEnabled}
placeholder={t('password:inputFields.newPassword.placeholder')}
id="newPassword"
onChange={onChange('newPassword')}
value={newPassword}
/>
</Box>
<Box flow={1}>
<Label htmlFor="confirmPassword">
{t('password:inputFields.confirmPassword.label')}
</Label>
<InputPassword
style={{ width: 240 }}
placeholder={t('password:inputFields.confirmPassword.placeholder')}
id="confirmPassword"
onChange={onChange('confirmPassword')}
value={confirmPassword}
error={
!isValid() &&
confirmPassword.length > 0 &&
t('password:errorMessageNotMatchingPassword')
}
/>
</Box>
</Box>
</form>
)
}
}
export default PasswordForm

136
src/components/SettingsPage/PasswordModal.js

@ -1,42 +1,35 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import { unlock } from 'reducers/application' import type { T } from 'types/common'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import InputPassword from 'components/base/InputPassword'
import Label from 'components/base/Label'
import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal' import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal'
import { ErrorMessageInput } from 'components/base/Input'
import type { T } from 'types/common'
const mapDispatchToProps = { import PasswordForm from './PasswordForm'
unlock,
}
type Props = { type Props = {
t: T, t: T,
onClose: Function, onClose: () => void,
unlock: Function, onChangePassword: (?string) => void,
isPasswordEnabled: boolean, isPasswordEnabled: boolean,
currentPasswordHash: string, currentPasswordHash: string,
onChangePassword: Function,
} }
type State = { type State = {
currentPassword: string, currentPassword: string,
newPassword: string, newPassword: string,
confirmPassword: string,
incorrectPassword: boolean, incorrectPassword: boolean,
} }
const INITIAL_STATE = { const INITIAL_STATE = {
currentPassword: '', currentPassword: '',
newPassword: '', newPassword: '',
confirmPassword: '',
incorrectPassword: false, incorrectPassword: false,
} }
@ -44,13 +37,15 @@ class PasswordModal extends PureComponent<Props, State> {
state = INITIAL_STATE state = INITIAL_STATE
handleSave = (e: SyntheticEvent<HTMLFormElement>) => { handleSave = (e: SyntheticEvent<HTMLFormElement>) => {
const { currentPassword, newPassword } = this.state
if (e) { if (e) {
e.preventDefault() e.preventDefault()
} }
if (!this.isValid()) { if (!this.isValid()) {
return return
} }
const { currentPassword, newPassword } = this.state
const { isPasswordEnabled, currentPasswordHash, onChangePassword } = this.props const { isPasswordEnabled, currentPasswordHash, onChangePassword } = this.props
if (isPasswordEnabled) { if (isPasswordEnabled) {
if (!bcrypt.compareSync(currentPassword, currentPasswordHash)) { if (!bcrypt.compareSync(currentPassword, currentPasswordHash)) {
@ -73,85 +68,66 @@ class PasswordModal extends PureComponent<Props, State> {
handleReset = () => this.setState(INITIAL_STATE) handleReset = () => this.setState(INITIAL_STATE)
isValid = () => { isValid = () => {
const { newPassword } = this.state const { newPassword, confirmPassword } = this.state
return newPassword return newPassword === confirmPassword
} }
render() { render() {
const { t, isPasswordEnabled, onClose, ...props } = this.props const { t, isPasswordEnabled, onClose, ...props } = this.props
const { currentPassword, newPassword, incorrectPassword } = this.state const { currentPassword, newPassword, incorrectPassword, confirmPassword } = this.state
const isValid = this.isValid()
return ( return (
<Modal <Modal
{...props} {...props}
onHide={this.handleReset} onHide={this.handleReset}
onClose={onClose} onClose={onClose}
render={({ onClose }) => ( render={({ onClose }) => (
<form onSubmit={this.handleSave}> <ModalBody onClose={onClose}>
<ModalBody onClose={onClose}> {isPasswordEnabled ? (
<ModalTitle>{t('settings:profile.passwordModalTitle')}</ModalTitle> <ModalTitle>{t('password:changePassword.title')}</ModalTitle>
<ModalContent> ) : (
<Box ff="Museo Sans|Regular" color="dark" textAlign="center" mb={2} mt={3}> <ModalTitle>{t('password:setPassword.title')}</ModalTitle>
{t('settings:profile.passwordModalSubtitle')} )}
</Box> <ModalContent>
<Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center" px={4}> <Box ff="Museo Sans|Regular" color="dark" textAlign="center" mb={2} mt={3}>
{t('settings:profile.passwordModalDesc')} {isPasswordEnabled
<Box px={7} mt={4} flow={3}> ? t('password:changePassword.subTitle')
{isPasswordEnabled && ( : t('password:setPassword.subTitle')}
<Box flow={1}> </Box>
<Label htmlFor="password"> <Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center" px={4}>
{t('settings:profile.passwordModalPasswordInput')} {isPasswordEnabled
</Label> ? t('password:changePassword.desc')
<InputPassword : t('password:setPassword.desc')}
autoFocus </Box>
type="password" <PasswordForm
placeholder={t('settings:profile.passwordModalPasswordInput')} onSubmit={this.handleSave}
id="password" isPasswordEnabled={isPasswordEnabled}
onChange={this.handleInputChange('currentPassword')} newPassword={newPassword}
value={currentPassword} currentPassword={currentPassword}
/> confirmPassword={confirmPassword}
{incorrectPassword && ( incorrectPassword={incorrectPassword}
<ErrorMessageInput> isValid={this.isValid}
{t('password:errorMessageIncorrectPassword')} onChange={this.handleInputChange}
</ErrorMessageInput> t={t}
)} />
</Box> </ModalContent>
)} <ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<Box flow={1}> <Button type="button" padded onClick={onClose}>
{isPasswordEnabled && ( {t('common:cancel')}
<Label htmlFor="newPassword"> </Button>
{t('settings:profile.passwordModalNewPasswordInput')} <Button
</Label> padded
)} primary
<InputPassword onClick={this.handleSave}
autoFocus={!isPasswordEnabled} disabled={!this.isValid() || !newPassword.length || !confirmPassword.length}
placeholder={t('settings:profile.passwordModalNewPasswordInput')} >
id="newPassword" {t('common:save')}
onChange={this.handleInputChange('newPassword')} </Button>
value={newPassword} </ModalFooter>
withStrength </ModalBody>
/>
</Box>
</Box>
</Box>
</ModalContent>
<ModalFooter horizontal align="center" justify="flex-end" flow={2}>
<Button type="button" onClick={onClose}>
{t('common:cancel')}
</Button>
<Button primary onClick={this.handleSave} disabled={!isValid}>
{t('settings:profile.passwordModalSave')}
</Button>
</ModalFooter>
</ModalBody>
</form>
)} )}
/> />
) )
} }
} }
export default connect( export default PasswordModal
null,
mapDispatchToProps,
)(PasswordModal)

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

@ -48,13 +48,6 @@ const Base = styled.input.attrs({
} }
` `
export const ErrorMessageInput = styled(Box).attrs({
alignItems: 'flex-start',
color: 'alertRed',
ff: 'Open Sans|SemiBold',
fontSize: 3,
})``
export const Textarea = styled.textarea.attrs({ export const Textarea = styled.textarea.attrs({
p: 2, p: 2,
fontSize: 4, fontSize: 4,

6
src/components/modals/AccountSettingRenderBody.js

@ -18,7 +18,7 @@ import Spoiler from 'components/base/Spoiler'
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import Input, { ErrorMessageInput } from 'components/base/Input' import Input from 'components/base/Input'
import Select from 'components/base/LegacySelect' import Select from 'components/base/LegacySelect'
import { ModalBody, ModalTitle, ModalFooter, ModalContent } from 'components/base/Modal' import { ModalBody, ModalTitle, ModalFooter, ModalContent } from 'components/base/Modal'
@ -143,10 +143,8 @@ class HelperComp extends PureComponent<Props, State> {
onChange={this.handleChangeName} onChange={this.handleChangeName}
renderLeft={<InputLeft currency={account.currency} />} renderLeft={<InputLeft currency={account.currency} />}
onFocus={e => this.handleFocus(e, 'accountName')} onFocus={e => this.handleFocus(e, 'accountName')}
error={accountNameError && t('account:settings.accountName.error')}
/> />
{accountNameError && (
<ErrorMessageInput>{t('account:settings.accountName.error')}</ErrorMessageInput>
)}
</Box> </Box>
</Container> </Container>
<Container> <Container>

19
src/reducers/onboarding.js

@ -93,16 +93,15 @@ const state: OnboardingState = {
showBreadcrumb: true, showBreadcrumb: true,
}, },
}, },
// UNTIL IS NEEDED SET PASSWORD IS COMMENTED OUT {
// { name: 'setPassword',
// name: 'setPassword', label: 'Set Password',
// label: 'Set Password', options: {
// options: { showFooter: false,
// showFooter: false, showBackground: true,
// showBackground: true, showBreadcrumb: true,
// showBreadcrumb: true, },
// }, },
// },
{ {
name: 'analytics', name: 'analytics',
label: 'Analytics & Bug report', label: 'Analytics & Bug report',

1
static/i18n/en/common.yml

@ -6,6 +6,7 @@ confirm: Confirm
cancel: Cancel cancel: Cancel
delete: Delete delete: Delete
continue: Continue continue: Continue
skipThisStep: Skip This Step
chooseWalletPlaceholder: Choose a wallet... chooseWalletPlaceholder: Choose a wallet...
currency: Currency currency: Currency
selectAccount: Select an account selectAccount: Select an account

8
static/i18n/en/onboarding.yml

@ -88,8 +88,12 @@ genuineCheck:
title: Something is wrong with your Ledger Blue title: Something is wrong with your Ledger Blue
desc: A problem occurred with your Ledger Blue. Contact Ledger Support to get assistance or go back to the security check. desc: A problem occurred with your Ledger Blue. Contact Ledger Support to get assistance or go back to the security check.
setPassword: setPassword:
title: Choose a password to securely access Ledger Live title: Protect your privacy (optional)
desc: Use uppercase, lowercase, special characters (#, @, !, ?) and numbers for a stronger password. desc: Set a password to prevent unauthorized access to Ledger Live data stored on your computer, including account names, balances, transactions and public addresses.
disclaimer:
note1: Make sure to remember your password and do not share it.
note2: Losing your password requires resetting Ledger Live and re-adding accounts.
note3: Loss of password doesn’t affect your crypto-assets.
analytics: analytics:
title: Help Ledger to improve its products and services title: Help Ledger to improve its products and services
desc: This is a long text, please replace it with the final wording once it’s done.
Lorem ipsum dolor amet ledger lorem dolor ipsum amet desc: This is a long text, please replace it with the final wording once it’s done.
Lorem ipsum dolor amet ledger lorem dolor ipsum amet

22
static/i18n/en/password.yml

@ -4,3 +4,25 @@ warning_2: Warning 2
warning_3: Warning 3 warning_3: Warning 3
warning_4: Warning 4 warning_4: Warning 4
errorMessageIncorrectPassword: The password you entered is incorrect. errorMessageIncorrectPassword: The password you entered is incorrect.
errorMessageNotMatchingPassword: Passwords don't match.
inputFields:
newPassword:
label: Password
placeholder: Password
confirmPassword:
label: Confirm Password
placeholder: Confirm Password
currentPassword:
label: Current Password
placeholder: Current Password
changePassword:
title: Edit Password
subTitle: Change your password
desc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam. In eget ipsum arcu donec finibus
setPassword:
title: Set Password
subTitle: Set a password to lock your application
desc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam. In eget ipsum arcu donec finibus
disablePassword:
title: Disable Password
desc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam.

13
static/i18n/en/settings.yml

@ -23,22 +23,9 @@ currencies:
explorer: Blockchain explorer explorer: Blockchain explorer
explorerDesc: Lorem ipsum dolor sit amet explorerDesc: Lorem ipsum dolor sit amet
profile: profile:
username: Username
usernameDesc: Lorem ipsum dolor sit amet
password: Password password: Password
passwordDesc: Lorem ipsum dolor sit amet passwordDesc: Lorem ipsum dolor sit amet
changePassword: Change password changePassword: Change password
passwordModalTitle: Password
passwordModalSubtitle: Set a password to lock your application
passwordModalDesc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam.
passwordModalPasswordInput: Current password
passwordModalNewPasswordInput: New password
passwordModalRepeatPasswordInput: Repeat password
passwordModalSave: Save
disablePasswordModalTitle: Disable Password
disablePasswordModalDesc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam.
disablePasswordModalInput: Current password
disablePasswordModalSave: Save
sync: Sync accounts sync: Sync accounts
syncDesc: Lorem ipsum dolor sit amet syncDesc: Lorem ipsum dolor sit amet
export: Export logs export: Export logs

Loading…
Cancel
Save