Add user's collated comments onto profile

This commit is contained in:
Ian Philips 2022-05-05 16:25:54 -04:00
parent a97ca900c4
commit ce62edbc8c
3 changed files with 168 additions and 72 deletions

View File

@ -0,0 +1,69 @@
import { Comment } from '../../common/comment'
import { Contract } from '../../common/contract'
import { contractPath } from '../lib/firebase/contracts'
import { SiteLink } from './site-link'
import { Row } from './layout/row'
import { Avatar } from './avatar'
import { RelativeTimestamp } from './relative-timestamp'
import { UserLink } from './user-page'
import { User } from '../../common/user'
import _, { Dictionary } from 'lodash'
import { Col } from './layout/col'
export function UserCommentsList(props: {
user: User
commentsByContractId: Dictionary<Comment[]>
uniqueContracts: (Contract | undefined)[]
}) {
const { commentsByContractId, uniqueContracts } = props
return (
<Col className={'bg-white'}>
{uniqueContracts.map(
(contract) =>
contract && (
<div key={contract.id} className={'border-width-1 border-b p-5'}>
<div className={'mb-2 text-sm text-indigo-700'}>
<SiteLink href={contract ? contractPath(contract) : ''}>
{contract ? contract.question : '...'}
</SiteLink>
</div>
{commentsByContractId[contract.id].map((comment) => (
<div key={comment.id} className={'relative pb-6'}>
<div className="relative flex items-start space-x-3">
<ProfileComment comment={comment} />
</div>
</div>
))}
</div>
)
)}
</Col>
)
}
function ProfileComment(props: { comment: Comment }) {
const { comment } = props
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
// TODO: find and attach relevant bets by comment betId at some point
return (
<div>
<Row className={'gap-4'}>
<Avatar username={userUsername} avatarUrl={userAvatarUrl} />
<div className="min-w-0 flex-1">
<div>
<p className="mt-0.5 text-sm text-gray-500">
<UserLink
className="text-gray-500"
username={userUsername}
name={userName}
/>{' '}
<RelativeTimestamp time={createdTime} />
</p>
</div>
{text}
</div>
</Row>
</div>
)
}

View File

