Refactor useSaveShares to actually read from localStorage, use less bug-prone interface.
This commit is contained in:
parent
600203a675
commit
9d9dcea9b5
|
@ -37,7 +37,7 @@ import {
|
||||||
getPseudoProbability,
|
getPseudoProbability,
|
||||||
} from 'common/pseudo-numeric'
|
} from 'common/pseudo-numeric'
|
||||||
import { SellRow } from './sell-row'
|
import { SellRow } from './sell-row'
|
||||||
import { useSaveShares } from './use-save-shares'
|
import { useSaveBinaryShares } from './use-save-binary-shares'
|
||||||
import { SignUpPrompt } from './sign-up-prompt'
|
import { SignUpPrompt } from './sign-up-prompt'
|
||||||
import { isIOS } from 'web/lib/util/device'
|
import { isIOS } from 'web/lib/util/device'
|
||||||
import { ProbabilityInput } from './probability-input'
|
import { ProbabilityInput } from './probability-input'
|
||||||
|
@ -56,12 +56,7 @@ export function BetPanel(props: {
|
||||||
const userBets = useUserContractBets(user?.id, contract.id)
|
const userBets = useUserContractBets(user?.id, contract.id)
|
||||||
const unfilledBets = useUnfilledBets(contract.id) ?? []
|
const unfilledBets = useUnfilledBets(contract.id) ?? []
|
||||||
const yourUnfilledBets = unfilledBets.filter((bet) => bet.userId === user?.id)
|
const yourUnfilledBets = unfilledBets.filter((bet) => bet.userId === user?.id)
|
||||||
const { yesFloorShares, noFloorShares } = useSaveShares(contract, userBets)
|
const { sharesOutcome } = useSaveBinaryShares(contract, userBets)
|
||||||
const sharesOutcome = yesFloorShares
|
|
||||||
? 'YES'
|
|
||||||
: noFloorShares
|
|
||||||
? 'NO'
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
const [isLimitOrder, setIsLimitOrder] = useState(false)
|
const [isLimitOrder, setIsLimitOrder] = useState(false)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { Modal } from './layout/modal'
|
||||||
import { SellButton } from './sell-button'
|
import { SellButton } from './sell-button'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
||||||
import { useSaveShares } from './use-save-shares'
|
import { useSaveBinaryShares } from './use-save-binary-shares'
|
||||||
|
|
||||||
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
// Inline version of a bet panel. Opens BetPanel in a new modal.
|
||||||
export default function BetRow(props: {
|
export default function BetRow(props: {
|
||||||
|
@ -24,10 +24,8 @@ export default function BetRow(props: {
|
||||||
)
|
)
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const userBets = useUserContractBets(user?.id, contract.id)
|
const userBets = useUserContractBets(user?.id, contract.id)
|
||||||
const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares(
|
const { yesShares, noShares, hasYesShares, hasNoShares } =
|
||||||
contract,
|
useSaveBinaryShares(contract, userBets)
|
||||||
userBets
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -40,7 +38,7 @@ export default function BetRow(props: {
|
||||||
setBetChoice(choice)
|
setBetChoice(choice)
|
||||||
}}
|
}}
|
||||||
replaceNoButton={
|
replaceNoButton={
|
||||||
yesFloorShares > 0 ? (
|
hasYesShares ? (
|
||||||
<SellButton
|
<SellButton
|
||||||
panelClassName={betPanelClassName}
|
panelClassName={betPanelClassName}
|
||||||
contract={contract}
|
contract={contract}
|
||||||
|
@ -51,7 +49,7 @@ export default function BetRow(props: {
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
replaceYesButton={
|
replaceYesButton={
|
||||||
noFloorShares > 0 ? (
|
hasNoShares ? (
|
||||||
<SellButton
|
<SellButton
|
||||||
panelClassName={betPanelClassName}
|
panelClassName={betPanelClassName}
|
||||||
contract={contract}
|
contract={contract}
|
||||||
|
|
|
@ -52,10 +52,7 @@ export function ContractCard(props: {
|
||||||
const showQuickBet =
|
const showQuickBet =
|
||||||
user &&
|
user &&
|
||||||
!marketClosed &&
|
!marketClosed &&
|
||||||
!(
|
(outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC') &&
|
||||||
outcomeType === 'FREE_RESPONSE' && getTopAnswer(contract) === undefined
|
|
||||||
) &&
|
|
||||||
outcomeType !== 'NUMERIC' &&
|
|
||||||
!hideQuickBet
|
!hideQuickBet
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -7,7 +7,13 @@ import {
|
||||||
} from 'common/calculate'
|
} from 'common/calculate'
|
||||||
import { getExpectedValue } from 'common/calculate-dpm'
|
import { getExpectedValue } from 'common/calculate-dpm'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
import { Contract, NumericContract, resolution } from 'common/contract'
|
import {
|
||||||
|
BinaryContract,
|
||||||
|
Contract,
|
||||||
|
NumericContract,
|
||||||
|
PseudoNumericContract,
|
||||||
|
resolution,
|
||||||
|
} from 'common/contract'
|
||||||
import {
|
import {
|
||||||
formatLargeNumber,
|
formatLargeNumber,
|
||||||
formatMoney,
|
formatMoney,
|
||||||
|
@ -22,7 +28,7 @@ import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
||||||
import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon'
|
import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon'
|
||||||
import { Col } from '../layout/col'
|
import { Col } from '../layout/col'
|
||||||
import { OUTCOME_TO_COLOR } from '../outcome-label'
|
import { OUTCOME_TO_COLOR } from '../outcome-label'
|
||||||
import { useSaveShares } from '../use-save-shares'
|
import { useSaveBinaryShares } from '../use-save-binary-shares'
|
||||||
import { sellShares } from 'web/lib/firebase/api-call'
|
import { sellShares } from 'web/lib/firebase/api-call'
|
||||||
import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm'
|
import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
|
@ -31,26 +37,21 @@ import { useUnfilledBets } from 'web/hooks/use-bets'
|
||||||
|
|
||||||
const BET_SIZE = 10
|
const BET_SIZE = 10
|
||||||
|
|
||||||
export function QuickBet(props: { contract: Contract; user: User }) {
|
export function QuickBet(props: {
|
||||||
|
contract: BinaryContract | PseudoNumericContract
|
||||||
|
user: User
|
||||||
|
}) {
|
||||||
const { contract, user } = props
|
const { contract, user } = props
|
||||||
const { mechanism, outcomeType } = contract
|
const { mechanism, outcomeType } = contract
|
||||||
const isCpmm = mechanism === 'cpmm-1'
|
const isCpmm = mechanism === 'cpmm-1'
|
||||||
|
|
||||||
const userBets = useUserContractBets(user.id, contract.id)
|
const userBets = useUserContractBets(user.id, contract.id)
|
||||||
const unfilledBets = useUnfilledBets(contract.id) ?? []
|
const unfilledBets = useUnfilledBets(contract.id) ?? []
|
||||||
const topAnswer =
|
|
||||||
outcomeType === 'FREE_RESPONSE' ? getTopAnswer(contract) : undefined
|
|
||||||
|
|
||||||
// TODO: yes/no from useSaveShares doesn't work on numeric contracts
|
const { hasYesShares, hasNoShares, yesShares, noShares } =
|
||||||
const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares(
|
useSaveBinaryShares(contract, userBets)
|
||||||
contract,
|
const hasUpShares = hasYesShares
|
||||||
userBets,
|
const hasDownShares = hasNoShares && !hasUpShares
|
||||||
topAnswer?.number.toString() || undefined
|
|
||||||
)
|
|
||||||
const hasUpShares =
|
|
||||||
yesFloorShares || (noFloorShares && outcomeType === 'NUMERIC')
|
|
||||||
const hasDownShares =
|
|
||||||
noFloorShares && yesFloorShares <= 0 && outcomeType !== 'NUMERIC'
|
|
||||||
|
|
||||||
const [upHover, setUpHover] = useState(false)
|
const [upHover, setUpHover] = useState(false)
|
||||||
const [downHover, setDownHover] = useState(false)
|
const [downHover, setDownHover] = useState(false)
|
||||||
|
@ -134,13 +135,6 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcomeType === 'FREE_RESPONSE')
|
|
||||||
return (
|
|
||||||
<Col className="relative -my-4 -mr-5 min-w-[5.5rem] justify-center gap-2 pr-5 pl-1 align-middle">
|
|
||||||
<QuickOutcomeView contract={contract} previewProb={previewProb} />
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col
|
<Col
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -161,7 +155,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
{formatMoney(10)}
|
{formatMoney(10)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasUpShares > 0 ? (
|
{hasUpShares ? (
|
||||||
<TriangleFillIcon
|
<TriangleFillIcon
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mx-auto h-5 w-5',
|
'mx-auto h-5 w-5',
|
||||||
|
@ -196,7 +190,7 @@ export function QuickBet(props: { contract: Contract; user: User }) {
|
||||||
onMouseLeave={() => setDownHover(false)}
|
onMouseLeave={() => setDownHover(false)}
|
||||||
onClick={() => placeQuickBet('DOWN')}
|
onClick={() => placeQuickBet('DOWN')}
|
||||||
></div>
|
></div>
|
||||||
{hasDownShares > 0 ? (
|
{hasDownShares ? (
|
||||||
<TriangleDownFillIcon
|
<TriangleDownFillIcon
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'mx-auto h-5 w-5',
|
'mx-auto h-5 w-5',
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Row } from './layout/row'
|
||||||
import { formatWithCommas } from 'common/util/format'
|
import { formatWithCommas } from 'common/util/format'
|
||||||
import { OutcomeLabel } from './outcome-label'
|
import { OutcomeLabel } from './outcome-label'
|
||||||
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
||||||
import { useSaveShares } from './use-save-shares'
|
import { useSaveBinaryShares } from './use-save-binary-shares'
|
||||||
import { SellSharesModal } from './sell-modal'
|
import { SellSharesModal } from './sell-modal'
|
||||||
|
|
||||||
export function SellRow(props: {
|
export function SellRow(props: {
|
||||||
|
@ -20,16 +20,7 @@ export function SellRow(props: {
|
||||||
const [showSellModal, setShowSellModal] = useState(false)
|
const [showSellModal, setShowSellModal] = useState(false)
|
||||||
|
|
||||||
const { mechanism } = contract
|
const { mechanism } = contract
|
||||||
const { yesFloorShares, noFloorShares, yesShares, noShares } = useSaveShares(
|
const { sharesOutcome, shares } = useSaveBinaryShares(contract, userBets)
|
||||||
contract,
|
|
||||||
userBets
|
|
||||||
)
|
|
||||||
const floorShares = yesFloorShares || noFloorShares
|
|
||||||
const sharesOutcome = yesFloorShares
|
|
||||||
? 'YES'
|
|
||||||
: noFloorShares
|
|
||||||
? 'NO'
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
if (sharesOutcome && user && mechanism === 'cpmm-1') {
|
if (sharesOutcome && user && mechanism === 'cpmm-1') {
|
||||||
return (
|
return (
|
||||||
|
@ -37,7 +28,7 @@ export function SellRow(props: {
|
||||||
<Col className={className}>
|
<Col className={className}>
|
||||||
<Row className="items-center justify-between gap-2 ">
|
<Row className="items-center justify-between gap-2 ">
|
||||||
<div>
|
<div>
|
||||||
You have {formatWithCommas(floorShares)}{' '}
|
You have {formatWithCommas(shares)}{' '}
|
||||||
<OutcomeLabel
|
<OutcomeLabel
|
||||||
outcome={sharesOutcome}
|
outcome={sharesOutcome}
|
||||||
contract={contract}
|
contract={contract}
|
||||||
|
@ -64,7 +55,7 @@ export function SellRow(props: {
|
||||||
contract={contract}
|
contract={contract}
|
||||||
user={user}
|
user={user}
|
||||||
userBets={userBets ?? []}
|
userBets={userBets ?? []}
|
||||||
shares={sharesOutcome === 'YES' ? yesShares : noShares}
|
shares={shares}
|
||||||
sharesOutcome={sharesOutcome}
|
sharesOutcome={sharesOutcome}
|
||||||
setOpen={setShowSellModal}
|
setOpen={setShowSellModal}
|
||||||
/>
|
/>
|
||||||
|
|
56
web/components/use-save-binary-shares.ts
Normal file
56
web/components/use-save-binary-shares.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { BinaryContract, PseudoNumericContract } from 'common/contract'
|
||||||
|
import { Bet } from 'common/bet'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { partition, sumBy } from 'lodash'
|
||||||
|
import { safeLocalStorage } from 'web/lib/util/local'
|
||||||
|
|
||||||
|
export const useSaveBinaryShares = (
|
||||||
|
contract: BinaryContract | PseudoNumericContract,
|
||||||
|
userBets: Bet[] | undefined
|
||||||
|
) => {
|
||||||
|
const [savedShares, setSavedShares] = useState({ yesShares: 0, noShares: 0 })
|
||||||
|
|
||||||
|
const [yesBets, noBets] = partition(
|
||||||
|
userBets ?? [],
|
||||||
|
(bet) => bet.outcome === 'YES'
|
||||||
|
)
|
||||||
|
const [yesShares, noShares] = userBets
|
||||||
|
? [sumBy(yesBets, (bet) => bet.shares), sumBy(noBets, (bet) => bet.shares)]
|
||||||
|
: [savedShares.yesShares, savedShares.noShares]
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const local = safeLocalStorage()
|
||||||
|
|
||||||
|
// Read shares from local storage.
|
||||||
|
const savedShares = local?.getItem(`${contract.id}-shares`)
|
||||||
|
if (savedShares) {
|
||||||
|
setSavedShares(JSON.parse(savedShares))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userBets) {
|
||||||
|
// Save shares to local storage.
|
||||||
|
const sharesData = JSON.stringify({ yesShares, noShares })
|
||||||
|
local?.setItem(`${contract.id}-shares`, sharesData)
|
||||||
|
}
|
||||||
|
}, [contract.id, userBets, noShares, yesShares])
|
||||||
|
|
||||||
|
const hasYesShares = yesShares >= 1
|
||||||
|
const hasNoShares = noShares >= 1
|
||||||
|
|
||||||
|
const sharesOutcome = hasYesShares
|
||||||
|
? ('YES' as const)
|
||||||
|
: hasNoShares
|
||||||
|
? ('NO' as const)
|
||||||
|
: undefined
|
||||||
|
const shares =
|
||||||
|
sharesOutcome === 'YES' ? yesShares : sharesOutcome === 'NO' ? noShares : 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
yesShares,
|
||||||
|
noShares,
|
||||||
|
shares,
|
||||||
|
sharesOutcome,
|
||||||
|
hasYesShares,
|
||||||
|
hasNoShares,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,59 +0,0 @@
|
||||||
import { Contract } from 'common/contract'
|
|
||||||
import { Bet } from 'common/bet'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { partition, sumBy } from 'lodash'
|
|
||||||
import { safeLocalStorage } from 'web/lib/util/local'
|
|
||||||
|
|
||||||
export const useSaveShares = (
|
|
||||||
contract: Contract,
|
|
||||||
userBets: Bet[] | undefined,
|
|
||||||
freeResponseAnswerOutcome?: string
|
|
||||||
) => {
|
|
||||||
const [savedShares, setSavedShares] = useState<
|
|
||||||
| {
|
|
||||||
yesShares: number
|
|
||||||
noShares: number
|
|
||||||
yesFloorShares: number
|
|
||||||
noFloorShares: number
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
>()
|
|
||||||
|
|
||||||
// TODO: How do we handle numeric yes / no bets? - maybe bet amounts above vs below the highest peak
|
|
||||||
const [yesBets, noBets] = partition(userBets ?? [], (bet) =>
|
|
||||||
freeResponseAnswerOutcome
|
|
||||||
? bet.outcome === freeResponseAnswerOutcome
|
|
||||||
: bet.outcome === 'YES'
|
|
||||||
)
|
|
||||||
const [yesShares, noShares] = [
|
|
||||||
sumBy(yesBets, (bet) => bet.shares),
|
|
||||||
sumBy(noBets, (bet) => bet.shares),
|
|
||||||
]
|
|
||||||
|
|
||||||
const yesFloorShares = Math.round(yesShares) === 0 ? 0 : Math.floor(yesShares)
|
|
||||||
const noFloorShares = Math.round(noShares) === 0 ? 0 : Math.floor(noShares)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const local = safeLocalStorage()
|
|
||||||
// Save yes and no shares to local storage.
|
|
||||||
const savedShares = local?.getItem(`${contract.id}-shares`)
|
|
||||||
if (!userBets && savedShares) {
|
|
||||||
setSavedShares(JSON.parse(savedShares))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userBets) {
|
|
||||||
const updatedShares = { yesShares, noShares }
|
|
||||||
local?.setItem(`${contract.id}-shares`, JSON.stringify(updatedShares))
|
|
||||||
}
|
|
||||||
}, [contract.id, userBets, noShares, yesShares])
|
|
||||||
|
|
||||||
if (userBets) return { yesShares, noShares, yesFloorShares, noFloorShares }
|
|
||||||
return (
|
|
||||||
savedShares ?? {
|
|
||||||
yesShares: 0,
|
|
||||||
noShares: 0,
|
|
||||||
yesFloorShares: 0,
|
|
||||||
noFloorShares: 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user