From 538dd8dae054843367b9879384145687c4824282 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Tue, 2 Aug 2022 15:39:40 -0600 Subject: [PATCH] Add in group chat bubble --- web/components/groups/group-chat.tsx | 96 ++++++++++++++++++++++++++-- web/components/nav/sidebar.tsx | 22 +------ web/lib/firebase/comments.ts | 9 +++ web/pages/group/[...slugs]/index.tsx | 44 ++++++------- 4 files changed, 124 insertions(+), 47 deletions(-) diff --git a/web/components/groups/group-chat.tsx b/web/components/groups/group-chat.tsx index 2cf2d73d..a5ccbea8 100644 --- a/web/components/groups/group-chat.tsx +++ b/web/components/groups/group-chat.tsx @@ -1,6 +1,6 @@ import { Row } from 'web/components/layout/row' import { Col } from 'web/components/layout/col' -import { User } from 'common/user' +import { PrivateUser, User } from 'common/user' import React, { useEffect, memo, useState, useMemo } from 'react' import { Avatar } from 'web/components/avatar' import { Group } from 'common/group' @@ -23,6 +23,8 @@ import { Tipper } from 'web/components/tipper' import { sum } from 'lodash' import { formatMoney } from 'common/util/format' import { useWindowSize } from 'web/hooks/use-window-size' +import { useUnseenPreferredNotifications } from 'web/hooks/use-notifications' +import { ChatIcon, ChevronDownIcon } from '@heroicons/react/outline' export function GroupChat(props: { messages: Comment[] @@ -107,9 +109,7 @@ export function GroupChat(props: { // Subtract bottom bar when it's showing (less than lg screen) const bottomBarHeight = (width ?? 0) < 1024 ? 58 : 0 const remainingHeight = - (height ?? window.innerHeight) - - (containerRef?.offsetTop ?? 0) - - bottomBarHeight + (height ?? 0) - (containerRef?.offsetTop ?? 0) - bottomBarHeight return ( @@ -175,6 +175,94 @@ export function GroupChat(props: { ) } +export function GroupChatInBubble(props: { + messages: Comment[] + user: User | null | undefined + privateUser: PrivateUser | null | undefined + group: Group + tips: CommentTipMap +}) { + const { messages, user, group, tips, privateUser } = props + const [shouldShowChat, setShouldShowChat] = useState(false) + + return ( + + {shouldShowChat && ( + + )} + + + ) +} + +function GroupChatNotificationsIcon(props: { + group: Group + privateUser: PrivateUser +}) { + // const { privateUser, group } = props + // const router = useRouter() + // const preferredNotifications = useUnseenPreferredNotifications(privateUser, { + // customHref: '/group/', + // }) + // TODO: set notifications to seen when user clicks on chat + // // Set notification as seen if our current page is equal to the isSeenOnHref property + // useEffect(() => { + // const currentPageWithoutQuery = currentPage.split('?')[0] + // const currentPageGroupSlug = currentPageWithoutQuery.split('/')[2] + // preferredNotifications.forEach((notification) => { + // if ( + // notification.isSeenOnHref === currentPage || + // // Old chat style group chat notif was just /group/slug + // (notification.isSeenOnHref && + // currentPageWithoutQuery.includes(notification.isSeenOnHref)) || + // // They're on the home page, so if they've a chat notif, they're seeing the chat + // (notification.isSeenOnHref?.endsWith(GROUP_CHAT_SLUG) && + // currentPageWithoutQuery.endsWith(currentPageGroupSlug)) + // ) { + // setNotificationsAsSeen([notification]) + // } + // }) + // }, [currentPage, preferredNotifications]) + + return ( +
+ ) +} + const GroupMessage = memo(function GroupMessage_(props: { user: User | null | undefined comment: Comment diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index 581dd5fa..2e520179 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -18,7 +18,7 @@ import { ManifoldLogo } from './manifold-logo' import { MenuButton } from './menu' import { ProfileSummary } from './profile-menu' import NotificationsIcon from 'web/components/notifications-icon' -import React, { useEffect, useState } from 'react' +import React, { useState } from 'react' import { IS_PRIVATE_MANIFOLD } from 'common/envs/constants' import { CreateQuestionButton } from 'web/components/create-question-button' import { useMemberGroups } from 'web/hooks/use-group' @@ -27,7 +27,6 @@ import { trackCallback, withTracking } from 'web/lib/service/analytics' import { Group, GROUP_CHAT_SLUG } from 'common/group' import { Spacer } from '../layout/spacer' import { useUnseenPreferredNotifications } from 'web/hooks/use-notifications' -import { setNotificationsAsSeen } from 'web/pages/notifications' import { PrivateUser } from 'common/user' import { useWindowSize } from 'web/hooks/use-window-size' @@ -294,25 +293,6 @@ function GroupsList(props: { memberItems.length > 0 ? memberItems.length : undefined ) - // Set notification as seen if our current page is equal to the isSeenOnHref property - useEffect(() => { - const currentPageWithoutQuery = currentPage.split('?')[0] - const currentPageGroupSlug = currentPageWithoutQuery.split('/')[2] - preferredNotifications.forEach((notification) => { - if ( - notification.isSeenOnHref === currentPage || - // Old chat style group chat notif was just /group/slug - (notification.isSeenOnHref && - currentPageWithoutQuery.includes(notification.isSeenOnHref)) || - // They're on the home page, so if they've a chat notif, they're seeing the chat - (notification.isSeenOnHref?.endsWith(GROUP_CHAT_SLUG) && - currentPageWithoutQuery.endsWith(currentPageGroupSlug)) - ) { - setNotificationsAsSeen([notification]) - } - }) - }, [currentPage, preferredNotifications]) - const { height } = useWindowSize() const [containerRef, setContainerRef] = useState(null) const remainingHeight = diff --git a/web/lib/firebase/comments.ts b/web/lib/firebase/comments.ts index 3093f764..5775a2bb 100644 --- a/web/lib/firebase/comments.ts +++ b/web/lib/firebase/comments.ts @@ -81,6 +81,7 @@ export async function createCommentOnGroup( function getCommentsCollection(contractId: string) { return collection(db, 'contracts', contractId, 'comments') } + function getCommentsOnGroupCollection(groupId: string) { return collection(db, 'groups', groupId, 'comments') } @@ -91,6 +92,14 @@ export async function listAllComments(contractId: string) { return comments } +export async function listAllCommentsOnGroup(groupId: string) { + const comments = await getValues( + getCommentsOnGroupCollection(groupId) + ) + comments.sort((c1, c2) => c1.createdTime - c2.createdTime) + return comments +} + export function listenForCommentsOnContract( contractId: string, setComments: (comments: Comment[]) => void diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index 5c52c7dc..3903efc2 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -16,7 +16,7 @@ import { Row } from 'web/components/layout/row' import { UserLink } from 'web/components/user-page' import { firebaseLogin, getUser, User } from 'web/lib/firebase/users' import { Col } from 'web/components/layout/col' -import { useUser } from 'web/hooks/use-user' +import { usePrivateUser, useUser } from 'web/hooks/use-user' import { listMembers, useGroup, useMembers } from 'web/hooks/use-group' import { useRouter } from 'next/router' import { scoreCreators, scoreTraders } from 'common/scoring' @@ -30,7 +30,7 @@ import { fromPropz, usePropz } from 'web/hooks/use-propz' import { Tabs } from 'web/components/layout/tabs' import { CreateQuestionButton } from 'web/components/create-question-button' import React, { useState } from 'react' -import { GroupChat } from 'web/components/groups/group-chat' +import { GroupChat, GroupChatInBubble } from 'web/components/groups/group-chat' import { LoadingIndicator } from 'web/components/loading-indicator' import { Modal } from 'web/components/layout/modal' import { getSavedSort } from 'web/hooks/use-sort-and-query-params' @@ -45,11 +45,12 @@ import { SearchIcon } from '@heroicons/react/outline' import { useTipTxns } from 'web/hooks/use-tip-txns' import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button' import { searchInAny } from 'common/util/parse' -import { useWindowSize } from 'web/hooks/use-window-size' import { CopyLinkButton } from 'web/components/copy-link-button' import { ENV_CONFIG } from 'common/envs/constants' import { useSaveReferral } from 'web/hooks/use-save-referral' import { Button } from 'web/components/button' +import { listAllCommentsOnGroup } from 'web/lib/firebase/comments' +import { Comment } from 'common/comment' export const getStaticProps = fromPropz(getStaticPropz) export async function getStaticPropz(props: { params: { slugs: string[] } }) { @@ -65,6 +66,7 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { const bets = await Promise.all( contracts.map((contract: Contract) => listAllBets(contract.id)) ) + const messages = group && (await listAllCommentsOnGroup(group.id)) const creatorScores = scoreCreators(contracts) const traderScores = scoreTraders(contracts, bets) @@ -86,6 +88,7 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { topTraders, creatorScores, topCreators, + messages, }, revalidate: 60, // regenerate after a minute @@ -123,6 +126,7 @@ export default function GroupPage(props: { topTraders: User[] creatorScores: { [userId: string]: number } topCreators: User[] + messages: Comment[] }) { props = usePropz(props, getStaticPropz) ?? { group: null, @@ -132,6 +136,7 @@ export default function GroupPage(props: { topTraders: [], creatorScores: {}, topCreators: [], + messages: [], } const { creator, @@ -149,19 +154,18 @@ export default function GroupPage(props: { const group = useGroup(props.group?.id) ?? props.group const tips = useTipTxns({ groupId: group?.id }) - const messages = useCommentsOnGroup(group?.id) + const messages = useCommentsOnGroup(group?.id) ?? props.messages const user = useUser() + const privateUser = usePrivateUser(user?.id) useSaveReferral(user, { defaultReferrer: creator.username, groupId: group?.id, }) - const { width } = useWindowSize() const chatDisabled = !group || group.chatDisabled - const showChatSidebar = !chatDisabled && (width ?? 1280) >= 1280 - const showChatTab = !chatDisabled && !showChatSidebar + const showChatBubble = !chatDisabled if (group === null || !groupSubpages.includes(page) || slugs[2]) { return @@ -217,15 +221,6 @@ export default function GroupPage(props: { ) const tabs = [ - ...(!showChatTab - ? [] - : [ - { - title: 'Chat', - content: chatTab, - href: groupPath(group.slug, GROUP_CHAT_SLUG), - }, - ]), { title: 'Markets', content: questionsTab, @@ -246,17 +241,13 @@ export default function GroupPage(props: { const tabIndex = tabs.map((t) => t.title).indexOf(page ?? GROUP_CHAT_SLUG) return ( - + - +
0 ? tabIndex : 0} tabs={tabs} /> + {showChatBubble && ( + + )} ) }