@ -13,12 +13,14 @@ import { LinkIcon } from '@heroicons/react/solid'
import { genHash } from '../../common/util/random'
import { PencilIcon } from '@heroicons/react/outline'
import { Tabs } from './layout/tabs'
import React, { useEffect, useState } from 'react'
import { Comment } from '../../common/comment'
import { getUsersComments } from '../lib/firebase/comments'
import { Bet } from '../../common/bet'
import { formatMoney } from '../../common/util/format'
import { RelativeTimestamp } from './relative-timestamp'
import { UserCommentsList } from './comments-list'
import { useEffect, useState } from 'react'
import { Comment, getUsersComments } from '../lib/firebase/comments'
import { Contract } from '../../common/contract'
import { getContractFromId, listContracts } from '../lib/firebase/contracts'
import { LoadingIndicator } from './loading-indicator'
import { useRouter } from 'next/router'
import _ from 'lodash'
export function UserLink(props: {
name: string
@ -36,24 +38,43 @@ export function UserLink(props: {
)
}
export function UserPage(props: { user: User; currentUser?: User }) {
const { user, currentUser } = props
export function UserPage(props: {
user: User
currentUser?: User
defaultTabIndex?: number
}) {
const router = useRouter()
const { user, currentUser, defaultTabIndex } = props
const isCurrentUser = user.id === currentUser?.id
const bannerUrl = user.bannerUrl ?? defaultBannerUrl(user.id)
const [comments, setComments] = useState<Comment[]>([] as Comment[])
const [usersComments, setUsersComments] = useState<Comment[]>([] as Comment[])
const [usersContracts, setUsersContracts] = useState<Contract[] | 'loading'>(
'loading'
)
const [uniqueContracts, setUniqueContracts] = useState<
(Contract | undefined)[] | 'loading'
>('loading')
useEffect(() => {
if (user) {
getUsersComments(user.id).then(setComments)
getUsersComments(user.id).then(setUsersComments)
listContracts(user.id).then(setUsersContracts)
}
}, [user])
const items = comments
.sort((a, b) => b.createdTime - a.createdTime)
.map((comment) => ({
comment,
bet: undefined,
}))
useEffect(() => {
// get all unique contracts for the comments and group each comments array to a contract
if (usersComments) {
const uniqueContractIds = _.uniq(
usersComments.map((comment) => comment.contractId)
)
const uniqueContracts = Array.from(uniqueContractIds).map((id) =>
getContractFromId(id)
)
Promise.all(uniqueContracts).then(setUniqueContracts)
}
}, [usersComments])
return (
<Page>
<SEO
@ -158,28 +179,63 @@ export function UserPage(props: { user: User; currentUser?: User }) {
</Col>
<Spacer h={10} />
{usersContracts !== 'loading' && uniqueContracts != 'loading' ? (
<Tabs
className={'pb-2 pt-1 '}
defaultIndex={defaultTabIndex}
onClick={(tabName) =>
router.push(
{
pathname: `/${user.username}`,
query: { tab: tabName },
},
undefined,
{ shallow: true }
)
}
tabs={[
{
title: 'Markets',
content: <CreatorContractsList creator={user} />,
content: <CreatorContractsList contracts={usersContracts} />,
tabIcon: (
<div
className={clsx(
usersContracts.length > 9 ? 'px-1' : 'px-1.5',
'items-center rounded-full border-2 border-current py-0.5 text-xs'
)}
>
{usersContracts.length}
</div>
),
},
{
title: 'Comments',
content: (
<>
{items.map((item, activityItemIdx) => (
<div key={item.comment.id} className={'relative pb-6'}>
<div className="relative flex items-start space-x-3">
<ProfileComment comment={item.comment} bet={item.bet} />
<UserCommentsList
user={user}
commentsByContractId={_.groupBy(
usersComments,
(comment) => comment.contractId
)}
uniqueContracts={uniqueContracts}
/>
),
tabIcon: (
<div
className={clsx(
usersComments.length > 9 ? 'px-1' : 'px-1.5',
'items-center rounded-full border-2 border-current py-0.5 text-xs'
)}
>
{usersComments.length}
</div>
</div>
))}
</>
),
},
]}
/>
) : (
<LoadingIndicator />
)}
</Col>
</Page>
)
@ -197,35 +253,3 @@ export function defaultBannerUrl(userId: string) {
]
return defaultBanner[genHash(userId)() % defaultBanner.length]
}
function ProfileComment(props: { comment: Comment; bet: Bet | undefined }) {
const { comment, bet } = props
let money: string | undefined
let outcome: string | undefined
let bought: string | undefined
if (bet) {
outcome = bet.outcome
bought = bet.amount >= 0 ? 'bought' : 'sold'
money = formatMoney(Math.abs(bet.amount))
}
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
return (
<>
<Avatar username={userUsername} avatarUrl={userAvatarUrl} />
<div className="min-w-0 flex-1">
<div>
<p className="mt-0.5 text-sm text-gray-500">
<UserLink
className="text-gray-500"
username={userUsername}
name={userName}
/>{' '}
<RelativeTimestamp time={createdTime} />
</p>
</div>
{text}
</div>
</>
)
}

View File

@ -126,10 +126,13 @@ export async function getDailyComments(
return commentsByDay
}
// TODO: add firebase index for comments - userid
export async function getUsersComments(userId: string) {
const getUsersCommentsQuery = (userId: string) =>
query(collectionGroup(db, 'comments'), where('userId', '==', userId))
query(
collectionGroup(db, 'comments'),
where('userId', '==', userId),
orderBy('createdTime', 'desc')
)
const comments = await getValues<Comment>(getUsersCommentsQuery(userId))
return comments
}