[...path].tsx (3761B)
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 WikiBase from '@/components/wiki/WikiBase' 7 import { withConnection } from '@/lib/model_helpers' 8 import { getWikiAndPageACLViaPath } from '@/lib/models/wiki_acl' 9 import { countWikiChanges, getWikiChanges, WikiChangeListItem } from '@/lib/models/wiki_change' 10 import { getPageQuery } from '@/lib/utils/pagination' 11 import { getSlugAndPath } from '@/lib/utils/wiki' 12 import moment from 'moment' 13 import { GetServerSideProps } from 'next' 14 import Link from 'next/link' 15 import { useRouter } from 'next/router' 16 import { useMemo } from 'react' 17 18 const PAGE_SIZE = 30 19 20 export interface WikiLogsPageProps { 21 page: number 22 count: number 23 changes: WikiChangeListItem[] 24 } 25 26 export const getServerSideProps: GetServerSideProps<WikiLogsPageProps> = async (context) => { 27 const [slug, path] = getSlugAndPath(context) ?? [] 28 if (slug == null || path == null) { 29 console.error('slug or path is null', slug, path) 30 return { notFound: true } 31 } 32 33 const [page, offset] = getPageQuery(context.query, PAGE_SIZE) 34 35 const result = await withConnection(async (conn) => { 36 const aclInfo = await getWikiAndPageACLViaPath(conn, [slug, path]) 37 if (aclInfo == null || aclInfo.pageId == null) { 38 return null 39 } 40 41 const count = await countWikiChanges(conn, [aclInfo.pageId]) 42 const changes = await getWikiChanges(conn, [aclInfo.pageId, PAGE_SIZE, offset]) 43 44 return { count, changes } 45 }) 46 if (result == null || result.changes.length === 0) { 47 return { notFound: true } 48 } 49 50 return { 51 props: { 52 page, 53 count: result.count, 54 changes: result.changes 55 }, 56 } 57 } 58 59 export default function WikiLogsPage (props: WikiLogsPageProps) { 60 const router = useRouter() 61 const [slug, path] = useMemo(() => getSlugAndPath(router), [router]) 62 return ( 63 <> 64 <WikiBase pageKind={'logs'} title={`편집 기록: ${path}`}> 65 <Section> 66 <ul> 67 {props.changes.map((change, i, changes) => ( 68 <li key={change.id}> 69 <Link href={`/wiki/${slug}/${path}?rev=${change.id}`}> 70 {moment(change.createdAt).format('YYYY년 MM월 DD일 hh시 mm분')} 71 </Link> 72 73 <AuthorLink 74 authorId={change.authorId} 75 authorIp={change.authorIp} 76 nickname={change.nickname} 77 /> 78 79 <span>{change.size} bytes</span> 80 81 <SizeDiff 82 size={change.size} 83 prev={i + 1 === changes.length ? 0 : changes[i + 1].size} 84 /> 85 </li> 86 ))} 87 </ul> 88 89 <Pagination page={props.page} totalCount={props.count} pageSize={PAGE_SIZE}/> 90 </Section> 91 </WikiBase> 92 </> 93 ) 94 } 95 96 function AuthorLink (props: { authorId?: number; authorIp?: string; nickname?: string }) { 97 if (props.authorId != null && props.nickname != null) { 98 return ( 99 <Link href={`/users/${props.authorId}`}> 100 {props.nickname} 101 </Link> 102 ) 103 } 104 105 if (props.authorIp != null) { 106 return ( 107 <span>{props.authorIp}</span> 108 ) 109 } 110 111 return ( 112 <span>unknown</span> 113 ) 114 } 115 116 function SizeDiff (props: { size: number; prev: number }) { 117 const diff = props.size - props.prev 118 if (diff > 0) { 119 return ( 120 <span>(+{diff} bytes)</span> 121 ) 122 } 123 124 if (diff < 0) { 125 return ( 126 <span>(-{diff} bytes)</span> 127 ) 128 } 129 130 return ( 131 <span>(±0 bytes)</span> 132 ) 133 }