Merge branch 'main' into free-comments
This commit is contained in:
commit
e938e70aae
|
@ -18,7 +18,14 @@ import {
|
||||||
getDpmProbabilityAfterSale,
|
getDpmProbabilityAfterSale,
|
||||||
} from './calculate-dpm'
|
} from './calculate-dpm'
|
||||||
import { calculateFixedPayout } from './calculate-fixed-payouts'
|
import { calculateFixedPayout } from './calculate-fixed-payouts'
|
||||||
import { Binary, Contract, CPMM, DPM, FullContract } from './contract'
|
import {
|
||||||
|
Binary,
|
||||||
|
Contract,
|
||||||
|
CPMM,
|
||||||
|
DPM,
|
||||||
|
FreeResponseContract,
|
||||||
|
FullContract,
|
||||||
|
} from './contract'
|
||||||
|
|
||||||
export function getProbability(contract: FullContract<DPM | CPMM, Binary>) {
|
export function getProbability(contract: FullContract<DPM | CPMM, Binary>) {
|
||||||
return contract.mechanism === 'cpmm-1'
|
return contract.mechanism === 'cpmm-1'
|
||||||
|
@ -170,3 +177,15 @@ export function getContractBetNullMetrics() {
|
||||||
profitPercent: 0,
|
profitPercent: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTopAnswer(contract: FreeResponseContract) {
|
||||||
|
const { answers } = contract
|
||||||
|
const top = _.maxBy(
|
||||||
|
answers.map((answer) => ({
|
||||||
|
answer,
|
||||||
|
prob: getOutcomeProbability(contract, answer.id),
|
||||||
|
})),
|
||||||
|
({ prob }) => prob
|
||||||
|
)
|
||||||
|
return top?.answer
|
||||||
|
}
|
||||||
|
|
|
@ -10,3 +10,9 @@ export type ClickEvent = {
|
||||||
contractId: string
|
contractId: string
|
||||||
timestamp: number
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LatencyEvent = {
|
||||||
|
type: 'feed' | 'portfolio'
|
||||||
|
latency: number
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,10 @@ service cloud.firestore {
|
||||||
allow create: if userId == request.auth.uid;
|
allow create: if userId == request.auth.uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match /private-users/{userId}/latency/{loadTimeId} {
|
||||||
|
allow create: if userId == request.auth.uid;
|
||||||
|
}
|
||||||
|
|
||||||
match /contracts/{contractId} {
|
match /contracts/{contractId} {
|
||||||
allow read;
|
allow read;
|
||||||
allow update: if request.resource.data.diff(resource.data).affectedKeys()
|
allow update: if request.resource.data.diff(resource.data).affectedKeys()
|
||||||
|
|
|
@ -37,6 +37,8 @@ import {
|
||||||
resolvedPayout,
|
resolvedPayout,
|
||||||
getContractBetNullMetrics,
|
getContractBetNullMetrics,
|
||||||
} from '../../common/calculate'
|
} from '../../common/calculate'
|
||||||
|
import { useTimeSinceFirstRender } from '../hooks/use-time-since-first-render'
|
||||||
|
import { trackLatency } from '../lib/firebase/tracking'
|
||||||
|
|
||||||
type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
|
type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
|
||||||
type BetFilter = 'open' | 'closed' | 'resolved' | 'all'
|
type BetFilter = 'open' | 'closed' | 'resolved' | 'all'
|
||||||
|
@ -67,6 +69,14 @@ export function BetsList(props: { user: User }) {
|
||||||
}
|
}
|
||||||
}, [bets])
|
}, [bets])
|
||||||
|
|
||||||
|
const getTime = useTimeSinceFirstRender()
|
||||||
|
useEffect(() => {
|
||||||
|
if (bets && contracts) {
|
||||||
|
trackLatency('portfolio', getTime())
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [!!bets, !!contracts])
|
||||||
|
|
||||||
if (!bets || !contracts) {
|
if (!bets || !contracts) {
|
||||||
return <LoadingIndicator />
|
return <LoadingIndicator />
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {
|
||||||
BinaryContractOutcomeLabel,
|
BinaryContractOutcomeLabel,
|
||||||
FreeResponseOutcomeLabel,
|
FreeResponseOutcomeLabel,
|
||||||
} from '../outcome-label'
|
} from '../outcome-label'
|
||||||
import { getOutcomeProbability } from '../../../common/calculate'
|
import { getOutcomeProbability, getTopAnswer } from '../../../common/calculate'
|
||||||
import { AbbrContractDetails } from './contract-details'
|
import { AbbrContractDetails } from './contract-details'
|
||||||
|
|
||||||
export function ContractCard(props: {
|
export function ContractCard(props: {
|
||||||
|
@ -122,18 +122,6 @@ export function BinaryResolutionOrChance(props: {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTopAnswer(contract: FreeResponseContract) {
|
|
||||||
const { answers } = contract
|
|
||||||
const top = _.maxBy(
|
|
||||||
answers.map((answer) => ({
|
|
||||||
answer,
|
|
||||||
prob: getOutcomeProbability(contract, answer.id),
|
|
||||||
})),
|
|
||||||
({ prob }) => prob
|
|
||||||
)
|
|
||||||
return top?.answer
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FreeResponseResolutionOrChance(props: {
|
export function FreeResponseResolutionOrChance(props: {
|
||||||
contract: FreeResponseContract
|
contract: FreeResponseContract
|
||||||
truncate: 'short' | 'long' | 'none'
|
truncate: 'short' | 'long' | 'none'
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { Avatar } from '../avatar'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { ContractInfoDialog } from './contract-info-dialog'
|
import { ContractInfoDialog } from './contract-info-dialog'
|
||||||
import { Bet } from '../../../common/bet'
|
import { Bet } from '../../../common/bet'
|
||||||
|
import NewContractBadge from '../new-contract-badge'
|
||||||
|
|
||||||
export function AbbrContractDetails(props: {
|
export function AbbrContractDetails(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -25,7 +26,8 @@ export function AbbrContractDetails(props: {
|
||||||
showCloseTime?: boolean
|
showCloseTime?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { contract, showHotVolume, showCloseTime } = props
|
const { contract, showHotVolume, showCloseTime } = props
|
||||||
const { volume24Hours, creatorName, creatorUsername, closeTime } = contract
|
const { volume, volume24Hours, creatorName, creatorUsername, closeTime } =
|
||||||
|
contract
|
||||||
const { volumeLabel } = contractMetrics(contract)
|
const { volumeLabel } = contractMetrics(contract)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -54,11 +56,10 @@ export function AbbrContractDetails(props: {
|
||||||
{(closeTime || 0) < Date.now() ? 'Closed' : 'Closes'}{' '}
|
{(closeTime || 0) < Date.now() ? 'Closed' : 'Closes'}{' '}
|
||||||
{fromNow(closeTime || 0)}
|
{fromNow(closeTime || 0)}
|
||||||
</Row>
|
</Row>
|
||||||
|
) : volume > 0 ? (
|
||||||
|
<Row>{volumeLabel}</Row>
|
||||||
) : (
|
) : (
|
||||||
<Row className="gap-1">
|
<NewContractBadge />
|
||||||
{/* <DatabaseIcon className="h-5 w-5" /> */}
|
|
||||||
{volumeLabel}
|
|
||||||
</Row>
|
|
||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
UserIcon,
|
UserIcon,
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
|
SparklesIcon,
|
||||||
} from '@heroicons/react/solid'
|
} from '@heroicons/react/solid'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import Textarea from 'react-expanding-textarea'
|
import Textarea from 'react-expanding-textarea'
|
||||||
|
@ -47,6 +48,8 @@ import { User } from '../../../common/user'
|
||||||
import { Modal } from '../layout/modal'
|
import { Modal } from '../layout/modal'
|
||||||
import { trackClick } from '../../lib/firebase/tracking'
|
import { trackClick } from '../../lib/firebase/tracking'
|
||||||
import { firebaseLogin } from '../../lib/firebase/users'
|
import { firebaseLogin } from '../../lib/firebase/users'
|
||||||
|
import { DAY_MS } from '../../../common/util/time'
|
||||||
|
import NewContractBadge from '../new-contract-badge'
|
||||||
|
|
||||||
export function FeedItems(props: {
|
export function FeedItems(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
|
@ -385,19 +388,17 @@ export function FeedQuestion(props: {
|
||||||
contractPath?: string
|
contractPath?: string
|
||||||
}) {
|
}) {
|
||||||
const { contract, showDescription } = props
|
const { contract, showDescription } = props
|
||||||
const { creatorName, creatorUsername, question, resolution, outcomeType } =
|
const {
|
||||||
contract
|
creatorName,
|
||||||
|
creatorUsername,
|
||||||
|
question,
|
||||||
|
outcomeType,
|
||||||
|
volume,
|
||||||
|
createdTime,
|
||||||
|
} = contract
|
||||||
const { volumeLabel } = contractMetrics(contract)
|
const { volumeLabel } = contractMetrics(contract)
|
||||||
const isBinary = outcomeType === 'BINARY'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
|
const isNew = createdTime > Date.now() - DAY_MS
|
||||||
// const closeMessage =
|
|
||||||
// contract.isResolved || !contract.closeTime ? null : (
|
|
||||||
// <>
|
|
||||||
// <span className="mx-2">•</span>
|
|
||||||
// {contract.closeTime > Date.now() ? 'Closes' : 'Closed'}
|
|
||||||
// <RelativeTimestamp time={contract.closeTime || 0} />
|
|
||||||
// </>
|
|
||||||
// )
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -414,10 +415,15 @@ export function FeedQuestion(props: {
|
||||||
/>{' '}
|
/>{' '}
|
||||||
asked
|
asked
|
||||||
{/* Currently hidden on mobile; ideally we'd fit this in somewhere. */}
|
{/* Currently hidden on mobile; ideally we'd fit this in somewhere. */}
|
||||||
<span className="float-right hidden text-gray-400 sm:inline">
|
<div className="relative -top-2 float-right ">
|
||||||
{volumeLabel}
|
{isNew || volume === 0 ? (
|
||||||
{/* {closeMessage} */}
|
<NewContractBadge />
|
||||||
</span>
|
) : (
|
||||||
|
<span className="hidden text-gray-400 sm:inline">
|
||||||
|
{volumeLabel}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Col className="items-start justify-between gap-2 sm:flex-row sm:gap-4">
|
<Col className="items-start justify-between gap-2 sm:flex-row sm:gap-4">
|
||||||
<Col>
|
<Col>
|
||||||
|
|
9
web/components/new-contract-badge.tsx
Normal file
9
web/components/new-contract-badge.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { SparklesIcon } from '@heroicons/react/solid'
|
||||||
|
|
||||||
|
export default function NewContractBadge() {
|
||||||
|
return (
|
||||||
|
<span className="inline-flex items-center gap-1 rounded-full bg-blue-100 px-3 py-0.5 text-sm font-medium text-blue-800">
|
||||||
|
<SparklesIcon className="h-4 w-4" aria-hidden="true" /> New
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
|
@ -8,6 +8,14 @@ import { logInterpolation } from '../../common/util/math'
|
||||||
import { getRecommendedContracts } from '../../common/recommended-contracts'
|
import { getRecommendedContracts } from '../../common/recommended-contracts'
|
||||||
import { useSeenContracts } from './use-seen-contracts'
|
import { useSeenContracts } from './use-seen-contracts'
|
||||||
import { useGetUserBetContractIds, useUserBetContracts } from './use-user-bets'
|
import { useGetUserBetContractIds, useUserBetContracts } from './use-user-bets'
|
||||||
|
import { DAY_MS } from '../../common/util/time'
|
||||||
|
import {
|
||||||
|
getProbability,
|
||||||
|
getOutcomeProbability,
|
||||||
|
getTopAnswer,
|
||||||
|
} from '../../common/calculate'
|
||||||
|
import { useTimeSinceFirstRender } from './use-time-since-first-render'
|
||||||
|
import { trackLatency } from '../lib/firebase/tracking'
|
||||||
|
|
||||||
const MAX_FEED_CONTRACTS = 75
|
const MAX_FEED_CONTRACTS = 75
|
||||||
|
|
||||||
|
@ -29,8 +37,15 @@ export const useAlgoFeed = (
|
||||||
|
|
||||||
const [algoFeed, setAlgoFeed] = useState<Contract[]>([])
|
const [algoFeed, setAlgoFeed] = useState<Contract[]>([])
|
||||||
|
|
||||||
|
const getTime = useTimeSinceFirstRender()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialContracts && initialBets && initialComments) {
|
if (
|
||||||
|
initialContracts &&
|
||||||
|
initialBets &&
|
||||||
|
initialComments &&
|
||||||
|
yourBetContractIds
|
||||||
|
) {
|
||||||
const eligibleContracts = initialContracts.filter(
|
const eligibleContracts = initialContracts.filter(
|
||||||
(c) => !c.isResolved && (c.closeTime ?? Infinity) > Date.now()
|
(c) => !c.isResolved && (c.closeTime ?? Infinity) > Date.now()
|
||||||
)
|
)
|
||||||
|
@ -42,6 +57,7 @@ export const useAlgoFeed = (
|
||||||
seenContracts
|
seenContracts
|
||||||
)
|
)
|
||||||
setAlgoFeed(contracts)
|
setAlgoFeed(contracts)
|
||||||
|
trackLatency('feed', getTime())
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
initialBets,
|
initialBets,
|
||||||
|
@ -49,6 +65,7 @@ export const useAlgoFeed = (
|
||||||
initialContracts,
|
initialContracts,
|
||||||
seenContracts,
|
seenContracts,
|
||||||
yourBetContractIds,
|
yourBetContractIds,
|
||||||
|
getTime,
|
||||||
])
|
])
|
||||||
|
|
||||||
return algoFeed
|
return algoFeed
|
||||||
|
@ -120,11 +137,13 @@ function getContractsActivityScores(
|
||||||
)
|
)
|
||||||
|
|
||||||
const scoredContracts = contracts.map((contract) => {
|
const scoredContracts = contracts.map((contract) => {
|
||||||
|
const { outcomeType } = contract
|
||||||
|
|
||||||
const seenTime = seenContracts[contract.id]
|
const seenTime = seenContracts[contract.id]
|
||||||
const lastCommentTime = contractMostRecentComment[contract.id]?.createdTime
|
const lastCommentTime = contractMostRecentComment[contract.id]?.createdTime
|
||||||
const hasNewComments =
|
const hasNewComments =
|
||||||
!seenTime || (lastCommentTime && lastCommentTime > seenTime)
|
!seenTime || (lastCommentTime && lastCommentTime > seenTime)
|
||||||
const newCommentScore = hasNewComments ? 1 : 0.75
|
const newCommentScore = hasNewComments ? 1 : 0.5
|
||||||
|
|
||||||
const commentCount = contractComments[contract.id]?.length ?? 0
|
const commentCount = contractComments[contract.id]?.length ?? 0
|
||||||
const betCount = contractBets[contract.id]?.length ?? 0
|
const betCount = contractBets[contract.id]?.length ?? 0
|
||||||
|
@ -132,25 +151,39 @@ function getContractsActivityScores(
|
||||||
const activityCountScore =
|
const activityCountScore =
|
||||||
0.5 + 0.5 * logInterpolation(0, 200, activtyCount)
|
0.5 + 0.5 * logInterpolation(0, 200, activtyCount)
|
||||||
|
|
||||||
const lastBetTime = contractMostRecentBet[contract.id]?.createdTime
|
const lastBetTime =
|
||||||
const timeSinceLastBet = !lastBetTime
|
contractMostRecentBet[contract.id]?.createdTime ?? contract.createdTime
|
||||||
? contract.createdTime
|
const timeSinceLastBet = Date.now() - lastBetTime
|
||||||
: Date.now() - lastBetTime
|
const daysAgo = timeSinceLastBet / DAY_MS
|
||||||
const daysAgo = timeSinceLastBet / oneDayMs
|
|
||||||
const timeAgoScore = 1 - logInterpolation(0, 3, daysAgo)
|
const timeAgoScore = 1 - logInterpolation(0, 3, daysAgo)
|
||||||
|
|
||||||
const score = newCommentScore * activityCountScore * timeAgoScore
|
let prob = 0.5
|
||||||
|
if (outcomeType === 'BINARY') {
|
||||||
|
prob = getProbability(contract)
|
||||||
|
} else if (outcomeType === 'FREE_RESPONSE') {
|
||||||
|
const topAnswer = getTopAnswer(contract)
|
||||||
|
if (topAnswer)
|
||||||
|
prob = Math.max(0.5, getOutcomeProbability(contract, topAnswer.id))
|
||||||
|
}
|
||||||
|
const frac = 1 - Math.abs(prob - 0.5) ** 2 / 0.25
|
||||||
|
const probScore = 0.5 + frac * 0.5
|
||||||
|
|
||||||
|
const score =
|
||||||
|
newCommentScore * activityCountScore * timeAgoScore * probScore
|
||||||
|
|
||||||
// Map score to [0.5, 1] since no recent activty is not a deal breaker.
|
// Map score to [0.5, 1] since no recent activty is not a deal breaker.
|
||||||
const mappedScore = 0.5 + score / 2
|
const mappedScore = 0.5 + score / 2
|
||||||
return [contract.id, mappedScore] as [string, number]
|
const newMappedScore = 0.75 + score / 4
|
||||||
|
|
||||||
|
const isNew = Date.now() < contract.createdTime + DAY_MS
|
||||||
|
const activityScore = isNew ? newMappedScore : mappedScore
|
||||||
|
|
||||||
|
return [contract.id, activityScore] as [string, number]
|
||||||
})
|
})
|
||||||
|
|
||||||
return _.fromPairs(scoredContracts)
|
return _.fromPairs(scoredContracts)
|
||||||
}
|
}
|
||||||
|
|
||||||
const oneDayMs = 24 * 60 * 60 * 1000
|
|
||||||
|
|
||||||
function getSeenContractsScore(
|
function getSeenContractsScore(
|
||||||
contract: Contract,
|
contract: Contract,
|
||||||
seenContracts: { [contractId: string]: number }
|
seenContracts: { [contractId: string]: number }
|
||||||
|
@ -160,7 +193,7 @@ function getSeenContractsScore(
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const daysAgo = (Date.now() - lastSeen) / oneDayMs
|
const daysAgo = (Date.now() - lastSeen) / DAY_MS
|
||||||
|
|
||||||
if (daysAgo < 0.5) {
|
if (daysAgo < 0.5) {
|
||||||
const frac = logInterpolation(0, 0.5, daysAgo)
|
const frac = logInterpolation(0, 0.5, daysAgo)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
listenForContracts,
|
listenForContracts,
|
||||||
listenForHotContracts,
|
listenForHotContracts,
|
||||||
listenForInactiveContracts,
|
listenForInactiveContracts,
|
||||||
|
listenForNewContracts,
|
||||||
} from '../lib/firebase/contracts'
|
} from '../lib/firebase/contracts'
|
||||||
import { listenForTaggedContracts } from '../lib/firebase/folds'
|
import { listenForTaggedContracts } from '../lib/firebase/folds'
|
||||||
|
|
||||||
|
@ -20,13 +21,22 @@ export const useContracts = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useActiveContracts = () => {
|
export const useActiveContracts = () => {
|
||||||
const [contracts, setContracts] = useState<Contract[] | undefined>()
|
const [activeContracts, setActiveContracts] = useState<
|
||||||
|
Contract[] | undefined
|
||||||
|
>()
|
||||||
|
const [newContracts, setNewContracts] = useState<Contract[] | undefined>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return listenForActiveContracts(setContracts)
|
return listenForActiveContracts(setActiveContracts)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return contracts
|
useEffect(() => {
|
||||||
|
return listenForNewContracts(setNewContracts)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!activeContracts || !newContracts) return undefined
|
||||||
|
|
||||||
|
return [...activeContracts, ...newContracts]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useInactiveContracts = () => {
|
export const useInactiveContracts = () => {
|
||||||
|
|
13
web/hooks/use-time-since-first-render.ts
Normal file
13
web/hooks/use-time-since-first-render.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { useCallback, useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
export function useTimeSinceFirstRender() {
|
||||||
|
const startTimeRef = useRef(0)
|
||||||
|
useEffect(() => {
|
||||||
|
startTimeRef.current = Date.now()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return useCallback(() => {
|
||||||
|
if (!startTimeRef.current) return 0
|
||||||
|
return Date.now() - startTimeRef.current
|
||||||
|
}, [])
|
||||||
|
}
|
|
@ -54,13 +54,15 @@ export const useUserBetContracts = (userId: string | undefined) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGetUserBetContractIds = (userId: string | undefined) => {
|
export const useGetUserBetContractIds = (userId: string | undefined) => {
|
||||||
const [contractIds, setContractIds] = useState<string[]>([])
|
const [contractIds, setContractIds] = useState<string[] | undefined>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const key = `user-bet-contractIds-${userId}`
|
if (userId) {
|
||||||
const userBetContractJson = localStorage.getItem(key)
|
const key = `user-bet-contractIds-${userId}`
|
||||||
if (userBetContractJson) {
|
const userBetContractJson = localStorage.getItem(key)
|
||||||
setContractIds(JSON.parse(userBetContractJson))
|
if (userBetContractJson) {
|
||||||
|
setContractIds(JSON.parse(userBetContractJson))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [userId])
|
}, [userId])
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { getDpmProbability } from '../../../common/calculate-dpm'
|
||||||
import { createRNG, shuffle } from '../../../common/util/random'
|
import { createRNG, shuffle } from '../../../common/util/random'
|
||||||
import { getCpmmProbability } from '../../../common/calculate-cpmm'
|
import { getCpmmProbability } from '../../../common/calculate-cpmm'
|
||||||
import { formatMoney, formatPercent } from '../../../common/util/format'
|
import { formatMoney, formatPercent } from '../../../common/util/format'
|
||||||
|
import { DAY_MS } from '../../../common/util/time'
|
||||||
export type { Contract }
|
export type { Contract }
|
||||||
|
|
||||||
export function contractPath(contract: Contract) {
|
export function contractPath(contract: Contract) {
|
||||||
|
@ -162,6 +163,19 @@ export function listenForInactiveContracts(
|
||||||
return listenForValues<Contract>(inactiveContractsQuery, setContracts)
|
return listenForValues<Contract>(inactiveContractsQuery, setContracts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newContractsQuery = query(
|
||||||
|
contractCollection,
|
||||||
|
where('isResolved', '==', false),
|
||||||
|
where('volume7Days', '==', 0),
|
||||||
|
where('createdTime', '>', Date.now() - 7 * DAY_MS)
|
||||||
|
)
|
||||||
|
|
||||||
|
export function listenForNewContracts(
|
||||||
|
setContracts: (contracts: Contract[]) => void
|
||||||
|
) {
|
||||||
|
return listenForValues<Contract>(newContractsQuery, setContracts)
|
||||||
|
}
|
||||||
|
|
||||||
export function listenForContract(
|
export function listenForContract(
|
||||||
contractId: string,
|
contractId: string,
|
||||||
setContract: (contract: Contract | null) => void
|
setContract: (contract: Contract | null) => void
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { doc, collection, setDoc } from 'firebase/firestore'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import { db } from './init'
|
import { db } from './init'
|
||||||
import { ClickEvent, View } from '../../../common/tracking'
|
import { ClickEvent, LatencyEvent, View } from '../../../common/tracking'
|
||||||
import { listenForLogin, User } from './users'
|
import { listenForLogin, User } from './users'
|
||||||
|
|
||||||
let user: User | null = null
|
let user: User | null = null
|
||||||
|
@ -34,3 +34,19 @@ export async function trackClick(contractId: string) {
|
||||||
|
|
||||||
return await setDoc(ref, clickEvent)
|
return await setDoc(ref, clickEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function trackLatency(
|
||||||
|
type: 'feed' | 'portfolio',
|
||||||
|
latency: number
|
||||||
|
) {
|
||||||
|
if (!user) return
|
||||||
|
const ref = doc(collection(db, 'private-users', user.id, 'latency'))
|
||||||
|
|
||||||
|
const latencyEvent: LatencyEvent = {
|
||||||
|
type,
|
||||||
|
latency,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return await setDoc(ref, latencyEvent)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user