Skip to content

Commit f05cd14

Browse files
feat: add handleDonate to project
1 parent bed36d0 commit f05cd14

File tree

1 file changed

+157
-38
lines changed

1 file changed

+157
-38
lines changed

packages/grant-explorer/src/features/projects/ViewProject.tsx

Lines changed: 157 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
renderToHTML,
66
stringToBlobUrl,
77
TToken,
8+
useAllo,
89
useParams,
910
useValidateCredential,
1011
} from "common";
@@ -35,12 +36,17 @@ import { DefaultLayout } from "../common/DefaultLayout";
3536
import { useProject, useProjectApplications } from "./hooks/useProject";
3637
import NotFoundPage from "../common/NotFoundPage";
3738
import { useCartStorage } from "../../store";
38-
import { CartProject } from "../api/types";
39+
import { CartProject, ProgressStatus } from "../api/types";
3940
import InfoModal from "../common/InfoModal";
4041
import { Input } from "common/src/styles";
4142
import { PayoutTokenDropdown } from "../round/ViewCartPage/PayoutTokenDropdown";
4243
import { useAccount } from "wagmi";
4344
import { getVotingTokenOptions } from "../api/utils";
45+
import ErrorModal from "../common/ErrorModal";
46+
import ProgressModal, { errorModalDelayMs } from "../common/ProgressModal";
47+
import { useDirectAllocation } from "./hooks/useDirectAllocation";
48+
import { getDirectAllocationPoolId } from "common/dist/allo/backends/allo-v2";
49+
import { zeroAddress } from "viem";
4450

