Skip to content

Commit

Permalink
Merge branch 'develop' into generate-diff-2024-02-23
Browse files Browse the repository at this point in the history
  • Loading branch information
nicole-obrien authored Feb 23, 2024
2 parents 0d7807e + 4fd6091 commit 7a6a062
Show file tree
Hide file tree
Showing 7 changed files with 333 additions and 268 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export { default as ManageVotingPowerPane } from './ManageVotingPowerPane.svelte
export { default as ProposalAnswer } from './ProposalAnswer.svelte'
export { default as ProposalCard } from './ProposalCard.svelte'
export { default as ProposalDetailsMenu } from './ProposalDetailsMenu.svelte'
export { default as ProposalInformationPane } from './ProposalInformationPane.svelte'
export { default as ProposalList } from './ProposalList.svelte'
export { default as ProposalListDetails } from './ProposalListDetails.svelte'
export { default as ProposalQuestion } from './ProposalQuestion.svelte'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script lang="ts">
import { Table, Text } from '@bloomwalletio/ui'
import {
participationOverviewForSelectedAccount,
selectedParticipationEventStatus,
selectedProposal,
} from '@contexts/governance/stores'
import { calculateTotalVotesForTrackedParticipations } from '@contexts/governance/utils'
import { selectedAccount } from '@core/account/stores'
import { localize } from '@core/i18n'
import { networkStatus } from '@core/network/stores'
import { activeProfile } from '@core/profile/stores'
import { formatTokenAmountBestMatch } from '@core/token'
import { visibleSelectedAccountTokens } from '@core/token/stores'
import { EventStatus } from '@iota/sdk/out/types'
import { Pane } from '@ui'
const { metadata } = $visibleSelectedAccountTokens?.[$activeProfile?.network?.id]?.baseCoin ?? {}
let totalVotes = BigInt(0)
const hasMounted = false
$: selectedProposalOverview = $participationOverviewForSelectedAccount?.participations?.[$selectedProposal?.id]
$: trackedParticipations = Object.values(selectedProposalOverview ?? {})
$: currentMilestone = $networkStatus.currentMilestone
// Reactively start updating votes once component has mounted and participation overview is available.
$: hasMounted && $selectedParticipationEventStatus && trackedParticipations && currentMilestone && setTotalVotes()
function setTotalVotes(): void {
switch ($selectedParticipationEventStatus?.status) {
case EventStatus.Upcoming:
totalVotes = BigInt(0)
break
case EventStatus.Commencing:
totalVotes = BigInt(0)
break
case EventStatus.Holding:
totalVotes = calculateTotalVotesForTrackedParticipations(trackedParticipations)
break
case EventStatus.Ended:
totalVotes = calculateTotalVotesForTrackedParticipations(trackedParticipations)
break
}
}
</script>

<Pane classes="p-6 h-fit shrink-0 space-y-5">
<Text type="body2">
{localize('views.governance.details.yourVote.title')}
</Text>
<Table
items={[
{
key: localize('views.governance.details.yourVote.total'),
value: formatTokenAmountBestMatch(totalVotes, metadata),
},
{
key: localize('views.governance.details.yourVote.power'),
value: formatTokenAmountBestMatch($selectedAccount?.votingPower, metadata),
},
]}
/>
</Pane>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
import { MarkdownBlock, Text } from '@bloomwalletio/ui'
import { Pane } from '@ui'
import { ProposalDetailsMenu, ProposalStatusPill } from '../'
import { IProposal } from '@contexts/governance'
export let proposal: IProposal
</script>

