Skip to content

Commit 25974b1

Browse files
Merge pull request #3579 from gitcoinco/add-program
chore: add manageProfileMembers
2 parents 063d406 + 8d774f2 commit 25974b1

File tree

10 files changed

+501
-42
lines changed

10 files changed

+501
-42
lines changed

packages/common/src/allo/allo.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,19 @@ export interface Allo {
245245
}
246246
>;
247247

248+
manageProfileMembers: (args: {
249+
profileId: Hex;
250+
members: Address[];
251+
addOrRemove: "add" | "remove";
252+
}) => AlloOperation<
253+
Result<null>,
254+
{
255+
transaction: Result<Hex>;
256+
transactionStatus: Result<TransactionReceipt>;
257+
indexingStatus: Result<null>;
258+
}
259+
>;
260+
248261
directAllocation: (args: {
249262
tokenAddress: Address;
250263
poolId: string;

packages/common/src/allo/backends/allo-v1.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,25 @@ export class AlloV1 implements Allo {
13111311
});
13121312
}
13131313

1314+
manageProfileMembers(args: {
1315+
profileId: Hex;
1316+
members: Address[];
1317+
addOrRemove: "add" | "remove";
1318+
}): AlloOperation<
1319+
Result<null>,
1320+
{
1321+
transaction: Result<Hex>;
1322+
transactionStatus: Result<TransactionReceipt>;
1323+
indexingStatus: Result<null>;
1324+
}
1325+
> {
1326+
return new AlloOperation(async () => {
1327+
const result = new AlloError(`Unsupported on v1 ${args}`);
1328+
return error(result);
1329+
});
1330+
}
1331+
1332+
13141333
directAllocation(args: {
13151334
tokenAddress: Address;
13161335
poolId: string;

packages/common/src/allo/backends/allo-v2.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,64 @@ export class AlloV2 implements Allo {
15051505
});
15061506
}
15071507

