Skip to content

Commit

Permalink
Merge branch 'feat/manage-locks' into feat/add-messaging-for-multiple…
Browse files Browse the repository at this point in the history
…-lock-users
  • Loading branch information
denviljclarke committed Nov 6, 2024
2 parents 3a440ee + ca709d5 commit c5d007e
Show file tree
Hide file tree
Showing 26 changed files with 400 additions and 247 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
app/graphql/**/generated
lib/graphql/**/generated
1 change: 1 addition & 0 deletions .secretlintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.trunk
.env*.local
.next
node_modules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ export default function ExecutionCode({ calls }: Props) {

return (
<div>
<h3 className="my-8 flex justify-center text-3xl font-medium">
<h3 className="my-8 hidden justify-center text-3xl font-medium md:flex">
Execution Code
</h3>
<Card>
<Card className="flex flex-col gap-6">
<h3 className="text-center text-[32px]/none font-medium md:hidden">
Execution Code
</h3>
{formattedCalls.map((call, index) => (
<div key={index} className="break-words">
{index > 0 && <hr className="my-4" />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ProposalActionTitle } from "./proposal-action-title";
export const DisconnectedState = () => {
return (
<Card>
<div className="flex flex-col gap-[25px] ">
<div className="flex w-full flex-col items-center gap-[25px]">
<ProposalActionTitle />
<span>Please connect your wallet to participate in governance</span>
<ConnectButton fullwidth theme="primary" />
Expand Down
300 changes: 207 additions & 93 deletions src/app/proposals/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { stateToStatusColorMap } from "@/lib/interfaces/proposal.interface";
import {
Avatar,
BlockExplorerLink,
Card,
Loader,
MarkdownView,
Status,
Expand All @@ -19,7 +20,64 @@ import ExecutionCode from "./_components/execution-code.component";
import Participants from "./_components/participants.component";
import { Countdown, ProposalCurrentVotes } from "@/components/index";
import { ensureChainId } from "@/lib/helpers/ensureChainId";
import { ProposalState } from "@/lib/graphql";
import { Proposal, ProposalState } from "@/lib/graphql";
import { CELO_BLOCK_TIME } from "@/config/config.constants";

const ProposalCountdown = ({ proposal }: { proposal: Proposal }) => {
const { data: currentBlock } = useBlockNumber({
watch: true,
chainId: ensureChainId(useAccount().chainId),
});

const endBlock = useBlock({
blockNumber: BigInt(proposal.endBlock),
query: {
enabled: true,
},
});

const votingDeadline = useMemo(() => {
if (currentBlock) {
// If the end block is already mined, we can fetch the timestamp
if (Number(currentBlock) >= proposal.endBlock && endBlock.data) {
return new Date(Number(endBlock.data.timestamp) * 1000);
} else {
// If the end block is not mined yet, we estimate the time
return new Date(
Date.now() +
// Estimation of ~5 seconds per block
(proposal.endBlock - Number(currentBlock)) * CELO_BLOCK_TIME,
);
}
}
}, [currentBlock, endBlock.data, proposal.endBlock]);

const timeLockDeadLine = useMemo(() => {
if (proposal.state === ProposalState.Queued && proposal.proposalQueued[0]) {
return new Date(Number(proposal.proposalQueued[0].eta) * 1000);
}
}, [proposal]);

if (timeLockDeadLine && timeLockDeadLine.getTime() >= new Date().getTime()) {
return (
<Countdown
endTimestamp={timeLockDeadLine.getTime()}
updateIntervalInMs={1000}
/>
);
}

if (proposal.state === ProposalState.Active && votingDeadline) {
return (
<Countdown
endTimestamp={votingDeadline.getTime()}
updateIntervalInMs={1000}
/>
);
}

return null;
};

const Page = ({ params: { id } }: { params: { id: string } }) => {
const { proposal } = useProposal(BigInt(id));
Expand All @@ -43,8 +101,6 @@ const Page = ({ params: { id } }: { params: { id: string } }) => {
}, [proposal]);

const votingDeadline = useMemo(() => {
const CELO_BLOCK_TIME = 5000; // 5 seconds

if (proposal && currentBlock) {
// If the end block is already mined, we can fetch the timestamp
if (Number(currentBlock) >= proposal.endBlock && endBlock.data) {
Expand Down Expand Up @@ -75,115 +131,173 @@ const Page = ({ params: { id } }: { params: { id: string } }) => {
}

return (
<main className="flex flex-col">
<div className="mb-4 mt-6 flex items-center justify-between">
<Status
text={proposal.state.toString()}
type={stateToStatusColorMap[proposal.state]}
/>
<div className="lg:hidden">
{timeLockDeadLine &&
timeLockDeadLine.getTime() >= new Date().getTime() && (
<>
<main className="flex flex-col gap-8 pt-8 md:hidden">
<Card>
<div className="flex flex-col items-start">
<Status
text={proposal.state.toString()}
type={stateToStatusColorMap[proposal.state]}
/>
<h1 className="mb-4 mt-2 text-[32px]/none font-medium">
{proposal.metadata?.title}
</h1>
<ProposalCountdown proposal={proposal} />
</div>
<div className="flex flex-col gap-4 pt-6">
<div className="flex place-items-center gap-x2 font-inter">
<Avatar address={proposal.proposer.id} />
by{" "}
<span className="font-medium">
<WalletAddressWithCopy address={proposal.proposer.id} />
</span>
</div>
<div className="flex flex-col">
<span className="font-inter">Proposed on:</span>
<span className="font-inter font-medium">
{proposedOn && (
<BlockExplorerLink
className=" no-underline "
type="block"
item={proposal.startBlock}
>
{format(proposedOn, "MMMM do, yyyy")}
</BlockExplorerLink>
)}
</span>
</div>
<div className="flex flex-col">
<span className="font-inter">Voting deadline:</span>
<span className="font-inter font-medium">
{votingDeadline && (
<BlockExplorerLink
className=" no-underline"
type="block"
item={proposal.endBlock}
>
{format(votingDeadline, "MMMM do, yyyy")}{" "}
</BlockExplorerLink>
)}
</span>
</div>
</div>
</Card>
{proposal.votes ? (
<ProposalCurrentVotes proposal={proposal} className="md:mb-x6" />
) : (
<Loader isCenter />
)}
<ProposalActions proposal={proposal} />
<Suspense fallback={<Loader isCenter />}>
<Card className="flex flex-col gap-6">
<h2 className="text-center text-[32px]/none font-medium">
Proposal Description
</h2>
<MarkdownView markdown={proposal.metadata?.description} />
</Card>
</Suspense>
{proposal.calls && <ExecutionCode calls={proposal.calls} />}
{proposal.votes && <Participants votes={proposal.votes} />}
</main>

<main className="hidden md:flex md:flex-col">
<div className="mb-4 mt-6 flex items-center justify-between">
<Status
text={proposal.state.toString()}
type={stateToStatusColorMap[proposal.state]}
/>
</div>
<div className="flex flex-col gap-x1 md:grid md:grid-cols-7">
<div className="md:col-span-4 md:col-start-1">
<h1 className="text-[56px]/none font-medium">
<Suspense fallback={<Loader isCenter />}>
{proposal.metadata?.title}
</Suspense>
</h1>
</div>
<div className="md:col-span-3 md:col-start-5">
<div className="hidden lg:flex">
{timeLockDeadLine &&
timeLockDeadLine.getTime() >= new Date().getTime() && (
<Countdown
endTimestamp={timeLockDeadLine.getTime()}
updateIntervalInMs={1000}
/>
)}
</div>
{proposal.state === ProposalState.Active && votingDeadline && (
<Countdown
endTimestamp={timeLockDeadLine.getTime()}
endTimestamp={votingDeadline.getTime()}
updateIntervalInMs={1000}
/>
)}
</div>
</div>
</div>
<div className="flex flex-col gap-x1 md:grid md:grid-cols-7">
<div className="md:col-span-4 md:col-start-1">
<h1 className="text-[56px]/none font-medium">
<div className="mt-4 flex flex-wrap place-items-center justify-start gap-x6 font-inter lg:mt-12 ">
<div className="flex place-items-center gap-x2">
<Suspense fallback={<Loader isCenter />}>
{proposal.metadata?.title}
<Avatar address={proposal.proposer.id} />
by{" "}
<span className="font-medium">
<WalletAddressWithCopy address={proposal.proposer.id} />
</span>
</Suspense>
</h1>
</div>
<div className="md:col-span-3 md:col-start-5">
<div className="hidden lg:flex">
{timeLockDeadLine &&
timeLockDeadLine.getTime() >= new Date().getTime() && (
<Countdown
endTimestamp={timeLockDeadLine.getTime()}
updateIntervalInMs={1000}
/>
)}
</div>
{proposal.state === ProposalState.Active && votingDeadline && (
<Countdown
endTimestamp={votingDeadline.getTime()}
updateIntervalInMs={1000}
/>
)}
</div>
</div>
<div className="mt-4 flex flex-wrap place-items-center justify-start gap-x6 font-inter lg:mt-12 ">
<div className="flex place-items-center gap-x2">
<Suspense fallback={<Loader isCenter />}>
<Avatar address={proposal.proposer.id} />
by{" "}
<span className="font-medium">
<WalletAddressWithCopy address={proposal.proposer.id} />
<div className="flex place-items-center gap-x2">
<span className="font-light">Proposed on:</span>
<span className="">
<Suspense fallback={<Loader isCenter />}>
{proposedOn && (
<BlockExplorerLink
className="no-underline"
type="block"
item={proposal.startBlock}
>
{format(proposedOn, "MMMM do, yyyy 'at' hh:mm a")}
</BlockExplorerLink>
)}
</Suspense>
</span>
</Suspense>
</div>
<div className="flex place-items-center gap-x2">
<span className="font-light">Proposed on:</span>
<span className="">
<Suspense fallback={<Loader isCenter />}>
{proposedOn && (
</div>
<div className="flex place-items-center gap-x2">
<span className="font-light">Voting deadline:</span>
<span className="">
{votingDeadline && (
<BlockExplorerLink
className=" no-underline "
className=" no-underline"
type="block"
item={proposal.startBlock}
item={proposal.endBlock}
>
{format(proposedOn, "MMMM do, yyyy 'at' hh:mm a")}
{format(votingDeadline, "MMMM do, yyyy 'at' hh:mm a")}{" "}
</BlockExplorerLink>
)}
</Suspense>
</span>
</span>
</div>
</div>
<div className="flex place-items-center gap-x2">
<span className="font-light">Voting deadline:</span>
<span className="">
{votingDeadline && (
<BlockExplorerLink
className=" no-underline"
type="block"
item={proposal.endBlock}
>
{format(votingDeadline, "MMMM do, yyyy 'at' hh:mm a")}{" "}
</BlockExplorerLink>
<div className="mt-14 flex flex-col place-items-start gap-y-16 md:flex-row md:justify-between md:gap-1">
<div className="w-full max-w-2xl flex-1">
{proposal.votes ? (
<ProposalCurrentVotes proposal={proposal} className="md:mb-x6" />
) : (
<Loader isCenter />
)}
</span>
</div>
</div>
<div className="mt-14 flex flex-col place-items-start gap-y-16 md:flex-row md:justify-between md:gap-1">
<div className="w-full max-w-2xl flex-1">
{proposal.votes ? (
<ProposalCurrentVotes proposal={proposal} className="md:mb-x6" />
) : (
<Loader isCenter />
)}
<div className="my-x6 md:hidden">
<ProposalActions proposal={proposal} />
<h3 className="my-8 flex justify-center text-3xl font-medium">
Proposal Description
</h3>
<Suspense fallback={<Loader isCenter />}>
<MarkdownView markdown={proposal.metadata?.description} />
</Suspense>
{proposal.calls && <ExecutionCode calls={proposal.calls} />}
</div>
<h3 className="my-8 flex justify-center text-3xl font-medium">
Proposal Description
</h3>
<Suspense fallback={<Loader isCenter />}>
<MarkdownView markdown={proposal.metadata?.description} />
</Suspense>
{proposal.calls && <ExecutionCode calls={proposal.calls} />}
</div>
<div className="flex flex-col gap-x11 md:max-w-[350px]">
<div className="hidden md:block">
<ProposalActions proposal={proposal} />
<div className="flex flex-col gap-x11 md:max-w-[350px]">
<div className="hidden md:block">
<ProposalActions proposal={proposal} />
</div>
{proposal.votes && <Participants votes={proposal.votes} />}
</div>
{proposal.votes && <Participants votes={proposal.votes} />}
</div>
</div>
</main>
</main>
</>
);
};

Expand Down
3 changes: 1 addition & 2 deletions src/app/voting-power/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { LockInfo } from "@/components/lock-info/lock-info.component";

const Page = () => {
const { address } = useAccount();
const lockInfo = useLockInfo(address);
const { hasActiveLock, isLoading, refetch } = lockInfo;
const { hasActiveLock, isLoading, refetch } = useLockInfo(address);

return (
<main className="flex flex-col place-items-center gap-14">
Expand Down
Loading

0 comments on commit c5d007e

Please sign in to comment.