Show groups on user page, allow to join/leave (#594)
* Show groups on user page, allow to join/leave * Link to groups * Unused var
This commit is contained in:
parent
63528aa0f3
commit
8c3c30c707
|
@ -22,7 +22,7 @@ export function GroupSelector(props: {
|
|||
const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false)
|
||||
|
||||
const [query, setQuery] = useState('')
|
||||
const memberGroups = useMemberGroups(creator)
|
||||
const memberGroups = useMemberGroups(creator?.id)
|
||||
const filteredGroups = memberGroups
|
||||
? query === ''
|
||||
? memberGroups
|
||||
|
|
144
web/components/groups/groups-button.tsx
Normal file
144
web/components/groups/groups-button.tsx
Normal file
|
@ -0,0 +1,144 @@
|
|||
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 } 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 { GroupLink } from 'web/pages/groups'
|
||||
|
||||
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
|
||||
return (
|
||||
<Row className={clsx('items-center justify-between gap-2 p-2', className)}>
|
||||
<Row className="line-clamp-1 items-center gap-2">
|
||||
<GroupLink group={group} />
|
||||
</Row>
|
||||
<JoinOrLeaveGroupButton group={group} />
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export function JoinOrLeaveGroupButton(props: {
|
||||
group: Group
|
||||
small?: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const { group, small, className } = props
|
||||
const currentUser = useUser()
|
||||
const isFollowing = currentUser
|
||||
? group.memberIds.includes(currentUser.id)
|
||||
: false
|
||||
const onJoinGroup = () => {
|
||||
if (!currentUser) return
|
||||
joinGroup(group, currentUser.id)
|
||||
}
|
||||
const onLeaveGroup = () => {
|
||||
if (!currentUser) return
|
||||
leaveGroup(group, currentUser.id)
|
||||
}
|
||||
|
||||
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 (!currentUser || isFollowing === undefined) {
|
||||
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 Join
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
if (isFollowing) {
|
||||
return (
|
||||
<button
|
||||
className={clsx(
|
||||
'btn btn-outline btn-sm',
|
||||
small && smallStyle,
|
||||
className
|
||||
)}
|
||||
onClick={withTracking(onLeaveGroup, 'leave group')}
|
||||
>
|
||||
Leave
|
||||
</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')}
|
||||
>
|
||||
Join
|
||||
</button>
|
||||
)
|
||||
}
|
|
@ -185,7 +185,7 @@ export default function Sidebar(props: { className?: string }) {
|
|||
const mobileNavigationOptions = !user
|
||||
? signedOutMobileNavigation
|
||||
: signedInMobileNavigation
|
||||
const memberItems = (useMemberGroups(user) ?? []).map((group: Group) => ({
|
||||
const memberItems = (useMemberGroups(user?.id) ?? []).map((group: Group) => ({
|
||||
name: group.name,
|
||||
href: groupPath(group.slug),
|
||||
}))
|
||||
|
|
|
@ -36,6 +36,7 @@ import { FollowersButton, FollowingButton } from './following-button'
|
|||
import { useFollows } from 'web/hooks/use-follows'
|
||||
import { FollowButton } from './follow-button'
|
||||
import { PortfolioMetrics } from 'common/user'
|
||||
import { GroupsButton } from 'web/components/groups/groups-button'
|
||||
|
||||
export function UserLink(props: {
|
||||
name: string
|
||||
|
@ -197,6 +198,7 @@ export function UserPage(props: {
|
|||
<Row className="gap-4">
|
||||
<FollowingButton user={user} />
|
||||
<FollowersButton user={user} />
|
||||
<GroupsButton user={user} />
|
||||
</Row>
|
||||
|
||||
{user.website && (
|
||||
|
|
|
@ -29,11 +29,11 @@ export const useGroups = () => {
|
|||
return groups
|
||||
}
|
||||
|
||||
export const useMemberGroups = (user: User | null | undefined) => {
|
||||
export const useMemberGroups = (userId: string | null | undefined) => {
|
||||
const [memberGroups, setMemberGroups] = useState<Group[] | undefined>()
|
||||
useEffect(() => {
|
||||
if (user) return listenForMemberGroups(user.id, setMemberGroups)
|
||||
}, [user])
|
||||
if (userId) return listenForMemberGroups(userId, setMemberGroups)
|
||||
}, [userId])
|
||||
return memberGroups
|
||||
}
|
||||
|
||||
|
|
|
@ -94,3 +94,24 @@ export async function getGroupsWithContractId(
|
|||
const groups = await getValues<Group>(q)
|
||||
setGroups(groups)
|
||||
}
|
||||
|
||||
export async function joinGroup(group: Group, userId: string): Promise<Group> {
|
||||
const { memberIds } = group
|
||||
if (memberIds.includes(userId)) {
|
||||
return group
|
||||
}
|
||||
const newMemberIds = [...memberIds, userId]
|
||||
const newGroup = { ...group, memberIds: newMemberIds }
|
||||
await updateGroup(newGroup, { memberIds: newMemberIds })
|
||||
return newGroup
|
||||
}
|
||||
export async function leaveGroup(group: Group, userId: string): Promise<Group> {
|
||||
const { memberIds } = group
|
||||
if (!memberIds.includes(userId)) {
|
||||
return group
|
||||
}
|
||||
const newMemberIds = memberIds.filter((id) => id !== userId)
|
||||
const newGroup = { ...group, memberIds: newMemberIds }
|
||||
await updateGroup(newGroup, { memberIds: newMemberIds })
|
||||
return newGroup
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ import { getUser, User } from 'web/lib/firebase/users'
|
|||
import { Tabs } from 'web/components/layout/tabs'
|
||||
import { GroupMembersList } from 'web/pages/group/[...slugs]'
|
||||
import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params'
|
||||
import { SiteLink } from 'web/components/site-link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export async function getStaticProps() {
|
||||
const groups = await listAllGroups().catch((_) => [])
|
||||
|
@ -202,3 +204,16 @@ export function GroupCard(props: { group: Group; creator: User | undefined }) {
|
|||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
export function GroupLink(props: { group: Group; className?: string }) {
|
||||
const { group, className } = props
|
||||
|
||||
return (
|
||||
<SiteLink
|
||||
href={groupPath(group.slug)}
|
||||
className={clsx('z-10 truncate', className)}
|
||||
>
|
||||
{group.name}
|
||||
</SiteLink>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user