Hot markets! 🔥
This commit is contained in:
parent
4386422f02
commit
1bc323d575
|
@ -120,7 +120,7 @@ export function ContractDetails(props: {
|
||||||
|
|
||||||
<Row className="gap-2 flex-wrap">
|
<Row className="gap-2 flex-wrap">
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<div className="bg-gray-100 px-1">
|
<div key={tag} className="bg-gray-100 px-1">
|
||||||
<Linkify text={tag} gray />
|
<Linkify text={tag} gray />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { parseTags } from '../lib/util/parse'
|
||||||
import { ContractCard } from './contract-card'
|
import { ContractCard } from './contract-card'
|
||||||
import { Sort, useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
|
import { Sort, useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
|
||||||
|
|
||||||
function ContractsGrid(props: { contracts: Contract[] }) {
|
export function ContractsGrid(props: { contracts: Contract[] }) {
|
||||||
const [resolvedContracts, activeContracts] = _.partition(
|
const [resolvedContracts, activeContracts] = _.partition(
|
||||||
props.contracts,
|
props.contracts,
|
||||||
(c) => c.isResolved
|
(c) => c.isResolved
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
|
import _ from 'lodash'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Contract, listenForContracts } from '../lib/firebase/contracts'
|
import { Bet, listenForRecentBets } from '../lib/firebase/bets'
|
||||||
|
import {
|
||||||
|
computeHotContracts,
|
||||||
|
Contract,
|
||||||
|
listenForContracts,
|
||||||
|
} from '../lib/firebase/contracts'
|
||||||
|
|
||||||
export const useContracts = () => {
|
export const useContracts = () => {
|
||||||
const [contracts, setContracts] = useState<Contract[] | 'loading'>('loading')
|
const [contracts, setContracts] = useState<Contract[] | 'loading'>('loading')
|
||||||
|
@ -10,3 +16,16 @@ export const useContracts = () => {
|
||||||
|
|
||||||
return contracts
|
return contracts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useHotContracts = () => {
|
||||||
|
const [recentBets, setRecentBets] = useState<Bet[] | 'loading'>('loading')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const oneDay = 1000 * 60 * 60 * 24
|
||||||
|
return listenForRecentBets(oneDay, setRecentBets)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (recentBets === 'loading') return 'loading'
|
||||||
|
|
||||||
|
return computeHotContracts(recentBets)
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ import {
|
||||||
query,
|
query,
|
||||||
onSnapshot,
|
onSnapshot,
|
||||||
where,
|
where,
|
||||||
|
getDocs,
|
||||||
} from 'firebase/firestore'
|
} from 'firebase/firestore'
|
||||||
|
import _ from 'lodash'
|
||||||
import { db } from './init'
|
import { db } from './init'
|
||||||
|
|
||||||
export type Bet = {
|
export type Bet = {
|
||||||
|
@ -62,3 +64,34 @@ export function listenForUserBets(
|
||||||
setBets(bets)
|
setBets(bets)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listenForRecentBets(
|
||||||
|
timePeriodMs: number,
|
||||||
|
setBets: (bets: Bet[]) => void
|
||||||
|
) {
|
||||||
|
const recentQuery = query(
|
||||||
|
collectionGroup(db, 'bets'),
|
||||||
|
where('createdTime', '>', Date.now() - timePeriodMs)
|
||||||
|
)
|
||||||
|
return onSnapshot(recentQuery, (snap) => {
|
||||||
|
const bets = snap.docs.map((doc) => doc.data() as Bet)
|
||||||
|
|
||||||
|
bets.sort((bet1, bet2) => bet1.createdTime - bet2.createdTime)
|
||||||
|
|
||||||
|
setBets(bets)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRecentBets(timePeriodMs: number) {
|
||||||
|
const recentQuery = query(
|
||||||
|
collectionGroup(db, 'bets'),
|
||||||
|
where('createdTime', '>', Date.now() - timePeriodMs)
|
||||||
|
)
|
||||||
|
|
||||||
|
const snapshot = await getDocs(recentQuery)
|
||||||
|
const bets = snapshot.docs.map((doc) => doc.data() as Bet)
|
||||||
|
|
||||||
|
bets.sort((bet1, bet2) => bet1.createdTime - bet2.createdTime)
|
||||||
|
|
||||||
|
return bets
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,10 @@ import {
|
||||||
onSnapshot,
|
onSnapshot,
|
||||||
orderBy,
|
orderBy,
|
||||||
getDoc,
|
getDoc,
|
||||||
limit,
|
|
||||||
} from 'firebase/firestore'
|
} from 'firebase/firestore'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { Bet, getRecentBets } from './bets'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export type Contract = {
|
export type Contract = {
|
||||||
id: string
|
id: string
|
||||||
|
@ -131,3 +132,17 @@ export function listenForContract(
|
||||||
setContract((contractSnap.data() ?? null) as Contract | null)
|
setContract((contractSnap.data() ?? null) as Contract | null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function computeHotContracts(recentBets: Bet[]) {
|
||||||
|
const contractBets = _.groupBy(recentBets, (bet) => bet.contractId)
|
||||||
|
const hotContractIds = _.sortBy(Object.keys(contractBets), (contractId) =>
|
||||||
|
_.sumBy(contractBets[contractId], (bet) => -1 * bet.amount)
|
||||||
|
).slice(0, 4)
|
||||||
|
return hotContractIds
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getHotContracts() {
|
||||||
|
const oneDay = 1000 * 60 * 60 * 24
|
||||||
|
const recentBets = await getRecentBets(oneDay)
|
||||||
|
return computeHotContracts(recentBets)
|
||||||
|
}
|
||||||
|
|
|
@ -3,26 +3,42 @@ import React from 'react'
|
||||||
import { useUser } from '../hooks/use-user'
|
import { useUser } from '../hooks/use-user'
|
||||||
import Markets from './markets'
|
import Markets from './markets'
|
||||||
import LandingPage from './landing-page'
|
import LandingPage from './landing-page'
|
||||||
import { Contract, listAllContracts } from '../lib/firebase/contracts'
|
import {
|
||||||
|
Contract,
|
||||||
|
getHotContracts,
|
||||||
|
listAllContracts,
|
||||||
|
} from '../lib/firebase/contracts'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const contracts = await listAllContracts().catch((_) => [])
|
const [contracts, hotContractIds] = await Promise.all([
|
||||||
|
listAllContracts().catch((_) => []),
|
||||||
|
getHotContracts().catch(() => []),
|
||||||
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
contracts,
|
contracts,
|
||||||
|
hotContractIds,
|
||||||
},
|
},
|
||||||
|
|
||||||
revalidate: 60, // regenerate after a minute
|
revalidate: 60, // regenerate after a minute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Home = (props: { contracts: Contract[] }) => {
|
const Home = (props: { contracts: Contract[]; hotContractIds: string[] }) => {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
|
||||||
if (user === undefined) return <></>
|
if (user === undefined) return <></>
|
||||||
|
|
||||||
return user ? <Markets contracts={props.contracts} /> : <LandingPage />
|
return user ? (
|
||||||
|
<Markets
|
||||||
|
contracts={props.contracts}
|
||||||
|
hotContractIds={props.hotContractIds}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<LandingPage />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Home
|
export default Home
|
||||||
|
|
|
@ -1,30 +1,61 @@
|
||||||
import { SearchableGrid } from '../components/contracts-list'
|
import _ from 'lodash'
|
||||||
|
import { ContractsGrid, SearchableGrid } from '../components/contracts-list'
|
||||||
|
import { Spacer } from '../components/layout/spacer'
|
||||||
import { Page } from '../components/page'
|
import { Page } from '../components/page'
|
||||||
import { useContracts } from '../hooks/use-contracts'
|
import { Title } from '../components/title'
|
||||||
|
import { useContracts, useHotContracts } from '../hooks/use-contracts'
|
||||||
import { useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
|
import { useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
|
||||||
import { Contract, listAllContracts } from '../lib/firebase/contracts'
|
import {
|
||||||
|
Contract,
|
||||||
|
getHotContracts,
|
||||||
|
listAllContracts,
|
||||||
|
} from '../lib/firebase/contracts'
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const contracts = await listAllContracts().catch((_) => [])
|
const [contracts, hotContractIds] = await Promise.all([
|
||||||
|
listAllContracts().catch((_) => []),
|
||||||
|
getHotContracts().catch(() => []),
|
||||||
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
contracts,
|
contracts,
|
||||||
|
hotContractIds,
|
||||||
},
|
},
|
||||||
|
|
||||||
revalidate: 60, // regenerate after a minute
|
revalidate: 60, // regenerate after a minute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Markets(props: { contracts: Contract[] }) {
|
export default function Markets(props: {
|
||||||
|
contracts: Contract[]
|
||||||
|
hotContractIds: string[]
|
||||||
|
}) {
|
||||||
const contracts = useContracts()
|
const contracts = useContracts()
|
||||||
const { query, setQuery, sort, setSort } = useQueryAndSortParams()
|
const { query, setQuery, sort, setSort } = useQueryAndSortParams()
|
||||||
|
const hotContractIds = useHotContracts()
|
||||||
|
|
||||||
|
const readyHotContractIds =
|
||||||
|
hotContractIds === 'loading' ? props.hotContractIds : hotContractIds
|
||||||
|
const readyContracts = contracts === 'loading' ? props.contracts : contracts
|
||||||
|
|
||||||
|
const hotContracts = readyHotContractIds.map(
|
||||||
|
(hotId) =>
|
||||||
|
_.find(readyContracts, (contract) => contract.id === hotId) as Contract
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
<div className="w-full bg-indigo-50 border-2 border-indigo-100 p-6 rounded-lg shadow-md">
|
||||||
|
<Title className="mt-0" text="🔥 Markets" />
|
||||||
|
<ContractsGrid contracts={hotContracts} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spacer h={10} />
|
||||||
|
|
||||||
{(props.contracts || contracts !== 'loading') && (
|
{(props.contracts || contracts !== 'loading') && (
|
||||||
<SearchableGrid
|
<SearchableGrid
|
||||||
contracts={contracts === 'loading' ? props.contracts : contracts}
|
contracts={readyContracts}
|
||||||
query={query}
|
query={query}
|
||||||
setQuery={setQuery}
|
setQuery={setQuery}
|
||||||
sort={sort}
|
sort={sort}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user