Better bet summary (#936)
* show position, expected value, profit instead of "invested" * move bet summary outside trades on market page * refactor * pass in userbets * hide only if no bets; show invested on desktop * various
This commit is contained in:
parent
7ba19c274b
commit
723d9dbece
120
web/components/bet-summary.tsx
Normal file
120
web/components/bet-summary.tsx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import { sumBy } from 'lodash'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
|
import { Bet } from 'web/lib/firebase/bets'
|
||||||
|
import { formatMoney, formatWithCommas } from 'common/util/format'
|
||||||
|
import { Col } from './layout/col'
|
||||||
|
import { Contract } from 'web/lib/firebase/contracts'
|
||||||
|
import { Row } from './layout/row'
|
||||||
|
import { YesLabel, NoLabel } from './outcome-label'
|
||||||
|
import {
|
||||||
|
calculatePayout,
|
||||||
|
getContractBetMetrics,
|
||||||
|
getProbability,
|
||||||
|
} from 'common/calculate'
|
||||||
|
import { InfoTooltip } from './info-tooltip'
|
||||||
|
import { ProfitBadge } from './profit-badge'
|
||||||
|
|
||||||
|
export function BetsSummary(props: {
|
||||||
|
contract: Contract
|
||||||
|
userBets: Bet[]
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { contract, className } = props
|
||||||
|
const { resolution, outcomeType } = contract
|
||||||
|
const isBinary = outcomeType === 'BINARY'
|
||||||
|
|
||||||
|
const bets = props.userBets.filter((b) => !b.isAnte)
|
||||||
|
const { profitPercent, payout, profit, invested } = getContractBetMetrics(
|
||||||
|
contract,
|
||||||
|
bets
|
||||||
|
)
|
||||||
|
|
||||||
|
const excludeSales = bets.filter((b) => !b.isSold && !b.sale)
|
||||||
|
const yesWinnings = sumBy(excludeSales, (bet) =>
|
||||||
|
calculatePayout(contract, bet, 'YES')
|
||||||
|
)
|
||||||
|
const noWinnings = sumBy(excludeSales, (bet) =>
|
||||||
|
calculatePayout(contract, bet, 'NO')
|
||||||
|
)
|
||||||
|
|
||||||
|
const position = yesWinnings - noWinnings
|
||||||
|
|
||||||
|
const prob = isBinary ? getProbability(contract) : 0
|
||||||
|
const expectation = prob * yesWinnings + (1 - prob) * noWinnings
|
||||||
|
|
||||||
|
if (bets.length === 0) return <></>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col className={clsx(className, 'gap-4')}>
|
||||||
|
<Row className="flex-wrap gap-4 sm:flex-nowrap sm:gap-6">
|
||||||
|
{resolution ? (
|
||||||
|
<Col>
|
||||||
|
<div className="text-sm text-gray-500">Payout</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{formatMoney(payout)}{' '}
|
||||||
|
<ProfitBadge profitPercent={profitPercent} />
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
) : isBinary ? (
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Position{' '}
|
||||||
|
<InfoTooltip text="Number of shares you own on net. 1 YES share = M$1 if the market resolves YES." />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{position > 1e-7 ? (
|
||||||
|
<>
|
||||||
|
<YesLabel /> {formatWithCommas(position)}
|
||||||
|
</>
|
||||||
|
) : position < -1e-7 ? (
|
||||||
|
<>
|
||||||
|
<NoLabel /> {formatWithCommas(-position)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'——'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
) : (
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Expectation{''}
|
||||||
|
<InfoTooltip text="The estimated payout of your position using the current market probability." />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Col className="hidden sm:inline">
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Invested{' '}
|
||||||
|
<InfoTooltip text="Cash currently invested in this market." />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(invested)}</div>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
{isBinary && !resolution && (
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Expectation{' '}
|
||||||
|
<InfoTooltip text="The estimated payout of your position using the current market probability." />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(expectation)}</div>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Profit{' '}
|
||||||
|
<InfoTooltip text="Includes both realized & unrealized gains/losses." />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{formatMoney(profit)}
|
||||||
|
<ProfitBadge profitPercent={profitPercent} />
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ import {
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { sellBet } from 'web/lib/firebase/api'
|
import { sellBet } from 'web/lib/firebase/api'
|
||||||
import { ConfirmationButton } from './confirmation-button'
|
import { ConfirmationButton } from './confirmation-button'
|
||||||
import { OutcomeLabel, YesLabel, NoLabel } from './outcome-label'
|
import { OutcomeLabel } from './outcome-label'
|
||||||
import { LoadingIndicator } from './loading-indicator'
|
import { LoadingIndicator } from './loading-indicator'
|
||||||
import { SiteLink } from './site-link'
|
import { SiteLink } from './site-link'
|
||||||
import {
|
import {
|
||||||
|
@ -38,14 +38,14 @@ import { NumericContract } from 'common/contract'
|
||||||
import { formatNumericProbability } from 'common/pseudo-numeric'
|
import { formatNumericProbability } from 'common/pseudo-numeric'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { useUserBets } from 'web/hooks/use-user-bets'
|
import { useUserBets } from 'web/hooks/use-user-bets'
|
||||||
import { SellSharesModal } from './sell-modal'
|
|
||||||
import { useUnfilledBets } from 'web/hooks/use-bets'
|
import { useUnfilledBets } from 'web/hooks/use-bets'
|
||||||
import { LimitBet } from 'common/bet'
|
import { LimitBet } from 'common/bet'
|
||||||
import { floatingEqual } from 'common/util/math'
|
|
||||||
import { Pagination } from './pagination'
|
import { Pagination } from './pagination'
|
||||||
import { LimitOrderTable } from './limit-bets'
|
import { LimitOrderTable } from './limit-bets'
|
||||||
import { UserLink } from 'web/components/user-link'
|
import { UserLink } from 'web/components/user-link'
|
||||||
import { useUserBetContracts } from 'web/hooks/use-contracts'
|
import { useUserBetContracts } from 'web/hooks/use-contracts'
|
||||||
|
import { BetsSummary } from './bet-summary'
|
||||||
|
import { ProfitBadge } from './profit-badge'
|
||||||
|
|
||||||
type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
|
type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
|
||||||
type BetFilter = 'open' | 'limit_bet' | 'sold' | 'closed' | 'resolved' | 'all'
|
type BetFilter = 'open' | 'limit_bet' | 'sold' | 'closed' | 'resolved' | 'all'
|
||||||
|
@ -337,8 +337,7 @@ function ContractBets(props: {
|
||||||
<BetsSummary
|
<BetsSummary
|
||||||
className="mt-8 mr-5 flex-1 sm:mr-8"
|
className="mt-8 mr-5 flex-1 sm:mr-8"
|
||||||
contract={contract}
|
contract={contract}
|
||||||
bets={bets}
|
userBets={bets}
|
||||||
isYourBets={isYourBets}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{contract.mechanism === 'cpmm-1' && limitBets.length > 0 && (
|
{contract.mechanism === 'cpmm-1' && limitBets.length > 0 && (
|
||||||
|
@ -364,125 +363,6 @@ function ContractBets(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BetsSummary(props: {
|
|
||||||
contract: Contract
|
|
||||||
bets: Bet[]
|
|
||||||
isYourBets: boolean
|
|
||||||
className?: string
|
|
||||||
}) {
|
|
||||||
const { contract, isYourBets, className } = props
|
|
||||||
const { resolution, closeTime, outcomeType, mechanism } = contract
|
|
||||||
const isBinary = outcomeType === 'BINARY'
|
|
||||||
const isPseudoNumeric = outcomeType === 'PSEUDO_NUMERIC'
|
|
||||||
const isCpmm = mechanism === 'cpmm-1'
|
|
||||||
const isClosed = closeTime && Date.now() > closeTime
|
|
||||||
|
|
||||||
const bets = props.bets.filter((b) => !b.isAnte)
|
|
||||||
const { hasShares, invested, profitPercent, payout, profit, totalShares } =
|
|
||||||
getContractBetMetrics(contract, bets)
|
|
||||||
|
|
||||||
const excludeSales = bets.filter((b) => !b.isSold && !b.sale)
|
|
||||||
const yesWinnings = sumBy(excludeSales, (bet) =>
|
|
||||||
calculatePayout(contract, bet, 'YES')
|
|
||||||
)
|
|
||||||
const noWinnings = sumBy(excludeSales, (bet) =>
|
|
||||||
calculatePayout(contract, bet, 'NO')
|
|
||||||
)
|
|
||||||
|
|
||||||
const [showSellModal, setShowSellModal] = useState(false)
|
|
||||||
const user = useUser()
|
|
||||||
|
|
||||||
const sharesOutcome = floatingEqual(totalShares.YES ?? 0, 0)
|
|
||||||
? floatingEqual(totalShares.NO ?? 0, 0)
|
|
||||||
? undefined
|
|
||||||
: 'NO'
|
|
||||||
: 'YES'
|
|
||||||
|
|
||||||
const canSell =
|
|
||||||
isYourBets &&
|
|
||||||
isCpmm &&
|
|
||||||
(isBinary || isPseudoNumeric) &&
|
|
||||||
!isClosed &&
|
|
||||||
!resolution &&
|
|
||||||
hasShares &&
|
|
||||||
sharesOutcome &&
|
|
||||||
user
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Col className={clsx(className, 'gap-4')}>
|
|
||||||
<Row className="flex-wrap gap-4 sm:flex-nowrap sm:gap-6">
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Invested
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(invested)}</div>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">Profit</div>
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
{formatMoney(profit)} <ProfitBadge profitPercent={profitPercent} />
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
{canSell && (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className="btn btn-sm self-end"
|
|
||||||
onClick={() => setShowSellModal(true)}
|
|
||||||
>
|
|
||||||
Sell
|
|
||||||
</button>
|
|
||||||
{showSellModal && (
|
|
||||||
<SellSharesModal
|
|
||||||
contract={contract}
|
|
||||||
user={user}
|
|
||||||
userBets={bets}
|
|
||||||
shares={totalShares[sharesOutcome]}
|
|
||||||
sharesOutcome={sharesOutcome}
|
|
||||||
setOpen={setShowSellModal}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
<Row className="flex-wrap-none gap-4">
|
|
||||||
{resolution ? (
|
|
||||||
<Col>
|
|
||||||
<div className="text-sm text-gray-500">Payout</div>
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
{formatMoney(payout)}{' '}
|
|
||||||
<ProfitBadge profitPercent={profitPercent} />
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
) : isBinary ? (
|
|
||||||
<>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Payout if <YesLabel />
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
{formatMoney(yesWinnings)}
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Payout if <NoLabel />
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(noWinnings)}</div>
|
|
||||||
</Col>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Expected value
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ContractBetsTable(props: {
|
export function ContractBetsTable(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
|
@ -750,30 +630,3 @@ function SellButton(props: {
|
||||||
</ConfirmationButton>
|
</ConfirmationButton>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProfitBadge(props: {
|
|
||||||
profitPercent: number
|
|
||||||
round?: boolean
|
|
||||||
className?: string
|
|
||||||
}) {
|
|
||||||
const { profitPercent, round, className } = props
|
|
||||||
if (!profitPercent) return null
|
|
||||||
const colors =
|
|
||||||
profitPercent > 0
|
|
||||||
? 'bg-green-100 text-green-800'
|
|
||||||
: 'bg-red-100 text-red-800'
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={clsx(
|
|
||||||
'ml-1 inline-flex items-center rounded-full px-3 py-0.5 text-sm font-medium',
|
|
||||||
colors,
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{(profitPercent > 0 ? '+' : '') +
|
|
||||||
profitPercent.toFixed(round ? 0 : 1) +
|
|
||||||
'%'}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { groupBy, sortBy } from 'lodash'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
import { PAST_BETS } from 'common/user'
|
import { PAST_BETS } from 'common/user'
|
||||||
import { ContractBetsTable, BetsSummary } from '../bets-list'
|
import { ContractBetsTable } from '../bets-list'
|
||||||
import { Spacer } from '../layout/spacer'
|
import { Spacer } from '../layout/spacer'
|
||||||
import { Tabs } from '../layout/tabs'
|
import { Tabs } from '../layout/tabs'
|
||||||
import { Col } from '../layout/col'
|
import { Col } from '../layout/col'
|
||||||
|
@ -17,59 +17,45 @@ import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
import { useComments } from 'web/hooks/use-comments'
|
import { useComments } from 'web/hooks/use-comments'
|
||||||
import { useLiquidity } from 'web/hooks/use-liquidity'
|
import { useLiquidity } from 'web/hooks/use-liquidity'
|
||||||
import { useTipTxns } from 'web/hooks/use-tip-txns'
|
import { useTipTxns } from 'web/hooks/use-tip-txns'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
|
||||||
import { capitalize } from 'lodash'
|
import { capitalize } from 'lodash'
|
||||||
import {
|
import {
|
||||||
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
|
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
|
||||||
HOUSE_LIQUIDITY_PROVIDER_ID,
|
HOUSE_LIQUIDITY_PROVIDER_ID,
|
||||||
} from 'common/antes'
|
} from 'common/antes'
|
||||||
import { useIsMobile } from 'web/hooks/use-is-mobile'
|
import { buildArray } from 'common/util/array'
|
||||||
|
|
||||||
export function ContractTabs(props: { contract: Contract; bets: Bet[] }) {
|
export function ContractTabs(props: {
|
||||||
const { contract, bets } = props
|
contract: Contract
|
||||||
|
bets: Bet[]
|
||||||
const isMobile = useIsMobile()
|
userBets: Bet[]
|
||||||
const user = useUser()
|
}) {
|
||||||
const userBets =
|
const { contract, bets, userBets } = props
|
||||||
user && bets.filter((bet) => !bet.isAnte && bet.userId === user.id)
|
|
||||||
|
|
||||||
const yourTrades = (
|
const yourTrades = (
|
||||||
<div>
|
<div>
|
||||||
<BetsSummary
|
|
||||||
className="px-2"
|
|
||||||
contract={contract}
|
|
||||||
bets={userBets ?? []}
|
|
||||||
isYourBets
|
|
||||||
/>
|
|
||||||
<Spacer h={6} />
|
<Spacer h={6} />
|
||||||
<ContractBetsTable contract={contract} bets={userBets ?? []} isYourBets />
|
<ContractBetsTable contract={contract} bets={userBets} isYourBets />
|
||||||
<Spacer h={12} />
|
<Spacer h={12} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const tabs = buildArray(
|
||||||
|
{
|
||||||
|
title: 'Comments',
|
||||||
|
content: <CommentsTabContent contract={contract} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: capitalize(PAST_BETS),
|
||||||
|
content: <BetsTabContent contract={contract} bets={bets} />,
|
||||||
|
},
|
||||||
|
userBets.length > 0 && {
|
||||||
|
title: 'Your trades',
|
||||||
|
content: yourTrades,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs className="mb-4" currentPageForAnalytics={'contract'} tabs={tabs} />
|
||||||
className="mb-4"
|
|
||||||
currentPageForAnalytics={'contract'}
|
|
||||||
tabs={[
|
|
||||||
{
|
|
||||||
title: 'Comments',
|
|
||||||
content: <CommentsTabContent contract={contract} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: capitalize(PAST_BETS),
|
|
||||||
content: <BetsTabContent contract={contract} bets={bets} />,
|
|
||||||
},
|
|
||||||
...(!user || !userBets?.length
|
|
||||||
? []
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
title: isMobile ? `You` : `Your ${PAST_BETS}`,
|
|
||||||
content: yourTrades,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
web/components/profit-badge.tsx
Normal file
28
web/components/profit-badge.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
|
export function ProfitBadge(props: {
|
||||||
|
profitPercent: number
|
||||||
|
round?: boolean
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { profitPercent, round, className } = props
|
||||||
|
if (!profitPercent) return null
|
||||||
|
const colors =
|
||||||
|
profitPercent > 0
|
||||||
|
? 'bg-green-100 text-green-800'
|
||||||
|
: 'bg-red-100 text-red-800'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
'ml-1 inline-flex items-center rounded-full px-3 py-0.5 text-sm font-medium',
|
||||||
|
colors,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{(profitPercent > 0 ? '+' : '') +
|
||||||
|
profitPercent.toFixed(round ? 0 : 1) +
|
||||||
|
'%'}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { memo, useEffect, useMemo, useState } from 'react'
|
import React, { memo, useEffect, useMemo, useState } from 'react'
|
||||||
import { ArrowLeftIcon } from '@heroicons/react/outline'
|
import { ArrowLeftIcon } from '@heroicons/react/outline'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
import { useContractWithPreload } from 'web/hooks/use-contract'
|
import { useContractWithPreload } from 'web/hooks/use-contract'
|
||||||
import { ContractOverview } from 'web/components/contract/contract-overview'
|
import { ContractOverview } from 'web/components/contract/contract-overview'
|
||||||
|
@ -44,8 +45,7 @@ import { useAdmin } from 'web/hooks/use-admin'
|
||||||
import { BetSignUpPrompt } from 'web/components/sign-up-prompt'
|
import { BetSignUpPrompt } from 'web/components/sign-up-prompt'
|
||||||
import { PlayMoneyDisclaimer } from 'web/components/play-money-disclaimer'
|
import { PlayMoneyDisclaimer } from 'web/components/play-money-disclaimer'
|
||||||
import BetButton from 'web/components/bet-button'
|
import BetButton from 'web/components/bet-button'
|
||||||
|
import { BetsSummary } from 'web/components/bet-summary'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz(props: {
|
export async function getStaticPropz(props: {
|
||||||
|
@ -167,6 +167,10 @@ export function ContractPageContent(
|
||||||
[bets]
|
[bets]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const userBets = user
|
||||||
|
? bets.filter((bet) => !bet.isAnte && bet.userId === user.id)
|
||||||
|
: []
|
||||||
|
|
||||||
const [showConfetti, setShowConfetti] = useState(false)
|
const [showConfetti, setShowConfetti] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -248,7 +252,14 @@ export function ContractPageContent(
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ContractTabs contract={contract} bets={bets} />
|
<BetsSummary
|
||||||
|
className="mb-4 px-2"
|
||||||
|
contract={contract}
|
||||||
|
userBets={userBets}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ContractTabs contract={contract} bets={bets} userBets={userBets} />
|
||||||
|
|
||||||
{!user ? (
|
{!user ? (
|
||||||
<Col className="mt-4 max-w-sm items-center xl:hidden">
|
<Col className="mt-4 max-w-sm items-center xl:hidden">
|
||||||
<BetSignUpPrompt />
|
<BetSignUpPrompt />
|
||||||
|
|
|
@ -33,7 +33,6 @@ import { groupPath, joinGroup, leaveGroup } from 'web/lib/firebase/groups'
|
||||||
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
|
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
||||||
import { ProfitBadge } from 'web/components/bets-list'
|
|
||||||
import { calculatePortfolioProfit } from 'common/calculate-metrics'
|
import { calculatePortfolioProfit } from 'common/calculate-metrics'
|
||||||
import { hasCompletedStreakToday } from 'web/components/profile/betting-streak-modal'
|
import { hasCompletedStreakToday } from 'web/components/profile/betting-streak-modal'
|
||||||
import { ContractsGrid } from 'web/components/contract/contracts-grid'
|
import { ContractsGrid } from 'web/components/contract/contracts-grid'
|
||||||
|
@ -45,6 +44,7 @@ import { usePrefetch } from 'web/hooks/use-prefetch'
|
||||||
import { Title } from 'web/components/title'
|
import { Title } from 'web/components/title'
|
||||||
import { CPMMBinaryContract } from 'common/contract'
|
import { CPMMBinaryContract } from 'common/contract'
|
||||||
import { useContractsByDailyScoreGroups } from 'web/hooks/use-contracts'
|
import { useContractsByDailyScoreGroups } from 'web/hooks/use-contracts'
|
||||||
|
import { ProfitBadge } from 'web/components/profit-badge'
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user