Skip to content

Commit

Permalink
Merge pull request #224 from cosmos/jordan/23-multiple-accounts
Browse files Browse the repository at this point in the history
Multiple accounts
  • Loading branch information
faboweb authored Dec 13, 2017
2 parents 7e726d2 + 1932d25 commit 2bf2613
Show file tree
Hide file tree
Showing 18 changed files with 529 additions and 100 deletions.
2 changes: 1 addition & 1 deletion app/src/renderer/components/common/NiFieldSeed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ export default {
@require '~variables'
.ni-field.ni-field-seed
height 5rem
height 6rem
</style>
2 changes: 1 addition & 1 deletion app/src/renderer/components/common/NiHardwareState.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default {
flex-flow column nowrap
align-items center
justify-content center
margin 0 auto
margin 1.5rem auto
height 15rem
width 15rem
Expand Down
4 changes: 3 additions & 1 deletion app/src/renderer/components/common/NiSession.vue
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ export default {
&:not(.ni-form)
&.ni-form .ni-form-main
width 32rem
height 36rem
min-height 28rem
max-height 90vh
height auto
.ni-session-header
background app-fg
Expand Down
27 changes: 22 additions & 5 deletions app/src/renderer/components/common/NiSessionSignIn.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
<template lang="pug">
.ni-session: form-struct(:submit='onSubmit').ni-session-container
.ni-session-header
a(@click="setState('welcome')"): i.material-icons arrow_back
.ni-session-title Sign In
a(@click="help"): i.material-icons help_outline
.ni-session-main
form-group(field-id='sign-in-name' field-label='Select Account')
field#sign-in-name(
type="select"
v-model="fields.signInName"
:options="accounts")
form-msg(name='Name' type='required' v-if='!$v.fields.signInName.required')

form-group(:error='$v.fields.signInPassword.$error'
field-id='sign-in-password' field-label='Password')
field#sign-in-password(
type="password"
v-model="fields.signInPassword")
form-msg(name='Password' type='required' v-if='!$v.fields.signInPassword.required')
form-msg(name='Password' type='minLength' min="10" v-if='!$v.fields.signInPassword.minLength')
form-group
a(@click="setState('delete')") Sign Out and Remove Account
.ni-session-footer
btn(icon="arrow_forward" icon-pos="right" value="Next" size="lg")
</template>

<script>
import {mapGetters} from 'vuex'
import {required, minLength} from 'vuelidate/lib/validators'
import Btn from '@nylira/vue-button'
import Field from '@nylira/vue-field'
Expand All @@ -37,6 +44,7 @@ export default {
},
data: () => ({
fields: {
signInName: '',
signInPassword: ''
}
}),
Expand All @@ -47,20 +55,29 @@ export default {
this.$v.$touch()
if (this.$v.$error) return
try {
await this.$store.dispatch('testLogin', {password: this.fields.signInPassword})
this.$store.dispatch('signIn', {password: this.fields.signInPassword})
await this.$store.dispatch('testLogin', { password: this.fields.signInPassword, account: this.fields.signInName })
this.$store.dispatch('signIn', { password: this.fields.signInPassword, account: this.fields.signInName })
this.$store.commit('setModalSession', false)
this.$store.commit('notify', { title: 'Signed In', body: `You are now signed in to your Cosmos account.` })
} catch (err) {
this.$store.commit('notifyError', { title: 'Signing In Failed', body: err.message })
}
}
},
computed: {
...mapGetters(['user']),
accounts () {
let accounts = this.user.accounts
accounts = accounts.filter((name) => name !== 'trunk')
return accounts.map((name) => ({ key: name, value: name }))
}
},
mounted () {
this.$el.querySelector('#sign-in-password').focus()
this.$el.querySelector('#sign-in-name').focus()
},
validations: () => ({
fields: {
signInName: { required },
signInPassword: { required, minLength: minLength(10) }
}
})
Expand Down
28 changes: 19 additions & 9 deletions app/src/renderer/components/common/NiSessionSignUp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@
.ni-session-title Create Account
a(@click="help"): i.material-icons help_outline
.ni-session-main
form-group(field-id='sign-up-name' field-label='Account Name' :error='$v.fields.signUpName.$error')
field#sign-up-name(
type="text"
placeholder="at least 5 characters"
v-model="fields.signUpName")
form-msg(name='Name' type='required' v-if='!$v.fields.signUpName.required')
form-msg(name='Name' type='minLength' min="5" v-if='!$v.fields.signUpName.minLength')

form-group(field-id='sign-up-seed' field-label='Seed (write it down)')
field-seed#sign-up-seed(v-model="fields.signUpSeed" disabled)
form-msg(body='Please back up the seed phrase for this account. These words cannot be recovered!')

