Skip to content

Commit

Permalink
add Modal to CheckPassport & Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucianosc committed Aug 5, 2024
1 parent 0a8e6a8 commit b7479ce
Show file tree
Hide file tree
Showing 5 changed files with 2,678 additions and 4,115 deletions.
275 changes: 101 additions & 174 deletions apps/web/components/CheckPassport.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import React, { ReactElement, useEffect, useState } from "react";
import React, { ReactElement, useCallback, useMemo, useState } from "react";
import { Address } from "viem";
import { useAccount } from "wagmi";
import {
Expand All @@ -11,15 +11,9 @@ import {
import { Button } from "./Button";
import { Modal } from "@/components";
import { isProd } from "@/constants/contracts";
import useModal from "@/hooks/useModal";
import { useSubgraphQuery } from "@/hooks/useSubgraphQuery";
import { CV_PERCENTAGE_SCALE } from "@/utils/numbers";

type SubmitPassportResponse = {
data: any;
error: boolean;
};

type CheckPassportProps = {
strategyAddr: Address;
children: ReactElement<{
Expand All @@ -28,208 +22,136 @@ type CheckPassportProps = {
enableCheck?: boolean;
};

// component should only wrap button component

export function CheckPassport({
strategyAddr,
children,
enableCheck = true,
}: CheckPassportProps) {
//force active passport for testing
if (!isProd) {
(window as any).togglePassportEnable = (enable: boolean): string => {
if (passportStrategy) {
passportStrategy.active = enable;
return "passportStrategy.active set to " + enable;
} else {
return "No passportStrategy found";
}
};
}

const { address: walletAddr } = useAccount();
const { ref, openModal, closeModal } = useModal();
const [score, setScore] = useState<number>(0);
const [threshold, setThreshold] = useState<number>(0);
const [shouldOpenModal, setShouldOpenModal] = useState(false);
const [isSubmiting, setIsSubmiting] = useState<boolean>(false);

useEffect(() => {
if (!enableCheck) {
return;
}
if (shouldOpenModal) {
openModal();
setShouldOpenModal(false);
}
}, [shouldOpenModal]);
const [isOpenModal, setIsOpenModal] = useState(false);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);

const { data: passportUserData } = useSubgraphQuery<getPassportUserQuery>({
query: getPassportUserDocument,
variables: { userId: walletAddr?.toLowerCase() },
enabled: !!walletAddr && enableCheck,
//TODO: add changeScope = passportUserData
});
const passportUser = passportUserData?.passportUser;

const { data: passportStrategyData } =
useSubgraphQuery<getPassportStrategyQuery>({
query: getPassportStrategyDocument,
variables: { strategyId: strategyAddr },
enabled: enableCheck,
//TODO: add changeScope = passport
});

const passportUser = passportUserData?.passportUser;
const passportStrategy = passportStrategyData?.passportStrategy;

if (!enableCheck) {
return <>{children}</>;
}
const isPassportRequired = useMemo(
() => !!passportStrategy && passportStrategy.active,
[passportStrategy],
);

//force active passport for testing
if (!isProd) {
(window as any).togglePassportEnable = (enable: boolean): string => {
if (passportStrategy) {
passportStrategy.active = enable;
return "passportStrategy.active set to " + enable;
} else {
return "No passportStrategy found";
}
};
}
const submitPassport = useCallback(async (address: string) => {
const response = await fetch("/api/passport/submitPassport", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address }),
});
if (!response.ok) throw new Error("Failed to submit passport");
return response.json();
}, []);

const writeScorer = useCallback(async (address: string) => {
const response = await fetch("/api/passport-oracle/writeScore", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ user: address }),
});
if (!response.ok) throw new Error("Failed to write scorer");
return response.json();
}, []);

const checkScoreRequirement = useCallback(
(userScore: number, strategyThreshold: number) => {
const normalizedScore = userScore / CV_PERCENTAGE_SCALE;
const normalizedThreshold = strategyThreshold / CV_PERCENTAGE_SCALE;
setScore(normalizedScore);
setThreshold(normalizedThreshold);
return normalizedScore > normalizedThreshold;
},
[],
);

