Skip to content

Commit

Permalink
Adds better loading state on voter page
Browse files Browse the repository at this point in the history
  • Loading branch information
mcgingras committed Oct 29, 2024
1 parent 563e34a commit cd4ead2
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 99 deletions.
10 changes: 5 additions & 5 deletions src/app/api/common/citizens/getCitizens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async function getCitizens({
sort = "shuffle",
seed,
}: {
pagination: PaginationParams;
pagination?: PaginationParams;
sort: string;
seed?: number;
}): Promise<PaginatedResult<DelegateChunk[]>> {
Expand All @@ -26,7 +26,7 @@ async function getCitizens({
if (sort === "shuffle") {
return prisma.$queryRawUnsafe<DelegatesGetPayload[]>(
`
SELECT
SELECT
citizens.address AS delegate,
delegate.voting_power,
advanced_vp,
Expand Down Expand Up @@ -54,7 +54,7 @@ async function getCitizens({
WHERE citizens.retro_funding_round = (SELECT MAX(retro_funding_round) FROM agora.citizens)
ORDER BY md5(CAST(citizens.address AS TEXT) || CAST($2 AS TEXT))
OFFSET $3
LIMIT $4;
LIMIT $4;
`,
slug,
seed,
Expand All @@ -64,7 +64,7 @@ async function getCitizens({
} else {
return prisma.$queryRawUnsafe<DelegatesGetPayload[]>(
`
SELECT
SELECT
citizens.address AS delegate,
delegate.voting_power,
direct_vp,
Expand Down Expand Up @@ -92,7 +92,7 @@ async function getCitizens({
AND citizens.dao_slug = $1::config.dao_slug
WHERE citizens.retro_funding_round = (SELECT MAX(retro_funding_round) FROM agora.citizens)
ORDER BY COALESCE(delegate.voting_power, 0) DESC,
citizens.address ASC
citizens.address ASC
OFFSET $2
LIMIT $3;
`,
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/common/delegates/getDelegates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async function getDelegates({
seed,
filters,
}: {
pagination: PaginationParams;
pagination?: PaginationParams;
sort: string;
seed?: number;
filters?: {
Expand Down
96 changes: 8 additions & 88 deletions src/app/delegates/page.jsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
import { fetchCitizens as apiFetchCitizens } from "@/app/api/common/citizens/getCitizens";
import { fetchDelegates as apiFetchDelegates } from "@/app/api/common/delegates/getDelegates";
import { fetchCurrentDelegators as apiFetchCurrentDelegators } from "@/app/api/common/delegations/getDelegations";
import DelegateCardList from "@/components/Delegates/DelegateCardList/DelegateCardList";
import CitizenCardList from "@/components/Delegates/DelegateCardList/CitzenCardList";
import DelegateTabs from "@/components/Delegates/DelegatesTabs/DelegatesTabs";
import Hero from "@/components/Hero/Hero";
import { TabsContent } from "@/components/ui/tabs";
import { citizensFilterOptions, delegatesFilterOptions } from "@/lib/constants";
import Tenant from "@/lib/tenant/tenant";
import React from "react";

async function fetchCitizens(sort, seed, pagination) {
"use server";

return apiFetchCitizens({ pagination, seed, sort });
}

async function fetchDelegates(sort, seed, filters, pagination) {
"use server";

return apiFetchDelegates({ pagination, seed, sort, filters });
}

async function fetchDelegators(address) {
"use server";

return apiFetchCurrentDelegators(address);
}
import React, { Suspense } from "react";
import DelegateCardWrapper, {
DelegateCardLoadingState,
} from "@/components/Delegates/DelegateCardList/DelegateCardWrapper";
import Hero from "@/components/Hero/Hero";

export async function generateMetadata({}, parent) {
const { ui } = Tenant.current();
Expand Down Expand Up @@ -58,69 +35,12 @@ export async function generateMetadata({}, parent) {
}

export default async function Page({ searchParams }) {
const { ui } = Tenant.current();

const sort =
delegatesFilterOptions[searchParams.orderBy]?.sort ||
delegatesFilterOptions.weightedRandom.sort;
const citizensSort =
citizensFilterOptions[searchParams.citizensOrderBy]?.value ||
citizensFilterOptions.shuffle.sort;

const filters = {
...(searchParams.delegateeFilter && {
delegatee: searchParams.delegateeFilter,
}),
...(searchParams.issueFilter && { issues: searchParams.issueFilter }),
...(searchParams.stakeholderFilter && {
stakeholders: searchParams.stakeholderFilter,
}),
};

const endorsedToggle = ui.toggle("delegates/endorsed-filter");
if (endorsedToggle?.enabled) {
const defaultFilter = endorsedToggle.config.defaultFilter;
filters.endorsed =
searchParams?.endorsedFilter === undefined
? defaultFilter
: searchParams.endorsedFilter === "true";
}

const tab = searchParams.tab;
const seed = Math.random();
const delegates =
tab === "citizens"
? await fetchCitizens(citizensSort, seed)
: await fetchDelegates(sort, seed, filters);

return (
<section>
<Hero />
<DelegateTabs>
<TabsContent value="delegates">
<DelegateCardList
isDelegatesCitizensFetching={tab === "citizens"}
initialDelegates={delegates}
fetchDelegates={async (pagination, seed) => {
"use server";
return apiFetchDelegates({ pagination, seed, sort, filters });
}}
fetchDelegators={fetchDelegators}
/>
</TabsContent>
<TabsContent value="citizens">
<CitizenCardList
isDelegatesCitizensFetching={tab !== "citizens"}
initialDelegates={delegates}
fetchDelegates={async (pagination, seed) => {
"use server";

return apiFetchCitizens({ pagination, seed, sort: citizensSort });
}}
fetchDelegators={fetchDelegators}
/>{" "}
</TabsContent>
</DelegateTabs>
<Suspense fallback={<DelegateCardLoadingState />}>
<DelegateCardWrapper searchParams={searchParams} />
</Suspense>
</section>
);
}
6 changes: 3 additions & 3 deletions src/components/Delegates/DelegateCardList/CitzenCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import useConnectedDelegate from "@/hooks/useConnectedDelegate";
import { cn } from "@/lib/utils";
import { useAgoraContext } from "@/contexts/AgoraContext";
import { PaginatedResult, PaginationParams } from "@/app/lib/pagination";
import { Delegate } from "@/app/api/common/delegates/delegate";
import { DelegateChunk } from "@/app/api/common/delegates/delegate";

interface Props {
isDelegatesCitizensFetching: boolean;
initialDelegates: PaginatedResult<Delegate[]>;
initialDelegates: PaginatedResult<DelegateChunk[]>;
fetchDelegates: (
pagination: PaginationParams,
seed: number
) => Promise<PaginatedResult<Delegate[]>>;
) => Promise<PaginatedResult<DelegateChunk[]>>;
fetchDelegators: (addressOrENSName: string) => Promise<Delegation[] | null>;
}

Expand Down
137 changes: 137 additions & 0 deletions src/components/Delegates/DelegateCardList/DelegateCardWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { fetchCitizens as apiFetchCitizens } from "@/app/api/common/citizens/getCitizens";
import { fetchDelegates as apiFetchDelegates } from "@/app/api/common/delegates/getDelegates";
import { fetchCurrentDelegators as apiFetchCurrentDelegators } from "@/app/api/common/delegations/getDelegations";
import DelegateCardList from "@/components/Delegates/DelegateCardList/DelegateCardList";
import CitizenCardList from "@/components/Delegates/DelegateCardList/CitzenCardList";
import DelegateTabs from "@/components/Delegates/DelegatesTabs/DelegatesTabs";
import { TabsContent } from "@/components/ui/tabs";
import { citizensFilterOptions, delegatesFilterOptions } from "@/lib/constants";
import Tenant from "@/lib/tenant/tenant";
import React from "react";
import { PaginationParams } from "@/app/lib/pagination";
import { UIEndorsedConfig } from "@/lib/tenant/tenantUI";

async function fetchCitizens(
sort: string,
seed: number,
pagination?: PaginationParams
) {
"use server";

return apiFetchCitizens({ pagination, seed, sort });
}

async function fetchDelegates(
sort: string,
seed: number,
filters: any,
pagination?: PaginationParams
) {
"use server";

return apiFetchDelegates({ pagination, seed, sort, filters });
}

async function fetchDelegators(address: string) {
"use server";

return apiFetchCurrentDelegators(address);
}

const DelegateCardWrapper = async ({ searchParams }: { searchParams: any }) => {
const { ui } = Tenant.current();

const sort =
delegatesFilterOptions[
searchParams.orderBy as keyof typeof delegatesFilterOptions
]?.sort || delegatesFilterOptions.weightedRandom.sort;
const citizensSort =
citizensFilterOptions[
searchParams.citizensOrderBy as keyof typeof citizensFilterOptions
]?.value || citizensFilterOptions.shuffle.sort;

const filters = {
...(searchParams.delegateeFilter && {
delegatee: searchParams.delegateeFilter,
}),
...(searchParams.issueFilter && { issues: searchParams.issueFilter }),
...(searchParams.stakeholderFilter && {
stakeholders: searchParams.stakeholderFilter,
}),
};

const endorsedToggle = ui.toggle("delegates/endorsed-filter");
if (endorsedToggle?.enabled) {
const defaultFilter = (endorsedToggle.config as UIEndorsedConfig)
.defaultFilter;
filters.endorsed =
searchParams?.endorsedFilter === undefined
? defaultFilter
: searchParams.endorsedFilter === "true";
}

const tab = searchParams.tab;
const seed = Math.random();
const delegates =
tab === "citizens"
? await fetchCitizens(citizensSort, seed)
: await fetchDelegates(sort, seed, filters);

return (
<DelegateTabs>
<TabsContent value="delegates">
<DelegateCardList
isDelegatesCitizensFetching={tab === "citizens"}
initialDelegates={delegates}
fetchDelegates={async (pagination, seed) => {
"use server";
return apiFetchDelegates({ pagination, seed, sort, filters });
}}
// @ts-ignore
fetchDelegators={fetchDelegators}
/>
</TabsContent>
<TabsContent value="citizens">
<CitizenCardList
isDelegatesCitizensFetching={tab !== "citizens"}
initialDelegates={delegates}
fetchDelegates={async (pagination, seed) => {
"use server";

return apiFetchCitizens({ pagination, seed, sort: citizensSort });
}}
// @ts-ignore
fetchDelegators={fetchDelegators}
/>{" "}
</TabsContent>
</DelegateTabs>
);
};

export const DelegateCardLoadingState = () => {
return (
<div>
<div className="flex flex-row justify-between items-center">
<h1 className="text-2xl font-bold">Delegates</h1>
<div className="flex flex-row gap-2">
<span className="block w-[150px] h-[36px] rounded-full bg-tertiary/10 animate-pulse"></span>
<span className="block w-[150px] h-[36px] rounded-full bg-tertiary/10 animate-pulse"></span>
<span className="block w-[150px] h-[36px] rounded-full bg-tertiary/10 animate-pulse"></span>
</div>
</div>
<div className="grid grid-flow-row grid-cols-1 sm:grid-cols-3 justify-around sm:justify-between py-4 gap-4 sm:gap-8">
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
<span className="block w-full h-[250px] rounded-lg bg-tertiary/10 animate-pulse"></span>
</div>
</div>
);
};

export default DelegateCardWrapper;
3 changes: 1 addition & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
"**/*.tsx",
"**/*.jsx",
"**/*.js",
".next/types/**/*.ts",
"src/components/Dialogs/UndelegateDialog"
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}

0 comments on commit cd4ead2

Please sign in to comment.