Fix some efficiency problems with ContractProbGraph
(#806)
* Memoize bets input to ContractOverview * Optimize a bunch of nonsense in `ContractProbGraph`
This commit is contained in:
parent
3e976eadac
commit
5d8f5d41fc
|
@ -16,6 +16,7 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
}) {
|
}) {
|
||||||
const { contract, height } = props
|
const { contract, height } = props
|
||||||
const { resolutionTime, closeTime, outcomeType } = contract
|
const { resolutionTime, closeTime, outcomeType } = contract
|
||||||
|
const now = Date.now()
|
||||||
const isBinary = outcomeType === 'BINARY'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
const isLogScale = outcomeType === 'PSEUDO_NUMERIC' && contract.isLogScale
|
const isLogScale = outcomeType === 'PSEUDO_NUMERIC' && contract.isLogScale
|
||||||
|
|
||||||
|
@ -23,10 +24,7 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
|
|
||||||
const startProb = getInitialProbability(contract)
|
const startProb = getInitialProbability(contract)
|
||||||
|
|
||||||
const times = [
|
const times = [contract.createdTime, ...bets.map((bet) => bet.createdTime)]
|
||||||
contract.createdTime,
|
|
||||||
...bets.map((bet) => bet.createdTime),
|
|
||||||
].map((time) => new Date(time))
|
|
||||||
|
|
||||||
const f: (p: number) => number = isBinary
|
const f: (p: number) => number = isBinary
|
||||||
? (p) => p
|
? (p) => p
|
||||||
|
@ -36,17 +34,17 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
|
|
||||||
const probs = [startProb, ...bets.map((bet) => bet.probAfter)].map(f)
|
const probs = [startProb, ...bets.map((bet) => bet.probAfter)].map(f)
|
||||||
|
|
||||||
const isClosed = !!closeTime && Date.now() > closeTime
|
const isClosed = !!closeTime && now > closeTime
|
||||||
const latestTime = dayjs(
|
const latestTime = dayjs(
|
||||||
resolutionTime && isClosed
|
resolutionTime && isClosed
|
||||||
? Math.min(resolutionTime, closeTime)
|
? Math.min(resolutionTime, closeTime)
|
||||||
: isClosed
|
: isClosed
|
||||||
? closeTime
|
? closeTime
|
||||||
: resolutionTime ?? Date.now()
|
: resolutionTime ?? now
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add a fake datapoint so the line continues to the right
|
// Add a fake datapoint so the line continues to the right
|
||||||
times.push(latestTime.toDate())
|
times.push(latestTime.valueOf())
|
||||||
probs.push(probs[probs.length - 1])
|
probs.push(probs[probs.length - 1])
|
||||||
|
|
||||||
const quartiles = [0, 25, 50, 75, 100]
|
const quartiles = [0, 25, 50, 75, 100]
|
||||||
|
@ -58,16 +56,16 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
const numXTickValues = !width || width < 800 ? 2 : 5
|
const numXTickValues = !width || width < 800 ? 2 : 5
|
||||||
const startDate = times[0]
|
const startDate = dayjs(times[0])
|
||||||
const endDate = dayjs(startDate).add(1, 'hour').isAfter(latestTime)
|
const endDate = startDate.add(1, 'hour').isAfter(latestTime)
|
||||||
? latestTime.add(1, 'hours').toDate()
|
? latestTime.add(1, 'hours')
|
||||||
: latestTime.toDate()
|
: latestTime
|
||||||
const includeMinute = dayjs(endDate).diff(startDate, 'hours') < 2
|
const includeMinute = endDate.diff(startDate, 'hours') < 2
|
||||||
|
|
||||||
// Minimum number of points for the graph to have. For smooth tooltip movement
|
// Minimum number of points for the graph to have. For smooth tooltip movement
|
||||||
// On first load, width is undefined, skip adding extra points to let page load faster
|
// If we aren't actually loading any data yet, skip adding extra points to let page load faster
|
||||||
// This fn runs again once DOM is finished loading
|
// This fn runs again once DOM is finished loading
|
||||||
const totalPoints = width ? (width > 800 ? 300 : 50) : 1
|
const totalPoints = width && bets.length ? (width > 800 ? 300 : 50) : 1
|
||||||
|
|
||||||
const timeStep: number = latestTime.diff(startDate, 'ms') / totalPoints
|
const timeStep: number = latestTime.diff(startDate, 'ms') / totalPoints
|
||||||
|
|
||||||
|
@ -75,20 +73,16 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
const s = isBinary ? 100 : 1
|
const s = isBinary ? 100 : 1
|
||||||
|
|
||||||
for (let i = 0; i < times.length - 1; i++) {
|
for (let i = 0; i < times.length - 1; i++) {
|
||||||
points[points.length] = { x: times[i], y: s * probs[i] }
|
const p = probs[i]
|
||||||
const numPoints: number = Math.floor(
|
const d0 = times[i]
|
||||||
dayjs(times[i + 1]).diff(dayjs(times[i]), 'ms') / timeStep
|
const d1 = times[i + 1]
|
||||||
)
|
const msDiff = d1 - d0
|
||||||
|
const numPoints = Math.floor(msDiff / timeStep)
|
||||||
|
points.push({ x: new Date(times[i]), y: s * p })
|
||||||
if (numPoints > 1) {
|
if (numPoints > 1) {
|
||||||
const thisTimeStep: number =
|
const thisTimeStep: number = msDiff / numPoints
|
||||||
dayjs(times[i + 1]).diff(dayjs(times[i]), 'ms') / numPoints
|
|
||||||
for (let n = 1; n < numPoints; n++) {
|
for (let n = 1; n < numPoints; n++) {
|
||||||
points[points.length] = {
|
points.push({ x: new Date(d0 + thisTimeStep * n), y: s * p })
|
||||||
x: dayjs(times[i])
|
|
||||||
.add(thisTimeStep * n, 'ms')
|
|
||||||
.toDate(),
|
|
||||||
y: s * probs[i],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,8 +91,8 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
{ id: 'Yes', data: points, color: isBinary ? '#11b981' : '#5fa5f9' },
|
{ id: 'Yes', data: points, color: isBinary ? '#11b981' : '#5fa5f9' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const multiYear = !dayjs(startDate).isSame(latestTime, 'year')
|
const multiYear = !startDate.isSame(latestTime, 'year')
|
||||||
const lessThanAWeek = dayjs(startDate).add(8, 'day').isAfter(latestTime)
|
const lessThanAWeek = startDate.add(8, 'day').isAfter(latestTime)
|
||||||
|
|
||||||
const formatter = isBinary
|
const formatter = isBinary
|
||||||
? formatPercent
|
? formatPercent
|
||||||
|
@ -133,16 +127,16 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
}}
|
}}
|
||||||
xScale={{
|
xScale={{
|
||||||
type: 'time',
|
type: 'time',
|
||||||
min: startDate,
|
min: startDate.toDate(),
|
||||||
max: endDate,
|
max: endDate.toDate(),
|
||||||
}}
|
}}
|
||||||
xFormat={(d) =>
|
xFormat={(d) =>
|
||||||
formatTime(+d.valueOf(), multiYear, lessThanAWeek, lessThanAWeek)
|
formatTime(now, +d.valueOf(), multiYear, lessThanAWeek, lessThanAWeek)
|
||||||
}
|
}
|
||||||
axisBottom={{
|
axisBottom={{
|
||||||
tickValues: numXTickValues,
|
tickValues: numXTickValues,
|
||||||
format: (time) =>
|
format: (time) =>
|
||||||
formatTime(+time, multiYear, lessThanAWeek, includeMinute),
|
formatTime(now, +time, multiYear, lessThanAWeek, includeMinute),
|
||||||
}}
|
}}
|
||||||
colors={{ datum: 'color' }}
|
colors={{ datum: 'color' }}
|
||||||
curve="stepAfter"
|
curve="stepAfter"
|
||||||
|
@ -178,23 +172,20 @@ function formatPercent(y: DatumValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(
|
function formatTime(
|
||||||
|
now: number,
|
||||||
time: number,
|
time: number,
|
||||||
includeYear: boolean,
|
includeYear: boolean,
|
||||||
includeHour: boolean,
|
includeHour: boolean,
|
||||||
includeMinute: boolean
|
includeMinute: boolean
|
||||||
) {
|
) {
|
||||||
const d = dayjs(time)
|
const d = dayjs(time)
|
||||||
|
if (d.add(1, 'minute').isAfter(now) && d.subtract(1, 'minute').isBefore(now))
|
||||||
if (
|
|
||||||
d.add(1, 'minute').isAfter(Date.now()) &&
|
|
||||||
d.subtract(1, 'minute').isBefore(Date.now())
|
|
||||||
)
|
|
||||||
return 'Now'
|
return 'Now'
|
||||||
|
|
||||||
let format: string
|
let format: string
|
||||||
if (d.isSame(Date.now(), 'day')) {
|
if (d.isSame(now, 'day')) {
|
||||||
format = '[Today]'
|
format = '[Today]'
|
||||||
} else if (d.add(1, 'day').isSame(Date.now(), 'day')) {
|
} else if (d.add(1, 'day').isSame(now, 'day')) {
|
||||||
format = '[Yesterday]'
|
format = '[Yesterday]'
|
||||||
} else {
|
} else {
|
||||||
format = 'MMM D'
|
format = 'MMM D'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { ArrowLeftIcon } from '@heroicons/react/outline'
|
import { ArrowLeftIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
import { useContractWithPreload } from 'web/hooks/use-contract'
|
import { useContractWithPreload } from 'web/hooks/use-contract'
|
||||||
|
@ -165,6 +165,10 @@ export function ContractPageContent(
|
||||||
})
|
})
|
||||||
|
|
||||||
const bets = useBets(contract.id) ?? props.bets
|
const bets = useBets(contract.id) ?? props.bets
|
||||||
|
const nonChallengeBets = useMemo(
|
||||||
|
() => bets.filter((b) => !b.challengeSlug),
|
||||||
|
[bets]
|
||||||
|
)
|
||||||
|
|
||||||
// Sort for now to see if bug is fixed.
|
// Sort for now to see if bug is fixed.
|
||||||
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
||||||
|
@ -220,10 +224,7 @@ export function ContractPageContent(
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ContractOverview
|
<ContractOverview contract={contract} bets={nonChallengeBets} />
|
||||||
contract={contract}
|
|
||||||
bets={bets.filter((b) => !b.challengeSlug)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{outcomeType === 'NUMERIC' && (
|
{outcomeType === 'NUMERIC' && (
|
||||||
<AlertBox
|
<AlertBox
|
||||||
|
|
Loading…
Reference in New Issue
Block a user