diff --git a/.changeset/giant-cougars-sort.md b/.changeset/giant-cougars-sort.md
new file mode 100644
index 000000000..98055f8a3
--- /dev/null
+++ b/.changeset/giant-cougars-sort.md
@@ -0,0 +1,5 @@
+---
+"@carrot-kpi/host-frontend": minor
+---
+
+Refactor the all campaigns page to fetch and resolve all the kpi tokens
diff --git a/.changeset/quiet-sheep-decide.md b/.changeset/quiet-sheep-decide.md
new file mode 100644
index 000000000..ac009aa14
--- /dev/null
+++ b/.changeset/quiet-sheep-decide.md
@@ -0,0 +1,5 @@
+---
+"@carrot-kpi/react": minor
+---
+
+Add new useResolvedKPITokens hook to fetch and resolve the kpi tokens
diff --git a/packages/frontend/src/pages/campaigns/grid/index.tsx b/packages/frontend/src/pages/campaigns/grid/index.tsx
index 0bc3b60de..9b6b22424 100644
--- a/packages/frontend/src/pages/campaigns/grid/index.tsx
+++ b/packages/frontend/src/pages/campaigns/grid/index.tsx
@@ -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)
@@ -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);
@@ -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(
@@ -73,25 +66,24 @@ export const Grid = ({
return (
-
- {loading || !kpiTokensReady ? (
- placeholder
- ) : paginatedItems.length > 0 ? (
- paginatedItems.map(
- (kpiToken: KPIToken | ResolvedKPIToken) => {
- return (
-
- );
- },
- )
- ) : (
-
- )}
-
+ {!loading && kpiTokensReady && paginatedItems.length === 0 ? (
+
+ ) : (
+
+ {loading || !kpiTokensReady
+ ? placeholder
+ : paginatedItems.map(
+ (kpiToken: KPIToken | ResolvedKPIToken) => {
+ return (
+
+ );
+ },
+ )}
+
+ )}
{
const { t } = useTranslation();
const location = useLocation();
const [, setSearchParams] = useSearchParams();
- const { loading, kpiTokens: response } = useKPITokens();
+ const { loading, resolvedKPITokens } = useResolvedKPITokens();
const [kpiTokens, setKpiTokens] = useState<(KPIToken | ResolvedKPIToken)[]>(
[],
@@ -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);
@@ -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);
@@ -145,7 +116,7 @@ export const Campaigns = () => {
return (
-
+
@@ -180,7 +151,6 @@ export const Campaigns = () => {
loading={loading}
kpiTokensReady={kpiTokensReady}
items={sortedAndfilteredKPITokens}
- onResolved={handleOnResolvedKPIToken}
/>
diff --git a/packages/react/package.json b/packages/react/package.json
index cbe22dbed..a7637c1b4 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -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",
@@ -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"
],
diff --git a/packages/react/src/hooks/usePagination.ts b/packages/react/src/hooks/usePagination.ts
index 8b525eac3..7377d5d8a 100644
--- a/packages/react/src/hooks/usePagination.ts
+++ b/packages/react/src/hooks/usePagination.ts
@@ -1,19 +1,20 @@
-const ITEMS_PER_PAGE = 18;
+const ITEMS_PER_PAGE = 18 as const;
interface PaginationParams
{
data: T[];
- currentPage: number;
- totalItems?: number;
+ page: number;
+ size?: number;
}
export const usePagination = (params: PaginationParams) => {
- 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 };
};
diff --git a/packages/react/src/hooks/useResolvedKPITokens.ts b/packages/react/src/hooks/useResolvedKPITokens.ts
new file mode 100644
index 000000000..529567b4e
--- /dev/null
+++ b/packages/react/src/hooks/useResolvedKPITokens.ts
@@ -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 {
+ if (!cancelled) setLoading(true);
+
+ try {
+ 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) => [
+ ...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 };
+}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 669fdba28..21a982ec2 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -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";