Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fabo/1835 signing methods #1860

Merged
merged 20 commits into from
Jan 25, 2019
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- [\#1660](https://github.com/cosmos/voyager/issues/1660) Add parameters and pool to store @fedekunze
- [\#1739](https://github.com/cosmos/voyager/issues/1739) Init jsDoc into project @sabau
- [\#1674](https://github.com/cosmos/voyager/issues/1674) Add PageProfile component with shared styles for validator and proposal profiles @jbibla
- [\#1835](https://github.com/cosmos/voyager/issues/1835) allow user to use different signing methods @faboweb

### Changed

Expand Down
142 changes: 134 additions & 8 deletions app/src/renderer/components/common/ActionModal.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<transition name="slide-fade">
<transition v-if="show" name="slide-fade">
<div v-click-outside="close" class="action-modal">
<div class="action-modal-header">
<img
Expand All @@ -14,9 +14,68 @@
<i class="material-icons">close</i>
</div>
</div>
<div class="action-modal-form"><slot></slot></div>
<div class="action-modal-form">
<slot></slot>

<tm-form-group
faboweb marked this conversation as resolved.
Show resolved Hide resolved
v-if="signMethods.length > 1"
class="action-modal-form-group"
field-id="sign-method"
field-label="Signing Method"
>
<tm-field
id="sign-method"
v-model="selectedSignMethod"
:options="signMethods"
type="select"
/>
</tm-form-group>

<tm-form-group
v-if="selectedSignMethod === `local`"
:error="$v.password.$error && $v.password.$invalid"
class="action-modal-group"
field-id="password"
field-label="Password"
>
<tm-field
id="password"
v-model="password"
type="password"
placeholder="Password"
/>
<tm-form-msg
v-if="$v.password.$error && !$v.password.required"
name="Password"
type="required"
/>
</tm-form-group>
</div>
<div class="action-modal-footer">
<slot name="action-modal-footer"></slot>
<slot name="action-modal-footer">
<tm-form-group class="action-modal-group">
<div class="action-modal-footer">
<tm-btn
v-if="sending"
value="Sending..."
disabled="disabled"
color="primary"
/>
<tm-btn
v-else-if="!connected"
value="Connecting..."
disabled="disabled"
color="primary"
/>
<tm-btn
v-else
color="primary"
value="Submit"
@click.native="validateForm"
/>
</div>
</tm-form-group>
</slot>
<p
v-if="submissionError"
class="tm-form-msg sm tm-form-msg--error submission-error"
Expand All @@ -30,38 +89,105 @@

<script>
import ClickOutside from "vue-click-outside"
import TmBtn from "common/TmBtn"
import TmField from "common/TmField"
import TmFormGroup from "common/TmFormGroup"
import TmFormMsg from "common/TmFormMsg"
import { mapGetters } from "vuex"
import { requiredIf } from "vuelidate/lib/validators"

export default {
name: `action-modal`,
directives: {
ClickOutside
},
components: {
TmBtn,
TmField,
TmFormGroup,
TmFormMsg
},
props: {
title: {
type: String,
required: true
},
submitFn: {
type: Function,
required: true
},
validate: {
type: Function,
required: true
},
submissionErrorPrefix: {
type: String,
default: `Submitting data failed`
}
},
data: () => ({
submissionError: null
signMethod: null,
password: null,
selectedSignMethod: `local`,
signMethods: [
faboweb marked this conversation as resolved.
Show resolved Hide resolved
{
key: `(Unsafe) Local Account`,
value: `local`
}
// {
// key: `Ledger`,
// value: `ledger`
// },
// {
// key: `Cosmos Signer App`,
// value: `signer-app`
// }
],
sending: false,
submissionError: null,
show: false
}),
computed: {
...mapGetters([`connected`])
},
methods: {
open() {
this.show = true
},
close() {
this.$emit(`close-action-modal`)
this.show = false
},
async validateForm() {
this.sending = true
this.$v.$touch()

// An ActionModal is only the prototype of a parent modal
// here we trigger the validation of the form that this parent modal
let childFormValid = this.validate()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice rename. thank you!


if (!this.$v.$invalid && childFormValid) {
await this.submit()
}
this.sending = false
},
async submit(submitFn, submissionErrorPrefix = `Submitting data failed`) {
async submit() {
try {
await submitFn()
await this.submitFn(this.selectedSignMethod, this.password)

this.close()
} catch ({ message }) {
this.submissionError = `${submissionErrorPrefix}: ${message}.`
this.submissionError = `${this.submissionErrorPrefix}: ${message}.`

setTimeout(() => {
this.submissionError = null
}, 5000)
}
}
},
validations() {
return {
password: requiredIf(() => this.selectedSignMethod === `local`)
}
}
}
</script>
Expand Down
134 changes: 41 additions & 93 deletions app/src/renderer/components/governance/ModalDeposit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,44 @@
<action-modal
id="modal-deposit"
ref="actionModal"
:submit-fn="submitForm"
:validate="validateForm"
title="Deposit"
class="modal-deposit"
@close-action-modal="close"
submission-error-prefix="Depositing failed"
>
<tm-form-group
:error="
$v.amount.$error && $v.amount.$invalid && (amount > 0 || balance === 0)
"
:error="$v.amount.$error && $v.amount.$invalid"
class="action-modal-form-group"
field-id="amount"
field-label="Amount"
>
<span class="input-suffix">{{ denom }}</span>
<tm-field v-focus id="amount" v-model="amount" type="number" />
<tm-field id="amount" v-model="amount" type="number" />
<tm-form-msg
v-if="
$v.amount.$error && !$v.amount.between && amount > 0 && balance > 0
"
:max="$v.amount.$params.between.max"
:min="$v.amount.$params.between.min"
name="Amount"
type="between"
/>
<tm-form-msg
v-else-if="balance === 0"
:msg="`doesn't hold any ${denom}s`"
v-if="balance === 0"
:msg="`doesn't have any ${denom}s`"
name="Wallet"
type="custom"
/>
<hr />
</tm-form-group>
<tm-form-group
class="action-modal-form-group"
field-id="password"
field-label="Password"
>
<tm-field
id="password"
v-model="password"
type="password"
placeholder="Password"
/>
<tm-form-msg
v-if="$v.password.$error && !$v.password.required"
name="Password"
v-else-if="$v.amount.$error && (!$v.amount.required || amount === 0)"
name="Amount"
type="required"
/>
</tm-form-group>
<div class="action-modal-footer">
<tm-btn
v-if="sending"
value="Sending..."
disabled="disabled"
color="primary"
/>
<tm-btn
v-else-if="!connected"
value="Connecting..."
disabled="disabled"
color="primary"
/>
<tm-btn
v-else
id="submit-deposit"
color="primary"
value="Submit Deposit"
@click.native="validateForm"
<tm-form-msg
v-else-if="$v.amount.$error && !$v.amount.between"
:max="$v.amount.$params.between.max"
:min="$v.amount.$params.between.min"
name="Amount"
type="between"
/>
</div>
</tm-form-group>
</action-modal>
</template>

<script>
import { mapGetters } from "vuex"
import ClickOutside from "vue-click-outside"
import { required, between } from "vuelidate/lib/validators"
import Modal from "common/TmModal"
import TmBtn from "common/TmBtn"
Expand All @@ -97,9 +60,6 @@ export default {
TmFormGroup,
TmFormMsg
},
directives: {
ClickOutside
},
props: {
proposalId: {
type: [Number, String],
Expand All @@ -115,12 +75,10 @@ export default {
}
},
data: () => ({
amount: 0,
password: ``,
sending: false
amount: 0
}),
computed: {
...mapGetters([`wallet`, `connected`]),
...mapGetters([`wallet`]),
balance() {
// TODO: refactor to get the selected coin when multicoin deposit is enabled
if (!this.wallet.loading && !!this.wallet.balances.length) {
Expand All @@ -138,48 +96,38 @@ export default {
required,
isInteger,
between: between(1, this.balance)
},
password: {
required
}
}
},
methods: {
close() {
this.$emit(`update:showModalDeposit`, false)
open() {
this.$refs.actionModal.open()
},
async validateForm() {
validateForm() {
jbibla marked this conversation as resolved.
Show resolved Hide resolved
this.$v.$touch()

if (!this.$v.$invalid) {
await this.submitForm()
}
return !this.$v.$invalid
},
async submitForm() {
this.sending = true

await this.$refs.actionModal.submit(async () => {
// TODO: support multiple coins
await this.$store.dispatch(`submitDeposit`, {
proposal_id: this.proposalId,
amount: [
{
amount: String(this.amount),
denom: this.denom
}
],
password: this.password
})

this.$store.commit(`notify`, {
title: `Successful deposit!`,
body: `You have successfully deposited your ${
this.denom
}s on proposal #${this.proposalId}`
})
}, `Depositing failed`)
async submitForm(submitType, password) {
// TODO: support multiple coins
await this.$store.dispatch(`submitDeposit`, {
submitType,
password,
proposal_id: this.proposalId,
amount: [
{
amount: String(this.amount),
denom: this.denom
}
]
})

this.sending = false
this.$store.commit(`notify`, {
title: `Successful deposit!`,
body: `You have successfully deposited your ${
this.denom
}s on proposal #${this.proposalId}`
})
}
}
}
Expand Down
Loading