WIP top comments and users

This commit is contained in:
keyan 2021-12-16 17:05:31 -06:00
parent 54c1c8c41b
commit 8e5327022d
10 changed files with 228 additions and 116 deletions

View File

@ -32,11 +32,33 @@ export async function getItem (parent, { id }, { models }) {
return item
}
function topClause (within) {
let interval = ' AND created_at >= $1 - INTERVAL '
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
default:
interval = ''
break
}
return interval
}
export default {
Query: {
moreItems: async (parent, { sort, cursor, name, within }, { me, models }) => {
const decodedCursor = decodeCursor(cursor)
let items; let user; let interval = 'INTERVAL '
let items; let user
switch (sort) {
case 'user':
@ -88,27 +110,12 @@ export default {
}
break
case 'top':
switch (within?.pop()) {
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}` : ''}
${topClause(within)}
ORDER BY x.sats DESC NULLS LAST, created_at DESC
OFFSET $2
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
@ -128,26 +135,45 @@ export default {
items
}
},
moreFlatComments: async (parent, { cursor, name }, { me, models }) => {
moreFlatComments: async (parent, { cursor, name, sort, within }, { me, models }) => {
const decodedCursor = decodeCursor(cursor)
if (!name) {
throw new UserInputError('must supply name', { argumentName: 'name' })
}
let comments, user
switch (sort) {
case 'user':
if (!name) {
throw new UserInputError('must supply name', { argumentName: 'name' })
}
const user = await models.user.findUnique({ where: { name } })
if (!user) {
throw new UserInputError('no user has that name', { argumentName: 'name' })
}
user = await models.user.findUnique({ where: { name } })
if (!user) {
throw new UserInputError('no user has that name', { argumentName: 'name' })
}
const comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE "userId" = $1 AND "parentId" IS NOT NULL
AND created_at <= $2
ORDER BY created_at DESC
OFFSET $3
LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset)
comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE "userId" = $1 AND "parentId" IS NOT NULL
AND created_at <= $2
ORDER BY created_at DESC
OFFSET $3
LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset)
break
case 'top':
comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
${timedLeftJoinSats(1)}
WHERE "parentId" IS NOT NULL
AND created_at <= $1
${topClause(within)}
ORDER BY x.sats DESC NULLS LAST, created_at DESC
OFFSET $2
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
break
default:
throw new UserInputError('invalid sort type', { argumentName: 'sort' })
}
return {
cursor: comments.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,

View File

@ -2,8 +2,8 @@ import { gql } from 'apollo-server-micro'
export default gql`
extend type Query {
moreItems(sort: String!, cursor: String, name: String, within: [String]): Items
moreFlatComments(cursor: String, name: String!): Comments
moreItems(sort: String!, cursor: String, name: String, within: String): Items
moreFlatComments(sort: String!, cursor: String, name: String, within: String): Comments
item(id: ID!): Item
pageTitle(url: String!): String
dupes(url: String!): [Item!]

116
components/top-header.js Normal file
View File

@ -0,0 +1,116 @@
import { Nav, Navbar } from 'react-bootstrap'
import styles from './header.module.css'
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function TopHeader ({ cat }) {
const router = useRouter()
const within = router.query.within
return (
<>
<Navbar className='pt-0'>
<Nav
className={`${styles.navbarNav} justify-content-around`}
activeKey={cat}
>
<Nav.Item>
<Link href={`/top/posts/${within}`} passHref>
<Nav.Link
eventKey='posts'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'day')}
>
posts
</Nav.Link>
</Link>
</Nav.Item>
<Nav.Item>
<Link href={`/top/comments/${within}`} passHref>
<Nav.Link
eventKey='comments'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'week')}
>
comments
</Nav.Link>
</Link>
</Nav.Item>
<Nav.Item>
<Link href={`/top/users/${within}`} passHref>
<Nav.Link
eventKey='users'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'month')}
>
users
</Nav.Link>
</Link>
</Nav.Item>
</Nav>
</Navbar>
<Navbar className='pt-0'>
<Nav
className={styles.navbarNav}
activeKey={within}
>
<Nav.Item>
<Link href={`/top/${cat}/day`} passHref>
<Nav.Link
eventKey='day'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'day')}
>
day
</Nav.Link>
</Link>
</Nav.Item>
<Nav.Item>
<Link href={`/top/${cat}/week`} passHref>
<Nav.Link
eventKey='week'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'week')}
>
week
</Nav.Link>
</Link>
</Nav.Item>
<Nav.Item>
<Link href={`/top/${cat}/month`} passHref>
<Nav.Link
eventKey='month'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'month')}
>
month
</Nav.Link>
</Link>
</Nav.Item>
<Nav.Item>
<Link href={`/top/${cat}/year`} passHref>
<Nav.Link
eventKey='year'
className={styles.navLink}
onClick={() => localStorage.setItem('topWithin', 'year')}
>
year
</Nav.Link>
</Link>
</Nav.Item>
<Nav.Item>
<Link href={`/top/${cat}/forever`} passHref>
<Nav.Link
eventKey='forever'
className={styles.navLink}
onClick={() => localStorage.removeItem('topWithin')}
>
forever
</Nav.Link>
</Link>
</Nav.Item>
</Nav>
</Navbar>
</>
)
}

