Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Campaigns page #431

Merged
merged 5 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/giant-cougars-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@carrot-kpi/host-frontend": minor
---

Refactor the all campaigns page to fetch and resolve all the kpi tokens
5 changes: 5 additions & 0 deletions .changeset/quiet-sheep-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@carrot-kpi/react": minor
---

Add new useResolvedKPITokens hook to fetch and resolve the kpi tokens
52 changes: 22 additions & 30 deletions packages/frontend/src/pages/campaigns/grid/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { usePagination } from "@carrot-kpi/react";
import type { KPIToken, ResolvedKPIToken } from "@carrot-kpi/sdk";
import React, { useCallback, useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { KPITokenCard } from "../../../components/ui/kpi-token-card";
import { Empty } from "../../../components/ui/empty";
import { Pagination } from "../../../components/pagination";
import { usePagination } from "@carrot-kpi/react";

const placeholder = new Array(8)
.fill(null)
Expand All @@ -14,15 +14,9 @@ interface GridProps {
loading?: boolean;
kpiTokensReady?: boolean;
items: (KPIToken | ResolvedKPIToken)[];
onResolved: (resolved: ResolvedKPIToken) => void;
}

export const Grid = ({
loading,
kpiTokensReady,
items,
onResolved,
}: GridProps) => {
export const Grid = ({ loading, kpiTokensReady, items }: GridProps) => {
const [searchParams, setSearchParams] = useSearchParams();
const [page, setPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(4);
Expand All @@ -45,15 +39,14 @@ export const Grid = ({

resizeObserver.observe(document.body);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
resizeObserver.unobserve(document.body);
};
});

const { data: paginatedItems, totalPages } = usePagination({
data: items,
currentPage: page,
totalItems: itemsPerPage,
page,
size: itemsPerPage,
});

const handlePageChange = useCallback(
Expand All @@ -73,25 +66,24 @@ export const Grid = ({
return (
<div className="flex flex-col items-center w-full">
<div className="space-y-12 md:space-y-16">
<div className="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-8">
{loading || !kpiTokensReady ? (
placeholder
) : paginatedItems.length > 0 ? (
paginatedItems.map(
(kpiToken: KPIToken | ResolvedKPIToken) => {
return (
<KPITokenCard
key={kpiToken.address}
kpiToken={kpiToken}
onResolved={onResolved}
/>
);
},
)
) : (
<Empty />
)}
</div>
{!loading && kpiTokensReady && paginatedItems.length === 0 ? (
<Empty />
) : (
<div className="grid xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-8">
{loading || !kpiTokensReady
? placeholder
: paginatedItems.map(
(kpiToken: KPIToken | ResolvedKPIToken) => {
return (
<KPITokenCard
key={kpiToken.address}
kpiToken={kpiToken}
/>
);
},
)}
</div>
)}
<Pagination
onCurrentPageChange={handlePageChange}
currentPage={page}
Expand Down
40 changes: 5 additions & 35 deletions packages/frontend/src/pages/campaigns/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Typography, type SelectOption } from "@carrot-kpi/ui";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Layout } from "../../components/layout";
import { useKPITokens } from "@carrot-kpi/react";
import { useResolvedKPITokens } from "@carrot-kpi/react";
import { KPIToken, ResolvedKPIToken } from "@carrot-kpi/sdk";
import { CampaignsTopNav } from "./top-nav";
import {
Expand All @@ -23,7 +23,7 @@ export const Campaigns = () => {
const { t } = useTranslation();
const location = useLocation();
const [, setSearchParams] = useSearchParams();
const { loading, kpiTokens: response } = useKPITokens();
const { loading, resolvedKPITokens } = useResolvedKPITokens();

const [kpiTokens, setKpiTokens] = useState<(KPIToken | ResolvedKPIToken)[]>(
[],
Expand All @@ -49,9 +49,9 @@ export const Campaigns = () => {

useEffect(() => {
if (loading || kpiTokens.length > 0) return;
setKpiTokens(Object.values(response));
setKpiTokens(Object.values(resolvedKPITokens));
setKpiTokensReady(true);
}, [response, kpiTokens, loading]);
}, [resolvedKPITokens, kpiTokens, loading]);

useEffect(() => {
const url = new URLSearchParams(location.search);
Expand All @@ -78,35 +78,6 @@ export const Campaigns = () => {
);
}, [kpiTokens, state.value, sort.value, debouncedSearchQuery]);

const handleOnResolvedKPIToken = useCallback(
(resolved: ResolvedKPIToken) => {
if (
(
kpiTokens.find(
(kpiToken) => kpiToken.address === resolved.address,
) as ResolvedKPIToken
).specification
)
return;

const updatedKPITokens = kpiTokens.map((kpiToken) => {
if (kpiToken.address === resolved.address) {
return {
...kpiToken,
specification: resolved.specification,
expired: resolved.expired,
oracles: resolved.oracles,
template: resolved.template,
};
} else {
return kpiToken;
}
});
setKpiTokens(updatedKPITokens);
},
[kpiTokens],
);

const updateURLParams = useCallback(
(key: string, value: string) => {
const searchParams = new URLSearchParams(location.search);
Expand Down Expand Up @@ -145,7 +116,7 @@ export const Campaigns = () => {

return (
<Layout>
<div className="relative bg-grid-light dark:bg-grid-dark">
<div className="relative">
<div className="relative">
<div className="px-6 py-16 md:px-10">
<Typography variant="h1">
Expand Down Expand Up @@ -180,7 +151,6 @@ export const Campaigns = () => {
loading={loading}
kpiTokensReady={kpiTokensReady}
items={sortedAndfilteredKPITokens}
onResolved={handleOnResolvedKPIToken}
/>
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@
"import": "./dist/es/hooks/useResolvedKPIToken.mjs",
"default": "./dist/cjs/hooks/useResolvedKPIToken.cjs"
},
"./useResolvedKPITokens": {
"types": "./dist/types/hooks/useResolvedKPITokens.d.ts",
"import": "./dist/es/hooks/useResolvedKPITokens.mjs",
"default": "./dist/cjs/hooks/useResolvedKPITokens.cjs"
},
"./useResolvedTemplate": {
"types": "./dist/types/hooks/useResolvedTemplate.d.ts",
"import": "./dist/es/hooks/useResolvedTemplate.mjs",
Expand Down Expand Up @@ -273,6 +278,9 @@
"useResolvedKPIToken": [
"./dist/types/hooks/useResolvedKPIToken.d.ts"
],
"useResolvedKPITokens": [
"./dist/types/hooks/useResolvedKPITokens.d.ts"
],
"useResolvedTemplate": [
"./dist/types/hooks/useResolvedTemplate.d.ts"
],
Expand Down
23 changes: 12 additions & 11 deletions packages/react/src/hooks/usePagination.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
const ITEMS_PER_PAGE = 18;
const ITEMS_PER_PAGE = 18 as const;

interface PaginationParams<T> {
data: T[];
currentPage: number;
totalItems?: number;
page: number;
size?: number;
}

export const usePagination = <T>(params: PaginationParams<T>) => {
const { data, currentPage, totalItems } = params;
const itemsPerPage = totalItems ? totalItems : ITEMS_PER_PAGE;
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const slicedData =
data.length > itemsPerPage ? data.slice(startIndex, endIndex) : data;
const totalPages = Math.ceil(data.length / itemsPerPage);
const { data, page, size } = params;

return { data: slicedData, totalPages };
const pageSize = size ? size : ITEMS_PER_PAGE;
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const pageData =
data.length > pageSize ? data.slice(startIndex, endIndex) : data;
const totalPages = Math.ceil(data.length / pageSize);

return { data: pageData, totalPages };
};
77 changes: 77 additions & 0 deletions packages/react/src/hooks/useResolvedKPITokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Fetcher, ResolvedKPIToken } from "@carrot-kpi/sdk";
import { type Address, usePublicClient } from "wagmi";
import { useEffect, useState } from "react";
import { useIPFSGatewayURL } from "./useIPFSGatewayURL";
import { usePreferDecentralization } from "./usePreferDecentralization";

interface ResolvedKPITokensParams {
blacklisted?: Address[];
}

export function useResolvedKPITokens(params?: ResolvedKPITokensParams): {
loading: boolean;
resolvedKPITokens: ResolvedKPIToken[];
} {
const publicClient = usePublicClient();
const preferDecentralization = usePreferDecentralization();
const ipfsGatewayURL = useIPFSGatewayURL();
const [resolvedKPITokens, setResolvedKPITokens] = useState<
ResolvedKPIToken[]
>([]);

const [loading, setLoading] = useState(true);

useEffect(() => {
let cancelled = false;
async function fetchData(): Promise<void> {
if (!cancelled) setLoading(true);

try {
luzzif marked this conversation as resolved.
Show resolved Hide resolved
const kpiTokens = await Fetcher.fetchKPITokens({
publicClient,
preferDecentralization,
blacklisted: params?.blacklisted,
});

await Promise.allSettled(
Object.values(kpiTokens).map(async (kpiToken) => {
try {
const resolved = (
await Fetcher.resolveKPITokens({
ipfsGatewayURL,
kpiTokens: [kpiToken],
})
)[kpiToken.address];
if (!resolved) return;
if (!cancelled)
setResolvedKPITokens((previousState) => [
luzzif marked this conversation as resolved.
Show resolved Hide resolved
...previousState,
resolved,
]);
} catch (error) {
console.error(
`error resolving kpi token at address ${kpiToken.address}`,
error,
);
}
}),
);
} catch (error) {
console.error("error fetching resolved kpi tokens", error);
} finally {
if (!cancelled) setLoading(false);
}
}
void fetchData();
return () => {
cancelled = true;
};
}, [
ipfsGatewayURL,
publicClient,
preferDecentralization,
params?.blacklisted,
]);

return { loading, resolvedKPITokens };
}
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./hooks/useOracleTemplates";
export * from "./hooks/usePagination";
export * from "./hooks/usePreferDecentralization";
export * from "./hooks/useResolvedKPIToken";
export * from "./hooks/useResolvedKPITokens";
export * from "./hooks/useResolvedTemplate";
export * from "./hooks/useResolvedTemplates";
export * from "./hooks/useSetDevMode";
Expand Down