manifold/web/components/answers/answers-panel.tsx

170 lines
4.7 KiB
TypeScript
Raw Normal View History

2022-02-20 22:25:58 +00:00
import _ from 'lodash'
import { useLayoutEffect, useState } from 'react'
import { DPM, FreeResponse, FullContract } from 'common/contract'
2022-02-20 22:25:58 +00:00
import { Col } from '../layout/col'
import { useUser } from 'web/hooks/use-user'
import { getDpmOutcomeProbability } from 'common/calculate-dpm'
import { useAnswers } from 'web/hooks/use-answers'
import { tradingAllowed } from 'web/lib/firebase/contracts'
2022-02-20 22:25:58 +00:00
import { AnswerItem } from './answer-item'
import { CreateAnswerPanel } from './create-answer-panel'
import { AnswerResolvePanel } from './answer-resolve-panel'
import { Spacer } from '../layout/spacer'
import { FeedItems } from '../feed/feed-items'
import { ActivityItem } from '../feed/activity-items'
import { User } from 'common/user'
import { getOutcomeProbability } from 'common/calculate'
import { Answer } from 'common/answer'
2022-02-20 22:25:58 +00:00
Cfmm (#64) * cpmm initial commit: common logic, cloud functions * remove unnecessary property * contract type * rename 'calculate.ts' => 'calculate-dpm.ts' * rename dpm calculations * use focus hook * mechanism-agnostic calculations * bet panel: use new calculations * use new calculations * delete markets cloud function * use correct contract type in scripts / functions * calculate fixed payouts; bets list calculations * new bet: use calculateCpmmPurchase * getOutcomeProbabilityAfterBet * use deductFixedFees * fix auto-refactor * fix antes * separate logic to payouts-dpm, payouts-fixed * liquidity provision tracking * remove comment * liquidity label * create liquidity provision even if no ante bet * liquidity fee * use all bets for getFixedCancelPayouts * updateUserBalance: allow negative balances * store initialProbability in contracts * turn on liquidity fee; turn off creator fee * Include time param in tweet url, so image preview is re-fetched * share redemption * cpmm ContractBetsTable display * formatMoney: handle minus zero * filter out redemption bets * track fees on contract and bets; change fee schedule for cpmm markets; only pay out creator fees at resolution * small fixes * small fixes * Redeem shares pays back loans first * Fix initial point on graph * calculateCpmmPurchase: deduct creator fee * Filter out redemption bets from feed * set env to dev for user-testing purposes * creator fees messaging * new cfmm: k = y^(1-p) * n^p * addCpmmLiquidity * correct price function * enable fees * handle overflow * liquidity provision tracking * raise fees * Fix merge error * fix dpm free response payout for single outcome * Fix DPM payout calculation * Remove hardcoding as dev Co-authored-by: James Grugett <jahooma@gmail.com>
2022-03-15 22:27:51 +00:00
export function AnswersPanel(props: {
contract: FullContract<DPM, FreeResponse>
}) {
2022-02-20 22:25:58 +00:00
const { contract } = props
2022-02-22 20:12:49 +00:00
const { creatorId, resolution, resolutions, totalBets } = contract
2022-02-20 22:25:58 +00:00
2022-04-11 21:13:26 +00:00
const answers = useAnswers(contract.id) ?? contract.answers
const [winningAnswers, losingAnswers] = _.partition(
2022-02-22 20:12:49 +00:00
answers.filter(
(answer) => answer.id !== '0' && totalBets[answer.id] > 0.000000001
),
2022-02-20 22:25:58 +00:00
(answer) =>
answer.id === resolution || (resolutions && resolutions[answer.id])
)
const sortedAnswers = [
..._.sortBy(winningAnswers, (answer) =>
resolutions ? -1 * resolutions[answer.id] : 0
),
..._.sortBy(
resolution ? [] : losingAnswers,
Cfmm (#64) * cpmm initial commit: common logic, cloud functions * remove unnecessary property * contract type * rename 'calculate.ts' => 'calculate-dpm.ts' * rename dpm calculations * use focus hook * mechanism-agnostic calculations * bet panel: use new calculations * use new calculations * delete markets cloud function * use correct contract type in scripts / functions * calculate fixed payouts; bets list calculations * new bet: use calculateCpmmPurchase * getOutcomeProbabilityAfterBet * use deductFixedFees * fix auto-refactor * fix antes * separate logic to payouts-dpm, payouts-fixed * liquidity provision tracking * remove comment * liquidity label * create liquidity provision even if no ante bet * liquidity fee * use all bets for getFixedCancelPayouts * updateUserBalance: allow negative balances * store initialProbability in contracts * turn on liquidity fee; turn off creator fee * Include time param in tweet url, so image preview is re-fetched * share redemption * cpmm ContractBetsTable display * formatMoney: handle minus zero * filter out redemption bets * track fees on contract and bets; change fee schedule for cpmm markets; only pay out creator fees at resolution * small fixes * small fixes * Redeem shares pays back loans first * Fix initial point on graph * calculateCpmmPurchase: deduct creator fee * Filter out redemption bets from feed * set env to dev for user-testing purposes * creator fees messaging * new cfmm: k = y^(1-p) * n^p * addCpmmLiquidity * correct price function * enable fees * handle overflow * liquidity provision tracking * raise fees * Fix merge error * fix dpm free response payout for single outcome * Fix DPM payout calculation * Remove hardcoding as dev Co-authored-by: James Grugett <jahooma@gmail.com>
2022-03-15 22:27:51 +00:00
(answer) => -1 * getDpmOutcomeProbability(contract.totalShares, answer.id)
2022-02-20 22:25:58 +00:00
),
]
const user = useUser()
const [resolveOption, setResolveOption] = useState<
'CHOOSE' | 'CHOOSE_MULTIPLE' | 'CANCEL' | undefined
>()
const [chosenAnswers, setChosenAnswers] = useState<{
[answerId: string]: number
}>({})
const chosenTotal = _.sum(Object.values(chosenAnswers))
const answerItems = getAnswerItems(
contract,
losingAnswers.length > 0 ? losingAnswers : sortedAnswers,
user
)
2022-02-20 22:25:58 +00:00
const onChoose = (answerId: string, prob: number) => {
if (resolveOption === 'CHOOSE') {
setChosenAnswers({ [answerId]: prob })
} else {
setChosenAnswers((chosenAnswers) => {
return {
...chosenAnswers,
[answerId]: prob,
}
})
}
}
const onDeselect = (answerId: string) => {
setChosenAnswers((chosenAnswers) => {
const newChosenAnswers = { ...chosenAnswers }
delete newChosenAnswers[answerId]
return newChosenAnswers
})
}
useLayoutEffect(() => {
setChosenAnswers({})
}, [resolveOption])
const showChoice = resolution
? undefined
: resolveOption === 'CHOOSE'
? 'radio'
: resolveOption === 'CHOOSE_MULTIPLE'
? 'checkbox'
: undefined
return (
<Col className="gap-3">
{(resolveOption || resolution) &&
sortedAnswers.map((answer) => (
<AnswerItem
key={answer.id}
answer={answer}
contract={contract}
showChoice={showChoice}
chosenProb={chosenAnswers[answer.id]}
totalChosenProb={chosenTotal}
onChoose={onChoose}
onDeselect={onDeselect}
/>
))}
2022-02-20 22:25:58 +00:00
{!resolveOption && (
<FeedItems
contract={contract}
items={answerItems}
className={'pr-2 md:pr-0'}
betRowClassName={''}
/>
)}
{answers.length <= 1 && (
<div className="pb-4 text-gray-500">No answers yet...</div>
)}
{tradingAllowed(contract) &&
(!resolveOption || resolveOption === 'CANCEL') && (
<CreateAnswerPanel contract={contract} />
)}
2022-02-20 22:25:58 +00:00
{user?.id === creatorId && !resolution && (
<>
<Spacer h={2} />
<AnswerResolvePanel
contract={contract}
resolveOption={resolveOption}
setResolveOption={setResolveOption}
chosenAnswers={chosenAnswers}
/>
</>
2022-02-20 22:25:58 +00:00
)}
</Col>
)
}
function getAnswerItems(
contract: FullContract<DPM, FreeResponse>,
answers: Answer[],
user: User | undefined | null
) {
let outcomes = _.uniq(
answers.map((answer) => answer.number.toString())
).filter((outcome) => getOutcomeProbability(contract, outcome) > 0.0001)
outcomes = _.sortBy(outcomes, (outcome) =>
getOutcomeProbability(contract, outcome)
).reverse()
return outcomes
.map((outcome) => {
const answer = answers.find((answer) => answer.id === outcome) as Answer
//unnecessary
return {
id: outcome,
type: 'answer' as const,
contract,
answer,
items: [] as ActivityItem[],
user,
}
})
.filter((group) => group.answer)
}