Compare commits
2 Commits
main
...
one-click-
Author | SHA1 | Date | |
---|---|---|---|
|
4a53000047 | ||
|
96d7f27819 |
|
@ -7,12 +7,27 @@ const formatter = new Intl.NumberFormat('en-US', {
|
|||
minimumFractionDigits: 0,
|
||||
})
|
||||
|
||||
const formatterCents = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
})
|
||||
|
||||
export function formatMoney(amount: number) {
|
||||
const newAmount = Math.round(amount) === 0 ? 0 : amount // handle -0 case
|
||||
return (
|
||||
ENV_CONFIG.moneyMoniker + ' ' + formatter.format(newAmount).replace('$', '')
|
||||
)
|
||||
}
|
||||
export function formatCents(probability: number) {
|
||||
const newAmount = Math.round(probability * 100) === 0 ? 0 : probability // handle -0 case
|
||||
return (
|
||||
ENV_CONFIG.moneyMoniker +
|
||||
' ' +
|
||||
formatterCents.format(newAmount).replace('$', '')
|
||||
)
|
||||
}
|
||||
|
||||
export function formatWithCommas(amount: number) {
|
||||
return formatter.format(amount).replace('$', '')
|
||||
|
|
|
@ -9,6 +9,7 @@ import { InfoTooltip } from './info-tooltip'
|
|||
import { Spacer } from './layout/spacer'
|
||||
import { calculateCpmmSale } from '../../common/calculate-cpmm'
|
||||
import { Binary, CPMM, FullContract } from '../../common/contract'
|
||||
import { ENV_CONFIG } from '../../common/envs/constants'
|
||||
|
||||
export function AmountInput(props: {
|
||||
amount: number | undefined
|
||||
|
@ -50,10 +51,10 @@ export function AmountInput(props: {
|
|||
return (
|
||||
<Col className={className}>
|
||||
<label className="input-group">
|
||||
<span className="bg-gray-200 text-sm">{label}</span>
|
||||
<span className="bg-gray-200 text-lg">{label}</span>
|
||||
<input
|
||||
className={clsx(
|
||||
'input input-bordered',
|
||||
'input input-bordered text-lg',
|
||||
error && 'input-error',
|
||||
inputClassName
|
||||
)}
|
||||
|
@ -119,6 +120,10 @@ export function BuyAmountInput(props: {
|
|||
? Math.min(amount ?? 0, MAX_LOAN_PER_CONTRACT - prevLoanAmount)
|
||||
: 0
|
||||
|
||||
const remainingBalance = Math.floor(
|
||||
(user?.balance ?? 0) - (amount ?? 0) + loanAmount
|
||||
)
|
||||
|
||||
const onAmountChange = (amount: number | undefined) => {
|
||||
onChange(amount)
|
||||
|
||||
|
@ -140,7 +145,7 @@ export function BuyAmountInput(props: {
|
|||
<AmountInput
|
||||
amount={amount}
|
||||
onChange={onAmountChange}
|
||||
label="M$"
|
||||
label={ENV_CONFIG.moneyMoniker}
|
||||
error={error}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
|
@ -148,10 +153,10 @@ export function BuyAmountInput(props: {
|
|||
inputRef={inputRef}
|
||||
>
|
||||
{user && (
|
||||
<Col className="gap-3 text-sm">
|
||||
<Col className="gap-3">
|
||||
{contractIdForLoan && (
|
||||
<Row className="items-center justify-between gap-2 text-gray-500">
|
||||
<Row className="items-center gap-2">
|
||||
<Row className="items-center gap-2 text-sm">
|
||||
Amount loaned{' '}
|
||||
<InfoTooltip
|
||||
text={`In every market, you get an interest-free loan on the first ${formatMoney(
|
||||
|
|
|
@ -103,7 +103,7 @@ export function AnswerBetPanel(props: {
|
|||
<Col className={clsx('px-2 pb-2 pt-4 sm:pt-0', className)}>
|
||||
<Row className="items-center justify-between self-stretch">
|
||||
<div className="text-xl">
|
||||
Buy {isModal ? `"${answer.text}"` : 'this answer'}
|
||||
Bet on {isModal ? `"${answer.text}"` : 'this answer'}
|
||||
</div>
|
||||
|
||||
{!isModal && (
|
||||
|
|
|
@ -94,7 +94,7 @@ export function BetPanel(props: {
|
|||
className
|
||||
)}
|
||||
>
|
||||
<Title className={clsx('!mt-0')} text="Place a trade" />
|
||||
<div className={clsx('mb-6 text-2xl')}> Place a trade</div>
|
||||
|
||||
<BuyPanel contract={contract} user={user} userBets={userBets ?? []} />
|
||||
|
||||
|
@ -304,6 +304,12 @@ function BuyPanel(props: {
|
|||
const currentReturn = betAmount ? (currentPayout - betAmount) / betAmount : 0
|
||||
const currentReturnPercent = formatPercent(currentReturn)
|
||||
|
||||
const averagePrice = betAmount
|
||||
? betAmount / currentPayout
|
||||
: betChoice === 'NO'
|
||||
? 1 - initialProb
|
||||
: initialProb
|
||||
|
||||
const dpmTooltip =
|
||||
contract.mechanism === 'dpm-2'
|
||||
? `Current payout for ${formatWithCommas(shares)} / ${formatWithCommas(
|
||||
|
@ -335,8 +341,15 @@ function BuyPanel(props: {
|
|||
/>
|
||||
|
||||
<Col className="mt-3 w-full gap-3">
|
||||
<Row className="items-center justify-between text-sm">
|
||||
<div className="text-gray-500">Probability</div>
|
||||
{/* <Row className="items-center justify-between">
|
||||
<div className="text-sm text-gray-500">Average price</div>
|
||||
<Row>
|
||||
<div>{formatCents(averagePrice)}</div>
|
||||
</Row>
|
||||
</Row> */}
|
||||
|
||||
<Row className="items-center justify-between">
|
||||
<div className="text-sm text-gray-500">Probability change</div>
|
||||
<Row>
|
||||
<div>{formatPercent(initialProb)}</div>
|
||||
<div className="mx-2">→</div>
|
||||
|
@ -344,9 +357,9 @@ function BuyPanel(props: {
|
|||
</Row>
|
||||
</Row>
|
||||
|
||||
<Row className="items-center justify-between gap-2 text-sm">
|
||||
<Row className="items-center justify-between gap-2">
|
||||
<Row className="flex-nowrap items-center gap-2 whitespace-nowrap text-gray-500">
|
||||
<div>
|
||||
<div className="text-sm">
|
||||
{contract.mechanism === 'dpm-2' ? (
|
||||
<>
|
||||
Estimated
|
||||
|
|
|
@ -10,10 +10,11 @@ import { Modal } from './layout/modal'
|
|||
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
||||
export default function BetRow(props: {
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
large?: boolean
|
||||
className?: string
|
||||
labelClassName?: string
|
||||
}) {
|
||||
const { className, labelClassName } = props
|
||||
const { large, className, labelClassName } = props
|
||||
const [open, setOpen] = useState(false)
|
||||
const [betChoice, setBetChoice] = useState<'YES' | 'NO' | undefined>(
|
||||
undefined
|
||||
|
@ -27,7 +28,8 @@ export default function BetRow(props: {
|
|||
Place a trade
|
||||
</div>
|
||||
<YesNoSelector
|
||||
btnClassName="btn-sm w-24"
|
||||
btnClassName={clsx('btn-sm w-20', large && 'w-32 h-10')}
|
||||
large={large}
|
||||
onSelect={(choice) => {
|
||||
setOpen(true)
|
||||
setBetChoice(choice)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { Contract, tradingAllowed } from '../lib/firebase/contracts'
|
||||
import {
|
||||
Contract,
|
||||
getBinaryProbPercent,
|
||||
tradingAllowed,
|
||||
} from '../lib/firebase/contracts'
|
||||
import { Col } from './layout/col'
|
||||
import { Spacer } from './layout/spacer'
|
||||
import { ContractProbGraph } from './contract-prob-graph'
|
||||
|
@ -31,39 +35,44 @@ export const ContractOverview = (props: {
|
|||
const user = useUser()
|
||||
const isCreator = user?.id === creatorId
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
const allowTrade = tradingAllowed(contract)
|
||||
|
||||
return (
|
||||
<Col className={clsx('mb-6', className)}>
|
||||
<Col className="gap-4 px-2">
|
||||
<Row className="justify-between gap-4">
|
||||
<div className="text-2xl text-indigo-700 md:text-3xl">
|
||||
<Linkify text={question} />
|
||||
</div>
|
||||
<Row>
|
||||
<Col className="gap-4 px-2">
|
||||
<Row className="justify-between gap-4">
|
||||
<div className="text-2xl text-indigo-700 md:text-3xl">
|
||||
<Linkify text={question} />
|
||||
</div>
|
||||
|
||||
{(isBinary || resolution) && (
|
||||
{/* {(isBinary || resolution) && (
|
||||
<ResolutionOrChance
|
||||
className="hidden items-end xl:flex"
|
||||
className="items-end xl:flex"
|
||||
contract={contract}
|
||||
large
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
)} */}
|
||||
</Row>
|
||||
|
||||
<Row className="items-center justify-between gap-4 xl:hidden">
|
||||
{(isBinary || resolution) && (
|
||||
<ResolutionOrChance contract={contract} />
|
||||
)}
|
||||
|
||||
{isBinary && tradingAllowed(contract) && (
|
||||
<BetRow contract={contract} labelClassName="hidden" />
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<ContractDetails contract={contract} isCreator={isCreator} />
|
||||
</Col>
|
||||
<ContractDetails contract={contract} isCreator={isCreator} />
|
||||
</Col>
|
||||
<QuickBetWidget contract={contract} />
|
||||
</Row>
|
||||
|
||||
<Spacer h={4} />
|
||||
|
||||
{/* {isBinary && allowTrade && (
|
||||
<Row className="my-6 items-center justify-between gap-4 lg:justify-center">
|
||||
{(isBinary || resolution) && (
|
||||
<ResolutionOrChance contract={contract} className="lg:hidden" />
|
||||
)}
|
||||
{isBinary && tradingAllowed(contract) && (
|
||||
<BetRow large contract={contract} labelClassName="hidden" />
|
||||
)}
|
||||
</Row>
|
||||
)} */}
|
||||
|
||||
{isBinary ? (
|
||||
<ContractProbGraph contract={contract} bets={bets} />
|
||||
) : (
|
||||
|
@ -108,3 +117,54 @@ export const ContractOverview = (props: {
|
|||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function Triangle(props: {
|
||||
direction: 'up' | 'down'
|
||||
width: number
|
||||
color: string
|
||||
className?: string
|
||||
}) {
|
||||
const { direction, width, color, className } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'border-x-solid group h-0 w-0 cursor-pointer border-x-[30px] border-x-transparent transition-colors',
|
||||
'relative',
|
||||
className,
|
||||
direction === 'up' &&
|
||||
'border-b-solid border-b-[30px] border-b-gray-200 hover:border-b-green-300 focus:ring-green-500',
|
||||
direction === 'down' &&
|
||||
'border-t-solid border-t-[30px] border-t-gray-200 hover:border-t-red-300'
|
||||
)}
|
||||
tabIndex={0}
|
||||
>
|
||||
{/* {direction === 'up' && (
|
||||
<div className="absolute top-4 -left-2 select-none text-xs text-gray-500 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
M$
|
||||
</div>
|
||||
)}
|
||||
{direction === 'down' && (
|
||||
<div className="absolute -top-9 -left-2 hidden select-none text-xs text-gray-500 group-hover:block">
|
||||
M$
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function QuickBetWidget(props: { contract: Contract }) {
|
||||
const { contract } = props
|
||||
return (
|
||||
<Col className="items-center gap-2">
|
||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||
Click to trade
|
||||
</div>
|
||||
<Triangle direction="up" width={50} color="#e3e3e3" />
|
||||
<div className="text-primary text-3xl">
|
||||
{getBinaryProbPercent(contract)}
|
||||
</div>
|
||||
<Triangle direction="down" width={50} color="#e3e3e3" />
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,41 +7,40 @@ import { Row } from './layout/row'
|
|||
export function YesNoSelector(props: {
|
||||
selected?: 'YES' | 'NO'
|
||||
onSelect: (selected: 'YES' | 'NO') => void
|
||||
large?: boolean
|
||||
className?: string
|
||||
btnClassName?: string
|
||||
}) {
|
||||
const { selected, onSelect, className, btnClassName } = props
|
||||
const { selected, onSelect, large, className, btnClassName } = props
|
||||
|
||||
const commonClassNames =
|
||||
'inline-flex flex-1 items-center justify-center rounded-3xl border-2 p-2'
|
||||
const commonClassNames = clsx(
|
||||
'inline-flex flex-1 items-center justify-center rounded-3xl p-2 border-2 border-gray-300 shadow',
|
||||
large && 'text-lg w-32'
|
||||
)
|
||||
|
||||
return (
|
||||
<Row className={clsx('space-x-3', className)}>
|
||||
<button
|
||||
className={clsx(
|
||||
commonClassNames,
|
||||
'hover:bg-primary-focus border-primary hover:border-primary-focus hover:text-white',
|
||||
selected == 'YES'
|
||||
? 'bg-primary text-white'
|
||||
: 'text-primary bg-transparent',
|
||||
'border-primary hover:bg-primary-focus hover:border-primary-focus hover:text-white',
|
||||
selected == 'YES' ? 'bg-primary text-white' : 'text-primary bg-white',
|
||||
btnClassName
|
||||
)}
|
||||
onClick={() => onSelect('YES')}
|
||||
>
|
||||
Buy YES
|
||||
Bet YES
|
||||
</button>
|
||||
<button
|
||||
className={clsx(
|
||||
commonClassNames,
|
||||
'border-red-400 hover:border-red-500 hover:bg-red-500 hover:text-white',
|
||||
selected == 'NO'
|
||||
? 'bg-red-400 text-white'
|
||||
: 'bg-transparent text-red-400',
|
||||
selected == 'NO' ? 'bg-red-400 text-white' : 'bg-white text-red-400',
|
||||
btnClassName
|
||||
)}
|
||||
onClick={() => onSelect('NO')}
|
||||
>
|
||||
Buy NO
|
||||
Bet NO
|
||||
</button>
|
||||
</Row>
|
||||
)
|
||||
|
@ -173,7 +172,7 @@ export function BuyButton(props: { className?: string; onClick?: () => void }) {
|
|||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
Buy
|
||||
Bet
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -115,18 +115,14 @@ export default function ContractPage(props: {
|
|||
|
||||
const isCreator = user?.id === creatorId
|
||||
const isBinary = outcomeType === 'BINARY'
|
||||
const allowTrade = tradingAllowed(contract)
|
||||
const allowResolve = !isResolved && isCreator && !!user
|
||||
const hasSidePanel = isBinary && (allowTrade || allowResolve)
|
||||
const hasSidePanel = isBinary && allowResolve
|
||||
|
||||
const ogCardProps = getOpenGraphProps(contract)
|
||||
|
||||
const rightSidebar = hasSidePanel ? (
|
||||
<Col className="gap-4">
|
||||
{allowTrade && (
|
||||
<BetPanel className="hidden xl:flex" contract={contract} />
|
||||
)}
|
||||
{allowResolve && <ResolutionPanel creator={user} contract={contract} />}
|
||||
<ResolutionPanel creator={user} contract={contract} />
|
||||
</Col>
|
||||
) : null
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user