Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(VisitLinkRequirement): use Tailwind CSS and Radix UI #1529

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 85 additions & 96 deletions src/requirements/VisitLink/VisitLinkRequirement.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogBody,
AlertDialogCancel,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
Box,
Button,
HStack,
Icon,
Link,
Stack,
Text,
useColorModeValue,
useDisclosure,
} from "@chakra-ui/react"
import { ArrowSquareOut, Link as LinkIcon } from "@phosphor-icons/react"
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/AlertDialog"
import { Anchor } from "@/components/ui/Anchor"
import { Button, buttonVariants } from "@/components/ui/Button"
import { useErrorToast } from "@/components/ui/hooks/useErrorToast"
import { ArrowSquareOut, Link } from "@phosphor-icons/react/dist/ssr"
import { useMembershipUpdate } from "components/[guild]/JoinModal/hooks/useMembershipUpdate"
import {
Requirement,
Expand All @@ -31,12 +29,9 @@ import {
} from "components/[guild]/Requirements/components/ResetRequirementButton"
import { ViewOriginalPopover } from "components/[guild]/Requirements/components/ViewOriginalPopover"
import useUser from "components/[guild]/hooks/useUser"
import { Alert } from "components/common/Modal"
import { useRoleMembership } from "components/explorer/hooks/useMembership"
import useShowErrorToast from "hooks/useShowErrorToast"
import { SignedValidation, useSubmitWithSign } from "hooks/useSubmit"
import NextLink from "next/link"
import { useRef } from "react"
import { ReactNode } from "react"
import { useFormContext } from "react-hook-form"
import fetcher from "utils/fetcher"

Expand All @@ -50,7 +45,6 @@ const visitLink = (signedValidation: SignedValidation) =>

const VisitLinkRequirement = ({ ...props }: RequirementProps) => {
const formContext = useFormContext()
const { isOpen, onOpen, onClose } = useDisclosure()

const { id: requirementId, data, roleId } = useRequirementContext()
const { id: userId } = useUser()
Expand All @@ -61,10 +55,10 @@ const VisitLinkRequirement = ({ ...props }: RequirementProps) => {
(req) => req.requirementId === requirementId
)?.access

const showErrorToast = useShowErrorToast()
const errorToast = useErrorToast()
const { onSubmit } = useSubmitWithSign(visitLink, {
onSuccess: () => triggerMembershipUpdate({ roleIds: [roleId] }),
onError: () => showErrorToast("Something went wrong"),
onError: () => errorToast("Something went wrong"),
})

const isCustomName =
Expand All @@ -89,125 +83,120 @@ const VisitLinkRequirement = ({ ...props }: RequirementProps) => {
return (
<>
{"Visit link: "}
<Link
<Anchor
href={data.id}
isExternal
colorScheme="blue"
wordBreak={wordBreak}
variant="highlighted"
showExternal
target="_blank"
onClick={onVisit}
>
{data.id}
</Link>
</Anchor>
</>
)
}

return (
<Requirement
image={<Icon as={LinkIcon} boxSize={6} />}
image={<Link weight="bold" className="size-6" />}
{...props}
showViewOriginal={false}
footer={
(isCustomName || !!data?.customImage) && (
<ViewOriginalPopover>
<HStack gap={4}>
<div className="flex items-center gap-4">
<RequirementImageCircle>
<RequirementImage image={<Icon as={LinkIcon} boxSize={6} />} />
<RequirementImage
image={<Link weight="bold" className="size-6" />}
/>
</RequirementImageCircle>
<Stack
direction={{ base: "column", md: "row" }}
alignItems={{ base: "flex-start", md: "center" }}
spacing={{ base: 2, md: 5 }}
>
<Text wordBreak="break-word" flexGrow={1}>
<div className="flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:gap-4">
<p className="break-words">
<Original />
</Text>
</p>
{/* We only need to show it in the edit drawer, hence the formContext check */}
{!!formContext && <ResetRequirementButton />}
</Stack>
</HStack>
</div>
</div>
</ViewOriginalPopover>
)
}
>
{isCustomName ? (
<Text as="span">
{first}
<Button
variant="link"
fontWeight="medium"
colorScheme="blue"
wordBreak="break-all"
whiteSpace="normal"
textAlign="start"
onClick={onOpen}
>
{link}
</Button>

{second}
</Text>
<>
<span>{first}</span>
<LeaveGuildToExternalLinkAlert
trigger={
<Button
variant="unstyled"
className="h-auto break-words p-0 text-anchor-foreground underline-offset-2 hover:underline"
>
{link}
</Button>
}
url={data.id}
onVisit={onVisit}
/>
<span>{second}</span>
</>
) : (
<Original />
)}

<LeaveGuildToExternalLinkAlert
{...{ isOpen, onClose, onVisit }}
url={data.id}
/>
</Requirement>
)
}

const LeaveGuildToExternalLinkAlert = ({ isOpen, onClose, onVisit, url }) => {
const cancelRef = useRef(null)
const bg = useColorModeValue("gray.50", "blackAlpha.50")

const LeaveGuildToExternalLinkAlert = ({
trigger,
onVisit,
url,
}: {
trigger: ReactNode
onVisit: () => void
url: string
}) => {
const urlObj = new URL(url)
const urlArray = url.split(urlObj.hostname)

return (
<Alert {...{ isOpen, onClose }} leastDestructiveRef={cancelRef}>
<AlertDialogOverlay />
<AlertDialog>
<AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>Leaving Guild</AlertDialogHeader>
<AlertDialogBody pt="0">
<Text mb="4">
<AlertDialogHeader>
<AlertDialogTitle>Leaving Guild</AlertDialogTitle>
</AlertDialogHeader>

<AlertDialogBody className="gap-4">
<p>
This link is taking you to the following website. Please confirm it's
safe before continuing!
</Text>
<Box p="4" borderRadius="xl" borderWidth="1px" bg={bg}>
<Text fontWeight={"medium"}>
<Text as="span" colorScheme="gray">
{urlArray[0]}
</Text>
<Text as="span">{urlObj.hostname}</Text>
<Text as="span" colorScheme="gray">
{urlArray[1]}
</Text>
</Text>
</Box>
</p>

<div className="rounded-xl border border-border bg-blackAlpha-soft p-4">
<p className="font-medium">
<span className="text-muted-foreground">{urlArray[0]}</span>
<span>{urlObj.hostname}</span>
<span className="text-muted-foreground">{urlArray[1]}</span>
</p>
</div>
</AlertDialogBody>
<AlertDialogFooter display={"flex"} gap={2}>
<Button ref={cancelRef} onClick={onClose} variant="ghost">
Cancel
</Button>
<Button
as={NextLink}
href={url}
target="_blank"
onClick={() => {
onVisit()
onClose()
}}
colorScheme="indigo"
rightIcon={<ArrowSquareOut />}
>
Visit Site
</Button>

<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction asChild>
<a
href={url}
target="_blank"
onClick={() => onVisit()}
className={buttonVariants({ colorScheme: "primary" })}
>
<span>Visit Site</span>
<ArrowSquareOut weight="bold" />
</a>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</Alert>
</AlertDialog>
)
}

Expand Down
12 changes: 12 additions & 0 deletions src/v2/components/ui/AlertDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ const AlertDialogDescription = forwardRef<
))
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName

const AlertDialogBody = ({
className,
...props
}: HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("flex flex-col overflow-visible pb-10 has-[~div]:pb-0", className)}
{...props}
/>
)
AlertDialogBody.displayName = "AlertDialogBody"

const AlertDialogAction = forwardRef<
ElementRef<typeof AlertDialogPrimitive.Action>,
ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
Expand Down Expand Up @@ -130,6 +141,7 @@ export {
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogBody,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
Expand Down
Loading