View File

@ -32,8 +32,8 @@ export const COMMENT_FIELDS = gql`
export const MORE_FLAT_COMMENTS = gql`
${COMMENT_FIELDS}
query MoreFlatComments($cursor: String, $name: String!) {
moreFlatComments(cursor: $cursor, name: $name) {
query MoreFlatComments($sort: String!, $cursor: String, $name: String, $within: String) {
moreFlatComments(sort: $sort, cursor: $cursor, name: $name, within: $within) {
cursor
comments {
...CommentFields

View File

@ -33,7 +33,7 @@ export const ITEM_FIELDS = gql`
export const MORE_ITEMS = gql`
${ITEM_FIELDS}
query MoreItems($sort: String!, $cursor: String, $name: String, $within: [String]) {
query MoreItems($sort: String!, $cursor: String, $name: String, $within: String) {
moreItems(sort: $sort, cursor: $cursor, name: $name, within: $within) {
cursor
items {

View File

@ -62,7 +62,7 @@ export const USER_WITH_COMMENTS = gql`
...ItemWithComments
}
}
moreFlatComments(name: $name) {
moreFlatComments(sort: "user", name: $name) {
cursor
comments {
...CommentFields

View File

@ -39,7 +39,7 @@ export default function getApolloClient () {
}
},
moreFlatComments: {
keyArgs: ['name'],
keyArgs: ['name', 'sort', 'within'],
merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.comments)) {
return incoming

View File

@ -1,76 +0,0 @@
import Layout from '../../components/layout'
import Items from '../../components/items'
import { useRouter } from 'next/router'
import { getGetServerSideProps } 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 const getServerSideProps = getGetServerSideProps(MORE_ITEMS, { sort: 'top'})
export default function Index ({ data: { moreItems: { items, cursor } } }) {
const router = useRouter()
const path = router.asPath.split('?')[0]
return (
<Layout>
<Navbar className='pt-0'>
<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 }} rank
/>
</Layout>
)
}

View File

@ -0,0 +1,23 @@
import Layout from '../../../components/layout'
import { useRouter } from 'next/router'
import { getGetServerSideProps } from '../../../api/ssrApollo'
import TopHeader from '../../../components/top-header'
import { MORE_FLAT_COMMENTS } from '../../../fragments/comments'
import CommentsFlat from '../../../components/comments-flat'
export const getServerSideProps = getGetServerSideProps(MORE_FLAT_COMMENTS, { sort: 'top' })
export default function Index ({ data: { moreFlatComments: { comments, cursor } } }) {
const router = useRouter()
return (
<Layout>
<TopHeader cat='comments' />
<CommentsFlat
comments={comments} cursor={cursor}
variables={{ sort: 'top', within: router.query?.within }}
includeParent noReply
/>
</Layout>
)
}

View File

@ -0,0 +1,23 @@
import Layout from '../../../components/layout'
import Items from '../../../components/items'
import { useRouter } from 'next/router'
import { getGetServerSideProps } from '../../../api/ssrApollo'
import { MORE_ITEMS } from '../../../fragments/items'
import TopHeader from '../../../components/top-header'
export const getServerSideProps = getGetServerSideProps(MORE_ITEMS, { sort: 'top' })
export default function Index ({ data: { moreItems: { items, cursor } } }) {
const router = useRouter()
return (
<Layout>
<TopHeader cat='posts' />
<Items
items={items} cursor={cursor}
variables={{ sort: 'top', within: router.query?.within }} rank
/>
</Layout>
)
}