Merge branch 'manifoldmarkets:main' into main

This commit is contained in:
marsteralex 2022-09-09 10:27:49 -07:00 committed by GitHub
commit 6e77b4a987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 176 additions and 81 deletions

View File

@ -13,7 +13,10 @@ export const getRedeemableAmount = (bets: RedeemableBet[]) => {
const yesShares = sumBy(yesBets, (b) => b.shares)
const noShares = sumBy(noBets, (b) => b.shares)
const shares = Math.max(Math.min(yesShares, noShares), 0)
const soldFrac = shares > 0 ? Math.min(yesShares, noShares) / shares : 0
const soldFrac =
shares > 0
? Math.min(yesShares, noShares) / Math.max(yesShares, noShares)
: 0
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
const loanPayment = loanAmount * soldFrac
const netAmount = shares - loanPayment

View File

@ -60,23 +60,27 @@ Parameters:
Requires no authorization.
### `GET /v0/groups/[slug]`
### `GET /v0/group/[slug]`
Gets a group by its slug.
Requires no authorization.
Note: group is singular in the URL.
### `GET /v0/group/by-id/[id]`
Gets a group by its unique ID.
Requires no authorization.
Note: group is singular in the URL.
### `GET /v0/group/by-id/[id]/markets`
Gets a group's markets by its unique ID.
Requires no authorization.
Note: group is singular in the URL.
### `GET /v0/markets`

View File

@ -122,6 +122,18 @@ export function BuyAmountInput(props: {
}
}
const parseRaw = (x: number) => {
if (x <= 100) return x
if (x <= 130) return 100 + (x - 100) * 5
return 250 + (x - 130) * 10
}
const getRaw = (x: number) => {
if (x <= 100) return x
if (x <= 250) return 100 + (x - 100) / 5
return 130 + (x - 250) / 10
}
return (
<>
<AmountInput
@ -138,10 +150,10 @@ export function BuyAmountInput(props: {
<input
type="range"
min="0"
max="200"
value={amount ?? 0}
onChange={(e) => onAmountChange(parseInt(e.target.value))}
className="range range-lg z-40 mb-2 xl:hidden"
max="205"
value={getRaw(amount ?? 0)}
onChange={(e) => onAmountChange(parseRaw(parseInt(e.target.value)))}
className="range range-lg only-thumb z-40 mb-2 xl:hidden"
step="5"
/>
)}

View File

@ -26,7 +26,7 @@ import { Bet } from 'common/bet'
import { track } from 'web/lib/service/analytics'
import { BetSignUpPrompt } from '../sign-up-prompt'
import { isIOS } from 'web/lib/util/device'
import { AlertBox } from '../alert-box'
import { WarningConfirmationButton } from '../warning-confirmation-button'
export function AnswerBetPanel(props: {
answer: Answer
@ -116,6 +116,15 @@ export function AnswerBetPanel(props: {
const bankrollFraction = (betAmount ?? 0) / (user?.balance ?? 1e9)
const warning =
(betAmount ?? 0) > 10 && bankrollFraction >= 0.5 && bankrollFraction <= 1
? `You might not want to spend ${formatPercent(
bankrollFraction
)} of your balance on a single bet. \n\nCurrent balance: ${formatMoney(
user?.balance ?? 0
)}`
: undefined
return (
<Col className={clsx('px-2 pb-2 pt-4 sm:pt-0', className)}>
<Row className="items-center justify-between self-stretch">
@ -148,21 +157,6 @@ export function AnswerBetPanel(props: {
showSliderOnMobile
/>
{(betAmount ?? 0) > 10 &&
bankrollFraction >= 0.5 &&
bankrollFraction <= 1 ? (
<AlertBox
title="Whoa, there!"
text={`You might not want to spend ${formatPercent(
bankrollFraction
)} of your balance on a single bet. \n\nCurrent balance: ${formatMoney(
user?.balance ?? 0
)}`}
/>
) : (
''
)}
<Col className="mt-3 w-full gap-3">
<Row className="items-center justify-between text-sm">
<div className="text-gray-500">Probability</div>
@ -198,16 +192,17 @@ export function AnswerBetPanel(props: {
<Spacer h={6} />
{user ? (
<button
className={clsx(
<WarningConfirmationButton
warning={warning}
onSubmit={submitBet}
isSubmitting={isSubmitting}
disabled={!!betDisabled}
openModalButtonClass={clsx(
'btn self-stretch',
betDisabled ? 'btn-disabled' : 'btn-primary',
isSubmitting ? 'loading' : ''
)}
onClick={betDisabled ? undefined : submitBet}
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
/>
) : (
<BetSignUpPrompt />
)}

View File

@ -40,7 +40,8 @@ import { LimitBets } from './limit-bets'
import { PillButton } from './buttons/pill-button'
import { YesNoSelector } from './yes-no-selector'
import { PlayMoneyDisclaimer } from './play-money-disclaimer'
import { AlertBox } from './alert-box'
import { isAndroid, isIOS } from 'web/lib/util/device'
import { WarningConfirmationButton } from './warning-confirmation-button'
export function BetPanel(props: {
contract: CPMMBinaryContract | PseudoNumericContract
@ -184,17 +185,13 @@ function BuyPanel(props: {
const [inputRef, focusAmountInput] = useFocus()
// useEffect(() => {
// if (selected) {
// if (isIOS()) window.scrollTo(0, window.scrollY + 200)
// focusAmountInput()
// }
// }, [selected, focusAmountInput])
function onBetChoice(choice: 'YES' | 'NO') {
setOutcome(choice)
setWasSubmitted(false)
focusAmountInput()
if (!isIOS() && !isAndroid()) {
focusAmountInput()
}
}
function onBetChange(newAmount: number | undefined) {
@ -274,25 +271,15 @@ function BuyPanel(props: {
const bankrollFraction = (betAmount ?? 0) / (user?.balance ?? 1e9)
const warning =
(betAmount ?? 0) > 10 &&
bankrollFraction >= 0.5 &&
bankrollFraction <= 1 ? (
<AlertBox
title="Whoa, there!"
text={`You might not want to spend ${formatPercent(
(betAmount ?? 0) > 10 && bankrollFraction >= 0.5 && bankrollFraction <= 1
? `You might not want to spend ${formatPercent(
bankrollFraction
)} of your balance on a single trade. \n\nCurrent balance: ${formatMoney(
user?.balance ?? 0
)}`}
/>
) : (betAmount ?? 0) > 10 && probChange >= 0.3 && bankrollFraction <= 1 ? (
<AlertBox
title="Whoa, there!"
text={`Are you sure you want to move the market by ${displayedDifference}?`}
/>
) : (
<></>
)
)}`
: (betAmount ?? 0) > 10 && probChange >= 0.3 && bankrollFraction <= 1
? `Are you sure you want to move the market by ${displayedDifference}?`
: undefined
return (
<Col className={hidden ? 'hidden' : ''}>
@ -325,8 +312,6 @@ function BuyPanel(props: {
showSliderOnMobile
/>
{warning}
<Col className="mt-3 w-full gap-3">
<Row className="items-center justify-between text-sm">
<div className="text-gray-500">
@ -367,20 +352,20 @@ function BuyPanel(props: {
<Spacer h={8} />
{user && (
<button
className={clsx(
<WarningConfirmationButton
warning={warning}
onSubmit={submitBet}
isSubmitting={isSubmitting}
disabled={!!betDisabled}
openModalButtonClass={clsx(
'btn mb-2 flex-1',
betDisabled
? 'btn-disabled'
: outcome === 'YES'
? 'btn-primary'
: 'border-none bg-red-400 hover:bg-red-500',
isSubmitting ? 'loading' : ''
: 'border-none bg-red-400 hover:bg-red-500'
)}
onClick={betDisabled ? undefined : submitBet}
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
/>
)}
{wasSubmitted && <div className="mt-4">Trade submitted!</div>}

View File

@ -47,13 +47,13 @@ export function ConfirmationButton(props: {
{children}
<Row className="gap-4">
<div
className={clsx('btn normal-case', cancelBtn?.className)}
className={clsx('btn', cancelBtn?.className)}
onClick={() => updateOpen(false)}
>
{cancelBtn?.label ?? 'Cancel'}
</div>
<div
className={clsx('btn normal-case', submitBtn?.className)}
className={clsx('btn', submitBtn?.className)}
onClick={
onSubmitWithSuccess
? () =>
@ -69,7 +69,7 @@ export function ConfirmationButton(props: {
</Col>
</Modal>
<div
className={clsx('btn normal-case', openModalBtn.className)}
className={clsx('btn', openModalBtn.className)}
onClick={() => updateOpen(true)}
>
{openModalBtn.icon}

View File

@ -13,7 +13,6 @@ import { Tabs } from '../layout/tabs'
import { Col } from '../layout/col'
import { tradingAllowed } from 'web/lib/firebase/contracts'
import { CommentTipMap } from 'web/hooks/use-tip-txns'
import { useBets } from 'web/hooks/use-bets'
import { useComments } from 'web/hooks/use-comments'
import { useLiquidity } from 'web/hooks/use-liquidity'
import { BetSignUpPrompt } from '../sign-up-prompt'
@ -27,24 +26,23 @@ export function ContractTabs(props: {
comments: ContractComment[]
tips: CommentTipMap
}) {
const { contract, user, tips } = props
const { contract, user, bets, tips } = props
const { outcomeType } = contract
const bets = useBets(contract.id) ?? props.bets
const lps = useLiquidity(contract.id) ?? []
const lps = useLiquidity(contract.id)
const userBets =
user && bets.filter((bet) => !bet.isAnte && bet.userId === user.id)
const visibleBets = bets.filter(
(bet) => !bet.isAnte && !bet.isRedemption && bet.amount !== 0
)
const visibleLps = lps.filter((l) => !l.isAnte && l.amount > 0)
const visibleLps = lps?.filter((l) => !l.isAnte && l.amount > 0)
// Load comments here, so the badge count will be correct
const updatedComments = useComments(contract.id)
const comments = updatedComments ?? props.comments
const betActivity = (
const betActivity = visibleLps && (
<ContractBetsActivity
contract={contract}
bets={visibleBets}

View File

@ -0,0 +1,74 @@
import clsx from 'clsx'
import React from 'react'
import { Row } from './layout/row'
import { ConfirmationButton } from './confirmation-button'
import { ExclamationIcon } from '@heroicons/react/solid'
export function WarningConfirmationButton(props: {
warning?: string
onSubmit: () => void
disabled?: boolean
isSubmitting: boolean
openModalButtonClass?: string
submitButtonClassName?: string
}) {
const {
onSubmit,
warning,
disabled,
isSubmitting,
openModalButtonClass,
submitButtonClassName,
} = props
if (!warning) {
return (
<button
className={clsx(
openModalButtonClass,
isSubmitting ? 'loading' : '',
disabled && 'btn-disabled'
)}
onClick={onSubmit}
disabled={disabled}
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
)
}
return (
<ConfirmationButton
openModalBtn={{
className: clsx(
openModalButtonClass,
isSubmitting && 'btn-disabled loading'
),
label: 'Submit',
}}
cancelBtn={{
label: 'Cancel',
className: 'btn-warning',
}}
submitBtn={{
label: 'Submit',
className: clsx(
'border-none btn-sm btn-ghost self-center',
submitButtonClassName
),
}}
onSubmit={onSubmit}
>
<Row className="items-center text-xl">
<ExclamationIcon
className="h-16 w-16 text-yellow-400"
aria-hidden="true"
/>
Whoa, there!
</Row>
<p>{warning}</p>
</ConfirmationButton>
)
}

View File

@ -12,3 +12,7 @@ export function isIOS() {
(navigator.userAgent.includes('Mac') && 'ontouchend' in document)
)
}
export function isAndroid() {
return navigator.userAgent.includes('Android')
}

View File

@ -77,13 +77,21 @@ const Salem = {
const tourneys: Tourney[] = [
{
title: 'Cause Exploration Prizes',
title: 'Manifold F2P Tournament',
blurb:
'Which new charity ideas will Open Philanthropy find most promising?',
award: 'M$100k',
endTime: toDate('Sep 9, 2022'),
groupId: 'cMcpBQ2p452jEcJD2SFw',
'Who can amass the most mana starting from a free-to-play (F2P) account?',
award: 'Poem',
endTime: toDate('Sep 15, 2022'),
groupId: '6rrIja7tVW00lUVwtsYS',
},
// {
// title: 'Cause Exploration Prizes',
// blurb:
// 'Which new charity ideas will Open Philanthropy find most promising?',
// award: 'M$100k',
// endTime: toDate('Sep 9, 2022'),
// groupId: 'cMcpBQ2p452jEcJD2SFw',
// },
{
title: 'Fantasy Football Stock Exchange',
blurb: 'How many points will each NFL player score this season?',
@ -135,7 +143,7 @@ export default function TournamentPage(props: { sections: SectionInfo[] }) {
title="Tournaments"
description="Win money by betting in forecasting touraments on current events, sports, science, and more"
/>
<Col className="mx-4 mt-4 gap-10 sm:mx-10 xl:w-[125%]">
<Col className="m-4 gap-10 sm:mx-10 sm:gap-24 xl:w-[125%]">
{sections.map(({ tourney, slug, numPeople }) => (
<div key={slug}>
<SectionHeader

View File

@ -60,6 +60,18 @@ module.exports = {
'overflow-wrap': 'anywhere',
'word-break': 'break-word', // for Safari
},
'.only-thumb': {
'pointer-events': 'none',
'&::-webkit-slider-thumb': {
'pointer-events': 'auto !important',
},
'&::-moz-range-thumb': {
'pointer-events': 'auto !important',
},
'&::-ms-thumb': {
'pointer-events': 'auto !important',
},
},
})
}),
],