Skip to content

Commit

Permalink
enhancement: add outdated node url state for removed proposals (#6034)
Browse files Browse the repository at this point in the history
* chore: add UI for outdated node

* chore: add tooltip

* chore: add event if is an error

* feat: add the stuff

* chore: fix type check

* fix: pr comments

* fix: disable voting if proposal has error

* chore: pass one object to Svelte component

* feat: remove proposals if in error state

* fix: add optional chaining

* chore: simplify logic

* feat: reactively update participation event status

* fix: fix removing proposals

---------

Co-authored-by: Mark Nardi <mark.nardi@iota.org>
Co-authored-by: Tuditi <45079109+Tuditi@users.noreply.github.com>
Co-authored-by: Tuditi <daviddetroch@pm.me>
  • Loading branch information
4 people committed Mar 8, 2023
1 parent 88d1b6f commit 4a81abd
Show file tree
Hide file tree
Showing 23 changed files with 209 additions and 67 deletions.
22 changes: 17 additions & 5 deletions packages/desktop/components/popups/RemoveProposalPopup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,45 @@
import { Button, Text, TextType, TextHint } from 'shared/components'
import { ButtonVariant } from 'shared/components/enums'
import { handleError } from '@core/error/handlers'
import { deregisterParticipationEvent } from '@contexts/governance/actions'
import { ProposalStatus } from '@contexts/governance/enums'
import { selectedProposal } from '@contexts/governance/stores'
import {
clearSelectedParticipationEventStatus,
removePersistedProposal,
selectedProposal,
selectedProposalId,
} from '@contexts/governance/stores'
import { localize } from '@core/i18n'
import { governanceRouter } from '@core/router'
import { closePopup } from '@auxiliary/popup'
import { showAppNotification } from '@auxiliary/notification'
import { selectedAccount } from '@core/account/stores'
function handleCancel(): void {
closePopup()
}
async function handleConfirm(): Promise<void> {
try {
await deregisterParticipationEvent($selectedProposal.id)
await $selectedAccount.deregisterParticipationEvent($selectedProposalId)
$governanceRouter.previous()
clearEvent()
closePopup()
showAppNotification({
type: 'success',
message: localize('views.governance.proposals.successRemove'),
alert: true,
})
closePopup()
$governanceRouter.previous()
} catch (err) {
handleError(err)
}
}
function clearEvent(): void {
removePersistedProposal($selectedProposalId, $selectedAccount.index)
$selectedProposalId = null
clearSelectedParticipationEventStatus()
}
// TODO: User can only remove a proposal when he is not voting for it
$: isTextHintVisible =
$selectedProposal?.status === ProposalStatus.Commencing || $selectedProposal?.status === ProposalStatus.Holding
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/features/governance.features.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const governanceFeatures = {
enabled: true,
removeProposals: {
enabled: false,
enabled: true,
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
import { localize } from '@core/i18n'
import { openPopup } from '@auxiliary/popup/actions'
import { selectedAccount } from '@core/account/stores'
import { getVotingEvent } from '@contexts/governance/actions'
import {
clearParticipationEventStatusPoll,
getVotingEvent,
pollParticipationEventStatus,
} from '@contexts/governance/actions'
import { ABSTAIN_VOTE_VALUE } from '@contexts/governance/constants'
import { ProposalStatus } from '@contexts/governance/enums'
import {
Expand All @@ -38,10 +42,6 @@
import { formatTokenAmountBestMatch } from '@core/wallet/utils'
import { visibleSelectedAccountAssets } from '@core/wallet/stores'
import { handleError } from '@core/error/handlers'
import {
clearParticipationEventStatusPoll,
pollParticipationEventStatus,
} from '@contexts/governance/actions/pollParticipationEventStatus'
import { PopupId } from '@auxiliary/popup'
const { metadata } = $visibleSelectedAccountAssets?.baseCoin
Expand Down Expand Up @@ -192,6 +192,10 @@
}
function getTextHintString(): string {
if (!$selectedProposal) {
return ''
}
const millis =
milestoneToDate(
$networkStatus.currentMilestone,
Expand Down Expand Up @@ -220,7 +224,7 @@
<div class="w-2/5 flex flex-col space-y-4">
<Pane classes="p-6 flex flex-col h-fit">
<header-container class="flex justify-between items-center mb-4">
<ProposalStatusPill status={$selectedProposal?.status} />
<ProposalStatusPill proposal={$selectedProposal} />
<ProposalDetailsButton proposal={$selectedProposal} />
</header-container>
<div class="flex flex-1 flex-col justify-between">
Expand Down
19 changes: 19 additions & 0 deletions packages/shared/components/OutdatedNodeTooltip.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import { FontWeight, Text, Tooltip } from 'shared/components'
import { Position } from './enums'
import { localize } from '@core/i18n'
export let anchor: HTMLElement
export let position: Position = Position.Right
</script>

<Tooltip {anchor} {position}>
<div class="flex flex-col text-left">
<Text fontWeight={FontWeight.semibold} fontSize="16" classes="mb-2">
{localize('tooltips.governance.outdatedNode.title')}
</Text>
<Text fontWeight={FontWeight.normal}>
{localize('tooltips.governance.outdatedNode.body')}
</Text>
</div>
</Tooltip>
4 changes: 3 additions & 1 deletion packages/shared/components/ProposalQuestion.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
$: answers = [...question?.answers, { value: 0, text: 'Abstain', additionalInfo: '' }]
$: percentages = getPercentagesFromAnswerStatuses(answerStatuses)
$: disabled =
$selectedProposal?.status === ProposalStatus.Upcoming || $selectedProposal?.status === ProposalStatus.Ended
$selectedProposal?.status === ProposalStatus.Upcoming ||
$selectedProposal?.status === ProposalStatus.Ended ||
!!$selectedProposal?.error
$: answerStatuses, setWinnerAnswerIndex()
$: showMargin =
isOpened ||
Expand Down
27 changes: 23 additions & 4 deletions packages/shared/components/ProposalStatusInfo.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
<script lang="ts">
import { IProposal } from '@contexts/governance/interfaces'
import { ProposalStatusPill, ProposalStatusTimelineTooltip } from 'shared/components'
import {
ProposalStatusPill,
ProposalStatusTimelineTooltip,
OutdatedNodeTooltip,
ResultsNotAvailableTooltip,
} from 'shared/components'
import { Position } from 'shared/components/enums'
import { IProposal } from '@contexts/governance/interfaces'
import { ProposalError } from '../lib/contexts/governance'
export let proposal: IProposal
export let position: Position = Position.Right
Expand All @@ -15,8 +21,21 @@
</script>

<div bind:this={anchor} on:mouseenter={() => showTooltip(true)} on:mouseleave={() => showTooltip(false)}>
<ProposalStatusPill status={proposal?.status} />
<ProposalStatusPill {proposal} />
</div>
{#if isTooltipVisible}
<ProposalStatusTimelineTooltip bind:anchor milestones={proposal.milestones} status={proposal?.status} {position} />
{#if proposal?.error}
{#if proposal?.error === ProposalError.NodeOutdated}
<OutdatedNodeTooltip bind:anchor {position} />
{:else if proposal?.error === ProposalError.ResultsNotAvailable}
<ResultsNotAvailableTooltip bind:anchor {position} />
{/if}
{:else}
<ProposalStatusTimelineTooltip
bind:anchor
milestones={proposal.milestones}
status={proposal?.status}
{position}
/>
{/if}
{/if}
19 changes: 19 additions & 0 deletions packages/shared/components/ResultsNotAvailableTooltip.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import { FontWeight, Text, Tooltip } from 'shared/components'
import { Position } from './enums'
import { localize } from '@core/i18n'
export let anchor: HTMLElement
export let position: Position = Position.Right
</script>

<Tooltip {anchor} {position}>
<div class="flex flex-col text-left">
<Text fontWeight={FontWeight.semibold} fontSize="16" classes="mb-2">
{localize('tooltips.governance.resultsNotAvailable.title')}
</Text>
<Text fontWeight={FontWeight.normal}>
{localize('tooltips.governance.resultsNotAvailable.body')}
</Text>
</div>
</Tooltip>
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,24 @@
onClick: onRemoveProposalClick,
variant: 'error',
isLoading: isBusy,
disabled: !features.governance.removeProposals.enabled || isVotingForProposal,
disabled: getDisabled(proposal, isVotingForProposal),
enableTooltipVisible: isVotingForProposal !== undefined,
tooltip: localize('tooltips.governance.removeProposalWarning'),
},
]
function getDisabled(proposal: IProposal, isVoting: boolean): boolean {
if (features.governance.removeProposals.enabled) {
if (proposal.error === undefined) {
return isVoting
} else {
return false
}
} else {
return true
}
}
function onChangeNodeClick(): void {
openPopup({
id: PopupId.AddProposal,
Expand Down
1 change: 1 addition & 0 deletions packages/shared/components/atoms/pills/Pill.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"
>
{#if data}
<slot />
{data}
{:else}
<slot />
Expand Down
25 changes: 17 additions & 8 deletions packages/shared/components/atoms/pills/ProposalStatusPill.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
<script lang="ts">
import { Pill } from 'shared/components'
import { Icon, Pill } from 'shared/components'
import { Icon as _Icon } from '@auxiliary/icon'
import { localize } from '@core/i18n'
import { ProposalStatus } from '@contexts/governance/enums'
import { IProposal } from '@contexts/governance/interfaces'
export let status: string
export let proposal: IProposal
$: status = proposal?.status
$: error = proposal?.error
const STATUS_COLORS: Record<ProposalStatus, string> = {
[ProposalStatus.Upcoming]: 'purple-200',
Expand All @@ -14,10 +19,14 @@
</script>

<Pill
data={localize(`pills.proposalStatus.${status}`)}
textColor="gray-800"
darkTextColor="gray-800"
backgroundColor={STATUS_COLORS[status]}
darkBackgroundColor={STATUS_COLORS[status]}
data={localize(`pills.governance.proposalStatus.${error ? error : status}`)}
textColor={error ? 'red-700' : 'grey-800'}
darkTextColor={error ? 'red-700' : 'grey-800'}
backgroundColor={error ? 'red-200' : STATUS_COLORS[status]}
darkBackgroundColor={error ? 'red-200' : STATUS_COLORS[status]}
classes="rounded-full px-2 py-1 flex items-center {status ? '' : 'invisible'}"
/>
>
{#if error}
<Icon icon={error ? _Icon.StatusError : undefined} classes="text-red-700" />
{/if}
</Pill>
2 changes: 2 additions & 0 deletions packages/shared/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export { default as Link } from './Link.svelte'
export { default as LoadingScreen } from './LoadingScreen.svelte'
export { default as Logo } from './Logo.svelte'
export { default as NetworkIndicator } from './NetworkIndicator.svelte'
export { default as OutdatedNodeTooltip } from './OutdatedNodeTooltip.svelte'
export { default as Profile } from './Profile.svelte'
export { default as ProgressBar } from './ProgressBar.svelte'
export { default as Proposals } from './Proposals.svelte'
Expand All @@ -21,6 +22,7 @@ export { default as ProposalStatusInfo } from './ProposalStatusInfo.svelte'
export { default as ProposalStatusTimelineTooltip } from './ProposalStatusTimelineTooltip.svelte'
export { default as QR } from './QR.svelte'
export { default as RecoveryPhrase } from './RecoveryPhrase.svelte'
export { default as ResultsNotAvailableTooltip } from './ResultsNotAvailableTooltip.svelte'
export { default as SettingsNavigator } from './SettingsNavigator.svelte'
export { default as Spinner } from './Spinner.svelte'
export { default as Swiper } from './Swiper.svelte'
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { get } from 'svelte/store'

import type { ParticipationEventId, ParticipationEventStatus } from '@iota/wallet'

import { IAccount } from '@core/account/interfaces'
import { selectedAccount } from '@core/account/stores'
import { IAccount } from '@core/account'

export async function getAccountsParticipationEventStatusForEvent(
export function getAccountsParticipationEventStatusForEvent(
eventId: ParticipationEventId,
account: IAccount = get(selectedAccount)
): Promise<ParticipationEventStatus> {
try {
const status = await account?.getParticipationEventStatus(eventId)
return status
} catch (err) {
return undefined
}
return account?.getParticipationEventStatus(eventId)
}
1 change: 0 additions & 1 deletion packages/shared/lib/contexts/governance/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './addProposalsFromParticipationEventMap'
export * from './deregisterParticipationEvent'
export * from './getVotingEvent'
export * from './getAccountsParticipationEventStatusForEvent'
export * from './initializeParticipationOverviews'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { get } from 'svelte/store'

import { IAccountState, selectedAccount } from '@core/account'
import { activeAccounts } from '@core/profile'
import { get } from 'svelte/store'

import { IRegisteredProposals } from '../interfaces'
import { registeredProposals } from '../stores'
import { createProposalFromEvent } from '../utils'
import { createProposalFromError, createProposalFromEvent } from '../utils'
import { getAccountsParticipationEventStatusForEvent } from './getAccountsParticipationEventStatusForEvent'

export async function initializeRegisteredProposals(): Promise<void> {
const allProposals: { [accountId: number]: IRegisteredProposals } = {}
Expand All @@ -28,7 +31,13 @@ async function getParticipationEventsAndCreateProposalsForAccount(
const proposals: IRegisteredProposals = {}
const events = await account.getParticipationEvents()
for (const event of Object.values(events)) {
proposals[event.id] = createProposalFromEvent(event)
const proposal = createProposalFromEvent(event)
try {
await getAccountsParticipationEventStatusForEvent(event.id, account)
proposals[event.id] = proposal
} catch (err) {
proposals[event.id] = createProposalFromError(proposal, err)
}
}
return proposals
}
1 change: 1 addition & 0 deletions packages/shared/lib/contexts/governance/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './proposal-error.enum'
export * from './proposal-order-option.enum'
export * from './proposal-status.enum'
export * from './proposal-type.enum'
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ProposalError {
NodeOutdated = 'nodeOutdated',
ResultsNotAvailable = 'resultsNotAvailable',
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ParticipationEventStatus, Question } from '@iota/wallet'
import { IOrganization } from '..'
import { IOrganization, ProposalError } from '..'
import { ProposalStatus, ProposalType } from '../enums'

export interface IProposal extends IProposalMetadata {
Expand All @@ -25,4 +25,5 @@ export interface IProposalMetadata {
additionalInfo: string
title: string
nodeUrl: string
error?: ProposalError
}

0 comments on commit 4a81abd

Please sign in to comment.