1508+
manageProfileMembers(args: {
1509+
profileId: Hex;
1510+
members: Address[];
1511+
addOrRemove: "add" | "remove";
1512+
}): AlloOperation<
1513+
Result<null>,
1514+
{
1515+
transaction: Result<Hex>;
1516+
transactionStatus: Result<TransactionReceipt>;
1517+
indexingStatus: Result<null>;
1518+
}
1519+
> {
1520+
return new AlloOperation(async ({ emit }) => {
1521+
const txData =
1522+
args.addOrRemove === "add"
1523+
? this.registry.addMembers({
1524+
profileId: args.profileId,
1525+
members: args.members,
1526+
})
1527+
: this.registry.removeMembers({
1528+
profileId: args.profileId,
1529+
members: args.members,
1530+
});
1531+
1532+
const txResult = await sendRawTransaction(this.transactionSender, {
1533+
to: txData.to,
1534+
data: txData.data,
1535+
value: BigInt(txData.value),
1536+
});
1537+
1538+
emit("transaction", txResult);
1539+
1540+
if (txResult.type === "error") {
1541+
return error(txResult.error);
1542+
}
1543+
1544+
let receipt: TransactionReceipt;
1545+
try {
1546+
receipt = await this.transactionSender.wait(txResult.value);
1547+
emit("transactionStatus", success(receipt));
1548+
} catch (err) {
1549+
console.log(err);
1550+
const result = new AlloError(`Failed to ${args.addOrRemove} profile members`);
1551+
emit("transactionStatus", error(result));
1552+
return error(result);
1553+
}
1554+
1555+
await this.waitUntilIndexerSynced({
1556+
chainId: this.chainId,
1557+
blockNumber: receipt.blockNumber,
1558+
});
1559+
1560+
emit("indexingStatus", success(null));
1561+
1562+
return success(null);
1563+
});
1564+
}
1565+
15081566
directAllocation(args: {
15091567
tokenAddress: Address;
15101568
poolId: string;
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import React, { SetStateAction, createContext, useContext } from "react";
2+
import { ProgressStatus } from "../../features/api/types";
3+
import { Allo } from "common";
4+
import { Hex } from "viem";
5+
import { datadogLogs } from "@datadog/browser-logs";
6+
7+
type SetStatusFn = React.Dispatch<SetStateAction<ProgressStatus>>;
8+
9+
export enum AddOrRemove {
10+
ADD = "add",
11+
REMOVE = "remove",
12+
}
13+
14+
export type UpdateRolesData = {
15+
profileId: Hex;
16+
members: Hex[];
17+
addOrRemove: AddOrRemove;
18+
allo: Allo;
19+
};
20+
21+
export interface UpdateRolesState {
22+
contractUpdatingStatus: ProgressStatus;
23+
setContractUpdatingStatus: SetStatusFn;
24+
indexingStatus: ProgressStatus;
25+
setIndexingStatus: SetStatusFn;
26+
}
27+
28+
export const initialUpdateRolesState: UpdateRolesState = {
29+
contractUpdatingStatus: ProgressStatus.IN_PROGRESS,
30+
setContractUpdatingStatus: () => {
31+
/* empty */
32+
},
33+
indexingStatus: ProgressStatus.NOT_STARTED,
34+
setIndexingStatus: () => {
35+
/* empty */
36+
},
37+
};
38+
39+
export const UpdateRolesContext = createContext<UpdateRolesState>(
40+
initialUpdateRolesState
41+
);
42+
43+
export const UpdateRolesProvider = ({
44+
children,
45+
}: {
46+
children: React.ReactNode;
47+
}) => {
48+
const [contractUpdatingStatus, setContractUpdatingStatus] =
49+
React.useState<ProgressStatus>(
50+
initialUpdateRolesState.contractUpdatingStatus
51+
);
52+
53+
const [indexingStatus, setIndexingStatus] = React.useState<ProgressStatus>(
54+
initialUpdateRolesState.indexingStatus
55+
);
56+
57+
const providerProps: UpdateRolesState = {
58+
contractUpdatingStatus,
59+
setContractUpdatingStatus,
60+
indexingStatus,
61+
setIndexingStatus,
62+
};
63+
64+
return (
65+
<UpdateRolesContext.Provider value={providerProps}>
66+
{children}
67+
</UpdateRolesContext.Provider>
68+
);
69+
};
70+
71+
interface _updateRolesParams {
72+
context: UpdateRolesState;
73+
UpdateRolesData: UpdateRolesData;
74+
}
75+
76+
const _updateRoles = async ({
77+
context,
78+
UpdateRolesData,
79+
}: _updateRolesParams) => {
80+
const { profileId, members, addOrRemove, allo } = UpdateRolesData;
81+
const { setContractUpdatingStatus, setIndexingStatus } = context;
82+
83+
await allo
84+
.manageProfileMembers({
85+
profileId,
86+
members,
87+
addOrRemove,
88+
})
89+
.on("transactionStatus", (res) => {
90+
if (res.type === "success") {
91+
setContractUpdatingStatus(ProgressStatus.IS_SUCCESS);
92+
setIndexingStatus(ProgressStatus.IN_PROGRESS);
93+
} else {
94+
console.error("Transaction Status Error", res.error);
95+
datadogLogs.logger.error(`_updateRoles: ${res.error}`);
96+
setContractUpdatingStatus(ProgressStatus.IS_ERROR);
97+
}
98+
})
99+
.on("indexingStatus", (res) => {
100+
if (res.type === "success") {
101+
setIndexingStatus(ProgressStatus.IS_SUCCESS);
102+
} else {
103+
console.error("Indexing Status Error", res.error);
104+
datadogLogs.logger.error(`_updateRoles: ${res.error}`);
105+
setIndexingStatus(ProgressStatus.IS_ERROR);
106+
}
107+
})
108+
.execute();
109+
};
110+
111+
export const useUpdateRoles = () => {
112+
const context = useContext(UpdateRolesContext);
113+
if (!context) throw new Error("Missing UpdateRolesContext");
114+
115+
const { setContractUpdatingStatus, setIndexingStatus } = context;
116+
117+
const updateRoles = async (UpdateRolesData: UpdateRolesData) => {
118+
setContractUpdatingStatus(initialUpdateRolesState.contractUpdatingStatus);
119+
setIndexingStatus(initialUpdateRolesState.indexingStatus);
120+
121+
return _updateRoles({
122+
context,
123+
UpdateRolesData,
124+
});
125+
};
126+
127+
return {
128+
updateRoles,
129+
contractUpdatingStatus: context.contractUpdatingStatus,
130+
indexingStatus: context.indexingStatus,
131+
};
132+
};

packages/round-manager/src/features/common/useApplicationsByRoundId.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useAccount } from "wagmi";
2-
import { GrantApplication } from "../../features/api/types";
32
import { useDataLayer } from "data-layer";
43
import useSWR from "swr";
54
import { convertApplications } from "../api/utils";

packages/round-manager/src/features/program/TabGroup.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { Fragment, useState, Key } from "react";
44
import { classNames, getStatusStyle, prettyDates3 } from "../common/Utils";
5-
import { RefreshIcon, PlusIcon, PlusSmIcon } from "@heroicons/react/solid";
5+
import { PlusIcon, PlusSmIcon } from "@heroicons/react/solid";
66
import Close from "../../assets/close.svg";
77
import DirectGrants from "../../assets/direct-grants.svg";
88
import QuadraticFundingSVG from "../../assets/quadratic-funding.svg";
@@ -13,6 +13,7 @@ import { Link, useParams } from "react-router-dom";
1313
import { RoundCard } from "../round/RoundCard";
1414
import { datadogLogs } from "@datadog/browser-logs";
1515
import { useProgramById } from "../../context/program/ReadProgramContext";
16+
import { ViewManageProgram } from "./ViewManageProgram";
1617
import { useRounds } from "../../context/round/RoundContext";
1718
import { ProgressStatus, Round } from "../api/types";
1819
import { Transition, Dialog } from "@headlessui/react";
@@ -21,6 +22,7 @@ import { useAccount } from "wagmi";
2122
const tabs = [
2223
{ name: "Quadratic funding", current: true },
2324
{ name: "Direct grants", current: false },
25+
{ name: "Settings", current: false},
2426
];
2527

2628
export const TabGroup = () => {
@@ -31,7 +33,7 @@ export const TabGroup = () => {
3133
chainId?: string;
3234
id: string;
3335
};
34-
const { chain } = useAccount();
36+
const { chain, address } = useAccount();
3537
const programChainId = chainId ? Number(chainId) : chain?.id;
3638
const { program: programToRender } = useProgramById(programId);
3739
const { data: rounds, fetchRoundStatus } = useRounds(
@@ -145,10 +147,6 @@ export const TabGroup = () => {
145147
<div className="bg-[#F3F3F5] p-8 rounded">
146148
<div className="px-12 ml-10 mr-10">
147149
<div className="flex px-12 ml-10 mr-10 justify-center flex-col text-center">
148-
<RefreshIcon
149-
className="h-12 w-12 mt-8 mx-auto bg-white rounded-full p-3"
150-
aria-hidden="true"
151-
></RefreshIcon>
152150
<h2 className="text-2xl my-4 pt-8">My Rounds</h2>
153151
<p
154152
className="text-grey-400 text-sm mb-8"
@@ -314,13 +312,13 @@ export const TabGroup = () => {
314312
aria-current={tab.name === currentTab ? "page" : undefined}
315313
>
316314
<span>{tab.name}</span>
317-
<span
318-
className={`py-1 px-2 mx-2 bg-${tab.name === "Quadratic funding" ? "green" : "yellow"}-100 rounded-full text-xs font-mono`}
319-
>
320-
{tab.name === "Quadratic funding"
321-
? qfRounds.length
322-
: dgRounds.length}
323-
</span>
315+
{["Quadratic funding", "Direct grants"].includes(tab.name) &&
316+
<span
317+
className={`py-1 px-2 mx-2 bg-${tab.name === "Quadratic funding" ? "green" : "yellow"}-100 rounded-full text-xs font-mono`}
318+
>
319+
{tab.name === "Quadratic funding" ? qfRounds.length : dgRounds.length}
320+
</span>
321+
}
324322
</span>
325323
))}
326324
</div>
@@ -357,12 +355,17 @@ export const TabGroup = () => {
357355
<div className="md:mb-8">{dgRoundItems}</div>
358356
</div>
359357
)}
358+
{ currentTab === "Settings" && (
359+
<ViewManageProgram program={programToRender!} userAddress={address || "0x"} />
360+
)}
360361
</div>
361362
</div>
362363
{isRoundsFetched &&
363364
rounds.length === 0 &&
364365
programToRender?.tags?.includes(getAlloVersion()) &&
365-
noRoundsGroup}
366+
currentTab !== "Settings" &&
367+
noRoundsGroup
368+
}
366369
<Transition.Root show={isModalOpen} as={Fragment}>
367370
<Dialog
368371
as="div"

0 commit comments

Comments
 (0)