Use modal for sell shares on desktop.
This commit is contained in:
parent
e1d265f7aa
commit
d772a20d5e
|
@ -224,7 +224,6 @@ export function SellAmountInput(props: {
|
||||||
|
|
||||||
// Check for errors.
|
// Check for errors.
|
||||||
if (amount !== undefined) {
|
if (amount !== undefined) {
|
||||||
console.log(shares, amount)
|
|
||||||
if (amount > shares) {
|
if (amount > shares) {
|
||||||
setError(`Maximum ${formatWithCommas(Math.floor(shares))} shares`)
|
setError(`Maximum ${formatWithCommas(Math.floor(shares))} shares`)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -32,10 +32,88 @@ import {
|
||||||
calculateCpmmSale,
|
calculateCpmmSale,
|
||||||
getCpmmProbability,
|
getCpmmProbability,
|
||||||
} from '../../common/calculate-cpmm'
|
} from '../../common/calculate-cpmm'
|
||||||
|
import { Modal } from './layout/modal'
|
||||||
|
|
||||||
export function BetPanel(props: {
|
export function BetPanel(props: {
|
||||||
contract: FullContract<DPM | CPMM, Binary>
|
contract: FullContract<DPM | CPMM, Binary>
|
||||||
className?: string
|
className?: string
|
||||||
|
}) {
|
||||||
|
const { contract, className } = props
|
||||||
|
const { mechanism } = contract
|
||||||
|
|
||||||
|
const user = useUser()
|
||||||
|
const userBets = useUserContractBets(user?.id, contract.id)
|
||||||
|
|
||||||
|
const [showSellModal, setShowSellModal] = useState(false)
|
||||||
|
|
||||||
|
const { yesShares, noShares } = useSaveShares(contract, userBets)
|
||||||
|
|
||||||
|
const shares = yesShares || noShares
|
||||||
|
const sharesOutcome = yesShares ? 'YES' : noShares ? 'NO' : undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col className={className}>
|
||||||
|
{sharesOutcome && user && mechanism === 'cpmm-1' && (
|
||||||
|
<Col className="rounded-t-md bg-gray-100 px-6 py-6">
|
||||||
|
<Row className="items-center justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
You have {formatWithCommas(Math.floor(shares))}{' '}
|
||||||
|
<OutcomeLabel outcome={sharesOutcome} /> shares
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn btn-sm"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'white',
|
||||||
|
border: '2px solid',
|
||||||
|
color: '#3D4451',
|
||||||
|
}}
|
||||||
|
onClick={() => setShowSellModal(true)}
|
||||||
|
>
|
||||||
|
Sell
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showSellModal && (
|
||||||
|
<SellSharesModal
|
||||||
|
contract={contract as FullContract<CPMM, Binary>}
|
||||||
|
user={user}
|
||||||
|
userBets={userBets ?? []}
|
||||||
|
shares={shares}
|
||||||
|
sharesOutcome={sharesOutcome}
|
||||||
|
setOpen={setShowSellModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Col
|
||||||
|
className={clsx(
|
||||||
|
'rounded-b-md bg-white px-8 py-6',
|
||||||
|
!sharesOutcome && 'rounded-t-md',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Title className={clsx('!mt-0')} text="Place a trade" />
|
||||||
|
|
||||||
|
<BuyPanel contract={contract} user={user} userBets={userBets ?? []} />
|
||||||
|
|
||||||
|
{user === null && (
|
||||||
|
<button
|
||||||
|
className="btn flex-1 whitespace-nowrap border-none bg-gradient-to-r from-teal-500 to-green-500 px-10 text-lg font-medium normal-case hover:from-teal-600 hover:to-green-600"
|
||||||
|
onClick={firebaseLogin}
|
||||||
|
>
|
||||||
|
Sign in to trade!
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BetPanelSwitcher(props: {
|
||||||
|
contract: FullContract<DPM | CPMM, Binary>
|
||||||
|
className?: string
|
||||||
title?: string // Set if BetPanel is on a feed modal
|
title?: string // Set if BetPanel is on a feed modal
|
||||||
selected?: 'YES' | 'NO'
|
selected?: 'YES' | 'NO'
|
||||||
onBetSuccess?: () => void
|
onBetSuccess?: () => void
|
||||||
|
@ -451,3 +529,36 @@ const useSaveShares = (
|
||||||
if (userBets) return { yesShares, noShares }
|
if (userBets) return { yesShares, noShares }
|
||||||
return savedShares ?? { yesShares: 0, noShares: 0 }
|
return savedShares ?? { yesShares: 0, noShares: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SellSharesModal(props: {
|
||||||
|
contract: FullContract<CPMM, Binary>
|
||||||
|
userBets: Bet[]
|
||||||
|
shares: number
|
||||||
|
sharesOutcome: 'YES' | 'NO'
|
||||||
|
user: User
|
||||||
|
setOpen: (open: boolean) => void
|
||||||
|
}) {
|
||||||
|
const { contract, shares, sharesOutcome, userBets, user, setOpen } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={true} setOpen={setOpen}>
|
||||||
|
<Col className="rounded-md bg-white px-8 py-6">
|
||||||
|
<Title className="!mt-0" text={'Sell shares'} />
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
You have {formatWithCommas(Math.floor(shares))}{' '}
|
||||||
|
<OutcomeLabel outcome={sharesOutcome} /> shares
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SellPanel
|
||||||
|
contract={contract}
|
||||||
|
shares={shares}
|
||||||
|
sharesOutcome={sharesOutcome}
|
||||||
|
user={user}
|
||||||
|
userBets={userBets ?? []}
|
||||||
|
onSellSuccess={() => setOpen(false)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { Fragment, useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Dialog, Transition } from '@headlessui/react'
|
|
||||||
|
|
||||||
import { BetPanel } from './bet-panel'
|
import { BetPanelSwitcher } from './bet-panel'
|
||||||
import { Row } from './layout/row'
|
import { Row } from './layout/row'
|
||||||
import { YesNoSelector } from './yes-no-selector'
|
import { YesNoSelector } from './yes-no-selector'
|
||||||
import { Binary, CPMM, DPM, FullContract } from '../../common/contract'
|
import { Binary, CPMM, DPM, FullContract } from '../../common/contract'
|
||||||
|
import { Modal } from './layout/modal'
|
||||||
|
|
||||||
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
||||||
export default function BetRow(props: {
|
export default function BetRow(props: {
|
||||||
|
@ -27,7 +27,7 @@ export default function BetRow(props: {
|
||||||
Place a trade
|
Place a trade
|
||||||
</div>
|
</div>
|
||||||
<YesNoSelector
|
<YesNoSelector
|
||||||
btnClassName="btn-sm w-20"
|
btnClassName="btn-sm w-24"
|
||||||
onSelect={(choice) => {
|
onSelect={(choice) => {
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
setBetChoice(choice)
|
setBetChoice(choice)
|
||||||
|
@ -35,7 +35,7 @@ export default function BetRow(props: {
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<Modal open={open} setOpen={setOpen}>
|
<Modal open={open} setOpen={setOpen}>
|
||||||
<BetPanel
|
<BetPanelSwitcher
|
||||||
contract={props.contract}
|
contract={props.contract}
|
||||||
title={props.contract.question}
|
title={props.contract.question}
|
||||||
selected={betChoice}
|
selected={betChoice}
|
||||||
|
@ -46,57 +46,3 @@ export default function BetRow(props: {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From https://tailwindui.com/components/application-ui/overlays/modals
|
|
||||||
export function Modal(props: {
|
|
||||||
children: React.ReactNode
|
|
||||||
open: boolean
|
|
||||||
setOpen: (open: boolean) => void
|
|
||||||
}) {
|
|
||||||
const { children, open, setOpen } = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Transition.Root show={open} as={Fragment}>
|
|
||||||
<Dialog
|
|
||||||
as="div"
|
|
||||||
className="fixed inset-0 z-50 overflow-y-auto"
|
|
||||||
onClose={setOpen}
|
|
||||||
>
|
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
{/* This element is to trick the browser into centering the modal contents. */}
|
|
||||||
<span
|
|
||||||
className="hidden sm:inline-block sm:h-screen sm:align-middle"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
​
|
|
||||||
</span>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
>
|
|
||||||
<div className="inline-block transform overflow-hidden text-left align-bottom transition-all sm:my-8 sm:w-full sm:max-w-md sm:p-6 sm:align-middle">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ import { DateTimeTooltip } from '../datetime-tooltip'
|
||||||
import { Bet } from '../../lib/firebase/bets'
|
import { Bet } from '../../lib/firebase/bets'
|
||||||
import { JoinSpans } from '../join-spans'
|
import { JoinSpans } from '../join-spans'
|
||||||
import { fromNow } from '../../lib/util/time'
|
import { fromNow } from '../../lib/util/time'
|
||||||
import BetRow, { Modal } from '../bet-row'
|
import BetRow from '../bet-row'
|
||||||
import { parseTags } from '../../../common/util/parse'
|
import { parseTags } from '../../../common/util/parse'
|
||||||
import { Avatar } from '../avatar'
|
import { Avatar } from '../avatar'
|
||||||
import { useAdmin } from '../../hooks/use-admin'
|
import { useAdmin } from '../../hooks/use-admin'
|
||||||
|
@ -48,6 +48,7 @@ import { getDpmOutcomeProbability } from '../../../common/calculate-dpm'
|
||||||
import { AnswerBetPanel } from '../answers/answer-bet-panel'
|
import { AnswerBetPanel } from '../answers/answer-bet-panel'
|
||||||
import { useSaveSeenContract } from '../../hooks/use-seen-contracts'
|
import { useSaveSeenContract } from '../../hooks/use-seen-contracts'
|
||||||
import { User } from '../../../common/user'
|
import { User } from '../../../common/user'
|
||||||
|
import { Modal } from '../layout/modal'
|
||||||
|
|
||||||
export function FeedItems(props: {
|
export function FeedItems(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
|
56
web/components/layout/modal.tsx
Normal file
56
web/components/layout/modal.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { Fragment } from 'react'
|
||||||
|
import { Dialog, Transition } from '@headlessui/react'
|
||||||
|
|
||||||
|
// From https://tailwindui.com/components/application-ui/overlays/modals
|
||||||
|
export function Modal(props: {
|
||||||
|
children: React.ReactNode
|
||||||
|
open: boolean
|
||||||
|
setOpen: (open: boolean) => void
|
||||||
|
}) {
|
||||||
|
const { children, open, setOpen } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition.Root show={open} as={Fragment}>
|
||||||
|
<Dialog
|
||||||
|
as="div"
|
||||||
|
className="fixed inset-0 z-50 overflow-y-auto"
|
||||||
|
onClose={setOpen}
|
||||||
|
>
|
||||||
|
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
{/* This element is to trick the browser into centering the modal contents. */}
|
||||||
|
<span
|
||||||
|
className="hidden sm:inline-block sm:h-screen sm:align-middle"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
​
|
||||||
|
</span>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
>
|
||||||
|
<div className="inline-block transform overflow-hidden text-left align-bottom transition-all sm:my-8 sm:w-full sm:max-w-md sm:p-6 sm:align-middle">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition.Root>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user