Skip to content

Commit 7145a89

Browse files
authored
feat: Collaterals popup (#224)
1 parent b0970c5 commit 7145a89

14 files changed

+505
-16
lines changed

core/src/types.ts

+10
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,13 @@ export declare interface WalletBalances {
138138
walletVatDAI: BigNumber;
139139
walletLastUpdatedDate: Date;
140140
}
141+
142+
export declare interface CollateralStatus {
143+
type: string;
144+
symbol: string;
145+
isAuthorized: boolean;
146+
isAuthorizing: boolean;
147+
isDepositingOrWithdrawing: boolean;
148+
address?: string | null;
149+
balance?: BigNumber;
150+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { storiesOf } from '@storybook/vue';
2+
import BigNumber from 'bignumber.js';
3+
import ManageCollateralTable from './ManageCollateralTable';
4+
import { generateFakeCollateralStatuses } from '~/helpers/generateFakeCollateral';
5+
6+
const common = {
7+
components: { ManageCollateralTable },
8+
data: () => ({
9+
collateralStatuses: generateFakeCollateralStatuses(),
10+
}),
11+
methods: {
12+
authorize(collateralType) {
13+
const collateralStatus = this.collateralStatuses.find(c => c.type === collateralType);
14+
collateralStatus.isAuthorizing = true;
15+
setTimeout(() => {
16+
collateralStatus.isAuthorized = true;
17+
collateralStatus.isAuthorizing = false;
18+
}, 1000);
19+
},
20+
withdraw(collateralType) {
21+
const collateralStatus = this.collateralStatuses.find(c => c.type === collateralType);
22+
collateralStatus.isDepositingOrWithdrawing = true;
23+
setTimeout(() => {
24+
collateralStatus.balance = new BigNumber(0);
25+
collateralStatus.isDepositingOrWithdrawing = false;
26+
}, 1000);
27+
},
28+
},
29+
};
30+
31+
storiesOf('ManageCollateralTable', module).add('Default', () => ({
32+
...common,
33+
template: `<ManageCollateralTable
34+
:collateralStatuses="collateralStatuses"
35+
@authorizeCollateral="authorize"
36+
@withdrawCollateral="withdraw"
37+
/>`,
38+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<template>
2+
<TextBlock>
3+
<TextBlock v-if="isExplanationsShown" class="mb-2">
4+
This is a list of collaterals supported by the Maker Protocol. Each row provides the possibility to
5+
withdraw collateral from the VAT (if there is any) and to pre-authorize VAT transactions.
6+
</TextBlock>
7+
<table class="Table">
8+
<tr class="Heading">
9+
<th>Collateral</th>
10+
<th>Token</th>
11+
<th>Authorization</th>
12+
<th>Balance</th>
13+
</tr>
14+
<tbody>
15+
<tr v-for="collateralStatus of sortedCollaterals" :key="collateralStatus.type" class="Body">
16+
<td>
17+
<div class="flex items-center gap-2">
18+
<CurrencyIcon :currency-symbol="collateralStatus.symbol" />
19+
{{ collateralStatus.type }}
20+
</div>
21+
</td>
22+
<td>
23+
<span v-if="collateralStatus.address === undefined" class="opacity-50">Fetching</span>
24+
<span v-else-if="collateralStatus.address === null" class="opacity-50">Not found</span>
25+
<format-address v-else :value="collateralStatus.address" shorten type="address" />
26+
</td>
27+
<td>
28+
<template v-if="collateralStatus.address">
29+
<span v-if="collateralStatus.isAuthorized">Authorized</span>
30+
<BaseButton
31+
v-else
32+
class="w-full"
33+
:is-loading="collateralStatus.isAuthorizing"
34+
@click="$emit('authorizeCollateral', collateralStatus.type)"
35+
>
36+
Authorize
37+
</BaseButton>
38+
</template>
39+
</td>
40+
<td>
41+
<template v-if="collateralStatus.address">
42+
<Tooltip :title="generateWithdrawalErrorMessage(collateralStatus)" placement="top">
43+
<div>
44+
<BaseButton
45+
class="w-full"
46+
:disabled="!canWithdrawCollateral(collateralStatus)"
47+
:is-loading="collateralStatus.isDepositingOrWithdrawing"
48+
@click="$emit('withdrawCollateral', collateralStatus.type)"
49+
>
50+
<span class="mr-1">Withdraw</span
51+
><format-currency :value="collateralStatus.balance" />
52+
</BaseButton>
53+
</div>
54+
</Tooltip>
55+
</template>
56+
</td>
57+
</tr>
58+
</tbody>
59+
</table>
60+
</TextBlock>
61+
</template>
62+
63+
<script lang="ts">
64+
import type { CollateralStatus } from 'auction-core/src/types';
65+
import Vue from 'vue';
66+
import { Tooltip } from 'ant-design-vue';
67+
import TextBlock from '~/components/common/TextBlock.vue';
68+
import FormatAddress from '~/components/utils/FormatAddress.vue';
69+
import FormatCurrency from '~/components/utils/FormatCurrency.vue';
70+
import CurrencyIcon from '~/components/common/CurrencyIcon.vue';
71+
import BaseButton from '~/components/common/BaseButton.vue';
72+
73+
export default Vue.extend({
74+
components: {
75+
Tooltip,
76+
TextBlock,
77+
FormatAddress,
78+
FormatCurrency,
79+
CurrencyIcon,
80+
BaseButton,
81+
},
82+
props: {
83+
collateralStatuses: {
84+
type: Array as Vue.PropType<CollateralStatus[]>,
85+
default: () => [],
86+
},
87+
isExplanationsShown: {
88+
type: Boolean,
89+
default: true,
90+
},
91+
},
92+
computed: {
93+
sortedCollaterals() {
94+
const statuses = this.collateralStatuses;
95+
return statuses.sort((firstCollateral: CollateralStatus, secondCollateral: CollateralStatus) =>
96+
firstCollateral.type.localeCompare(secondCollateral.type)
97+
);
98+
},
99+
},
100+
methods: {
101+
canWithdrawCollateral(collateral: CollateralStatus): boolean {
102+
return collateral.balance?.isGreaterThan(0) && collateral.isAuthorized;
103+
},
104+
generateWithdrawalErrorMessage(collateral: CollateralStatus): string | undefined {
105+
if (!collateral.balance?.isGreaterThan(0)) {
106+
return `There is no ${collateral.type} collateral to withdraw`;
107+
}
108+
if (!collateral.isAuthorized) {
109+
return `The ${collateral.type} collateral needs to be authorized first`;
110+
}
111+
},
112+
},
113+
});
114+
</script>
115+
116+
<style scoped>
117+
.Table {
118+
@apply w-full border-2 border-gray-300 dark:border-gray-600 bg-transparent;
119+
}
120+
121+
.Element {
122+
@apply p-2 h-12 border-r-2 border-b-2 border-gray-300 dark:border-gray-600;
123+
}
124+
125+
.Heading > th {
126+
@apply Element text-gray-700 dark:text-gray-100;
127+
}
128+
129+
.Body > td {
130+
@apply Element text-gray-500 dark:text-gray-300;
131+
}
132+
</style>

frontend/components/layout/Header.vue

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
@changeWalletType="$emit('changeWalletType', $event)"
4545
@openWalletModal="$emit('openWalletModal')"
4646
@openTermsModal="$emit('openTermsModal')"
47+
@openManageCollateralModal="$emit('openManageCollateralModal')"
4748
/>
4849

4950
<ThemeSwitcher :dark-mode="darkMode" @update="$emit('update:darkMode', $event)" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { storiesOf } from '@storybook/vue';
2+
import BigNumber from 'bignumber.js';
3+
import ManageCollateralModal from './ManageCollateralModal';
4+
import { generateFakeCollateralStatuses } from '~/helpers/generateFakeCollateral';
5+
6+
const common = {
7+
components: { ManageCollateralModal },
8+
data: () => ({
9+
isShown: true,
10+
isExplanationsShown: true,
11+
collateralStatuses: generateFakeCollateralStatuses(),
12+
}),
13+
methods: {
14+
authorize(collateralType) {
15+
const collateralStatus = this.collateralStatuses.find(c => c.type === collateralType);
16+
collateralStatus.isAuthorizing = true;
17+
setTimeout(() => {
18+
collateralStatus.isAuthorized = true;
19+
collateralStatus.isAuthorizing = false;
20+
}, 1000);
21+
},
22+
withdraw(collateralType) {
23+
const collateralStatus = this.collateralStatuses.find(c => c.type === collateralType);
24+
collateralStatus.isDepositingOrWithdrawing = true;
25+
setTimeout(() => {
26+
collateralStatus.balance = new BigNumber(0);
27+
collateralStatus.isDepositingOrWithdrawing = false;
28+
}, 1000);
29+
},
30+
},
31+
template: `<ManageCollateralModal
32+
:isShown="isShown"
33+
:collateralStatuses="collateralStatuses"
34+
@authorizeCollateral="authorize"
35+
@withdrawCollateral="withdraw"
36+
/>`,
37+
};
38+
39+
storiesOf('Modals/ManageCollateralModal', module)
40+
.add('Default', () => ({
41+
...common,
42+
}))
43+
.add('Expert Mode', () => ({
44+
...common,
45+
data: () => ({
46+
...common.data(),
47+
isExplanationsShown: false,
48+
}),
49+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<modal
3+
:visible="isShown"
4+
:footer="null"
5+
:class="{ dark: isDarkMode }"
6+
title="Collaterals in VAT"
7+
:dialog-style="{ top: '60px' }"
8+
:width="620"
9+
destroy-on-close
10+
@cancel="$emit('cancel')"
11+
>
12+
<ManageCollateralTable
13+
class="p-4"
14+
:is-explanations-shown="isExplanationsShown"
15+
:collateral-statuses="collateralStatuses"
16+
@authorizeCollateral="$emit('authorizeCollateral', $event)"
17+
@withdrawCollateral="$emit('withdrawCollateral', $event)"
18+
/>
19+
</modal>
20+
</template>
21+
22+
<script lang="ts">
23+
import Vue from 'vue';
24+
import { Modal } from 'ant-design-vue';
25+
import ManageCollateralTable from '../ManageCollateralTable.vue';
26+
27+
export default Vue.extend({
28+
components: {
29+
Modal,
30+
ManageCollateralTable,
31+
},
32+
props: {
33+
isShown: {
34+
type: Boolean,
35+
default: false,
36+
},
37+
isDarkMode: {
38+
type: Boolean,
39+
default: false,
40+
},
41+
isExplanationsShown: {
42+
type: Boolean,
43+
default: true,
44+
},
45+
collateralStatuses: {
46+
type: Array as Vue.PropType<CollateralStatus[]>,
47+
default: () => [],
48+
},
49+
},
50+
});
51+
</script>

frontend/components/utils/WalletSelector.vue

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export default Vue.extend({
7373
menuOptions(): SelectOption[] {
7474
return [
7575
{ label: 'Manage Wallet', value: 'openWalletModal' },
76+
{ label: 'Manage Collaterals', value: 'openManageCollateralModal' },
7677
{ label: 'Disconnect', value: 'changeWalletType' },
7778
];
7879
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<template>
2+
<ManageCollateralModal
3+
:is-shown="isManageCollateralModalShown"
4+
:is-dark-mode="isDarkMode"
5+
:is-explanations-shown="isExplanationsShown"
6+
:collateral-statuses="collateralStatuses"
7+
@authorizeCollateral="authorize"
8+
@withdrawCollateral="withdraw"
9+
@cancel="setManageCollateralModal(false)"
10+
/>
11+
</template>
12+
13+
<script lang="ts">
14+
import Vue from 'vue';
15+
import { mapActions, mapGetters } from 'vuex';
16+
import ManageCollateralModal from '~/components/modals/ManageCollateralModal.vue';
17+
18+
export default Vue.extend({
19+
name: 'ManageCollateralModalContainer',
20+
components: { ManageCollateralModal },
21+
computed: {
22+
...mapGetters('collaterals', {
23+
collateralStatuses: 'collateralStatuses',
24+
}),
25+
...mapGetters('modals', {
26+
isManageCollateralModalShown: 'getManageCollateralModal',
27+
}),
28+
isExplanationsShown: {
29+
get() {
30+
return this.$store.getters['preferences/getIsExplanationsShown'];
31+
},
32+
set(newIsExplanationsShown) {
33+
this.$store.dispatch('preferences/setExplanationsAction', newIsExplanationsShown);
34+
},
35+
},
36+
isDarkMode: {
37+
get(): Boolean {
38+
return this.$store.getters['preferences/getIsDarkMode'];
39+
},
40+
set(newIsDarkMode) {
41+
this.$store.dispatch('preferences/setIsDarkMode', newIsDarkMode);
42+
},
43+
},
44+
},
45+
watch: {
46+
isManageCollateralModalShown: {
47+
immediate: true,
48+
handler(isShown) {
49+
if (isShown) {
50+
this.$store.dispatch('collaterals/fetchCollateralStatuses');
51+
}
52+
},
53+
},
54+
},
55+
methods: {
56+
...mapActions('wallet', ['withdrawAllCollateralFromVat']),
57+
setManageCollateralModal(open: boolean): void {
58+
this.$store.commit('modals/setManageCollateralModal', open);
59+
},
60+
async authorize(collateralType: string) {
61+
await this.$store.dispatch('authorizations/authorizeCollateral', collateralType);
62+
},
63+
async withdraw(collateralType: string) {
64+
await this.$store.dispatch('wallet/withdrawAllCollateralFromVat', collateralType);
65+
},
66+
},
67+
});
68+
</script>

0 commit comments

Comments
 (0)