import React, { useEffect, useState } from 'react' import { ArrowLeftIcon } from '@heroicons/react/outline' import { useContractWithPreload } from 'web/hooks/use-contract' import { ContractOverview } from 'web/components/contract/contract-overview' import { BetPanel } from 'web/components/bet-panel' import { Col } from 'web/components/layout/col' import { useUser } from 'web/hooks/use-user' import { ResolutionPanel } from 'web/components/resolution-panel' import { Spacer } from 'web/components/layout/spacer' import { Contract, getContractFromSlug, tradingAllowed, getBinaryProbPercent, } from 'web/lib/firebase/contracts' import { SEO } from 'web/components/SEO' import { Page } from 'web/components/page' import { Bet, listAllBets } from 'web/lib/firebase/bets' import { Comment, 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' import { ContractTabs } from 'web/components/contract/contract-tabs' import { contractTextDetails } from 'web/components/contract/contract-details' import { useWindowSize } from 'web/hooks/use-window-size' import Confetti from 'react-confetti' import { NumericBetPanel } from '../../components/numeric-bet-panel' import { NumericResolutionPanel } from '../../components/numeric-resolution-panel' import { useIsIframe } from 'web/hooks/use-is-iframe' import ContractEmbedPage from '../embed/[username]/[contractSlug]' import { useBets } from 'web/hooks/use-bets' import { CPMMBinaryContract } from 'common/contract' import { AlertBox } from 'web/components/alert-box' import { useTracking } from 'web/hooks/use-tracking' import { useTipTxns } from 'web/hooks/use-tip-txns' import { useLiquidity } from 'web/hooks/use-liquidity' import { richTextToString } from 'common/util/parse' import { useSaveReferral } from 'web/hooks/use-save-referral' import { ContractLeaderboard, ContractTopTrades, } from 'web/components/contract/contract-leaderboard' export const getStaticProps = fromPropz(getStaticPropz) export async function getStaticPropz(props: { params: { username: string; contractSlug: string } }) { const { username, 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) : [], ]) 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), }, revalidate: 60, // regenerate after a minute } } export async function getStaticPaths() { return { paths: [], fallback: 'blocking' } } export default function ContractPage(props: { contract: Contract | null username: string bets: Bet[] comments: Comment[] slug: string backToHome?: () => void }) { props = usePropz(props, getStaticPropz) ?? { contract: null, username: '', comments: [], bets: [], slug: '', } const inIframe = useIsIframe() if (inIframe) { return } const { contract } = props if (!contract) { return } return } export function ContractPageContent( props: Parameters[0] & { contract: Contract } ) { const { backToHome, comments } = props const contract = useContractWithPreload(props.contract) ?? props.contract useTracking('view market', { slug: contract.slug, contractId: contract.id, creatorId: contract.creatorId, }) const bets = useBets(contract.id) ?? props.bets const liquidityProvisions = useLiquidity(contract.id)?.filter((l) => !l.isAnte && l.amount > 0) ?? [] // Sort for now to see if bug is fixed. comments.sort((c1, c2) => c1.createdTime - c2.createdTime) const tips = useTipTxns({ contractId: contract.id }) const user = useUser() const { width, height } = useWindowSize() const [showConfetti, setShowConfetti] = useState(false) useEffect(() => { const shouldSeeConfetti = !!( user && contract.creatorId === user.id && Date.now() - contract.createdTime < 10 * 1000 ) setShowConfetti(shouldSeeConfetti) }, [contract, user]) const { creatorId, isResolved, question, outcomeType } = contract const isCreator = user?.id === creatorId const isBinary = outcomeType === 'BINARY' const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC' const isNumeric = outcomeType === 'NUMERIC' const allowTrade = tradingAllowed(contract) const allowResolve = !isResolved && isCreator && !!user const hasSidePanel = (isBinary || isNumeric || isPseudoNumeric) && (allowTrade || allowResolve) const ogCardProps = getOpenGraphProps(contract) useSaveReferral(user, { defaultReferrer: contract.creatorUsername, contractId: contract.id, }) const rightSidebar = hasSidePanel ? ( {allowTrade && (isNumeric ? ( ) : ( ))} {allowResolve && (isNumeric || isPseudoNumeric ? ( ) : ( ))} ) : null return ( {showConfetti && ( )} {ogCardProps && ( )} {backToHome && ( )} {isNumeric && ( )} {(outcomeType === 'FREE_RESPONSE' || outcomeType === 'MULTIPLE_CHOICE') && ( <> )} {isNumeric && allowTrade && ( )} {isResolved && ( <>
)}
) } const getOpenGraphProps = (contract: Contract) => { const { resolution, question, creatorName, creatorUsername, outcomeType, creatorAvatarUrl, description: desc, } = contract const probPercent = outcomeType === 'BINARY' ? getBinaryProbPercent(contract) : undefined const stringDesc = typeof desc === 'string' ? desc : richTextToString(desc) const description = resolution ? `Resolved ${resolution}. ${stringDesc}` : probPercent ? `${probPercent} chance. ${stringDesc}` : stringDesc return { question, probability: probPercent, metadata: contractTextDetails(contract), creatorName, creatorUsername, creatorAvatarUrl, description, } }