<Pane classes="p-6 flex flex-col h-fit">
<header-container class="flex justify-between items-center mb-4">
<ProposalStatusPill {proposal} />
<ProposalDetailsMenu {proposal} modalPosition={{ right: '24px', top: '54px' }} />
</header-container>
<div class="flex flex-1 flex-col space-y-4 justify-between scrollable-y">
<Text type="h4">{proposal?.title}</Text>
{#if proposal?.additionalInfo}
<MarkdownBlock text={proposal?.additionalInfo} />
{/if}
</div>
</Pane>
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
<script lang="ts">
import {
EventStatus,
ParticipationEventType,
VotingEventPayload,
TrackedParticipationOverview,
} from '@iota/sdk/out/types'
import { Alert, Button } from '@bloomwalletio/ui'
import { getVotingEvent } from '@contexts/governance/actions'
import { ABSTAIN_VOTE_VALUE } from '@contexts/governance/constants'
import {
participationOverviewForSelectedAccount,
selectedParticipationEventStatus,
selectedProposal,
} from '@contexts/governance/stores'
import { getActiveParticipation, isProposalVotable, isVotingForSelectedProposal } from '@contexts/governance/utils'
import { selectedAccount } from '@core/account/stores'
import { handleError } from '@core/error/handlers'
import { localize } from '@core/i18n'
import { networkStatus } from '@core/network/stores'
import { getBestTimeDuration, milestoneToDate } from '@core/utils'
import { PopupId, openPopup } from '@desktop/auxiliary/popup'
import { ProposalQuestion } from '../../components'
import { Pane } from '@ui'
import { onMount } from 'svelte'
export let statusLoaded: boolean = false
export let overviewLoaded: boolean = false
let selectedAnswerValues: number[] = []
let votedAnswerValues: number[] = []
let votingPayload: VotingEventPayload
let hasMounted = false
let alertText = ''
let proposalQuestions: HTMLElement
let isVotingForProposal: boolean = false
let openedQuestionIndex: number = -1
let isUpdatingVotedAnswerValues: boolean = false
let lastAction: 'vote' | 'stopVote'
$: selectedProposalOverview = $participationOverviewForSelectedAccount?.participations?.[$selectedProposal?.id]
$: trackedParticipations = Object.values(selectedProposalOverview ?? {})
$: currentMilestone = $networkStatus.currentMilestone
// Reactively start updating votes once component has mounted and participation overview is available.
$: hasMounted &&
$selectedParticipationEventStatus &&
trackedParticipations &&
currentMilestone &&
setVotedAnswerValues()
$: hasMounted && selectedProposalOverview && updateIsVoting()
$: questions = votingPayload?.questions
$: if (questions?.length > 0 && selectedAnswerValues?.length === 0) {
selectedAnswerValues = [
...(getActiveParticipation($selectedProposal?.id)?.answers ?? Array.from({ length: questions?.length })),
]
}
$: $selectedParticipationEventStatus, (alertText = getAlertText())
$: hasGovernanceTransactionInProgress =
$selectedAccount?.hasVotingPowerTransactionInProgress || $selectedAccount?.hasVotingTransactionInProgress
$: areSelectedAndVotedAnswersEqual = JSON.stringify(selectedAnswerValues) === JSON.stringify(votedAnswerValues)
$: {
if (hasGovernanceTransactionInProgress) {
isUpdatingVotedAnswerValues = true
}
const hasVoted = lastAction === 'vote' && areSelectedAndVotedAnswersEqual
const hasStoppedVoting = lastAction === 'stopVote' && !areSelectedAndVotedAnswersEqual
if (hasVoted || hasStoppedVoting) {
isUpdatingVotedAnswerValues = hasGovernanceTransactionInProgress
}
}
function hasSelectedNoAnswers(_selectedAnswerValues: number[]): boolean {
return (
_selectedAnswerValues.length === 0 ||
_selectedAnswerValues.every((answerValue) => answerValue === undefined)
)
}
async function setVotingEventPayload(eventId: string): Promise<void> {
try {
const event = await getVotingEvent(eventId)
if (!event) {
throw new Error('Event not found')
}
if (event.data?.payload?.type === ParticipationEventType.Voting) {
votingPayload = event.data.payload
} else {
throw new Error('Event is a staking event')
}
} catch (err) {
handleError(err)
}
}
function updateIsVoting(): void {
isVotingForProposal = isVotingForSelectedProposal()
}
function setVotedAnswerValues(): void {
let lastActiveOverview: TrackedParticipationOverview
switch ($selectedParticipationEventStatus?.status) {
case EventStatus.Commencing:
lastActiveOverview = trackedParticipations?.find((overview) => overview.endMilestoneIndex === 0)
break
case EventStatus.Holding:
lastActiveOverview = trackedParticipations?.find((overview) => overview.endMilestoneIndex === 0)
break
case EventStatus.Ended:
lastActiveOverview = trackedParticipations?.find(
(overview) => overview.endMilestoneIndex > $selectedProposal.milestones.ended
)
break
}
votedAnswerValues = lastActiveOverview?.answers ?? []
}
function onQuestionClick(questionIndex: number): void {
openedQuestionIndex = questionIndex === openedQuestionIndex ? null : questionIndex
}
function onStopVotingClick(): void {
lastAction = 'stopVote'
openPopup({
id: PopupId.StopVoting,
})
}
function onVoteClick(): void {
lastAction = 'vote'
const chosenAnswerValues = selectedAnswerValues.map((answerValue) =>
answerValue === undefined ? ABSTAIN_VOTE_VALUE : answerValue
)
openPopup({
id: PopupId.VoteForProposal,
props: { selectedAnswerValues: chosenAnswerValues },
})
}
function onAnswerClick(answerValue: number, questionIndex: number): void {
selectedAnswerValues[questionIndex] = answerValue
openedQuestionIndex = questionIndex + 1
const selectedQuestionElement: HTMLElement = proposalQuestions?.querySelector(
`proposal-question:nth-child(${openedQuestionIndex})`
)
setTimeout(() => {
proposalQuestions.scrollTo({ top: selectedQuestionElement?.offsetTop, behavior: 'smooth' })
}, 250)
}
function getAlertText(): string {
if (!$selectedProposal) {
return ''
}
const millis =
milestoneToDate(
$networkStatus.currentMilestone,
$selectedProposal.milestones[EventStatus.Commencing]
).getTime() - new Date().getTime()
const timeString = getBestTimeDuration(millis, 'second')
return localize('views.governance.details.hintVote', { values: { time: timeString } })
}
onMount(async () => {
await setVotingEventPayload($selectedProposal?.id)
openedQuestionIndex = votingPayload?.questions.length > 1 ? -1 : 0
hasMounted = true
})
</script>

<Pane classes="w-3/5 h-full p-6 pr-3 flex flex-col justify-between">
<proposal-questions
class="relative flex flex-1 flex-col space-y-5 overflow-y-scroll pr-3"
bind:this={proposalQuestions}
>
{#if questions}
{#each questions as question, questionIndex}
<ProposalQuestion
{question}
{questionIndex}
isOpened={openedQuestionIndex === questionIndex}
isLoading={!overviewLoaded || !statusLoaded}
selectedAnswerValue={selectedAnswerValues[questionIndex]}
votedAnswerValue={votedAnswerValues[questionIndex]}
answerStatuses={$selectedParticipationEventStatus?.questions[questionIndex]?.answers}
{onQuestionClick}
{onAnswerClick}
/>
{/each}
{/if}
</proposal-questions>
{#if $selectedProposal?.status === EventStatus.Upcoming}
<Alert variant="info" text={alertText} />
{:else if [EventStatus.Commencing, EventStatus.Holding].includes($selectedProposal?.status)}
{@const isLoaded = questions && overviewLoaded && statusLoaded}
{@const isStoppingVote = lastAction === 'stopVote' && hasGovernanceTransactionInProgress}
{@const isStopVotingDisabled = !isLoaded || !isVotingForProposal || isUpdatingVotedAnswerValues}
{@const isVoting = lastAction === 'vote' && hasGovernanceTransactionInProgress}
{@const isVotingDisabled =
!isLoaded ||
!isProposalVotable($selectedProposal?.status) ||
hasSelectedNoAnswers(selectedAnswerValues) ||
isUpdatingVotedAnswerValues ||
areSelectedAndVotedAnswersEqual}
<buttons-container class="flex w-full space-x-4 mt-6">
<Button
variant="outlined"
width="full"
on:click={onStopVotingClick}
disabled={isStopVotingDisabled}
busy={isStoppingVote}
text={localize('actions.stopVoting')}
/>
<Button
width="full"
on:click={onVoteClick}
disabled={isVotingDisabled}
busy={isVoting}
text={localize('actions.vote')}
/>
</buttons-container>
{/if}
</Pane>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as ProposalAccountVotingPane } from './ProposalAccountVotingPane.svelte'
export { default as ProposalDetailsPane } from './ProposalDetailsPane.svelte'
export { default as ProposalInformationPane } from './ProposalInformationPane.svelte'
export { default as ProposalQuestionListPane } from './ProposalQuestionListPane.svelte'
Loading

0 comments on commit 7a6a062

Please sign in to comment.