WIP top comments and users
This commit is contained in:
parent
54c1c8c41b
commit
8e5327022d
@ -32,11 +32,33 @@ export async function getItem (parent, { id }, { models }) {
|
|||||||
return item
|
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 {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
moreItems: async (parent, { sort, cursor, name, within }, { me, models }) => {
|
moreItems: async (parent, { sort, cursor, name, within }, { me, models }) => {
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
let items; let user; let interval = 'INTERVAL '
|
let items; let user
|
||||||
|
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
case 'user':
|
case 'user':
|
||||||
@ -88,27 +110,12 @@ export default {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'top':
|
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(`
|
items = await models.$queryRaw(`
|
||||||
${SELECT}
|
${SELECT}
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
${timedLeftJoinSats(1)}
|
${timedLeftJoinSats(1)}
|
||||||
WHERE "parentId" IS NULL AND created_at <= $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
|
ORDER BY x.sats DESC NULLS LAST, created_at DESC
|
||||||
OFFSET $2
|
OFFSET $2
|
||||||
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
|
LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
|
||||||
@ -128,26 +135,45 @@ export default {
|
|||||||
items
|
items
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
moreFlatComments: async (parent, { cursor, name }, { me, models }) => {
|
moreFlatComments: async (parent, { cursor, name, sort, within }, { me, models }) => {
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
|
|
||||||
if (!name) {
|
let comments, user
|
||||||
throw new UserInputError('must supply name', { argumentName: 'name' })
|
switch (sort) {
|
||||||
}
|
case 'user':
|
||||||
|
if (!name) {
|
||||||
|
throw new UserInputError('must supply name', { argumentName: 'name' })
|
||||||
|
}
|
||||||
|
|
||||||
const user = await models.user.findUnique({ where: { name } })
|
user = await models.user.findUnique({ where: { name } })
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new UserInputError('no user has that name', { argumentName: 'name' })
|
throw new UserInputError('no user has that name', { argumentName: 'name' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const comments = await models.$queryRaw(`
|
comments = await models.$queryRaw(`
|
||||||
${SELECT}
|
${SELECT}
|
||||||
FROM "Item"
|
FROM "Item"
|
||||||
WHERE "userId" = $1 AND "parentId" IS NOT NULL
|
WHERE "userId" = $1 AND "parentId" IS NOT NULL
|
||||||
AND created_at <= $2
|
AND created_at <= $2
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
OFFSET $3
|
OFFSET $3
|
||||||
LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset)
|
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 {
|
return {
|
||||||
cursor: comments.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
cursor: comments.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
||||||
|
@ -2,8 +2,8 @@ import { gql } from 'apollo-server-micro'
|
|||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
extend type Query {
|
extend type Query {
|
||||||
moreItems(sort: String!, cursor: String, name: String, within: [String]): Items
|
moreItems(sort: String!, cursor: String, name: String, within: String): Items
|
||||||
moreFlatComments(cursor: String, name: String!): Comments
|
moreFlatComments(sort: String!, cursor: String, name: String, within: String): Comments
|
||||||
item(id: ID!): Item
|
item(id: ID!): Item
|
||||||
pageTitle(url: String!): String
|
pageTitle(url: String!): String
|
||||||
dupes(url: String!): [Item!]
|
dupes(url: String!): [Item!]
|
||||||
|
116
components/top-header.js
Normal file
116
components/top-header.js
Normal 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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -32,8 +32,8 @@ export const COMMENT_FIELDS = gql`
|
|||||||
export const MORE_FLAT_COMMENTS = gql`
|
export const MORE_FLAT_COMMENTS = gql`
|
||||||
${COMMENT_FIELDS}
|
${COMMENT_FIELDS}
|
||||||
|
|
||||||
query MoreFlatComments($cursor: String, $name: String!) {
|
query MoreFlatComments($sort: String!, $cursor: String, $name: String, $within: String) {
|
||||||
moreFlatComments(cursor: $cursor, name: $name) {
|
moreFlatComments(sort: $sort, cursor: $cursor, name: $name, within: $within) {
|
||||||
cursor
|
cursor
|
||||||
comments {
|
comments {
|
||||||
...CommentFields
|
...CommentFields
|
||||||
|
@ -33,7 +33,7 @@ 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, $name: String, $within: [String]) {
|
query MoreItems($sort: String!, $cursor: String, $name: String, $within: String) {
|
||||||
moreItems(sort: $sort, cursor: $cursor, name: $name, within: $within) {
|
moreItems(sort: $sort, cursor: $cursor, name: $name, within: $within) {
|
||||||
cursor
|
cursor
|
||||||
items {
|
items {
|
||||||
|
@ -62,7 +62,7 @@ export const USER_WITH_COMMENTS = gql`
|
|||||||
...ItemWithComments
|
...ItemWithComments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
moreFlatComments(name: $name) {
|
moreFlatComments(sort: "user", name: $name) {
|
||||||
cursor
|
cursor
|
||||||
comments {
|
comments {
|
||||||
...CommentFields
|
...CommentFields
|
||||||
|
@ -39,7 +39,7 @@ export default function getApolloClient () {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
moreFlatComments: {
|
moreFlatComments: {
|
||||||
keyArgs: ['name'],
|
keyArgs: ['name', 'sort', 'within'],
|
||||||
merge (existing, incoming) {
|
merge (existing, incoming) {
|
||||||
if (isFirstPage(incoming.cursor, existing?.comments)) {
|
if (isFirstPage(incoming.cursor, existing?.comments)) {
|
||||||
return incoming
|
return incoming
|
||||||
|
@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
23
pages/top/comments/[within].js
Normal file
23
pages/top/comments/[within].js
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
23
pages/top/posts/[within].js
Normal file
23
pages/top/posts/[within].js
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user