Skip to content

Commit

Permalink
chore: migrate the MemberCount component to Tailwind CSS & Radix UI (
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickheadJohnny authored Oct 2, 2024
1 parent f18491a commit 22e60c9
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 140 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HStack, Heading, Icon, Spacer } from "@chakra-ui/react"
import { DotsSixVertical } from "@phosphor-icons/react"
import MemberCount from "components/[guild]/RoleCard/components/MemberCount"
import { MemberCount } from "components/[guild]/RoleCard/components/MemberCount"
import Visibility from "components/[guild]/Visibility"
import Card from "components/common/Card"
import GuildLogo from "components/common/GuildLogo"
Expand Down Expand Up @@ -29,7 +29,10 @@ const DraggableRoleCard = ({ role }: Props) => {
{role.name}
</Heading>

<MemberCount memberCount={role.members?.length ?? role.memberCount} />
<MemberCount
memberCount={role.members?.length ?? role.memberCount}
className="!bg-transparent text-muted-foreground"
/>
<Visibility
ml={-2}
mt={0.5}
Expand Down
12 changes: 7 additions & 5 deletions src/components/[guild]/GuildPageImageAndName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { LayoutTitle } from "@/components/Layout"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/Avatar"
import { Skeleton } from "@/components/ui/Skeleton"
import { cn } from "@/lib/utils"
import MemberCount from "./RoleCard/components/MemberCount"
import { MemberCount } from "./RoleCard/components/MemberCount"
import { useThemeContext } from "./ThemeContext"
import useGuild from "./hooks/useGuild"

const GuildPageImageAndName = () => {
const { imageUrl, name, tags, memberCount } = useGuild()
const { avatarBg, textColor, buttonColorScheme } = useThemeContext()
const { avatarBg, buttonColorSchemeClassName } = useThemeContext()

return (
<>
Expand All @@ -33,9 +33,11 @@ const GuildPageImageAndName = () => {

<MemberCount
memberCount={memberCount ?? 0}
bgColor={`${buttonColorScheme}.200`}
color={textColor}
maxW="max-content"
className={cn(
buttonColorSchemeClassName,
// TODO: we're essentially resetting bg color in hover/active states, this looks quite hacky, so we should find a better solution here
"max-w-max hover:bg-[''] active:bg-['']"
)}
/>
</div>
</>
Expand Down
5 changes: 4 additions & 1 deletion src/components/[guild]/RecheckAccessesAndLeaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ const RecheckAccessesAndLeaveButton = () => {
<ButtonGroup isAttached>
<TopRecheckAccessesButton
// TODO: find a better solution for this once we migrate the whole guild page to Tailwind
className={cn(buttonColorSchemeClassName, "h-11 rounded-r-none")}
className={cn(
buttonColorSchemeClassName,
"h-11 rounded-r-none text-banner-foreground"
)}
/>
<Divider orientation="vertical" h="var(--chakra-sizes-11)" />
<LeaveButton {...chakraButtonProps} />
Expand Down
144 changes: 61 additions & 83 deletions src/components/[guild]/RoleCard/components/MemberCount.tsx
Original file line number Diff line number Diff line change
@@ -1,123 +1,103 @@
import { Badge, BadgeProps } from "@/components/ui/Badge"
import { ProgressIndicator, ProgressRoot } from "@/components/ui/Progress"
import {
HStack,
Popover,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
Portal,
Progress,
Spinner,
Tag,
TagLabel,
TagLeftIcon,
TagProps,
TagRightIcon,
Text,
} from "@chakra-ui/react"
import { Users } from "@phosphor-icons/react"
Tooltip,
TooltipContent,
TooltipPortal,
TooltipTrigger,
} from "@/components/ui/Tooltip"
import { cn } from "@/lib/utils"
import { CircleNotch, Users } from "@phosphor-icons/react/dist/ssr"
import useGuild from "components/[guild]/hooks/useGuild"
import useGuildPermission from "components/[guild]/hooks/useGuildPermission"
import useUser from "components/[guild]/hooks/useUser"
import useActiveStatusUpdates from "hooks/useActiveStatusUpdates"
import { PropsWithChildren } from "react"
import MemberCountLastSyncTooltip, {
import {
MemberCountLastSyncTooltip,
SyncRoleButton,
} from "./MemberCountLastSyncTooltip"

type Props = {
memberCount: number
size?: "sm" | "md"
} & TagProps
} & BadgeProps

const MemberCount = ({
memberCount,
size = "md",
children,
...rest
className,
...badgeProps
}: PropsWithChildren<Props>) => {
const iconSize = size === "sm" ? "14px" : "16px"

return (
<Tag
bg="unset"
color="gray"
mt="3px !important"
flexShrink={0}
size={size}
{...rest}
>
<TagLeftIcon as={Users} boxSize={iconSize} mr="1.5" />
<TagLabel mb="-1px">
<Badge className={cn("group mt-1 shrink-0", className)} {...badgeProps}>
<Users weight="bold" />
<span>
{new Intl.NumberFormat("en", { notation: "compact" }).format(
memberCount ?? 0
)}
</TagLabel>
</span>
{children}
</Tag>
</Badge>
)
}

type WithSyncProps = Props & {
roleId?: number
}

export const MemberCountWithSyncIndicator = ({
const MemberCountWithSyncIndicator = ({
roleId,
...rest
}: PropsWithChildren<WithSyncProps>) => {
const { status, data } = useActiveStatusUpdates(roleId)

if (status === "STARTED")
return (
<Popover trigger="hover" placement="bottom" isLazy>
<PopoverTrigger>
<MemberCount colorScheme="blue" mt="2px !important" {...rest}>
<TagRightIcon as={Spinner} />
<Tooltip>
<TooltipTrigger>
<MemberCount {...rest}>
<CircleNotch weight="bold" className="animate-spin" />
</MemberCount>
</PopoverTrigger>
<Portal>
<PopoverContent>
<PopoverArrow />
<PopoverHeader fontWeight="semibold" border={0} px={3} pb={0}>
Syncing members
</PopoverHeader>
<PopoverBody pt="1">
Updating all member accesses. This can take a few hours, feel free to
come back later!
<StatusProgress {...{ data, status }} />
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent variant="popover" side="bottom" className="text-left">
<div className="mb-1 font-semibold">Syncing members</div>
<div>
<p>
Updating all member accesses. This can take a few hours, feel free to
come back later!
</p>
<StatusProgress data={data} status={status} />
</div>
</TooltipContent>
</TooltipPortal>
</Tooltip>
)

return <MemberCount mt="3px !important" {...rest} />
return <MemberCount {...rest} />
}

const StatusProgress = ({ data, status }) => {
const percentage = (data.doneChunks / data.totalChunks) * 100
const StatusProgress = ({
data,
status,
}: Pick<ReturnType<typeof useActiveStatusUpdates>, "data" | "status">) => {
const percentage = !!data ? data.doneChunks / data.totalChunks : 0

return (
<HStack mt="3" mb="1">
<Progress
value={status === null ? 100 : percentage || 1}
colorScheme="blue"
size="sm"
borderRadius={"full"}
flex={1}
transition="width .3s"
/>
<Text colorScheme="gray" fontSize="sm" fontWeight={"bold"} flexShrink={0}>
<div className="mt-3 mb-1 flex items-center gap-1">
<ProgressRoot>
<ProgressIndicator value={status === null ? 1 : percentage || 0.01} />
</ProgressRoot>
<span className="shrink-0 font-bold text-muted-foreground text-sm">
{percentage.toFixed(0)}%
</Text>
</HStack>
</span>
</div>
)
}

export const RoleCardMemberCount = ({
const RoleCardMemberCount = ({
memberCount,
roleId,
lastSyncedAt,
Expand All @@ -127,21 +107,19 @@ export const RoleCardMemberCount = ({
const { isSuperAdmin } = useUser()

return (
<MemberCountWithSyncIndicator memberCount={memberCount} roleId={roleId}>
<MemberCountWithSyncIndicator
memberCount={memberCount}
roleId={roleId}
className="!bg-transparent text-muted-foreground"
>
{isSuperAdmin ? (
<MemberCountLastSyncTooltip lastSyncedAt={lastSyncedAt}>
<PopoverFooter
pt={0.5}
pb={3}
display="flex"
justifyContent={"flex-end"}
border={0}
>
<div className="mt-2 flex justify-end">
<SyncRoleButton roleId={roleId} />
</PopoverFooter>
</div>
</MemberCountLastSyncTooltip>
) : isAdmin &&
featureFlags.includes("PERIODIC_SYNC") &&
featureFlags?.includes("PERIODIC_SYNC") &&
/* temporarily only showing for superAdmins when lastSyncedAt is null, until we know what to communicate to admins in this case */
lastSyncedAt ? (
<MemberCountLastSyncTooltip lastSyncedAt={lastSyncedAt} />
Expand All @@ -150,4 +128,4 @@ export const RoleCardMemberCount = ({
)
}

export default MemberCount
export { MemberCount, MemberCountWithSyncIndicator, RoleCardMemberCount }
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { Button } from "@/components/ui/Button"
import {
Popover,
PopoverArrow,
PopoverContent,
PopoverHeader,
PopoverTrigger,
Portal,
TagRightIcon,
} from "@chakra-ui/react"
import { Info, UserSwitch } from "@phosphor-icons/react"
import Button from "components/common/Button"
import useShowErrorToast from "hooks/useShowErrorToast"
Tooltip,
TooltipContent,
TooltipPortal,
TooltipTrigger,
} from "@/components/ui/Tooltip"
import { useErrorToast } from "@/components/ui/hooks/useErrorToast"
import { useToast } from "@/components/ui/hooks/useToast"
import { Info, UserSwitch } from "@phosphor-icons/react/dist/ssr"
import useSubmit from "hooks/useSubmit"
import useToast from "hooks/useToast"
import { PropsWithChildren, useMemo } from "react"
import fetcher from "utils/fetcher"
import formatRelativeTimeFromNow, {
Expand Down Expand Up @@ -44,35 +41,30 @@ const MemberCountLastSyncTooltip = ({
}, [lastSyncedAt])

return (
<Popover trigger="hover" placement="bottom" isLazy>
<PopoverTrigger>
<TagRightIcon
as={Info}
opacity={0}
_groupHover={{ opacity: 1 }}
transition={"opacity .2s"}
mt="1px"
<Tooltip>
<TooltipTrigger className="group/trigger">
<Info
weight="bold"
className="opacity-0 transition-opacity group-hover:opacity-100 group-[&:not([data-state=closed])]/trigger:opacity-100"
/>
</PopoverTrigger>
<Portal>
<PopoverContent minW="max-content">
<PopoverArrow />
<PopoverHeader
border="0"
px={3}
fontSize={"sm"}
fontWeight={"medium"}
>{`Last updated all member accesses ${readableDate} ago`}</PopoverHeader>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent
variant="popover"
side="bottom"
className="min-w-max text-left"
>
<p className="font-medium text-sm">{`Last updated all member accesses ${readableDate} ago`}</p>
{children}
</PopoverContent>
</Portal>
</Popover>
</TooltipContent>
</TooltipPortal>
</Tooltip>
)
}

export const SyncRoleButton = ({ roleId }) => {
const toast = useToast()
const showErrorToast = useShowErrorToast()
const { toast } = useToast()
const errorToast = useErrorToast()

const submit = () =>
// TODO: use fetcherWithSign (with params in array) when the BE will add auth back
Expand All @@ -81,22 +73,18 @@ export const SyncRoleButton = ({ roleId }) => {
const { onSubmit, isLoading } = useSubmit(submit, {
onSuccess: () => {
toast({
status: "success",
variant: "success",
title: "Successfully moved job to the start of the queue",
})
},
onError: (err) => {
showErrorToast(err)
},
onError: (err) => errorToast(err),
})

return (
<Button
size="sm"
variant="outline"
leftIcon={<UserSwitch />}
borderRadius="lg"
borderWidth="1.5px"
leftIcon={<UserSwitch weight="bold" />}
onClick={onSubmit}
isLoading={isLoading}
>
Expand All @@ -105,4 +93,4 @@ export const SyncRoleButton = ({ roleId }) => {
)
}

export default MemberCountLastSyncTooltip
export { MemberCountLastSyncTooltip }
Loading

0 comments on commit 22e60c9

Please sign in to comment.