Various group & mobile ux improvements

This commit is contained in:
Ian Philips 2022-06-24 12:16:37 -05:00
parent ebc4bd6bcf
commit 8ced159d9a
4 changed files with 52 additions and 22 deletions

View File

@ -1,7 +1,7 @@
import { Row } from 'web/components/layout/row' import { Row } from 'web/components/layout/row'
import { Col } from 'web/components/layout/col' import { Col } from 'web/components/layout/col'
import { User } from 'common/user' import { User } from 'common/user'
import React, { useEffect, memo, useState } from 'react' import React, { useEffect, memo, useState, useMemo } from 'react'
import { Avatar } from 'web/components/avatar' import { Avatar } from 'web/components/avatar'
import { Group } from 'common/group' import { Group } from 'common/group'
import { Comment, createCommentOnGroup } from 'web/lib/firebase/comments' import { Comment, createCommentOnGroup } from 'web/lib/firebase/comments'
@ -19,7 +19,7 @@ import { UserLink } from 'web/components/user-page'
import { groupPath } from 'web/lib/firebase/groups' import { groupPath } from 'web/lib/firebase/groups'
import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time' import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time'
export function Discussion(props: { export function GroupChat(props: {
messages: Comment[] messages: Comment[]
user: User | null | undefined user: User | null | undefined
group: Group group: Group
@ -34,8 +34,30 @@ export function Discussion(props: {
useState<HTMLDivElement | null>(null) useState<HTMLDivElement | null>(null)
const [replyToUsername, setReplyToUsername] = useState('') const [replyToUsername, setReplyToUsername] = useState('')
const [inputRef, setInputRef] = useState<HTMLTextAreaElement | null>(null) const [inputRef, setInputRef] = useState<HTMLTextAreaElement | null>(null)
const [groupedMessages, setGroupedMessages] = useState<Comment[]>([])
const router = useRouter() const router = useRouter()
useMemo(() => {
// Group messages with createdTime within 2 minutes of each other.
const tempMessages = []
for (let i = 0; i < messages.length; i++) {
const message = messages[i]
if (i === 0) tempMessages.push({ ...message })
else {
const prevMessage = messages[i - 1]
const diff = message.createdTime - prevMessage.createdTime
const creatorsMatch = message.userId === prevMessage.userId
if (diff < 2 * 60 * 1000 && creatorsMatch) {
tempMessages[tempMessages.length - 1].text += `\n${message.text}`
} else {
tempMessages.push({ ...message })
}
}
}
setGroupedMessages(tempMessages)
}, [messages])
useEffect(() => { useEffect(() => {
scrollToMessageRef?.scrollIntoView() scrollToMessageRef?.scrollIntoView()
}, [scrollToMessageRef]) }, [scrollToMessageRef])
@ -78,7 +100,7 @@ export function Discussion(props: {
} }
ref={setScrollToBottomRef} ref={setScrollToBottomRef}
> >
{messages.map((message) => ( {groupedMessages.map((message) => (
<GroupMessage <GroupMessage
user={user} user={user}
key={message.id} key={message.id}
@ -142,8 +164,8 @@ const GroupMessage = memo(function GroupMessage_(props: {
<Col <Col
ref={setRef} ref={setRef}
className={clsx( className={clsx(
isCreatorsComment ? 'mr-2 self-end' : ' ml-2', isCreatorsComment ? 'mr-2 self-end' : '',
'w-fit max-w-md gap-1 space-x-3 rounded-md bg-white p-2 p-2 px-4 text-sm text-gray-500 transition-all duration-1000', 'w-fit max-w-sm gap-1 space-x-3 rounded-md bg-white p-1 text-sm text-gray-500 transition-colors duration-1000 sm:max-w-md sm:p-3 sm:leading-[1.3rem]',
highlight ? `-m-1 bg-indigo-500/[0.2] p-2` : '' highlight ? `-m-1 bg-indigo-500/[0.2] p-2` : ''
)} )}
> >
@ -151,8 +173,8 @@ const GroupMessage = memo(function GroupMessage_(props: {
{!isCreatorsComment && ( {!isCreatorsComment && (
<Col> <Col>
<Avatar <Avatar
className={'mx-2 ml-0'} className={'mx-2 ml-2.5'}
size={'sm'} size={'xs'}
username={userUsername} username={userUsername}
avatarUrl={userAvatarUrl} avatarUrl={userAvatarUrl}
/> />
@ -161,7 +183,7 @@ const GroupMessage = memo(function GroupMessage_(props: {
{!isCreatorsComment ? ( {!isCreatorsComment ? (
<UserLink username={userUsername} name={userName} /> <UserLink username={userUsername} name={userName} />
) : ( ) : (
<span>{'You'}</span> <span className={'ml-2.5'}>{'You'}</span>
)} )}
<CopyLinkDateTimeComponent <CopyLinkDateTimeComponent
prefix={'group'} prefix={'group'}

View File

@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { import {
Comment, Comment,
listenForCommentsOnContract, listenForCommentsOnContract,
listenForCommentsOnGroup,
listenForRecentComments, listenForRecentComments,
} from 'web/lib/firebase/comments' } from 'web/lib/firebase/comments'
@ -14,6 +15,15 @@ export const useComments = (contractId: string) => {
return comments return comments
} }
export const useCommentsOnGroup = (groupId: string | undefined) => {
const [comments, setComments] = useState<Comment[] | undefined>()
useEffect(() => {
if (groupId) return listenForCommentsOnGroup(groupId, setComments)
}, [groupId])
return comments
}
export const useRecentComments = () => { export const useRecentComments = () => {
const [recentComments, setRecentComments] = useState<Comment[] | undefined>() const [recentComments, setRecentComments] = useState<Comment[] | undefined>()

View File

@ -17,7 +17,7 @@ const groupCollection = collection(db, 'groups')
export function groupPath( export function groupPath(
groupSlug: string, groupSlug: string,
subpath?: 'edit' | 'questions' | 'details' | 'discussion' subpath?: 'edit' | 'questions' | 'details' | 'chat'
) { ) {
return `/group/${groupSlug}${subpath ? `/${subpath}` : ''}` return `/group/${groupSlug}${subpath ? `/${subpath}` : ''}`
} }

View File

@ -1,7 +1,6 @@
import { take, sortBy, debounce } from 'lodash' import { take, sortBy, debounce } from 'lodash'
import { Group } from 'common/group' import { Group } from 'common/group'
import { Comment } from 'common/comment'
import { Page } from 'web/components/page' import { Page } from 'web/components/page'
import { Title } from 'web/components/title' import { Title } from 'web/components/title'
import { listAllBets } from 'web/lib/firebase/bets' import { listAllBets } from 'web/lib/firebase/bets'
@ -32,14 +31,14 @@ import { Tabs } from 'web/components/layout/tabs'
import { ContractsGrid } from 'web/components/contract/contracts-list' import { ContractsGrid } from 'web/components/contract/contracts-list'
import { CreateQuestionButton } from 'web/components/create-question-button' import { CreateQuestionButton } from 'web/components/create-question-button'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { Discussion } from 'web/components/groups/discussion' import { GroupChat } from 'web/components/groups/group-chat'
import { listenForCommentsOnGroup } from 'web/lib/firebase/comments'
import { LoadingIndicator } from 'web/components/loading-indicator' import { LoadingIndicator } from 'web/components/loading-indicator'
import { Modal } from 'web/components/layout/modal' import { Modal } from 'web/components/layout/modal'
import { PlusIcon } from '@heroicons/react/outline' import { PlusIcon } from '@heroicons/react/outline'
import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params' import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params'
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group' import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
import { toast } from 'react-hot-toast' import { toast } from 'react-hot-toast'
import { useCommentsOnGroup } from 'web/hooks/use-comments'
export const getStaticProps = fromPropz(getStaticPropz) export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: { params: { slugs: string[] } }) { export async function getStaticPropz(props: { params: { slugs: string[] } }) {
@ -92,7 +91,7 @@ async function toTopUsers(userScores: { [userId: string]: number }) {
export async function getStaticPaths() { export async function getStaticPaths() {
return { paths: [], fallback: 'blocking' } return { paths: [], fallback: 'blocking' }
} }
const groupSubpages = [undefined, 'discussion', 'questions', 'details'] as const const groupSubpages = [undefined, 'chat', 'questions', 'details'] as const
export default function GroupPage(props: { export default function GroupPage(props: {
group: Group | null group: Group | null
@ -115,14 +114,11 @@ export default function GroupPage(props: {
const router = useRouter() const router = useRouter()
const { slugs } = router.query as { slugs: string[] } const { slugs } = router.query as { slugs: string[] }
const page = (slugs?.[1] ?? 'discussion') as typeof groupSubpages[number] const page = (slugs?.[1] ?? 'chat') as typeof groupSubpages[number]
const group = useGroup(props.group?.id) ?? props.group const group = useGroup(props.group?.id) ?? props.group
const [messages, setMessages] = useState<Comment[] | undefined>(undefined)
const [contracts, setContracts] = useState<Contract[] | undefined>(undefined) const [contracts, setContracts] = useState<Contract[] | undefined>(undefined)
useEffect(() => { const messages = useCommentsOnGroup(group?.id)
if (group) listenForCommentsOnGroup(group.id, setMessages)
}, [group])
useEffect(() => { useEffect(() => {
if (group) if (group)
@ -189,7 +185,9 @@ export default function GroupPage(props: {
<Row className={' items-center justify-between gap-4 '}> <Row className={' items-center justify-between gap-4 '}>
<div className={'mb-1'}> <div className={'mb-1'}>
<Title className={'line-clamp-2'} text={group.name} /> <Title className={'line-clamp-2'} text={group.name} />
<span className={'text-gray-700'}>{group.about}</span> <span className={'hidden text-gray-700 sm:block'}>
{group.about}
</span>
</div> </div>
{isMember && ( {isMember && (
<CreateQuestionButton <CreateQuestionButton
@ -209,13 +207,13 @@ export default function GroupPage(props: {
defaultIndex={page === 'details' ? 2 : page === 'questions' ? 1 : 0} defaultIndex={page === 'details' ? 2 : page === 'questions' ? 1 : 0}
tabs={[ tabs={[
{ {
title: 'Discussion', title: 'Chat',
content: messages ? ( content: messages ? (
<Discussion messages={messages} user={user} group={group} /> <GroupChat messages={messages} user={user} group={group} />
) : ( ) : (
<LoadingIndicator /> <LoadingIndicator />
), ),
href: groupPath(group.slug, 'discussion'), href: groupPath(group.slug, 'chat'),
}, },
{ {
title: 'Questions', title: 'Questions',