TC-98 | Add ProposalComments component with tab filters
This commit is contained in:
parent
3cef4c4d5b
commit
6e01a677ae
14 changed files with 876 additions and 336 deletions
|
@ -52,11 +52,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<b-button v-if="support" type="is-primary" icon-left="check" outlined @click="onCastVote(true)">
|
||||
<b-button
|
||||
v-if="support"
|
||||
:disabled="!isValid"
|
||||
type="is-primary"
|
||||
icon-left="check"
|
||||
outlined
|
||||
@click="onCastVote(true)"
|
||||
>
|
||||
{{ $t('for') }}
|
||||
</b-button>
|
||||
|
||||
<b-button v-else type="is-danger" icon-left="close" outlined @click="onCastVote(false)">
|
||||
<b-button
|
||||
v-else
|
||||
:disabled="!isValid"
|
||||
type="is-danger"
|
||||
icon-left="close"
|
||||
outlined
|
||||
@click="onCastVote(false)"
|
||||
>
|
||||
{{ $t('against') }}
|
||||
</b-button>
|
||||
</div>
|
||||
|
@ -88,6 +102,11 @@ export default {
|
|||
message: ''
|
||||
}
|
||||
}),
|
||||
computed: {
|
||||
isValid() {
|
||||
return this.validate()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validate() {
|
||||
const { form, fields, support } = this
|
||||
|
@ -100,9 +119,7 @@ export default {
|
|||
return fields.contact && fields.message
|
||||
},
|
||||
onCastVote() {
|
||||
const isValid = this.validate()
|
||||
|
||||
if (isValid) {
|
||||
if (this.isValid) {
|
||||
this.$emit('castVote', this.form)
|
||||
this.$emit('close')
|
||||
}
|
||||
|
|
|
@ -4,158 +4,159 @@
|
|||
<div class="column is-7-tablet is-8-desktop">
|
||||
<h1 class="title">{{ data.title }}</h1>
|
||||
<div class="description">
|
||||
<p>{{ data.description }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ProposalCommentsSkeleton
|
||||
v-if="isFetchingProposalComments"
|
||||
:size="proposalComments.length ? 1 : 3"
|
||||
/>
|
||||
<ProposalComment v-for="item in proposalComments" :key="item.id" v-bind="item" />
|
||||
<p class="proposal--description">{{ data.description }}</p>
|
||||
</div>
|
||||
<ProposalComments
|
||||
v-if="isEnabledGovernance && quorumPercent"
|
||||
:proposal="data"
|
||||
:is-initialized="isInitialized"
|
||||
class="proposal--comments"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-5-tablet is-4-desktop">
|
||||
<div v-if="data.status === 'active'" class="proposal-block">
|
||||
<div class="title">{{ $t('castYourVote') }}</div>
|
||||
<b-tooltip
|
||||
class="fit-content"
|
||||
:label="tooltipMessage"
|
||||
position="is-top"
|
||||
:active="readyForAction"
|
||||
multilined
|
||||
>
|
||||
<div class="buttons buttons__halfwidth">
|
||||
<b-button
|
||||
:disabled="readyForAction"
|
||||
type="is-primary"
|
||||
:icon-left="isFetchingBalances || isSaveProposal ? '' : 'check'"
|
||||
outlined
|
||||
:loading="isFetchingBalances || isSaveProposal"
|
||||
@click="onCastVote(true)"
|
||||
>{{ $t('for') }}</b-button
|
||||
>
|
||||
<b-button
|
||||
:disabled="readyForAction"
|
||||
type="is-danger"
|
||||
:icon-left="isFetchingBalances || isSaveProposal ? '' : 'close'"
|
||||
outlined
|
||||
:loading="isFetchingBalances || isSaveProposal"
|
||||
@click="onCastVote(false)"
|
||||
>{{ $t('against') }}</b-button
|
||||
>
|
||||
</div>
|
||||
</b-tooltip>
|
||||
<i18n
|
||||
v-if="voterReceipts[data.id] && voterReceipts[data.id].hasVoted"
|
||||
tag="div"
|
||||
path="yourCurrentVote"
|
||||
>
|
||||
<template v-slot:vote>
|
||||
<span
|
||||
:class="{
|
||||
'has-text-primary': voterReceipts[data.id].support,
|
||||
'has-text-danger': !voterReceipts[data.id].support
|
||||
}"
|
||||
>{{ $n(fromWeiToTorn(voterReceipts[data.id].balance)) }} TORN</span
|
||||
>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<div v-else-if="data.status === 'awaitingExecution'" class="proposal-block">
|
||||
<div class="title">{{ $t('executeProposal') }}</div>
|
||||
<b-tooltip
|
||||
class="fit-content"
|
||||
:label="$t('connectYourWalletFirst')"
|
||||
position="is-top"
|
||||
:active="!ethAccount"
|
||||
multilined
|
||||
>
|
||||
<b-button
|
||||
type="is-primary"
|
||||
icon-left="check"
|
||||
outlined
|
||||
:disabled="!ethAccount"
|
||||
expanded
|
||||
@click="onExecute"
|
||||
>{{ $t('execute') }}</b-button
|
||||
>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
<div class="proposal-block">
|
||||
<div class="title">{{ $t('currentResults') }}</div>
|
||||
<div class="label">
|
||||
{{ $t('for') }}
|
||||
<span class="percent"
|
||||
><number-format :value="data.results.for" /> TORN / {{ calculatePercent('for') }}%</span
|
||||
>
|
||||
</div>
|
||||
<b-progress :value="calculatePercent('for')" type="is-primary"></b-progress>
|
||||
<div class="label">
|
||||
{{ $t('against') }}
|
||||
<span class="percent"
|
||||
><number-format :value="data.results.against" class="value" /> TORN /
|
||||
{{ calculatePercent('against') }}%</span
|
||||
>
|
||||
</div>
|
||||
<b-progress :value="calculatePercent('against')" type="is-danger"></b-progress>
|
||||
<div class="label">
|
||||
{{ $t('quorum') }}
|
||||
<div class="proposal--blocks">
|
||||
<div v-if="data.status === 'active'" class="proposal-block">
|
||||
<div class="title">{{ $t('castYourVote') }}</div>
|
||||
<b-tooltip
|
||||
:label="
|
||||
$t('quorumTooltip', {
|
||||
days: $tc('dayPlural', votingPeriod),
|
||||
votes: $n(quorumVotes, 'compact')
|
||||
})
|
||||
"
|
||||
size="is-medium"
|
||||
class="fit-content"
|
||||
:label="tooltipMessage"
|
||||
position="is-top"
|
||||
:active="readyForAction"
|
||||
multilined
|
||||
>
|
||||
<button class="button is-primary has-icon">
|
||||
<span class="icon icon-info"></span>
|
||||
</button>
|
||||
<div class="buttons buttons__halfwidth">
|
||||
<b-button
|
||||
:disabled="readyForAction"
|
||||
type="is-primary"
|
||||
:icon-left="isFetchingBalances || isCastingVote ? '' : 'check'"
|
||||
outlined
|
||||
:loading="isFetchingBalances || isCastingVote"
|
||||
@click="onCastVote(true)"
|
||||
>{{ $t('for') }}</b-button
|
||||
>
|
||||
<b-button
|
||||
:disabled="readyForAction"
|
||||
type="is-danger"
|
||||
:icon-left="isFetchingBalances || isCastingVote ? '' : 'close'"
|
||||
outlined
|
||||
:loading="isFetchingBalances || isCastingVote"
|
||||
@click="onCastVote(false)"
|
||||
>{{ $t('against') }}</b-button
|
||||
>
|
||||
</div>
|
||||
</b-tooltip>
|
||||
<span class="percent"
|
||||
><number-format :value="isQuorumCompleted ? quorumVotes : quorumResult" class="value" /> TORN /
|
||||
{{ quorumPercent }}%</span
|
||||
<i18n
|
||||
v-if="voterReceipts[data.id] && voterReceipts[data.id].hasVoted"
|
||||
tag="div"
|
||||
path="yourCurrentVote"
|
||||
>
|
||||
<template v-slot:vote>
|
||||
<span
|
||||
:class="{
|
||||
'has-text-primary': voterReceipts[data.id].support,
|
||||
'has-text-danger': !voterReceipts[data.id].support
|
||||
}"
|
||||
>{{ $n(fromWeiToTorn(voterReceipts[data.id].balance)) }} TORN</span
|
||||
>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<b-progress :value="quorumPercent" type="is-violet"></b-progress>
|
||||
</div>
|
||||
<div class="proposal-block">
|
||||
<div class="title">{{ $t('information') }}</div>
|
||||
<div class="columns is-multiline is-small" :class="{ 'has-countdown': countdown }">
|
||||
<div class="column is-full-small">
|
||||
<strong>{{ $t('proposalAddress') }}</strong>
|
||||
<div class="value">
|
||||
<a :href="contractUrl" class="address" target="_blank" rel="noopener noreferrer">
|
||||
{{ data.target }}
|
||||
</a>
|
||||
<div v-else-if="data.status === 'awaitingExecution'" class="proposal-block">
|
||||
<div class="title">{{ $t('executeProposal') }}</div>
|
||||
<b-tooltip
|
||||
class="fit-content"
|
||||
:label="$t('connectYourWalletFirst')"
|
||||
position="is-top"
|
||||
:active="!ethAccount"
|
||||
multilined
|
||||
>
|
||||
<b-button
|
||||
type="is-primary"
|
||||
icon-left="check"
|
||||
outlined
|
||||
:disabled="!ethAccount"
|
||||
expanded
|
||||
@click="onExecute"
|
||||
>{{ $t('execute') }}</b-button
|
||||
>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="proposal-block">
|
||||
<div class="title">{{ $t('currentResults') }}</div>
|
||||
<div class="label">
|
||||
{{ $t('for') }}
|
||||
<span class="percent"
|
||||
><number-format :value="data.results.for" /> TORN / {{ calculatePercent('for') }}%</span
|
||||
>
|
||||
</div>
|
||||
<b-progress :value="calculatePercent('for')" type="is-primary"></b-progress>
|
||||
<div class="label">
|
||||
{{ $t('against') }}
|
||||
<span class="percent"
|
||||
><number-format :value="data.results.against" class="value" /> TORN /
|
||||
{{ calculatePercent('against') }}%</span
|
||||
>
|
||||
</div>
|
||||
<b-progress :value="calculatePercent('against')" type="is-danger"></b-progress>
|
||||
<div class="label">
|
||||
{{ $t('quorum') }}
|
||||
<b-tooltip
|
||||
:label="
|
||||
$t('quorumTooltip', {
|
||||
days: $tc('dayPlural', votingPeriod),
|
||||
votes: $n(quorumVotes, 'compact')
|
||||
})
|
||||
"
|
||||
size="is-medium"
|
||||
position="is-top"
|
||||
multilined
|
||||
>
|
||||
<button class="button is-primary has-icon">
|
||||
<span class="icon icon-info"></span>
|
||||
</button>
|
||||
</b-tooltip>
|
||||
<span class="percent"
|
||||
><number-format :value="isQuorumCompleted ? quorumVotes : quorumResult" class="value" /> TORN
|
||||
/ {{ quorumPercent }}%</span
|
||||
>
|
||||
</div>
|
||||
<b-progress :value="quorumPercent" type="is-violet"></b-progress>
|
||||
</div>
|
||||
<div class="proposal-block">
|
||||
<div class="title">{{ $t('information') }}</div>
|
||||
<div class="columns is-multiline is-small" :class="{ 'has-countdown': countdown }">
|
||||
<div class="column is-full-small">
|
||||
<strong>{{ $t('proposalAddress') }}</strong>
|
||||
<div class="value">
|
||||
<a :href="contractUrl" class="address" target="_blank" rel="noopener noreferrer">
|
||||
{{ data.target }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('id') }}</strong>
|
||||
<div class="value">{{ data.id }}</div>
|
||||
</div>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('status') }}</strong>
|
||||
<div class="value">
|
||||
<b-tag :type="getStatusType(data.status)">{{ $t(data.status) }}</b-tag>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('id') }}</strong>
|
||||
<div class="value">{{ data.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('startDate') }}</strong>
|
||||
<div class="value">{{ $moment.unix(data.startTime).format('llll') }}</div>
|
||||
</div>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('endDate') }}</strong>
|
||||
<div class="value">{{ $moment.unix(data.endTime).format('llll') }}</div>
|
||||
</div>
|
||||
<div v-if="countdown" class="column is-full-small">
|
||||
<strong>{{ $t(timerLabel) }}</strong>
|
||||
<div class="value">
|
||||
{{ countdown }}
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('status') }}</strong>
|
||||
<div class="value">
|
||||
<b-tag :type="getStatusType(data.status)">{{ $t(data.status) }}</b-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('startDate') }}</strong>
|
||||
<div class="value">{{ $moment.unix(data.startTime).format('llll') }}</div>
|
||||
</div>
|
||||
<div class="column is-half-small">
|
||||
<strong>{{ $t('endDate') }}</strong>
|
||||
<div class="value">{{ $moment.unix(data.endTime).format('llll') }}</div>
|
||||
</div>
|
||||
<div v-if="countdown" class="column is-full-small">
|
||||
<strong>{{ $t(timerLabel) }}</strong>
|
||||
<div class="value">
|
||||
{{ countdown }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -168,8 +169,7 @@
|
|||
<script>
|
||||
import { mapState, mapActions, mapGetters } from 'vuex'
|
||||
import quorum from './mixins/quorum'
|
||||
import ProposalCommentsSkeleton from './ProposalCommentsSkeleton.vue'
|
||||
import ProposalComment from './ProposalComment.vue'
|
||||
import ProposalComments from './ProposalComments.vue'
|
||||
import NumberFormat from '@/components/NumberFormat'
|
||||
import ProposalCommentFormModal from '@/components/ProposalCommentFormModal.vue'
|
||||
|
||||
|
@ -177,8 +177,7 @@ const { toBN, fromWei, toWei } = require('web3-utils')
|
|||
|
||||
export default {
|
||||
components: {
|
||||
ProposalCommentsSkeleton,
|
||||
ProposalComment,
|
||||
ProposalComments,
|
||||
NumberFormat
|
||||
},
|
||||
mixins: [quorum],
|
||||
|
@ -196,7 +195,7 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('governance/gov', ['proposals', 'voterReceipts', 'proposalComments', 'isSaveProposal']),
|
||||
...mapState('governance/gov', ['proposals', 'voterReceipts', 'isCastingVote']),
|
||||
...mapState('metamask', ['ethAccount', 'isInitialized']),
|
||||
...mapGetters('txHashKeeper', ['addressExplorerUrl']),
|
||||
...mapGetters('metamask', ['networkConfig']),
|
||||
|
@ -205,7 +204,6 @@ export default {
|
|||
'constants',
|
||||
'votingPeriod',
|
||||
'isFetchingBalances',
|
||||
'isFetchingProposalComments',
|
||||
'isEnabledGovernance'
|
||||
]),
|
||||
readyForAction() {
|
||||
|
@ -239,9 +237,7 @@ export default {
|
|||
isInitialized: {
|
||||
handler(isInitialized) {
|
||||
if (isInitialized && this.isEnabledGovernance) {
|
||||
const { id } = this.data
|
||||
this.fetchReceipt({ id })
|
||||
this.fetchProposalComments(this.data)
|
||||
this.fetchReceipt({ id: this.data.id })
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
|
@ -282,13 +278,7 @@ export default {
|
|||
clearTimeout(this.timeId)
|
||||
},
|
||||
methods: {
|
||||
...mapActions('governance/gov', [
|
||||
'castVote',
|
||||
'executeProposal',
|
||||
'fetchReceipt',
|
||||
'fetchProposals',
|
||||
'fetchProposalComments'
|
||||
]),
|
||||
...mapActions('governance/gov', ['castVote', 'executeProposal', 'fetchReceipt', 'fetchProposals']),
|
||||
getStatusType(status) {
|
||||
let statusType = ''
|
||||
switch (status) {
|
||||
|
@ -393,3 +383,20 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.proposal {
|
||||
&--description {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&--comments {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
&--blocks {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,39 +2,71 @@
|
|||
<div class="proposals-box">
|
||||
<div class="columns is-gapless">
|
||||
<div class="column proposals-box--tags">
|
||||
<div
|
||||
class="tag"
|
||||
:class="{
|
||||
'proposals-box--revote': revote,
|
||||
'is-primary': support,
|
||||
'is-danger': !support
|
||||
}"
|
||||
>
|
||||
<span><number-format :value="votes" /> TORN</span>
|
||||
<div class="proposals-box--tag-item">
|
||||
<div class="tag proposals-box--id">
|
||||
<span><number-format :value="votes" /> TORN</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-tooltip v-if="delegator" :label="delegator" position="is-top">
|
||||
<div class="tag proposals-box--id">{{ $t('delegated') }}</div>
|
||||
</b-tooltip>
|
||||
<div class="proposals-box--tag-item">
|
||||
<b-tooltip
|
||||
:label="ens.voter || voter"
|
||||
position="is-top"
|
||||
:multilined="ens.voter && ens.voter.length > 50"
|
||||
>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="addressExplorerUrl(voter)"
|
||||
rel="noopener noreferrer"
|
||||
class="tag proposals-box--id is-link"
|
||||
v-text="shortVoter"
|
||||
/>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
|
||||
<b-tooltip :label="voter" position="is-top">
|
||||
<div class="tag proposals-box--id">{{ shortVoter }}</div>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
<div class="column is-narrow proposals-box--date">
|
||||
<div class="date">
|
||||
<span>{{ $t('date') }}:</span> {{ date }}
|
||||
<div v-if="delegator" class="proposals-box--tag-item">
|
||||
<b-tooltip
|
||||
:label="ens.delegator || delegator"
|
||||
position="is-top"
|
||||
:multilined="ens.delegator && ens.delegator.length > 50"
|
||||
>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="addressExplorerUrl(delegator)"
|
||||
rel="noopener noreferrer"
|
||||
class="tag proposals-box--id is-link"
|
||||
v-text="$t('delegate')"
|
||||
/>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="proposals-box--tag-item is-percentage">
|
||||
<div class="tag proposals-box--id is-percentage">{{ percentage || '~0.1' }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span v-if="contact" class="proposals-box--title">{{ contact }}</span>
|
||||
<div v-if="message" class="proposals-box--info" v-text="message" />
|
||||
<div class="proposals-box--comment">
|
||||
<b-icon
|
||||
:icon="support ? 'check' : 'close'"
|
||||
:type="support ? 'is-primary' : 'is-danger'"
|
||||
class="proposals-box--status-icon"
|
||||
/>
|
||||
<span v-if="loading" class="proposals-box--skeleton">
|
||||
<b-skeleton height="21" width="260" style="width: auto;" />
|
||||
</span>
|
||||
<template v-else>
|
||||
<span v-if="contact" class="proposals-box--title">{{ contact }}</span>
|
||||
<span v-if="message" class="proposals-box--info">{{ message }}</span>
|
||||
<span v-if="!contact && !message">-</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { sliceAddress } from '@/utils'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { sliceAddress, sliceEnsName } from '@/utils'
|
||||
import NumberFormat from '@/components/NumberFormat'
|
||||
|
||||
export default {
|
||||
|
@ -43,6 +75,10 @@ export default {
|
|||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
contact: {
|
||||
type: String,
|
||||
required: true
|
||||
|
@ -55,10 +91,6 @@ export default {
|
|||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
timestamp: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
votes: {
|
||||
type: String,
|
||||
required: true
|
||||
|
@ -67,56 +99,105 @@ export default {
|
|||
type: String,
|
||||
required: true
|
||||
},
|
||||
revote: {
|
||||
type: Boolean,
|
||||
percentage: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
delegator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
ens: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator: (props) => 'delegator' in props && 'voter' in props
|
||||
}
|
||||
},
|
||||
data: (vm) => ({
|
||||
shortVoter: sliceAddress(vm.voter),
|
||||
date: [vm.$moment.unix(vm.timestamp).format('l'), vm.$moment.unix(vm.timestamp).format('hh:mm')].join(' ')
|
||||
})
|
||||
computed: {
|
||||
...mapGetters('txHashKeeper', ['addressExplorerUrl']),
|
||||
|
||||
shortVoter() {
|
||||
return sliceEnsName(this.ens.voter || '') || sliceAddress(this.voter)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$margin: 0.714rem;
|
||||
|
||||
.proposals-box {
|
||||
cursor: default;
|
||||
|
||||
.tag {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
|
||||
&.is-link {
|
||||
text-decoration: none;
|
||||
background-color: #363636;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(#363636, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-percentage {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.columns {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&--tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, auto));
|
||||
display: grid;
|
||||
grid-row-gap: 0.714rem;
|
||||
grid-column-gap: 0.714rem;
|
||||
margin: calc(#{-$margin * 0.5}) !important;
|
||||
}
|
||||
|
||||
&--date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
&--tag-item {
|
||||
margin: calc(#{$margin * 0.5});
|
||||
width: auto;
|
||||
min-width: 110px;
|
||||
|
||||
&--title {
|
||||
display: flex;
|
||||
@media screen and (max-width: 600px) {
|
||||
width: calc(50% - #{$margin});
|
||||
}
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.is-percentage {
|
||||
min-width: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&--title,
|
||||
&--info {
|
||||
word-break: break-word;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
&--revote {
|
||||
text-decoration: line-through;
|
||||
&--status-icon {
|
||||
vertical-align: middle;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
&--comment {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
&--skeleton {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
91
components/governance/ProposalComments.vue
Normal file
91
components/governance/ProposalComments.vue
Normal file
|
@ -0,0 +1,91 @@
|
|||
<template>
|
||||
<div class="proposal-comments">
|
||||
<div class="proposals-list--header proposal-comments--header">
|
||||
<b-field class="field-tabs">
|
||||
<template v-for="item in tabs">
|
||||
<b-radio-button :key="item.id" v-model="currentTab" :native-value="item.id" type="is-primary">
|
||||
<span>{{ $t(item.label) }}</span>
|
||||
</b-radio-button>
|
||||
</template>
|
||||
</b-field>
|
||||
</div>
|
||||
|
||||
<ProposalCommentsSkeleton v-if="isFetchingComments" :size="comments.length ? 1 : 3" />
|
||||
<ProposalComment
|
||||
v-for="item in commentsFiltered"
|
||||
:key="item.id"
|
||||
v-bind="item"
|
||||
:loading="isFetchingMessages"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters, mapState } from 'vuex'
|
||||
import ProposalCommentsSkeleton from './ProposalCommentsSkeleton.vue'
|
||||
import ProposalComment from './ProposalComment.vue'
|
||||
|
||||
const TabTypes = {
|
||||
all: 'all',
|
||||
for: 'for',
|
||||
against: 'against'
|
||||
}
|
||||
|
||||
const TAB_LIST = [
|
||||
{ id: TabTypes.all, label: 'all' },
|
||||
{ id: TabTypes.for, label: 'for' },
|
||||
{ id: TabTypes.against, label: 'against' }
|
||||
]
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProposalCommentsSkeleton,
|
||||
ProposalComment
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
proposal: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
tabs: TAB_LIST,
|
||||
currentTab: TAB_LIST[0].id
|
||||
}),
|
||||
computed: {
|
||||
...mapState('governance/proposal', ['isFetchingComments', 'isFetchingMessages']),
|
||||
...mapGetters('governance/proposal', ['comments']),
|
||||
|
||||
commentsFiltered() {
|
||||
const { comments } = this
|
||||
|
||||
switch (this.currentTab) {
|
||||
case TabTypes.for:
|
||||
return comments.filter((_) => _.support === true)
|
||||
|
||||
case TabTypes.against:
|
||||
return comments.filter((_) => _.support === false)
|
||||
|
||||
case TabTypes.all:
|
||||
default:
|
||||
return comments
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchComments(this.proposal)
|
||||
},
|
||||
methods: {
|
||||
...mapActions('governance/proposal', ['fetchComments'])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.proposal-comments {
|
||||
&--header {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,12 +2,26 @@
|
|||
<div>
|
||||
<div v-for="index in size" :key="index" class="proposals-box">
|
||||
<div class="columns is-gapless">
|
||||
<div class="column is-8-tablet is-9-desktop">
|
||||
<div class="column">
|
||||
<div class="proposals-box--title">
|
||||
<b-skeleton height="21" width="210" />
|
||||
<div class="proposals-box--skeleton">
|
||||
<b-skeleton height="30" width="110" />
|
||||
</div>
|
||||
<div class="proposals-box--skeleton">
|
||||
<b-skeleton height="30" width="110" />
|
||||
</div>
|
||||
|
||||
<div class="proposals-box--skeleton is-percentage">
|
||||
<b-skeleton height="30" width="36" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="proposals-box--info">
|
||||
<b-skeleton height="21" width="260" />
|
||||
<div class="proposals-box--comment">
|
||||
<div class="proposals-box--skeleton">
|
||||
<b-skeleton height="21" width="21" />
|
||||
</div>
|
||||
<div class="proposals-box--skeleton">
|
||||
<b-skeleton height="21" width="260" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,3 +39,27 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$margin: 0.714rem;
|
||||
|
||||
.proposals-box {
|
||||
&--title {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&--skeleton {
|
||||
display: inline-block;
|
||||
|
||||
&.is-percentage {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
&--comment {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue