sort by top posts
This commit is contained in:
parent
cd61263159
commit
68ddd0f86b
|
@ -34,9 +34,10 @@ export async function getItem (parent, { id }, { models }) {
|
|||
|
||||
export default {
|
||||
Query: {
|
||||
moreItems: async (parent, { sort, cursor, userId }, { me, models }) => {
|
||||
moreItems: async (parent, { sort, cursor, userId, within }, { me, models }) => {
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
let items
|
||||
let interval = 'INTERVAL '
|
||||
switch (sort) {
|
||||
case 'user':
|
||||
items = await models.$queryRaw(`
|
||||
|
@ -77,6 +78,32 @@ export default {
|
|||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
|
||||
}
|
||||
break
|
||||
case 'top':
|
||||
switch (within) {
|
||||
case 'day':
|
||||
interval += "'1 day'"
|
||||
break
|
||||
case 'week':
|
||||
interval += "'7 days'"
|
||||
break
|
||||
case 'month':
|
||||
interval += "'1 month'"
|
||||
break
|
||||
case 'year':
|
||||
interval += "'1 year'"
|
||||
break
|
||||
}
|
||||
|
||||
items = await models.$queryRaw(`
|
||||
${SELECT}
|
||||
FROM "Item"
|
||||
${timedLeftJoinSats(1)}
|
||||
WHERE "parentId" IS NULL AND created_at <= $1
|
||||
${within ? ` AND created_at >= $1 - ${interval}` : ''}
|
||||
ORDER BY x.sats DESC NULLS LAST, created_at DESC
|
||||
OFFSET $2
|
||||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
|
||||
break
|
||||
default:
|
||||
items = await models.$queryRaw(`
|
||||
${SELECT}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { gql } from 'apollo-server-micro'
|
|||
|
||||
export default gql`
|
||||
extend type Query {
|
||||
moreItems(sort: String!, cursor: String, userId: ID): Items
|
||||
moreItems(sort: String!, cursor: String, userId: ID, within: String): Items
|
||||
moreFlatComments(cursor: String, userId: ID): Comments
|
||||
item(id: ID!): Item
|
||||
userComments(userId: ID!): [Item!]
|
||||
|
|
|
@ -3,13 +3,13 @@ import Nav from 'react-bootstrap/Nav'
|
|||
import Link from 'next/link'
|
||||
import styles from './header.module.css'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button, Container, NavDropdown } from 'react-bootstrap'
|
||||
import { Button, Container, NavDropdown, SplitButton, Dropdown } from 'react-bootstrap'
|
||||
import Price from './price'
|
||||
import { useMe } from './me'
|
||||
import Head from 'next/head'
|
||||
import { signOut, signIn, useSession } from 'next-auth/client'
|
||||
import { useLightning } from './lightning'
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { randInRange } from '../lib/rand'
|
||||
|
||||
function WalletSummary ({ me }) {
|
||||
|
@ -31,6 +31,17 @@ export default function Header () {
|
|||
const path = router.asPath.split('?')[0]
|
||||
const me = useMe()
|
||||
const [session, loading] = useSession()
|
||||
const [sort, setSort] = useState('recent')
|
||||
const [within, setWithin] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
setSort(localStorage.getItem('sort') || 'recent')
|
||||
setWithin(localStorage.getItem('topWithin'))
|
||||
}, [])
|
||||
|
||||
const otherSort = sort === 'recent' ? 'top' : 'recent'
|
||||
const sortLink = `/${sort}${sort === 'top' && within ? `/${within}` : ''}`
|
||||
const otherSortLink = `/${otherSort}${otherSort === 'top' && within ? `/${within}` : ''}`
|
||||
|
||||
const Corner = () => {
|
||||
if (me) {
|
||||
|
@ -76,6 +87,9 @@ export default function Header () {
|
|||
<RefreshableLink href='/recent' passHref>
|
||||
<NavDropdown.Item>recent</NavDropdown.Item>
|
||||
</RefreshableLink>
|
||||
<RefreshableLink href={`/top${within ? `/${within}` : ''}`} passHref>
|
||||
<NavDropdown.Item>top</NavDropdown.Item>
|
||||
</RefreshableLink>
|
||||
{me
|
||||
? (
|
||||
<Link href='/post' passHref>
|
||||
|
@ -131,10 +145,22 @@ export default function Header () {
|
|||
<RefreshableLink href='/' passHref>
|
||||
<Navbar.Brand className={`${styles.brand} d-block d-sm-none`}>SN</Navbar.Brand>
|
||||
</RefreshableLink>
|
||||
<Nav.Item className='d-md-flex d-none'>
|
||||
<RefreshableLink href='/recent' passHref>
|
||||
<Nav.Link className={styles.navLink}>recent</Nav.Link>
|
||||
</RefreshableLink>
|
||||
<Nav.Item className='d-md-flex d-none nav-dropdown-toggle'>
|
||||
<SplitButton
|
||||
title={
|
||||
<RefreshableLink href={sortLink} passHref>
|
||||
<Nav.Link className={styles.navLink}>{sort}</Nav.Link>
|
||||
</RefreshableLink>
|
||||
}
|
||||
key={`/${sort}`}
|
||||
id='recent-top-button'
|
||||
variant='link'
|
||||
className='p-0'
|
||||
>
|
||||
<Link href={otherSortLink} passHref>
|
||||
<Dropdown.Item onClick={() => localStorage.setItem('sort', otherSort)}>{otherSort}</Dropdown.Item>
|
||||
</Link>
|
||||
</SplitButton>
|
||||
</Nav.Item>
|
||||
<Nav.Item className='d-md-flex d-none'>
|
||||
{me
|
||||
|
|
|
@ -9,14 +9,14 @@ services:
|
|||
ports:
|
||||
- "5431:5432"
|
||||
env_file:
|
||||
- .env.sample
|
||||
- ./.env.sample
|
||||
app:
|
||||
container_name: app
|
||||
build: ./
|
||||
depends_on:
|
||||
- db
|
||||
env_file:
|
||||
- .env.sample
|
||||
- ./.env.sample
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
|
|
|
@ -28,8 +28,8 @@ export const ITEM_FIELDS = gql`
|
|||
export const MORE_ITEMS = gql`
|
||||
${ITEM_FIELDS}
|
||||
|
||||
query MoreItems($sort: String!, $cursor: String, $userId: ID) {
|
||||
moreItems(sort: $sort, cursor: $cursor, userId: $userId) {
|
||||
query MoreItems($sort: String!, $cursor: String, $userId: ID, $within: String) {
|
||||
moreItems(sort: $sort, cursor: $cursor, userId: $userId, within: $within) {
|
||||
cursor
|
||||
items {
|
||||
...ItemFields
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function getApolloClient () {
|
|||
Query: {
|
||||
fields: {
|
||||
moreItems: {
|
||||
keyArgs: ['sort', 'userId'],
|
||||
keyArgs: ['sort', 'userId', 'within'],
|
||||
merge (existing, incoming) {
|
||||
if (isFirstPage(incoming.cursor, existing)) {
|
||||
return incoming
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -55,7 +55,6 @@ export async function getServerSideProps ({ req, res, query: { id, error = null
|
|||
}
|
||||
|
||||
function InviteHeader ({ invite }) {
|
||||
console.log(invite.poor)
|
||||
let Inner
|
||||
if (invite.revoked) {
|
||||
Inner = () => <div className='text-danger'>this invite link expired</div>
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import Layout from '../../components/layout'
|
||||
import Items from '../../components/items'
|
||||
import { useRouter } from 'next/router'
|
||||
import getSSRApolloClient from '../../api/ssrApollo'
|
||||
import { MORE_ITEMS } from '../../fragments/items'
|
||||
import { Nav, Navbar } from 'react-bootstrap'
|
||||
import styles from '../../components/header.module.css'
|
||||
import Link from 'next/link'
|
||||
|
||||
export async function getServerSideProps ({ req }) {
|
||||
const client = await getSSRApolloClient(req)
|
||||
const { data } = await client.query({
|
||||
query: MORE_ITEMS,
|
||||
variables: { sort: 'top', within: req.query?.within?.pop() }
|
||||
})
|
||||
|
||||
let items, cursor
|
||||
if (data) {
|
||||
({ moreItems: { items, cursor } } = data)
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
items,
|
||||
cursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Index ({ items, cursor }) {
|
||||
const router = useRouter()
|
||||
const path = router.asPath.split('?')[0]
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Navbar className={styles.navbar}>
|
||||
<Nav
|
||||
className={styles.navbarNav}
|
||||
activeKey={path}
|
||||
>
|
||||
<Nav.Item>
|
||||
<Link href='/top/day' passHref>
|
||||
<Nav.Link
|
||||
className={styles.navLink}
|
||||
onClick={() => localStorage.setItem('topWithin', 'day')}>
|
||||
day
|
||||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link href='/top/week' passHref>
|
||||
<Nav.Link
|
||||
className={styles.navLink}
|
||||
onClick={() => localStorage.setItem('topWithin', 'week')}>
|
||||
week
|
||||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link href='/top/month' passHref>
|
||||
<Nav.Link
|
||||
className={styles.navLink}
|
||||
onClick={() => localStorage.setItem('topWithin', 'month')}>
|
||||
month
|
||||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link href='/top/year' passHref>
|
||||
<Nav.Link
|
||||
className={styles.navLink}
|
||||
onClick={() => localStorage.setItem('topWithin', 'year')}>
|
||||
year
|
||||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Link href='/top' passHref>
|
||||
<Nav.Link
|
||||
className={styles.navLink}
|
||||
onClick={() => localStorage.removeItem('topWithin')}>
|
||||
forever
|
||||
</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
<Items
|
||||
items={items} cursor={cursor}
|
||||
variables={{ sort: 'top', within: router.query?.within?.pop() }} rank key={router.query.key}
|
||||
/>
|
||||
</Layout>
|
||||
)
|
||||
}
|
|
@ -129,6 +129,32 @@ footer {
|
|||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle .dropdown-toggle::after {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle .btn-link {
|
||||
color: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle .btn-link[aria-expanded="true"] {
|
||||
color: rgba(0, 0, 0, 0.9) !important;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle .dropdown .btn {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle .dropdown .dropdown-toggle {
|
||||
padding-left: .75rem;
|
||||
padding-right: .5rem;
|
||||
}
|
||||
|
||||
.nav-dropdown-toggle .nav-link {
|
||||
padding-right: 0 !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue