From c7a8c25faca058bbf0691b8a173da9d4abb5ccb5 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Sun, 10 Apr 2022 22:24:12 -0700 Subject: [PATCH 01/20] Combined yes, no, abstain, and turnout bars into one. --- apps/dapp/components/Progress.tsx | 35 +++++--- apps/dapp/components/ProposalDetails.tsx | 104 +++++++++++++++-------- 2 files changed, 91 insertions(+), 48 deletions(-) diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index 683b0c41d..881633481 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -1,19 +1,28 @@ // Need color and width literals here because tailwind isn't able to generate // the right classNames for the production build otherwise. +export const ProgressMany = ({ + data, +}: { + data: { + value: number + color: string + }[] +}) => ( +
+ {data.map(({ value, color }, index) => ( +
+ ))} +
+) + export const Progress = ({ - turnout, + value, color, }: { - turnout: number + value: number color: string -}) => { - turnout = Math.floor(turnout) - return ( -
-
-
- ) -} +}) => diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 0f14f67e1..35bac1b8c 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -61,7 +61,7 @@ import { CopyToClipboard } from './CopyToClipboard' import { CosmosMessageDisplay } from './CosmosMessageDisplay' import { Execute } from './Execute' import SvgAbstain from './icons/Abstain' -import { Progress } from './Progress' +import { Progress, ProgressMany } from './Progress' import { getEnd } from './ProposalList' import { StakingModal, StakingMode } from './StakingModal' import { Vote, VoteChoice } from './Vote' @@ -233,6 +233,10 @@ export function ProposalDetailsSidebar({ undefined, localeOptions ) + const abstainPercent = ((abstainVotes / totalWeight) * 100).toLocaleString( + undefined, + localeOptions + ) if (!proposal) { return
Error, no proposal
@@ -248,15 +252,21 @@ export function ProposalDetailsSidebar({

Details

-

Proposal

+

+ Proposal +

# {proposal.id.toString().padStart(6, '0')}

-

Status

+

+ Status +

-

Proposer

+

+ Proposer +

@@ -300,47 +310,37 @@ export function ProposalDetailsSidebar({

Referendum status

-
-

Yours

+
+

+ Your vote +

{!walletVote && ( -

+

{proposal.status === 'open' ? 'Pending...' : 'None'}

)} {walletVote === 'yes' && ( -

+

Yes

)} {walletVote === 'no' && ( -

+

No

)} {walletVote === 'abstain' && ( -

+

Abstain

)} -

Yes

-
-
- -
-

{yesPercent}%

-
-

No

-
-
- -
-

{noPercent}%

-
-

Threshold

-
+

+ Threshold +

+
@@ -348,11 +348,13 @@ export function ProposalDetailsSidebar({
{quorum && ( <> -

Quorum

-
+

+ Quorum +

+
@@ -360,16 +362,48 @@ export function ProposalDetailsSidebar({
)} -

Turnout

-
+

+ Turnout +

+
-

{turnoutPercent}%

+

+ Yes +

+
+

{yesPercent}%

+
+

+ No +

+
+

{noPercent}%

+
+

+ Abstain +

+
+

{abstainPercent}%

+
) From 15fa97d83ccc18e3cb0ceb77cdf3d34d0eee9e11 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 12 Apr 2022 02:29:02 -0700 Subject: [PATCH 02/20] Combined threshold and quorum into Needs bar representing what a passing bar looks like. Also added explanatory text under the bars. --- apps/dapp/components/ContractView.tsx | 2 +- apps/dapp/components/ProposalDetails.tsx | 179 +++++++++++------- .../pages/dao/[contractAddress]/index.tsx | 3 +- apps/dapp/pages/starred.tsx | 2 +- apps/dapp/selectors/cosm.ts | 27 +++ apps/dapp/selectors/daos.ts | 41 +--- apps/dapp/selectors/multisigs.ts | 8 +- apps/dapp/selectors/proposals.ts | 11 +- 8 files changed, 169 insertions(+), 104 deletions(-) diff --git a/apps/dapp/components/ContractView.tsx b/apps/dapp/components/ContractView.tsx index 14e075fab..f30f3c1f0 100644 --- a/apps/dapp/components/ContractView.tsx +++ b/apps/dapp/components/ContractView.tsx @@ -12,7 +12,7 @@ import { useThemeContext } from 'ui' import { Button } from '@components' import { contractInstantiateTime } from 'selectors/contracts' -import { isMemberSelector } from 'selectors/daos' +import { isMemberSelector } from 'selectors/cosm' import { cw20Balances, cw20TokenInfo, diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 35bac1b8c..ec3e9246d 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -28,6 +28,7 @@ import { proposalUpdateCountAtom, proposalsUpdated } from 'atoms/proposals' import { MarkdownPreview } from 'components/MarkdownPreview' import { cosmWasmSigningClient, + isMemberSelector, walletAddress as walletAddressSelector, } from 'selectors/cosm' import { @@ -36,6 +37,7 @@ import { proposalStartBlockSelector, proposalTallySelector, votingPowerAtHeightSelector, + WalletVote, walletVoteSelector, } from 'selectors/proposals' import { walletTokenBalanceLoading } from 'selectors/treasury' @@ -181,6 +183,8 @@ export function ProposalDetailsSidebar({ walletVoteSelector({ contractAddress, proposalId }) ) + const { member } = useRecoilValue(isMemberSelector(contractAddress)) + const configWrapper = new ContractConfigWrapper(sigConfig) const tokenDecimals = configWrapper.gov_token_decimals @@ -247,6 +251,12 @@ export function ProposalDetailsSidebar({ !!multisig, tokenDecimals ) + const thresholdValue = threshold?.endsWith('%') + ? Number(threshold.slice(0, -1)) + : undefined + const quorumValue = quorum?.endsWith('%') + ? Number(quorum.slice(0, -1)) + : undefined return (
@@ -311,81 +321,122 @@ export function ProposalDetailsSidebar({
-

- Your vote -

- {!walletVote && ( -

- {proposal.status === 'open' ? 'Pending...' : 'None'} -

- )} - {walletVote === 'yes' && ( -

- Yes -

- )} - {walletVote === 'no' && ( -

- No -

- )} - {walletVote === 'abstain' && ( -

- Abstain -

+ {member && ( + <> +

+ Your vote +

+ + {walletVote === WalletVote.Yes ? ( +

+ Yes +

+ ) : walletVote === WalletVote.No ? ( +

+ No +

+ ) : walletVote === WalletVote.Abstain ? ( +

+ Abstain +

+ ) : walletVote === WalletVote.Veto ? ( +

+ Veto +

+ ) : walletVote ? ( +

+ Unknown: {walletVote} +

+ ) : ( +

+ {proposal.status === 'open' ? 'Pending...' : 'None'} +

+ )} + )} +

- Threshold + Needs

-
-
- -
-

{threshold}

+
+ {multisig ? ( +

{threshold}

+ ) : thresholdValue !== undefined ? ( +
+ +
+ ) : null}
- {quorum && ( + + {!multisig && threshold !== undefined && thresholdValue !== undefined && ( <> -

- Quorum +

+

+ + {quorumValue === undefined + ? threshold.slice(0, -1) + : Number((thresholdValue / 100) * quorumValue).toLocaleString( + undefined, + localeOptions + )} + % yes + + ,{' '} + {quorum ?? threshold} turnout

-
-
- -
-

{quorum}

-
)} +

- Turnout + Has

-
-
- -
-

{turnoutPercent}%

+
+
+ +

+

+ + {Number(yesPercent).toLocaleString(undefined, localeOptions)}% yes + + , {turnoutPercent}% turnout +

+

Yes

diff --git a/apps/dapp/pages/dao/[contractAddress]/index.tsx b/apps/dapp/pages/dao/[contractAddress]/index.tsx index 41d667f6a..f02d3d77b 100644 --- a/apps/dapp/pages/dao/[contractAddress]/index.tsx +++ b/apps/dapp/pages/dao/[contractAddress]/index.tsx @@ -25,10 +25,9 @@ import { } from 'components/ContractView' import ErrorBoundary from 'components/ErrorBoundary' import { StakingModal, StakingMode } from 'components/StakingModal' -import { CHAIN_RPC_ENDPOINT } from 'selectors/cosm' +import { CHAIN_RPC_ENDPOINT, isMemberSelector } from 'selectors/cosm' import { daoSelector, - isMemberSelector, proposalCount, tokenConfig, totalStaked, diff --git a/apps/dapp/pages/starred.tsx b/apps/dapp/pages/starred.tsx index fe07ca846..61ea5b2a8 100644 --- a/apps/dapp/pages/starred.tsx +++ b/apps/dapp/pages/starred.tsx @@ -7,8 +7,8 @@ import { MapIcon, PlusIcon, StarIcon } from '@heroicons/react/outline' import { pinnedDaosAtom, pinnedMultisigsAtom } from 'atoms/pinned' import { ContractCard } from 'components/ContractCard' +import { isMemberSelector } from 'selectors/cosm' import { - isMemberSelector, memberDaoSelector, proposalCount, } from 'selectors/daos' diff --git a/apps/dapp/selectors/cosm.ts b/apps/dapp/selectors/cosm.ts index cfe6f0720..97b7a89ff 100644 --- a/apps/dapp/selectors/cosm.ts +++ b/apps/dapp/selectors/cosm.ts @@ -15,6 +15,11 @@ import { walletTokenBalanceUpdateCountAtom } from './treasury' export type WalletConnection = 'keplr' | '' +export interface MemberStatus { + member: boolean + weight: number +} + export const CHAIN_RPC_ENDPOINT = process.env.NEXT_PUBLIC_CHAIN_RPC_ENDPOINT || '' const CHAIN_ID = process.env.NEXT_PUBLIC_CHAIN_ID @@ -185,3 +190,25 @@ export const voterInfoSelector = selectorFamily({ } }, }) + +export const isMemberSelector = selectorFamily({ + key: 'isMember', + get: + (contractAddress) => + async ({ get }) => { + const wallet = get(walletAddress) + if (!wallet) { + return { + member: false, + weight: 0, + } + } + const voterInfo = get( + voterInfoSelector({ contractAddress, walletAddress: wallet }) + ) + return { + member: voterInfo.weight !== 0, + weight: voterInfo.weight, + } + }, +}) diff --git a/apps/dapp/selectors/daos.ts b/apps/dapp/selectors/daos.ts index d4a3c6be0..67772c40d 100644 --- a/apps/dapp/selectors/daos.ts +++ b/apps/dapp/selectors/daos.ts @@ -1,24 +1,23 @@ -import { cosmWasmClient, voterInfoSelector } from 'selectors/cosm' -import { contractsByCodeId } from 'selectors/contracts' -import { selector, selectorFamily } from 'recoil' -import { DAO_CODE_ID, NATIVE_DENOM } from 'util/constants' +import { selectorFamily } from 'recoil' + +import { TokenInfoResponse } from '@dao-dao/types/contracts/cw20-gov' import { Config, ConfigResponse, Duration, } from '@dao-dao/types/contracts/cw3-dao' -import { TokenInfoResponse } from '@dao-dao/types/contracts/cw20-gov' + +import { contractsByCodeId } from 'selectors/contracts' +import { cosmWasmClient, isMemberSelector } from 'selectors/cosm' +import { DAO_CODE_ID, NATIVE_DENOM } from 'util/constants' + + import { nativeBalance, walletAddress, walletTokenBalanceUpdateCountAtom, } from './treasury' -export interface MemberStatus { - member: boolean - weight: number -} - export interface DaoListType { address: string member: boolean @@ -76,28 +75,6 @@ export const proposalCount = selectorFamily({ }, }) -export const isMemberSelector = selectorFamily({ - key: 'isMember', - get: - (contractAddress) => - async ({ get }) => { - const wallet = get(walletAddress) - if (!wallet) { - return { - member: false, - weight: 0, - } - } - const voterInfo = get( - voterInfoSelector({ contractAddress, walletAddress: wallet }) - ) - return { - member: voterInfo.weight !== 0, - weight: voterInfo.weight, - } - }, -}) - export const memberDaoSelector = selectorFamily({ key: 'memberDaosSelector', get: diff --git a/apps/dapp/selectors/multisigs.ts b/apps/dapp/selectors/multisigs.ts index 4ee76fc5d..b9714873b 100644 --- a/apps/dapp/selectors/multisigs.ts +++ b/apps/dapp/selectors/multisigs.ts @@ -1,9 +1,11 @@ -import { ConfigResponse } from '@dao-dao/types/contracts/cw3-multisig' import { selectorFamily } from 'recoil' + +import { ConfigResponse } from '@dao-dao/types/contracts/cw3-multisig' + import { contractsByCodeId } from 'selectors/contracts' -import { cosmWasmClient } from 'selectors/cosm' +import { cosmWasmClient, isMemberSelector } from 'selectors/cosm' import { MULTISIG_CODE_ID } from 'util/constants' -import { isMemberSelector } from './daos' + import { walletAddress } from './treasury' export interface MultisigListType { diff --git a/apps/dapp/selectors/proposals.ts b/apps/dapp/selectors/proposals.ts index 782965ae4..5429b7252 100644 --- a/apps/dapp/selectors/proposals.ts +++ b/apps/dapp/selectors/proposals.ts @@ -6,6 +6,7 @@ import { VoteInfo, VoteResponse, } from '@dao-dao/types/contracts/cw3-dao' + import { contractProposalMapAtom, proposalsRequestIdAtom, @@ -51,6 +52,14 @@ export type ProposalExecuteParams = { walletAddress: string } +export enum WalletVote { + Yes = 'yes', + No = 'no', + Abstain = 'abstain', + Veto = 'veto', +} +type WalletVoteValue = `${WalletVote}` + export const onChainProposalsSelector = selectorFamily< ProposalResponse[], { @@ -149,7 +158,7 @@ export const proposalStartBlockSelector = selectorFamily< }) export const walletVoteSelector = selectorFamily< - 'yes' | 'no' | 'abstain' | 'veto' | undefined, + WalletVoteValue | undefined, { contractAddress: string; proposalId: number } >({ key: 'walletHasVotedOnProposalStatusSelector', From be5dfddbbb27bba2c6d63cc86370bbfcff4da659 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 12 Apr 2022 12:32:43 -0700 Subject: [PATCH 03/20] More comprehensive combined bar with threshold and quorum. --- apps/dapp/components/Progress.tsx | 48 ++++++++-- apps/dapp/components/ProposalDetails.tsx | 106 +++++++++++++++-------- 2 files changed, 108 insertions(+), 46 deletions(-) diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index 881633481..d2898482d 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -1,20 +1,50 @@ // Need color and width literals here because tailwind isn't able to generate // the right classNames for the production build otherwise. export const ProgressMany = ({ - data, + rows, + verticalBars = [], }: { - data: { + rows: { + backgroundColor?: string + data: { + value: number + color: string + }[] + }[] + verticalBars?: { value: number - color: string + label?: string }[] }) => ( -
- {data.map(({ value, color }, index) => ( +
+
+ {rows.map(({ backgroundColor, data }, rowIndex) => ( +
+ {data.map(({ value, color }, index) => ( +
+ ))} +
+ ))} +
+ + {verticalBars.map(({ value, label }, index) => (
+ className="absolute bg-brand w-[2px] h-3 -top-1 rounded-full" + style={{ left: `${Math.floor(value)}%` }} + > + {!!label &&

{label}

} +
))}
) @@ -25,4 +55,4 @@ export const Progress = ({ }: { value: number color: string -}) => +}) => diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index ec3e9246d..133b5f0a7 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -198,14 +198,14 @@ export function ProposalDetailsSidebar({ tokenDecimals ) ) - const noVotes = Number( + const noVotes = 10000000 /*Number( multisig ? proposalTally.votes.no : convertMicroDenomToDenomWithDecimals( proposalTally.votes.no, tokenDecimals ) - ) + )*/ const abstainVotes = Number( multisig @@ -225,10 +225,14 @@ export function ProposalDetailsSidebar({ ) ) - const turnoutPercent = ( - ((yesVotes + noVotes + abstainVotes) / totalWeight) * - 100 - ).toLocaleString(undefined, localeOptions) + const turnoutTotal = yesVotes + noVotes + abstainVotes + const turnoutYesPercent = turnoutTotal ? (yesVotes / turnoutTotal) * 100 : 0 + const turnoutNoPercent = turnoutTotal ? (noVotes / turnoutTotal) * 100 : 0 + const turnoutAbstainPercent = turnoutTotal + ? (abstainVotes / turnoutTotal) * 100 + : 0 + + const turnoutPercent = (turnoutTotal / totalWeight) * 100 const yesPercent = ((yesVotes / totalWeight) * 100).toLocaleString( undefined, localeOptions @@ -253,10 +257,12 @@ export function ProposalDetailsSidebar({ ) const thresholdValue = threshold?.endsWith('%') ? Number(threshold.slice(0, -1)) - : undefined + : 0 const quorumValue = quorum?.endsWith('%') ? Number(quorum.slice(0, -1)) : undefined + const quorumInactiveOrMet = + quorumValue === undefined || turnoutPercent > quorumValue return (
@@ -355,7 +361,7 @@ export function ProposalDetailsSidebar({ )} -

+ {/*

Needs

@@ -386,9 +392,9 @@ export function ProposalDetailsSidebar({ />
) : null} -
+
*/} - {!multisig && threshold !== undefined && thresholdValue !== undefined && ( + {/* {!multisig && threshold !== undefined && thresholdValue !== undefined && ( <>

@@ -405,54 +411,80 @@ export function ProposalDetailsSidebar({ {quorum ?? threshold} turnout

- )} + )} */} -

- Has -

-
+
thresholdValue + ? 'rgba(var(--valid), 0.3)' + : turnoutNoPercent > thresholdValue + ? 'rgba(var(--error), 0.3)' + : 'rgba(var(--dark), 0.2)' + : 'rgba(var(--dark), 0.2)', + data: [ + ...[ + { + value: Number(yesPercent), + color: 'rgb(var(--valid))', + }, + { + value: Number(noPercent), + color: 'rgb(var(--error))', + }, + ].sort((a, b) => b.value - a.value), + { + value: Number(abstainPercent), + color: 'rgb(var(--dark))', + }, + ], }, { - value: Number(abstainPercent), - color: 'rgb(var(--dark))', + data: [ + { + value: + quorumValue === undefined + ? thresholdValue + : (thresholdValue / 100) * + Math.max(quorumValue, turnoutPercent), + color: 'rgb(var(--brand))', + }, + ], }, ]} + verticalBars={quorumValue !== undefined ? [{ + value: quorumValue, + label: "Quorum", + }] : []} /> +

Threshold

-

-

- - {Number(yesPercent).toLocaleString(undefined, localeOptions)}% yes - - , {turnoutPercent}% turnout +

+ Threshold

- -

+

+

{threshold}

+
+

Yes

-
+

{yesPercent}%

-

+

No

-
+

{noPercent}%

-

+

Abstain

-
+

{abstainPercent}%

From 5d528249d64a029981f31d42aef34f6059cc109b Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 12 Apr 2022 15:00:05 -0700 Subject: [PATCH 04/20] Made tally bar thicker than threshold bar. --- apps/dapp/components/Progress.tsx | 29 +++++++++++------------- apps/dapp/components/ProposalDetails.tsx | 27 +++++++++++----------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index d2898482d..f9bab35ed 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -1,11 +1,12 @@ // Need color and width literals here because tailwind isn't able to generate // the right classNames for the production build otherwise. -export const ProgressMany = ({ +export const Progress = ({ rows, verticalBars = [], }: { rows: { backgroundColor?: string + thickness: number data: { value: number color: string @@ -13,6 +14,7 @@ export const ProgressMany = ({ }[] verticalBars?: { value: number + color: string label?: string }[] }) => ( @@ -20,11 +22,14 @@ export const ProgressMany = ({
- {rows.map(({ backgroundColor, data }, rowIndex) => ( + {rows.map(({ backgroundColor, data, thickness }, rowIndex) => (
{data.map(({ value, color }, index) => (
- {verticalBars.map(({ value, label }, index) => ( + {verticalBars.map(({ value, color, label }, index) => (
row.thickness + sum, 10) }} > - {!!label &&

{label}

} + {!!label &&

{label}

}
))}
) - -export const Progress = ({ - value, - color, -}: { - value: number - color: string -}) => diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 133b5f0a7..c4396f1f1 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -63,7 +63,7 @@ import { CopyToClipboard } from './CopyToClipboard' import { CosmosMessageDisplay } from './CosmosMessageDisplay' import { Execute } from './Execute' import SvgAbstain from './icons/Abstain' -import { Progress, ProgressMany } from './Progress' +import { Progress } from './Progress' import { getEnd } from './ProposalList' import { StakingModal, StakingMode } from './StakingModal' import { Vote, VoteChoice } from './Vote' @@ -369,7 +369,7 @@ export function ProposalDetailsSidebar({

{threshold}

) : thresholdValue !== undefined ? (
- - thresholdValue - ? 'rgba(var(--valid), 0.3)' - : turnoutNoPercent > thresholdValue - ? 'rgba(var(--error), 0.3)' - : 'rgba(var(--dark), 0.2)' - : 'rgba(var(--dark), 0.2)', + thickness: 7, data: [ ...[ { @@ -443,6 +436,7 @@ export function ProposalDetailsSidebar({ ], }, { + thickness: 3, data: [ { value: @@ -450,14 +444,21 @@ export function ProposalDetailsSidebar({ ? thresholdValue : (thresholdValue / 100) * Math.max(quorumValue, turnoutPercent), - color: 'rgb(var(--brand))', + color: quorumInactiveOrMet && thresholdValue !== undefined + ? turnoutYesPercent > thresholdValue + ? 'rgb(var(--valid))' + : turnoutNoPercent > thresholdValue + ? 'rgb(var(--error))' + : 'rgba(var(--brand), 0.8)' + : 'rgba(var(--brand), 0.8)', }, ], }, ]} verticalBars={quorumValue !== undefined ? [{ value: quorumValue, - label: "Quorum", + color: `rgba(var(--brand), ${quorumInactiveOrMet ? 0.4 : 0.8})`, + label: 'Quorum', }] : []} />

Threshold

From 812a13e04a64dbc5869cd34c5303bc803ec7cc05 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 12 Apr 2022 15:37:32 -0700 Subject: [PATCH 05/20] Color threshold even if quorum not met. --- apps/dapp/components/DaoContractInfo.tsx | 2 +- apps/dapp/components/ProposalDetails.tsx | 9 ++++----- apps/dapp/util/conversion.ts | 19 +------------------ 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/apps/dapp/components/DaoContractInfo.tsx b/apps/dapp/components/DaoContractInfo.tsx index 433fb9bd8..b150e10c7 100644 --- a/apps/dapp/components/DaoContractInfo.tsx +++ b/apps/dapp/components/DaoContractInfo.tsx @@ -45,7 +45,7 @@ export function DaoContractInfo({ address }: { address: string }) { } text="Passing threshold" - value={threshold as string} + value={threshold} /> {quorum && ( thresholdValue ? 'rgb(var(--valid))' : turnoutNoPercent > thresholdValue diff --git a/apps/dapp/util/conversion.ts b/apps/dapp/util/conversion.ts index 16d8482a3..41f853ff0 100644 --- a/apps/dapp/util/conversion.ts +++ b/apps/dapp/util/conversion.ts @@ -67,28 +67,11 @@ export const zeroStakingCoin = { denom: process.env.NEXT_PUBLIC_STAKING_DENOM || 'ujuno', } -export const getThresholdAndQuorum = ( - t: ThresholdResponse | DaoThreshold | SigThreshold -) => { - if ('absolute_count' in t) { - const count = t.absolute_count.weight - return [count.toString(), undefined] - } else if ('absolute_percentage' in t) { - const threshold = t.absolute_percentage.percentage - return [threshold, undefined] - } else if ('threshold_quorum' in t) { - const quorum = t.threshold_quorum.quorum - const threshold = t.threshold_quorum.threshold - return [threshold, quorum] - } - return ['unknown', 'unknown'] -} - export const getThresholdAndQuorumDisplay = ( t: ThresholdResponse | DaoThreshold | SigThreshold, multisig: boolean, tokenDecimals: number -) => { +): [string, string | undefined] => { if ('absolute_count' in t) { const count = t.absolute_count.weight return [ From f2eab7006e6155aea37173d1f2995f81d563c835 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 12 Apr 2022 17:08:58 -0700 Subject: [PATCH 06/20] Split up threshold and quorum into easy to read progress bars. --- apps/dapp/components/Progress.tsx | 12 +- apps/dapp/components/ProposalDetails.tsx | 146 ++++++++++++++++++++++- apps/dapp/components/ProposalList.tsx | 28 ++--- apps/dapp/util/conversion.ts | 15 ++- 4 files changed, 175 insertions(+), 26 deletions(-) diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index f9bab35ed..6937850f3 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -3,6 +3,7 @@ export const Progress = ({ rows, verticalBars = [], + alignEnd, }: { rows: { backgroundColor?: string @@ -17,6 +18,7 @@ export const Progress = ({ color: string label?: string }[] + alignEnd?: boolean }) => (
(
(
))}
@@ -45,8 +47,8 @@ export const Progress = ({ {verticalBars.map(({ value, color, label }, index) => (
row.thickness + sum, 10) }} + className="absolute w-[2px] -top-[3px] rounded-full" + style={{ left: `${Math.floor(value)}%`, backgroundColor: color, height: rows.reduce((sum, row) => row.thickness + sum, 6) }} > {!!label &&

{label}

}
diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 6b05b3754..0c56f9ee4 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -54,7 +54,9 @@ import { } from 'util/contractConfigWrapper' import { convertMicroDenomToDenomWithDecimals, + expirationAtTimeToSecondsFromNow, getThresholdAndQuorumDisplay, + secondsToWdhms, } from 'util/conversion' import { decodedMessagesString, decodeMessages } from 'util/messagehelpers' @@ -263,9 +265,18 @@ export function ProposalDetailsSidebar({ const quorumInactiveOrMet = quorumValue === undefined || turnoutPercent > quorumValue + const maxVotingSeconds = + 'time' in sigConfig.config.max_voting_period + ? sigConfig.config.max_voting_period.time + : undefined + const expiresInSeconds = + proposal.expires && 'at_time' in proposal.expires + ? expirationAtTimeToSecondsFromNow(proposal.expires) + : undefined + return (
-

Details

+

Details

Proposal @@ -322,7 +333,7 @@ export function ProposalDetailsSidebar({

-

Referendum status

+

Referendum status

@@ -360,6 +371,133 @@ export function ProposalDetailsSidebar({ )} +

+ Ratio of votes +

+ +

+ + Yes {turnoutYesPercent.toLocaleString(undefined, localeOptions)}% + + + + No {turnoutNoPercent.toLocaleString(undefined, localeOptions)}% + +

+

+ Abstain{' '} + {turnoutAbstainPercent.toLocaleString(undefined, localeOptions)}% +

+ +
+ +
+ +
+

Passing threshold

+

{threshold}

+
+ +

+ Voter turnout +

+

+ {turnoutPercent.toLocaleString(undefined, localeOptions)}% +

+ +
+ +
+ + {quorum && ( +
+

Quorum

+

{quorum}

+
+ )} + + {expiresInSeconds !== undefined && expiresInSeconds > 0 && ( + <> +

+ Time left +

+ +

+ {secondsToWdhms(expiresInSeconds)} +

+ + {maxVotingSeconds !== undefined && ( +
+ +
+ )} + + )} + {/*

Needs

@@ -412,7 +550,7 @@ export function ProposalDetailsSidebar({ )} */} -
+ {/*

{abstainPercent}%

-
+
*/}
) diff --git a/apps/dapp/components/ProposalList.tsx b/apps/dapp/components/ProposalList.tsx index e0a1c93a1..cec340e90 100644 --- a/apps/dapp/components/ProposalList.tsx +++ b/apps/dapp/components/ProposalList.tsx @@ -26,6 +26,10 @@ import { proposalSelector, } from 'selectors/proposals' import { ExtendedProposalResponse } from 'types/proposals' +import { + expirationAtTimeToSecondsFromNow, + secondsToWdhms, +} from 'util/conversion' import { draftProposalsToExtendedResponses } from '../util/proposal' @@ -53,29 +57,21 @@ const zeroPad = (num: number, target: number) => { return '0'.repeat(target - s.length) + s } -const secondsToHm = (seconds: number) => { - var h = Math.floor(seconds / 3600) - var m = Math.floor((seconds % 3600) / 60) - var s = Math.floor((seconds % 3600) % 60) - - var hDisplay = - h > 0 ? h + (h == 1 ? ' hr' : ' hrs') + (m > 0 || s > 0 ? ', ' : '') : '' - var mDisplay = m > 0 ? m + (m == 1 ? ' min' : ' mins') : '' - return hDisplay + mDisplay -} - export const getEnd = (exp: Expiration, status: Status) => { if (status != 'open' && status != 'pending') { return 'Completed' } if (exp && 'at_time' in exp) { - const end = Number(exp['at_time']) - const nowSeconds = new Date().getTime() / 1000 - const endSeconds = end / 1000000000 - if (endSeconds <= nowSeconds) { + const secondsFromNow = expirationAtTimeToSecondsFromNow(exp) + // Type check, but should never happen. + if (secondsFromNow === undefined) { + return '' + } + + if (secondsFromNow <= 0) { return 'Completed' } else { - return secondsToHm(endSeconds - nowSeconds) + return secondsToWdhms(secondsFromNow) } } // Not much we can say about proposals that expire at a block diff --git a/apps/dapp/util/conversion.ts b/apps/dapp/util/conversion.ts index 41f853ff0..80c57238a 100644 --- a/apps/dapp/util/conversion.ts +++ b/apps/dapp/util/conversion.ts @@ -1,5 +1,6 @@ import { Duration, + Expiration, Threshold as DaoThreshold, ThresholdResponse, } from '@dao-dao/types/contracts/cw3-dao' @@ -128,7 +129,7 @@ export function humanReadableDuration(d: Duration) { } const secPerDay = 24 * 60 * 60 -export function secondsToWdhms(seconds: string): string { +export function secondsToWdhms(seconds: string | number): string { const secondsInt = Number(seconds) const w = Math.floor(secondsInt / (secPerDay * 7)) const d = Math.floor((secondsInt % (secPerDay * 7)) / secPerDay) @@ -181,3 +182,15 @@ export function nativeTokenDecimals(denom: string): number | undefined { : ibcAssets.tokens.find(({ denom: d }) => d === denom) return asset?.decimals } + +export const expirationAtTimeToSecondsFromNow = (exp: Expiration) => { + if (!('at_time' in exp)) { + return undefined + } + + const end = Number(exp['at_time']) + const nowSeconds = new Date().getTime() / 1000 + const endSeconds = end / 1000000000 + + return endSeconds - nowSeconds +} From 830c4dfcbe7a476527eabe1eb66529fc92b4fd60 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Tue, 12 Apr 2022 18:46:55 -0700 Subject: [PATCH 07/20] Threshold with and without quorum both display properly, added some TODOs of things to fix before ready. --- apps/dapp/components/ProposalDetails.tsx | 426 +++++++++++------------ 1 file changed, 201 insertions(+), 225 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 0c56f9ee4..00c015189 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -234,23 +234,15 @@ export function ProposalDetailsSidebar({ : 0 const turnoutPercent = (turnoutTotal / totalWeight) * 100 - const yesPercent = ((yesVotes / totalWeight) * 100).toLocaleString( - undefined, - localeOptions - ) - const noPercent = ((noVotes / totalWeight) * 100).toLocaleString( - undefined, - localeOptions - ) - const abstainPercent = ((abstainVotes / totalWeight) * 100).toLocaleString( - undefined, - localeOptions - ) + const totalYesPercent = (yesVotes / totalWeight) * 100 + const totalNoPercent = (noVotes / totalWeight) * 100 + const totalAbstainPercent = (abstainVotes / totalWeight) * 100 if (!proposal) { return
Error, no proposal
} + // TODO: Replace this function. const [threshold, quorum] = getThresholdAndQuorumDisplay( proposal.threshold, !!multisig, @@ -262,8 +254,6 @@ export function ProposalDetailsSidebar({ const quorumValue = quorum?.endsWith('%') ? Number(quorum.slice(0, -1)) : undefined - const quorumInactiveOrMet = - quorumValue === undefined || turnoutPercent > quorumValue const maxVotingSeconds = 'time' in sigConfig.config.max_voting_period @@ -371,100 +361,213 @@ export function ProposalDetailsSidebar({ )} -

- Ratio of votes -

+ {quorumValue === undefined ? ( + <> +
+

+ Turnout +

-

- - Yes {turnoutYesPercent.toLocaleString(undefined, localeOptions)}% - + {totalYesPercent > thresholdValue ? ( +

Yes

+ ) : totalNoPercent > thresholdValue ? ( +

No

+ ) : ( +

+ {totalYesPercent > totalNoPercent + ? "'Yes' leads" + : totalNoPercent > totalYesPercent + ? "'No' leads" + : 'Tied'} +

+ )} +
- - No {turnoutNoPercent.toLocaleString(undefined, localeOptions)}% - -

-

- Abstain{' '} - {turnoutAbstainPercent.toLocaleString(undefined, localeOptions)}% -

+

+ {[ + + Yes {totalYesPercent.toLocaleString(undefined, localeOptions)} + % + , + + No {totalNoPercent.toLocaleString(undefined, localeOptions)}% + , + ].sort((a, b) => totalYesPercent - totalNoPercent)} +

+

+ Abstain{' '} + {totalAbstainPercent.toLocaleString(undefined, localeOptions)}% +

-
- + b.value - a.value), + { + value: Number(totalAbstainPercent), + color: 'rgb(var(--dark))', + }, + ], }, + ]} + // TODO: Handle absolute_count. + verticalBars={ + 'absolute_count' in proposal.threshold + ? [] + : [ + { + value: thresholdValue, + color: 'rgba(var(--dark), 0.5)', + }, + ] + } + /> +
+ +
+

Passing threshold

+

{threshold}

+
+ + ) : ( + <> +
+

+ Ratio of votes +

+ + {proposal.status === 'open' || proposal.status === 'pending' ? ( +

+ {turnoutYesPercent > turnoutNoPercent + ? "'Yes' leads" + : turnoutNoPercent > turnoutYesPercent + ? "'No' leads" + : 'Tied'} +

+ ) : turnoutYesPercent > thresholdValue ? ( +

Yes

+ ) : turnoutNoPercent > thresholdValue ? ( +

No

+ ) : ( + // TODO: Decide what to show if proposal is no longer open but nothing is past the threshold. +

Tied

+ )} +
+ +

+ {[ + + Yes{' '} + {turnoutYesPercent.toLocaleString(undefined, localeOptions)}% + , + + No {turnoutNoPercent.toLocaleString(undefined, localeOptions)} + % + , + ].sort((a, b) => turnoutYesPercent - turnoutNoPercent)} +

+

+ Abstain{' '} + {turnoutAbstainPercent.toLocaleString(undefined, localeOptions)}% +

+ +
+ b.value - a.value), + { + value: Number(turnoutAbstainPercent), + color: 'rgb(var(--dark))', + }, + ], }, - ], - }, - ]} - // TODO: Handle absolute_count. - verticalBars={'absolute_count' in proposal.threshold ? [ - - ] : [ - { - value: thresholdValue, - color: 'rgba(var(--dark), 0.5)', - }, - ]} - /> -
+ ]} + // TODO: Handle absolute_count. + verticalBars={ + 'absolute_count' in proposal.threshold + ? [] + : [ + { + value: thresholdValue, + color: 'rgba(var(--dark), 0.5)', + }, + ] + } + /> +
-
-

Passing threshold

-

{threshold}

-
+
+

Passing threshold

+

{threshold}

+
-

- Voter turnout -

-

- {turnoutPercent.toLocaleString(undefined, localeOptions)}% -

+
+

+ Turnout +

-
- quorumValue && ( +

+ Quorum reached +

+ )} +
+ +

+ {turnoutPercent.toLocaleString(undefined, localeOptions)}% +

+ +
+ -
+ ]} + verticalBars={[ + { + value: quorumValue, + color: 'rgba(var(--dark), 0.5)', + }, + ]} + /> +
- {quorum && ( -
-

Quorum

-

{quorum}

-
+
+

Quorum

+

{quorum}

+
+ )} {expiresInSeconds !== undefined && expiresInSeconds > 0 && ( @@ -473,12 +576,13 @@ export function ProposalDetailsSidebar({ Time left

-

+

+ {/* TODO: Do not need to show in full detail. */} {secondsToWdhms(expiresInSeconds)}

{maxVotingSeconds !== undefined && ( -
+
)} - - {/*

- Needs -

-
- {multisig ? ( -

{threshold}

- ) : thresholdValue !== undefined ? ( -
- -
- ) : null} -
*/} - - {/* {!multisig && threshold !== undefined && thresholdValue !== undefined && ( - <> -

-

- - {quorumValue === undefined - ? threshold.slice(0, -1) - : Number((thresholdValue / 100) * quorumValue).toLocaleString( - undefined, - localeOptions - )} - % yes - - ,{' '} - {quorum ?? threshold} turnout -

- - )} */} - - {/*
- b.value - a.value), - { - value: Number(abstainPercent), - color: 'rgb(var(--dark))', - }, - ], - }, - { - thickness: 3, - data: [ - { - value: - quorumValue === undefined - ? thresholdValue - : (thresholdValue / 100) * - Math.max(quorumValue, turnoutPercent), - color: thresholdValue !== undefined - ? turnoutYesPercent > thresholdValue - ? 'rgb(var(--valid))' - : turnoutNoPercent > thresholdValue - ? 'rgb(var(--error))' - : 'rgba(var(--brand), 0.8)' - : 'rgba(var(--brand), 0.8)', - }, - ], - }, - ]} - verticalBars={quorumValue !== undefined ? [{ - value: quorumValue, - color: `rgba(var(--brand), ${quorumInactiveOrMet ? 0.4 : 0.8})`, - label: 'Quorum', - }] : []} - /> -

Threshold

-
- -

- Threshold -

-
-

{threshold}

-
-

- Yes -

-
-

{yesPercent}%

-
-

- No -

-
-

{noPercent}%

-
-

- Abstain -

-
-

{abstainPercent}%

-
*/}
) From c6e2539ec0e1f741403897bcfd08990617ac4b20 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 00:04:20 -0700 Subject: [PATCH 08/20] Added triangles that point to vertical bars on progress bars. --- apps/dapp/components/Progress.tsx | 2 +- apps/dapp/components/ProposalDetails.tsx | 35 +++++++++++++++++------ apps/dapp/components/icons/TriangleUp.tsx | 6 ++++ 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 apps/dapp/components/icons/TriangleUp.tsx diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index 6937850f3..32105a6f1 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -48,7 +48,7 @@ export const Progress = ({
row.thickness + sum, 6) }} + style={{ left: `${value}%`, backgroundColor: color, height: rows.reduce((sum, row) => row.thickness + sum, 6) }} > {!!label &&

{label}

}
diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 00c015189..e723a55dc 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -65,6 +65,7 @@ import { CopyToClipboard } from './CopyToClipboard' import { CosmosMessageDisplay } from './CosmosMessageDisplay' import { Execute } from './Execute' import SvgAbstain from './icons/Abstain' +import { TriangleUp } from './icons/TriangleUp' import { Progress } from './Progress' import { getEnd } from './ProposalList' import { StakingModal, StakingMode } from './StakingModal' @@ -435,10 +436,16 @@ export function ProposalDetailsSidebar({ } />
+ +
+ {thresholdValue !== undefined && ( + 90 ? "calc(100% - 32px)" : `calc(${thresholdValue}% - 17px)` }} /> + )} -
-

Passing threshold

-

{threshold}

+
+

Passing threshold

+

{threshold}

+
) : ( @@ -519,10 +526,16 @@ export function ProposalDetailsSidebar({ } />
+ +
+ {thresholdValue !== undefined && ( + 90 ? "calc(100% - 32px)" : `calc(${thresholdValue}% - 17px)` }} /> + )} -
-

Passing threshold

-

{threshold}

+
+

Passing threshold

+

{threshold}

+
@@ -563,9 +576,13 @@ export function ProposalDetailsSidebar({ />
-
-

Quorum

-

{quorum}

+
+ 90 ? "calc(100% - 32px)" : `calc(${quorumValue}% - 17px)` }} /> + +
+

Quorum

+

{quorum}

+
)} diff --git a/apps/dapp/components/icons/TriangleUp.tsx b/apps/dapp/components/icons/TriangleUp.tsx new file mode 100644 index 000000000..a38f221d2 --- /dev/null +++ b/apps/dapp/components/icons/TriangleUp.tsx @@ -0,0 +1,6 @@ +import * as React from 'react' +import { SVGProps } from 'react' + +export const TriangleUp = (props: SVGProps) => ( + +) From c176ee29e660ee8faf06acfc3773a226e3757fda Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 10:42:43 -0700 Subject: [PATCH 09/20] Created hook to extract threshold and quorum data from proposal, and added reached/not met status indicators to referendum status. --- apps/dapp/components/ProposalDetails.tsx | 495 +++++++++++++---------- apps/dapp/util/proposal.ts | 77 +++- 2 files changed, 351 insertions(+), 221 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index e723a55dc..ccbdecda7 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -55,10 +55,10 @@ import { import { convertMicroDenomToDenomWithDecimals, expirationAtTimeToSecondsFromNow, - getThresholdAndQuorumDisplay, secondsToWdhms, } from 'util/conversion' import { decodedMessagesString, decodeMessages } from 'util/messagehelpers' +import { useThresholdQuorum } from 'util/proposal' import { treasuryTokenListUpdates } from '../atoms/treasury' import { CopyToClipboard } from './CopyToClipboard' @@ -188,6 +188,12 @@ export function ProposalDetailsSidebar({ const { member } = useRecoilValue(isMemberSelector(contractAddress)) + const { threshold, quorum } = useThresholdQuorum( + contractAddress, + proposalId, + !!multisig + ) + const configWrapper = new ContractConfigWrapper(sigConfig) const tokenDecimals = configWrapper.gov_token_decimals @@ -243,19 +249,6 @@ export function ProposalDetailsSidebar({ return
Error, no proposal
} - // TODO: Replace this function. - const [threshold, quorum] = getThresholdAndQuorumDisplay( - proposal.threshold, - !!multisig, - tokenDecimals - ) - const thresholdValue = threshold.endsWith('%') - ? Number(threshold.slice(0, -1)) - : 0 - const quorumValue = quorum?.endsWith('%') - ? Number(quorum.slice(0, -1)) - : undefined - const maxVotingSeconds = 'time' in sigConfig.config.max_voting_period ? sigConfig.config.max_voting_period.time @@ -362,230 +355,294 @@ export function ProposalDetailsSidebar({ )} - {quorumValue === undefined ? ( - <> -
-

- Turnout -

- - {totalYesPercent > thresholdValue ? ( -

Yes

- ) : totalNoPercent > thresholdValue ? ( -

No

- ) : ( -

- {totalYesPercent > totalNoPercent - ? "'Yes' leads" - : totalNoPercent > totalYesPercent - ? "'No' leads" - : 'Tied'} + {threshold ? ( + quorum ? ( + <> +

+

+ Ratio of votes

- )} -
- -

- {[ - - Yes {totalYesPercent.toLocaleString(undefined, localeOptions)} - % - , - - No {totalNoPercent.toLocaleString(undefined, localeOptions)}% - , - ].sort((a, b) => totalYesPercent - totalNoPercent)} -

-

- Abstain{' '} - {totalAbstainPercent.toLocaleString(undefined, localeOptions)}% -

-
- + {turnoutYesPercent > turnoutNoPercent + ? "'Yes' leads" + : turnoutNoPercent > turnoutYesPercent + ? "'No' leads" + : 'Tied'} +

+ ) : turnoutYesPercent >= threshold.percent ? ( +

Yes

+ ) : turnoutNoPercent >= threshold.percent ? ( +

No

+ ) : ( +

Closed

+ )} +
+ +

+ {[ + + Yes{' '} + {turnoutYesPercent.toLocaleString(undefined, localeOptions)} + % + , + + No{' '} + {turnoutNoPercent.toLocaleString(undefined, localeOptions)}% + , + ].sort(() => turnoutYesPercent - turnoutNoPercent)} +

+

+ Abstain{' '} + {turnoutAbstainPercent.toLocaleString(undefined, localeOptions)} + % +

+ +
+ b.value - a.value), { - value: Number(totalNoPercent), - color: 'rgb(var(--error))', + value: Number(turnoutAbstainPercent), + color: 'rgb(var(--dark))', }, - ].sort((a, b) => b.value - a.value), + ], + }, + ]} + verticalBars={ + threshold && [ { - value: Number(totalAbstainPercent), - color: 'rgb(var(--dark))', + value: threshold.percent, + color: 'rgba(var(--dark), 0.5)', }, - ], - }, - ]} - // TODO: Handle absolute_count. - verticalBars={ - 'absolute_count' in proposal.threshold - ? [] - : [ - { - value: thresholdValue, - color: 'rgba(var(--dark), 0.5)', - }, - ] - } - /> -
- -
- {thresholdValue !== undefined && ( - 90 ? "calc(100% - 32px)" : `calc(${thresholdValue}% - 17px)` }} /> - )} - -
-

Passing threshold

-

{threshold}

+ ] + } + /> +
+ +
+ 90 + ? 'calc(100% - 32px)' + : `calc(${threshold.percent}% - 17px)`, + }} + /> + +
+

+ Passing threshold: {threshold.display} +

+ +

+ {turnoutYesPercent >= threshold.percent ? ( + <> + Reached + + ) : ( + <> + Not met + + )} +

+
-
- - ) : ( - <> -
-

- Ratio of votes -

- {proposal.status === 'open' || proposal.status === 'pending' ? ( -

- {turnoutYesPercent > turnoutNoPercent - ? "'Yes' leads" - : turnoutNoPercent > turnoutYesPercent - ? "'No' leads" - : 'Tied'} +

+

+ Turnout

- ) : turnoutYesPercent > thresholdValue ? ( -

Yes

- ) : turnoutNoPercent > thresholdValue ? ( -

No

- ) : ( - // TODO: Decide what to show if proposal is no longer open but nothing is past the threshold. -

Tied

- )} -
- -

- {[ - - Yes{' '} - {turnoutYesPercent.toLocaleString(undefined, localeOptions)}% - , - - No {turnoutNoPercent.toLocaleString(undefined, localeOptions)} - % - , - ].sort((a, b) => turnoutYesPercent - turnoutNoPercent)} -

-

- Abstain{' '} - {turnoutAbstainPercent.toLocaleString(undefined, localeOptions)}% -

-
- b.value - a.value), - { - value: Number(turnoutAbstainPercent), - color: 'rgb(var(--dark))', - }, - ], - }, - ]} - // TODO: Handle absolute_count. - verticalBars={ - 'absolute_count' in proposal.threshold - ? [] - : [ +

+ {turnoutPercent.toLocaleString(undefined, localeOptions)}% +

+
+ +
+ -
- -
- {thresholdValue !== undefined && ( - 90 ? "calc(100% - 32px)" : `calc(${thresholdValue}% - 17px)` }} /> - )} - -
-

Passing threshold

-

{threshold}

+ ], + }, + ]} + verticalBars={[ + { + value: quorum.percent, + color: 'rgba(var(--dark), 0.5)', + }, + ]} + />
-
-
-

- Turnout -

+
+ 90 + ? 'calc(100% - 32px)' + : `calc(${quorum.percent}% - 17px)`, + }} + /> - {turnoutPercent > quorumValue && ( -

- Quorum reached +

+

+ Quorum: {quorum.display} +

+ +

+ {turnoutPercent >= quorum.percent ? ( + <> + Reached + + ) : ( + <> + Not met + + )} +

+
+
+ + ) : ( + <> +
+

+ Turnout

- )} -
-

- {turnoutPercent.toLocaleString(undefined, localeOptions)}% -

+ {totalYesPercent >= threshold.percent ? ( +

Yes

+ ) : totalNoPercent >= threshold.percent ? ( +

No

+ ) : ( +

+ {totalYesPercent > totalNoPercent + ? "'Yes' leads" + : totalNoPercent > totalYesPercent + ? "'No' leads" + : 'Tied'} +

+ )} +
-
- -
- -
- 90 ? "calc(100% - 32px)" : `calc(${quorumValue}% - 17px)` }} /> - -
-

Quorum

-

{quorum}

+

+ {[ + + Yes{' '} + {totalYesPercent.toLocaleString(undefined, localeOptions)}% + , + + No {totalNoPercent.toLocaleString(undefined, localeOptions)} + % + , + ].sort(() => totalYesPercent - totalNoPercent)} +

+

+ Abstain{' '} + {totalAbstainPercent.toLocaleString(undefined, localeOptions)}% +

+ +
+ b.value - a.value), + { + value: Number(totalAbstainPercent), + color: 'rgb(var(--dark))', + }, + ], + }, + ]} + verticalBars={[ + { + value: threshold.percent, + color: 'rgba(var(--dark), 0.5)', + }, + ]} + />
-
- - )} + +
+ 90 + ? 'calc(100% - 32px)' + : `calc(${threshold.percent}% - 17px)`, + }} + /> + +
+

+ Passing threshold: {threshold.display} +

+ +

+ {totalYesPercent >= threshold.percent ? ( + <> + Reached + + ) : ( + <> + Not met + + )} +

+
+
+ + ) + ) : null} {expiresInSeconds !== undefined && expiresInSeconds > 0 && ( <> diff --git a/apps/dapp/util/proposal.ts b/apps/dapp/util/proposal.ts index 776a074ed..f66f981c7 100644 --- a/apps/dapp/util/proposal.ts +++ b/apps/dapp/util/proposal.ts @@ -1,4 +1,7 @@ +import { constSelector, TransactionInterface_UNSTABLE, useRecoilValue } from 'recoil' + import { Proposal, ProposalResponse } from '@dao-dao/types/contracts/cw3-dao' + import { contractProposalMapAtom, nextDraftProposalIdAtom, @@ -7,8 +10,8 @@ import { EmptyProposalResponse, EmptyThresholdResponse, } from 'models/proposal/proposal' -import { TransactionInterface_UNSTABLE } from 'recoil' -import { draftProposalsSelector } from 'selectors/proposals' +import { listMembers, MultisigMemberInfo } from 'selectors/multisigs' +import { draftProposalsSelector, proposalSelector, proposalTallySelector } from 'selectors/proposals' import { ContractProposalMap, ExtendedProposalResponse, @@ -16,6 +19,9 @@ import { ProposalMapItem, } from 'types/proposals' +import { Config, contractConfigSelector, ContractConfigWrapper } from './contractConfigWrapper' +import { convertMicroDenomToDenomWithDecimals } from './conversion' + // Prefix used in IDs for draft proposals const DRAFT_PROPOSAL_PREFFIX = 'draft:' @@ -140,3 +146,70 @@ export const draftProposalsToExtendedResponses = ( ) : [] } + +export const useThresholdQuorum = ( + contractAddress: string, + proposalId: number, + multisig: boolean +): { + threshold?: { + absolute?: number + percent: number + display: string + } + quorum?: { + percent: number + display: string + } +} => { + const config = useRecoilValue( + contractConfigSelector({ contractAddress, multisig }) + ) + const proposal = useRecoilValue( + proposalSelector({ contractAddress, proposalId }) + ) + const proposalTally = useRecoilValue( + proposalTallySelector({ contractAddress, proposalId }) + ) + + const thresholdConfig = proposal?.threshold + + const configWrapper = new ContractConfigWrapper(config) + const tokenDecimals = configWrapper.gov_token_decimals + + if (!config || !thresholdConfig || !proposal || !proposalTally) { + return {} + } + + if ('absolute_count' in thresholdConfig) { + const count = Number(thresholdConfig.absolute_count.weight) + const threshold = multisig + ? count + : convertMicroDenomToDenomWithDecimals(count, tokenDecimals) + + return { + threshold: { + absolute: threshold, + percent: threshold / Number(proposalTally.total_weight) * 100, + display: `${threshold} vote${count != 1 ? 's' : ''}`, + }, + } + } else if ('absolute_percentage' in thresholdConfig) { + const threshold = + Number(thresholdConfig.absolute_percentage.percentage) * 100 + + return { + threshold: { percent: threshold, display: `${threshold}%` }, + } + } else if ('threshold_quorum' in thresholdConfig) { + const quorum = Number(thresholdConfig.threshold_quorum.quorum) * 100 + const threshold = Number(thresholdConfig.threshold_quorum.threshold) * 100 + + return { + threshold: { percent: threshold, display: `${threshold}%` }, + quorum: { percent: quorum, display: `${quorum}%` }, + } + } + + return {} +} From ebc19a2ee8e029a558741263b10d8399cdcb8828 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 11:31:03 -0700 Subject: [PATCH 10/20] Improved status indicators and positioning of vote tallies. --- apps/dapp/components/ProposalDetails.tsx | 168 +++++++++++++---------- 1 file changed, 97 insertions(+), 71 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index ccbdecda7..b88971213 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -358,46 +358,46 @@ export function ProposalDetailsSidebar({ {threshold ? ( quorum ? ( <> -
-

- Ratio of votes -

- - {proposal.status === 'open' || proposal.status === 'pending' ? ( -

- {turnoutYesPercent > turnoutNoPercent - ? "'Yes' leads" - : turnoutNoPercent > turnoutYesPercent - ? "'No' leads" - : 'Tied'} -

- ) : turnoutYesPercent >= threshold.percent ? ( -

Yes

- ) : turnoutNoPercent >= threshold.percent ? ( -

No

- ) : ( -

Closed

- )} -
+

+ Ratio of votes +

-

+

{[ - +

Yes{' '} {turnoutYesPercent.toLocaleString(undefined, localeOptions)} % - , - +

, +

No{' '} {turnoutNoPercent.toLocaleString(undefined, localeOptions)}% - , - ].sort(() => turnoutYesPercent - turnoutNoPercent)} -

-

- Abstain{' '} - {turnoutAbstainPercent.toLocaleString(undefined, localeOptions)} - % -

+

, + ] + .sort(() => yesVotes - noVotes) + .map((elem, idx) => ( +
+ {elem} +
+ ))} +

+ Abstain{' '} + {turnoutAbstainPercent.toLocaleString( + undefined, + localeOptions + )} + % +

+

- Passing threshold: {threshold.display} + Passing threshold:{' '} + {threshold.display}

{turnoutYesPercent >= threshold.percent ? ( <> - Reached + Reached{' '} + ) : ( <> - Not met + Not met{' '} + )}

@@ -524,11 +533,19 @@ export function ProposalDetailsSidebar({

{turnoutPercent >= quorum.percent ? ( <> - Reached + Reached{' '} + ) : ( <> - Not met + Not met{' '} + )}

@@ -537,42 +554,42 @@ export function ProposalDetailsSidebar({ ) : ( <> -
-

- Turnout -

- - {totalYesPercent >= threshold.percent ? ( -

Yes

- ) : totalNoPercent >= threshold.percent ? ( -

No

- ) : ( -

- {totalYesPercent > totalNoPercent - ? "'Yes' leads" - : totalNoPercent > totalYesPercent - ? "'No' leads" - : 'Tied'} -

- )} -
+

+ Turnout +

-

+

{[ - +

Yes{' '} {totalYesPercent.toLocaleString(undefined, localeOptions)}% - , - +

, +

No {totalNoPercent.toLocaleString(undefined, localeOptions)} % - , - ].sort(() => totalYesPercent - totalNoPercent)} -

-

- Abstain{' '} - {totalAbstainPercent.toLocaleString(undefined, localeOptions)}% -

+

, + ] + .sort(() => yesVotes - noVotes) + .map((elem, idx) => ( +
+ {elem} +
+ ))} +

+ Abstain{' '} + {totalAbstainPercent.toLocaleString(undefined, localeOptions)} + % +

+

- Passing threshold: {threshold.display} + Passing threshold:{' '} + {threshold.display}

{totalYesPercent >= threshold.percent ? ( <> - Reached + Reached{' '} + ) : ( <> - Not met + Not met{' '} + )}

From 6603dddf41a562be468aeccbb5f6054364fc9d21 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 11:34:47 -0700 Subject: [PATCH 11/20] Only display first two units of time left. --- apps/dapp/components/ProposalDetails.tsx | 3 +-- apps/dapp/util/conversion.ts | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index b88971213..61fd1b398 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -677,8 +677,7 @@ export function ProposalDetailsSidebar({

- {/* TODO: Do not need to show in full detail. */} - {secondsToWdhms(expiresInSeconds)} + {secondsToWdhms(expiresInSeconds, 2)}

{maxVotingSeconds !== undefined && ( diff --git a/apps/dapp/util/conversion.ts b/apps/dapp/util/conversion.ts index 80c57238a..e765da50b 100644 --- a/apps/dapp/util/conversion.ts +++ b/apps/dapp/util/conversion.ts @@ -129,7 +129,7 @@ export function humanReadableDuration(d: Duration) { } const secPerDay = 24 * 60 * 60 -export function secondsToWdhms(seconds: string | number): string { +export function secondsToWdhms(seconds: string | number, numUnits = 5): string { const secondsInt = Number(seconds) const w = Math.floor(secondsInt / (secPerDay * 7)) const d = Math.floor((secondsInt % (secPerDay * 7)) / secPerDay) @@ -147,6 +147,8 @@ export function secondsToWdhms(seconds: string | number): string { [wDisplay, dDisplay, hDisplay, mDisplay, sDisplay] // Ignore empty values. .filter(Boolean) + // Only keep certain precision of units. + .slice(0, numUnits) // Separate with commas. .join(', ') ) From 883951f02b6791e3bd69a3d31fe3411ad7375eed Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 11:52:26 -0700 Subject: [PATCH 12/20] Formatted. --- apps/dapp/components/Progress.tsx | 23 +++++++++++++++++++---- apps/dapp/components/icons/TriangleUp.tsx | 13 ++++++++++++- apps/dapp/pages/starred.tsx | 5 +---- apps/dapp/selectors/daos.ts | 1 - apps/dapp/util/proposal.ts | 20 ++++++++++++++++---- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index 32105a6f1..eec4c0361 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -27,7 +27,9 @@ export const Progress = ({ {rows.map(({ backgroundColor, data, thickness }, rowIndex) => (
(
))} @@ -48,9 +52,20 @@ export const Progress = ({
row.thickness + sum, 6) }} + style={{ + left: `${value}%`, + backgroundColor: color, + height: rows.reduce((sum, row) => row.thickness + sum, 6), + }} > - {!!label &&

{label}

} + {!!label && ( +

+ {label} +

+ )}
))}
diff --git a/apps/dapp/components/icons/TriangleUp.tsx b/apps/dapp/components/icons/TriangleUp.tsx index a38f221d2..3b92791cf 100644 --- a/apps/dapp/components/icons/TriangleUp.tsx +++ b/apps/dapp/components/icons/TriangleUp.tsx @@ -2,5 +2,16 @@ import * as React from 'react' import { SVGProps } from 'react' export const TriangleUp = (props: SVGProps) => ( - + + + ) diff --git a/apps/dapp/pages/starred.tsx b/apps/dapp/pages/starred.tsx index 61ea5b2a8..19139e8dc 100644 --- a/apps/dapp/pages/starred.tsx +++ b/apps/dapp/pages/starred.tsx @@ -8,10 +8,7 @@ import { MapIcon, PlusIcon, StarIcon } from '@heroicons/react/outline' import { pinnedDaosAtom, pinnedMultisigsAtom } from 'atoms/pinned' import { ContractCard } from 'components/ContractCard' import { isMemberSelector } from 'selectors/cosm' -import { - memberDaoSelector, - proposalCount, -} from 'selectors/daos' +import { memberDaoSelector, proposalCount } from 'selectors/daos' import { sigSelector } from 'selectors/multisigs' import { cw20TokenInfo, nativeBalance } from 'selectors/treasury' import { addToken } from 'util/addToken' diff --git a/apps/dapp/selectors/daos.ts b/apps/dapp/selectors/daos.ts index 67772c40d..50ecd6aac 100644 --- a/apps/dapp/selectors/daos.ts +++ b/apps/dapp/selectors/daos.ts @@ -11,7 +11,6 @@ import { contractsByCodeId } from 'selectors/contracts' import { cosmWasmClient, isMemberSelector } from 'selectors/cosm' import { DAO_CODE_ID, NATIVE_DENOM } from 'util/constants' - import { nativeBalance, walletAddress, diff --git a/apps/dapp/util/proposal.ts b/apps/dapp/util/proposal.ts index f66f981c7..2ada1c7ef 100644 --- a/apps/dapp/util/proposal.ts +++ b/apps/dapp/util/proposal.ts @@ -1,4 +1,8 @@ -import { constSelector, TransactionInterface_UNSTABLE, useRecoilValue } from 'recoil' +import { + constSelector, + TransactionInterface_UNSTABLE, + useRecoilValue, +} from 'recoil' import { Proposal, ProposalResponse } from '@dao-dao/types/contracts/cw3-dao' @@ -11,7 +15,11 @@ import { EmptyThresholdResponse, } from 'models/proposal/proposal' import { listMembers, MultisigMemberInfo } from 'selectors/multisigs' -import { draftProposalsSelector, proposalSelector, proposalTallySelector } from 'selectors/proposals' +import { + draftProposalsSelector, + proposalSelector, + proposalTallySelector, +} from 'selectors/proposals' import { ContractProposalMap, ExtendedProposalResponse, @@ -19,7 +27,11 @@ import { ProposalMapItem, } from 'types/proposals' -import { Config, contractConfigSelector, ContractConfigWrapper } from './contractConfigWrapper' +import { + Config, + contractConfigSelector, + ContractConfigWrapper, +} from './contractConfigWrapper' import { convertMicroDenomToDenomWithDecimals } from './conversion' // Prefix used in IDs for draft proposals @@ -190,7 +202,7 @@ export const useThresholdQuorum = ( return { threshold: { absolute: threshold, - percent: threshold / Number(proposalTally.total_weight) * 100, + percent: (threshold / Number(proposalTally.total_weight)) * 100, display: `${threshold} vote${count != 1 ? 's' : ''}`, }, } From 4e5cc77cd1ff0e49ab9596c76590bd6480c0b3c1 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 12:00:18 -0700 Subject: [PATCH 13/20] Added missing function. --- apps/dapp/templates/configUpdate.tsx | 12 +++++------- apps/dapp/util/conversion.ts | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/apps/dapp/templates/configUpdate.tsx b/apps/dapp/templates/configUpdate.tsx index 0310ff1c1..2aaa817b1 100644 --- a/apps/dapp/templates/configUpdate.tsx +++ b/apps/dapp/templates/configUpdate.tsx @@ -15,7 +15,7 @@ import { secondsToWdhms, convertDenomToMicroDenomWithDecimals, convertMicroDenomToDenomWithDecimals, - getThresholdAndQuorum, + getDaoThresholdAndQuorum, } from 'util/conversion' import { validatePercent, @@ -64,9 +64,7 @@ export const daoConfigUpdateDefaults = ( if ('time' in config.max_voting_period) { max_voting_period = config.max_voting_period.time } - let [threshold, quorum] = getThresholdAndQuorum(config.threshold) - - const processedQuorum = quorum ? (Number(quorum) * 100).toString() : '33' + const { threshold, quorum } = getDaoThresholdAndQuorum(config.threshold) return { name: config.name, @@ -77,14 +75,14 @@ export const daoConfigUpdateDefaults = ( config.proposal_deposit, govTokenDecimals ), - threshold: (Number(threshold) * 100).toString(), - quorum: processedQuorum, + threshold: threshold ?? '50', + quorum: quorum ?? '33', refund_failed_proposals: !!config.refund_failed_proposals, // Store the default quorum in addition to the currently selected // quorum. This allows us to restore the value of the quorum if the absolute // threshold tab is selected (clearing it). - defaultQuorum: processedQuorum, + defaultQuorum: quorum ?? '33', } } diff --git a/apps/dapp/util/conversion.ts b/apps/dapp/util/conversion.ts index e765da50b..512084f3e 100644 --- a/apps/dapp/util/conversion.ts +++ b/apps/dapp/util/conversion.ts @@ -68,6 +68,22 @@ export const zeroStakingCoin = { denom: process.env.NEXT_PUBLIC_STAKING_DENOM || 'ujuno', } +export const getDaoThresholdAndQuorum = ( + t: DaoThreshold +): { threshold: string | undefined; quorum: string | undefined } => { + let threshold = undefined + let quorum = undefined + + if ('absolute_percentage' in t) { + threshold = (Number(t.absolute_percentage.percentage) * 100).toString() + } else if ('threshold_quorum' in t) { + threshold = (Number(t.threshold_quorum.threshold) * 100).toString() + quorum = (Number(t.threshold_quorum.quorum) * 100).toString() + } + + return { threshold, quorum } +} + export const getThresholdAndQuorumDisplay = ( t: ThresholdResponse | DaoThreshold | SigThreshold, multisig: boolean, From d6751eda1a9f3781428e7ef0c9b54da161b2e030 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 15:04:24 -0700 Subject: [PATCH 14/20] Fixed your vote layout, and added tie and all abstain clarifications. --- apps/dapp/components/ProposalDetails.tsx | 40 +++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 61fd1b398..2d7c5ad9d 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -322,37 +322,37 @@ export function ProposalDetailsSidebar({
{member && ( - <> +

Your vote

{walletVote === WalletVote.Yes ? ( -

+

Yes

) : walletVote === WalletVote.No ? ( -

+

No

) : walletVote === WalletVote.Abstain ? ( -

+

Abstain

) : walletVote === WalletVote.Veto ? ( -

+

Veto

) : walletVote ? ( -

+

Unknown: {walletVote}

) : ( -

+

{proposal.status === 'open' ? 'Pending...' : 'None'}

)} - +
)} {threshold ? ( @@ -700,6 +700,30 @@ export function ProposalDetailsSidebar({ )} )} + + {threshold?.percent === 50 && yesVotes === noVotes && ( +
+

+ Tie clarification +

+ +

+ {"'Yes' will win a tie vote."} +

+
+ )} + + {abstainVotes === turnoutTotal && ( +
+

+ All abstain clarification +

+ +

+ When all abstain, a proposal will fail. +

+
+ )}
) From 92603d3d44ad9dcca073f84b8672e9ad289dc94b Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 15:29:21 -0700 Subject: [PATCH 15/20] Added tooltips for quorum and passing threshold. --- apps/dapp/components/ProposalDetails.tsx | 183 ++++++++++++----------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 2d7c5ad9d..a9329cc1e 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -21,6 +21,7 @@ import { import { FormProvider, useForm } from 'react-hook-form' import toast from 'react-hot-toast' import { Button } from 'ui' +import Tooltip from '@reach/tooltip' import { ProposalStatus } from '@components' @@ -157,6 +158,11 @@ function executeProposalExecute( }) } +const PASSING_THRESHOLD_TOOLTIP = + "A proposal must attain this proportion of 'Yes' votes to pass." +const QUORUM_TOOLTIP = + 'This proportion of voting weight must vote for this proposal to pass.' + export function ProposalDetailsSidebar({ contractAddress, proposalId, @@ -449,32 +455,34 @@ export function ProposalDetailsSidebar({ }} /> -
-

- Passing threshold:{' '} - {threshold.display} -

- -

- {turnoutYesPercent >= threshold.percent ? ( - <> - Reached{' '} - - - ) : ( - <> - Not met{' '} - - - )} -

-
+ +
+

+ Passing threshold:{' '} + {threshold.display} +

+ +

+ {turnoutYesPercent >= threshold.percent ? ( + <> + Reached{' '} + + + ) : ( + <> + Not met{' '} + + + )} +

+
+
@@ -525,31 +533,34 @@ export function ProposalDetailsSidebar({ }} /> -
-

- Quorum: {quorum.display} -

- -

- {turnoutPercent >= quorum.percent ? ( - <> - Reached{' '} - - - ) : ( - <> - Not met{' '} - - - )} -

-
+ +
+

+ Quorum:{' '} + {quorum.display} +

+ +

+ {turnoutPercent >= quorum.percent ? ( + <> + Reached{' '} + + + ) : ( + <> + Not met{' '} + + + )} +

+
+
) : ( @@ -639,32 +650,34 @@ export function ProposalDetailsSidebar({ }} /> -
-

- Passing threshold:{' '} - {threshold.display} -

- -

- {totalYesPercent >= threshold.percent ? ( - <> - Reached{' '} - - - ) : ( - <> - Not met{' '} - - - )} -

-
+ +
+

+ Passing threshold:{' '} + {threshold.display} +

+ +

+ {totalYesPercent >= threshold.percent ? ( + <> + Reached{' '} + + + ) : ( + <> + Not met{' '} + + + )} +

+
+
) @@ -703,25 +716,17 @@ export function ProposalDetailsSidebar({ {threshold?.percent === 50 && yesVotes === noVotes && (
-

- Tie clarification -

+

Tie clarification

-

- {"'Yes' will win a tie vote."} -

+

{"'Yes' will win a tie vote."}

)} {abstainVotes === turnoutTotal && (
-

- All abstain clarification -

+

All abstain clarification

-

- When all abstain, a proposal will fail. -

+

When all abstain, a proposal will fail.

)}
From 996fc3973ced1f316dd029256dc7b9c731531316 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 15:32:07 -0700 Subject: [PATCH 16/20] Formatted. --- apps/dapp/components/ProposalDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index a9329cc1e..ed00328b7 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -18,10 +18,10 @@ import { EyeOffIcon, XIcon, } from '@heroicons/react/outline' +import Tooltip from '@reach/tooltip' import { FormProvider, useForm } from 'react-hook-form' import toast from 'react-hot-toast' import { Button } from 'ui' -import Tooltip from '@reach/tooltip' import { ProposalStatus } from '@components' From cf0c0e50d533291c217cfcd8386f1ee2986b5bc3 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Wed, 13 Apr 2022 15:36:25 -0700 Subject: [PATCH 17/20] Fixed grammar. --- apps/dapp/components/ProposalDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index ed00328b7..38898e8e0 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -161,7 +161,7 @@ function executeProposalExecute( const PASSING_THRESHOLD_TOOLTIP = "A proposal must attain this proportion of 'Yes' votes to pass." const QUORUM_TOOLTIP = - 'This proportion of voting weight must vote for this proposal to pass.' + 'This proportion of voting weight must vote for a proposal to pass.' export function ProposalDetailsSidebar({ contractAddress, From c31dfedc26ba06b58013a97d36b5abf795ee8221 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Thu, 14 Apr 2022 01:46:52 -0700 Subject: [PATCH 18/20] @ekeziiel PR fixes. --- apps/dapp/components/Progress.tsx | 2 - apps/dapp/components/ProposalDetails.tsx | 603 ----------------- .../components/ProposalDetailsSidebar.tsx | 610 ++++++++++++++++++ .../proposals/[proposalId].tsx | 6 +- .../proposals/[proposalId].tsx | 6 +- apps/dapp/util/proposal.ts | 7 +- 6 files changed, 616 insertions(+), 618 deletions(-) create mode 100644 apps/dapp/components/ProposalDetailsSidebar.tsx diff --git a/apps/dapp/components/Progress.tsx b/apps/dapp/components/Progress.tsx index eec4c0361..3e9b18e81 100644 --- a/apps/dapp/components/Progress.tsx +++ b/apps/dapp/components/Progress.tsx @@ -1,5 +1,3 @@ -// Need color and width literals here because tailwind isn't able to generate -// the right classNames for the production build otherwise. export const Progress = ({ rows, verticalBars = [], diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 38898e8e0..1001a1d13 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -5,40 +5,29 @@ import { useRouter } from 'next/router' import { SetterOrUpdater, useRecoilValue, - useRecoilValueLoadable, useSetRecoilState, } from 'recoil' import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { CosmosMsgFor_Empty } from '@dao-dao/types/contracts/cw3-dao' import { - CheckIcon, - ExternalLinkIcon, EyeIcon, EyeOffIcon, - XIcon, } from '@heroicons/react/outline' -import Tooltip from '@reach/tooltip' import { FormProvider, useForm } from 'react-hook-form' import toast from 'react-hot-toast' import { Button } from 'ui' -import { ProposalStatus } from '@components' - import { proposalUpdateCountAtom, proposalsUpdated } from 'atoms/proposals' import { MarkdownPreview } from 'components/MarkdownPreview' import { cosmWasmSigningClient, - isMemberSelector, walletAddress as walletAddressSelector, } from 'selectors/cosm' import { - proposalExecutionTXHashSelector, proposalSelector, proposalStartBlockSelector, - proposalTallySelector, votingPowerAtHeightSelector, - WalletVote, walletVoteSelector, } from 'selectors/proposals' import { walletTokenBalanceLoading } from 'selectors/treasury' @@ -48,27 +37,11 @@ import { messageTemplateAndValuesForDecodedCosmosMsg, } from 'templates/templateList' import { cleanChainError } from 'util/cleanChainError' -import { CHAIN_TXN_URL_PREFIX } from 'util/constants' -import { - contractConfigSelector, - ContractConfigWrapper, -} from 'util/contractConfigWrapper' -import { - convertMicroDenomToDenomWithDecimals, - expirationAtTimeToSecondsFromNow, - secondsToWdhms, -} from 'util/conversion' import { decodedMessagesString, decodeMessages } from 'util/messagehelpers' -import { useThresholdQuorum } from 'util/proposal' import { treasuryTokenListUpdates } from '../atoms/treasury' -import { CopyToClipboard } from './CopyToClipboard' import { CosmosMessageDisplay } from './CosmosMessageDisplay' import { Execute } from './Execute' -import SvgAbstain from './icons/Abstain' -import { TriangleUp } from './icons/TriangleUp' -import { Progress } from './Progress' -import { getEnd } from './ProposalList' import { StakingModal, StakingMode } from './StakingModal' import { Vote, VoteChoice } from './Vote' @@ -158,582 +131,6 @@ function executeProposalExecute( }) } -const PASSING_THRESHOLD_TOOLTIP = - "A proposal must attain this proportion of 'Yes' votes to pass." -const QUORUM_TOOLTIP = - 'This proportion of voting weight must vote for a proposal to pass.' - -export function ProposalDetailsSidebar({ - contractAddress, - proposalId, - multisig, -}: { - contractAddress: string - proposalId: number - multisig?: boolean -}) { - const proposal = useRecoilValue( - proposalSelector({ contractAddress, proposalId }) - ) - const proposalTally = useRecoilValue( - proposalTallySelector({ contractAddress, proposalId }) - ) - const { state: proposalExecutionTXHashState, contents: txHashContents } = - useRecoilValueLoadable( - proposalExecutionTXHashSelector({ contractAddress, proposalId }) - ) - const proposalExecutionTXHash: string | null = - proposalExecutionTXHashState === 'hasValue' ? txHashContents : null - - const sigConfig = useRecoilValue( - contractConfigSelector({ contractAddress, multisig: !!multisig }) - ) - const walletVote = useRecoilValue( - walletVoteSelector({ contractAddress, proposalId }) - ) - - const { member } = useRecoilValue(isMemberSelector(contractAddress)) - - const { threshold, quorum } = useThresholdQuorum( - contractAddress, - proposalId, - !!multisig - ) - - const configWrapper = new ContractConfigWrapper(sigConfig) - const tokenDecimals = configWrapper.gov_token_decimals - - const localeOptions = { maximumSignificantDigits: 3 } - - const yesVotes = Number( - multisig - ? proposalTally.votes.yes - : convertMicroDenomToDenomWithDecimals( - proposalTally.votes.yes, - tokenDecimals - ) - ) - const noVotes = Number( - multisig - ? proposalTally.votes.no - : convertMicroDenomToDenomWithDecimals( - proposalTally.votes.no, - tokenDecimals - ) - ) - const abstainVotes = Number( - multisig - ? proposalTally.votes.abstain - : convertMicroDenomToDenomWithDecimals( - proposalTally.votes.abstain, - tokenDecimals - ) - ) - - const totalWeight = Number( - multisig - ? proposalTally.total_weight - : convertMicroDenomToDenomWithDecimals( - proposalTally.total_weight, - tokenDecimals - ) - ) - - const turnoutTotal = yesVotes + noVotes + abstainVotes - const turnoutYesPercent = turnoutTotal ? (yesVotes / turnoutTotal) * 100 : 0 - const turnoutNoPercent = turnoutTotal ? (noVotes / turnoutTotal) * 100 : 0 - const turnoutAbstainPercent = turnoutTotal - ? (abstainVotes / turnoutTotal) * 100 - : 0 - - const turnoutPercent = (turnoutTotal / totalWeight) * 100 - const totalYesPercent = (yesVotes / totalWeight) * 100 - const totalNoPercent = (noVotes / totalWeight) * 100 - const totalAbstainPercent = (abstainVotes / totalWeight) * 100 - - if (!proposal) { - return
Error, no proposal
- } - - const maxVotingSeconds = - 'time' in sigConfig.config.max_voting_period - ? sigConfig.config.max_voting_period.time - : undefined - const expiresInSeconds = - proposal.expires && 'at_time' in proposal.expires - ? expirationAtTimeToSecondsFromNow(proposal.expires) - : undefined - - return ( -
-

Details

-
-

- Proposal -

-

- # {proposal.id.toString().padStart(6, '0')} -

-

- Status -

-
- -
-

- Proposer -

-

- -

- {proposal.status === 'executed' && - proposalExecutionTXHashState === 'loading' ? ( - <> -

TX

-

Loading...

- - ) : !!proposalExecutionTXHash ? ( - <> - {CHAIN_TXN_URL_PREFIX ? ( - - TX - - - ) : ( -

TX

- )} -

- -

- - ) : null} - {proposal.status === 'open' && ( - <> -

Expires

-

- {getEnd(proposal.expires, proposal.status) || 'never'} -

- - )} -
- -
-

Referendum status

-
- -
- {member && ( -
-

- Your vote -

- - {walletVote === WalletVote.Yes ? ( -

- Yes -

- ) : walletVote === WalletVote.No ? ( -

- No -

- ) : walletVote === WalletVote.Abstain ? ( -

- Abstain -

- ) : walletVote === WalletVote.Veto ? ( -

- Veto -

- ) : walletVote ? ( -

- Unknown: {walletVote} -

- ) : ( -

- {proposal.status === 'open' ? 'Pending...' : 'None'} -

- )} -
- )} - - {threshold ? ( - quorum ? ( - <> -

- Ratio of votes -

- -
- {[ -

- Yes{' '} - {turnoutYesPercent.toLocaleString(undefined, localeOptions)} - % -

, -

- No{' '} - {turnoutNoPercent.toLocaleString(undefined, localeOptions)}% -

, - ] - .sort(() => yesVotes - noVotes) - .map((elem, idx) => ( -
- {elem} -
- ))} -

- Abstain{' '} - {turnoutAbstainPercent.toLocaleString( - undefined, - localeOptions - )} - % -

-
- -
- b.value - a.value), - { - value: Number(turnoutAbstainPercent), - color: 'rgb(var(--dark))', - }, - ], - }, - ]} - verticalBars={ - threshold && [ - { - value: threshold.percent, - color: 'rgba(var(--dark), 0.5)', - }, - ] - } - /> -
- -
- 90 - ? 'calc(100% - 32px)' - : `calc(${threshold.percent}% - 17px)`, - }} - /> - - -
-

- Passing threshold:{' '} - {threshold.display} -

- -

- {turnoutYesPercent >= threshold.percent ? ( - <> - Reached{' '} - - - ) : ( - <> - Not met{' '} - - - )} -

-
-
-
- -
-

- Turnout -

- -

- {turnoutPercent.toLocaleString(undefined, localeOptions)}% -

-
- -
- -
- -
- 90 - ? 'calc(100% - 32px)' - : `calc(${quorum.percent}% - 17px)`, - }} - /> - - -
-

- Quorum:{' '} - {quorum.display} -

- -

- {turnoutPercent >= quorum.percent ? ( - <> - Reached{' '} - - - ) : ( - <> - Not met{' '} - - - )} -

-
-
-
- - ) : ( - <> -

- Turnout -

- -
- {[ -

- Yes{' '} - {totalYesPercent.toLocaleString(undefined, localeOptions)}% -

, -

- No {totalNoPercent.toLocaleString(undefined, localeOptions)} - % -

, - ] - .sort(() => yesVotes - noVotes) - .map((elem, idx) => ( -
- {elem} -
- ))} -

- Abstain{' '} - {totalAbstainPercent.toLocaleString(undefined, localeOptions)} - % -

-
- -
- b.value - a.value), - { - value: Number(totalAbstainPercent), - color: 'rgb(var(--dark))', - }, - ], - }, - ]} - verticalBars={[ - { - value: threshold.percent, - color: 'rgba(var(--dark), 0.5)', - }, - ]} - /> -
- -
- 90 - ? 'calc(100% - 32px)' - : `calc(${threshold.percent}% - 17px)`, - }} - /> - - -
-

- Passing threshold:{' '} - {threshold.display} -

- -

- {totalYesPercent >= threshold.percent ? ( - <> - Reached{' '} - - - ) : ( - <> - Not met{' '} - - - )} -

-
-
-
- - ) - ) : null} - - {expiresInSeconds !== undefined && expiresInSeconds > 0 && ( - <> -

- Time left -

- -

- {secondsToWdhms(expiresInSeconds, 2)} -

- - {maxVotingSeconds !== undefined && ( -
- -
- )} - - )} - - {threshold?.percent === 50 && yesVotes === noVotes && ( -
-

Tie clarification

- -

{"'Yes' will win a tie vote."}

-
- )} - - {abstainVotes === turnoutTotal && ( -
-

All abstain clarification

- -

When all abstain, a proposal will fail.

-
- )} -
-
- ) -} - interface ProposalMessageTemplateListItemProps { template: MessageTemplate values: any diff --git a/apps/dapp/components/ProposalDetailsSidebar.tsx b/apps/dapp/components/ProposalDetailsSidebar.tsx new file mode 100644 index 000000000..f3b6cf1b2 --- /dev/null +++ b/apps/dapp/components/ProposalDetailsSidebar.tsx @@ -0,0 +1,610 @@ +import { useRecoilValue, useRecoilValueLoadable } from 'recoil' + +import { ExternalLinkIcon, CheckIcon, XIcon } from '@heroicons/react/outline' +import Tooltip from '@reach/tooltip' + +import { + proposalSelector, + proposalTallySelector, + proposalExecutionTXHashSelector, + walletVoteSelector, + proposalStartBlockSelector, + votingPowerAtHeightSelector, + WalletVote, +} from 'selectors/proposals' +import { CHAIN_TXN_URL_PREFIX } from 'util/constants' +import { + contractConfigSelector, + ContractConfigWrapper, +} from 'util/contractConfigWrapper' +import { + convertMicroDenomToDenomWithDecimals, + expirationAtTimeToSecondsFromNow, + secondsToWdhms, +} from 'util/conversion' +import { useThresholdQuorum } from 'util/proposal' + +import { CopyToClipboard } from './CopyToClipboard' +import SvgAbstain from './icons/Abstain' +import { TriangleUp } from './icons/TriangleUp' +import { Progress } from './Progress' +import { ProposalStatus } from './ProposalStatus' + +const PASSING_THRESHOLD_TOOLTIP = + "A proposal must attain this proportion of 'Yes' votes to pass." +const QUORUM_TOOLTIP = + 'This proportion of voting weight must vote for a proposal to pass.' + +export function ProposalDetailsSidebar({ + contractAddress, + proposalId, + multisig, +}: { + contractAddress: string + proposalId: number + multisig?: boolean +}) { + const proposal = useRecoilValue( + proposalSelector({ contractAddress, proposalId }) + ) + const proposalTally = useRecoilValue( + proposalTallySelector({ contractAddress, proposalId }) + ) + const { state: proposalExecutionTXHashState, contents: txHashContents } = + useRecoilValueLoadable( + proposalExecutionTXHashSelector({ contractAddress, proposalId }) + ) + const proposalExecutionTXHash: string | null = + proposalExecutionTXHashState === 'hasValue' ? txHashContents : null + + const sigConfig = useRecoilValue( + contractConfigSelector({ contractAddress, multisig: !!multisig }) + ) + const walletVote = useRecoilValue( + walletVoteSelector({ contractAddress, proposalId }) + ) + + const height = useRecoilValue( + proposalStartBlockSelector({ proposalId, contractAddress }) + ) + const votingPower = useRecoilValue( + votingPowerAtHeightSelector({ + contractAddress, + multisig: !!multisig, + height, + }) + ) + const memberWhenProposalCreated = votingPower > 0 + + const { threshold, quorum } = useThresholdQuorum( + contractAddress, + proposalId, + !!multisig + ) + + const configWrapper = new ContractConfigWrapper(sigConfig) + const tokenDecimals = configWrapper.gov_token_decimals + + const localeOptions = { maximumSignificantDigits: 3 } + + const yesVotes = Number( + multisig + ? proposalTally.votes.yes + : convertMicroDenomToDenomWithDecimals( + proposalTally.votes.yes, + tokenDecimals + ) + ) + const noVotes = Number( + multisig + ? proposalTally.votes.no + : convertMicroDenomToDenomWithDecimals( + proposalTally.votes.no, + tokenDecimals + ) + ) + const abstainVotes = Number( + multisig + ? proposalTally.votes.abstain + : convertMicroDenomToDenomWithDecimals( + proposalTally.votes.abstain, + tokenDecimals + ) + ) + + const totalWeight = Number( + multisig + ? proposalTally.total_weight + : convertMicroDenomToDenomWithDecimals( + proposalTally.total_weight, + tokenDecimals + ) + ) + + const turnoutTotal = yesVotes + noVotes + abstainVotes + const turnoutYesPercent = turnoutTotal ? (yesVotes / turnoutTotal) * 100 : 0 + const turnoutNoPercent = turnoutTotal ? (noVotes / turnoutTotal) * 100 : 0 + const turnoutAbstainPercent = turnoutTotal + ? (abstainVotes / turnoutTotal) * 100 + : 0 + + const turnoutPercent = (turnoutTotal / totalWeight) * 100 + const totalYesPercent = (yesVotes / totalWeight) * 100 + const totalNoPercent = (noVotes / totalWeight) * 100 + const totalAbstainPercent = (abstainVotes / totalWeight) * 100 + + if (!proposal) { + return
Error, no proposal
+ } + + const maxVotingSeconds = + 'time' in sigConfig.config.max_voting_period + ? sigConfig.config.max_voting_period.time + : undefined + const expiresInSeconds = + proposal.expires && 'at_time' in proposal.expires + ? expirationAtTimeToSecondsFromNow(proposal.expires) + : undefined + + return ( +
+

Details

+
+

+ Proposal +

+

+ # {proposal.id.toString().padStart(6, '0')} +

+

+ Status +

+
+ +
+

+ Proposer +

+

+ +

+ {proposal.status === 'executed' && + proposalExecutionTXHashState === 'loading' ? ( + <> +

TX

+

Loading...

+ + ) : !!proposalExecutionTXHash ? ( + <> + {CHAIN_TXN_URL_PREFIX ? ( + + TX + + + ) : ( +

TX

+ )} +

+ +

+ + ) : null} + {memberWhenProposalCreated && ( + <> +

+ Your vote +

+ + {walletVote === WalletVote.Yes ? ( +

+ Yes +

+ ) : walletVote === WalletVote.No ? ( +

+ No +

+ ) : walletVote === WalletVote.Abstain ? ( +

+ Abstain +

+ ) : walletVote === WalletVote.Veto ? ( +

+ Veto +

+ ) : walletVote ? ( +

+ Unknown: {walletVote} +

+ ) : ( +

+ {proposal.status === 'open' ? 'Pending...' : 'None'} +

+ )} + + )} +
+ +
+

Referendum status

+
+ +
+ {threshold ? ( + quorum ? ( + <> +

+ Ratio of votes +

+ +
+ {[ +

+ Yes{' '} + {turnoutYesPercent.toLocaleString(undefined, localeOptions)} + % +

, +

+ No{' '} + {turnoutNoPercent.toLocaleString(undefined, localeOptions)}% +

, + ] + .sort(() => yesVotes - noVotes) + .map((elem, idx) => ( +
+ {elem} +
+ ))} +

+ Abstain{' '} + {turnoutAbstainPercent.toLocaleString( + undefined, + localeOptions + )} + % +

+
+ +
+ b.value - a.value), + { + value: Number(turnoutAbstainPercent), + color: 'rgb(var(--dark))', + }, + ], + }, + ]} + verticalBars={ + threshold && [ + { + value: threshold.percent, + color: 'rgba(var(--dark), 0.5)', + }, + ] + } + /> +
+ +
+ 90 + ? 'calc(100% - 32px)' + : `calc(${threshold.percent}% - 17px)`, + }} + /> + + +
+

