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

SOV-3010: Individual Proposal page #583

Merged
merged 21 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/angry-mails-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frontend": patch
---

SOV-3196: add voting tooltips to Voting proposals page
5 changes: 5 additions & 0 deletions .changeset/fresh-goats-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frontend": patch
---

SOV-3225: Open proposal on row click
5 changes: 5 additions & 0 deletions .changeset/giant-ducks-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frontend": patch
---

SOV-3195: add voting power section
5 changes: 5 additions & 0 deletions .changeset/many-fishes-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frontend": patch
---

fix: Bitocracy proposal issues
6 changes: 6 additions & 0 deletions .changeset/olive-olives-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"frontend": patch
"@sovryn/contracts": patch
---

SOV-3239: Support/Reject button in Cast your vote
6 changes: 6 additions & 0 deletions .changeset/pink-buckets-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"frontend": patch
"@sovryn/ui": patch
---

SOV-3010: Individual Proposal page
5 changes: 5 additions & 0 deletions .changeset/pink-monkeys-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"frontend": patch
---

SOV-3011: Export CSV
6 changes: 6 additions & 0 deletions .changeset/yellow-tips-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"frontend": patch
"@sovryn/contracts": patch
---

SOV-3009: voting actions
4 changes: 4 additions & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@
"react-dom": "18.2.0",
"react-helmet-async": "1.3.0",
"react-i18next": "12.0.0",
"react-markdown": "8.0.7",
"react-router-dom": "6.4.2",
"react-scripts": "5.0.1",
"react-timer-hook": "3.0.7",
"reactjs-localstorage": "1.0.1",
"remark-gfm": "3.0.1",
"rxjs": "7.5.6",
"sanitize-html": "2.11.0",
"socket.io-client": "4.5.4",
"utf8": "^3.0.0"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { FC, useMemo } from 'react';

import { t } from 'i18next';

import { Paragraph, ParagraphSize } from '@sovryn/ui';

import { VP } from '../../5_pages/StakePage/StakePage.constants';
import { useGetPersonalStakingStatistics } from '../../5_pages/StakePage/components/PersonalStakingStatistics/hooks/useGetPersonalStakingStatistics';
import { useGetStakingStatistics } from '../../5_pages/StakePage/components/StakingStatistics/hooks/useGetStakingStatistics';
import { useAccount } from '../../../hooks/useAccount';
import { translations } from '../../../locales/i18n';
import { decimalic, fromWei } from '../../../utils/math';
import { LabeledAmount } from './ProposalVotingPower.utils';

