diff --git a/packages/desktop/views/dashboard/governance/Governance.svelte b/packages/desktop/views/dashboard/governance/Governance.svelte index 4e76a62b5e..b428ed0105 100644 --- a/packages/desktop/views/dashboard/governance/Governance.svelte +++ b/packages/desktop/views/dashboard/governance/Governance.svelte @@ -1,7 +1,13 @@ {#if $selectedAccount} diff --git a/packages/desktop/views/dashboard/governance/views/DetailsView.svelte b/packages/desktop/views/dashboard/governance/views/DetailsView.svelte index 41e0c091c9..e62df2b629 100644 --- a/packages/desktop/views/dashboard/governance/views/DetailsView.svelte +++ b/packages/desktop/views/dashboard/governance/views/DetailsView.svelte @@ -21,15 +21,16 @@ import type { IParticipations } from '@core/governance/interfaces' import { openPopup } from '@auxiliary/popup' import { ProposalStatus } from '@core/governance/enums' - import { networkStatus } from '@core/network' + import { proposalsState } from '@core/governance/stores' + import { networkStatus } from '@core/network/stores' let selectedIndices: number[] = [] - let votingPayload: VotingEventPayload - $: void setVotingEventPayload($selectedProposal?.id) - let totalVotes = 0 + + $: void setVotingEventPayload($selectedProposal?.id) $: void setTotalVotes() + $: proposalStatus = $proposalsState[$selectedProposal?.id]?.status $: votesCounter = { total: totalVotes, @@ -41,8 +42,8 @@ selectedIndices = Array(questions?.length) } $: isVotingDisabled = - $selectedProposal?.status === ProposalStatus.Announcement || - $selectedProposal?.status === ProposalStatus.Closed || + proposalStatus === ProposalStatus.Upcoming || + proposalStatus === ProposalStatus.Ended || selectedIndices?.length === 0 || selectedIndices?.includes(undefined) diff --git a/packages/shared/components/ProposalStatusTimelineTooltip.svelte b/packages/shared/components/ProposalStatusTimelineTooltip.svelte index ac6ed1b219..8cddc7556a 100644 --- a/packages/shared/components/ProposalStatusTimelineTooltip.svelte +++ b/packages/shared/components/ProposalStatusTimelineTooltip.svelte @@ -12,16 +12,16 @@ let eventProgress: number switch (status) { - case ProposalStatus.Announcement: + case ProposalStatus.Upcoming: eventProgress = 0 break - case ProposalStatus.VotingOpen: + case ProposalStatus.Commencing: eventProgress = 1 break - case ProposalStatus.Counting: + case ProposalStatus.Holding: eventProgress = 2 break - case ProposalStatus.Closed: + case ProposalStatus.Ended: eventProgress = 3 break default: diff --git a/packages/shared/components/atoms/pills/ProposalStatusPill.svelte b/packages/shared/components/atoms/pills/ProposalStatusPill.svelte index 83ab2df425..bfcd00907a 100644 --- a/packages/shared/components/atoms/pills/ProposalStatusPill.svelte +++ b/packages/shared/components/atoms/pills/ProposalStatusPill.svelte @@ -6,10 +6,10 @@ export let status: ProposalStatus const STATUS_COLORS: Record = { - [ProposalStatus.Announcement]: 'purple-200', - [ProposalStatus.VotingOpen]: 'blue-200', - [ProposalStatus.Counting]: 'green-300', - [ProposalStatus.Closed]: 'gray-200', + [ProposalStatus.Upcoming]: 'purple-200', + [ProposalStatus.Commencing]: 'blue-200', + [ProposalStatus.Holding]: 'green-300', + [ProposalStatus.Ended]: 'gray-200', } diff --git a/packages/shared/components/organisms/ProposalCard.svelte b/packages/shared/components/organisms/ProposalCard.svelte index 8d0bbbbf2a..ff078928b9 100644 --- a/packages/shared/components/organisms/ProposalCard.svelte +++ b/packages/shared/components/organisms/ProposalCard.svelte @@ -8,9 +8,12 @@ import { FontWeight, Position } from '../enums' import { Icon } from '@auxiliary/icon/enums' import { localize } from '@core/i18n' + import { proposalsState } from '@core/governance/stores' export let proposal: IProposal + $: proposalState = $proposalsState[proposal?.id] + function handleProposalClick(): void { $selectedProposal = proposal $governanceRouter.goTo(GovernanceRoute.Details) @@ -20,7 +23,7 @@
{#if proposal.organization} @@ -38,7 +41,7 @@ {proposal.title}
- + {#if proposal.hasVoted} diff --git a/packages/shared/components/organisms/ProposalInformation.svelte b/packages/shared/components/organisms/ProposalInformation.svelte index 1a279945e9..8b179fe84e 100644 --- a/packages/shared/components/organisms/ProposalInformation.svelte +++ b/packages/shared/components/organisms/ProposalInformation.svelte @@ -7,7 +7,7 @@ const proposalInformation = { countingEnds: formatDate( - milestoneToDate($networkStatus.currentMilestone, $selectedProposal.milestones?.closed), + milestoneToDate($networkStatus.currentMilestone, $selectedProposal.milestones?.ended), DATE_FORMAT ), eventId: truncateString($selectedProposal?.id, 9, 9), diff --git a/packages/shared/lib/core/governance/actions/index.ts b/packages/shared/lib/core/governance/actions/index.ts index bcb399c0a7..7afbeb7557 100644 --- a/packages/shared/lib/core/governance/actions/index.ts +++ b/packages/shared/lib/core/governance/actions/index.ts @@ -1 +1,2 @@ +export * from './proposalsStatePolling' export * from './setVotingPower' diff --git a/packages/shared/lib/core/governance/actions/proposalsStatePolling.ts b/packages/shared/lib/core/governance/actions/proposalsStatePolling.ts new file mode 100644 index 0000000000..ffdf3c6bea --- /dev/null +++ b/packages/shared/lib/core/governance/actions/proposalsStatePolling.ts @@ -0,0 +1,13 @@ +import { PROPOSAL_STATUS_POLL_INTERVAL } from '../constants' +import { updateProposalsState } from '../stores' + +let pollInterval + +export async function pollProposalsState(): Promise { + await updateProposalsState() + pollInterval = setInterval(() => void updateProposalsState(), PROPOSAL_STATUS_POLL_INTERVAL) +} + +export function clearPollProposalsStateInterval(): void { + clearInterval(pollInterval) +} diff --git a/packages/shared/lib/core/governance/constants/index.ts b/packages/shared/lib/core/governance/constants/index.ts new file mode 100644 index 0000000000..6be61878e3 --- /dev/null +++ b/packages/shared/lib/core/governance/constants/index.ts @@ -0,0 +1 @@ +export * from './proposal-status-poll-interval.constant' diff --git a/packages/shared/lib/core/governance/constants/proposal-status-poll-interval.constant.ts b/packages/shared/lib/core/governance/constants/proposal-status-poll-interval.constant.ts new file mode 100644 index 0000000000..5ee80be050 --- /dev/null +++ b/packages/shared/lib/core/governance/constants/proposal-status-poll-interval.constant.ts @@ -0,0 +1,4 @@ +import { SECONDS_PER_MILESTONE } from '@core/network/constants' +import { MILLISECONDS_PER_SECOND } from '@core/utils/constants' + +export const PROPOSAL_STATUS_POLL_INTERVAL = SECONDS_PER_MILESTONE * MILLISECONDS_PER_SECOND diff --git a/packages/shared/lib/core/governance/enums/proposal-status.enum.ts b/packages/shared/lib/core/governance/enums/proposal-status.enum.ts index a6977b6171..498055b08c 100644 --- a/packages/shared/lib/core/governance/enums/proposal-status.enum.ts +++ b/packages/shared/lib/core/governance/enums/proposal-status.enum.ts @@ -1,6 +1,6 @@ export enum ProposalStatus { - Announcement = 'announcement', - VotingOpen = 'votingOpen', - Counting = 'counting', - Closed = 'closed', + Upcoming = 'upcoming', + Commencing = 'commencing', + Holding = 'holding', + Ended = 'ended', } diff --git a/packages/shared/lib/core/governance/stores/index.ts b/packages/shared/lib/core/governance/stores/index.ts index 0b1e9b751e..8a2efd8c37 100644 --- a/packages/shared/lib/core/governance/stores/index.ts +++ b/packages/shared/lib/core/governance/stores/index.ts @@ -1 +1,2 @@ +export * from './proposals-state.store' export * from './selected-proposal.store' diff --git a/packages/shared/lib/core/governance/stores/proposals-state.store.ts b/packages/shared/lib/core/governance/stores/proposals-state.store.ts new file mode 100644 index 0000000000..316bc6b521 --- /dev/null +++ b/packages/shared/lib/core/governance/stores/proposals-state.store.ts @@ -0,0 +1,34 @@ +import { get } from 'svelte/store' +import type { EventStatus } from '@iota/wallet' +import { getVotingProposalState } from '@core/profile-manager/api' +import { persistent } from '@core/utils/store' + +export const proposalsState = persistent<{ [key in string]: EventStatus }>('proposalsState', {}) + +export async function addProposalState(eventId: string): Promise { + const _proposalsState = get(proposalsState) + const hasRegisteredProposalStatus = Object.keys(_proposalsState).includes(eventId) + if (!hasRegisteredProposalStatus) { + const proposalStatus = await getVotingProposalState(eventId) + _proposalsState[eventId] = proposalStatus + proposalsState.set(_proposalsState) + } +} + +export function removeProposalState(eventId: string): void { + const _proposalsState = get(proposalsState) + const hasRegisteredProposalStatus = Object.keys(_proposalsState).includes(eventId) + if (hasRegisteredProposalStatus) { + delete _proposalsState[eventId] + proposalsState.set(_proposalsState) + } +} + +export async function updateProposalsState(): Promise { + const _proposalsState = get(proposalsState) + for (const eventId in _proposalsState) { + const proposalStatus = await getVotingProposalState(eventId) + _proposalsState[eventId] = proposalStatus + } + proposalsState.set(_proposalsState) +} diff --git a/packages/shared/lib/core/governance/utils/createProposalsFromEvents.ts b/packages/shared/lib/core/governance/utils/createProposalsFromEvents.ts index 0ee826a1c9..f193d1ded5 100644 --- a/packages/shared/lib/core/governance/utils/createProposalsFromEvents.ts +++ b/packages/shared/lib/core/governance/utils/createProposalsFromEvents.ts @@ -12,12 +12,12 @@ export function createProposalsFromEvents(events: Event[]): IProposal[] { const proposal = { id, title: data.name, - status: ProposalStatus.Announcement, + status: ProposalStatus.Upcoming, milestones: { - [ProposalStatus.Announcement]: 0, // TODO: fix this - [ProposalStatus.VotingOpen]: data.milestoneIndexCommence, - [ProposalStatus.Counting]: data.milestoneIndexStart, - [ProposalStatus.Closed]: data.milestoneIndexEnd, + [ProposalStatus.Upcoming]: 0, // TODO: fix this + [ProposalStatus.Commencing]: data.milestoneIndexCommence, + [ProposalStatus.Holding]: data.milestoneIndexStart, + [ProposalStatus.Ended]: data.milestoneIndexEnd, }, // TODO: figure out a better way to get the node URLs nodeUrls: get(activeProfile)?.clientOptions?.nodes, diff --git a/packages/shared/lib/core/profile-manager/api/deregisterParticipationEvent.ts b/packages/shared/lib/core/profile-manager/api/deregisterParticipationEvent.ts index 2d57fe13cd..3c8761e3a4 100644 --- a/packages/shared/lib/core/profile-manager/api/deregisterParticipationEvent.ts +++ b/packages/shared/lib/core/profile-manager/api/deregisterParticipationEvent.ts @@ -1,8 +1,10 @@ -import { profileManager } from '@core/profile-manager/stores' -import type { EventId } from '@iota/wallet' import { get } from 'svelte/store' +import type { EventId } from '@iota/wallet' +import { removeProposalState } from '@core/governance' +import { profileManager } from '../stores' export function deregisterParticipationEvent(eventId: EventId): Promise { const manager = get(profileManager) + removeProposalState(eventId) return manager.deregisterParticipationEvent(eventId) } diff --git a/packages/shared/lib/core/profile-manager/api/getVotingProposalState.ts b/packages/shared/lib/core/profile-manager/api/getVotingProposalState.ts new file mode 100644 index 0000000000..b40f7b3cc9 --- /dev/null +++ b/packages/shared/lib/core/profile-manager/api/getVotingProposalState.ts @@ -0,0 +1,8 @@ +import { get } from 'svelte/store' +import { EventStatus, EventId } from '@iota/wallet' +import { profileManager } from '../stores' + +export function getVotingProposalState(eventId: EventId): Promise { + const manager = get(profileManager) + return manager.getParticipationEventStatus(eventId) +} diff --git a/packages/shared/lib/core/profile-manager/api/index.ts b/packages/shared/lib/core/profile-manager/api/index.ts index 5eb5c2f978..b03d53b3c0 100644 --- a/packages/shared/lib/core/profile-manager/api/index.ts +++ b/packages/shared/lib/core/profile-manager/api/index.ts @@ -11,6 +11,7 @@ export * from './getLedgerNanoStatus' export * from './getNodeInfo' export * from './getVotingProposals' export * from './getVotingProposal' +export * from './getVotingProposalState' export * from './isStrongholdUnlocked' export * from './recoverAccounts' export * from './registerParticipationEvent' diff --git a/packages/shared/lib/core/profile-manager/api/registerParticipationEvent.ts b/packages/shared/lib/core/profile-manager/api/registerParticipationEvent.ts index 60a2bcfb3c..93b651d777 100644 --- a/packages/shared/lib/core/profile-manager/api/registerParticipationEvent.ts +++ b/packages/shared/lib/core/profile-manager/api/registerParticipationEvent.ts @@ -1,8 +1,11 @@ +import { addProposalState } from '@core/governance' import { profileManager } from '@core/profile-manager/stores' import type { Event, EventId, Node } from '@iota/wallet' import { get } from 'svelte/store' -export function registerParticipationEvent(eventId: EventId, nodes: Node[]): Promise { +export async function registerParticipationEvent(eventId: EventId, nodes: Node[]): Promise { const manager = get(profileManager) - return manager.registerParticipationEvent(eventId, nodes) + const event = await manager.registerParticipationEvent(eventId, nodes) + await addProposalState(eventId) + return event } diff --git a/packages/shared/lib/core/profile-manager/interfaces/profile-manager.interface.ts b/packages/shared/lib/core/profile-manager/interfaces/profile-manager.interface.ts index ab3f8cfde2..670040852a 100644 --- a/packages/shared/lib/core/profile-manager/interfaces/profile-manager.interface.ts +++ b/packages/shared/lib/core/profile-manager/interfaces/profile-manager.interface.ts @@ -9,6 +9,7 @@ import type { Node, NodeInfoWrapper, WalletEvent, + EventStatus, } from '@iota/wallet' import { IAuth } from '@core/network' @@ -40,6 +41,7 @@ export interface IProfileManager { getLedgerNanoStatus(): Promise getParticipationEvent(eventId: EventId): Promise getParticipationEvents(): Promise + getParticipationEventStatus(eventId: EventId): Promise hexToBech32(hex: string, bech32Hrp?: string): Promise isStrongholdPasswordAvailable(): Promise listen(eventTypes: EventType[], callback: WalletApiEventHandler): void diff --git a/packages/shared/lib/core/profile/actions/active-profile/logout.ts b/packages/shared/lib/core/profile/actions/active-profile/logout.ts index 1b6997a948..b2a1d28338 100644 --- a/packages/shared/lib/core/profile/actions/active-profile/logout.ts +++ b/packages/shared/lib/core/profile/actions/active-profile/logout.ts @@ -1,5 +1,6 @@ import { closePopup } from '@auxiliary/popup' import { resetSelectedAccount } from '@core/account' +import { clearPollProposalsStateInterval } from '@core/governance' import { isPollingLedgerDeviceStatus, stopPollingLedgerNanoStatus } from '@core/ledger' import { clearPollMarketPrices } from '@core/market/actions' import { clearPollNetworkInterval } from '@core/network' @@ -33,6 +34,7 @@ export function logout(clearActiveProfile: boolean = true, _lockStronghold: bool clearPollNetworkInterval() clearPollMarketPrices() + clearPollProposalsStateInterval() const _activeProfile = get(activeProfile) if (_activeProfile) { const manager = get(profileManager) diff --git a/packages/shared/lib/tests/__mocks__/profile-manager.mock.ts b/packages/shared/lib/tests/__mocks__/profile-manager.mock.ts index bad4ef2d30..d9a5925dec 100644 --- a/packages/shared/lib/tests/__mocks__/profile-manager.mock.ts +++ b/packages/shared/lib/tests/__mocks__/profile-manager.mock.ts @@ -12,6 +12,7 @@ import type { Event, Node, EventId, + EventStatus, } from '@iota/wallet' import { IAccount } from '@core/account' @@ -160,6 +161,10 @@ export class ProfileManagerMock implements IProfileManager { throw new Error('Method not implemented.') } + getParticipationEventStatus(eventId: EventId): Promise { + throw new Error('Method not implemented.') + } + hexToBech32(hex: string, bech32Hrp?: string): Promise { throw new Error('Method not implemented.') } diff --git a/packages/shared/locales/en.json b/packages/shared/locales/en.json index eec85c3009..ff2e29c8f1 100644 --- a/packages/shared/locales/en.json +++ b/packages/shared/locales/en.json @@ -583,10 +583,10 @@ "successRemove": "Proposal successfully removed" }, "statusTimeline": { - "announcement": "Announcement", - "votingOpen": "Voting open", - "counting": "Counting starts", - "closed": "Counting stops" + "upcoming": "Announcement", + "commencing": "Voting open", + "holding": "Counting starts", + "ended": "Counting stops" }, "details": { "fullProposal": "Read the full proposal", @@ -1983,10 +1983,10 @@ "locked": "locked", "smartContract": "smart contract", "proposalStatus": { - "announcement": "Announcement", - "votingOpen": "Voting open", - "counting": "Counting", - "closed": "Closed" + "upcoming": "Announcement", + "commencing": "Voting open", + "holding": "Counting", + "ended": "Closed" } }, "menus": {