diff --git a/common/util/random-string.ts b/common/util/random-string.ts deleted file mode 100644 index a1cc40a9..00000000 --- a/common/util/random-string.ts +++ /dev/null @@ -1 +0,0 @@ -export const randomString = () => Math.random().toString(16).substr(2, 14) diff --git a/common/util/random.ts b/common/util/random.ts new file mode 100644 index 00000000..59ab5ba9 --- /dev/null +++ b/common/util/random.ts @@ -0,0 +1,46 @@ +export const randomString = () => Math.random().toString(16).substr(2, 14) + +export function createRNG(seed: string) { + // https://stackoverflow.com/a/47593316/1592933 + + function genHash(str: string) { + // xmur3 + for (var i = 0, h = 1779033703 ^ str.length; i < str.length; i++) { + h = Math.imul(h ^ str.charCodeAt(i), 3432918353) + h = (h << 13) | (h >>> 19) + } + return function () { + h = Math.imul(h ^ (h >>> 16), 2246822507) + h = Math.imul(h ^ (h >>> 13), 3266489909) + return (h ^= h >>> 16) >>> 0 + } + } + + const gen = genHash(seed) + let [a, b, c, d] = [gen(), gen(), gen(), gen()] + + // sfc32 + return function () { + a >>>= 0 + b >>>= 0 + c >>>= 0 + d >>>= 0 + var t = (a + b) | 0 + a = b ^ (b >>> 9) + b = (c + (c << 3)) | 0 + c = (c << 21) | (c >>> 11) + d = (d + 1) | 0 + t = (t + d) | 0 + c = (c + t) | 0 + return (t >>> 0) / 4294967296 + } +} + +export const shuffle = (array: any[], rand: () => number) => { + for (let i = 0; i < array.length; i++) { + const swapIndex = Math.floor(rand() * (array.length - i)) + const temp = array[i] + array[i] = array[swapIndex] + array[swapIndex] = temp + } +} diff --git a/functions/src/create-contract.ts b/functions/src/create-contract.ts index e588f42b..4c522e99 100644 --- a/functions/src/create-contract.ts +++ b/functions/src/create-contract.ts @@ -4,7 +4,7 @@ import * as admin from 'firebase-admin' import { chargeUser, getUser } from './utils' import { Contract } from '../../common/contract' import { slugify } from '../../common/util/slugify' -import { randomString } from '../../common/util/random-string' +import { randomString } from '../../common/util/random' import { getNewContract } from '../../common/new-contract' import { getAnteBets, MINIMUM_ANTE } from '../../common/antes' diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts index 9dc3e2bc..6e596c32 100644 --- a/web/lib/firebase/contracts.ts +++ b/web/lib/firebase/contracts.ts @@ -19,6 +19,7 @@ import { app } from './init' import { getValues, listenForValues } from './utils' import { Contract } from '../../../common/contract' import { getProbability } from '../../../common/calculate' +import { createRNG, shuffle } from '../../../common/util/random' export type { Contract } export function contractPath(contract: Contract) { @@ -135,15 +136,24 @@ const hotContractsQuery = query( where('isResolved', '==', false), where('visibility', '==', 'public'), orderBy('volume24Hours', 'desc'), - limit(4) + limit(16) ) +function chooseHotContracts(contracts: Contract[]) { + const fiveMinutes = 5 * 60 * 1000 + const seed = Math.round(Date.now() / fiveMinutes).toString() + shuffle(contracts, createRNG(seed)) + return contracts.slice(0, 4) +} + export function listenForHotContracts( setHotContracts: (contracts: Contract[]) => void ) { - return listenForValues(hotContractsQuery, setHotContracts) + return listenForValues(hotContractsQuery, (contracts) => + setHotContracts(chooseHotContracts(contracts)) + ) } export function getHotContracts() { - return getValues(hotContractsQuery) + return getValues(hotContractsQuery).then(chooseHotContracts) } diff --git a/web/pages/index.tsx b/web/pages/index.tsx index 0959ab43..9e33e84a 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -17,6 +17,7 @@ import { import { Col } from '../components/layout/col' import { ContractCard } from '../components/contract-card' import { Bet, listAllBets } from '../lib/firebase/bets' +import { useHotContracts } from '../hooks/use-contracts' export async function getStaticProps() { const [contracts, hotContracts, recentComments] = await Promise.all([ @@ -51,12 +52,9 @@ const Home = (props: { activeContractComments: Comment[][] hotContracts: Contract[] }) => { - const { - hotContracts, - activeContracts, - activeContractBets, - activeContractComments, - } = props + const { activeContracts, activeContractBets, activeContractComments } = props + + const hotContracts = useHotContracts() ?? props.hotContracts return ( @@ -73,6 +71,8 @@ const Home = (props: { const HotMarkets = (props: { hotContracts: Contract[] }) => { const { hotContracts } = props + if (hotContracts.length < 4) return <> + const [c1, c2, c3, c4] = hotContracts return (