sort by top posts

This commit is contained in:
keyan 2021-10-21 17:05:06 -05:00
parent cd61263159
commit 68ddd0f86b
10 changed files with 11827 additions and 56 deletions

View File

@ -34,9 +34,10 @@ export async function getItem (parent, { id }, { models }) {
export default { export default {
Query: { Query: {
moreItems: async (parent, { sort, cursor, userId }, { me, models }) => { moreItems: async (parent, { sort, cursor, userId, within }, { me, models }) => {
const decodedCursor = decodeCursor(cursor) const decodedCursor = decodeCursor(cursor)
let items let items
let interval = 'INTERVAL '
switch (sort) { switch (sort) {
case 'user': case 'user':
items = await models.$queryRaw(` items = await models.$queryRaw(`
@ -77,6 +78,32 @@ export default {
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset) LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
} }
break 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: default:
items = await models.$queryRaw(` items = await models.$queryRaw(`
${SELECT} ${SELECT}

View File

@ -2,7 +2,7 @@ import { gql } from 'apollo-server-micro'
export default gql` export default gql`
extend type Query { 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 moreFlatComments(cursor: String, userId: ID): Comments
item(id: ID!): Item item(id: ID!): Item
userComments(userId: ID!): [Item!] userComments(userId: ID!): [Item!]

View File

@ -3,13 +3,13 @@ import Nav from 'react-bootstrap/Nav'
import Link from 'next/link' import Link from 'next/link'
import styles from './header.module.css' import styles from './header.module.css'
import { useRouter } from 'next/router' 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 Price from './price'
import { useMe } from './me' import { useMe } from './me'
import Head from 'next/head' import Head from 'next/head'
import { signOut, signIn, useSession } from 'next-auth/client' import { signOut, signIn, useSession } from 'next-auth/client'
import { useLightning } from './lightning' import { useLightning } from './lightning'
import { useEffect } from 'react' import { useEffect, useState } from 'react'
import { randInRange } from '../lib/rand' import { randInRange } from '../lib/rand'
function WalletSummary ({ me }) { function WalletSummary ({ me }) {
@ -31,6 +31,17 @@ export default function Header () {
const path = router.asPath.split('?')[0] const path = router.asPath.split('?')[0]
const me = useMe() const me = useMe()
const [session, loading] = useSession() 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 = () => { const Corner = () => {
if (me) { if (me) {
@ -76,6 +87,9 @@ export default function Header () {
<RefreshableLink href='/recent' passHref> <RefreshableLink href='/recent' passHref>
<NavDropdown.Item>recent</NavDropdown.Item> <NavDropdown.Item>recent</NavDropdown.Item>
</RefreshableLink> </RefreshableLink>
<RefreshableLink href={`/top${within ? `/${within}` : ''}`} passHref>
<NavDropdown.Item>top</NavDropdown.Item>
</RefreshableLink>
{me {me
? ( ? (
<Link href='/post' passHref> <Link href='/post' passHref>
@ -131,10 +145,22 @@ export default function Header () {
<RefreshableLink href='/' passHref> <RefreshableLink href='/' passHref>
<Navbar.Brand className={`${styles.brand} d-block d-sm-none`}>SN</Navbar.Brand> <Navbar.Brand className={`${styles.brand} d-block d-sm-none`}>SN</Navbar.Brand>
</RefreshableLink> </RefreshableLink>
<Nav.Item className='d-md-flex d-none'> <Nav.Item className='d-md-flex d-none nav-dropdown-toggle'>
<RefreshableLink href='/recent' passHref> <SplitButton
<Nav.Link className={styles.navLink}>recent</Nav.Link> title={
</RefreshableLink> <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>
<Nav.Item className='d-md-flex d-none'> <Nav.Item className='d-md-flex d-none'>
{me {me

View File

@ -9,14 +9,14 @@ services:
ports: ports:
- "5431:5432" - "5431:5432"
env_file: env_file:
- .env.sample - ./.env.sample
app: app:
container_name: app container_name: app
build: ./ build: ./
depends_on: depends_on:
- db - db
env_file: env_file:
- .env.sample - ./.env.sample
ports: ports:
- "3000:3000" - "3000:3000"
volumes: volumes:

View File

@ -28,8 +28,8 @@ export const ITEM_FIELDS = gql`
export const MORE_ITEMS = gql` export const MORE_ITEMS = gql`
${ITEM_FIELDS} ${ITEM_FIELDS}
query MoreItems($sort: String!, $cursor: String, $userId: ID) { query MoreItems($sort: String!, $cursor: String, $userId: ID, $within: String) {
moreItems(sort: $sort, cursor: $cursor, userId: $userId) { moreItems(sort: $sort, cursor: $cursor, userId: $userId, within: $within) {
cursor cursor
items { items {
...ItemFields ...ItemFields

View File

@ -26,7 +26,7 @@ export default function getApolloClient () {
Query: { Query: {
fields: { fields: {
moreItems: { moreItems: {
keyArgs: ['sort', 'userId'], keyArgs: ['sort', 'userId', 'within'],
merge (existing, incoming) { merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing)) { if (isFirstPage(incoming.cursor, existing)) {
return incoming return incoming

11683
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,6 @@ export async function getServerSideProps ({ req, res, query: { id, error = null
} }
function InviteHeader ({ invite }) { function InviteHeader ({ invite }) {
console.log(invite.poor)
let Inner let Inner
if (invite.revoked) { if (invite.revoked) {
Inner = () => <div className='text-danger'>this invite link expired</div> Inner = () => <div className='text-danger'>this invite link expired</div>

View File

@ -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>
)
}

View File

@ -129,6 +129,32 @@ footer {
font-weight: 500; 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 { .dropdown-toggle::after {
display: none; display: none;
} }