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 (
<>
}
- iconSpacing="1"
+ leftIcon={
}
+ // TODO: extract it to a constant, just like we did with PLATFORM_COLORS
+ className={cn(
+ "bg-cyan-500 text-white hover:bg-cyan-600 active:bg-cyan-700 active:dark:bg-cyan-300 hover:dark:bg-cyan-400",
+ className
+ )}
+ onClick={() => onOpen()}
{...props}
>
Complete CAPTCHA
-
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
-
- >
- )}
-
-
-
-
+
)
}
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",
})
},