export const ProposalVotingPower: FC = () => {
const { account } = useAccount();
const { votingPower } = useGetPersonalStakingStatistics();
const { totalVotingPower } = useGetStakingStatistics();

const votingPowerShare = useMemo(() => {
if (!votingPower || !totalVotingPower) {
return 0;
}
const votingPowerDecimal = decimalic(votingPower.toString());
const totalVotingPowerDecimal = decimalic(totalVotingPower.toString());
const getVotingPowerShare = votingPowerDecimal
.div(totalVotingPowerDecimal)
.mul(100);
return getVotingPowerShare.toNumber();
}, [votingPower, totalVotingPower]);

return (
<div className="bg-gray-90 rounded mb-6 p-6">
<Paragraph size={ParagraphSize.base} className="font-medium mb-6">
{t(translations.proposalVotingPower.title)}
</Paragraph>

<div className="flex items-center">
<LabeledAmount
label={t(translations.proposalVotingPower.votingPower)}
amount={account ? fromWei(votingPower) : undefined}
amountSuffix={VP}
/>
<LabeledAmount
label={t(translations.proposalVotingPower.votingPowerShare)}
amount={account ? votingPowerShare : undefined}
amountSuffix="%"
precision={5}
/>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';

import { t } from 'i18next';

import { Paragraph, ParagraphSize } from '@sovryn/ui';

import { AmountRenderer } from '../../2_molecules/AmountRenderer/AmountRenderer';
import { TOKEN_RENDER_PRECISION } from '../../../constants/currencies';
import { translations } from '../../../locales/i18n';

export const LabeledAmount = ({
label,
amount,
amountSuffix = '',
precision = TOKEN_RENDER_PRECISION,
}) => (
<div className="basis-full font-medium">
<Paragraph
size={ParagraphSize.base}
className="text-gray-30 md:mb-3 leading-4 text-xs"
>
{label}
</Paragraph>
{amount ? (
<AmountRenderer
value={amount}
suffix={amountSuffix}
precision={precision}
/>
) : (
t(translations.common.na)
)}
</div>
);
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import React, { FC, useMemo } from 'react';
import React, { FC, useCallback, useMemo } from 'react';

import { t } from 'i18next';
import { nanoid } from 'nanoid';

import {
Bar,
Button,
ButtonStyle,
HelperButton,
NotificationType,
Paragraph,
ParagraphSize,
} from '@sovryn/ui';
import { Decimal } from '@sovryn/utils';

import { ExportCSV } from '../../2_molecules/ExportCSV/ExportCSV';
import { useNotificationContext } from '../../../contexts/NotificationContext';
import { useBlockNumber } from '../../../hooks/useBlockNumber';
import { translations } from '../../../locales/i18n';
import { Proposal } from '../../../utils/graphql/rsk/generated';
import { decimalic, formatValue } from '../../../utils/math';
import { dateFormat } from '../../../utils/helpers';
import { decimalic, formatValue, fromWei } from '../../../utils/math';

export type ProposalVotingResultsProps = {
proposal: Proposal;
onExportCSV?: () => void;
};

export const ProposalVotingResults: FC<ProposalVotingResultsProps> = ({
proposal,
onExportCSV,
}) => {
const { addNotification } = useNotificationContext();
const currentBlock = useBlockNumber().value;
const { votesFor, votesAgainst, endBlock, emittedBy, quorum } = proposal;
const { majorityPercentageVotes, quorumPercentageVotes } = emittedBy;
Expand All @@ -35,10 +37,9 @@ export const ProposalVotingResults: FC<ProposalVotingResultsProps> = ({
Decimal.fromBigNumberString(votesAgainst),
);

const support = Decimal.fromBigNumberString(votesFor)
.div(votes)
.mul(100)
.toNumber();
const support = votes.isZero()
? 0
: Decimal.fromBigNumberString(votesFor).div(votes).mul(100).toNumber();

const turnout = votes
.div(
Expand All @@ -56,6 +57,29 @@ export const ProposalVotingResults: FC<ProposalVotingResultsProps> = ({
};
}, [quorum, quorumPercentageVotes, votesAgainst, votesFor]);

const exportData = useCallback(async () => {
if (!proposal.votes || !proposal.votes.length) {
addNotification({
type: NotificationType.warning,
title: t(translations.common.tables.actions.noDataToExport),
content: '',
dismissible: true,
id: nanoid(),
});
return [];
}

return proposal.votes?.map(vote => ({
date: dateFormat(vote.timestamp),
address: vote.voter.id,
vote: vote.support
? t(translations.proposalPage.support)
: t(translations.proposalPage.reject),
votingPower: fromWei(vote.votes),
TXID: vote.transaction.id,
}));
}, [addNotification, proposal]);

return (
<div className="w-full bg-gray-90 p-6">
<div className="flex flex-row justify-between mb-8">
Expand All @@ -67,10 +91,10 @@ export const ProposalVotingResults: FC<ProposalVotingResultsProps> = ({
{t(translations.proposalVotingResults.inProgress)}
</Paragraph>
) : (
<Button
text={t(translations.proposalVotingResults.exportCSV)}
onClick={onExportCSV}
style={ButtonStyle.secondary}
<ExportCSV
getData={exportData}
filename="proposal-voting-results"
disabled={!proposal.votes || !proposal.votes.length}
/>
)}
</div>
Expand Down
25 changes: 1 addition & 24 deletions apps/frontend/src/app/5_pages/BitocracyPage/BitocracyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,15 @@ import {
ParagraphSize,
} from '@sovryn/ui';

import { ProposalVotingResults } from '../../3_organisms/ProposalVotingResults';
import { useAccount } from '../../../hooks/useAccount';
import { translations } from '../../../locales/i18n';
import { sharedState } from '../../../store/rxjs/shared-state';
import { Proposal } from '../../../utils/graphql/rsk/generated';
import { useGetPersonalStakingStatistics } from '../StakePage/components/PersonalStakingStatistics/hooks/useGetPersonalStakingStatistics';
import { Proposals } from './components/Proposals/Proposals';
import { useGetProposals } from './hooks/useGetProposals';
import { translations } from '../../../locales/i18n';

const pageTranslations = translations.bitocracyPage;

// DUMMY DATA, REMOVE WHEN REAL DATA IS AVAILABLE
const proposal = {
id: '0x6496df39d000478a7a7352c01e0e713835051ccd-32',
votesFor: '25642394317449679336562867',
votesAgainst: '9204395962492958076500991',
endBlock: 5505959,
startBlock: 5505952,
quorum: '20928484835262004265672060',
majorityPercentage: '73249696923417014929852210',
emittedBy: {
id: '0x6496df39d000478a7a7352c01e0e713835051ccd',
majorityPercentageVotes: 70,
quorumPercentageVotes: 20,
},
} as Proposal;

const BitocracyPage: FC = () => {
const { account } = useAccount();
const { votingPower } = useGetPersonalStakingStatistics();
Expand Down Expand Up @@ -88,11 +70,6 @@ const BitocracyPage: FC = () => {
</div>
)}
<Proposals proposals={proposals} loading={loading} />

<div className="mt-12" />
<ProposalVotingResults proposal={proposal} />
<div className="mt-12" />
<ProposalVotingResults proposal={{ ...proposal, endBlock: 0 }} />
</div>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Proposal } from '../../../utils/graphql/rsk/generated';
import { dateFormat } from '../../../utils/helpers';
import { BLOCK_TIME_IN_SECONDS } from './BitocracyPage.constants';

export const renderEndDate = (item: Proposal) => {
export const renderProposalEndDate = (item: Proposal) => {
const secondsBetweenBlocks =
(item.endBlock - item.startBlock) * BLOCK_TIME_IN_SECONDS;
const endTime = item.timestamp + secondsBetweenBlocks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Icon, IconNames, Paragraph } from '@sovryn/ui';

import { translations } from '../../../../../locales/i18n';
import { Proposal } from '../../../../../utils/graphql/rsk/generated';
import { prettifyId, renderEndDate } from '../../BitocracyPage.utils';
import { renderProposalEndDate } from '../../BitocracyPage.utils';
import { ProposalStatus } from '../ProposalStatus/ProposalStatus';
import { ProposalType } from '../ProposalType/ProposalType';

Expand All @@ -23,7 +23,7 @@ export const ProposalCardsMobile: FC<ProposalCardsMobileProps> = ({
const navigate = useNavigate();

const handleCardClick = useCallback(
(proposal: Proposal) => navigate(`/bitocracy/${prettifyId(proposal.id)}`),
(proposal: Proposal) => navigate(`/bitocracy/${proposal.id}`),
[navigate],
);

Expand Down Expand Up @@ -70,7 +70,7 @@ export const ProposalCardsMobile: FC<ProposalCardsMobileProps> = ({
</div>
<div className="text-gray-30 text-xs flex items-center justify-between">
<span>{t(translations.bitocracyPage.table.endDate)}</span>
<span>{renderEndDate(proposal)}</span>
<span>{renderProposalEndDate(proposal)}</span>
</div>
</div>
));
Expand Down
Loading