Skip to content

Commit

Permalink
feat: move dialog to separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszjasiuk committed Aug 3, 2023
1 parent 9a68b7a commit 9e9d7ec
Show file tree
Hide file tree
Showing 16 changed files with 458 additions and 399 deletions.
1 change: 1 addition & 0 deletions apps/namada-interface/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"crypto-browserify": "^3.12.0",
"dompurify": "^3.0.2",
"framer-motion": "^6.2.8",
"io-ts": "^2.2.20",
"next-qrcode": "^2.0.0",
"path": "^0.12.7",
"react": "^17.0.2",
Expand Down
2 changes: 2 additions & 0 deletions apps/namada-interface/src/App/App.components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export const AppContainer = styled.div`

export const AppLoader = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,12 @@ export const ProposalCardId = styled.span`
margin-right: 4px;
`;

export const ProposalCardVotes = styled.div<{ yes: number; no: number }>`
width: 32px;
height: 32px;
export const ProposalCardVotesContainer = styled.div<{
yes: number;
no: number;
}>`
width: 40px;
height: 40px;
background-image: conic-gradient(
${(props) => props.theme.colors.primary.main} 0,
${(props) => props.theme.colors.primary.main} ${(p) => p.yes}%,
Expand All @@ -104,7 +107,7 @@ export const ProposalCardInfoContainer = styled.div`
height: 40px;
gap: 4px;
& ${ProposalCardVotes} {
& ${ProposalCardVotesContainer} {
flex-shrink: 0;
align-self: center;
}
Expand All @@ -114,7 +117,7 @@ export const ProposalCardInfoContainer = styled.div`
}
`;

export const ProposalDetails = styled.dialog`
export const ProposalDetailsContainer = styled.dialog`
display: flex;
align-items: center;
justify-content: center;
Expand Down
274 changes: 58 additions & 216 deletions apps/namada-interface/src/App/Governance/Governance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ import {
ProposalCardStatusContainer,
ProposalCardStatusInfo,
ProposalCardText,
ProposalCardVoteButton,
ProposalCardVotes,
ProposalDetails,
ProposalDetailsAddresses,
ProposalDetailsAddressesHeader,
ProposalDetailsButtons,
ProposalDetailsContent,
ProposalCardVotesContainer,
ProposalsContainer,
} from "./Governance.components";
import { chains } from "@namada/chains";
Expand All @@ -21,17 +15,40 @@ import { useAppSelector } from "store";
import { useCallback, useEffect, useState } from "react";
import { Query } from "@namada/shared";
import BigNumber from "bignumber.js";
import { getIntegration } from "@namada/hooks";
import { AccountType, Signer, Tokens } from "@namada/types";
import { Option, shortenAddress } from "@namada/utils";
import { Select } from "@namada/components";
import { AccountsState } from "slices/accounts";
import { Option } from "@namada/utils";
import { ProposalDetails } from "./ProposalDetails";

export type Proposal = {
id: string;
proposalType: string;
author: string;
startEpoch: bigint;
endEpoch: bigint;
graceEpoch: bigint;
content: Content;
status: string;
yesVotes?: string;
totalVotingPower?: string;
result?: string;
};

export type Content = {
abstract: string;
authors: string;
created: string;
details: string;
discussionsTo: string;
license: string;
motivation: string;
requires: string;
title: string;
};

const getStatus = (status: string, result?: string): string => {
return result || status;
};

const ProposalCardVotes2 = ({
const ProposalCardVotes = ({
yes,
total,
}: {
Expand All @@ -45,7 +62,7 @@ const ProposalCardVotes2 = ({
const noPercentage = 100 - yesPercentage;

return (
<ProposalCardVotes
<ProposalCardVotesContainer
title={`Yes: ${yes}, Total: ${total} (${yesPercentage.toFixed(2)})%`}
yes={yesPercentage}
no={noPercentage}
Expand All @@ -55,77 +72,31 @@ const ProposalCardVotes2 = ({

export const Governance = (): JSX.Element => {
const { chainId } = useAppSelector<SettingsState>((state) => state.settings);
const { derived } = useAppSelector<AccountsState>((state) => state.accounts);
const addresses = Object.keys(derived[chainId]);
const [proposals, setProposals] = useState<Proposal[]>([]);
const [activeProposal, setActiveProposal] = useState<Option<Proposal>>(
Option.none()
);
const [activeDelegator, setActiveDelegator] = useState<Option<string>>(
Option.none()
);
const [delegators, setDelegators] = useState<
Option<Record<string, BigNumber>>
>(Option.none());
const [activeProposalVotes, setActiveProposalVotes] = useState<
Map<string, boolean>
>(new Map());

const { rpc } = chains[chainId];
const query = new Query(rpc);

const vote = useCallback(
async (vote: boolean) => {
const voteStr = vote ? "yay" : "nay";
const integration = getIntegration(chainId);
const signer = integration.signer() as Signer;

const address = Option.match(
activeDelegator,
(address) => address,
() => {
throw new Error("No address selected");
}
);

const proposalId = Option.match(
activeProposal,
(proposal) => proposal.id,
() => {
throw new Error("No proposal selected");
}
);

await signer.submitVoteProposal(
{
tx: {
token: Tokens.NAM.address || "",
feeAmount: new BigNumber(0),
gasLimit: new BigNumber(0),
chainId,
},
signer: address,
vote: voteStr,
proposalId: BigInt(proposalId),
},

AccountType.Mnemonic
);
},
[activeDelegator, activeProposal]
);

useEffect(() => {
const fetchProposals = async (): Promise<void> => {
try {
const proposals = await query.query_proposals();
const p = proposals.map((proposal: any) => ({
const sdkProposals = await query.query_proposals();
const proposals = sdkProposals.map((proposal) => ({
...proposal,
start_epoch: BigInt(proposal.start_epoch),
end_epoch: BigInt(proposal.end_epoch),
grace_epoch: BigInt(proposal.grace_epoch),
proposalType: proposal.proposal_type,
startEpoch: BigInt(proposal.start_epoch),
endEpoch: BigInt(proposal.end_epoch),
graceEpoch: BigInt(proposal.grace_epoch),
content: {
...proposal.content,
discussionsTo: proposal.content["discussions-to"],
},
totalVotingPower: proposal.total_voting_power,
}));
setProposals(p);
setProposals(proposals);
} catch (e) {
console.error(e);
setProposals([]);
Expand All @@ -134,27 +105,13 @@ export const Governance = (): JSX.Element => {
fetchProposals();
}, []);

const onProposalClick = useCallback(
async (proposal: Proposal) => {
try {
setActiveProposal(Option.some(proposal));

const votes = await query.get_proposal_votes(BigInt(proposal.id));
setActiveProposalVotes(new Map(votes));
const onProposalClick = useCallback((proposal: Proposal) => {
setActiveProposal(Option.some(proposal));
}, []);

const totalDelegations = await query.get_total_delegations(
addresses,
//TODO: start_epoch should be BigInt
proposal.start_epoch - BigInt(1)
);
setDelegators(Option.some(totalDelegations));
setActiveDelegator(Option.some(Object.keys(totalDelegations)[0]));
} catch (e) {
// TODO: handle rpc error
}
},
[addresses]
);
const onDetailsClose = useCallback(() => {
setActiveProposal(Option.none());
}, []);

return (
<GovernanceContainer>
Expand All @@ -174,136 +131,21 @@ export const Governance = (): JSX.Element => {
<ProposalCardId>{"#" + proposal.id}</ProposalCardId>
{proposal.content.title}: {proposal.content.details}
</ProposalCardText>
{proposal.yes_votes && proposal.total_voting_power && (
<ProposalCardVotes2
yes={proposal.yes_votes}
total={proposal.total_voting_power}
{proposal.yesVotes && proposal.totalVotingPower && (
<ProposalCardVotes
yes={proposal.yesVotes}
total={proposal.totalVotingPower}
/>
)}
</ProposalCardInfoContainer>
</ProposalCard>
))}
</ProposalsContainer>
{activeProposal.some && activeDelegator.some && delegators.some && (
<ProposalDetails open onClick={() => setActiveProposal(Option.none())}>
<ProposalDetailsContent onClick={(e) => e.stopPropagation()}>
<h1>
#{activeProposal.value.id} {activeProposal.value.content.title}
</h1>
<p>by: {activeProposal.value.content.authors}</p>
<h3>Details:</h3>
<p>{activeProposal.value.content.details}</p>
<h3>Motivation:</h3>
<p>{activeProposal.value.content.motivation}</p>
<h3>License:</h3>
<p>{activeProposal.value.content.license}</p>

{activeProposal.value.status === "on-going" &&
!activeProposal.value.result && (
<>
<ProposalDetailsAddresses>
<ProposalDetailsAddressesHeader>
Vote with address:
</ProposalDetailsAddressesHeader>
<Select
value={activeDelegator.value}
data={Object.keys(delegators.value).map((a) => ({
value: a,
label: shortenAddress(a, 32, 32),
}))}
onChange={(e) => {
setActiveDelegator(Option.some(e.target.value));
}}
/>
Power: {delegators.value[activeDelegator.value]}
</ProposalDetailsAddresses>
<ProposalDetailsButtons>
<ProposalCardVoteButton
onClick={() => vote(true)}
disabled={
activeProposalVotes.get(activeDelegator.value) === true
}
title={
activeProposalVotes.get(activeDelegator.value)
? "You have already voted YAY"
: ""
}
>
Vote YAY
</ProposalCardVoteButton>
<ProposalCardVoteButton
onClick={() => vote(false)}
disabled={
activeProposalVotes.get(activeDelegator.value) === false
}
title={
!activeProposalVotes.get(activeDelegator.value)
? "You have already voted NAY"
: ""
}
className="inverse"
>
Vote NAY
</ProposalCardVoteButton>
</ProposalDetailsButtons>
</>
)}
</ProposalDetailsContent>
</ProposalDetails>
)}
<ProposalDetails
open={activeProposal.some}
onClose={onDetailsClose}
maybeProposal={activeProposal}
/>
</GovernanceContainer>
);
};

type Content = {
abstract: string;
authors: string;
created: string;
details: string;
"discussions-to": string;
license: string;
motivation: string;
requires: string;
title: string;
};

type Proposal = {
id: string;
proposal_type: string;
author: string;
start_epoch: bigint;
end_epoch: bigint;
grace_epoch: bigint;
content: Content;
status: string;
yes_votes?: string;
total_voting_power?: string;
result?: string;
};

const status = ["pending", "on-going"];
const result = [undefined, "passed", "rejected"];
const fakeProposals: Proposal[] = [...Array(9).keys()].map((i) => ({
id: i + "",
proposal_type: "Fake",
author: "Author " + i,
start_epoch: BigInt(i),
end_epoch: BigInt(i + 10),
grace_epoch: BigInt(i + 15),
content: {
abstract: "Abstract " + i,
authors: "Authors " + i,
created: "Created " + i,
details:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"discussions-to": "Discussions-to " + i,
license: "License " + i,
motivation: "Motivation " + i,
requires: "Requires " + i,
title: "Title " + i,
},
status: status[Math.floor(Math.random() * status.length)],
yes_votes: "400",
total_voting_power: "6000",
result: result[Math.floor(Math.random() * result.length)],
}));
Loading

0 comments on commit 9e9d7ec

Please sign in to comment.