[id].tsx (3018B)
1 import Title from '@/components/elements/Title' 2 import Container from '@/components/layout/Container' 3 import Hero from '@/components/layout/Hero' 4 import Section from '@/components/layout/Section' 5 import { Pagination } from '@/components/Pagination' 6 import ThreadCommentView from '@/components/threads/ThreadCommentView' 7 import ThreadCommentList from '@/components/threads/ThreadCommentList' 8 import ThreadCommentWrite from '@/components/threads/ThreadCommentWrite' 9 import useRefresh from '@/lib/hooks/use_refresh' 10 import { withConnection } from '@/lib/model_helpers' 11 import { 12 getThreadAndFirstComment, 13 listThreadComments, 14 Thread, 15 ThreadComment, 16 } from '@/lib/models/thread' 17 import { parseIntOrDefault } from '@/lib/utils/number' 18 import { GetServerSideProps, GetServerSidePropsResult } from 'next' 19 import { useRouter } from 'next/router' 20 import { useMemo } from 'react' 21 22 const PAGE_SIZE = 10 23 24 export interface ThreadViewPageProps { 25 page: number 26 thread: Thread 27 firstComment: ThreadComment 28 comments: ThreadComment[] 29 } 30 31 function getIdFromQuery (query: any) { 32 if (typeof query.id === 'string') { 33 return parseInt(query.id) 34 } 35 return null 36 } 37 38 export const getServerSideProps: GetServerSideProps<ThreadViewPageProps> = async (context) => { 39 const id = getIdFromQuery(context.query) 40 if (id == null) { 41 return { 42 notFound: true, 43 } 44 } 45 46 const page = Math.max(1, parseIntOrDefault(context.query.page, 1)) 47 48 return await withConnection(async (conn): Promise<GetServerSidePropsResult<ThreadViewPageProps>> => { 49 const threadAndFirstComment = await getThreadAndFirstComment(conn, [id]) 50 if (threadAndFirstComment == null) { 51 return { 52 notFound: true, 53 } 54 } 55 56 const [thread, firstComment] = threadAndFirstComment 57 const comments = await listThreadComments(conn, [id, PAGE_SIZE, (page - 1) * PAGE_SIZE]) 58 59 return { 60 props: { 61 page, 62 thread, 63 firstComment, 64 comments, 65 }, 66 } 67 }) 68 } 69 70 export default function ThreadViewPage (props: ThreadViewPageProps) { 71 const router = useRouter() 72 73 const handleRefresh = async () => { 74 await router.replace({ 75 pathname: router.pathname, 76 query: { 77 ...router.query, 78 page: 1, 79 } 80 }) 81 } 82 83 const idStartsFrom = useMemo(() => { 84 return (props.thread.commentCount ?? 0) - (props.page - 1) * PAGE_SIZE 85 }, [props.page]) 86 87 return ( 88 <> 89 <Hero> 90 <Title kind="headline">{props.thread.title}</Title> 91 </Hero> 92 93 <Container> 94 <Section> 95 <ThreadCommentView index={0} comment={props.firstComment} /> 96 </Section> 97 98 <Section> 99 <ThreadCommentWrite threadId={props.thread.id} onWritten={handleRefresh} /> 100 <ThreadCommentList startsFrom={idStartsFrom} comments={props.comments} /> 101 <Pagination 102 page={props.page} 103 totalCount={props.thread.commentCount ?? 0} 104 pageSize={PAGE_SIZE} 105 /> 106 </Section> 107 </Container> 108 </> 109 ) 110 }