Skip to content

Commit

Permalink
Add Keystore Account rework (#1350)
Browse files Browse the repository at this point in the history
* not showing password entry screen - why?

* flow working - need to polish up

* flow working - need to polish up

* remove keystore app tests temporarily

* remove intermediary state

* need to fix transitions & then re-do tests

* tests green

* tests re-added for AddKeystore

* use advanceTimersAfterInput

* addressing PR comments - stop flashing of old stater

* fix tests

* lowercase

* allow advanceTimersAfterInput to be a number, pass through

* remove act

* rebase fixup

* fix prop

---------

Co-authored-by: goosewobbler <432005+goosewobbler@users.noreply.github.com>
Co-authored-by: goosewobbler <goosewobbler@protonmail.com>
Co-authored-by: Matt Holtzman <matt.holtzman@gmail.com>
  • Loading branch information
4 people authored Feb 10, 2023
1 parent d1f6cb3 commit c9d7bc6
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 249 deletions.
322 changes: 90 additions & 232 deletions app/dash/Accounts/Add/AddKeystore/index.js
Original file line number Diff line number Diff line change
@@ -1,251 +1,109 @@
import React from 'react'
import Restore from 'react-restore'

import React, { useEffect, useState } from 'react'
import { AddHotAccount } from '../Components'
import link from '../../../../../resources/link'
import svg from '../../../../../resources/svg'
import RingIcon from '../../../../../resources/Components/RingIcon'

class AddRing extends React.Component {
constructor(...args) {
super(...args)
this.state = {
index: 0,
adding: false,
password: '',
status: '',
error: false,
mode: this.props.mode ? this.props.mode : 'manual',
keystore: '',
keystorePassword: ''
}
this.forms = {
keystorePassword: React.createRef(),
keystoreCreatePassword: React.createRef()
import { PasswordInput } from '../../../../../resources/Components/Password'

const navForward = (accountData) =>
link.send('nav:forward', 'dash', {
view: 'accounts',
data: {
showAddAccounts: true,
newAccountType: 'keystore',
accountData
}
}
})

onChange(key, e) {
e.preventDefault()
const update = {}
update[key] = e.target.value || ''
this.setState(update)
}

onBlur(key, e) {
e.preventDefault()
const update = {}
update[key] = this.state[key] || ''
this.setState(update)
}
const LocateKeystore = ({ addKeystore, error, setError }) => {
useEffect(() => {
if (!error) return
setTimeout(() => {
setError('')
}, 1_500)
}, [error])
return (
<div className='addAccountItemOptionSetupFrame'>
{error ? (
<div role='button' className='addAccountItemOptionError'>
{error}
</div>
) : (
<div
role='button'
className='addAccountItemOptionSubmit'
style={{ marginTop: '10px' }}
onClick={() => addKeystore()}
>
Locate Keystore File (json)
</div>
)}
</div>
)
}

onFocus(key, e) {
e.preventDefault()
if (this.state[key] === '') {
const update = {}
update[key] = ''
this.setState(update)
}
const Locating = () => (
<div className='addAccountItemOptionSetupFrame'>
<div role={'status'} className='addAccountItemOptionTitle' style={{ marginTop: '15px' }}>
Locating Keystore file
</div>
</div>
)

const EnterKeystorePassword = ({ keystore }) => {
const next = (keystorePassword) => {
navForward({
secret: keystore,
creationArgs: [keystorePassword]
})
}
//TODO: validate keystore password here?
const getError = () => {}
const title = 'Enter Keystore Password'
const buttonText = 'Continue'
return <PasswordInput {...{ next, getError, title, buttonText }} />
}

next() {
this.blurActive()
this.setState({ index: ++this.state.index })
this.focusActive()
}
const LoadKeystore = ({ accountData }) => {
const { keystore, selecting, secret } = accountData

createKeystore() {
this.next()
link.rpc(
'createFromKeystore',
this.state.keystore,
this.state.keystorePassword,
this.state.password,
(err, signer) => {
if (err) {
this.setState({ status: err, error: true })
} else {
// reset nav state to before the start of the flow and open the new signer
link.send('tray:action', 'backDash', 2)
const crumb = {
view: 'expandedSigner',
data: { signer: signer.id }
}
link.send('tray:action', 'navDash', crumb)
}
}
)
}
const [error, setError] = useState('')

addKeystore() {
this.setState({ mode: 'keystore' })
this.next()
const addKeystore = () => {
navForward({ selecting: true })
setTimeout(() => {
link.rpc('locateKeystore', (err, keystore) => {
link.rpc('locateKeystore', (err, locatedKeystore) => {
link.send('nav:back', 'dash')
if (err) {
this.setState({ keystore: '', error: err })
setError(err)
} else {
this.setState({ keystore })
this.next()
navForward({ keystore: locatedKeystore })
}
})
}, 640)
}

restart() {
this.setState({
index: 1,
adding: false,
password: '',
mode: 'manual',
privateKey: '',
keystore: '',
keystorePassword: ''
})
setTimeout(() => {
this.setState({ status: '', error: false })
}, 500)
this.focusActive()
}

keyPress(e, next) {
if (e.key === 'Enter') {
e.preventDefault()
next()
}
}

adding() {
this.setState({ adding: true })
}

blurActive() {
const formInput = this.currentForm()
if (formInput) formInput.current.blur()
}

focusActive() {
setTimeout(() => {
const formInput = this.currentForm()
if (formInput) formInput.current.focus()
}, 500)
}
const viewIndex = secret || keystore ? 2 : selecting ? 1 : 0

currentForm() {
let current
if (this.state.index === 2) current = this.forms.keystorePassword
if (this.state.index === 3) current = this.forms.keystoreCreatePassword
return current
}

render() {
let itemClass = 'addAccountItem addAccountItemSmart addAccountItemAdding'
return (
<div className={itemClass} style={{ transitionDelay: (0.64 * this.props.index) / 4 + 's' }}>
<div className='addAccountItemBar addAccountItemHot' />
<div className='addAccountItemWrap'>
<div className='addAccountItemTop'>
<div className='addAccountItemTopType'>
<div className='addAccountItemIcon'>
<div className='addAccountItemIconType addAccountItemIconHot'>
<RingIcon svgName={'file'} />
</div>
<div className='addAccountItemIconHex addAccountItemIconHexHot' />
</div>
<div className='addAccountItemTopTitle'>Keystore</div>
</div>
{/* <div className='addAccountItemClose' onMouseDown={() => this.props.close()}>{'Done'}</div> */}
<div className='addAccountItemSummary'>
A keystore account lets you add accounts from your keystore.json file
</div>
</div>
<div className='addAccountItemOption'>
<div
className='addAccountItemOptionIntro'
onMouseDown={() => {
this.adding()
setTimeout(() => {
link.send('tray:action', 'navDash', {
view: 'notify',
data: { notify: 'hotAccountWarning', notifyData: {} }
})
}, 800)
}}
>
Add Keyring Account
</div>
<div
className='addAccountItemOptionSetup'
style={{ transform: `translateX(-${100 * this.state.index}%)` }}
>
<div className='addAccountItemOptionSetupFrames'>
<div className='addAccountItemOptionSetupFrame'>
<div className='addAccountItemOptionTitle'>Add Keystore File</div>
<div
className='addAccountItemOptionSubmit'
style={{ marginTop: '10px' }}
onMouseDown={() => this.addKeystore()}
>
Locate Keystore File (json)
</div>
</div>
<div className='addAccountItemOptionSetupFrame'>
<div className='addAccountItemOptionTitle'>Locating Keystore</div>
</div>
<div className='addAccountItemOptionSetupFrame'>
<div className='addAccountItemOptionTitle'>Enter Keystore Password</div>
<div className='addAccountItemOptionInput'>
<input
type='password'
tabIndex='-1'
ref={this.forms.keystorePassword}
value={this.state.keystorePassword}
onChange={(e) => this.onChange('keystorePassword', e)}
onFocus={(e) => this.onFocus('keystorePassword', e)}
onBlur={(e) => this.onBlur('keystorePassword', e)}
onKeyPress={(e) => this.keyPress(e, () => this.next())}
/>
</div>
<div className='addAccountItemOptionSubmit' onMouseDown={() => this.next()}>
Next
</div>
</div>
<div className='addAccountItemOptionSetupFrame'>
<div className='addAccountItemOptionTitle'>Create Account Password</div>
<div className='addAccountItemOptionInput addAccountItemOptionInputPassword'>
<div className='addAccountItemOptionSubtitle'>
password must be 12 characters or longer
</div>
<input
type='password'
tabIndex='-1'
ref={this.forms.keystoreCreatePassword}
value={this.state.password}
onChange={(e) => this.onChange('password', e)}
onFocus={(e) => this.onFocus('password', e)}
onBlur={(e) => this.onBlur('password', e)}
onKeyPress={(e) => this.keyPress(e, () => this.createKeystore())}
/>
</div>
<div className='addAccountItemOptionSubmit' onMouseDown={() => this.createKeystore()}>
Create
</div>
</div>
<div className='addAccountItemOptionSetupFrame'>
<div className='addAccountItemOptionTitle'>{this.state.status}</div>
{this.state.error ? (
<div className='addAccountItemOptionSubmit' onMouseDown={() => this.restart()}>
try again
</div>
) : null}
</div>
</div>
</div>
</div>
<div className='addAccountItemFooter' />
</div>
</div>
)
}
const steps = [
<LocateKeystore key={0} {...{ addKeystore, error, setError }} />,
<Locating key={1} />,
<EnterKeystorePassword key={2} keystore={accountData.keystore} />
]
return <>{steps[viewIndex]}</>
}

export default Restore.connect(AddRing)
const AddKeystore = ({ accountData }) => (
<AddHotAccount
title='Key Store'
summary='A keystore account lets you add accounts from your keystore.json file'
svgName='file'
intro='Add KeyStore Account'
createSignerMethod='createFromKeystore'
newAccountType='keystore'
backSteps={6}
accountData={accountData}
firstStep={<LoadKeystore key={0} accountData={accountData} />}
validateSecret={() => {}}
/>
)

export default AddKeystore
21 changes: 14 additions & 7 deletions app/dash/Accounts/Add/Components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,35 +129,42 @@ export function AddHotAccount({
accountData,
createSignerMethod,
newAccountType,
validateSecret
validateSecret,
firstStep,
backSteps = 4
}) {
const { secret, password, error } = accountData
const { secret, password, error, creationArgs = [] } = accountData
const viewIndex = error ? 3 : !secret ? 0 : !password ? 1 : 2

const onCreate = (password) => {
navForward(newAccountType, {
secret,
password
password,
creationArgs
})
}

const onConfirm = (password) =>
link.rpc(createSignerMethod, secret, password, (err, signer) => {
const onConfirm = () =>
link.rpc(createSignerMethod, secret, password, ...creationArgs, (err, signer) => {
if (err) {
return navForward(newAccountType, {
error: err
})
}

link.send('nav:back', 'dash', 4)
link.send('nav:back', 'dash', backSteps)
link.send(`nav:forward`, 'dash', {
view: 'expandedSigner',
data: { signer: signer.id }
})
})

const firstFlowStep = firstStep || (
<EnterSecret key={0} {...{ validateSecret, title, newAccountType, autofocus: viewIndex === 0 }} />
)

const steps = [
<EnterSecret key={0} {...{ validateSecret, title, newAccountType, autofocus: viewIndex === 0 }} />,
firstFlowStep,
<CreatePassword key={1} onCreate={onCreate} autofocus={viewIndex === 1} />,
<ConfirmPassword key={2} password={password} onConfirm={onConfirm} autofocus={viewIndex === 2} />,
<Error key={3} error={error} />
Expand Down
Loading

0 comments on commit c9d7bc6

Please sign in to comment.