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 { genHash } from '../../common/util/random'
|
||||||
import { PencilIcon } from '@heroicons/react/outline'
|
import { PencilIcon } from '@heroicons/react/outline'
|
||||||
import { Tabs } from './layout/tabs'
|
import { Tabs } from './layout/tabs'
|
||||||
import React, { useEffect, useState } from 'react'
|
import { UserCommentsList } from './comments-list'
|
||||||
import { Comment } from '../../common/comment'
|
import { useEffect, useState } from 'react'
|
||||||
import { getUsersComments } from '../lib/firebase/comments'
|
import { Comment, getUsersComments } from '../lib/firebase/comments'
|
||||||
import { Bet } from '../../common/bet'
|
import { Contract } from '../../common/contract'
|
||||||
import { formatMoney } from '../../common/util/format'
|
import { getContractFromId, listContracts } from '../lib/firebase/contracts'
|
||||||
import { RelativeTimestamp } from './relative-timestamp'
|
import { LoadingIndicator } from './loading-indicator'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export function UserLink(props: {
|
export function UserLink(props: {
|
||||||
name: string
|
name: string
|
||||||
|
@ -36,24 +38,43 @@ export function UserLink(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UserPage(props: { user: User; currentUser?: User }) {
|
export function UserPage(props: {
|
||||||
const { user, currentUser } = props
|
user: User
|
||||||
|
currentUser?: User
|
||||||
|
defaultTabIndex?: number
|
||||||
|
}) {
|
||||||
|
const router = useRouter()
|
||||||
|
const { user, currentUser, defaultTabIndex } = props
|
||||||
const isCurrentUser = user.id === currentUser?.id
|
const isCurrentUser = user.id === currentUser?.id
|
||||||
const bannerUrl = user.bannerUrl ?? defaultBannerUrl(user.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(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
getUsersComments(user.id).then(setComments)
|
getUsersComments(user.id).then(setUsersComments)
|
||||||
|
listContracts(user.id).then(setUsersContracts)
|
||||||
}
|
}
|
||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
const items = comments
|
useEffect(() => {
|
||||||
.sort((a, b) => b.createdTime - a.createdTime)
|
// get all unique contracts for the comments and group each comments array to a contract
|
||||||
.map((comment) => ({
|
if (usersComments) {
|
||||||
comment,
|
const uniqueContractIds = _.uniq(
|
||||||
bet: undefined,
|
usersComments.map((comment) => comment.contractId)
|
||||||
}))
|
)
|
||||||
|
const uniqueContracts = Array.from(uniqueContractIds).map((id) =>
|
||||||
|
getContractFromId(id)
|
||||||
|
)
|
||||||
|
Promise.all(uniqueContracts).then(setUniqueContracts)
|
||||||
|
}
|
||||||
|
}, [usersComments])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<SEO
|
<SEO
|
||||||
|
@ -158,28 +179,63 @@ export function UserPage(props: { user: User; currentUser?: User }) {
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Spacer h={10} />
|
<Spacer h={10} />
|
||||||
|
{usersContracts !== 'loading' && uniqueContracts != 'loading' ? (
|
||||||
<Tabs
|
<Tabs
|
||||||
|
className={'pb-2 pt-1 '}
|
||||||
|
defaultIndex={defaultTabIndex}
|
||||||
|
onClick={(tabName) =>
|
||||||
|
router.push(
|
||||||
|
{
|
||||||
|
pathname: `/${user.username}`,
|
||||||
|
query: { tab: tabName },
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
{ shallow: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
title: 'Markets',
|
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',
|
title: 'Comments',
|
||||||
content: (
|
content: (
|
||||||
<>
|
<UserCommentsList
|
||||||
{items.map((item, activityItemIdx) => (
|
user={user}
|
||||||
<div key={item.comment.id} className={'relative pb-6'}>
|
commentsByContractId={_.groupBy(
|
||||||
<div className="relative flex items-start space-x-3">
|
usersComments,
|
||||||
<ProfileComment comment={item.comment} bet={item.bet} />
|
(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>
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<LoadingIndicator />
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
|
@ -197,35 +253,3 @@ export function defaultBannerUrl(userId: string) {
|
||||||
]
|
]
|
||||||
return defaultBanner[genHash(userId)() % defaultBanner.length]
|
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
|
return commentsByDay
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add firebase index for comments - userid
|
|
||||||
export async function getUsersComments(userId: string) {
|
export async function getUsersComments(userId: string) {
|
||||||
const getUsersCommentsQuery = (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))
|
const comments = await getValues<Comment>(getUsersCommentsQuery(userId))
|
||||||
return comments
|
return comments
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user