Skip to content

Commit

Permalink
feat: add result page
Browse files Browse the repository at this point in the history
  • Loading branch information
kittybest committed Nov 14, 2024
1 parent 25af435 commit 3328fdb
Show file tree
Hide file tree
Showing 37 changed files with 361 additions and 239 deletions.
5 changes: 0 additions & 5 deletions packages/coordinator/scripts/uploadRoundMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export interface RoundMetadata {
registrationEndsAt: Date;
votingStartsAt: Date;
votingEndsAt: Date;
tallyFile: string;
}

interface IUploadMetadataProps {
Expand Down Expand Up @@ -137,17 +136,13 @@ export async function collectMetadata(): Promise<RoundMetadata> {

rl.close();

// NOTICE! this is when you use vercel blob storage, if you're using another tool, please change this part.
const vercelStoragePrefix = `https://${process.env.BLOB_READ_WRITE_TOKEN?.split("_")[3]}.public.blob.vercel-storage.com`;

return {
roundId,
description,
startsAt,
registrationEndsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000),
votingStartsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000),
votingEndsAt: new Date(startsAt.getTime() + registrationEndsIn * 1000 + votingEndsIn * 1000),
tallyFile: `${vercelStoragePrefix}/tally-${roundId}.json`,
};
}

Expand Down
5 changes: 5 additions & 0 deletions packages/interface/public/bronze.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/interface/public/gold.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/interface/public/line-chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/interface/public/silver.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/interface/src/components/BallotOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const BallotOverview = ({ title = undefined, pollId }: IBallotOverviewPro

const ballot = useMemo(() => getBallot(pollId), [pollId, getBallot]);

const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });

return (
<Link
Expand Down
2 changes: 1 addition & 1 deletion packages/interface/src/components/EligibilityDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const EligibilityDialog = ({ pollId = "" }: IEligibilityDialogProps): JSX
useMaci();
const router = useRouter();

const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });

const votingEndsAt = useMemo(() => {
const round = getRoundByPollId(pollId);
Expand Down
2 changes: 1 addition & 1 deletion packages/interface/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const Header = ({ navLinks, pollId = "" }: IHeaderProps) => {
const { asPath } = useRouter();
const [isOpen, setOpen] = useState(false);
const { getBallot } = useBallot();
const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });
const { theme, setTheme } = useTheme();

const ballot = useMemo(() => getBallot(pollId), [pollId, getBallot]);
Expand Down
2 changes: 1 addition & 1 deletion packages/interface/src/components/Info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const Info = ({
showAppState = false,
showBallot = false,
}: IInfoProps): JSX.Element => {
const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });

const { getRoundByPollId } = useRound();
const round = getRoundByPollId(pollId);
Expand Down
27 changes: 23 additions & 4 deletions packages/interface/src/contexts/Round.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import { config } from "~/config";
import { api } from "~/utils/api";

import type { RoundContextType, RoundProviderProps } from "./types";
import type { IRoundData } from "~/utils/types";
import type { IRoundData, Tally } from "~/utils/types";

export const RoundContext = createContext<RoundContextType | undefined>(undefined);

