2022-07-10 18:05:44 +00:00
|
|
|
import { LimitBet } from 'common/bet'
|
|
|
|
import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract'
|
|
|
|
import { getFormattedMappedValue } from 'common/pseudo-numeric'
|
|
|
|
import { formatMoney, formatPercent } from 'common/util/format'
|
|
|
|
import { sortBy } from 'lodash'
|
|
|
|
import { useState } from 'react'
|
2022-09-14 08:33:59 +00:00
|
|
|
import { useUser } from 'web/hooks/use-user'
|
2022-07-10 22:03:15 +00:00
|
|
|
import { cancelBet } from 'web/lib/firebase/api'
|
2022-07-13 17:14:58 +00:00
|
|
|
import { Avatar } from './avatar'
|
2022-07-16 19:21:24 +00:00
|
|
|
import { Button } from './button'
|
2022-07-10 18:05:44 +00:00
|
|
|
import { Col } from './layout/col'
|
2022-07-16 19:21:24 +00:00
|
|
|
import { Modal } from './layout/modal'
|
|
|
|
import { Row } from './layout/row'
|
2022-07-10 18:05:44 +00:00
|
|
|
import { LoadingIndicator } from './loading-indicator'
|
|
|
|
import { BinaryOutcomeLabel, PseudoNumericOutcomeLabel } from './outcome-label'
|
2022-07-16 19:21:24 +00:00
|
|
|
import { Subtitle } from './subtitle'
|
|
|
|
import { Title } from './title'
|
2022-07-10 18:05:44 +00:00
|
|
|
|
|
|
|
export function LimitBets(props: {
|
|
|
|
contract: CPMMBinaryContract | PseudoNumericContract
|
|
|
|
bets: LimitBet[]
|
|
|
|
className?: string
|
|
|
|
}) {
|
2022-07-13 17:14:58 +00:00
|
|
|
const { contract, bets, className } = props
|
2022-08-21 17:57:00 +00:00
|
|
|
const user = useUser()
|
|
|
|
|
|
|
|
const yourBets = sortBy(
|
|
|
|
bets.filter((bet) => bet.userId === user?.id),
|
2022-07-10 18:05:44 +00:00
|
|
|
(bet) => -1 * bet.limitProb,
|
2022-08-21 17:57:00 +00:00
|
|
|
(bet) => bet.createdTime
|
2022-07-10 18:05:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
return (
|
2022-07-16 19:21:24 +00:00
|
|
|
<Col className={className}>
|
|
|
|
{yourBets.length === 0 && (
|
|
|
|
<OrderBookButton
|
|
|
|
className="self-end"
|
2022-08-21 17:57:00 +00:00
|
|
|
limitBets={bets}
|
2022-07-16 19:21:24 +00:00
|
|
|
contract={contract}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{yourBets.length > 0 && (
|
|
|
|
<Col
|
|
|
|
className={'mt-4 gap-2 overflow-hidden rounded bg-white px-4 py-3'}
|
|
|
|
>
|
|
|
|
<Row className="mt-2 mb-4 items-center justify-between">
|
2022-07-16 19:37:03 +00:00
|
|
|
<Subtitle className="!mt-0 !mb-0" text="Your orders" />
|
2022-07-16 19:21:24 +00:00
|
|
|
|
|
|
|
<OrderBookButton
|
|
|
|
className="self-end"
|
2022-08-21 17:57:00 +00:00
|
|
|
limitBets={bets}
|
2022-07-16 19:21:24 +00:00
|
|
|
contract={contract}
|
|
|
|
/>
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
<LimitOrderTable
|
|
|
|
limitBets={yourBets}
|
|
|
|
contract={contract}
|
|
|
|
isYou={true}
|
|
|
|
/>
|
|
|
|
</Col>
|
2022-07-11 03:07:42 +00:00
|
|
|
)}
|
2022-07-10 18:05:44 +00:00
|
|
|
</Col>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-07-15 02:27:00 +00:00
|
|
|
export function LimitOrderTable(props: {
|
2022-07-13 17:14:58 +00:00
|
|
|
limitBets: LimitBet[]
|
|
|
|
contract: CPMMBinaryContract | PseudoNumericContract
|
|
|
|
isYou: boolean
|
|
|
|
}) {
|
|
|
|
const { limitBets, contract, isYou } = props
|
2022-07-15 02:27:00 +00:00
|
|
|
const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
|
2022-07-13 17:14:58 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<table className="table-compact table w-full rounded text-gray-500">
|
2022-07-13 19:59:49 +00:00
|
|
|
<thead>
|
2022-07-19 21:57:32 +00:00
|
|
|
<tr>
|
|
|
|
{!isYou && <th></th>}
|
|
|
|
<th>Outcome</th>
|
|
|
|
<th>{isPseudoNumeric ? 'Value' : 'Prob'}</th>
|
|
|
|
<th>Amount</th>
|
|
|
|
{isYou && <th></th>}
|
|
|
|
</tr>
|
2022-07-13 19:59:49 +00:00
|
|
|
</thead>
|
2022-07-13 17:14:58 +00:00
|
|
|
<tbody>
|
|
|
|
{limitBets.map((bet) => (
|
|
|
|
<LimitBet key={bet.id} bet={bet} contract={contract} isYou={isYou} />
|
|
|
|
))}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-07-10 18:05:44 +00:00
|
|
|
function LimitBet(props: {
|
|
|
|
contract: CPMMBinaryContract | PseudoNumericContract
|
|
|
|
bet: LimitBet
|
2022-07-13 17:14:58 +00:00
|
|
|
isYou: boolean
|
2022-07-10 18:05:44 +00:00
|
|
|
}) {
|
2022-07-13 17:14:58 +00:00
|
|
|
const { contract, bet, isYou } = props
|
2022-07-10 18:05:44 +00:00
|
|
|
const { orderAmount, amount, limitProb, outcome } = bet
|
|
|
|
const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
|
|
|
|
|
|
|
|
const [isCancelling, setIsCancelling] = useState(false)
|
|
|
|
|
|
|
|
const onCancel = () => {
|
|
|
|
cancelBet({ betId: bet.id })
|
|
|
|
setIsCancelling(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<tr>
|
2022-07-13 17:14:58 +00:00
|
|
|
{!isYou && (
|
|
|
|
<td>
|
|
|
|
<Avatar
|
|
|
|
size={'sm'}
|
2022-09-14 08:33:59 +00:00
|
|
|
avatarUrl={bet.userAvatarUrl}
|
|
|
|
username={bet.userUsername}
|
2022-07-13 17:14:58 +00:00
|
|
|
/>
|
|
|
|
</td>
|
|
|
|
)}
|
2022-07-10 18:05:44 +00:00
|
|
|
<td>
|
|
|
|
<div className="pl-2">
|
|
|
|
{isPseudoNumeric ? (
|
|
|
|
<PseudoNumericOutcomeLabel outcome={outcome as 'YES' | 'NO'} />
|
|
|
|
) : (
|
|
|
|
<BinaryOutcomeLabel outcome={outcome as 'YES' | 'NO'} />
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
{isPseudoNumeric
|
|
|
|
? getFormattedMappedValue(contract)(limitProb)
|
|
|
|
: formatPercent(limitProb)}
|
|
|
|
</td>
|
2022-07-19 19:53:33 +00:00
|
|
|
<td>{formatMoney(orderAmount - amount)}</td>
|
2022-07-13 17:14:58 +00:00
|
|
|
{isYou && (
|
|
|
|
<td>
|
|
|
|
{isCancelling ? (
|
|
|
|
<LoadingIndicator />
|
|
|
|
) : (
|
|
|
|
<button
|
|
|
|
className="btn btn-xs btn-outline my-auto normal-case"
|
|
|
|
onClick={onCancel}
|
|
|
|
>
|
|
|
|
Cancel
|
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</td>
|
|
|
|
)}
|
2022-07-10 18:05:44 +00:00
|
|
|
</tr>
|
|
|
|
)
|
|
|
|
}
|
2022-07-16 19:21:24 +00:00
|
|
|
|
|
|
|
export function OrderBookButton(props: {
|
|
|
|
limitBets: LimitBet[]
|
|
|
|
contract: CPMMBinaryContract | PseudoNumericContract
|
|
|
|
className?: string
|
|
|
|
}) {
|
|
|
|
const { limitBets, contract, className } = props
|
|
|
|
const [open, setOpen] = useState(false)
|
|
|
|
|
2022-08-21 20:58:49 +00:00
|
|
|
const sortedBets = sortBy(
|
|
|
|
limitBets,
|
2022-08-21 17:57:00 +00:00
|
|
|
(bet) => -1 * bet.limitProb,
|
|
|
|
(bet) => bet.createdTime
|
|
|
|
)
|
2022-08-21 20:58:49 +00:00
|
|
|
|
|
|
|
const yesBets = sortedBets.filter((bet) => bet.outcome === 'YES')
|
2022-08-21 17:57:00 +00:00
|
|
|
const noBets = sortBy(
|
2022-08-21 20:58:49 +00:00
|
|
|
sortedBets.filter((bet) => bet.outcome === 'NO'),
|
2022-08-21 17:57:00 +00:00
|
|
|
(bet) => bet.limitProb,
|
|
|
|
(bet) => bet.createdTime
|
|
|
|
)
|
2022-07-16 19:21:24 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Button
|
|
|
|
className={className}
|
|
|
|
onClick={() => setOpen(true)}
|
|
|
|
size="xs"
|
|
|
|
color="blue"
|
|
|
|
>
|
2022-10-04 04:56:39 +00:00
|
|
|
Order book ({limitBets.length})
|
2022-07-16 19:21:24 +00:00
|
|
|
</Button>
|
|
|
|
|
|
|
|
<Modal open={open} setOpen={setOpen} size="lg">
|
|
|
|
<Col className="rounded bg-white p-4 py-6">
|
|
|
|
<Title className="!mt-0" text="Order book" />
|
2022-07-18 23:19:30 +00:00
|
|
|
<Row className="hidden items-start justify-start gap-2 md:flex">
|
2022-07-16 19:21:24 +00:00
|
|
|
<LimitOrderTable
|
|
|
|
limitBets={yesBets}
|
|
|
|
contract={contract}
|
|
|
|
isYou={false}
|
|
|
|
/>
|
|
|
|
<LimitOrderTable
|
|
|
|
limitBets={noBets}
|
|
|
|
contract={contract}
|
|
|
|
isYou={false}
|
|
|
|
/>
|
2022-07-18 23:19:30 +00:00
|
|
|
</Row>
|
|
|
|
<Col className="md:hidden">
|
|
|
|
<LimitOrderTable
|
2022-08-21 20:58:49 +00:00
|
|
|
limitBets={sortedBets}
|
2022-07-18 23:19:30 +00:00
|
|
|
contract={contract}
|
|
|
|
isYou={false}
|
|
|
|
/>
|
2022-07-16 19:21:24 +00:00
|
|
|
</Col>
|
|
|
|
</Col>
|
|
|
|
</Modal>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|