import _ from 'lodash' import Link from 'next/link' import clsx from 'clsx' import { useEffect, useState } from 'react' import { contractMetrics, Contract, listContracts, } from '../lib/firebase/contracts' import { User } from '../lib/firebase/users' import { Col } from './layout/col' import { SiteLink } from './site-link' import { parseTags } from '../../common/util/parse' import { ContractCard } from './contract-card' import { Sort, useQueryAndSortParams } from '../hooks/use-sort-and-query-params' export function ContractsGrid(props: { contracts: Contract[] showHotVolume?: boolean showCloseTime?: boolean }) { const { showHotVolume, showCloseTime } = props const [resolvedContracts, activeContracts] = _.partition( props.contracts, (c) => c.isResolved ) const contracts = [...activeContracts, ...resolvedContracts].slice( 0, MAX_CONTRACTS_DISPLAYED ) if (contracts.length === 0) { return (

No markets found. Why not create one?

) } return ( ) } const MAX_GROUPED_CONTRACTS_DISPLAYED = 6 function CreatorContractsGrid(props: { contracts: Contract[] }) { const { contracts } = props const byCreator = _.groupBy(contracts, (contract) => contract.creatorId) const creator7DayVol = _.mapValues(byCreator, (contracts) => _.sumBy(contracts, (contract) => contract.volume7Days) ) const creatorIds = _.sortBy( Object.keys(byCreator), (creatorId) => -1 * creator7DayVol[creatorId] ) let numContracts = 0 let maxIndex = 0 for (; maxIndex < creatorIds.length; maxIndex++) { numContracts += Math.min( MAX_GROUPED_CONTRACTS_DISPLAYED, byCreator[creatorIds[maxIndex]].length ) if (numContracts > MAX_CONTRACTS_DISPLAYED) break } const creatorIdsSubset = creatorIds.slice(0, maxIndex) return ( {creatorIdsSubset.map((creatorId) => { const { creatorUsername, creatorName } = byCreator[creatorId][0] return ( {creatorName} {byCreator[creatorId].length > MAX_GROUPED_CONTRACTS_DISPLAYED ? ( e.stopPropagation()} > See all ) : (
)} ) })} ) } function TagContractsGrid(props: { contracts: Contract[] }) { const { contracts } = props const contractTags = _.flatMap(contracts, (contract) => parseTags(contract.question + ' ' + contract.description).map((tag) => ({ tag, contract, })) ) const groupedByTag = _.groupBy(contractTags, ({ tag }) => tag) const byTag = _.mapValues(groupedByTag, (contractTags) => contractTags.map(({ contract }) => contract) ) const tag7DayVol = _.mapValues(byTag, (contracts) => _.sumBy(contracts, (contract) => contract.volume7Days) ) const tags = _.sortBy( Object.keys(byTag), (creatorId) => -1 * tag7DayVol[creatorId] ) let numContracts = 0 let maxIndex = 0 for (; maxIndex < tags.length; maxIndex++) { numContracts += Math.min( MAX_GROUPED_CONTRACTS_DISPLAYED, byTag[tags[maxIndex]].length ) if (numContracts > MAX_CONTRACTS_DISPLAYED) break } const tagsSubset = tags.slice(0, maxIndex) return ( {tagsSubset.map((tag) => { return ( #{tag} {byTag[tag].length > MAX_GROUPED_CONTRACTS_DISPLAYED ? ( e.stopPropagation()} > See all ) : (
)} ) })} ) } const MAX_CONTRACTS_DISPLAYED = 99 export function SearchableGrid(props: { contracts: Contract[] query: string setQuery: (query: string) => void sort: Sort setSort: (sort: Sort) => void byOneCreator?: boolean }) { const { contracts, query, setQuery, sort, setSort, byOneCreator } = props function check(corpus: String) { return corpus.toLowerCase().includes(query.toLowerCase()) } let matches = contracts.filter( (c) => check(c.question) || check(c.description) || check(c.creatorName) || check(c.creatorUsername) ) if (sort === 'newest' || sort === 'all') { matches.sort((a, b) => b.createdTime - a.createdTime) } else if (sort === 'resolved') { matches = _.sortBy( matches, (contract) => -1 * (contract.resolutionTime ?? 0) ) } else if (sort === 'close-date') { matches = _.sortBy(matches, ({ volume24Hours }) => -1 * volume24Hours) matches = _.sortBy(matches, (contract) => contract.closeTime) // Hide contracts that have already closed matches = matches.filter(({ closeTime }) => (closeTime || 0) > Date.now()) } else if (sort === 'most-traded') { matches.sort( (a, b) => contractMetrics(b).truePool - contractMetrics(a).truePool ) } else if (sort === '24-hour-vol') { // Use lodash for stable sort, so previous sort breaks all ties. matches = _.sortBy(matches, ({ volume7Days }) => -1 * volume7Days) matches = _.sortBy(matches, ({ volume24Hours }) => -1 * volume24Hours) } else if (sort === 'creator' || sort === 'tag') { matches.sort((a, b) => b.volume7Days - a.volume7Days) } if (sort !== 'all') { // Filter for (or filter out) resolved contracts matches = matches.filter((c) => sort === 'resolved' ? c.resolution : !c.resolution ) } return (
{/* Show a search input next to a sort dropdown */}
setQuery(e.target.value)} placeholder="Search markets" className="input input-bordered w-full" />
{sort === 'tag' ? ( ) : !byOneCreator && sort === 'creator' ? ( ) : ( )}
) } export function CreatorContractsList(props: { creator: User }) { const { creator } = props const [contracts, setContracts] = useState('loading') const { query, setQuery, sort, setSort } = useQueryAndSortParams({ defaultSort: 'all', }) useEffect(() => { if (creator?.id) { // TODO: stream changes from firestore listContracts(creator.id).then(setContracts) } }, [creator]) if (contracts === 'loading') return <> return ( ) }