form-group(:error='$v.fields.signInPassword.$error'
form-group(:error='$v.fields.signUpPassword.$error'
field-id='sign-in-password' field-label='Password')
field#sign-in-password(
type="password"
placeholder="at least 10 characters"
v-model="fields.signInPassword")
v-model="fields.signUpPassword")
form-msg(body="Create a password to secure your new account")
form-msg(name='Password' type='required' v-if='!$v.fields.signInPassword.required')
form-msg(name='Password' type='minLength' min="10" v-if='!$v.fields.signInPassword.minLength')
form-msg(name='Password' type='required' v-if='!$v.fields.signUpPassword.required')
form-msg(name='Password' type='minLength' min="10" v-if='!$v.fields.signUpPassword.minLength')

form-group(field-id="sign-up-warning" field-label=' '
:error='$v.fields.signUpWarning.$error')
Expand Down Expand Up @@ -63,8 +71,9 @@ export default {
data: () => ({
creating: true,
fields: {
signUpName: '',
signUpSeed: 'Creating seed...',
signInPassword: '',
signUpPassword: '',
signUpWarning: false,
signUpBackup: false
}
Expand All @@ -75,16 +84,16 @@ export default {
async onSubmit () {
this.$v.$touch()
if (this.$v.$error) return
let key = await this.$store.dispatch('createKey', { seedPhrase: this.fields.signUpSeed, password: this.fields.signInPassword })
let key = await this.$store.dispatch('createKey', { seedPhrase: this.fields.signUpSeed, password: this.fields.signUpPassword, name: this.fields.signUpName })
if (key) {
this.$store.commit('setModalSession', false)
this.$store.commit('notify', { title: 'Signed Up', body: 'Your account has been created.' })
this.$store.dispatch('signIn', {password: this.fields.signInPassword})
this.$store.dispatch('signIn', { password: this.fields.signUpPassword, account: this.fields.signUpName })
}
}
},
mounted () {
this.$el.querySelector('#sign-up-warning').focus()
this.$el.querySelector('#sign-up-name').focus()
this.$store.dispatch('createSeed')
.then(seedPhrase => {
this.creating = false
Expand All @@ -93,7 +102,8 @@ export default {
},
validations: () => ({
fields: {
signInPassword: { required, minLength: minLength(10) },
signUpName: { required, minLength: minLength(5) },
signUpPassword: { required, minLength: minLength(10) },
signUpWarning: { required },
signUpBackup: { required }
}
Expand Down
9 changes: 8 additions & 1 deletion app/src/renderer/components/common/NiSessionWelcome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
.ni-session-title Welcome to Cosmos!
a(@click="help"): i.material-icons help_outline
.ni-session-main
li-session(
v-if="accountExists"
@click.native="setState('sign-in')"
icon="lock"
title="Sign in with password"
subtitle="If you have an account, choose this option")
li-session(
@click.native="setState('sign-up')"
icon="create"
Expand Down Expand Up @@ -35,7 +41,8 @@ export default {
LiSession
},
computed: {
...mapGetters(['config'])
...mapGetters(['config', 'user']),
accountExists () { return this.user.accounts.length > 0 }
},
methods: {
help () { this.$store.commit('setModalHelp', true) },
Expand Down
6 changes: 3 additions & 3 deletions app/src/renderer/components/common/NiToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ export default {
top 3rem
right 0
background hover
background bc
border-radius 0.25rem
line-height 2.5rem
padding 0 0.75rem
color bright
color txt
z-index 101
white-space nowrap
Expand All @@ -76,7 +76,7 @@ export default {
height 0
border-left 0.375rem solid transparent
border-right 0.375rem solid transparent
border-bottom 0.375rem solid hover
border-bottom 0.375rem solid bc
display block
content ''
Expand Down
9 changes: 6 additions & 3 deletions app/src/renderer/components/common/NiUserPane.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ list-item.ni-li-user(
@click.native="close"
icon="face"
title="CosmosUser01")
div(v-else-if="user.signedIn")
div(v-else-if="user.signedIn")
list-item.ni-li-user(
type="link"
icon="mood"
icon="exit_to_app"
@click.native="signOut"
title="Signed In (Sign Out)")
title="Sign Out")
list-item.ni-li-user(
v-else
@click.native="openSession"
Expand Down Expand Up @@ -59,4 +59,7 @@ export default {
border-top px solid bc
height 3rem + px
cursor: pointer
.ni-li-title
color link
</style>
2 changes: 1 addition & 1 deletion app/src/renderer/components/wallet/PageBalances.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ page(title='Balance')
:key="i.denom"
:dt="i.denom.toUpperCase()"
:dd="i.amount")
list-item(v-if='wallet.balances.length === 0' dt="N/A" dd="None Available")
list-item(v-if='wallet.denoms.length === 0 && wallet.balances.length === 0' dt="N/A" dd="None Available")
</template>

<script>
Expand Down
2 changes: 1 addition & 1 deletion app/src/renderer/styles/variables.styl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ app-bg-hover = hsl(app-hue, 33%, 20%)
// text
bright = #fff
txt = hsl(app-hue, 13%, 85%)
dim = hsl(app-hue, 13%, 69%)
dim = hsl(app-hue, 13%, 60%)

// borders
bc = hsl(app-hue, 22%, 23%)
Expand Down
38 changes: 20 additions & 18 deletions app/src/renderer/vuex/modules/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { KEY_PASSWORD, KEY_NAME } from './wallet'

export default ({ commit, node }) => {
const emptyNomination = {
keybase: '',
Expand All @@ -20,35 +18,38 @@ export default ({ commit, node }) => {
delegation: [],
pubkey: '',
privkey: null,
signedIn: false
signedIn: false,
accounts: []
}

const state = JSON.parse(JSON.stringify(emptyUser))

const mutations = {
activateDelegation (state) {
state.delegationActive = true
},
setAccounts (state, accounts) {
state.accounts = accounts
}
}

const actions = {
async showInitialScreen ({ dispatch }) {
let exists = await dispatch('accountExists')
await dispatch('loadAccounts')
let exists = state.accounts.length > 0
let screen = exists ? 'sign-in' : 'welcome'
commit('setModalSessionState', screen)
commit('setModalSession', true)
},
async accountExists (state, account = KEY_NAME) {
async loadAccounts ({ commit }) {
try {
let keys = await node.listKeys()
return !!keys.find(key => key.name === account)
commit('setAccounts', keys.map((key) => key.name))
} catch (err) {
commit('notifyError', { title: `Couldn't read keys'`, body: err.message })
commit('notifyError', { title: `Couldn't read keys`, body: err.message })
}
},
// testing if the provided password works so we can show the user early if he uses the wrong password
// testing this by trying to change the password... to the same password...
async testLogin (state, {password, account = KEY_NAME}) {
async testLogin (state, { password, account }) {
try {
return await node.updateKey(account, {
name: account,
Expand All @@ -62,41 +63,42 @@ export default ({ commit, node }) => {
// to create a temporary seed phrase, we create a junk account with name 'trunk' for now
async createSeed ({ commit }) {
let JUNK_ACCOUNT_NAME = 'trunk'
let TRUNK_PASSWORD = '1234567890'
try {
// cleanup an existing junk account
let keys = await node.listKeys()
if (keys.find(key => key.name === JUNK_ACCOUNT_NAME)) {
await node.deleteKey(JUNK_ACCOUNT_NAME, {
password: KEY_PASSWORD,
password: TRUNK_PASSWORD,
name: JUNK_ACCOUNT_NAME
})
}

// generate seedPhrase with junk account
let temporaryKey = await node.generateKey({ name: JUNK_ACCOUNT_NAME, password: KEY_PASSWORD })
let temporaryKey = await node.generateKey({ name: JUNK_ACCOUNT_NAME, password: TRUNK_PASSWORD })
return temporaryKey.seed_phrase
} catch (err) {
commit('notifyError', { title: 'Couln\'t create a seed', body: err.message })
commit('notifyError', { title: `Couldn't create a seed`, body: err.message })
}
},
async createKey ({ commit, dispatch }, { seedPhrase, password, name = KEY_NAME }) {
async createKey ({ commit, dispatch }, { seedPhrase, password, name }) {
try {
let {key} = await node.recoverKey({ name, password, seed_phrase: seedPhrase })
dispatch('initializeWallet', key)
return key
} catch (err) {
commit('notifyError', { title: 'Couln\'t create a key', body: err.message })
commit('notifyError', { title: `Couldn't create a key`, body: err.message })
}
},
async deleteKey ({ commit, dispatch }, { password, name = KEY_NAME }) {
async deleteKey ({ commit, dispatch }, { password, name }) {
try {
await node.deleteKey(name, { name, password })
return true
} catch (err) {
commit('notifyError', { title: `Couln't delete account ${name}`, body: err.message })
commit('notifyError', { title: `Couldn't delete account ${name}`, body: err.message })
}
},
async signIn ({ state, dispatch }, {password, account = KEY_NAME}) {
async signIn ({ state, dispatch }, { password, account }) {
state.password = password
state.account = account
state.signedIn = true
Expand Down
9 changes: 4 additions & 5 deletions app/src/renderer/vuex/modules/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ let fs = require('fs-extra')
let { join } = require('path')
let root = require('../../../root.js')

export const KEY_NAME = 'default'
// TODO: add UI for password, instead of hardcoding one
export const KEY_PASSWORD = '1234567890'

export default ({ commit, node }) => {
let state = {
balances: [],
Expand All @@ -23,9 +19,12 @@ export default ({ commit, node }) => {
},
setWalletKey (state, key) {
state.key = key
// clear previous account state
state.balances = []
state.history = []
state.sequence = 0
},
setWalletSequence (state, sequence) {
if (state.sequence === sequence) return
state.sequence = sequence
},
setWalletHistory (state, history) {
Expand Down
Loading

0 comments on commit 2bf2613

Please sign in to comment.