From 7b5ae85ff766a8bb58dedb330fdb9d9488bfa14b Mon Sep 17 00:00:00 2001 From: Jean Ribeiro Date: Tue, 27 Feb 2024 02:49:01 -0300 Subject: [PATCH 1/2] feat: adds votes percentage projection toggle --- .../components/ProposalQuestion.svelte | 11 ++++- .../ProposalQuestionListPane.svelte | 16 +++++-- .../utils/getProjectedPercentages.ts | 44 +++++++++++++++++++ .../lib/contexts/governance/utils/index.ts | 1 + packages/shared/src/locales/en.json | 6 ++- 5 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts diff --git a/packages/desktop/views/dashboard/governance/components/ProposalQuestion.svelte b/packages/desktop/views/dashboard/governance/components/ProposalQuestion.svelte index cacb6edc86..ca4874b36a 100644 --- a/packages/desktop/views/dashboard/governance/components/ProposalQuestion.svelte +++ b/packages/desktop/views/dashboard/governance/components/ProposalQuestion.svelte @@ -5,7 +5,11 @@ import { Icon, IconName, Text, TooltipIcon } from '@bloomwalletio/ui' import { ABSTAIN_VOTE_VALUE } from '@contexts/governance/constants' - import { getPercentagesFromAnswerStatuses, IProposalAnswerPercentages } from '@contexts/governance' + import { + getPercentagesFromAnswerStatuses, + getProjectedPercentages, + IProposalAnswerPercentages, + } from '@contexts/governance' import { selectedProposal } from '@contexts/governance/stores' export let onQuestionClick: (questionIndex: number) => void @@ -18,12 +22,15 @@ export let selectedAnswerValue: number = undefined export let votedAnswerValue: number = undefined export let isLoading: boolean = false + export let projected: boolean = false let percentages: IProposalAnswerPercentages = {} let winnerAnswerIndex: number $: answers = [...(question?.answers ?? []), { value: 0, text: 'Abstain', additionalInfo: '' }] - $: percentages = getPercentagesFromAnswerStatuses(answerStatuses) + $: percentages = projected + ? getProjectedPercentages(answerStatuses) + : getPercentagesFromAnswerStatuses(answerStatuses) $: disabled = $selectedProposal?.status === EventStatus.Upcoming || $selectedProposal?.status === EventStatus.Ended || diff --git a/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalQuestionListPane.svelte b/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalQuestionListPane.svelte index b56fdc68d4..a9db06ea26 100644 --- a/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalQuestionListPane.svelte +++ b/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalQuestionListPane.svelte @@ -5,7 +5,7 @@ VotingEventPayload, TrackedParticipationOverview, } from '@iota/sdk/out/types' - import { Alert, Button } from '@bloomwalletio/ui' + import { Alert, Button, Text, Toggle, TooltipIcon } from '@bloomwalletio/ui' import { getVotingEvent } from '@contexts/governance/actions' import { ABSTAIN_VOTE_VALUE } from '@contexts/governance/constants' import { @@ -37,6 +37,7 @@ let openedQuestionIndex: number = -1 let isUpdatingVotedAnswerValues: boolean = false let lastAction: 'vote' | 'stopVote' + let projected: boolean = false $: selectedProposalOverview = $participationOverviewForSelectedAccount?.participations?.[$selectedProposal?.id] $: trackedParticipations = Object.values(selectedProposalOverview ?? {}) @@ -179,7 +180,15 @@ }) - + + {@const isVotable = [EventStatus.Commencing, EventStatus.Holding].includes($selectedProposal?.status)} + {#if isVotable} +
+ + {localize('views.governance.details.projection.label')} + +
+ {/if} {/each} {/if} {#if $selectedProposal?.status === EventStatus.Upcoming} - {:else if [EventStatus.Commencing, EventStatus.Holding].includes($selectedProposal?.status)} + {:else if isVotable} {@const isLoaded = questions && overviewLoaded && statusLoaded} {@const isStoppingVote = lastAction === 'stopVote' && hasGovernanceTransactionInProgress} {@const isStopVotingDisabled = !isLoaded || !isVotingForProposal || isUpdatingVotedAnswerValues} diff --git a/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts b/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts new file mode 100644 index 0000000000..45ccd54e99 --- /dev/null +++ b/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts @@ -0,0 +1,44 @@ +import { get } from 'svelte/store' +import { IProposal, IProposalAnswerPercentages, selectedProposal } from '..' +import { AnswerStatus } from '@iota/sdk' +import { networkStatus } from '@core/network/stores/network-status.store' +import { round } from '@core/utils/number' + +export function getProjectedPercentages( + answerStatuses: AnswerStatus[], + proposal: IProposal = get(selectedProposal) +): IProposalAnswerPercentages { + if (!proposal) { + return {} + } + + const answerStatusesWithProjection = answerStatuses.map((answerStatus) => { + return { ...answerStatus, projected: getProjectedVotesFromAnswerStatus(answerStatus, proposal) } + }) + + const totalVotes = answerStatusesWithProjection?.reduce((acc, answerStatus) => acc + answerStatus.projected, 0) ?? 0 + if (totalVotes === 0 || Number.isNaN(totalVotes)) { + return {} + } + + let percentages: IProposalAnswerPercentages = {} + answerStatusesWithProjection.forEach((answerStatus) => { + if (answerStatus.value !== undefined) { + const divisionResult = (answerStatus.projected ?? 0) / totalVotes + percentages = { + ...percentages, + [answerStatus.value]: Number.isNaN(divisionResult) ? '0%' : `${round(divisionResult * 100, 1)}%`, + } + } + }) + + return percentages +} + +function getProjectedVotesFromAnswerStatus(answerStatus: AnswerStatus, proposal: IProposal): number { + const { accumulated, current } = answerStatus + const endingMilestone = proposal.milestones?.ended ?? 0 + const currentMilestone = get(networkStatus)?.currentMilestone ?? 0 + + return Math.max(accumulated, accumulated + current * (endingMilestone - currentMilestone)) +} diff --git a/packages/shared/src/lib/contexts/governance/utils/index.ts b/packages/shared/src/lib/contexts/governance/utils/index.ts index 0f869782e9..24ce2398a1 100644 --- a/packages/shared/src/lib/contexts/governance/utils/index.ts +++ b/packages/shared/src/lib/contexts/governance/utils/index.ts @@ -9,6 +9,7 @@ export * from './getNumberOfVotedProposals' export * from './getNumberOfVotingProposals' export * from './getParticipationsForProposal' export * from './getPercentagesFromAnswerStatuses' +export * from './getProjectedPercentages' export * from './getProposalStatusForMilestone' export * from './isAccountVoting' export * from './isParticipationOutput' diff --git a/packages/shared/src/locales/en.json b/packages/shared/src/locales/en.json index b7e92aefbe..d92055e4b2 100644 --- a/packages/shared/src/locales/en.json +++ b/packages/shared/src/locales/en.json @@ -688,7 +688,11 @@ "nodeUrl": "Node URL" }, "fetching": "Fetching proposal data", - "hintVote": "You can not vote on a proposal that is in the announcement phase, voting will open in {time}." + "hintVote": "You can not vote on a proposal that is in the announcement phase, voting will open in {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { From 254d044ffae70363b05499e6dc5d9c2bd61b425c Mon Sep 17 00:00:00 2001 From: Jean Ribeiro Date: Tue, 27 Feb 2024 10:10:52 -0300 Subject: [PATCH 2/2] fix: adds Mark suggestion Co-authored-by: Mark Nardi --- .../lib/contexts/governance/utils/getProjectedPercentages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts b/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts index 45ccd54e99..07e4e46543 100644 --- a/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts +++ b/packages/shared/src/lib/contexts/governance/utils/getProjectedPercentages.ts @@ -24,7 +24,7 @@ export function getProjectedPercentages( let percentages: IProposalAnswerPercentages = {} answerStatusesWithProjection.forEach((answerStatus) => { if (answerStatus.value !== undefined) { - const divisionResult = (answerStatus.projected ?? 0) / totalVotes + const divisionResult = answerStatus.projected / totalVotes percentages = { ...percentages, [answerStatus.value]: Number.isNaN(divisionResult) ? '0%' : `${round(divisionResult * 100, 1)}%`,