challenge winner page
This commit is contained in:
parent
a16814b9c7
commit
d6f29ac9b9
|
@ -2,11 +2,7 @@ import React, { useEffect, useState } from 'react'
|
||||||
import Confetti from 'react-confetti'
|
import Confetti from 'react-confetti'
|
||||||
|
|
||||||
import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
import { fromPropz, usePropz } from 'web/hooks/use-propz'
|
||||||
import {
|
import { contractPath, getContractFromSlug } from 'web/lib/firebase/contracts'
|
||||||
Contract,
|
|
||||||
contractPath,
|
|
||||||
getContractFromSlug,
|
|
||||||
} from 'web/lib/firebase/contracts'
|
|
||||||
import { useContractWithPreload } from 'web/hooks/use-contract'
|
import { useContractWithPreload } from 'web/hooks/use-contract'
|
||||||
import { DOMAIN } from 'common/envs/constants'
|
import { DOMAIN } from 'common/envs/constants'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
|
@ -31,11 +27,6 @@ import { formatMoney } from 'common/util/format'
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||||
import { Bet, listAllBets } from 'web/lib/firebase/bets'
|
import { Bet, listAllBets } from 'web/lib/firebase/bets'
|
||||||
import {
|
|
||||||
BinaryResolutionOrChance,
|
|
||||||
PseudoNumericResolutionOrExpectation,
|
|
||||||
} from 'web/components/contract/contract-card'
|
|
||||||
import { ContractProbGraph } from 'web/components/contract/contract-prob-graph'
|
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
import { getOpenGraphProps } from 'web/components/contract/contract-card-preview'
|
import { getOpenGraphProps } from 'web/components/contract/contract-card-preview'
|
||||||
import Custom404 from 'web/pages/404'
|
import Custom404 from 'web/pages/404'
|
||||||
|
@ -124,7 +115,6 @@ export default function ChallengePage(props: {
|
||||||
contract={contract}
|
contract={contract}
|
||||||
challenge={challenge}
|
challenge={challenge}
|
||||||
creator={user}
|
creator={user}
|
||||||
bets={bets}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<OpenChallengeContent
|
<OpenChallengeContent
|
||||||
|
@ -139,29 +129,13 @@ export default function ChallengePage(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const userRow = (challenger: User) => (
|
|
||||||
<Row className={'mb-2 w-full items-center justify-center gap-2'}>
|
|
||||||
<Avatar
|
|
||||||
size={12}
|
|
||||||
avatarUrl={challenger.avatarUrl}
|
|
||||||
username={challenger.username}
|
|
||||||
/>
|
|
||||||
<UserLink
|
|
||||||
className={'text-2xl'}
|
|
||||||
name={challenger.name}
|
|
||||||
username={challenger.username}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
|
|
||||||
function ClosedChallengeContent(props: {
|
function ClosedChallengeContent(props: {
|
||||||
contract: BinaryContract
|
contract: BinaryContract
|
||||||
challenge: Challenge
|
challenge: Challenge
|
||||||
creator: User
|
creator: User
|
||||||
bets: Bet[]
|
|
||||||
}) {
|
}) {
|
||||||
const { contract, challenge, creator, bets } = props
|
const { contract, challenge, creator } = props
|
||||||
const { resolution } = contract
|
const { resolution, question } = contract
|
||||||
const {
|
const {
|
||||||
acceptances,
|
acceptances,
|
||||||
creatorAmount,
|
creatorAmount,
|
||||||
|
@ -171,6 +145,7 @@ function ClosedChallengeContent(props: {
|
||||||
} = challenge
|
} = challenge
|
||||||
|
|
||||||
const user = useUserById(acceptances[0].userId)
|
const user = useUserById(acceptances[0].userId)
|
||||||
|
|
||||||
const [showConfetti, setShowConfetti] = useState(false)
|
const [showConfetti, setShowConfetti] = useState(false)
|
||||||
const { width, height } = useWindowSize()
|
const { width, height } = useWindowSize()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -178,62 +153,22 @@ function ClosedChallengeContent(props: {
|
||||||
if (acceptances[0].createdTime > Date.now() - 1000 * 60)
|
if (acceptances[0].createdTime > Date.now() - 1000 * 60)
|
||||||
setShowConfetti(true)
|
setShowConfetti(true)
|
||||||
}, [acceptances])
|
}, [acceptances])
|
||||||
|
|
||||||
const creatorWon = resolution === creatorOutcome
|
const creatorWon = resolution === creatorOutcome
|
||||||
const amountWon = creatorWon ? acceptances[0].amount : creatorAmount
|
// const amountWon = creatorWon ? acceptances[0].amount : creatorAmount
|
||||||
const yourCost =
|
const yourCost =
|
||||||
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
|
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
|
||||||
|
|
||||||
|
const href = `https://${DOMAIN}${contractPath(contract)}`
|
||||||
|
|
||||||
if (!user) return <LoadingIndicator />
|
if (!user) return <LoadingIndicator />
|
||||||
|
|
||||||
const userWonCol = (user: User, amount: number) => (
|
const winner = (creatorWon ? creator : user).name
|
||||||
<Col className="w-full items-start justify-center gap-1 p-4">
|
|
||||||
<Row className={'mb-2 w-full items-center justify-center gap-2'}>
|
|
||||||
<span className={'mx-2 text-3xl'}>🥇</span>
|
|
||||||
<Avatar size={12} avatarUrl={user.avatarUrl} username={user.username} />
|
|
||||||
<UserLink
|
|
||||||
className={'text-2xl'}
|
|
||||||
name={user.name}
|
|
||||||
username={user.username}
|
|
||||||
/>
|
|
||||||
<span className={'mx-2 text-3xl'}>🥇</span>
|
|
||||||
</Row>
|
|
||||||
<Row className={'w-full items-center justify-center'}>
|
|
||||||
<span className={'text-lg'}>
|
|
||||||
WON <span className={'text-primary'}>{formatMoney(amount)}</span>
|
|
||||||
</span>
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
|
|
||||||
const userLostCol = (challenger: User, amount: number) => (
|
const title = resolution
|
||||||
<Col className="w-full items-start justify-center gap-1">
|
? `🥇 ${winner} wins the bet 🥇`
|
||||||
{userRow(challenger)}
|
: `⚔️ Challenge accepted ⚔️`
|
||||||
<Row className={'w-full items-center justify-center'}>
|
|
||||||
<span className={'text-lg'}>
|
|
||||||
LOST <span className={'text-red-500'}>{formatMoney(amount)}</span>
|
|
||||||
</span>
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
|
|
||||||
const userCol = (
|
|
||||||
challenger: User,
|
|
||||||
outcome: string,
|
|
||||||
prob: number,
|
|
||||||
amount: number
|
|
||||||
) => (
|
|
||||||
<Col className="w-full items-start justify-center gap-1">
|
|
||||||
{userRow(challenger)}
|
|
||||||
<Row className={'w-full items-center justify-center'}>
|
|
||||||
<span className={'text-lg'}>
|
|
||||||
is betting {formatMoney(amount)}
|
|
||||||
{' on '}
|
|
||||||
<BinaryOutcomeLabel outcome={outcome as any} /> at{' '}
|
|
||||||
{Math.round(prob * 100)}%
|
|
||||||
</span>
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showConfetti && (
|
{showConfetti && (
|
||||||
|
@ -252,76 +187,43 @@ function ClosedChallengeContent(props: {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Col className=" w-full rounded border-0 border-gray-100 bg-white py-6 pl-1 pr-2 sm:items-center sm:justify-center sm:px-2 md:px-6 md:py-8">
|
<Col className=" w-full rounded border-0 border-gray-100 bg-white py-6 pl-1 pr-2 sm:items-center sm:justify-center sm:px-2 md:px-6 md:py-8">
|
||||||
{!resolution && (
|
<Title className="!mt-0" text={title} />
|
||||||
<Row
|
|
||||||
className={
|
|
||||||
'items-center justify-center gap-2 text-xl text-gray-600'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span className={'text-xl'}>⚔️️</span>
|
|
||||||
Challenge Accepted
|
|
||||||
<span className={'text-xl'}>⚔️️</span>
|
|
||||||
</Row>
|
|
||||||
)}
|
|
||||||
{resolution == 'YES' || resolution == 'NO' ? (
|
|
||||||
<Col
|
|
||||||
className={
|
|
||||||
'max-h-[60vh] w-full content-between justify-between gap-1'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Row className={'mt-4 w-full'}>
|
|
||||||
{userWonCol(creatorWon ? creator : user, amountWon)}
|
|
||||||
</Row>
|
|
||||||
<Row className={'mt-4'}>
|
|
||||||
{userLostCol(creatorWon ? user : creator, amountWon)}
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
) : (
|
|
||||||
<Col
|
|
||||||
className={
|
|
||||||
'h-full w-full content-between justify-between gap-1 py-10 sm:flex-row'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{userCol(
|
|
||||||
creator,
|
|
||||||
creatorOutcome,
|
|
||||||
creatorOutcomeProb,
|
|
||||||
creatorAmount
|
|
||||||
)}
|
|
||||||
<Col className="items-center justify-center py-4 text-xl">VS</Col>
|
|
||||||
{userCol(user, yourOutcome, 1 - creatorOutcomeProb, yourCost)}
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
<Spacer h={3} />
|
|
||||||
<ChallengeContract contract={contract} bets={bets} />
|
|
||||||
</Col>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ChallengeContract(props: { contract: Contract; bets: Bet[] }) {
|
<Row className="my-4 justify-center px-8 pb-4 text-lg sm:text-xl">
|
||||||
const { contract, bets } = props
|
|
||||||
const { question } = contract
|
|
||||||
const href = `https://${DOMAIN}${contractPath(contract)}`
|
|
||||||
|
|
||||||
const isBinary = contract.outcomeType === 'BINARY'
|
|
||||||
const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
|
|
||||||
return (
|
|
||||||
<Col className="mt-5 w-full flex-1 bg-white px-10">
|
|
||||||
<div className="relative flex flex-col pt-2">
|
|
||||||
<Row className="justify-between px-3 text-xl text-indigo-700 md:text-2xl">
|
|
||||||
<SiteLink href={href}>{question}</SiteLink>
|
<SiteLink href={href}>{question}</SiteLink>
|
||||||
{isBinary && <BinaryResolutionOrChance contract={contract} />}
|
|
||||||
{isPseudoNumeric && (
|
|
||||||
<PseudoNumericResolutionOrExpectation contract={contract} />
|
|
||||||
)}
|
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{(isBinary || isPseudoNumeric) && (
|
<Col
|
||||||
<ContractProbGraph contract={contract} bets={bets} height={400} />
|
className={
|
||||||
)}
|
'h-full max-h-[50vh] w-full content-between justify-between gap-1 sm:flex-row'
|
||||||
</div>
|
}
|
||||||
</Col>
|
>
|
||||||
|
<UserBetColumn
|
||||||
|
challenger={creator}
|
||||||
|
outcome={creatorOutcome}
|
||||||
|
amount={creatorAmount}
|
||||||
|
isResolved={!!resolution}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Col className="items-center justify-center py-8 text-2xl sm:text-4xl">
|
||||||
|
VS
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<UserBetColumn
|
||||||
|
challenger={user?.id === creator.id ? undefined : user}
|
||||||
|
outcome={yourOutcome}
|
||||||
|
amount={yourCost}
|
||||||
|
isResolved={!!resolution}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Spacer h={3} />
|
||||||
|
|
||||||
|
{/* <Row className="mt-8 items-center">
|
||||||
|
<span className='mr-4'>Share</span> <CopyLinkButton url={window.location.href} />
|
||||||
|
</Row> */}
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,11 +244,10 @@ function OpenChallengeContent(props: {
|
||||||
yourOutcome,
|
yourOutcome,
|
||||||
} = challenge
|
} = challenge
|
||||||
|
|
||||||
const href = `https://${DOMAIN}${contractPath(contract)}`
|
|
||||||
|
|
||||||
const yourCost =
|
const yourCost =
|
||||||
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
|
((1 - creatorOutcomeProb) / creatorOutcomeProb) * creatorAmount
|
||||||
|
|
||||||
|
const href = `https://${DOMAIN}${contractPath(contract)}`
|
||||||
const title = `${creator.name} is challenging you to bet`
|
const title = `${creator.name} is challenging you to bet`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -394,12 +295,28 @@ function OpenChallengeContent(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userRow = (challenger: User) => (
|
||||||
|
<Row className={'mb-2 w-full items-center justify-center gap-2'}>
|
||||||
|
<Avatar
|
||||||
|
size={12}
|
||||||
|
avatarUrl={challenger.avatarUrl}
|
||||||
|
username={challenger.username}
|
||||||
|
/>
|
||||||
|
<UserLink
|
||||||
|
className={'text-2xl'}
|
||||||
|
name={challenger.name}
|
||||||
|
username={challenger.username}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
|
||||||
function UserBetColumn(props: {
|
function UserBetColumn(props: {
|
||||||
challenger: User | null | undefined
|
challenger: User | null | undefined
|
||||||
outcome: string
|
outcome: string
|
||||||
amount: number
|
amount: number
|
||||||
|
isResolved?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { challenger, outcome, amount } = props
|
const { challenger, outcome, amount, isResolved } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col className="w-full items-start justify-center gap-1">
|
<Col className="w-full items-start justify-center gap-1">
|
||||||
|
@ -412,7 +329,9 @@ function UserBetColumn(props: {
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
<Row className={'w-full items-center justify-center'}>
|
<Row className={'w-full items-center justify-center'}>
|
||||||
<span className={'text-lg'}>{challenger ? 'is' : 'are'} betting </span>
|
<span className={'text-lg'}>
|
||||||
|
{isResolved ? 'had bet' : challenger ? 'is betting' : 'are betting'}
|
||||||
|
</span>
|
||||||
</Row>
|
</Row>
|
||||||
<Row className={'w-full items-center justify-center'}>
|
<Row className={'w-full items-center justify-center'}>
|
||||||
<span className={'text-lg'}>
|
<span className={'text-lg'}>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user