Skip to content

Commit 95ba3b2

Browse files
feat: karma gap project activities (#3820)
1 parent 376bc45 commit 95ba3b2

File tree

5 files changed

+603
-3
lines changed

5 files changed

+603
-3
lines changed

.env.example

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,9 @@ REACT_APP_COINGECKO_API_KEY=
7979
# ---------------------------
8080

8181
REACT_APP_STAKING_APP=https://boost.explorer.gitcoin.co/
82-
REACT_APP_STAKING_HUB_ENDPOINT=https://api.staking-hub.gitcoin.co
82+
REACT_APP_STAKING_HUB_ENDPOINT=https://api.staking-hub.gitcoin.co
83+
84+
# ---------------------------
85+
# Karma
86+
REACT_APP_KARMA_GAP_INDEXER_URL=https://gapapi.karmahq.xyz
87+
REACT_APP_KARMA_GAP_APP_URL=https://gap.karmahq.xyz

packages/grant-explorer/src/features/api/gap.ts

Lines changed: 195 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,73 @@ export interface IGapVerified {
7474
};
7575
}
7676

77+
export interface IGapProjectUpdate {
78+
id: string;
79+
uid: Hex;
80+
schemaUID: Hex;
81+
refUID: Hex;
82+
attester: Hex;
83+
recipient: Hex;
84+
revoked: boolean;
85+
revocationTime: number;
86+
createdAt: string;
87+
updatedAt: string;
88+
chainID: number;
89+
type: string;
90+
data: {
91+
title: string;
92+
text: string;
93+
startDate?: Date;
94+
endDate?: Date;
95+
grants?: string[];
96+
indicators?: {
97+
name: string;
98+
indicatorId: string;
99+
}[];
100+
deliverables?: {
101+
name: string;
102+
proof: string;
103+
description: string;
104+
}[];
105+
type: "project-update";
106+
};
107+
}
108+
109+
export interface IGapProjectIndicatorData {
110+
id: string;
111+
name: string;
112+
description: string;
113+
unitOfMeasure: string;
114+
createdAt?: string;
115+
updatedAt?: string;
116+
programs: {
117+
programId: string;
118+
chainID: number;
119+
}[];
120+
datapoints: {
121+
value: number | string;
122+
proof: string;
123+
startDate: string;
124+
endDate: string;
125+
outputTimestamp?: string;
126+
}[];
127+
hasData: boolean;
128+
isAssociatedWithPrograms: boolean;
129+
}
130+
131+
export interface IGapGrantData {
132+
uid: string;
133+
details: {
134+
data: {
135+
title: string;
136+
};
137+
};
138+
}
139+
77140
export function useGap(projectId?: string) {
78141
const [grants, setGrants] = useState<IGapGrant[]>([]);
79142
const [impacts, setImpacts] = useState<IGapImpact[]>([]);
143+
const [updates, setUpdates] = useState<IGapProjectUpdate[]>([]);
80144

81145
const getGrantsFor = async (projectRegistryId: string) => {
82146
if (!indexerUrl) throw new Error("GAP Indexer url not set.");
@@ -141,6 +205,25 @@ export function useGap(projectId?: string) {
141205
setImpacts([]);
142206
}
143207
};
208+
const getProjectUpdatesFor = async (projectRegistryId: string) => {
209+
if (!indexerUrl) throw new Error("GAP Indexer url not set.");
210+
try {
211+
const items: IGapProjectUpdate[] = await fetch(
212+
`${indexerUrl}/grants/external-id/${projectRegistryId}/updates`
213+
).then((res) => res.json());
214+
215+
if (!Array.isArray(items)) {
216+
setUpdates([]);
217+
return;
218+
}
219+
220+
setUpdates(items);
221+
} catch (e) {
222+
console.error(`No updates found for project: ${projectRegistryId}`);
223+
console.error(e);
224+
setUpdates([]);
225+
}
226+
};
144227

145228
const { isLoading: isGrantsLoading } = useSWR(
146229
`${indexerUrl}/grants/external-id/${projectId}`,
@@ -156,6 +239,13 @@ export function useGap(projectId?: string) {
156239
}
157240
);
158241

242+
const { isLoading: isUpdatesLoading } = useSWR(
243+
projectId ? `${indexerUrl}/grants/external-id/${projectId}/updates` : null,
244+
{
245+
fetcher: async () => projectId && getProjectUpdatesFor(projectId),
246+
}
247+
);
248+
159249
return {
160250
/**
161251
* Fetch GAP Indexer for grants for a project
@@ -178,7 +268,108 @@ export function useGap(projectId?: string) {
178268
/**
179269
* Loading state for grants and impacts
180270
*/
181-
isGapLoading: isGrantsLoading || isImpactsLoading,
271+
isGapLoading: isGrantsLoading || isImpactsLoading || isUpdatesLoading,
272+
/**
273+
* Updates for a project (loaded from GAP)
274+
*/
275+
updates,
276+
/**
277+
* Loading state for updates
278+
*/
279+
isUpdatesLoading,
280+
};
281+
}
282+
283+
export function useGapGrants(projectUID?: string) {
284+
const [grants, setGrants] = useState<IGapGrantData[]>([]);
285+
286+
const {
287+
data: grantsData,
288+
error: grantsError,
289+
isLoading: grantsLoading,
290+
} = useSWR(
291+
projectUID ? `${indexerUrl}/projects/${projectUID}/grants` : null,
292+
async () => {
293+
if (!indexerUrl || !projectUID) return [];
294+
295+
try {
296+
const response = await fetch(
297+
`${indexerUrl}/projects/${projectUID}/grants`
298+
);
299+
300+
if (!response.ok) {
301+
throw new Error(`Failed to fetch grants: ${response.statusText}`);
302+
}
303+
304+
const data: IGapGrantData[] = await response.json();
305+
306+
if (!Array.isArray(data)) {
307+
console.error("Unexpected grants data format", data);
308+
return [];
309+
}
310+
311+
setGrants(data);
312+
return data;
313+
} catch (e) {
314+
console.error(
315+
`Error fetching indicators for project: ${projectUID}`,
316+
e
317+
);
318+
setGrants([]);
319+
return [];
320+
}
321+
}
322+
);
323+
324+
return {
325+
grants,
326+
grantsLoading,
327+
grantsError,
328+
};
329+
}
330+
331+
export function useGapIndicators(projectUID?: string) {
332+
const [indicators, setIndicators] = useState<IGapProjectIndicatorData[]>([]);
333+
334+
const { data, error, isLoading } = useSWR(
335+
projectUID
336+
? `${indexerUrl}/projects/${projectUID}/indicators/data/all`
337+
: null,
338+
async () => {
339+
if (!indexerUrl || !projectUID) return [];
340+
341+
try {
342+
const response = await fetch(
343+
`${indexerUrl}/projects/${projectUID}/indicators/data/all`
344+
);
345+
346+
if (!response.ok) {
347+
throw new Error(`Failed to fetch indicators: ${response.statusText}`);
348+
}
349+
350+
const data: IGapProjectIndicatorData[] = await response.json();
351+
352+
if (!Array.isArray(data)) {
353+
console.error("Unexpected indicators data format", data);
354+
return [];
355+
}
356+
357+
setIndicators(data);
358+
return data;
359+
} catch (e) {
360+
console.error(
361+
`Error fetching indicators for project: ${projectUID}`,
362+
e
363+
);
364+
return [];
365+
}
366+
}
367+
);
368+
369+
return {
370+
indicators,
371+
isLoading,
372+
error,
182373
};
183374
}
184375

@@ -191,3 +382,6 @@ export const getGapProjectGrantUrl = (projectUID: string, grantUID?: string) =>
191382

192383
export const getGapProjectImpactUrl = (projectUID: string) =>
193384
`${gapAppUrl}/project/${projectUID}/impact`;
385+
386+
export const getGapProjectUrl = (projectUID: string) =>
387+
`${gapAppUrl}/project/${projectUID}`;

0 commit comments

Comments
 (0)