diff --git a/web/components/carousel.tsx b/web/components/carousel.tsx
index 9719ba06..79baa451 100644
--- a/web/components/carousel.tsx
+++ b/web/components/carousel.tsx
@@ -33,7 +33,7 @@ export function Carousel(props: {
}, 500)
// eslint-disable-next-line react-hooks/exhaustive-deps
- useEffect(onScroll, [])
+ useEffect(onScroll, [children])
return (
diff --git a/web/hooks/use-pagination.ts b/web/hooks/use-pagination.ts
index 485afca8..ab991d1f 100644
--- a/web/hooks/use-pagination.ts
+++ b/web/hooks/use-pagination.ts
@@ -103,6 +103,7 @@ export const usePagination =
(opts: PaginationOptions) => {
isEnd: state.isComplete && state.pageEnd >= state.docs.length,
getPrev: () => dispatch({ type: 'PREV' }),
getNext: () => dispatch({ type: 'NEXT' }),
+ allItems: () => state.docs.map((d) => d.data()),
getItems: () =>
state.docs.slice(state.pageStart, state.pageEnd).map((d) => d.data()),
}
diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts
index c7e32f71..5c65b23f 100644
--- a/web/lib/firebase/contracts.ts
+++ b/web/lib/firebase/contracts.ts
@@ -104,11 +104,18 @@ export async function listContracts(creatorId: string): Promise {
return snapshot.docs.map((doc) => doc.data())
}
+export const contractsByGroupSlugQuery = (slug: string) =>
+ query(
+ contracts,
+ where('groupSlugs', 'array-contains', slug),
+ where('isResolved', '==', false),
+ orderBy('popularityScore', 'desc')
+ )
+
export async function listContractsByGroupSlug(
slug: string
): Promise {
- const q = query(contracts, where('groupSlugs', 'array-contains', slug))
- const snapshot = await getDocs(q)
+ const snapshot = await getDocs(contractsByGroupSlugQuery(slug))
return snapshot.docs.map((doc) => doc.data())
}
diff --git a/web/pages/tournaments/index.tsx b/web/pages/tournaments/index.tsx
index 9bfdfb89..c9827f72 100644
--- a/web/pages/tournaments/index.tsx
+++ b/web/pages/tournaments/index.tsx
@@ -1,16 +1,10 @@
import { ClockIcon } from '@heroicons/react/outline'
import { UsersIcon } from '@heroicons/react/solid'
-import {
- BinaryContract,
- Contract,
- PseudoNumericContract,
-} from 'common/contract'
-import { Group } from 'common/group'
-import dayjs, { Dayjs } from 'dayjs'
+import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
-import { keyBy, mapValues, sortBy } from 'lodash'
+import { zip } from 'lodash'
import Image, { ImageProps, StaticImageData } from 'next/image'
import Link from 'next/link'
import { useState } from 'react'
@@ -20,27 +14,33 @@ import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row'
import { Page } from 'web/components/page'
import { SEO } from 'web/components/SEO'
-import { listContractsByGroupSlug } from 'web/lib/firebase/contracts'
+import { contractsByGroupSlugQuery } from 'web/lib/firebase/contracts'
import { getGroup, groupPath } from 'web/lib/firebase/groups'
import elon_pic from './_cspi/Will_Elon_Buy_Twitter.png'
import china_pic from './_cspi/Chinese_Military_Action_against_Taiwan.png'
import mpox_pic from './_cspi/Monkeypox_Cases.png'
import race_pic from './_cspi/Supreme_Court_Ban_Race_in_College_Admissions.png'
import { SiteLink } from 'web/components/site-link'
-import { getProbability } from 'common/calculate'
import { Carousel } from 'web/components/carousel'
+import { usePagination } from 'web/hooks/use-pagination'
+import { LoadingIndicator } from 'web/components/loading-indicator'
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(customParseFormat)
-const toDate = (d: string) => dayjs(d, 'MMM D, YYYY').tz('America/Los_Angeles')
+const toDate = (d: string) =>
+ dayjs(d, 'MMM D, YYYY').tz('America/Los_Angeles').valueOf()
+
+type MarketImage = {
+ marketUrl: string
+ image: StaticImageData
+}
type Tourney = {
title: string
- url?: string
blurb: string // actual description in the click-through
award?: string
- endTime?: Dayjs
+ endTime?: number
groupId: string
}
@@ -50,7 +50,7 @@ const Salem = {
url: 'https://salemcenter.manifold.markets/',
award: '$25,000',
endTime: toDate('Jul 31, 2023'),
- markets: [],
+ contractIds: [],
images: [
{
marketUrl:
@@ -107,33 +107,27 @@ const tourneys: Tourney[] = [
// },
]
-export async function getStaticProps() {
- const groupIds = tourneys
- .map((data) => data.groupId)
- .filter((id) => id != undefined) as string[]
- const groups = (await Promise.all(groupIds.map(getGroup)))
- // Then remove undefined groups
- .filter(Boolean) as Group[]
-
- const contracts = await Promise.all(
- groups.map((g) => listContractsByGroupSlug(g?.slug ?? ''))
- )
-
- const markets = Object.fromEntries(groups.map((g, i) => [g.id, contracts[i]]))
-
- const groupMap = keyBy(groups, 'id')
- const numPeople = mapValues(groupMap, (g) => g?.totalMembers)
- const slugs = mapValues(groupMap, 'slug')
-
- return { props: { markets, numPeople, slugs }, revalidate: 60 * 10 }
+type SectionInfo = {
+ tourney: Tourney
+ slug: string
+ numPeople: number
}
-export default function TournamentPage(props: {
- markets: { [groupId: string]: Contract[] }
- numPeople: { [groupId: string]: number }
- slugs: { [groupId: string]: string }
-}) {
- const { markets = {}, numPeople = {}, slugs = {} } = props
+export async function getStaticProps() {
+ const groupIds = tourneys.map((data) => data.groupId)
+ const groups = await Promise.all(groupIds.map(getGroup))
+ const sections = zip(tourneys, groups)
+ .filter(([_tourney, group]) => group != null)
+ .map(([tourney, group]) => ({
+ tourney,
+ slug: group!.slug, // eslint-disable-line
+ numPeople: group!.totalMembers, // eslint-disable-line
+ }))
+ return { props: { sections } }
+}
+
+export default function TournamentPage(props: { sections: SectionInfo[] }) {
+ const { sections } = props
return (
@@ -141,96 +135,111 @@ export default function TournamentPage(props: {
title="Tournaments"
description="Win money by betting in forecasting touraments on current events, sports, science, and more"
/>
-
- {tourneys.map(({ groupId, ...data }) => (
-
+
+ {sections.map(({ tourney, slug, numPeople }) => (
+
+
+ {tourney.blurb}
+
+
))}
-
+
+
+ {Salem.blurb}
+
+
)
}
-function Section(props: {
- title: string
+const SectionHeader = (props: {
url: string
- blurb: string
- award?: string
+ title: string
ppl?: number
- endTime?: Dayjs
- markets: Contract[]
- images?: { marketUrl: string; image: StaticImageData }[] // hack for cspi
-}) {
- const { title, url, blurb, award, ppl, endTime, images } = props
- // Sort markets by probability, highest % first
- const markets = sortBy(props.markets, (c) =>
- getProbability(c as BinaryContract | PseudoNumericContract)
- )
- .reverse()
- .filter((c) => !c.isResolved)
-
+ award?: string
+ endTime?: number
+}) => {
+ const { url, title, ppl, award, endTime } = props
return (
-
+ ))}
+
+ See more
+
+
+ )
+}
+
+const MarketCarousel = (props: { slug: string }) => {
+ const { slug } = props
+ const q = contractsByGroupSlugQuery(slug)
+ const { allItems, getNext, isLoading } = usePagination({ q, pageSize: 12 })
+ return isLoading ? (
+
+ ) : (
+
+
+ {allItems().map((m) => (
+
+ ))}
+
)
}