Compare commits
15 Commits
main
...
bet-timed-
Author | SHA1 | Date | |
---|---|---|---|
|
583a797a61 | ||
|
139902f15e | ||
|
668d111973 | ||
|
31e1f4ba28 | ||
|
f3092892d8 | ||
|
1adfb874c3 | ||
|
3aad05e2a6 | ||
|
13de5de24d | ||
|
1cfbd86bd0 | ||
|
ba9411161d | ||
|
fd49ccea39 | ||
|
1b17d3a102 | ||
|
98510f233d | ||
|
d1fab9937f | ||
|
3d368216b2 |
|
@ -30,17 +30,17 @@ export function formatPercent(zeroToOne: number) {
|
|||
}
|
||||
|
||||
// Eg 1234567.89 => 1.23M; 5678 => 5.68K
|
||||
export function formatLargeNumber(num: number, sigfigs = 3): string {
|
||||
export function formatLargeNumber(num: number, sigfigs = 2): string {
|
||||
const absNum = Math.abs(num)
|
||||
if (absNum < 1000) {
|
||||
return num.toPrecision(sigfigs)
|
||||
return '' + Number(num.toPrecision(sigfigs))
|
||||
}
|
||||
|
||||
const suffix = ['', 'K', 'M', 'B', 'T', 'Q']
|
||||
const suffixIdx = Math.floor(Math.log10(absNum) / 3)
|
||||
const suffixStr = suffix[suffixIdx]
|
||||
const numStr = (num / Math.pow(10, 3 * suffixIdx)).toPrecision(sigfigs)
|
||||
return `${numStr}${suffixStr}`
|
||||
return `${Number(numStr)}${suffixStr}`
|
||||
}
|
||||
|
||||
export function toCamelCase(words: string) {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { Row } from '../layout/row'
|
||||
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
||||
import {
|
||||
formatLargeNumber,
|
||||
formatMoney,
|
||||
formatPercent,
|
||||
} from 'common/util/format'
|
||||
import {
|
||||
Contract,
|
||||
contractPath,
|
||||
|
@ -25,8 +29,14 @@ import {
|
|||
OUTCOME_TO_COLOR,
|
||||
} from '../outcome-label'
|
||||
import { getOutcomeProbability, getTopAnswer } from 'common/calculate'
|
||||
import { AbbrContractDetails } from './contract-details'
|
||||
import { AvatarDetails, MiscDetails } from './contract-details'
|
||||
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
||||
import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon'
|
||||
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
||||
import toast from 'react-hot-toast'
|
||||
import { CheckIcon, XIcon } from '@heroicons/react/solid'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||
|
||||
// Return a number from 0 to 1 for this contract
|
||||
// Resolved contracts are set to 1, for coloring purposes (even if NO)
|
||||
|
@ -50,6 +60,9 @@ function getNumericScale(contract: NumericContract) {
|
|||
}
|
||||
|
||||
function getColor(contract: Contract) {
|
||||
// TODO: Not sure why eg green-400 doesn't work here; try upgrading Tailwind
|
||||
// TODO: Try injecting a gradient here
|
||||
// return 'primary'
|
||||
const { resolution } = contract
|
||||
if (resolution) {
|
||||
return (
|
||||
|
@ -83,35 +96,121 @@ export function ContractCard(props: {
|
|||
const prob = getProb(contract)
|
||||
const color = getColor(contract)
|
||||
const marketClosed = (contract.closeTime || Infinity) < Date.now()
|
||||
const showTopBar = prob >= 0.5 || marketClosed
|
||||
|
||||
// TODO: switch to useContract after you place a bet on it
|
||||
|
||||
function betToast(outcome: string) {
|
||||
let canceled = false
|
||||
const toastId = toast.custom(
|
||||
<BetToast
|
||||
outcome={outcome}
|
||||
seconds={3}
|
||||
onToastFinish={onToastFinish}
|
||||
onToastCancel={onToastCancel}
|
||||
/>,
|
||||
{
|
||||
duration: 3000,
|
||||
}
|
||||
)
|
||||
|
||||
function onToastCancel() {
|
||||
toast.remove(toastId)
|
||||
canceled = true
|
||||
}
|
||||
|
||||
function onToastFinish() {
|
||||
if (canceled) return
|
||||
console.log('Finishing toast')
|
||||
toast.remove(toastId)
|
||||
placeBet({
|
||||
amount: 10,
|
||||
outcome,
|
||||
contractId: contract.id,
|
||||
})
|
||||
.then((r) => {
|
||||
// Success
|
||||
console.log('placed bet. Result:', r)
|
||||
toast.success('Bet placed!', { duration: 1000 })
|
||||
})
|
||||
.catch((e) => {
|
||||
// Failure
|
||||
if (e instanceof APIError) {
|
||||
toast.error(e.toString(), { duration: 1000 })
|
||||
} else {
|
||||
console.error(e)
|
||||
toast.error('Could not place bet')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Col
|
||||
className={clsx(
|
||||
'relative gap-3 rounded-lg bg-white p-6 pr-7 shadow-md hover:bg-gray-100',
|
||||
'relative gap-3 rounded-lg bg-white py-4 pl-6 pr-5 shadow-md hover:cursor-pointer hover:bg-gray-100',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Row>
|
||||
<Col className="relative flex-1 gap-3 pr-1">
|
||||
<div
|
||||
className={clsx(
|
||||
'peer absolute -left-6 -top-4 -bottom-4 z-10',
|
||||
// Hack: Extend the clickable area for closed markets
|
||||
marketClosed ? 'right-[-6.5rem]' : 'right-0'
|
||||
)}
|
||||
>
|
||||
<Link href={contractPath(contract)}>
|
||||
<a className="absolute left-0 right-0 top-0 bottom-0" />
|
||||
<a className="absolute top-0 left-0 right-0 bottom-0" />
|
||||
</Link>
|
||||
|
||||
<AbbrContractDetails
|
||||
contract={contract}
|
||||
showHotVolume={showHotVolume}
|
||||
showCloseTime={showCloseTime}
|
||||
/>
|
||||
|
||||
<Row className={clsx('justify-between gap-4')}>
|
||||
<Col className="gap-3">
|
||||
</div>
|
||||
<AvatarDetails contract={contract} />
|
||||
<p
|
||||
className="break-words font-medium text-indigo-700"
|
||||
className="break-words font-medium text-indigo-700 peer-hover:underline peer-hover:decoration-indigo-400 peer-hover:decoration-2"
|
||||
style={{ /* For iOS safari */ wordBreak: 'break-word' }}
|
||||
>
|
||||
{question}
|
||||
</p>
|
||||
|
||||
{outcomeType === 'FREE_RESPONSE' && (
|
||||
<FreeResponseTopAnswer
|
||||
contract={contract as FullContract<DPM, FreeResponse>}
|
||||
truncate="long"
|
||||
/>
|
||||
)}
|
||||
|
||||
<MiscDetails
|
||||
contract={contract}
|
||||
showHotVolume={showHotVolume}
|
||||
showCloseTime={showCloseTime}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col className="relative -my-4 -mr-5 min-w-[6rem] justify-center gap-2 pr-5 pl-3 align-middle">
|
||||
{!marketClosed && (
|
||||
<div>
|
||||
<div
|
||||
className="peer absolute top-0 left-0 right-0 h-[50%]"
|
||||
onClick={() => betToast('YES')}
|
||||
></div>
|
||||
<div className="my-1 text-center text-xs text-transparent peer-hover:text-gray-400">
|
||||
{formatMoney(20)}
|
||||
</div>
|
||||
|
||||
{contract.createdTime % 3 == 0 ? (
|
||||
<TriangleFillIcon
|
||||
className={clsx(
|
||||
'mx-auto h-5 w-5 text-opacity-60 peer-hover:text-opacity-100',
|
||||
`text-${color}`
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<TriangleFillIcon className="mx-auto h-5 w-5 text-gray-200 peer-hover:text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{outcomeType === 'BINARY' && (
|
||||
<BinaryResolutionOrChance
|
||||
className="items-center"
|
||||
|
@ -125,7 +224,6 @@ export function ContractCard(props: {
|
|||
contract={contract as NumericContract}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
{outcomeType === 'FREE_RESPONSE' && (
|
||||
<FreeResponseResolutionOrChance
|
||||
|
@ -135,6 +233,30 @@ export function ContractCard(props: {
|
|||
/>
|
||||
)}
|
||||
|
||||
{!marketClosed && (
|
||||
<div>
|
||||
<div
|
||||
className="peer absolute bottom-0 left-0 right-0 h-[50%]"
|
||||
onClick={() => betToast('NO')}
|
||||
></div>
|
||||
{contract.createdTime % 3 == 2 ? (
|
||||
<TriangleDownFillIcon
|
||||
className={clsx(
|
||||
'mx-auto h-5 w-5 text-opacity-60 peer-hover:text-opacity-100',
|
||||
`text-${color}`
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<TriangleDownFillIcon className="mx-auto h-5 w-5 text-gray-200 peer-hover:text-gray-400" />
|
||||
)}
|
||||
<div className="my-1 text-center text-xs text-transparent peer-hover:text-gray-400">
|
||||
{formatMoney(20)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute right-0 top-0 w-2 rounded-tr-md',
|
||||
|
@ -182,15 +304,31 @@ export function BinaryResolutionOrChance(props: {
|
|||
) : (
|
||||
<>
|
||||
<div className={textColor}>{getBinaryProbPercent(contract)}</div>
|
||||
<div className={clsx(textColor, large ? 'text-xl' : 'text-base')}>
|
||||
chance
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function FreeResponseTopAnswer(props: {
|
||||
contract: FreeResponseContract
|
||||
truncate: 'short' | 'long' | 'none'
|
||||
className?: string
|
||||
}) {
|
||||
const { contract, truncate } = props
|
||||
const { resolution } = contract
|
||||
|
||||
const topAnswer = getTopAnswer(contract)
|
||||
|
||||
return topAnswer ? (
|
||||
<AnswerLabel
|
||||
className="!text-gray-600"
|
||||
answer={topAnswer}
|
||||
truncate={truncate}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
export function FreeResponseResolutionOrChance(props: {
|
||||
contract: FreeResponseContract
|
||||
truncate: 'short' | 'long' | 'none'
|
||||
|
@ -217,16 +355,10 @@ export function FreeResponseResolutionOrChance(props: {
|
|||
) : (
|
||||
topAnswer && (
|
||||
<Row className="items-center gap-6">
|
||||
<AnswerLabel
|
||||
className="!text-gray-600"
|
||||
answer={topAnswer}
|
||||
truncate={truncate}
|
||||
/>
|
||||
<Col className={clsx('text-3xl', textColor)}>
|
||||
<div>
|
||||
{formatPercent(getOutcomeProbability(contract, topAnswer.id))}
|
||||
</div>
|
||||
<div className="text-base">chance</div>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
|
@ -241,6 +373,7 @@ export function NumericResolutionOrExpectation(props: {
|
|||
}) {
|
||||
const { contract, className } = props
|
||||
const { resolution } = contract
|
||||
const textColor = `text-${getColor(contract)}`
|
||||
|
||||
const resolutionValue =
|
||||
contract.resolutionValue ?? getValueFromBucket(resolution ?? '', contract)
|
||||
|
@ -254,12 +387,72 @@ export function NumericResolutionOrExpectation(props: {
|
|||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-3xl text-blue-400">
|
||||
<div className={clsx('text-3xl', textColor)}>
|
||||
{formatLargeNumber(getExpectedValue(contract))}
|
||||
</div>
|
||||
<div className="text-base text-blue-400">expected</div>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function BetToast(props: {
|
||||
outcome: string
|
||||
seconds: number
|
||||
onToastFinish: () => void
|
||||
onToastCancel: () => void
|
||||
}) {
|
||||
const { outcome, seconds, onToastFinish, onToastCancel } = props
|
||||
|
||||
// Track the number of seconds left, starting with durationMs
|
||||
const [secondsLeft, setSecondsLeft] = useState(seconds)
|
||||
console.log('renderings using', secondsLeft)
|
||||
|
||||
// Update the secondsLeft state every second
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setSecondsLeft((seconds) => seconds - 1)
|
||||
}, 1000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
if (secondsLeft <= 0) {
|
||||
console.log('finishing')
|
||||
onToastFinish()
|
||||
// return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5">
|
||||
<div className="p-4">
|
||||
<div className="flex items-center">
|
||||
<div className="flex w-0 flex-1 justify-between">
|
||||
<p className="w-0 flex-1 text-sm font-medium text-gray-900">
|
||||
Betting M$10 on {outcome} in {secondsLeft}s
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToastCancel}
|
||||
className="ml-3 flex-shrink-0 rounded-md bg-white text-sm font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
{/* <div className="ml-4 flex flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
// TODO
|
||||
// setShow(false)
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ import {
|
|||
PencilIcon,
|
||||
CurrencyDollarIcon,
|
||||
TrendingUpIcon,
|
||||
StarIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import { StarIcon as SolidStarIcon } from '@heroicons/react/solid'
|
||||
import { Row } from '../layout/row'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { UserLink } from '../user-page'
|
||||
|
@ -26,20 +28,13 @@ import NewContractBadge from '../new-contract-badge'
|
|||
import { CATEGORY_LIST } from 'common/categories'
|
||||
import { TagsList } from '../tags-list'
|
||||
|
||||
export function AbbrContractDetails(props: {
|
||||
export function MiscDetails(props: {
|
||||
contract: Contract
|
||||
showHotVolume?: boolean
|
||||
showCloseTime?: boolean
|
||||
}) {
|
||||
const { contract, showHotVolume, showCloseTime } = props
|
||||
const {
|
||||
volume,
|
||||
volume24Hours,
|
||||
creatorName,
|
||||
creatorUsername,
|
||||
closeTime,
|
||||
tags,
|
||||
} = contract
|
||||
const { volume, volume24Hours, closeTime, tags } = contract
|
||||
const { volumeLabel } = contractMetrics(contract)
|
||||
// Show at most one category that this contract is tagged by
|
||||
const categories = CATEGORY_LIST.filter((category) =>
|
||||
|
@ -47,26 +42,20 @@ export function AbbrContractDetails(props: {
|
|||
).slice(0, 1)
|
||||
|
||||
return (
|
||||
<Col className={clsx('gap-2 text-sm text-gray-500')}>
|
||||
<Row className="items-center justify-between">
|
||||
<Row className="items-center gap-2">
|
||||
<Avatar
|
||||
username={creatorUsername}
|
||||
avatarUrl={contract.creatorAvatarUrl}
|
||||
size={6}
|
||||
/>
|
||||
<UserLink name={creatorName} username={creatorUsername} />
|
||||
</Row>
|
||||
<Row className="items-center gap-3 text-sm text-gray-400">
|
||||
{/* {contract.createdTime % 3 == 1 ? (
|
||||
<SolidStarIcon className="h-6 w-6 text-indigo-600" />
|
||||
) : (
|
||||
<StarIcon className="h-6 w-6 text-gray-400" />
|
||||
)} */}
|
||||
|
||||
<Row className="gap-3 text-gray-400">
|
||||
{categories.length > 0 && (
|
||||
<TagsList className="text-gray-400" tags={categories} noLabel />
|
||||
)}
|
||||
|
||||
{showHotVolume ? (
|
||||
<Row className="gap-0.5">
|
||||
<TrendingUpIcon className="h-5 w-5" />{' '}
|
||||
{formatMoney(volume24Hours)}
|
||||
<TrendingUpIcon className="h-5 w-5" /> {formatMoney(volume24Hours)}
|
||||
</Row>
|
||||
) : showCloseTime ? (
|
||||
<Row className="gap-0.5">
|
||||
|
@ -80,6 +69,41 @@ export function AbbrContractDetails(props: {
|
|||
<NewContractBadge />
|
||||
)}
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export function AvatarDetails(props: { contract: Contract }) {
|
||||
const { contract } = props
|
||||
const { creatorName, creatorUsername } = contract
|
||||
|
||||
return (
|
||||
<Row className="items-center gap-2 text-sm text-gray-500">
|
||||
<Avatar
|
||||
username={creatorUsername}
|
||||
avatarUrl={contract.creatorAvatarUrl}
|
||||
size={6}
|
||||
/>
|
||||
<UserLink name={creatorName} username={creatorUsername} />
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export function AbbrContractDetails(props: {
|
||||
contract: Contract
|
||||
showHotVolume?: boolean
|
||||
showCloseTime?: boolean
|
||||
}) {
|
||||
const { contract, showHotVolume, showCloseTime } = props
|
||||
return (
|
||||
<Col className="gap-2">
|
||||
<Row className="items-center justify-between">
|
||||
<AvatarDetails contract={contract} />
|
||||
|
||||
<MiscDetails
|
||||
contract={contract}
|
||||
showHotVolume={showHotVolume}
|
||||
showCloseTime={showCloseTime}
|
||||
/>
|
||||
</Row>
|
||||
</Col>
|
||||
)
|
||||
|
@ -93,7 +117,7 @@ export function ContractDetails(props: {
|
|||
}) {
|
||||
const { contract, bets, isCreator, disabled } = props
|
||||
const { closeTime, creatorName, creatorUsername } = contract
|
||||
const { volumeLabel, createdDate, resolvedDate } = contractMetrics(contract)
|
||||
const { volumeLabel, resolvedDate } = contractMetrics(contract)
|
||||
|
||||
return (
|
||||
<Row className="flex-1 flex-wrap items-center gap-x-4 gap-y-2 text-sm text-gray-500">
|
||||
|
|
|
@ -84,11 +84,12 @@ export function FreeResponseOutcomeLabel(props: {
|
|||
if (!chosen) return <AnswerNumberLabel number={resolution} />
|
||||
return (
|
||||
<FreeResponseAnswerToolTip text={chosen.text}>
|
||||
<AnswerLabel
|
||||
<span className="text-blue-400">TOP</span>
|
||||
{/* <AnswerLabel
|
||||
answer={chosen}
|
||||
truncate={truncate}
|
||||
className={answerClassName}
|
||||
/>
|
||||
/> */}
|
||||
</FreeResponseAnswerToolTip>
|
||||
)
|
||||
}
|
||||
|
@ -117,7 +118,7 @@ export function ProbLabel() {
|
|||
}
|
||||
|
||||
export function MultiLabel() {
|
||||
return <span className="text-blue-400">MULTI</span>
|
||||
return <span className="text-blue-400">MANY</span>
|
||||
}
|
||||
|
||||
export function ProbPercentLabel(props: { prob: number }) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import clsx from 'clsx'
|
||||
import { BottomNavBar } from './nav/nav-bar'
|
||||
import Sidebar from './nav/sidebar'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
|
||||
export function Page(props: {
|
||||
margin?: boolean
|
||||
|
@ -20,6 +21,7 @@ export function Page(props: {
|
|||
)}
|
||||
style={suspend ? visuallyHiddenStyle : undefined}
|
||||
>
|
||||
<Toaster />
|
||||
<Sidebar className="sticky top-4 hidden divide-gray-300 self-start pl-2 lg:col-span-2 lg:block" />
|
||||
<main
|
||||
className={clsx(
|
||||
|
|
17
web/lib/icons/triangle-down-fill-icon.tsx
Normal file
17
web/lib/icons/triangle-down-fill-icon.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Icon from Bootstrap: https://icons.getbootstrap.com/
|
||||
export default function TriangleDownFillIcon(props: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
className={props.className}
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
transform="rotate(-180 8 8)"
|
||||
d="M7.022 1.566a1.13 1.13 0 0 1 1.96 0l6.857 11.667c.457.778-.092 1.767-.98 1.767H1.144c-.889 0-1.437-.99-.98-1.767L7.022 1.566z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
16
web/lib/icons/triangle-fill-icon.tsx
Normal file
16
web/lib/icons/triangle-fill-icon.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Icon from Bootstrap: https://icons.getbootstrap.com/
|
||||
export default function TriangleFillIcon(props: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
className={props.className}
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7.022 1.566a1.13 1.13 0 0 1 1.96 0l6.857 11.667c.457.778-.092 1.767-.98 1.767H1.144c-.889 0-1.437-.99-.98-1.767L7.022 1.566z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
|
@ -35,6 +35,7 @@
|
|||
"react-confetti": "6.0.1",
|
||||
"react-dom": "17.0.2",
|
||||
"react-expanding-textarea": "2.3.5",
|
||||
"react-hot-toast": "^2.2.0",
|
||||
"react-instantsearch-hooks-web": "6.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -3056,6 +3056,11 @@ globby@^11.0.4:
|
|||
merge2 "^1.4.1"
|
||||
slash "^3.0.0"
|
||||
|
||||
goober@^2.1.1:
|
||||
version "2.1.9"
|
||||
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.9.tgz#0faee08fab1a5d55b23e9ec043bb5a1b46fa025a"
|
||||
integrity sha512-PAtnJbrWtHbfpJUIveG5PJIB6Mc9Kd0gimu9wZwPyA+wQUSeOeA4x4Ug16lyaaUUKZ/G6QEH1xunKOuXP1F4Vw==
|
||||
|
||||
google-auth-library@^7.0.0, google-auth-library@^7.6.1, google-auth-library@^7.9.2:
|
||||
version "7.11.0"
|
||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.11.0.tgz#b63699c65037310a424128a854ba7e736704cbdb"
|
||||
|
@ -4716,6 +4721,13 @@ react-expanding-textarea@2.3.5:
|
|||
react-with-forwarded-ref "^0.3.3"
|
||||
tslib "^2.0.3"
|
||||
|
||||
react-hot-toast@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.2.0.tgz#ab6f4caed4214b9534f94bb8cfaaf21b051e62b9"
|
||||
integrity sha512-248rXw13uhf/6TNDVzagX+y7R8J183rp7MwUMNkcrBRyHj/jWOggfXTGlM8zAOuh701WyVW+eUaWG2LeSufX9g==
|
||||
dependencies:
|
||||
goober "^2.1.1"
|
||||
|
||||
react-instantsearch-hooks-web@6.24.1:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/react-instantsearch-hooks-web/-/react-instantsearch-hooks-web-6.24.1.tgz#392be70c584583f3cd9fe22eda5a59f7449e5ac9"
|
||||
|
|
Loading…
Reference in New Issue
Block a user