From 8c3c30c70743cfb89281eab332268493dc4b882f Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Wed, 29 Jun 2022 11:00:43 -0500 Subject: [PATCH] Show groups on user page, allow to join/leave (#594) * Show groups on user page, allow to join/leave * Link to groups * Unused var --- web/components/groups/group-selector.tsx | 2 +- web/components/groups/groups-button.tsx | 144 +++++++++++++++++++++++ web/components/nav/sidebar.tsx | 2 +- web/components/user-page.tsx | 2 + web/hooks/use-group.ts | 6 +- web/lib/firebase/groups.ts | 21 ++++ web/pages/groups.tsx | 15 +++ 7 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 web/components/groups/groups-button.tsx diff --git a/web/components/groups/group-selector.tsx b/web/components/groups/group-selector.tsx index 6bc943dc..ea1597f2 100644 --- a/web/components/groups/group-selector.tsx +++ b/web/components/groups/group-selector.tsx @@ -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 diff --git a/web/components/groups/groups-button.tsx b/web/components/groups/groups-button.tsx new file mode 100644 index 00000000..e6ee217d --- /dev/null +++ b/web/components/groups/groups-button.tsx @@ -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 ( + <> + setIsOpen(true)}> + {groups?.length ?? ''} Groups + + + + + ) +} + +function GroupsDialog(props: { + user: User + groups: Group[] + isOpen: boolean + setIsOpen: (isOpen: boolean) => void +}) { + const { user, groups, isOpen, setIsOpen } = props + + return ( + + +
{user.name}
+
@{user.username}
+ + +
+ ) +} + +function GroupsList(props: { groups: Group[] }) { + const { groups } = props + return ( + + {groups.length === 0 && ( +
No groups yet...
+ )} + {groups + .sort((group1, group2) => group2.createdTime - group1.createdTime) + .map((group) => ( + + ))} + + ) +} + +function GroupItem(props: { group: Group; className?: string }) { + const { group, className } = props + return ( + + + + + + + ) +} + +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
Closed
+ return ( + + ) + } + + if (isFollowing) { + return ( + + ) + } + + if (!group.anyoneCanJoin) + return
Closed
+ return ( + + ) +} diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index e5f3cd5c..0b3d9393 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -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), })) diff --git a/web/components/user-page.tsx b/web/components/user-page.tsx index 2019a9de..246ed2aa 100644 --- a/web/components/user-page.tsx +++ b/web/components/user-page.tsx @@ -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: { + {user.website && ( diff --git a/web/hooks/use-group.ts b/web/hooks/use-group.ts index f73fd04e..41f84707 100644 --- a/web/hooks/use-group.ts +++ b/web/hooks/use-group.ts @@ -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() useEffect(() => { - if (user) return listenForMemberGroups(user.id, setMemberGroups) - }, [user]) + if (userId) return listenForMemberGroups(userId, setMemberGroups) + }, [userId]) return memberGroups } diff --git a/web/lib/firebase/groups.ts b/web/lib/firebase/groups.ts index 1438dd4c..d7244f98 100644 --- a/web/lib/firebase/groups.ts +++ b/web/lib/firebase/groups.ts @@ -94,3 +94,24 @@ export async function getGroupsWithContractId( const groups = await getValues(q) setGroups(groups) } + +export async function joinGroup(group: Group, userId: string): Promise { + 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 { + 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 +} diff --git a/web/pages/groups.tsx b/web/pages/groups.tsx index c8f08b25..a8f99b23 100644 --- a/web/pages/groups.tsx +++ b/web/pages/groups.tsx @@ -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 }) { ) } + +export function GroupLink(props: { group: Group; className?: string }) { + const { group, className } = props + + return ( + + {group.name} + + ) +}