Skip to content

Commit 18217e0

Browse files
committed
feat: integrates staking round view
1 parent 7b8b92a commit 18217e0

File tree

2 files changed

+128
-33
lines changed

2 files changed

+128
-33
lines changed
Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { useMemo } from "react";
22

33
import { Project, Round } from "../../api/types";
4-
5-
import { BasicCard } from "../../common/styles";
6-
74
import { Application, useDataLayer } from "data-layer";
85
import { useRoundApprovedApplications } from "../../projects/hooks/useRoundApplications";
6+
import { useRoundStakingSummary } from "../../projects/hooks/useRoundStakingSummary";
7+
import {
8+
SortOption,
9+
useSortApplications,
10+
} from "../../projects/hooks/useSortApplications";
911
import { ProjectCard } from "./ProjectCard";
12+
import { useSearchParams } from "react-router-dom";
13+
import { useIsStakable } from "../ViewProjectDetails/components/StakingBannerAndModal/hooks/useIsStakable";
1014

1115
export const ProjectList = (props: {
1216
projects?: Project[];
@@ -20,39 +24,109 @@ export const ProjectList = (props: {
2024
setCurrentProjectAddedToCart: React.Dispatch<React.SetStateAction<Project>>;
2125
setShowCartNotification: React.Dispatch<React.SetStateAction<boolean>>;
2226
}): JSX.Element => {
23-
const { projects, roundRoutePath, chainId, roundId } = props;
27+
const { projects: _projects, roundRoutePath, chainId, roundId } = props;
2428
const dataLayer = useDataLayer();
29+
const [searchParams] = useSearchParams();
30+
const sortOption = searchParams.get("orderBy");
31+
const isStakableRound = useIsStakable({
32+
chainId,
33+
roundId,
34+
});
2535

26-
const { data: applications } = useRoundApprovedApplications(
27-
{
28-
chainId,
29-
roundId,
30-
},
36+
enum SortOptionEnum {
37+
TOTAL_STAKED_DESC = "totalStakedDesc",
38+
TOTAL_DONATIONS_DESC = "totalDonationsDesc",
39+
TOTAL_CONTRIBUTORS_DESC = "totalContributorsDesc",
40+
TOTAL_STAKED_ASC = "totalStakedAsc",
41+
TOTAL_CONTRIBUTORS_ASC = "totalContributorsAsc",
42+
TOTAL_DONATIONS_ASC = "totalDonationsAsc",
43+
}
44+
const params = isStakableRound
45+
? {}
46+
: {
47+
chainId,
48+
roundId,
49+
};
50+
51+
const { data: _applications } = useRoundApprovedApplications(
52+
params,
3153
dataLayer
3254
);
3355

56+
const { data: poolSummary, isLoading } = useRoundStakingSummary(
57+
roundId,
58+
chainId.toString(),
59+
isStakableRound
60+
);
61+
62+
const _stakedApplications = useSortApplications(
63+
poolSummary,
64+
chainId.toString(),
65+
roundId,
66+
SortOptionEnum[sortOption as keyof typeof SortOptionEnum] as SortOption
67+
);
68+
69+
const applications = useMemo(() => {
70+
return _applications?.length ? _applications : (_stakedApplications ?? []);
71+
}, [_stakedApplications, _applications]);
72+
73+
const isDonationPeriodStarted = props.showProjectCardFooter;
74+
75+
const LeaderboardTitle = useMemo(() => {
76+
return sortOption === "TOTAL_STAKED_DESC"
77+
? "Leaderboard - Most GTC Staked"
78+
: sortOption === "TOTAL_DONATIONS_DESC"
79+
? "Leaderboard - Most Donations"
80+
: sortOption === "TOTAL_CONTRIBUTORS_DESC"
81+
? "Leaderboard - Most Contributors"
82+
: sortOption === "TOTAL_STAKED_ASC"
83+
? "Leaderboard - Least GTC Staked"
84+
: sortOption === "TOTAL_CONTRIBUTORS_ASC"
85+
? "Leaderboard - Least Contributors"
86+
: sortOption === "TOTAL_DONATIONS_ASC"
87+
? "Leaderboard - Least Donations"
88+
: isStakableRound && isDonationPeriodStarted
89+
? "Leaderboard - Most GTC Staked"
90+
: "";
91+
}, [sortOption, isStakableRound, isDonationPeriodStarted]);
92+
93+
const projects = useMemo(() => {
94+
return (applications.map((application) => {
95+
return _projects?.find(
96+
(project) =>
97+
project.anchorAddress?.toLowerCase() ===
98+
application.anchorAddress?.toLowerCase()
99+
);
100+
}) ?? []) as Project[];
101+
}, [applications, _projects]);
102+
34103
const applicationsMapByGrantApplicationId:
35-
| Map<string, Application>
104+
| Map<string, Application & { totalStaked?: number }>
36105
| undefined = useMemo(() => {
37106
if (!applications) return;
38-
const map: Map<string, Application> = new Map();
107+
const map: Map<string, Application & { totalStaked?: number }> = new Map();
39108
applications.forEach((application) =>
40109
map.set(application.projectId, application)
41110
);
42111
return map;
43112
}, [applications]);
44113

45114
return (
46-
<>
115+
<div className="flex flex-col gap-y-8">
116+
{LeaderboardTitle && !props.isProjectsLoading && !isLoading && (
117+
<span className="text-[32px]/[39px] font-modern-era-medium">
118+
{LeaderboardTitle}
119+
</span>
120+
)}
47121
<div className="grid gap-x-6 gap-y-12 gap-5 justify-around md:justify-start sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 w-full">
48-
{props.isProjectsLoading ? (
122+
{props.isProjectsLoading || isLoading ? (
49123
<>
50124
{Array(6)
51125
.fill("")
52126
.map((item, index) => (
53-
<BasicCard
127+
<div
54128
key={index}
55-
className="relative animate-pulse bg-grey-100"
129+
className="relative animate-pulse bg-grey-100 w-[326px] rounded-3xl h-[370px]"
56130
/>
57131
))}
58132
</>
@@ -61,7 +135,7 @@ export const ProjectList = (props: {
61135
{projects.map((project) => {
62136
return (
63137
<ProjectCard
64-
key={project.projectRegistryId}
138+
key={project?.projectRegistryId}
65139
project={project}
66140
roundRoutePath={roundRoutePath}
67141
showProjectCardFooter={props.showProjectCardFooter}
@@ -75,14 +149,19 @@ export const ProjectList = (props: {
75149
setShowCartNotification={props.setShowCartNotification}
76150
crowdfundedUSD={
77151
applicationsMapByGrantApplicationId?.get(
78-
project.projectRegistryId
152+
project?.projectRegistryId ?? ""
79153
)?.totalAmountDonatedInUsd ?? 0
80154
}
81155
uniqueContributorsCount={
82156
applicationsMapByGrantApplicationId?.get(
83-
project.projectRegistryId
157+
project?.projectRegistryId ?? ""
84158
)?.uniqueDonorsCount ?? 0
85159
}
160+
totalStaked={
161+
applicationsMapByGrantApplicationId?.get(
162+
project?.projectRegistryId ?? ""
163+
)?.totalStaked ?? 0
164+
}
86165
/>
87166
);
88167
})}
@@ -91,6 +170,6 @@ export const ProjectList = (props: {
91170
<p>No projects</p>
92171
)}
93172
</div>
94-
</>
173+
</div>
95174
);
96175
};

packages/grant-explorer/src/features/round/ViewRoundPage/RoundPage.tsx

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const builderURL = process.env.REACT_APP_BUILDER_URL;
2626
import CartNotification from "../../common/CartNotification";
2727
import { useAccount, useToken } from "wagmi";
2828
import { getAddress } from "viem";
29-
import { DefaultLayout } from "../../common/DefaultLayout";
29+
import { RoundViewLayout } from "../../common/DefaultLayout";
3030
import { getUnixTime } from "date-fns";
3131
import { PresentationChartBarIcon } from "@heroicons/react/24/outline";
3232

@@ -36,7 +36,10 @@ import { ProjectList } from "./ProjectList";
3636
import { RoundStatsTabContent } from "./RoundStatsTabContent";
3737
import { RoundTabs } from "./RoundTabs";
3838
import { getAlloVersion } from "common/src/config";
39+
import { StakingBannerAndModal } from "../ViewProjectDetails/components/StakingBannerAndModal";
40+
import { useIsStakable } from "../ViewProjectDetails/components/StakingBannerAndModal/hooks/useIsStakable";
3941

42+
import { SortDropdown } from "./SortDropdown";
4043
const alloVersion = getAlloVersion();
4144

4245
export function RoundPage(props: {
@@ -54,6 +57,11 @@ export function RoundPage(props: {
5457
const [projects, setProjects] = useState<Project[]>();
5558
const [randomizedProjects, setRandomizedProjects] = useState<Project[]>();
5659
const { address: walletAddress } = useAccount();
60+
61+
const isStakableRound = useIsStakable({
62+
chainId: Number(chainId),
63+
roundId,
64+
});
5765
const isSybilDefenseEnabled =
5866
round?.roundMetadata?.quadraticFundingConfig?.sybilDefense === true ||
5967
round?.roundMetadata?.quadraticFundingConfig?.sybilDefense !== "none";
@@ -254,7 +262,7 @@ export function RoundPage(props: {
254262

255263
return (
256264
<>
257-
<DefaultLayout>
265+
<RoundViewLayout infoCard={<StakingBannerAndModal isRoundView={true} />}>
258266
{showCartNotification && renderCartNotification()}
259267
{props.isAfterRoundEndDate && (
260268
<div className="relative top-6">
@@ -442,24 +450,32 @@ export function RoundPage(props: {
442450
onChange={handleTabChange}
443451
/>
444452
{selectedTab === 0 && (
445-
<div className="relative">
446-
<Search className="absolute h-4 w-4 mt-3 ml-3 " />
447-
<Input
448-
className="w-full lg:w-64 h-8 rounded-full pl-10 font-mono"
449-
type="text"
450-
placeholder="Search"
451-
value={searchQuery}
452-
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
453-
setSearchQuery(e.target.value)
454-
}
455-
/>
453+
<div className="relative flex items-center gap-2">
454+
<div className="relative">
455+
<Search className="absolute h-4 w-4 mt-3 ml-3 " />
456+
<Input
457+
className="w-full lg:w-64 h-8 rounded-full pl-10 font-mono"
458+
type="text"
459+
placeholder="Search"
460+
value={searchQuery}
461+
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
462+
setSearchQuery(e.target.value)
463+
}
464+
/>
465+
</div>
466+
{isStakableRound && props.isAfterRoundStartDate && (
467+
<div className="flex gap-2 items-center">
468+
<div>Sort by</div>
469+
<SortDropdown />
470+
</div>
471+
)}
456472
</div>
457473
)}
458474
</div>
459475

460476
<div>{projectDetailsTabs[selectedTab].content}</div>
461477
</div>
462-
</DefaultLayout>
478+
</RoundViewLayout>
463479
</>
464480
);
465481
}

0 commit comments

Comments
 (0)