+ Passing threshold:{' '} + {threshold.display} +

+ +

+ {turnoutYesPercent >= threshold.percent ? ( + <> + Reached{' '} + + + ) : ( + <> + Not met{' '} + + + )} +

+
+
+
+ +
+

+ Turnout +

+ +

+ {turnoutPercent.toLocaleString(undefined, localeOptions)}% +

+
+ +
+ +
+ +
+ 90 + ? 'calc(100% - 32px)' + : `calc(${quorum.percent}% - 17px)`, + }} + /> + + +
+

+ Quorum:{' '} + {quorum.display} +

+ +

+ {turnoutPercent >= quorum.percent ? ( + <> + Reached{' '} + + + ) : ( + <> + Not met{' '} + + + )} +

+
+
+
+ + ) : ( + <> +

+ Turnout +

+ +
+ {[ +

+ Yes{' '} + {totalYesPercent.toLocaleString(undefined, localeOptions)}% +

, +

+ No {totalNoPercent.toLocaleString(undefined, localeOptions)} + % +

, + ] + .sort(() => yesVotes - noVotes) + .map((elem, idx) => ( +
+ {elem} +
+ ))} +

+ Abstain{' '} + {totalAbstainPercent.toLocaleString(undefined, localeOptions)} + % +

+
+ +
+ b.value - a.value), + { + value: Number(totalAbstainPercent), + color: 'rgb(var(--dark))', + }, + ], + }, + ]} + verticalBars={[ + { + value: threshold.percent, + color: 'rgba(var(--dark), 0.5)', + }, + ]} + /> +
+ +
+ 90 + ? 'calc(100% - 32px)' + : `calc(${threshold.percent}% - 17px)`, + }} + /> + + +
+

