From 2c92db1740ef2b0ccdfe79a38090cbe6dba8bdeb Mon Sep 17 00:00:00 2001 From: Joel Gustafson Date: Sun, 19 Dec 2021 18:31:57 -0500 Subject: [PATCH] added pagination and users page --- components/About.tsx | 9 ++++- components/Directory.tsx | 41 +++++++++++++++++++ components/MessageView.tsx | 2 +- components/PageNav.tsx | 25 ++++++++++++ components/ThreadView.tsx | 2 +- pages/index.tsx | 52 ++++++++++++++++++++---- pages/thread/[id].tsx | 65 ++++++++++++++++++++++------- pages/users.tsx | 83 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 254 insertions(+), 25 deletions(-) create mode 100644 components/Directory.tsx create mode 100644 components/PageNav.tsx create mode 100644 pages/users.tsx diff --git a/components/About.tsx b/components/About.tsx index 0da2c8d..51767ff 100644 --- a/components/About.tsx +++ b/components/About.tsx @@ -2,8 +2,13 @@ import React from "react" export function About({}) { return ( -
- GitHub +
+ + GitHub +
) } diff --git a/components/Directory.tsx b/components/Directory.tsx new file mode 100644 index 0000000..11e5fd4 --- /dev/null +++ b/components/Directory.tsx @@ -0,0 +1,41 @@ +import React from "react" +import { User } from "utils/types" +import { UserIcon } from "./UserIcon" + +interface DirectoryProps { + users: User[] + userCount: number +} + +export const Directory: React.FC = (props) => { + const rest = props.userCount - props.users.length + return ( +
+ {props.users.map((user) => ( +
+ + + {user.twitterHandle} + + + + +
+ ))} + {rest > 0 && ( + + ... all users + + )} +
+ ) +} diff --git a/components/MessageView.tsx b/components/MessageView.tsx index aea6cc9..83b7ad0 100644 --- a/components/MessageView.tsx +++ b/components/MessageView.tsx @@ -20,7 +20,7 @@ export function MessageView(props: MessageViewProps) { }, []) return ( -
+
{props.message.body}
diff --git a/components/PageNav.tsx b/components/PageNav.tsx new file mode 100644 index 0000000..58f8857 --- /dev/null +++ b/components/PageNav.tsx @@ -0,0 +1,25 @@ +import React from "react" + +interface PageNavProps { + currentPage: number + lastPage: number +} + +export const PageNav: React.FC = (props) => { + return ( +
+
+ page {props.currentPage} of {props.lastPage} +
+ first + · + previous + · + + next + + · + last +
+ ) +} diff --git a/components/ThreadView.tsx b/components/ThreadView.tsx index c67f196..0c7c7a7 100644 --- a/components/ThreadView.tsx +++ b/components/ThreadView.tsx @@ -29,7 +29,7 @@ export function ThreadView(props: ThreadViewProps) { }, []) return ( -
+
{props.thread.firstMessage.body}
diff --git a/pages/index.tsx b/pages/index.tsx index ee7009e..ed4dbc9 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -21,8 +21,14 @@ import { CreateThread } from "components/CreateThread" import { ThreadView } from "components/ThreadView" import { getVKeys } from "utils/server/vkeys" import { About } from "components/About" +import { Directory } from "components/Directory" +import { number } from "fp-ts" +import { PageNav } from "components/PageNav" interface IndexPageProps extends PageProps { + currentPage: number + threadCount: number + userCount: number defaultUsers: User[] vKeys: VKeys threads: (Thread & { @@ -32,19 +38,29 @@ interface IndexPageProps extends PageProps { })[] } +const threadPageSize = 20 +const defaultUserLimit = 20 + export const getServerSideProps: GetServerSideProps = async (ctx) => { const { publicKey } = nookies.get(ctx) const defaultUsers = await prisma.user.findMany({ + // afaikt this should work but doesn't... // orderBy: { threads: { _count: "desc" } }, select: userProps, - take: 20, + take: defaultUserLimit, }) + const userCount = await prisma.user.count() + + const page = + typeof ctx.query.page === "string" ? parseInt(ctx.query.page) : NaN + const threads = await prisma.thread.findMany({ - take: 20, - orderBy: { createdAt: "desc" }, + take: threadPageSize, + skip: isNaN(page) ? 0 : (page - 1) * threadPageSize, + orderBy: { updatedAt: "desc" }, where: { firstMessageId: { not: null } }, select: { ...threadProps, @@ -56,6 +72,8 @@ export const getServerSideProps: GetServerSideProps = }, }) + const threadCount = await prisma.thread.count() + const serializedThreads = threads.map( ({ createdAt, updatedAt, firstMessage, _count, ...thread }) => ({ id: thread.id, @@ -73,9 +91,18 @@ export const getServerSideProps: GetServerSideProps = ) const vKeys = getVKeys() + const currentPage = isNaN(page) ? 1 : page if (publicKey === undefined) { return { - props: { vKeys, user: null, threads: serializedThreads, defaultUsers }, + props: { + vKeys, + user: null, + threads: serializedThreads, + defaultUsers, + userCount, + currentPage, + threadCount, + }, } } else { const user = await prisma.user.findUnique({ @@ -88,24 +115,35 @@ export const getServerSideProps: GetServerSideProps = } return { - props: { vKeys, user, threads: serializedThreads, defaultUsers }, + props: { + vKeys, + user, + threads: serializedThreads, + defaultUsers, + userCount, + currentPage, + threadCount, + }, } } } export default function IndexPage(props: IndexPageProps) { + const lastPage = Math.ceil(props.threadCount / threadPageSize) return (
-
+
{props.threads.map((thread) => ( ))} +
-
+
+
diff --git a/pages/thread/[id].tsx b/pages/thread/[id].tsx index d7d0e6b..2bbf62f 100644 --- a/pages/thread/[id].tsx +++ b/pages/thread/[id].tsx @@ -22,6 +22,7 @@ import { UserIcon } from "components/UserIcon" import { CreateMessage } from "components/CreateMessage" import { PageContext } from "utils/context" +import { PageNav } from "components/PageNav" type ThreadPageParams = { id: string } @@ -30,8 +31,12 @@ interface ThreadPageProps extends PageProps { thread: Thread group: User[] messages: Message[] + currentPage: number + messageCount: number } +const messagePageSize = 20 + export const getServerSideProps: GetServerSideProps< ThreadPageProps, ThreadPageParams @@ -42,12 +47,23 @@ export const getServerSideProps: GetServerSideProps< const { publicKey } = nookies.get(ctx, "publicKey") + const page = + typeof ctx.query.page === "string" ? parseInt(ctx.query.page) : NaN + const thread = await prisma.thread.findUnique({ where: { id: ctx.params.id }, select: { ...threadProps, group: { select: userProps }, - messages: { select: messageProps }, + messages: { + take: messagePageSize, + skip: isNaN(page) ? 0 : (page - 1) * messagePageSize, + orderBy: { createdAt: "desc" }, + select: messageProps, + }, + _count: { + select: { messages: true }, + }, }, }) @@ -56,6 +72,7 @@ export const getServerSideProps: GetServerSideProps< } const { id, createdAt, updatedAt, group, messages } = thread + const messageCount = thread._count.messages const serializedThread = { id, @@ -73,6 +90,7 @@ export const getServerSideProps: GetServerSideProps< ) const vKeys = getVKeys() + const currentPage = isNaN(page) ? 1 : page if (publicKey === undefined) { return { props: { @@ -81,6 +99,8 @@ export const getServerSideProps: GetServerSideProps< thread: serializedThread, group, messages: serializedMessages, + currentPage, + messageCount, }, } } else { @@ -100,6 +120,8 @@ export const getServerSideProps: GetServerSideProps< thread: serializedThread, group, messages: serializedMessages, + currentPage, + messageCount, }, } } @@ -114,21 +136,13 @@ export default function ThreadPage(props: ThreadPageProps) { [user] ) + const lastPage = Math.ceil(props.messageCount / messagePageSize) + return (
-
-
- {props.group.map((user) => ( -
- {user.twitterHandle} -
- ))} -
+
{props.messages.map((message) => ( ))} - {userInGroup && ( + {userInGroup && props.currentPage === lastPage && ( )} +
- +
+
in this thread
+ {props.group.map((user) => ( + + ))} +
diff --git a/pages/users.tsx b/pages/users.tsx new file mode 100644 index 0000000..1f04858 --- /dev/null +++ b/pages/users.tsx @@ -0,0 +1,83 @@ +import React from "react" +import type { GetServerSideProps } from "next" + +import nookies from "nookies" +import { PageProps, User, userProps } from "utils/types" +import { UserIcon } from "components/UserIcon" + +interface UsersPageProps extends PageProps { + users: User[] +} + +const pageSize = 100 + +export const getServerSideProps: GetServerSideProps = + async (ctx) => { + const { publicKey } = nookies.get(ctx) + const { after } = ctx.query + + const cursor = + typeof after === "string" ? { cursor: { publicKey: after } } : null + + const users = await prisma.user.findMany({ + select: userProps, + take: pageSize, + orderBy: { publicKey: "asc" }, + skip: typeof after === "string" ? 1 : 0, + ...cursor, + }) + + if (publicKey === undefined) { + return { props: { user: null, users } } + } else { + const user = await prisma.user.findUnique({ + where: { publicKey }, + select: userProps, + }) + + if (user === null) { + nookies.destroy(ctx, "publicKey", { path: "/", httpOnly: true }) + } + + return { props: { user, users } } + } + } + +export default function UsersPage(props: UsersPageProps) { + const cursor: User | undefined = props.users[pageSize - 1] + + return ( +
+
+ {props.users.map((user) => ( + + ))} + {cursor && ( + + next + + )} +
+
+ ) +}