151 lines
4.0 KiB
TypeScript
151 lines
4.0 KiB
TypeScript
import clsx from 'clsx'
|
|
import { User } from 'common/user'
|
|
import { useState } from 'react'
|
|
import { useUser } from 'web/hooks/use-user'
|
|
import { withTracking } from 'web/lib/service/analytics'
|
|
import { Row } from 'web/components/layout/row'
|
|
import { useMemberGroups, useMemberIds } from 'web/hooks/use-group'
|
|
import { TextButton } from 'web/components/text-button'
|
|
import { Group } from 'common/group'
|
|
import { Modal } from 'web/components/layout/modal'
|
|
import { Col } from 'web/components/layout/col'
|
|
import { joinGroup, leaveGroup } from 'web/lib/firebase/groups'
|
|
import { firebaseLogin } from 'web/lib/firebase/users'
|
|
import { GroupLinkItem } from 'web/pages/groups'
|
|
import toast from 'react-hot-toast'
|
|
|
|
export function GroupsButton(props: { user: User }) {
|
|
const { user } = props
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const groups = useMemberGroups(user.id)
|
|
|
|
return (
|
|
<>
|
|
<TextButton onClick={() => setIsOpen(true)}>
|
|
<span className="font-semibold">{groups?.length ?? ''}</span> Groups
|
|
</TextButton>
|
|
|
|
<GroupsDialog
|
|
user={user}
|
|
groups={groups ?? []}
|
|
isOpen={isOpen}
|
|
setIsOpen={setIsOpen}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function GroupsDialog(props: {
|
|
user: User
|
|
groups: Group[]
|
|
isOpen: boolean
|
|
setIsOpen: (isOpen: boolean) => void
|
|
}) {
|
|
const { user, groups, isOpen, setIsOpen } = props
|
|
|
|
return (
|
|
<Modal open={isOpen} setOpen={setIsOpen}>
|
|
<Col className="rounded bg-white p-6">
|
|
<div className="p-2 pb-1 text-xl">{user.name}</div>
|
|
<div className="p-2 pt-0 text-sm text-gray-500">@{user.username}</div>
|
|
<GroupsList groups={groups} />
|
|
</Col>
|
|
</Modal>
|
|
)
|
|
}
|
|
|
|
function GroupsList(props: { groups: Group[] }) {
|
|
const { groups } = props
|
|
return (
|
|
<Col className="gap-2">
|
|
{groups.length === 0 && (
|
|
<div className="text-gray-500">No groups yet...</div>
|
|
)}
|
|
{groups
|
|
.sort((group1, group2) => group2.createdTime - group1.createdTime)
|
|
.map((group) => (
|
|
<GroupItem key={group.id} group={group} />
|
|
))}
|
|
</Col>
|
|
)
|
|
}
|
|
|
|
function GroupItem(props: { group: Group; className?: string }) {
|
|
const { group, className } = props
|
|
const user = useUser()
|
|
const memberIds = useMemberIds(group.id)
|
|
return (
|
|
<Row className={clsx('items-center justify-between gap-2 p-2', className)}>
|
|
<Row className="line-clamp-1 items-center gap-2">
|
|
<GroupLinkItem group={group} />
|
|
</Row>
|
|
<JoinOrLeaveGroupButton
|
|
group={group}
|
|
user={user}
|
|
isMember={user ? memberIds?.includes(user.id) : false}
|
|
/>
|
|
</Row>
|
|
)
|
|
}
|
|
|
|
export function JoinOrLeaveGroupButton(props: {
|
|
group: Group
|
|
isMember: boolean
|
|
user: User | undefined | null
|
|
small?: boolean
|
|
className?: string
|
|
}) {
|
|
const { group, small, className, isMember, user } = props
|
|
const smallStyle =
|
|
'btn !btn-xs border-2 border-gray-500 bg-white normal-case text-gray-500 hover:border-gray-500 hover:bg-white hover:text-gray-500'
|
|
|
|
if (!user) {
|
|
if (!group.anyoneCanJoin)
|
|
return <div className={clsx(className, 'text-gray-500')}>Closed</div>
|
|
return (
|
|
<button
|
|
onClick={firebaseLogin}
|
|
className={clsx('btn btn-sm', small && smallStyle, className)}
|
|
>
|
|
Login to follow
|
|
</button>
|
|
)
|
|
}
|
|
const onJoinGroup = () => {
|
|
joinGroup(group, user.id).catch(() => {
|
|
toast.error('Failed to join group')
|
|
})
|
|
}
|
|
const onLeaveGroup = () => {
|
|
leaveGroup(group, user.id).catch(() => {
|
|
toast.error('Failed to leave group')
|
|
})
|
|
}
|
|
|
|
if (isMember) {
|
|
return (
|
|
<button
|
|
className={clsx(
|
|
'btn btn-outline btn-xs',
|
|
small && smallStyle,
|
|
className
|
|
)}
|
|
onClick={withTracking(onLeaveGroup, 'leave group')}
|
|
>
|
|
Unfollow
|
|
</button>
|
|
)
|
|
}
|
|
|
|
if (!group.anyoneCanJoin)
|
|
return <div className={clsx(className, 'text-gray-500')}>Closed</div>
|
|
return (
|
|
<button
|
|
className={clsx('btn btn-sm', small && smallStyle, className)}
|
|
onClick={withTracking(onJoinGroup, 'join group')}
|
|
>
|
|
Follow
|
|
</button>
|
|
)
|
|
}
|