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

feat: show vote percentages #5440

Merged
merged 24 commits into from Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c552f20
feat: show icon if has already voted
jeeanribeiro Dec 17, 2022
0133f89
Merge branch 'stardust-develop' into feat/show-votes-if-already-voted
jeeanribeiro Dec 17, 2022
55ae2d9
Merge branch 'stardust-develop' into feat/show-votes-if-already-voted
jeeanribeiro Dec 17, 2022
1ed18fd
fix: correct value passed for vote
Tuditi Dec 19, 2022
b99d8c4
feat: show selected answer if voted for in UI
Tuditi Dec 19, 2022
3978682
enhancement: ui for voted option
Tuditi Dec 19, 2022
85ab24b
feat: add abstain option
Tuditi Dec 19, 2022
c5e0b74
chore: replace callback with async/void
Tuditi Dec 19, 2022
10ad6ab
chore: cleanup scss
Tuditi Dec 19, 2022
1938d2b
fix: correctly pass answer value
jeeanribeiro Dec 19, 2022
3f32ef0
fix: correctly iterate over proposals
Tuditi Dec 20, 2022
6b82ce9
fix: pr reviews
Tuditi Dec 20, 2022
9b4989c
fix: show abstained vote
Tuditi Dec 20, 2022
5564f0b
removes the not working logic
jeeanribeiro Dec 20, 2022
502af0d
Merge branch 'stardust-develop' into feat/show-votes-if-already-voted
jeeanribeiro Dec 20, 2022
b1bbef5
feat: adds the percentage of vote
jeeanribeiro Dec 20, 2022
63ad1fd
Merge remote-tracking branch 'origin/stardust-develop' into feat/show…
Tuditi Dec 22, 2022
ad26d03
chore: renamed some values
Tuditi Dec 22, 2022
9c5dea4
fixes from review
jeeanribeiro Dec 22, 2022
2dab423
Merge branch 'stardust-develop' into feat/show-votes-if-already-voted
jeeanribeiro Dec 22, 2022
793b35f
Merge branch 'stardust-develop' into feat/show-votes-if-already-voted
Tuditi Dec 22, 2022
520dfdc
fix vertical alignment, spacing and duplicate import
jeeanribeiro Dec 22, 2022
391c453
fix spacing
jeeanribeiro Dec 22, 2022
6e7ae4c
Merge branch 'stardust-develop' into feat/show-votes-if-already-voted
Tuditi Dec 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,4 +1,5 @@
<script lang="typescript">
import { onMount } from 'svelte'
import { localize } from '@core/i18n'
import {
Button,
Expand All @@ -23,28 +24,25 @@
import { proposalsState, selectedProposal } from '@contexts/governance/stores'
import { networkStatus } from '@core/network/stores'

let selectedIndices: number[] = []
let selectedAnswerValues: number[] = []
let votingPayload: VotingEventPayload
let totalVotes = 0

$: void setVotingEventPayload($selectedProposal?.id)
$: void setTotalVotes()
$: proposalStatus = $proposalsState[$selectedProposal?.id]?.status

$: proposalState = $proposalsState[$selectedProposal?.id]
$: votesCounter = {
total: totalVotes,
power: $selectedAccount?.votingPower,
}
$: questions = votingPayload?.questions

$: if (questions?.length > 0 && selectedIndices?.length === 0) {
selectedIndices = Array<number>(questions?.length)
$: if (questions?.length > 0 && selectedAnswerValues?.length === 0) {
selectedAnswerValues = Array<number>(questions?.length)
}
$: isVotingDisabled =
proposalStatus === ProposalStatus.Upcoming ||
proposalStatus === ProposalStatus.Ended ||
selectedIndices?.length === 0 ||
selectedIndices?.includes(undefined)
proposalState?.status === ProposalStatus.Upcoming ||
proposalState?.status === ProposalStatus.Ended ||
selectedAnswerValues?.length === 0 ||
selectedAnswerValues?.includes(undefined)

async function setVotingEventPayload(eventId: string): Promise<void> {
const event = await getVotingEvent(eventId)
Expand Down Expand Up @@ -86,9 +84,14 @@
function handleVoteClick(): void {
openPopup({
type: 'voteForProposal',
props: { selectedAnswers: selectedIndices },
props: { selectedAnswerValues },
})
}

onMount(() => {
void setVotingEventPayload($selectedProposal?.id)
void setTotalVotes()
})
</script>

<div class="w-full h-full flex flex-nowrap p-8 relative flex-1 space-x-4 bg-gray-50 dark:bg-gray-900">
Expand Down Expand Up @@ -135,7 +138,8 @@
{question}
isOpened={openedQuestionIndex === index}
{index}
bind:selectedIndices
bind:selectedAnswerValues
currentVote={proposalState?.questions[index]}
onClick={() => handleQuestionClick(index)}
/>
{/each}
Expand Down
44 changes: 31 additions & 13 deletions packages/shared/components/ProposalAnswer.svelte
Expand Up @@ -8,31 +8,41 @@
export let answer: Answer
export let hidden: boolean = null
export let isSelected: boolean = null
export let isVotedFor: boolean = null
export let answerIndex: number = undefined

$: showBorder = isVotedFor || isSelected

const dispatch = createEventDispatcher()

function handleClick(): void {
dispatch('answerClicked', answerIndex)
dispatch('answerClicked', answer?.value)
}
</script>

<proposal-answer
class:hidden
class="flex justify-between items-center p-3 rounded-md border border-solid {isSelected
? 'border-blue-500'
: 'border-gray-200'} "
class:hidden={isVotedFor ? false : hidden}
class="flex justify-between items-center p-3 rounded-md border border-solid
{isVotedFor ? 'bg-blue-100' : ''}
{showBorder ? 'border-blue-500' : 'border-gray-200'}
"
on:click={handleClick}
>
<div class="flex space-x-3">
<div class="flex space-x-3 items-center">
{#if answerIndex !== undefined}
<span
class="flex items-center justify-center h-5 w-5 text-12 {isSelected
? 'bg-blue-500 text-white'
: 'text-gray-500'} text-700 border border-solid border-gray-200"
>
{answerIndex + 1}
</span>
{#if isVotedFor}
<div class="flex justify-center w-5">
<span class="ring flex items-center justify-center h-1.5 w-1.5 bg-blue-500 rounded-full" />
</div>
{:else}
<span
class="flex items-center justify-center h-5 w-5 text-12 {isSelected
? 'bg-blue-500 text-white'
: 'text-gray-500'} text-700 border border-solid border-gray-200"
>
{answerIndex + 1}
</span>
{/if}
{/if}
<Text fontWeight={FontWeight.medium}>{answer.text}</Text>
</div>
Expand All @@ -47,3 +57,11 @@
/>
{/if}
</proposal-answer>

<style type="text/scss">
.ring {
@apply ring-4;
@apply ring-blue-500;
@apply ring-opacity-20;
}
</style>
26 changes: 16 additions & 10 deletions packages/shared/components/ProposalQuestion.svelte
@@ -1,20 +1,25 @@
<script lang="typescript">
import { Text, FontWeight, Icon, ProposalAnswer } from 'shared/components'
import { Icon as IconEnum } from '@auxiliary/icon'
import type { Question } from '@iota/wallet'
import type { Answer, Question } from '@iota/wallet'

export let question: Question
export let index: number = undefined
export let selectedIndices: number[] // TODO, maybe should be a svelte store
export let selectedAnswerValues: number[] // TODO, maybe should be a svelte store
export let currentVote: Answer = null
export let isOpened = false

export let onClick: () => unknown = () => {}

function handleAnswerClick(answerIndex: number): void {
if (selectedIndices[index] === answerIndex) {
selectedIndices[index] = undefined
$: voteValue = currentVote?.answers?.find((answer) => answer?.current !== 0)?.value
$: answers = [...question?.answers, { value: 0, text: 'Abstain', additionalInfo: '' }]
$: showMargin = isOpened || (voteValue && !isOpened)

function handleAnswerClick(answer: number): void {
jeeanribeiro marked this conversation as resolved.
Show resolved Hide resolved
if (selectedAnswerValues[index] === answer) {
selectedAnswerValues[index] = undefined
} else {
selectedIndices[index] = answerIndex
selectedAnswerValues[index] = answer
}
}
</script>
Expand All @@ -33,14 +38,15 @@
<Icon icon={IconEnum.ChevronDown} classes="text-gray-500" />
</div>
</div>
<proposal-answers class:mt-4={isOpened} class="space-y-2">
{#each question.answers as answer, answerIndex}
<proposal-answers class:mt-4={showMargin} class={isOpened ? 'space-y-2' : ''}>
{#each answers as answer, answerIndex}
<ProposalAnswer
{answer}
{answerIndex}
on:answerClicked={() => handleAnswerClick(answerIndex)}
on:answerClicked={(event) => handleAnswerClick(event.detail)}
hidden={!isOpened}
isSelected={selectedIndices[index] === answerIndex}
isSelected={selectedAnswerValues[index] === answer?.value}
isVotedFor={voteValue === answer?.value}
/>
{/each}
</proposal-answers>
Expand Down
12 changes: 11 additions & 1 deletion packages/shared/components/organisms/ProposalCard.svelte
Expand Up @@ -9,15 +9,25 @@
import { Icon } from '@auxiliary/icon/enums'
import { localize } from '@core/i18n'
import { proposalsState } from '@contexts/governance/stores'
import { isVotingForProposal } from '@contexts/governance/utils'
import { onMount } from 'svelte'

export let proposal: IProposal

let hasVoted = false

$: proposalState = $proposalsState[proposal?.id]

async function setHasVoted(): Promise<void> {
hasVoted = await isVotingForProposal(proposal?.id)
}
Tuditi marked this conversation as resolved.
Show resolved Hide resolved

function handleProposalClick(): void {
$selectedProposal = proposal
$governanceRouter.goTo(GovernanceRoute.Details)
}

onMount(() => void setHasVoted())
</script>

<proposal-card
Expand All @@ -42,7 +52,7 @@
</div>
<div class="flex justify-between items-center">
<ProposalStatusInfo status={proposalState?.status} milestones={proposal.milestones} />
{#if proposal.hasVoted}
{#if hasVoted}
<TooltipIcon icon={Icon.Voted} size="small" position={Position.Left} iconClasses="text-gray-500">
<Text smaller overrideColor fontWeight={FontWeight.semibold} classes="text-gray-600">
{localize('views.governance.proposals.voted')}
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/components/popups/VoteForProposalPopup.svelte
Expand Up @@ -9,7 +9,7 @@
import { showAppNotification } from '@auxiliary/notification'
import { selectedProposal } from '@contexts/governance/stores'

export let selectedAnswers: number[]
export let selectedAnswerValues: number[]

$: formattedVotingPower = formatTokenAmountBestMatch(
Number($selectedAccount?.votingPower),
Expand All @@ -20,7 +20,7 @@
async function handleVoteClick(): Promise<void> {
try {
await checkActiveProfileAuth(async () => {
await vote($selectedAccount.index, $selectedProposal?.id, selectedAnswers)
await vote($selectedAccount.index, $selectedProposal?.id, selectedAnswerValues)
showAppNotification({
type: 'success',
message: localize('notifications.vote.success'),
Expand Down
Expand Up @@ -3,7 +3,6 @@ import { ProposalStatus } from '../enums'
import { IOrganization } from './organization.interface'

export interface IProposal {
hasVoted?: boolean
id: string
milestones?: Record<ProposalStatus, number>
organization?: IOrganization
Expand Down
Expand Up @@ -26,7 +26,7 @@ export function removeProposalState(eventId: string): void {

export async function updateProposalsState(): Promise<void> {
const _proposalsState = get(proposalsState)
for (const eventId in _proposalsState) {
for (const eventId of Object.keys(_proposalsState)) {
const proposalStatus = await getVotingProposalState(eventId)
_proposalsState[eventId] = proposalStatus
}
Expand Down
1 change: 1 addition & 0 deletions packages/shared/lib/contexts/governance/utils/index.ts
@@ -1,2 +1,3 @@
export * from './createProposalsFromEvents'
export * from './isVotingForProposal'
export * from './isVotingForSelectedProposal'
@@ -0,0 +1,22 @@
import { get } from 'svelte/store'

import { TrackedParticipationOverview } from '@iota/wallet'

import { getParticipationOverview } from '@core/account/api'
import { selectedAccount } from '@core/account/stores'

export async function isVotingForProposal(proposalId: string): Promise<boolean> {
const overview = await getParticipationOverview(get(selectedAccount)?.index)
if (overview) {
if (proposalId in overview.participations) {
const participationOutputs: TrackedParticipationOverview[] = Object.values(
overview.participations[proposalId]
)
return participationOutputs.some((output) => output?.endMilestoneIndex === 0)
} else {
return false
}
} else {
return false
}
}
@@ -1,25 +1,9 @@
import { get } from 'svelte/store'

import { TrackedParticipationOverview } from '@iota/wallet'

import { getParticipationOverview } from '@core/account/api'
import { selectedAccount } from '@core/account/stores'

import { selectedProposal } from '../stores'
import { isVotingForProposal } from './isVotingForProposal'

export async function isVotingForSelectedProposal(): Promise<boolean> {
const overview = await getParticipationOverview(get(selectedAccount)?.index)
if (overview) {
const proposalId = get(selectedProposal)?.id
if (proposalId in overview.participations) {
const participationOutputs: TrackedParticipationOverview[] = Object.values(
overview.participations[proposalId]
)
return participationOutputs.some((output) => output?.endMilestoneIndex === 0)
} else {
return false
}
} else {
return false
}
const proposalId = get(selectedProposal)?.id
return isVotingForProposal(proposalId)
}