Sort & query url params (#17)

* Sort query in progress

* Search and query url params!
This commit is contained in:
James Grugett 2022-01-02 16:46:04 -06:00 committed by GitHub
parent b375256e96
commit bad7a2b543
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 22 deletions

View File

@ -3,17 +3,13 @@ import Link from 'next/link'
import clsx from 'clsx' import clsx from 'clsx'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { import { compute, Contract, listContracts } from '../lib/firebase/contracts'
compute,
Contract,
listContracts,
path,
} from '../lib/firebase/contracts'
import { User } from '../lib/firebase/users' import { User } from '../lib/firebase/users'
import { Col } from './layout/col' import { Col } from './layout/col'
import { SiteLink } from './site-link' import { SiteLink } from './site-link'
import { parseTags } from '../lib/util/parse' 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'
function ContractsGrid(props: { contracts: Contract[] }) { function ContractsGrid(props: { contracts: Contract[] }) {
const [resolvedContracts, activeContracts] = _.partition( const [resolvedContracts, activeContracts] = _.partition(
@ -61,7 +57,7 @@ function CreatorContractsGrid(props: { contracts: Contract[] }) {
const { creatorUsername, creatorName } = byCreator[creatorId][0] const { creatorUsername, creatorName } = byCreator[creatorId][0]
return ( return (
<Col className="gap-4"> <Col className="gap-4" key={creatorUsername}>
<SiteLink className="text-lg" href={`/${creatorUsername}`}> <SiteLink className="text-lg" href={`/${creatorUsername}`}>
{creatorName} {creatorName}
</SiteLink> </SiteLink>
@ -116,7 +112,7 @@ function TagContractsGrid(props: { contracts: Contract[] }) {
<Col className="gap-6"> <Col className="gap-6">
{tags.map((tag) => { {tags.map((tag) => {
return ( return (
<Col className="gap-4"> <Col className="gap-4" key={tag}>
<SiteLink className="text-lg" href={`/tag/${tag}`}> <SiteLink className="text-lg" href={`/tag/${tag}`}>
#{tag} #{tag}
</SiteLink> </SiteLink>
@ -152,17 +148,15 @@ function TagContractsGrid(props: { contracts: Contract[] }) {
const MAX_CONTRACTS_DISPLAYED = 99 const MAX_CONTRACTS_DISPLAYED = 99
type Sort = 'creator' | 'tag' | 'createdTime' | 'pool' | 'resolved' | 'all'
export function SearchableGrid(props: { export function SearchableGrid(props: {
contracts: Contract[] contracts: Contract[]
defaultSort?: Sort query: string
setQuery: (query: string) => void
sort: Sort
setSort: (sort: Sort) => void
byOneCreator?: boolean byOneCreator?: boolean
}) { }) {
const { contracts, defaultSort, byOneCreator } = props const { contracts, query, setQuery, sort, setSort, byOneCreator } = props
const [query, setQuery] = useState('')
const [sort, setSort] = useState(
defaultSort || (byOneCreator ? 'pool' : 'creator')
)
function check(corpus: String) { function check(corpus: String) {
return corpus.toLowerCase().includes(query.toLowerCase()) return corpus.toLowerCase().includes(query.toLowerCase())
@ -175,9 +169,9 @@ export function SearchableGrid(props: {
check(c.creatorUsername) check(c.creatorUsername)
) )
if (sort === 'createdTime' || sort === 'resolved' || sort === 'all') { if (sort === 'newest' || sort === 'resolved' || sort === 'all') {
matches.sort((a, b) => b.createdTime - a.createdTime) matches.sort((a, b) => b.createdTime - a.createdTime)
} else if (sort === 'pool' || sort === 'creator' || sort === 'tag') { } else if (sort === 'most-traded' || sort === 'creator' || sort === 'tag') {
matches.sort((a, b) => compute(b).truePool - compute(a).truePool) matches.sort((a, b) => compute(b).truePool - compute(a).truePool)
} }
@ -213,8 +207,8 @@ export function SearchableGrid(props: {
<option value="creator">By creator</option> <option value="creator">By creator</option>
)} )}
<option value="tag">By tag</option> <option value="tag">By tag</option>
<option value="pool">Most traded</option> <option value="most-traded">Most traded</option>
<option value="createdTime">Newest first</option> <option value="newest">Newest</option>
<option value="resolved">Resolved</option> <option value="resolved">Resolved</option>
</select> </select>
</div> </div>
@ -234,6 +228,10 @@ export function CreatorContractsList(props: { creator: User }) {
const { creator } = props const { creator } = props
const [contracts, setContracts] = useState<Contract[] | 'loading'>('loading') const [contracts, setContracts] = useState<Contract[] | 'loading'>('loading')
const { query, setQuery, sort, setSort } = useQueryAndSortParams({
defaultSort: 'all',
})
useEffect(() => { useEffect(() => {
if (creator?.id) { if (creator?.id) {
// TODO: stream changes from firestore // TODO: stream changes from firestore
@ -243,5 +241,14 @@ export function CreatorContractsList(props: { creator: User }) {
if (contracts === 'loading') return <></> if (contracts === 'loading') return <></>
return <SearchableGrid contracts={contracts} byOneCreator defaultSort="all" /> return (
<SearchableGrid
contracts={contracts}
byOneCreator
query={query}
setQuery={setQuery}
sort={sort}
setSort={setSort}
/>
)
} }

View File

@ -0,0 +1,40 @@
import { useRouter } from 'next/router'
export type Sort =
| 'creator'
| 'tag'
| 'newest'
| 'most-traded'
| 'resolved'
| 'all'
export function useQueryAndSortParams(options?: { defaultSort: Sort }) {
const router = useRouter()
const { s: sort, q: query } = router.query as {
q?: string
s?: Sort
}
const setSort = (sort: Sort | undefined) => {
router.query.s = sort
router.push(router, undefined, { shallow: true })
}
const setQuery = (query: string | undefined) => {
if (query) {
router.query.q = query
} else {
delete router.query.q
}
router.push(router, undefined, { shallow: true })
}
return {
sort: sort ?? options?.defaultSort ?? 'creator',
query: query ?? '',
setSort,
setQuery,
}
}

View File

@ -13,6 +13,7 @@ import { SearchableGrid } from '../components/contracts-list'
import { Col } from '../components/layout/col' import { Col } from '../components/layout/col'
import { NavBar } from '../components/nav-bar' import { NavBar } from '../components/nav-bar'
import Link from 'next/link' import Link from 'next/link'
import { useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
export default function LandingPage() { export default function LandingPage() {
return ( return (
@ -159,13 +160,20 @@ function FeaturesSection() {
function ExploreMarketsSection() { function ExploreMarketsSection() {
const contracts = useContracts() const contracts = useContracts()
const { query, setQuery, sort, setSort } = useQueryAndSortParams()
return ( return (
<div className="max-w-4xl px-4 py-8 mx-auto"> <div className="max-w-4xl px-4 py-8 mx-auto">
<p className="my-12 text-3xl leading-8 font-extrabold tracking-tight text-indigo-700 sm:text-4xl"> <p className="my-12 text-3xl leading-8 font-extrabold tracking-tight text-indigo-700 sm:text-4xl">
Explore our markets Explore our markets
</p> </p>
<SearchableGrid contracts={contracts === 'loading' ? [] : contracts} /> <SearchableGrid
contracts={contracts === 'loading' ? [] : contracts}
query={query}
setQuery={setQuery}
sort={sort}
setSort={setSort}
/>
</div> </div>
) )
} }

View File

@ -1,6 +1,7 @@
import { SearchableGrid } from '../components/contracts-list' import { SearchableGrid } from '../components/contracts-list'
import { Page } from '../components/page' import { Page } from '../components/page'
import { useContracts } from '../hooks/use-contracts' import { useContracts } from '../hooks/use-contracts'
import { useQueryAndSortParams } from '../hooks/use-sort-and-query-params'
import { Contract, listAllContracts } from '../lib/firebase/contracts' import { Contract, listAllContracts } from '../lib/firebase/contracts'
export async function getStaticProps() { export async function getStaticProps() {
@ -17,12 +18,17 @@ export async function getStaticProps() {
export default function Markets(props: { contracts: Contract[] }) { export default function Markets(props: { contracts: Contract[] }) {
const contracts = useContracts() const contracts = useContracts()
const { query, setQuery, sort, setSort } = useQueryAndSortParams()
return ( return (
<Page> <Page>
{(props.contracts || contracts !== 'loading') && ( {(props.contracts || contracts !== 'loading') && (
<SearchableGrid <SearchableGrid
contracts={contracts === 'loading' ? props.contracts : contracts} contracts={contracts === 'loading' ? props.contracts : contracts}
query={query}
setQuery={setQuery}
sort={sort}
setSort={setSort}
/> />
)} )}
</Page> </Page>

View File

@ -3,6 +3,7 @@ import { SearchableGrid } from '../../components/contracts-list'
import { Page } from '../../components/page' import { Page } from '../../components/page'
import { Title } from '../../components/title' import { Title } from '../../components/title'
import { useContracts } from '../../hooks/use-contracts' import { useContracts } from '../../hooks/use-contracts'
import { useQueryAndSortParams } from '../../hooks/use-sort-and-query-params'
export default function TagPage() { export default function TagPage() {
const router = useRouter() const router = useRouter()
@ -18,13 +19,23 @@ export default function TagPage() {
) )
} }
const { query, setQuery, sort, setSort } = useQueryAndSortParams({
defaultSort: 'most-traded',
})
return ( return (
<Page> <Page>
<Title text={`#${tag}`} /> <Title text={`#${tag}`} />
{contracts === 'loading' ? ( {contracts === 'loading' ? (
<></> <></>
) : ( ) : (
<SearchableGrid contracts={contracts} /> <SearchableGrid
contracts={contracts}
query={query}
setQuery={setQuery}
sort={sort}
setSort={setSort}
/>
)} )}
</Page> </Page>
) )