WIP: Try a toast that counts down before betting
There's some weird timing/React hook error that sometimes allows this thing to bet twice? IDK, my brain is fried trying to understand it. Also, progress bar that scrolls down might be cooler than a number countdown? See https://www.npmjs.com/package/react-toastify, although the API for that is more unwieldy/less Tailwind-friendly?
This commit is contained in:
parent
139902f15e
commit
583a797a61
|
@ -33,6 +33,10 @@ import { AvatarDetails, MiscDetails } from './contract-details'
|
||||||
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
||||||
import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon'
|
import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon'
|
||||||
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
import { CheckIcon, XIcon } from '@heroicons/react/solid'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||||
|
|
||||||
// Return a number from 0 to 1 for this contract
|
// Return a number from 0 to 1 for this contract
|
||||||
// Resolved contracts are set to 1, for coloring purposes (even if NO)
|
// Resolved contracts are set to 1, for coloring purposes (even if NO)
|
||||||
|
@ -93,6 +97,53 @@ export function ContractCard(props: {
|
||||||
const color = getColor(contract)
|
const color = getColor(contract)
|
||||||
const marketClosed = (contract.closeTime || Infinity) < Date.now()
|
const marketClosed = (contract.closeTime || Infinity) < Date.now()
|
||||||
|
|
||||||
|
// TODO: switch to useContract after you place a bet on it
|
||||||
|
|
||||||
|
function betToast(outcome: string) {
|
||||||
|
let canceled = false
|
||||||
|
const toastId = toast.custom(
|
||||||
|
<BetToast
|
||||||
|
outcome={outcome}
|
||||||
|
seconds={3}
|
||||||
|
onToastFinish={onToastFinish}
|
||||||
|
onToastCancel={onToastCancel}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
duration: 3000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function onToastCancel() {
|
||||||
|
toast.remove(toastId)
|
||||||
|
canceled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function onToastFinish() {
|
||||||
|
if (canceled) return
|
||||||
|
console.log('Finishing toast')
|
||||||
|
toast.remove(toastId)
|
||||||
|
placeBet({
|
||||||
|
amount: 10,
|
||||||
|
outcome,
|
||||||
|
contractId: contract.id,
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
// Success
|
||||||
|
console.log('placed bet. Result:', r)
|
||||||
|
toast.success('Bet placed!', { duration: 1000 })
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
// Failure
|
||||||
|
if (e instanceof APIError) {
|
||||||
|
toast.error(e.toString(), { duration: 1000 })
|
||||||
|
} else {
|
||||||
|
console.error(e)
|
||||||
|
toast.error('Could not place bet')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Col
|
<Col
|
||||||
|
@ -141,9 +192,7 @@ export function ContractCard(props: {
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="peer absolute top-0 left-0 right-0 h-[50%]"
|
className="peer absolute top-0 left-0 right-0 h-[50%]"
|
||||||
onClick={() => {
|
onClick={() => betToast('YES')}
|
||||||
console.log('success')
|
|
||||||
}}
|
|
||||||
></div>
|
></div>
|
||||||
<div className="my-1 text-center text-xs text-transparent peer-hover:text-gray-400">
|
<div className="my-1 text-center text-xs text-transparent peer-hover:text-gray-400">
|
||||||
{formatMoney(20)}
|
{formatMoney(20)}
|
||||||
|
@ -188,7 +237,7 @@ export function ContractCard(props: {
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="peer absolute bottom-0 left-0 right-0 h-[50%]"
|
className="peer absolute bottom-0 left-0 right-0 h-[50%]"
|
||||||
onClick={() => {}}
|
onClick={() => betToast('NO')}
|
||||||
></div>
|
></div>
|
||||||
{contract.createdTime % 3 == 2 ? (
|
{contract.createdTime % 3 == 2 ? (
|
||||||
<TriangleDownFillIcon
|
<TriangleDownFillIcon
|
||||||
|
@ -346,3 +395,64 @@ export function NumericResolutionOrExpectation(props: {
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BetToast(props: {
|
||||||
|
outcome: string
|
||||||
|
seconds: number
|
||||||
|
onToastFinish: () => void
|
||||||
|
onToastCancel: () => void
|
||||||
|
}) {
|
||||||
|
const { outcome, seconds, onToastFinish, onToastCancel } = props
|
||||||
|
|
||||||
|
// Track the number of seconds left, starting with durationMs
|
||||||
|
const [secondsLeft, setSecondsLeft] = useState(seconds)
|
||||||
|
console.log('renderings using', secondsLeft)
|
||||||
|
|
||||||
|
// Update the secondsLeft state every second
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setSecondsLeft((seconds) => seconds - 1)
|
||||||
|
}, 1000)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (secondsLeft <= 0) {
|
||||||
|
console.log('finishing')
|
||||||
|
onToastFinish()
|
||||||
|
// return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5">
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex w-0 flex-1 justify-between">
|
||||||
|
<p className="w-0 flex-1 text-sm font-medium text-gray-900">
|
||||||
|
Betting M$10 on {outcome} in {secondsLeft}s
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onToastCancel}
|
||||||
|
className="ml-3 flex-shrink-0 rounded-md bg-white text-sm font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* <div className="ml-4 flex flex-shrink-0">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||||
|
onClick={() => {
|
||||||
|
// TODO
|
||||||
|
// setShow(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user