export const RoundProvider: React.FC<RoundProviderProps> = ({ children }: RoundProviderProps) => {
const [isLoading, setIsLoading] = useState<boolean>(false);

const polls = api.maci.poll.useQuery(undefined, { enabled: Boolean(config.maciSubgraphUrl) });
const rounds = api.maci.round.useQuery({ polls: polls.data ?? [] }, { enabled: Boolean(polls.data) });
const polls = api.maci.polls.useQuery(undefined, { enabled: Boolean(config.maciSubgraphUrl) });
const rounds = api.maci.rounds.useQuery({ polls: polls.data ?? [] }, { enabled: Boolean(polls.data) });
const tallies = api.maci.tallies.useQuery(undefined, { enabled: Boolean(config.maciSubgraphUrl) });

// on load we fetch the data from the poll
useEffect(() => {
Expand All @@ -28,6 +29,15 @@ export const RoundProvider: React.FC<RoundProviderProps> = ({ children }: RoundP
rounds.refetch().catch(console.error);
}, [polls, rounds]);

useEffect(() => {
if (tallies.data) {
return;
}

// eslint-disable-next-line no-console
tallies.refetch().catch(console.error);
}, [tallies]);

const getRoundByRoundId = useCallback(
(roundId: string): IRoundData | undefined => rounds.data?.find((round) => round.roundId === roundId),
[rounds],
Expand All @@ -38,14 +48,23 @@ export const RoundProvider: React.FC<RoundProviderProps> = ({ children }: RoundP
[rounds],
);

const isRoundTallied = useCallback(
(tallyAddress: string): boolean => {
const t = tallies.data?.find((tally: Tally) => tally.id === tallyAddress);
return !!t && t.results.length > 0;
},
[tallies],
);

const value = useMemo(
() => ({
rounds: rounds.data,
getRoundByRoundId,
getRoundByPollId,
isLoading,
isRoundTallied,
}),
[rounds, getRoundByRoundId, getRoundByPollId, isLoading],
[rounds, getRoundByRoundId, getRoundByPollId, isLoading, isRoundTallied],
);

return <RoundContext.Provider value={value as RoundContextType}>{children}</RoundContext.Provider>;
Expand Down
1 change: 1 addition & 0 deletions packages/interface/src/contexts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface RoundContextType {
getRoundByRoundId: (roundId: string) => IRoundData | undefined;
getRoundByPollId: (pollId: string) => IRoundData | undefined;
isLoading: boolean;
isRoundTallied: (tallyAddress: string) => boolean;
}

export interface RoundProviderProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const getServerSideProps: GetServerSideProps = async ({ query: { pollId }

export const BallotConfirmation = ({ pollId }: IBallotConfirmationProps): JSX.Element => {
const { getBallot, sumBallot } = useBallot();
const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });
const { getRoundByPollId } = useRound();
const round = useMemo(() => getRoundByPollId(pollId), [pollId, getRoundByPollId]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import { ZeroAddress } from "ethers";
import { useMemo } from "react";
import { Hex } from "viem";

import { Button } from "~/components/ui/Button";
import { config } from "~/config";
import { useRound } from "~/contexts/Round";
import { useProjectResults } from "~/hooks/useResults";
import { formatNumber } from "~/utils/formatNumber";

export interface IProjectAwardedProps {
pollId: string;
tallyFile?: string;
registryAddress: string;
id?: string;
}

export const ProjectAwarded = ({
pollId,
tallyFile = undefined,
registryAddress,
id = "",
}: IProjectAwardedProps): JSX.Element | null => {
const amount = useProjectResults(id, registryAddress as Hex, pollId, tallyFile);
export const ProjectAwarded = ({ pollId, registryAddress, id = "" }: IProjectAwardedProps): JSX.Element | null => {
const { getRoundByPollId } = useRound();
const round = useMemo(() => getRoundByPollId(pollId), [pollId, getRoundByPollId]);
const amount = useProjectResults(id, registryAddress as Hex, pollId, (round?.tallyAddress ?? ZeroAddress) as Hex);

if (amount.isLoading) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const ProjectDetails = ({ pollId, project, action = undefined }: IProjectDetails
const { bio, websiteUrl, payoutAddress, github, twitter, fundingSources, profileImageUrl, bannerImageUrl } =
metadata.data ?? {};

const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });

return (
<div className="relative dark:text-white">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const ProjectItem = ({
action = undefined,
}: IProjectItemProps): JSX.Element => {
const metadata = useProjectMetadata(recipient.metadataUrl);
const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });

return (
<article
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { type Hex, zeroAddress } from "viem";

import { InfiniteLoading } from "~/components/InfiniteLoading";
import { useRound } from "~/contexts/Round";
import { useResults, useProjectsResults } from "~/hooks/useResults";
import { useProjects } from "~/hooks/useProjects";
import { useResults } from "~/hooks/useResults";
import { useRoundState } from "~/utils/state";
import { ERoundState } from "~/utils/types";

Expand All @@ -22,9 +23,13 @@ export const ProjectsResults = ({ pollId }: IProjectsResultsProps): JSX.Element
const router = useRouter();
const { getRoundByPollId } = useRound();
const round = useMemo(() => getRoundByPollId(pollId), [pollId, getRoundByPollId]);
const projects = useProjectsResults((round?.registryAddress ?? zeroAddress) as Hex);
const results = useResults(pollId, (round?.registryAddress ?? zeroAddress) as Hex, round?.tallyFile);
const roundState = useRoundState(pollId);
const projects = useProjects((round?.registryAddress ?? zeroAddress) as Hex);
const results = useResults(
pollId,
(round?.registryAddress ?? zeroAddress) as Hex,
(round?.tallyAddress ?? zeroAddress) as Hex,
);
const roundState = useRoundState({ pollId });

const handleAction = useCallback(
(projectId: string) => (e: Event) => {
Expand Down
46 changes: 46 additions & 0 deletions packages/interface/src/features/results/components/ResultItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Image from "next/image";
import Link from "next/link";
import { useEffect } from "react";

import { useProjectMetadata } from "~/features/projects/hooks/useProjects";

import type { IRecipientWithVotes } from "~/utils/types";

export interface IResultItemProps {
pollId: string;
rank: number;
project: IRecipientWithVotes;
}

export const ResultItem = ({ pollId, rank, project }: IResultItemProps): JSX.Element => {
const metadata = useProjectMetadata(project.metadataUrl);

useEffect(() => {
if (metadata.data) {
return;
}

// eslint-disable-next-line no-console
metadata.refetch().catch(console.error);
}, [metadata]);

return (
<Link href={`/rounds/${pollId}/${project.id}`}>
<div className="flex cursor-pointer leading-8 hover:bg-blue-50">
<div className="my-1 w-8 flex-none justify-center">
{rank === 1 && <Image alt="gold" height="26" src="/gold.svg" width="26" />}

{rank === 2 && <Image alt="silver" height="26" src="/silver.svg" width="26" />}

{rank === 3 && <Image alt="bronze" height="26" src="/bronze.svg" width="26" />}
</div>

<div className="w-6 flex-none text-center">{rank}</div>

<div className="mx-2 flex-1">{metadata.data?.name}</div>

<div className="w-24 flex-none text-end">{`${project.votes} votes`}</div>
</div>
</Link>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface IProjectsProps {
}

export const Projects = ({ pollId = "" }: IProjectsProps): JSX.Element => {
const roundState = useRoundState(pollId);
const roundState = useRoundState({ pollId });

const { getRoundByPollId } = useRound();
const round = useMemo(() => getRoundByPollId(pollId), [pollId, getRoundByPollId]);
Expand All @@ -35,7 +35,11 @@ export const Projects = ({ pollId = "" }: IProjectsProps): JSX.Element => {
const { isRegistered } = useMaci();
const { addToBallot, removeFromBallot, ballotContains, getBallot } = useBallot();

const results = useResults(pollId, (round?.registryAddress ?? zeroAddress) as Hex, round?.tallyFile);
const results = useResults(
pollId,
(round?.registryAddress ?? zeroAddress) as Hex,
(round?.tallyAddress ?? zeroAddress) as Hex,
);

const ballot = useMemo(() => getBallot(pollId), [pollId, getBallot]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const RoundTag = ({ state }: IRoundTagProps): JSX.Element => {
};

export const RoundItem = ({ round }: IRoundItemProps): JSX.Element => {
const roundState = useRoundState(round.pollId);
const roundState = useRoundState({ pollId: round.pollId });

return (
<Link href={`/rounds/${round.pollId}`}>
Expand Down
15 changes: 15 additions & 0 deletions packages/interface/src/hooks/useProjects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { config } from "~/config";
import { api } from "~/utils/api";

import type { UseTRPCInfiniteQueryResult } from "@trpc/react-query/shared";
import type { IRecipient } from "~/utils/types";

const seed = 0;
export function useProjects(registryAddress: string): UseTRPCInfiniteQueryResult<IRecipient[], unknown, unknown> {
return api.projects.projects.useInfiniteQuery(
{ registryAddress, limit: config.pageSize, seed },
{
getNextPageParam: (_, pages) => pages.length,
},
);
}
Loading

0 comments on commit 3328fdb

Please sign in to comment.