Submit bets to firestore

This commit is contained in:
jahooma 2021-12-10 11:14:05 -06:00
parent b8b5868eb8
commit 77ce27f45f
5 changed files with 101 additions and 23 deletions

View File

@ -1,4 +1,7 @@
import clsx from 'clsx'
import React, { useState } from 'react' import React, { useState } from 'react'
import { useUser } from '../hooks/use-user'
import { Bet, saveBet } from '../lib/firebase/bets'
import { Contract } from '../lib/firebase/contracts' import { Contract } from '../lib/firebase/contracts'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Row } from './layout/row' import { Row } from './layout/row'
@ -8,14 +11,51 @@ import { YesNoSelector } from './yes-no-selector'
export function BetPanel(props: { contract: Contract; className?: string }) { export function BetPanel(props: { contract: Contract; className?: string }) {
const { contract, className } = props const { contract, className } = props
const user = useUser()
const [betChoice, setBetChoice] = useState<'YES' | 'NO'>('YES') const [betChoice, setBetChoice] = useState<'YES' | 'NO'>('YES')
const [betAmount, setBetAmount] = useState<number | undefined>(undefined) const [betAmount, setBetAmount] = useState<number | undefined>(undefined)
const [isSubmitting, setIsSubmitting] = useState(false)
const [wasSubmitted, setWasSubmitted] = useState(false)
function onBetChange(str: string) { function onBetChange(str: string) {
const amount = parseInt(str) const amount = parseInt(str)
setBetAmount(isNaN(amount) ? undefined : amount) setBetAmount(isNaN(amount) ? undefined : amount)
} }
async function submitBet() {
if (!user || !betAmount) return
const now = Date.now()
const bet: Bet = {
id: `${now}-${user.id}`,
userId: user.id,
contractId: contract.id,
createdTime: now,
outcome: betChoice,
amount: betAmount,
// Placeholder.
dpmWeight: betAmount,
}
setIsSubmitting(true)
await saveBet(bet)
setIsSubmitting(false)
setWasSubmitted(true)
}
function newBet() {
setBetAmount(undefined)
setWasSubmitted(false)
}
const betDisabled = isSubmitting || wasSubmitted
return ( return (
<Col className={'bg-gray-600 p-6 rounded ' + className}> <Col className={'bg-gray-600 p-6 rounded ' + className}>
<div className="p-2 font-medium">Pick outcome</div> <div className="p-2 font-medium">Pick outcome</div>
@ -47,9 +87,7 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
<Spacer h={4} /> <Spacer h={4} />
<div className="p-2 font-medium">Average price</div> <div className="p-2 font-medium">Average price</div>
<div className="px-2"> <div className="px-2">{betChoice === 'YES' ? 0.57 : 0.43} points</div>
{betChoice === 'YES' ? 0.57 : 0.43} points
</div>
<Spacer h={2} /> <Spacer h={2} />
@ -60,7 +98,29 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
<Spacer h={6} /> <Spacer h={6} />
<button className="btn btn-primary">Place bet</button> <button
className={clsx(
'btn',
betDisabled ? 'btn-disabled' : 'btn-primary'
)}
onClick={betDisabled ? undefined : submitBet}
>
Place bet
</button>
{wasSubmitted && (
<Col>
<Spacer h={4} />
<div>Bet submitted!</div>
<Spacer h={4} />
<button className="btn btn-primary btn-xs" onClick={newBet}>
New bet
</button>
</Col>
)}
</> </>
)} )}
</Col> </Col>

22
web/lib/firebase/bets.ts Normal file
View File

@ -0,0 +1,22 @@
import { doc, setDoc } from 'firebase/firestore'
import { db } from './init'
export type Bet = {
id: string
userId: string
contractId: string
amount: number // Amount of USD bid
outcome: 'YES' | 'NO' // Chosen outcome
// Calculate and replace these on server?
createdTime: number
dpmWeight: number // Dynamic Parimutuel weight
}
// Push bet to Firestore
// TODO: Should bets be subcollections under its contract?
export async function saveBet(bet: Bet) {
const docRef = doc(db, 'contracts', bet.contractId, 'bets', bet.id)
await setDoc(docRef, bet)
}

View File

@ -32,18 +32,6 @@ export type Contract = {
resolution?: 'YES' | 'NO' | 'CANCEL' // Chosen by creator; must be one of outcomes resolution?: 'YES' | 'NO' | 'CANCEL' // Chosen by creator; must be one of outcomes
} }
export type Bet = {
id: string
userId: string
contractId: string
size: number // Amount of USD bid
outcome: 'YES' | 'NO' // Chosen outcome
createdTime: number
dpmWeight: number // Dynamic Parimutuel weight
}
const db = getFirestore(app) const db = getFirestore(app)
const contractCollection = collection(db, 'contracts') const contractCollection = collection(db, 'contracts')
@ -79,10 +67,3 @@ export function listenForContract(
setContract(contractSnap.data() as Contract) setContract(contractSnap.data() as Contract)
}) })
} }
// Push bet to Firestore
// TODO: Should bets be subcollections under its contract?
export async function setBet(bet: Bet) {
const docRef = doc(db, 'bets', bet.id)
await setDoc(docRef, bet)
}

14
web/package-lock.json generated
View File

@ -9,6 +9,7 @@
"@headlessui/react": "1.4.2", "@headlessui/react": "1.4.2",
"@heroicons/react": "1.0.5", "@heroicons/react": "1.0.5",
"chart.js": "3.6.1", "chart.js": "3.6.1",
"clsx": "^1.1.1",
"daisyui": "1.16.2", "daisyui": "1.16.2",
"firebase": "9.6.0", "firebase": "9.6.0",
"next": "12.0.4", "next": "12.0.4",
@ -2879,6 +2880,14 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"node_modules/clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
"engines": {
"node": ">=6"
}
},
"node_modules/color": { "node_modules/color": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
@ -9908,6 +9917,11 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
},
"color": { "color": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",

View File

@ -12,6 +12,7 @@
"@headlessui/react": "1.4.2", "@headlessui/react": "1.4.2",
"@heroicons/react": "1.0.5", "@heroicons/react": "1.0.5",
"chart.js": "3.6.1", "chart.js": "3.6.1",
"clsx": "^1.1.1",
"daisyui": "1.16.2", "daisyui": "1.16.2",
"firebase": "9.6.0", "firebase": "9.6.0",
"next": "12.0.4", "next": "12.0.4",