move search bar to top of page (#433)
* move search bar to top of page * move select inputs below search bar * reduce complexity * default search to zaprank * votes => zaprank --------- Co-authored-by: rleed <rleed1@pm.me> Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> Co-authored-by: keyan <keyan.kousha+huumn@gmail.com>
This commit is contained in:
parent
2ba969ebab
commit
1969d82741
@ -146,7 +146,7 @@ const orderByClause = async (by, me, models, type) => {
|
|||||||
return 'ORDER BY "Item".ncomments DESC'
|
return 'ORDER BY "Item".ncomments DESC'
|
||||||
case 'sats':
|
case 'sats':
|
||||||
return 'ORDER BY "Item".msats DESC'
|
return 'ORDER BY "Item".msats DESC'
|
||||||
case 'votes':
|
case 'zaprank':
|
||||||
return await topOrderByWeightedSats(me, models)
|
return await topOrderByWeightedSats(me, models)
|
||||||
default:
|
default:
|
||||||
return `ORDER BY ${type === 'bookmarks' ? '"bookmarkCreatedAt"' : '"Item".created_at'} DESC`
|
return `ORDER BY ${type === 'bookmarks' ? '"bookmarkCreatedAt"' : '"Item".created_at'} DESC`
|
||||||
@ -410,10 +410,10 @@ export default {
|
|||||||
${typeClause(type)}
|
${typeClause(type)}
|
||||||
${whenClause(when, type)}
|
${whenClause(when, type)}
|
||||||
${await filterClause(me, models, type)}
|
${await filterClause(me, models, type)}
|
||||||
${await orderByClause(by || 'votes', me, models, type)}
|
${await orderByClause(by || 'zaprank', me, models, type)}
|
||||||
OFFSET $2
|
OFFSET $2
|
||||||
LIMIT $3`,
|
LIMIT $3`,
|
||||||
orderBy: await orderByClause(by || 'votes', me, models, type)
|
orderBy: await orderByClause(by || 'zaprank', me, models, type)
|
||||||
}, decodedCursor.time, decodedCursor.offset, limit, ...subArr)
|
}, decodedCursor.time, decodedCursor.offset, limit, ...subArr)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
@ -131,10 +131,10 @@ export default {
|
|||||||
case 'sats':
|
case 'sats':
|
||||||
sortArr.push({ sats: 'desc' })
|
sortArr.push({ sats: 'desc' })
|
||||||
break
|
break
|
||||||
case 'votes':
|
case 'match':
|
||||||
sortArr.push({ wvotes: 'desc' })
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
sortArr.push({ wvotes: 'desc' })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sortArr.push('_score')
|
sortArr.push('_score')
|
||||||
|
@ -27,10 +27,12 @@ export default function Layout ({
|
|||||||
|
|
||||||
export function SearchLayout ({ sub, children, ...props }) {
|
export function SearchLayout ({ sub, children, ...props }) {
|
||||||
return (
|
return (
|
||||||
<Layout sub={sub} seo={false} footer={false} {...props}>
|
<Layout sub={sub} seo={false} contain={false} {...props}>
|
||||||
<SeoSearch sub={sub} />
|
<SeoSearch sub={sub} />
|
||||||
|
<Search sub={sub} />
|
||||||
|
<Container as='main' className='py-4 px-sm-0'>
|
||||||
{children}
|
{children}
|
||||||
<Search />
|
</Container>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,17 @@
|
|||||||
import Button from 'react-bootstrap/Button'
|
|
||||||
import Container from 'react-bootstrap/Container'
|
import Container from 'react-bootstrap/Container'
|
||||||
import styles from './search.module.css'
|
import styles from './search.module.css'
|
||||||
import SearchIcon from '../svgs/search-line.svg'
|
import SearchIcon from '../svgs/search-line.svg'
|
||||||
import CloseIcon from '../svgs/close-line.svg'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { Form, Input, Select, SubmitButton } from './form'
|
import { Form, Input, Select, SubmitButton } from './form'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export default function Search ({ sub }) {
|
export default function Search ({ sub }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [searching, setSearching] = useState(router.query.q)
|
|
||||||
const [q, setQ] = useState(router.query.q || '')
|
const [q, setQ] = useState(router.query.q || '')
|
||||||
const [atBottom, setAtBottom] = useState()
|
const inputRef = useRef(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAtBottom(Math.ceil(window.innerHeight + window.pageYOffset) >= document.body.offsetHeight)
|
inputRef.current?.focus()
|
||||||
window.onscroll = function (ev) {
|
|
||||||
if (Math.ceil(window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
|
|
||||||
setAtBottom(true)
|
|
||||||
} else {
|
|
||||||
setAtBottom(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const search = async values => {
|
const search = async values => {
|
||||||
@ -43,7 +33,7 @@ export default function Search ({ sub }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (values.what === '' || values.what === 'all') delete values.what
|
if (values.what === '' || values.what === 'all') delete values.what
|
||||||
if (values.sort === '' || values.sort === 'match') delete values.sort
|
if (values.sort === '' || values.sort === 'zaprank') delete values.sort
|
||||||
if (values.when === '' || values.when === 'forever') delete values.when
|
if (values.when === '' || values.when === 'forever') delete values.when
|
||||||
await router.push({
|
await router.push({
|
||||||
pathname: prefix + '/search',
|
pathname: prefix + '/search',
|
||||||
@ -52,25 +42,39 @@ export default function Search ({ sub }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showSearch = atBottom || searching || router.query.q
|
|
||||||
const filter = sub !== 'jobs'
|
const filter = sub !== 'jobs'
|
||||||
const what = router.pathname.startsWith('/stackers') ? 'stackers' : router.query.what || 'all'
|
const what = router.pathname.startsWith('/stackers') ? 'stackers' : router.query.what || 'all'
|
||||||
const sort = router.query.sort || 'match'
|
const sort = router.query.sort || 'zaprank'
|
||||||
const when = router.query.when || 'forever'
|
const when = router.query.when || 'forever'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`${styles.searchSection} ${showSearch ? styles.solid : styles.hidden}`}>
|
<div className={styles.searchSection}>
|
||||||
<Container className={`px-md-0 ${styles.searchContainer} ${filter ? styles.leaveRoom : ''}`}>
|
<Container className={`px-md-0 ${styles.searchContainer} ${filter ? styles.leaveRoom : ''}`}>
|
||||||
{showSearch
|
|
||||||
? (
|
|
||||||
<Form
|
<Form
|
||||||
className={styles.formActive}
|
|
||||||
initial={{ q, what, sort, when }}
|
initial={{ q, what, sort, when }}
|
||||||
onSubmit={search}
|
onSubmit={search}
|
||||||
>
|
>
|
||||||
|
<div className={`${styles.active} my-3`}>
|
||||||
|
<Input
|
||||||
|
name='q'
|
||||||
|
required
|
||||||
|
autoFocus
|
||||||
|
groupClassName='me-3 mb-0 flex-grow-1'
|
||||||
|
className='flex-grow-1'
|
||||||
|
clear
|
||||||
|
innerRef={inputRef}
|
||||||
|
overrideValue={q}
|
||||||
|
onChange={async (formik, e) => {
|
||||||
|
setQ(e.target.value?.trim())
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SubmitButton variant='primary' className={styles.search}>
|
||||||
|
<SearchIcon width={22} height={22} />
|
||||||
|
</SubmitButton>
|
||||||
|
</div>
|
||||||
{filter &&
|
{filter &&
|
||||||
<div className='text-muted fw-bold my-3 d-flex align-items-center'>
|
<div className='text-muted fw-bold d-flex align-items-center'>
|
||||||
<Select
|
<Select
|
||||||
groupClassName='me-2 mb-0'
|
groupClassName='me-2 mb-0'
|
||||||
onChange={(formik, e) => search({ ...formik?.values, what: e.target.value })}
|
onChange={(formik, e) => search({ ...formik?.values, what: e.target.value })}
|
||||||
@ -88,7 +92,7 @@ export default function Search ({ sub }) {
|
|||||||
name='sort'
|
name='sort'
|
||||||
size='sm'
|
size='sm'
|
||||||
overrideValue={sort}
|
overrideValue={sort}
|
||||||
items={['match', 'recent', 'comments', 'sats', 'votes']}
|
items={['zaprank', 'match', 'recent', 'comments', 'sats']}
|
||||||
/>
|
/>
|
||||||
for
|
for
|
||||||
<Select
|
<Select
|
||||||
@ -102,46 +106,9 @@ export default function Search ({ sub }) {
|
|||||||
|
|
||||||
</>}
|
</>}
|
||||||
</div>}
|
</div>}
|
||||||
<div className={styles.active}>
|
|
||||||
<Input
|
|
||||||
name='q'
|
|
||||||
required
|
|
||||||
autoFocus
|
|
||||||
groupClassName='me-3 mb-0 flex-grow-1'
|
|
||||||
className='flex-grow-1'
|
|
||||||
clear
|
|
||||||
overrideValue={q}
|
|
||||||
onChange={async (formik, e) => {
|
|
||||||
setSearching(true)
|
|
||||||
setQ(e.target.value?.trim())
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{q || atBottom || router.query.q
|
|
||||||
? (
|
|
||||||
<SubmitButton variant='primary' className={styles.search}>
|
|
||||||
<SearchIcon width={22} height={22} />
|
|
||||||
</SubmitButton>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<Button
|
|
||||||
className={styles.search} onClick={() => {
|
|
||||||
setSearching(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon width={26} height={26} />
|
|
||||||
</Button>)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Form>
|
</Form>
|
||||||
)
|
|
||||||
: (
|
|
||||||
<Button className={`${styles.search} ${styles.formActive}`} onClick={() => setSearching(true)}>
|
|
||||||
<SearchIcon width={22} height={22} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles.searchPadding} ${filter ? styles.leaveRoom : ''}`} />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
.searchSection {
|
.searchSection {
|
||||||
position: fixed;
|
position: sticky;
|
||||||
bottom: 0;
|
top: 0;
|
||||||
left: 0;
|
box-shadow: 0 4px 12px -4px hsl(0deg 0% 59% / 10%);
|
||||||
right: 0;
|
background-color: var(--bs-body-bg);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchContainer {
|
.searchContainer {
|
||||||
@ -14,17 +15,6 @@
|
|||||||
height: 130px !important;
|
height: 130px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchSection.solid {
|
|
||||||
pointer-events: auto;
|
|
||||||
background: var(--bs-body-bg);
|
|
||||||
box-shadow: 0 -4px 12px hsl(0deg 0% 59% / 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchSection.hidden {
|
|
||||||
pointer-events: none;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@ -36,21 +26,13 @@
|
|||||||
left: auto !important;
|
left: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.formActive {
|
|
||||||
pointer-events: auto;
|
|
||||||
bottom: 18px;
|
|
||||||
right: 18px;
|
|
||||||
left: 18px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
form>.active {
|
form>.active {
|
||||||
display: flex;
|
display: flex;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
flex-flow: row wrap;
|
flex-flow: row nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchPadding {
|
form>.active :global(.input-group) {
|
||||||
padding-bottom: 88px;
|
flex-flow: nowrap;
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ export default function TopHeader ({ sub, cat }) {
|
|||||||
if (typeof query.by !== 'undefined') {
|
if (typeof query.by !== 'undefined') {
|
||||||
if (query.by === '' ||
|
if (query.by === '' ||
|
||||||
(what === 'stackers' && (query.by === 'stacked' || !USER_SORTS.includes(query.by))) ||
|
(what === 'stackers' && (query.by === 'stacked' || !USER_SORTS.includes(query.by))) ||
|
||||||
(what !== 'stackers' && (query.by === 'votes' || !ITEM_SORTS.includes(query.by)))) {
|
(what !== 'stackers' && (query.by === 'zaprank' || !ITEM_SORTS.includes(query.by)))) {
|
||||||
delete query.by
|
delete query.by
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ export default function TopHeader ({ sub, cat }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const what = cat
|
const what = cat
|
||||||
const by = router.query.by || (what === 'stackers' ? 'stacked' : 'votes')
|
const by = router.query.by || (what === 'stackers' ? 'stacked' : 'zaprank')
|
||||||
const when = router.query.when || ''
|
const when = router.query.when || ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -33,7 +33,7 @@ module.exports = {
|
|||||||
SUBS,
|
SUBS,
|
||||||
SUBS_NO_JOBS,
|
SUBS_NO_JOBS,
|
||||||
USER_SORTS: ['stacked', 'spent', 'comments', 'posts', 'referrals'],
|
USER_SORTS: ['stacked', 'spent', 'comments', 'posts', 'referrals'],
|
||||||
ITEM_SORTS: ['votes', 'comments', 'sats'],
|
ITEM_SORTS: ['zaprank', 'comments', 'sats'],
|
||||||
WHENS: ['day', 'week', 'month', 'year', 'forever'],
|
WHENS: ['day', 'week', 'month', 'year', 'forever'],
|
||||||
ITEM_TYPES: context => {
|
ITEM_TYPES: context => {
|
||||||
const items = ['all', 'posts', 'comments', 'bounties', 'links', 'discussions', 'polls']
|
const items = ['all', 'posts', 'comments', 'bounties', 'links', 'discussions', 'polls']
|
||||||
|
@ -2,7 +2,6 @@ import { SearchLayout } from '../../components/layout'
|
|||||||
import { getGetServerSideProps } from '../../api/ssrApollo'
|
import { getGetServerSideProps } from '../../api/ssrApollo'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { SUB_SEARCH } from '../../fragments/subs'
|
import { SUB_SEARCH } from '../../fragments/subs'
|
||||||
import Down from '../../svgs/arrow-down-line.svg'
|
|
||||||
import Items from '../../components/items'
|
import Items from '../../components/items'
|
||||||
|
|
||||||
export const getServerSideProps = getGetServerSideProps({
|
export const getServerSideProps = getGetServerSideProps({
|
||||||
@ -26,7 +25,7 @@ export default function Index ({ ssrData }) {
|
|||||||
/>
|
/>
|
||||||
: (
|
: (
|
||||||
<div className='text-muted text-center mt-5' style={{ fontFamily: 'lightning', fontSize: '2rem', opacity: '0.75' }}>
|
<div className='text-muted text-center mt-5' style={{ fontFamily: 'lightning', fontSize: '2rem', opacity: '0.75' }}>
|
||||||
<Down width={22} height={22} className='me-2' />search for something<Down width={22} height={22} className='ms-2' />
|
search for something
|
||||||
</div>)}
|
</div>)}
|
||||||
</SearchLayout>
|
</SearchLayout>
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user