diff --git a/.circleci/config.yml b/.circleci/config.yml
index 506f4ab8bb..3d85ad2c73 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -15,15 +15,15 @@ commands:
steps:
- restore_cache:
keys:
- - v5-dependencies-root-{{ checksum "yarn.lock" }}
- - v5-dependencies-root-
+ - v6-dependencies-root-{{ checksum "yarn.lock" }}
+ - v6-dependencies-root-
- run: yarn install
- save_cache:
paths:
- yarn.lock
- node_modules
- key: v5-dependencies-root-{{ checksum "yarn.lock" }}
+ key: v6-dependencies-root-{{ checksum "yarn.lock" }}
# use npm as the extension install happens in the mashine image which doesn't have yarn
npm-install-extension:
diff --git a/changes/ana_emoney-sendmodal-multiple-denoms b/changes/ana_emoney-sendmodal-multiple-denoms
new file mode 100644
index 0000000000..a851e5f0cc
--- /dev/null
+++ b/changes/ana_emoney-sendmodal-multiple-denoms
@@ -0,0 +1 @@
+[Added] [#3370](https://github.com/cosmos/lunie/pull/3370) Now it is possible to select different tokens to send in the SendModal for networks with multiple tokens @Bitcoinera
\ No newline at end of file
diff --git a/package.json b/package.json
index 5f9e208775..3bef70e9ae 100644
--- a/package.json
+++ b/package.json
@@ -119,6 +119,7 @@
"stylelint": "12.0.1",
"stylelint-config-standard": "19.0.0",
"stylelint-webpack-plugin": "1.1.2",
+ "vue-jest": "3.0.5",
"vue-template-compiler": "^2.6.10",
"webpack": "4.41.5"
},
@@ -140,4 +141,4 @@
"type": "git",
"url": "git+https://github.com/luniehq/lunie.git"
}
-}
\ No newline at end of file
+}
diff --git a/src/ActionModal/components/SendModal.vue b/src/ActionModal/components/SendModal.vue
index d66e73f304..773c3fd3b1 100644
--- a/src/ActionModal/components/SendModal.vue
+++ b/src/ActionModal/components/SendModal.vue
@@ -38,12 +38,36 @@
/>
+
+
+
+
- {{ denom }}
+ {{
+ selectedToken
+ }}
@@ -149,8 +173,8 @@ export default {
TmBtn
},
props: {
- denom: {
- type: String,
+ denoms: {
+ type: Array,
required: true
}
},
@@ -160,22 +184,29 @@ export default {
memo: defaultMemo,
max_memo_characters: 256,
editMemo: false,
- balance: {
- amount: null,
- denom: ``
- }
+ isFirstLoad: true,
+ selectedToken: ``,
+ selectedBalance: ``,
+ balances: [
+ {
+ amount: null,
+ denom: ``
+ }
+ ]
}),
computed: {
...mapGetters([`network`]),
...mapGetters({ userAddress: `address` }),
transactionData() {
+ // This is the best place I have found so far to call this function
+ this.setTokenAndBalance()
return {
type: transaction.SEND,
toAddress: this.address,
amounts: [
{
amount: uatoms(+this.amount),
- denom: toMicroDenom(this.denom)
+ denom: toMicroDenom(this.selectedToken)
}
],
memo: this.memo
@@ -184,10 +215,24 @@ export default {
notifyMessage() {
return {
title: `Successful Send`,
- body: `Successfully sent ${+this.amount} ${this.denom}s to ${
+ body: `Successfully sent ${+this.amount} ${this.selectedToken}s to ${
this.address
}`
}
+ },
+ getDenoms() {
+ return this.denoms.map(denom => (denom = { key: denom, value: denom }))
+ }
+ },
+ watch: {
+ // we set the amount in the input to zero every time the user selects another token so they
+ // realize they are dealing with a different balance each time
+ selectedToken: function() {
+ if (!this.isFirstLoad) {
+ this.amount = 0
+ } else {
+ this.isFirstLoad = false
+ }
}
},
mounted() {
@@ -215,15 +260,36 @@ export default {
this.sending = false
},
setMaxAmount() {
- this.amount = this.balance.amount
+ this.amount = this.selectedBalance.amount
},
isMaxAmount() {
- if (this.balance.amount === 0) {
+ if (this.selectedBalance.amount === 0) {
return false
} else {
- return parseFloat(this.amount) === parseFloat(this.balance.amount)
+ return (
+ parseFloat(this.amount) === parseFloat(this.selectedBalance.amount)
+ )
}
},
+ setTokenAndBalance() {
+ // if it is single-token network, then we take the first and only value from the
+ // balances array
+ if (this.balances.length === 1) {
+ this.selectedToken = this.getDenoms[0].value
+ this.selectedBalance = this.balances[0]
+ // if it is a multiple-tokens network and we already have a selectedToken by the user
+ // then we search for the corresponding balance from the array
+ } else if (this.selectedToken) {
+ this.selectedBalance = this.balances.filter(
+ balance => balance.denom === this.selectedToken
+ )[0]
+ }
+ },
+ token() {
+ if (!this.selectedToken) return ``
+
+ return this.selectedToken
+ },
bech32Validate(param) {
try {
b32.decode(param)
@@ -252,23 +318,20 @@ export default {
amount: {
required: x => !!x && x !== `0`,
decimal,
- between: between(SMALLEST, this.balance.amount)
+ between: between(SMALLEST, this.selectedBalance.amount)
},
- denom: { required },
+ denoms: { required },
+ selectedToken: { required },
memo: {
maxLength: maxLength(this.max_memo_characters)
}
}
},
apollo: {
- balance: {
+ balances: {
query: gql`
- query BalanceSendModal(
- $networkId: String!
- $address: String!
- $denom: String!
- ) {
- balance(networkId: $networkId, address: $address, denom: $denom) {
+ query BalancesSendModal($networkId: String!, $address: String!) {
+ balances(networkId: $networkId, address: $address) {
amount
denom
}
@@ -280,8 +343,7 @@ export default {
variables() {
return {
networkId: this.network,
- address: this.userAddress,
- denom: this.denom
+ address: this.userAddress
}
}
},
@@ -314,4 +376,8 @@ export default {
font-size: 12px;
cursor: pointer;
}
+
+#form-group-amount {
+ margin-bottom: 30px;
+}
diff --git a/src/components/common/TmBalance.vue b/src/components/common/TmBalance.vue
index 57000d20e3..9ccaf6a2da 100644
--- a/src/components/common/TmBalance.vue
+++ b/src/components/common/TmBalance.vue
@@ -46,7 +46,7 @@
/>
-
+
@@ -82,6 +82,14 @@ export default {
// the validator rewards are needed to filter the top 5 validators to withdraw from
readyToWithdraw() {
return this.overview.totalRewards > 0
+ },
+ getAllDenoms() {
+ if (this.overview.balances) {
+ const balances = this.overview.balances
+ return balances.map(({ denom }) => denom)
+ } else {
+ return [this.stakingDenom]
+ }
}
},
methods: {
@@ -99,6 +107,10 @@ export default {
overview(networkId: $networkId, address: $address) {
totalRewards
liquidStake
+ balances {
+ denom
+ amount
+ }
totalStake
}
}
diff --git a/tests/unit/specs/components/ActionModal/components/SendModal.spec.js b/tests/unit/specs/components/ActionModal/components/SendModal.spec.js
index c7b85d8368..d270b8b03d 100644
--- a/tests/unit/specs/components/ActionModal/components/SendModal.spec.js
+++ b/tests/unit/specs/components/ActionModal/components/SendModal.spec.js
@@ -37,16 +37,18 @@ describe(`SendModal`, () => {
}
},
propsData: {
- denom: "STAKE"
+ denoms: ["STAKE"]
},
sync: false
})
wrapper.setData({
- balance: {
- denom: `STAKE`,
- amount: 10000
- }
+ balances: [
+ {
+ denom: `STAKE`,
+ amount: 10000
+ }
+ ]
})
wrapper.vm.$refs.actionModal = {
@@ -211,10 +213,12 @@ describe(`SendModal`, () => {
})
it(`should not show warning message if balance = 0`, async () => {
wrapper.setData({
- balance: {
- amount: 0,
- denom: "STAKE"
- }
+ balances: [
+ {
+ amount: 0,
+ denom: "STAKE"
+ }
+ ]
})
wrapper.vm.setMaxAmount()
await wrapper.vm.$nextTick()
@@ -224,14 +228,49 @@ describe(`SendModal`, () => {
})
it(`isMaxAmount() should return false if balance = 0`, async () => {
wrapper.setData({
- balance: {
- amount: 0,
- denom: "STAKE"
- }
+ balances: [
+ {
+ amount: 0,
+ denom: "STAKE"
+ }
+ ]
})
wrapper.vm.setMaxAmount()
await wrapper.vm.$nextTick()
expect(wrapper.vm.isMaxAmount()).toBe(false)
})
})
+
+ describe(`Set token and balance`, () => {
+ it(`it takes the corresponding balance from the balances array when
+ selectedToken has been chosen and balances has a length over 1`, async () => {
+ wrapper.setData({
+ selectedToken: `TOKEN1`,
+ balances: [
+ {
+ amount: 1,
+ denom: "TOKEN1"
+ },
+ {
+ amount: 2,
+ denom: "TOKEN2"
+ }
+ ]
+ })
+ wrapper.vm.setTokenAndBalance()
+ expect(wrapper.vm.selectedBalance.amount).toBe(1)
+ })
+ // This one creates a lot of ugly errors
+ // it(`returns empty string if selectedToken hasn't been chosen yet`, () => {
+ // wrapper.setData({
+ // balances: []
+ // })
+ // const res = wrapper.vm.token()
+ // expect(res).toBe("")
+ // })
+ it(`returns selectedToken if selectedToken has been chosen`, () => {
+ const res = wrapper.vm.token()
+ expect(res).toBe("STAKE")
+ })
+ })
})
diff --git a/tests/unit/specs/components/ActionModal/components/__snapshots__/SendModal.spec.js.snap b/tests/unit/specs/components/ActionModal/components/__snapshots__/SendModal.spec.js.snap
index 93aa332f8e..38a2899be1 100644
--- a/tests/unit/specs/components/ActionModal/components/__snapshots__/SendModal.spec.js.snap
+++ b/tests/unit/specs/components/ActionModal/components/__snapshots__/SendModal.spec.js.snap
@@ -26,10 +26,13 @@ exports[`SendModal should display send modal form 1`] = `
+
+
+
+
+
+
+
+
+
+
@@ -129,7 +129,7 @@ exports[`TmBalance show the balance header when signed in 1`] = `
diff --git a/yarn.lock b/yarn.lock
index 150fadc59e..43d36d2876 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12632,7 +12632,7 @@ vue-infinite-scroll@^2.0.2:
resolved "https://registry.yarnpkg.com/vue-infinite-scroll/-/vue-infinite-scroll-2.0.2.tgz#ca37a91fe92ee0ad3b74acf8682c00917144b711"
integrity sha512-n+YghR059YmciANGJh9SsNWRi1YZEBVlODtmnb/12zI+4R72QZSWd+EuZ5mW6auEo/yaJXgxzwsuhvALVnm73A==
-vue-jest@^3.0.5:
+vue-jest@3.0.5, vue-jest@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-3.0.5.tgz#d6f124b542dcbff207bf9296c19413f4c40b70c9"
integrity sha512-xWDxde91pDqYBGDlODENZ3ezPgw+IQFoVDtf+5Awlg466w3KvMSqWzs8PxcTeTr+wmAHi0j+a+Lm3R7aUJa1jA==