Load bets and comments tabs data on user page independently

This commit is contained in:
Marshall Polaris 2022-08-12 00:59:13 -07:00
parent 79be0c555b
commit 374f5e6b2f
3 changed files with 92 additions and 123 deletions

View File

@ -1,5 +1,14 @@
import Link from 'next/link'
import { groupBy, mapValues, sortBy, partition, sumBy } from 'lodash'
import {
Dictionary,
keyBy,
groupBy,
mapValues,
sortBy,
partition,
sumBy,
uniq,
} from 'lodash'
import dayjs from 'dayjs'
import { useEffect, useMemo, useState } from 'react'
import clsx from 'clsx'
@ -19,6 +28,7 @@ import {
Contract,
contractPath,
getBinaryProbPercent,
getContractFromId,
} from 'web/lib/firebase/contracts'
import { Row } from './layout/row'
import { UserLink } from './user-page'
@ -41,10 +51,12 @@ import { trackLatency } from 'web/lib/firebase/tracking'
import { NumericContract } from 'common/contract'
import { formatNumericProbability } from 'common/pseudo-numeric'
import { useUser } from 'web/hooks/use-user'
import { useUserBets } from 'web/hooks/use-user-bets'
import { SellSharesModal } from './sell-modal'
import { useUnfilledBets } from 'web/hooks/use-bets'
import { LimitBet } from 'common/bet'
import { floatingEqual } from 'common/util/math'
import { filterDefined } from 'common/util/array'
import { Pagination } from './pagination'
import { LimitOrderTable } from './limit-bets'
@ -52,25 +64,35 @@ type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
type BetFilter = 'open' | 'limit_bet' | 'sold' | 'closed' | 'resolved' | 'all'
const CONTRACTS_PER_PAGE = 50
const JUNE_1_2022 = new Date('2022-06-01T00:00:00.000Z').valueOf()
export function BetsList(props: {
user: User
bets: Bet[] | undefined
contractsById: { [id: string]: Contract } | undefined
hideBetsBefore?: number
}) {
const { user, bets: allBets, contractsById, hideBetsBefore } = props
export function BetsList(props: { user: User }) {
const { user } = props
const signedInUser = useUser()
const isYourBets = user.id === signedInUser?.id
const hideBetsBefore = isYourBets ? 0 : JUNE_1_2022
const userBets = useUserBets(user.id, { includeRedemptions: true })
const [contractsById, setContractsById] = useState<
Dictionary<Contract> | undefined
>()
// Hide bets before 06-01-2022 if this isn't your own profile
// NOTE: This means public profits also begin on 06-01-2022 as well.
const bets = useMemo(
() => allBets?.filter((bet) => bet.createdTime >= (hideBetsBefore ?? 0)),
[allBets, hideBetsBefore]
() => userBets?.filter((bet) => bet.createdTime >= (hideBetsBefore ?? 0)),
[userBets, hideBetsBefore]
)
useEffect(() => {
if (bets) {
const contractIds = uniq(bets.map((b) => getContractFromId(b.contractId)))
Promise.all(contractIds).then((contracts) => {
setContractsById(keyBy(filterDefined(contracts), 'id'))
})
}
}, [bets])
const [sort, setSort] = useState<BetSort>('newest')
const [filter, setFilter] = useState<BetFilter>('open')
const [page, setPage] = useState(0)

View File

@ -1,6 +1,12 @@
import { useEffect, useState } from 'react'
import { Dictionary, groupBy, keyBy } from 'lodash'
import { Comment } from 'common/comment'
import { Contract } from 'common/contract'
import { filterDefined } from 'common/util/array'
import { contractPath } from 'web/lib/firebase/contracts'
import { getUsersComments } from 'web/lib/firebase/comments'
import { getContractFromId } from 'web/lib/firebase/contracts'
import { SiteLink } from './site-link'
import { Row } from './layout/row'
import { Avatar } from './avatar'
@ -8,24 +14,41 @@ import { RelativeTimestamp } from './relative-timestamp'
import { UserLink } from './user-page'
import { User } from 'common/user'
import { Col } from './layout/col'
import { groupBy } from 'lodash'
import { Content } from './editor'
import { LoadingIndicator } from './loading-indicator'
export function UserCommentsList(props: {
user: User
comments: Comment[]
contractsById: { [id: string]: Contract }
}) {
const { comments, contractsById } = props
export function UserCommentsList(props: { user: User }) {
const { user } = props
const [comments, setComments] = useState<Dictionary<Comment[]> | undefined>()
const [contracts, setContracts] = useState<Dictionary<Contract> | undefined>()
// we don't show comments in groups here atm, just comments on contracts
const contractComments = comments.filter((c) => c.contractId)
const commentsByContract = groupBy(contractComments, 'contractId')
useEffect(() => {
getUsersComments(user.id).then((cs) => {
// we don't show comments in groups here atm, just comments on contracts
const contractComments = cs.filter((c) => c.contractId)
const commentsByContractId = groupBy(contractComments, 'contractId')
setComments(commentsByContractId)
})
}, [user.id])
useEffect(() => {
if (comments) {
Promise.all(Object.keys(comments).map(getContractFromId)).then(
(contracts) => {
setContracts(keyBy(filterDefined(contracts), 'id'))
}
)
}
}, [comments])
if (comments == null || contracts == null) {
return <LoadingIndicator />
}
return (
<Col className={'bg-white'}>
{Object.entries(commentsByContract).map(([contractId, comments]) => {
const contract = contractsById[contractId]
{Object.entries(comments).map(([contractId, comments]) => {
const contract = contracts[contractId]
return (
<div key={contractId} className="border-b p-5">
<SiteLink

View File

@ -1,5 +1,4 @@
import clsx from 'clsx'
import { Dictionary, keyBy, uniq } from 'lodash'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { LinkIcon } from '@heroicons/react/solid'
@ -18,18 +17,12 @@ import { Row } from './layout/row'
import { genHash } from 'common/util/random'
import { QueryUncontrolledTabs } from './layout/tabs'
import { UserCommentsList } from './comments-list'
import { Comment, getUsersComments } from 'web/lib/firebase/comments'
import { Contract } from 'common/contract'
import { getContractFromId, listContracts } from 'web/lib/firebase/contracts'
import { LoadingIndicator } from './loading-indicator'
import { FullscreenConfetti } from 'web/components/fullscreen-confetti'
import { BetsList } from './bets-list'
import { FollowersButton, FollowingButton } from './following-button'
import { UserFollowButton } from './follow-button'
import { GroupsButton } from 'web/components/groups/groups-button'
import { PortfolioValueSection } from './portfolio/portfolio-value-section'
import { filterDefined } from 'common/util/array'
import { useUserBets } from 'web/hooks/use-user-bets'
import { ReferralsButton } from 'web/components/referrals-button'
import { formatMoney } from 'common/util/format'
import { ShareIconButton } from 'web/components/share-icon-button'
@ -56,26 +49,12 @@ export function UserLink(props: {
}
export const TAB_IDS = ['markets', 'comments', 'bets', 'groups']
const JUNE_1_2022 = new Date('2022-06-01T00:00:00.000Z').valueOf()
export function UserPage(props: { user: User; currentUser?: User }) {
const { user, currentUser } = props
const router = useRouter()
const isCurrentUser = user.id === currentUser?.id
const bannerUrl = user.bannerUrl ?? defaultBannerUrl(user.id)
const [usersComments, setUsersComments] = useState<Comment[] | undefined>()
const [usersContracts, setUsersContracts] = useState<Contract[] | 'loading'>(
'loading'
)
const userBets = useUserBets(user.id, { includeRedemptions: true })
const betCount =
userBets === undefined
? 0
: userBets.filter((bet) => !bet.isRedemption && bet.amount !== 0).length
const [contractsById, setContractsById] = useState<
Dictionary<Contract> | undefined
>()
const [showConfetti, setShowConfetti] = useState(false)
useEffect(() => {
@ -83,30 +62,6 @@ export function UserPage(props: { user: User; currentUser?: User }) {
setShowConfetti(claimedMana)
}, [router])
useEffect(() => {
if (!user) return
getUsersComments(user.id).then(setUsersComments)
listContracts(user.id).then(setUsersContracts)
}, [user])
// TODO: display comments on groups
useEffect(() => {
if (usersComments && userBets) {
const uniqueContractIds = uniq([
...usersComments.map((comment) => comment.contractId),
...(userBets?.map((bet) => bet.contractId) ?? []),
])
Promise.all(
uniqueContractIds.map((contractId) =>
contractId ? getContractFromId(contractId) : undefined
)
).then((contracts) => {
const contractsById = keyBy(filterDefined(contracts), 'id')
setContractsById(contractsById)
})
}
}, [userBets, usersComments])
const profit = user.profitCached.allTime
return (
@ -163,9 +118,7 @@ export function UserPage(props: { user: User; currentUser?: User }) {
</span>{' '}
profit
</span>
<Spacer h={4} />
{user.bio && (
<>
<div>
@ -174,7 +127,6 @@ export function UserPage(props: { user: User; currentUser?: User }) {
<Spacer h={4} />
</>
)}
<Col className="flex-wrap gap-2 sm:flex-row sm:items-center sm:gap-4">
<Row className="gap-4">
<FollowingButton user={user} />
@ -236,7 +188,6 @@ export function UserPage(props: { user: User; currentUser?: User }) {
</SiteLink>
)}
</Col>
<Spacer h={5} />
{currentUser?.id === user.id && (
<Row
@ -259,58 +210,31 @@ export function UserPage(props: { user: User; currentUser?: User }) {
</Row>
)}
<Spacer h={5} />
{usersContracts !== 'loading' && contractsById && usersComments ? (
<QueryUncontrolledTabs
currentPageForAnalytics={'profile'}
labelClassName={'pb-2 pt-1 '}
tabs={[
{
title: 'Markets',
content: (
<CreatorContractsList user={currentUser} creator={user} />
),
tabIcon: (
<span className="px-0.5 font-bold">
{usersContracts.length}
</span>
),
},
{
title: 'Comments',
content: (
<UserCommentsList
user={user}
contractsById={contractsById}
comments={usersComments}
/>
),
tabIcon: (
<span className="px-0.5 font-bold">
{usersComments.length}
</span>
),
},
{
title: 'Bets',
content: (
<div>
<PortfolioValueSection userId={user.id} />
<BetsList
user={user}
bets={userBets}
hideBetsBefore={isCurrentUser ? 0 : JUNE_1_2022}
contractsById={contractsById}
/>
</div>
),
tabIcon: <span className="px-0.5 font-bold">{betCount}</span>,
},
]}
/>
) : (
<LoadingIndicator />
)}
<QueryUncontrolledTabs
currentPageForAnalytics={'profile'}
labelClassName={'pb-2 pt-1 '}
tabs={[
{
title: 'Markets',
content: (
<CreatorContractsList user={currentUser} creator={user} />
),
},
{
title: 'Comments',
content: <UserCommentsList user={user} />,
},
{
title: 'Bets',
content: (
<>
<PortfolioValueSection userId={user.id} />
<BetsList user={user} />
</>
),
},
]}
/>
</Col>
</Page>
)