Add user's collated comments onto profile
This commit is contained in:
parent
a97ca900c4
commit
ce62edbc8c
69
web/components/comments-list.tsx
Normal file
69
web/components/comments-list.tsx
Normal 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>
|
||||
)
|
||||
}
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user