+ Passing threshold:{' '} + {threshold.display} +

+ +

+ {totalYesPercent >= threshold.percent ? ( + <> + Reached{' '} + + + ) : ( + <> + Not met{' '} + + + )} +

+
+
+
+ + ) + ) : null} + + {expiresInSeconds !== undefined && expiresInSeconds > 0 && ( + <> +

+ Time left +

+ +

+ {secondsToWdhms(expiresInSeconds, 2)} +

+ + {maxVotingSeconds !== undefined && ( +
+ +
+ )} + + )} + + {threshold?.percent === 50 && yesVotes === noVotes && ( +
+

Tie clarification

+ +

{"'Yes' will win a tie vote."}

+
+ )} + + {abstainVotes === turnoutTotal && ( +
+

All abstain clarification

+ +

+ When all abstain, a proposal will fail. +

+
+ )} +
+
+ ) +} diff --git a/apps/dapp/pages/dao/[contractAddress]/proposals/[proposalId].tsx b/apps/dapp/pages/dao/[contractAddress]/proposals/[proposalId].tsx index 7537d1899..10fb5141f 100644 --- a/apps/dapp/pages/dao/[contractAddress]/proposals/[proposalId].tsx +++ b/apps/dapp/pages/dao/[contractAddress]/proposals/[proposalId].tsx @@ -4,10 +4,8 @@ import { useRouter } from 'next/router' import { useRecoilValue } from 'recoil' import { Breadcrumbs } from 'components/Breadcrumbs' -import { - ProposalDetails, - ProposalDetailsSidebar, -} from 'components/ProposalDetails' +import { ProposalDetails } from 'components/ProposalDetails' +import { ProposalDetailsSidebar } from 'components/ProposalDetailsSidebar' import { daoSelector } from 'selectors/daos' import { cw20TokenInfo } from 'selectors/treasury' diff --git a/apps/dapp/pages/multisig/[contractAddress]/proposals/[proposalId].tsx b/apps/dapp/pages/multisig/[contractAddress]/proposals/[proposalId].tsx index 5d76112d4..74e4dbc14 100644 --- a/apps/dapp/pages/multisig/[contractAddress]/proposals/[proposalId].tsx +++ b/apps/dapp/pages/multisig/[contractAddress]/proposals/[proposalId].tsx @@ -4,10 +4,8 @@ import { useRouter } from 'next/router' import { useRecoilValue } from 'recoil' import { Breadcrumbs } from 'components/Breadcrumbs' -import { - ProposalDetails, - ProposalDetailsSidebar, -} from 'components/ProposalDetails' +import { ProposalDetails } from 'components/ProposalDetails' +import { ProposalDetailsSidebar } from 'components/ProposalDetailsSidebar' import { sigSelector } from 'selectors/multisigs' const MultisigProposal: NextPage = () => { diff --git a/apps/dapp/util/proposal.ts b/apps/dapp/util/proposal.ts index 2ada1c7ef..9272eef31 100644 --- a/apps/dapp/util/proposal.ts +++ b/apps/dapp/util/proposal.ts @@ -177,19 +177,16 @@ export const useThresholdQuorum = ( const config = useRecoilValue( contractConfigSelector({ contractAddress, multisig }) ) - const proposal = useRecoilValue( - proposalSelector({ contractAddress, proposalId }) - ) const proposalTally = useRecoilValue( proposalTallySelector({ contractAddress, proposalId }) ) - const thresholdConfig = proposal?.threshold + const thresholdConfig = proposalTally?.threshold const configWrapper = new ContractConfigWrapper(config) const tokenDecimals = configWrapper.gov_token_decimals - if (!config || !thresholdConfig || !proposal || !proposalTally) { + if (!config || !thresholdConfig || !proposalTally) { return {} } From 80ad763802a5c6e0559b6f1fc6f681ff0b7b9c79 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Thu, 14 Apr 2022 01:51:42 -0700 Subject: [PATCH 19/20] Formatted. --- apps/dapp/components/ProposalDetails.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/dapp/components/ProposalDetails.tsx b/apps/dapp/components/ProposalDetails.tsx index 1001a1d13..03ad23e4e 100644 --- a/apps/dapp/components/ProposalDetails.tsx +++ b/apps/dapp/components/ProposalDetails.tsx @@ -2,18 +2,11 @@ import { ReactNode, useState } from 'react' import { useRouter } from 'next/router' -import { - SetterOrUpdater, - useRecoilValue, - useSetRecoilState, -} from 'recoil' +import { SetterOrUpdater, useRecoilValue, useSetRecoilState } from 'recoil' import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' import { CosmosMsgFor_Empty } from '@dao-dao/types/contracts/cw3-dao' -import { - EyeIcon, - EyeOffIcon, -} from '@heroicons/react/outline' +import { EyeIcon, EyeOffIcon } from '@heroicons/react/outline' import { FormProvider, useForm } from 'react-hook-form' import toast from 'react-hot-toast' import { Button } from 'ui' From 446738c31987108928ab977f3ba70efb8d2dd251 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Thu, 14 Apr 2022 02:20:11 -0700 Subject: [PATCH 20/20] Made abstain gray and increased font size of section header. --- apps/dapp/components/ProposalDetailsSidebar.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/dapp/components/ProposalDetailsSidebar.tsx b/apps/dapp/components/ProposalDetailsSidebar.tsx index f3b6cf1b2..a94295bd4 100644 --- a/apps/dapp/components/ProposalDetailsSidebar.tsx +++ b/apps/dapp/components/ProposalDetailsSidebar.tsx @@ -148,7 +148,7 @@ export function ProposalDetailsSidebar({ return (
-

Details

+

Details

Proposal @@ -229,9 +229,7 @@ export function ProposalDetailsSidebar({ )}

-
-

Referendum status

-
+

Referendum status

{threshold ? ( @@ -265,7 +263,7 @@ export function ProposalDetailsSidebar({
))}

@@ -296,7 +294,8 @@ export function ProposalDetailsSidebar({ ].sort((a, b) => b.value - a.value), { value: Number(turnoutAbstainPercent), - color: 'rgb(var(--dark))', + // Secondary is dark with 80% opacity. + color: 'rgba(var(--dark), 0.8)', }, ], }, @@ -465,7 +464,7 @@ export function ProposalDetailsSidebar({

))}

@@ -493,7 +492,8 @@ export function ProposalDetailsSidebar({ ].sort((a, b) => b.value - a.value), { value: Number(totalAbstainPercent), - color: 'rgb(var(--dark))', + // Secondary is dark with 80% opacity. + color: 'rgba(var(--dark), 0.8)', }, ], },