4551
const CalendarIcon = (props: React.SVGProps<SVGSVGElement>) => {
4652
return (
@@ -67,9 +73,9 @@ export default function ViewProject() {
6773
const { chainId } = useAccount();
6874
const [showDirectAllocationModal, setShowDirectAllocationModal] =
6975
useState<boolean>(false);
70-
const [fixedDonation, setFixedDonation] = useState<string | undefined>(
71-
undefined
72-
);
76+
const [openProgressModal, setOpenProgressModal] = useState(false);
77+
const [directDonationAmount, setDirectDonationAmount] =
78+
useState<string>("0.0");
7379

7480
const payoutTokenOptions: TToken[] = getVotingTokenOptions(
7581
Number(chainId)
@@ -82,6 +88,7 @@ export default function ViewProject() {
8288
const { projectId } = useParams();
8389

8490
const dataLayer = useDataLayer();
91+
const allo = useAllo();
8592

8693
const {
8794
data: projectData,
@@ -105,6 +112,14 @@ export default function ViewProject() {
105112
dataLayer
106113
);
107114

115+
const {
116+
directAllocation,
117+
tokenApprovalStatus,
118+
fundStatus,
119+
indexingStatus,
120+
txHash,
121+
} = useDirectAllocation();
122+
108123
const pastRroundApplications = projectApplications?.filter(
109124
(projectApplication) =>
110125
new Date(projectApplication.round.donationsEndTime) < new Date()
@@ -127,6 +142,32 @@ export default function ViewProject() {
127142
},
128143
] as BreadcrumbItem[];
129144

145+
const progressSteps = [
146+
{
147+
name: "Token Approval",
148+
description: "Approving token transfer.",
149+
status: tokenApprovalStatus,
150+
},
151+
{
152+
name: "Donating",
153+
description: "Donating to the project.",
154+
status: fundStatus,
155+
},
156+
{
157+
name: "Indexing",
158+
description: "Indexing the data.",
159+
status: indexingStatus,
160+
},
161+
{
162+
name: "Redirecting",
163+
description: "Just another moment while we finish things up.",
164+
status:
165+
indexingStatus === ProgressStatus.IS_SUCCESS
166+
? ProgressStatus.IN_PROGRESS
167+
: ProgressStatus.NOT_STARTED,
168+
},
169+
];
170+
130171
const {
131172
metadata: { title, description = "", bannerImg },
132173
} = project ?? { metadata: {} };
@@ -243,47 +284,125 @@ export default function ViewProject() {
243284
<p>Couldn't load project data.</p>
244285
)}
245286
</div>
246-
<InfoModal
247-
title="Donate!"
248-
body={
249-
<div className="flex gap-4">
250-
<p className="mt-4 md:mt-3 text-xs md:text-sm amount-text font-medium">
251-
Amount
252-
</p>
253-
<Input
254-
aria-label={"Donation amount for all projects "}
255-
id={"input-donationamount"}
256-
min="0"
257-
type="number"
258-
value={fixedDonation ?? ""}
259-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
260-
setFixedDonation(e.target.value);
261-
}}
262-
className="w-16 lg:w-18"
263-
/>
264-
<PayoutTokenDropdown
265-
selectedPayoutToken={payoutToken}
266-
setSelectedPayoutToken={(token) => {
267-
setPayoutToken(token);
268-
}}
269-
payoutTokenOptions={payoutTokenOptions}
270-
/>
271-
</div>
272-
}
273-
isOpen={showDirectAllocationModal}
274-
cancelButtonAction={() => setShowDirectAllocationModal(false)}
275-
continueButtonAction={() => setShowDirectAllocationModal(false)}
276-
// disableContinueButton={true}
277-
continueButtonText={"Donate"}
278-
setIsOpen={setShowDirectAllocationModal}
279-
/>
287+
<DirectDonationModals />
280288
</div>
281289
</DefaultLayout>
282290
) : (
283291
<NotFoundPage />
284292
)}
285293
</>
286294
);
295+
296+
function DirectDonationModals() {
297+
return (
298+
<>
299+
<InfoModal
300+
title="Donate!"
301+
body={
302+
<div className="flex gap-4">
303+
<p className="mt-4 md:mt-3 text-xs md:text-sm amount-text font-medium">
304+
Amount
305+
</p>
306+
<Input
307+
aria-label={"Donation amount for all projects "}
308+
id={"input-donationamount"}
309+
min="0"
310+
type="number"
311+
value={directDonationAmount}
312+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
313+
setDirectDonationAmount(e.target.value);
314+
}}
315+
className="w-16 lg:w-18"
316+
/>
317+
<PayoutTokenDropdown
318+
selectedPayoutToken={payoutToken}
319+
setSelectedPayoutToken={(token) => {
320+
setPayoutToken(token);
321+
}}
322+
payoutTokenOptions={payoutTokenOptions}
323+
/>
324+
</div>
325+
}
326+
isOpen={showDirectAllocationModal}
327+
cancelButtonAction={() => setShowDirectAllocationModal(false)}
328+
continueButtonAction={() => {
329+
setShowDirectAllocationModal(false);
330+
setOpenProgressModal(true);
331+
handleDonate();
332+
}}
333+
// disableContinueButton={true}
334+
continueButtonText={"Donate"}
335+
setIsOpen={setShowDirectAllocationModal}
336+
/>
337+
<ProgressModal
338+
isOpen={openProgressModal}
339+
subheading={"Please hold while we donate your funds to the project."}
340+
steps={progressSteps}
341+
/>
342+
{/* <ErrorModal
343+
isOpen={openErrorModal}
344+
setIsOpen={setOpenErrorModal}
345+
tryAgainFn={handleSubmitFund}
346+
subheading={errorModalSubHeading}
347+
/> */}
348+
</>
349+
);
350+
}
351+
352+
async function handleDonate() {
353+
if (
354+
directDonationAmount === undefined ||
355+
allo === null ||
356+
payoutToken === undefined
357+
) {
358+
return;
359+
}
360+
try {
361+
setTimeout(() => {
362+
setOpenProgressModal(true);
363+
}, errorModalDelayMs);
364+
365+
let requireTokenApproval = false;
366+
367+
const poolId = getDirectAllocationPoolId(chainId ?? 1)?.toString();
368+
369+
const recipient = project?.roles?.filter(
370+
(role) => role.role === "OWNER"
371+
)[0].address;
372+
373+
const nonce = project?.nonce;
374+
375+
if (
376+
poolId === undefined ||
377+
recipient === undefined ||
378+
nonce === undefined
379+
) {
380+
console.error("handleDonation - project", projectId, "missing data");
381+
return;
382+
}
383+
384+
if (
385+
payoutToken?.address !== undefined &&
386+
payoutToken?.address !== zeroAddress
387+
) {
388+
requireTokenApproval = true;
389+
}
390+
391+
await directAllocation({
392+
allo,
393+
poolId,
394+
fundAmount: Number(
395+
parseFloat(directDonationAmount).toFixed(payoutToken.decimals)
396+
),
397+
payoutToken,
398+
recipient,
399+
nonce,
400+
requireTokenApproval,
401+
});
402+
} catch (error) {
403+
console.error("handleDonation - project", projectId, error);
404+
}
405+
}
287406
}
288407

289408
function ProjectDetailsTabs(props: {

0 commit comments

Comments
 (0)