From a82d243a8273129e8c081fc81aa7f3b7a8acc083 Mon Sep 17 00:00:00 2001 From: BrickheadJohnny Date: Thu, 17 Oct 2024 11:11:32 +0200 Subject: [PATCH] feat(CaptchaRequirement): use Tailwind CSS and Radix UI --- .../components/CompleteCaptchaJoinStep.tsx | 10 +- .../components/RequirementAccessIndicator.tsx | 2 +- .../Captcha/CaptchaRequirement.tsx | 11 +- .../Captcha/components/CompleteCaptcha.tsx | 170 +++++++++--------- .../Captcha/hooks/useVerifyCaptcha.ts | 25 ++- 5 files changed, 116 insertions(+), 102 deletions(-) diff --git a/src/components/[guild]/JoinModal/components/CompleteCaptchaJoinStep.tsx b/src/components/[guild]/JoinModal/components/CompleteCaptchaJoinStep.tsx index d0bb192d35..b7b753aede 100644 --- a/src/components/[guild]/JoinModal/components/CompleteCaptchaJoinStep.tsx +++ b/src/components/[guild]/JoinModal/components/CompleteCaptchaJoinStep.tsx @@ -2,7 +2,7 @@ import { useWeb3ConnectionManager } from "@/components/Web3ConnectionManager/hoo import { useDisclosure } from "@/hooks/useDisclosure" import { Robot } from "@phosphor-icons/react/dist/ssr" import useSWRWithOptionalAuth from "hooks/useSWRWithOptionalAuth" -import { CompleteCaptchaModal } from "requirements/Captcha/components/CompleteCaptcha" +import { CompleteCaptchaDialog } from "requirements/Captcha/components/CompleteCaptcha" import { JoinStep } from "./JoinStep" const CompleteCaptchaJoinStep = (): JSX.Element => { @@ -14,7 +14,7 @@ const CompleteCaptchaJoinStep = (): JSX.Element => { mutate, } = useSWRWithOptionalAuth(`/v2/util/gate-proof-existence/CAPTCHA`) - const { onOpen, onClose, isOpen } = useDisclosure() + const { isOpen, onOpen, setValue } = useDisclosure() return ( <> @@ -34,9 +34,9 @@ const CompleteCaptchaJoinStep = (): JSX.Element => { }} /> - mutate(() => true, { revalidate: false })} /> diff --git a/src/components/[guild]/Requirements/components/RequirementAccessIndicator.tsx b/src/components/[guild]/Requirements/components/RequirementAccessIndicator.tsx index 4e4edeb259..d82539ccc6 100644 --- a/src/components/[guild]/Requirements/components/RequirementAccessIndicator.tsx +++ b/src/components/[guild]/Requirements/components/RequirementAccessIndicator.tsx @@ -69,7 +69,7 @@ const RequirementAccessIndicator = () => {

