diff --git a/web/components/contract/contract-leaderboard.tsx b/web/components/contract/contract-leaderboard.tsx
index 4d25ffa4..6cd2ae62 100644
--- a/web/components/contract/contract-leaderboard.tsx
+++ b/web/components/contract/contract-leaderboard.tsx
@@ -1,10 +1,10 @@
import { Bet } from 'common/bet'
-import { ContractComment } from 'common/comment'
import { resolvedPayout } from 'common/calculate'
import { Contract } from 'common/contract'
import { formatMoney } from 'common/util/format'
import { groupBy, mapValues, sumBy, sortBy, keyBy } from 'lodash'
import { useState, useMemo, useEffect } from 'react'
+import { useComments } from 'web/hooks/use-comments'
import { listUsers, User } from 'web/lib/firebase/users'
import { FeedBet } from '../feed/feed-bets'
import { FeedComment } from '../feed/feed-comments'
@@ -61,12 +61,10 @@ export function ContractLeaderboard(props: {
) : null
}
-export function ContractTopTrades(props: {
- contract: Contract
- bets: Bet[]
- comments: ContractComment[]
-}) {
- const { contract, bets, comments } = props
+export function ContractTopTrades(props: { contract: Contract; bets: Bet[] }) {
+ const { contract, bets } = props
+ // todo: this stuff should be calced in DB at resolve time
+ const comments = useComments(contract.id)
const commentsById = keyBy(comments, 'id')
const betsById = keyBy(bets, 'id')
diff --git a/web/components/contract/contract-tabs.tsx b/web/components/contract/contract-tabs.tsx
index 245a8d7d..008eb584 100644
--- a/web/components/contract/contract-tabs.tsx
+++ b/web/components/contract/contract-tabs.tsx
@@ -5,19 +5,19 @@ import { FeedBet } from '../feed/feed-bets'
import { FeedLiquidity } from '../feed/feed-liquidity'
import { FeedAnswerCommentGroup } from '../feed/feed-answer-comment-group'
import { FeedCommentThread, ContractCommentInput } from '../feed/feed-comments'
-import { CommentTipMap } from 'web/hooks/use-tip-txns'
import { groupBy, sortBy } from 'lodash'
import { Bet } from 'common/bet'
-import { Contract, FreeResponseContract } from 'common/contract'
-import { ContractComment } from 'common/comment'
-import { PAST_BETS, User } from 'common/user'
+import { Contract } from 'common/contract'
+import { PAST_BETS } from 'common/user'
import { ContractBetsTable, BetsSummary } from '../bets-list'
import { Spacer } from '../layout/spacer'
import { Tabs } from '../layout/tabs'
import { Col } from '../layout/col'
+import { LoadingIndicator } from 'web/components/loading-indicator'
import { useComments } from 'web/hooks/use-comments'
import { useLiquidity } from 'web/hooks/use-liquidity'
import { useTipTxns } from 'web/hooks/use-tip-txns'
+import { useUser } from 'web/hooks/use-user'
import { capitalize } from 'lodash'
import {
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
@@ -25,21 +25,13 @@ import {
} from 'common/antes'
import { useIsMobile } from 'web/hooks/use-is-mobile'
-export function ContractTabs(props: {
- contract: Contract
- user: User | null | undefined
- bets: Bet[]
- comments: ContractComment[]
-}) {
- const { contract, user, bets, comments } = props
+export function ContractTabs(props: { contract: Contract; bets: Bet[] }) {
+ const { contract, bets } = props
const isMobile = useIsMobile()
-
+ const user = useUser()
const userBets =
user && bets.filter((bet) => !bet.isAnte && bet.userId === user.id)
- const visibleBets = bets.filter(
- (bet) => !bet.isAnte && !bet.isRedemption && bet.amount !== 0
- )
const yourTrades = (
@@ -61,15 +53,11 @@ export function ContractTabs(props: {
tabs={[
{
title: 'Comments',
- content: (
-
- ),
+ content:
,
},
{
title: capitalize(PAST_BETS),
- content: (
-
- ),
+ content:
,
},
...(!user || !userBets?.length
? []
@@ -86,46 +74,87 @@ export function ContractTabs(props: {
const CommentsTabContent = memo(function CommentsTabContent(props: {
contract: Contract
- comments: ContractComment[]
}) {
- const { contract, comments } = props
+ const { contract } = props
const tips = useTipTxns({ contractId: contract.id })
- const updatedComments = useComments(contract.id) ?? comments
+ const comments = useComments(contract.id)
+ if (comments == null) {
+ return
+ }
if (contract.outcomeType === 'FREE_RESPONSE') {
+ const generalComments = comments.filter(
+ (c) => c.answerOutcome === undefined && c.betId === undefined
+ )
+ const sortedAnswers = sortBy(
+ contract.answers,
+ (a) => -getOutcomeProbability(contract, a.id)
+ )
+ const commentsByOutcome = groupBy(
+ comments,
+ (c) => c.answerOutcome ?? c.betOutcome ?? '_'
+ )
return (
<>
-
+ {sortedAnswers.map((answer) => (
+
+
+ c.createdTime
+ )}
+ tips={tips}
+ />
+
+ ))}
General Comments
-
- comment.answerOutcome === undefined &&
- comment.betId === undefined
- )}
- tips={tips}
- />
+
+ {generalComments.map((comment) => (
+
+ ))}
>
)
} else {
+ const commentsByParent = groupBy(comments, (c) => c.replyToCommentId ?? '_')
+ const topLevelComments = commentsByParent['_'] ?? []
return (
-
+ <>
+
+ {sortBy(topLevelComments, (c) => -c.createdTime).map((parent) => (
+ c.createdTime
+ )}
+ tips={tips}
+ />
+ ))}
+ >
)
}
})
-function ContractBetsActivity(props: { contract: Contract; bets: Bet[] }) {
+const BetsTabContent = memo(function BetsTabContent(props: {
+ contract: Contract
+ bets: Bet[]
+}) {
const { contract, bets } = props
const [page, setPage] = useState(0)
const ITEMS_PER_PAGE = 50
@@ -133,6 +162,9 @@ function ContractBetsActivity(props: { contract: Contract; bets: Bet[] }) {
const end = start + ITEMS_PER_PAGE
const lps = useLiquidity(contract.id) ?? []
+ const visibleBets = bets.filter(
+ (bet) => !bet.isAnte && !bet.isRedemption && bet.amount !== 0
+ )
const visibleLps = lps.filter(
(l) =>
!l.isAnte &&
@@ -142,7 +174,7 @@ function ContractBetsActivity(props: { contract: Contract; bets: Bet[] }) {
)
const items = [
- ...bets.map((bet) => ({
+ ...visibleBets.map((bet) => ({
type: 'bet' as const,
id: bet.id + '-' + bet.isSold,
bet,
@@ -184,74 +216,4 @@ function ContractBetsActivity(props: { contract: Contract; bets: Bet[] }) {
/>
>
)
-}
-
-function ContractCommentsActivity(props: {
- contract: Contract
- comments: ContractComment[]
- tips: CommentTipMap
-}) {
- const { contract, comments, tips } = props
- const commentsByParentId = groupBy(comments, (c) => c.replyToCommentId ?? '_')
- const topLevelComments = sortBy(
- commentsByParentId['_'] ?? [],
- (c) => -c.createdTime
- )
-
- return (
- <>
-
- {topLevelComments.map((parent) => (
- c.createdTime
- )}
- tips={tips}
- />
- ))}
- >
- )
-}
-
-function FreeResponseContractCommentsActivity(props: {
- contract: FreeResponseContract
- comments: ContractComment[]
- tips: CommentTipMap
-}) {
- const { contract, comments, tips } = props
-
- const sortedAnswers = sortBy(
- contract.answers,
- (answer) => -getOutcomeProbability(contract, answer.number.toString())
- )
- const commentsByOutcome = groupBy(
- comments,
- (c) => c.answerOutcome ?? c.betOutcome ?? '_'
- )
-
- return (
- <>
- {sortedAnswers.map((answer) => (
-
-
- c.createdTime
- )}
- tips={tips}
- />
-
- ))}
- >
- )
-}
+})
diff --git a/web/pages/[username]/[contractSlug].tsx b/web/pages/[username]/[contractSlug].tsx
index 3682e700..38df2fbf 100644
--- a/web/pages/[username]/[contractSlug].tsx
+++ b/web/pages/[username]/[contractSlug].tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react'
+import React, { memo, useEffect, useMemo, useState } from 'react'
import { ArrowLeftIcon } from '@heroicons/react/outline'
import { useContractWithPreload } from 'web/hooks/use-contract'
@@ -17,7 +17,6 @@ import {
import { SEO } from 'web/components/SEO'
import { Page } from 'web/components/page'
import { Bet, listAllBets } from 'web/lib/firebase/bets'
-import { listAllComments } from 'web/lib/firebase/comments'
import Custom404 from '../404'
import { AnswersPanel } from 'web/components/answers/answers-panel'
import { fromPropz, usePropz } from 'web/hooks/use-propz'
@@ -32,8 +31,6 @@ import { CPMMBinaryContract } from 'common/contract'
import { AlertBox } from 'web/components/alert-box'
import { useTracking } from 'web/hooks/use-tracking'
import { useSaveReferral } from 'web/hooks/use-save-referral'
-import { User } from 'common/user'
-import { ContractComment } from 'common/comment'
import { getOpenGraphProps } from 'common/contract-details'
import { ContractDescription } from 'web/components/contract/contract-description'
import {
@@ -54,25 +51,14 @@ export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: {
params: { username: string; contractSlug: string }
}) {
- const { username, contractSlug } = props.params
+ const { contractSlug } = props.params
const contract = (await getContractFromSlug(contractSlug)) || null
const contractId = contract?.id
-
- const [bets, comments] = await Promise.all([
- contractId ? listAllBets(contractId) : [],
- contractId ? listAllComments(contractId) : [],
- ])
+ const bets = contractId ? await listAllBets(contractId) : []
return {
- props: {
- contract,
- username,
- slug: contractSlug,
- // Limit the data sent to the client. Client will still load all bets and comments directly.
- bets: bets.slice(0, 5000),
- comments: comments.slice(0, 1000),
- },
-
+ // Limit the data sent to the client. Client will still load all bets directly.
+ props: { contract, bets: bets.slice(0, 5000) },
revalidate: 5, // regenerate after five seconds
}
}
@@ -83,21 +69,11 @@ export async function getStaticPaths() {
export default function ContractPage(props: {
contract: Contract | null
- username: string
bets: Bet[]
- comments: ContractComment[]
- slug: string
backToHome?: () => void
}) {
- props = usePropz(props, getStaticPropz) ?? {
- contract: null,
- username: '',
- comments: [],
- bets: [],
- slug: '',
- }
+ props = usePropz(props, getStaticPropz) ?? { contract: null, bets: [] }
- const user = useUser()
const inIframe = useIsIframe()
if (inIframe) {
return
@@ -109,9 +85,7 @@ export default function ContractPage(props: {
return
}
- return (
-
- )
+ return
}
// requires an admin to resolve a week after market closes
@@ -119,12 +93,10 @@ export function needsAdminToResolve(contract: Contract) {
return !contract.isResolved && dayjs().diff(contract.closeTime, 'day') > 7
}
-export function ContractPageSidebar(props: {
- user: User | null | undefined
- contract: Contract
-}) {
- const { contract, user } = props
+export function ContractPageSidebar(props: { contract: Contract }) {
+ const { contract } = props
const { creatorId, isResolved, outcomeType } = contract
+ const user = useUser()
const isCreator = user?.id === creatorId
const isBinary = outcomeType === 'BINARY'
const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC'
@@ -173,11 +145,11 @@ export function ContractPageSidebar(props: {
export function ContractPageContent(
props: Parameters[0] & {
contract: Contract
- user?: User | null
}
) {
- const { backToHome, comments, user } = props
+ const { backToHome } = props
const contract = useContractWithPreload(props.contract) ?? props.contract
+ const user = useUser()
usePrefetch(user?.id)
useTracking(
'view market',
@@ -217,9 +189,8 @@ export function ContractPageContent(
contractId: contract.id,
})
- const rightSidebar =
return (
-
+ }>
{showConfetti && (
)}
@@ -228,7 +199,7 @@ export function ContractPageContent(
)}
@@ -271,22 +242,13 @@ export function ContractPageContent(
<>
-
+
>
)}
-
+
{!user ? (
@@ -307,26 +269,28 @@ export function ContractPageContent(
)
}
-function RecommendedContractsWidget(props: { contract: Contract }) {
- const { contract } = props
- const user = useUser()
- const [recommendations, setRecommendations] = useState([])
- useEffect(() => {
- if (user) {
- getRecommendedContracts(contract, user.id, 6).then(setRecommendations)
+const RecommendedContractsWidget = memo(
+ function RecommendedContractsWidget(props: { contract: Contract }) {
+ const { contract } = props
+ const user = useUser()
+ const [recommendations, setRecommendations] = useState([])
+ useEffect(() => {
+ if (user) {
+ getRecommendedContracts(contract, user.id, 6).then(setRecommendations)
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [contract.id, user?.id])
+ if (recommendations.length === 0) {
+ return null
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [contract.id, user?.id])
- if (recommendations.length === 0) {
- return null
+ return (
+
+
+
+
+ )
}
- return (
-
-
-
-
- )
-}
+)
diff --git a/web/pages/embed/[username]/[contractSlug].tsx b/web/pages/embed/[username]/[contractSlug].tsx
index 62dd1ae1..75a9ad05 100644
--- a/web/pages/embed/[username]/[contractSlug].tsx
+++ b/web/pages/embed/[username]/[contractSlug].tsx
@@ -34,20 +34,14 @@ export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: {
params: { username: string; contractSlug: string }
}) {
- const { username, contractSlug } = props.params
+ const { contractSlug } = props.params
const contract = (await getContractFromSlug(contractSlug)) || null
const contractId = contract?.id
const bets = contractId ? await listAllBets(contractId) : []
return {
- props: {
- contract,
- username,
- slug: contractSlug,
- bets,
- },
-
+ props: { contract, bets },
revalidate: 60, // regenerate after a minute
}
}
@@ -58,16 +52,9 @@ export async function getStaticPaths() {
export default function ContractEmbedPage(props: {
contract: Contract | null
- username: string
bets: Bet[]
- slug: string
}) {
- props = usePropz(props, getStaticPropz) ?? {
- contract: null,
- username: '',
- bets: [],
- slug: '',
- }
+ props = usePropz(props, getStaticPropz) ?? { contract: null, bets: [] }
const contract = useContractWithPreload(props.contract)
const { bets } = props