const handleCheckPassport = (
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
) => {
if (!!passportStrategy && passportStrategy.active) {
if (walletAddr) {
checkPassportRequirements(walletAddr, e);
}
} else {
console.debug("No passport required, moving forward...");
closeModal();
}
};
const handleCheckPassport = useCallback(
async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
if (!isPassportRequired || !walletAddr) return;

const checkPassportRequirements = (
_walletAddr: Address,
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
) => {
if (passportUser) {
checkScoreRequirement(
Number(passportUser?.score) / CV_PERCENTAGE_SCALE,
passportStrategy?.threshold,
e,
);
} else {
console.debug("No passport found, Submitting passport...");
e.preventDefault();
e.stopPropagation();
submitAndWriteScorer(_walletAddr);
}
};

const checkScoreRequirement = (
_score: number | string,
_threshold: number | string,
e?: React.MouseEvent<HTMLDivElement, MouseEvent>,
) => {
_score = Number(_score);
_threshold = Number(_threshold) / CV_PERCENTAGE_SCALE;
if (score > threshold) {
console.debug("Score meets threshold, moving forward...");
setScore(score);
setThreshold(threshold);
} else {
console.debug("Score is too low, opening modal...");
e?.preventDefault();
e?.stopPropagation();
setScore(score);
setThreshold(threshold);
setShouldOpenModal(true);
}
};

const submitAndWriteScorer = async (_walletAddr: Address) => {
openModal();
setIsSubmiting(true);
try {
const passportResponse = await submitPassport(_walletAddr);
console.debug(passportResponse);
if (passportResponse?.data?.score) {
await writeScorer(_walletAddr);
}
// gitcoin passport score no need for formating
if (passportResponse?.data?.score) {
checkScoreRequirement(
passportResponse?.data?.score,
passportStrategy?.threshold,
);
}
} catch (error) {
console.error("Error submitting passport:", error);
setIsSubmiting(false);
}
setIsSubmiting(false);
};

const submitPassport = async (
address: string,
): Promise<SubmitPassportResponse> => {
const SUBMIT_SIGNED_PASSPORT_URI = "/api/passport/submitPassport";

try {
const response = await fetch(SUBMIT_SIGNED_PASSPORT_URI, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ address }),
});

if (!response.ok) {
return {
error: true,
data: response,
};
}

const data = await response.json();

return { error: false, data };
} catch (err) {
return {
error: true,
data: err,
};
}
};

const writeScorer = async (address: string): Promise<any> => {
const WRITE_SCORER_URI = "/api/passport-oracle/writeScore";
try {
const response = await fetch(WRITE_SCORER_URI, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ user: address }),
});

if (!response.ok) {
return {
error: true,
data: response,
};
setIsSubmitting(true);
try {
if (!passportUser) {
const passportResponse = await submitPassport(walletAddr);
await writeScorer(walletAddr);
if (passportResponse?.score) {
const meetsRequirement = checkScoreRequirement(
passportResponse.score,
passportStrategy?.threshold,
);
if (!meetsRequirement) setIsOpenModal(true);
}
} else {
const meetsRequirement = checkScoreRequirement(
Number(passportUser.score),
passportStrategy?.threshold,
);
if (!meetsRequirement) setIsOpenModal(true);
}
} catch (error) {
console.error("Error checking passport:", error);
} finally {
setIsSubmitting(false);
}
},
[
walletAddr,
passportUser,
passportStrategy,
isPassportRequired,
submitPassport,
writeScorer,
checkScoreRequirement,
],
);

const data = await response.json();
console.debug("Response from writeScorer API:", data);
return data;
} catch (err) {
console.error("Error calling writeScorer API:", err);
return {
error: true,
errorMessage: err,
};
}
};
if (!enableCheck) return <>{children}</>;

return (
<>
<div onClickCapture={(e) => handleCheckPassport(e)} className="w-fit">
<div onClickCapture={handleCheckPassport} className="w-fit">
{children}
</div>

<Modal title="Gitcoin passport" onClose={closeModal} ref={ref}>
<Modal
title="Gitcoin passport"
isOpen={isOpenModal}
onClose={() => setIsOpenModal(false)}
>
<div className="flex flex-col gap-8">
<div>
<p>
Expand Down Expand Up @@ -264,10 +186,15 @@ export function CheckPassport({
{score > threshold ?
children
: <Button
onClick={() => submitAndWriteScorer(walletAddr)}
onClick={() =>
handleCheckPassport({
preventDefault: () => {},
stopPropagation: () => {},
} as any)
}
className="w-fit"
btnStyle="outline"
isLoading={isSubmiting}
isLoading={isSubmitting}
>
Check again
</Button>
Expand Down
9 changes: 0 additions & 9 deletions apps/web/hooks/useModal.tsx

This file was deleted.

Loading

0 comments on commit b7479ce

Please sign in to comment.