{type === "CAPTCHA" ? ( - + ) : type.startsWith("GITCOIN_") ? ( ) : ( diff --git a/src/requirements/Captcha/CaptchaRequirement.tsx b/src/requirements/Captcha/CaptchaRequirement.tsx index 036d09f2fd..d7cfa83b6d 100644 --- a/src/requirements/Captcha/CaptchaRequirement.tsx +++ b/src/requirements/Captcha/CaptchaRequirement.tsx @@ -1,5 +1,4 @@ -import { Icon } from "@chakra-ui/react" -import { Robot } from "@phosphor-icons/react" +import { Robot } from "@phosphor-icons/react/dist/ssr" import { Requirement, RequirementProps, @@ -17,16 +16,16 @@ const CaptchaRequirement = (props: RequirementProps): JSX.Element => { return ( } + image={} footer={} {...props} > - {"Complete a CAPTCHA"} + Complete a CAPTCHA {captchaAge && ( <> - {` (valid until `} + {" (valid until "} {captchaAge} - {`)`} + {")"} )} diff --git a/src/requirements/Captcha/components/CompleteCaptcha.tsx b/src/requirements/Captcha/components/CompleteCaptcha.tsx index 1cb3361b99..e521d6a279 100644 --- a/src/requirements/Captcha/components/CompleteCaptcha.tsx +++ b/src/requirements/Captcha/components/CompleteCaptcha.tsx @@ -1,80 +1,83 @@ +import { Alert, AlertTitle } from "@/components/ui/Alert" +import { Button, ButtonProps } from "@/components/ui/Button" import { - Box, - ButtonProps, - Center, - Code, - Icon, - ModalBody, - ModalCloseButton, - ModalContent, - ModalHeader, - ModalOverlay, - Spinner, - Text, - useDisclosure, -} from "@chakra-ui/react" + Dialog, + DialogBody, + DialogCloseButton, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/Dialog" +import { useDisclosure } from "@/hooks/useDisclosure" +import { cn } from "@/lib/utils" import HCaptcha from "@hcaptcha/react-hcaptcha" -import { Robot } from "@phosphor-icons/react" +import { CircleNotch, Robot, WarningCircle } from "@phosphor-icons/react/dist/ssr" import { useMembershipUpdate } from "components/[guild]/JoinModal/hooks/useMembershipUpdate" import { useRequirementContext } from "components/[guild]/Requirements/components/RequirementContext" import useUser from "components/[guild]/hooks/useUser" -import Button from "components/common/Button" -import ErrorAlert from "components/common/ErrorAlert" -import { Modal } from "components/common/Modal" +import { DataBlock } from "components/common/DataBlock" import { useRoleMembership } from "components/explorer/hooks/useMembership" import { useFetcherWithSign } from "hooks/useFetcherWithSign" +import { Dispatch, ReactNode, SetStateAction } from "react" import useSWRImmutable from "swr/immutable" import useVerifyCaptcha from "../hooks/useVerifyCaptcha" -const CompleteCaptcha = (props: ButtonProps): JSX.Element => { +const CompleteCaptcha = ({ className, ...props }: ButtonProps): ReactNode => { const { id: userId } = useUser() const { id, roleId } = useRequirementContext() - const { onOpen, onClose, isOpen } = useDisclosure() const { triggerMembershipUpdate } = useMembershipUpdate() const { reqAccesses } = useRoleMembership(roleId) const reqAccess = reqAccesses?.find((err) => err.requirementId === id) + const { isOpen, onOpen, setValue } = useDisclosure() + if (!userId || (!!reqAccess?.access && !reqAccess?.errorType)) return null return ( <> - triggerMembershipUpdate({ roleIds: [roleId] })} /> ) } -type Props = { - isOpen: boolean - onClose: () => void +type CompleteCaptchaDialogProps = { + open: boolean + onOpenChange: Dispatch> onComplete?: () => void - shouldTriggerMembershipUpdate?: boolean } -const CompleteCaptchaModal = ({ isOpen, onClose, onComplete }: Props) => { +const CompleteCaptchaDialog = ({ + open, + onOpenChange, + onComplete, +}: CompleteCaptchaDialogProps) => { const fetcherWithSign = useFetcherWithSign() const { data: getGateCallbackData, isValidating, error: getGateCallbackError, } = useSWRImmutable( - isOpen + open ? [`/v2/util/gate-callbacks/session?requirementType=CAPTCHA`, { body: {} }] : null, fetcherWithSign @@ -82,7 +85,7 @@ const CompleteCaptchaModal = ({ isOpen, onClose, onComplete }: Props) => { const { onSubmit, isLoading } = useVerifyCaptcha(() => { onComplete?.() - onClose() + onOpenChange(false) }) const onVerify = (token: string) => @@ -92,55 +95,58 @@ const CompleteCaptchaModal = ({ isOpen, onClose, onComplete }: Props) => { }) return ( - - - - Complete CAPTCHA - - -
- {getGateCallbackError ? ( - - ) : (!getGateCallbackData?.callbackUrl && isValidating) || isLoading ? ( - <> - - - {`${isLoading ? "Verifying" : "Generating"} CAPTCHA`} - - - ) : ( - <> - - {typeof window !== "undefined" && - window.location.hostname === "localhost" ? ( - - HCaptcha doesn't work on localhost. Please use{" "} - 127.0.0.1 instead. - - ) : ( - - )} - - - Please complete the CAPTCHA above! The modal will automatically - close on success - - - )} -
-
-
-
+ + + + Complete CAPTCHA + + + + {getGateCallbackError ? ( + + + Couldn't generate CAPTCHA + + ) : (!getGateCallbackData?.callbackUrl && isValidating) || isLoading ? ( + <> + +

+ {`${isLoading ? "Verifying" : "Generating"} CAPTCHA`} +

+ + ) : ( + <> +
+ {typeof window !== "undefined" && + window.location.hostname === "localhost" ? ( +

+ {"HCaptcha doesn't work on localhost. Please use "} + 127.0.0.1 + {" instead."} +

+ ) : ( + + )} +
+ {typeof window !== "undefined" && + window.location.hostname !== "localhost" && ( +

+ Please complete the CAPTCHA above! The modal will automatically + close on success +

+ )} + + )} +
+ + +
+
) } export default CompleteCaptcha -export { CompleteCaptchaModal } +export { CompleteCaptchaDialog } diff --git a/src/requirements/Captcha/hooks/useVerifyCaptcha.ts b/src/requirements/Captcha/hooks/useVerifyCaptcha.ts index c14dbb4dc7..87ea4bd87e 100644 --- a/src/requirements/Captcha/hooks/useVerifyCaptcha.ts +++ b/src/requirements/Captcha/hooks/useVerifyCaptcha.ts @@ -1,29 +1,38 @@ -import useShowErrorToast from "hooks/useShowErrorToast" +import { useErrorToast } from "@/components/ui/hooks/useErrorToast" +import { useToast } from "@/components/ui/hooks/useToast" import useSubmit from "hooks/useSubmit" -import useToast from "hooks/useToast" import fetcher from "utils/fetcher" -const verifyCaptcha = ({ callback, token }: { callback: string; token: string }) => - fetcher(callback, { +const verifyCaptcha = ( + { callback, token }: { callback: string; token: string } = { + callback: "", + token: "", + } +) => { + if (!callback) throw new Error("Invalid or missing callback") + if (!token) throw new Error("Invalid or missing token") + + return fetcher(callback, { body: { token, }, }) +} const useVerifyCaptcha = (onSuccess?: () => void) => { - const showErrorToast = useShowErrorToast() - const toast = useToast() + const errorToast = useErrorToast() + const { toast } = useToast() return useSubmit(verifyCaptcha, { onError: (error) => { const errorMsg = "Couldn't verify CAPTCHA" const correlationId = error.correlationId - showErrorToast(correlationId ? { error: errorMsg, correlationId } : errorMsg) + errorToast(correlationId ? { error: errorMsg, correlationId } : errorMsg) }, onSuccess: () => { onSuccess?.() toast({ - status: "success", + variant: "success", title: "Successful verification", }) },