Submit bets to firestore
This commit is contained in:
parent
b8b5868eb8
commit
77ce27f45f
|
@ -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
22
web/lib/firebase/bets.ts
Normal 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)
|
||||||
|
}
|
|
@ -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
14
web/package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user