Various group & mobile ux improvements
This commit is contained in:
parent
ebc4bd6bcf
commit
8ced159d9a
|
@ -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'}
|
|
@ -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>()
|
||||||
|
|
|
@ -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}` : ''}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user