commit b6e43180cd82ba656e2ad0bd0a31151c9f04d06e
parent 16a82ae2a84f72372b54515472ce2ed0db9413e1
Author: Yongbin Kim <iam@yongbin.kim>
Date: Thu, 26 Jan 2023 10:38:23 +0900
feat: ThreadList 컴포넌트 추가
Signed-off-by: Yongbin Kim <iam@yongbin.kim>
Diffstat:
7 files changed, 144 insertions(+), 5 deletions(-)
diff --git a/components/threads/ThreadList.module.css b/components/threads/ThreadList.module.css
@@ -0,0 +1 @@
+.threads{display:flex;flex-direction:column;width:100%;gap:1rem}.thread{display:flex;flex-direction:column;align-items:flex-start;width:100%;padding:.5rem;cursor:pointer;user-select:none;border:1px solid #6f7975;background:#fafdfa;border-radius:.5rem}.thread a{color:inherit;font-weight:bold}.thread:hover{background-color:#d8dbd9;box-shadow:0px 1px 2px 0px rgba(0, 0, 0, 0.2),0px 2px 6px 2px rgba(0, 0, 0, 0.1)}@media(prefers-color-scheme: dark){.thread:hover{background-color:#373a39;box-shadow:0px 1px 2px 0px rgba(0, 0, 0, 0.2),0px 2px 6px 2px rgba(0, 0, 0, 0.1)}}@media(prefers-color-scheme: dark){.thread{border:1px solid #89938f;background:#191c1b;border-radius:.5rem}}.thread .title{display:block;padding:.5rem;line-height:1.75rem;font-size:1.375rem;letter-spacing:0rem;font-weight:400}.thread .meta{padding:.25rem .5rem;line-height:1rem;font-size:.75rem;letter-spacing:.0333333333rem;font-weight:400}.thread .meta a:hover{text-decoration:underline}.thread .preview{padding:.25rem .5rem;line-height:1.25rem;font-size:.875rem;letter-spacing:.0178571429rem;font-weight:400}/*# sourceMappingURL=ThreadList.module.css.map */
diff --git a/components/threads/ThreadList.module.css.map b/components/threads/ThreadList.module.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["ThreadList.module.scss","../../styles/core/_vars.scss","../../styles/core/_elevate.scss","../../styles/core/_colors.scss","../../styles/core/_typography.scss"],"names":[],"mappings":"AAKA,SACE,aACA,sBACA,WACA,ICTI,KDYN,QACE,aACA,sBACA,uBACA,WACA,cACA,eACA,iBAeE,yBACA,mBACA,oBAfF,UACE,cACA,iBAGF,cE0BA,yBAUA,iFCkEA,mCHtGA,cE0BA,yBAUA,kFCkEA,mCHpHF,QAsBI,yBACA,mBACA,qBAGF,eACE,cACA,cI6EA,oBACA,mBACA,oBACA,gBJ5EF,cACE,qBIwEA,iBACA,iBACA,8BACA,gBJxEA,sBACE,0BAIJ,iBACE,qBI+DA,oBACA,kBACA,8BACA","file":"ThreadList.module.css"}
+\ No newline at end of file
diff --git a/components/threads/ThreadList.module.scss b/components/threads/ThreadList.module.scss
@@ -0,0 +1,59 @@
+@use 'core/vars';
+@use 'core/typography';
+@use 'core/colors';
+@use 'core/elevate';
+
+.threads {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ gap: vars.$gap;
+}
+
+.thread {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ padding: 0.5rem;
+ cursor: pointer;
+ user-select: none;
+
+ a {
+ color: inherit;
+ font-weight: bold;
+ }
+
+ &:hover {
+ @include colors.apply-themes() using ($theme) {
+ @include elevate.apply-background-color($theme, 2, 'on-surface');
+ @include elevate.apply-box-shadow(2);
+ }
+ }
+
+ @include colors.apply-themes() using ($theme) {
+ border: 1px solid colors.get($theme, 'outline');
+ background: colors.get($theme, 'background');
+ border-radius: 0.5rem;
+ }
+
+ .title {
+ display: block;
+ padding: 0.5rem;
+ @include typography.apply('title-large');
+ }
+
+ .meta {
+ padding: 0.25rem 0.5rem;
+ @include typography.apply('body-small');
+
+ a:hover {
+ text-decoration: underline;
+ }
+ }
+
+ .preview {
+ padding: 0.25rem 0.5rem;
+ @include typography.apply('body-medium');
+ }
+}
diff --git a/components/threads/ThreadList.tsx b/components/threads/ThreadList.tsx
@@ -0,0 +1,55 @@
+import { Thread } from '@/lib/models/thread'
+import Link from 'next/link'
+import { useRouter } from 'next/router'
+import styles from './ThreadList.module.css'
+import { MouseEvent } from 'react'
+
+export interface ThreadListProps {
+ threads?: Thread[]
+}
+
+export default function ThreadList (props: ThreadListProps) {
+ const router = useRouter()
+
+ const stopPropagation = (e: MouseEvent) => {
+ e.stopPropagation()
+ }
+
+ const handleThreadClicked = (threadId: number) => {
+ return (e: MouseEvent<HTMLElement>) => {
+ e.preventDefault()
+ router.push(
+ '/threads/[...path]',
+ `/threads/${threadId}`,
+ ).catch((e) => {
+ console.error(e)
+ })
+ }
+ }
+
+ return (
+ <div className={styles['threads']}>
+ {props.threads?.map((thread) => (
+ <div
+ key={thread.id}
+ className={styles.thread}
+ onClick={handleThreadClicked(thread.id)}
+ >
+ <div className={styles.meta}>
+ Posted by
+ <Link
+ href={`/users/${thread.authorId}`}
+ onClick={stopPropagation}
+ >
+ {thread.authorName}
+ </Link>
+ </div>
+ <div className={styles.title}>{thread.title}</div>
+ {thread.preview && (
+ <div className={styles.preview}>{thread.preview}</div>
+ )}
+ </div>
+ ))}
+ </div>
+ )
+}
diff --git a/lib/models/thread.ts b/lib/models/thread.ts
@@ -6,11 +6,21 @@ export interface Thread {
authorId: number
authorName: string
title: string
+ preview?: string
createdAt: Date
}
const SQL_LIST_THREADS = `
- select t.id, t.author_id, u.nickname, t.title, t.created_at
+ select t.id,
+ t.author_id,
+ u.nickname,
+ t.title,
+ (select content
+ from thread_comments
+ where thread_id = t.id
+ order by id
+ limit 1) as preview,
+ t.created_at
from threads t
left join user_profiles u on u.login_id = t.author_id
order by t.created_at desc
@@ -30,7 +40,8 @@ export const listThreads = modelBehaviour<
authorId: row[1],
authorName: row[2],
title: row[3],
- createdAt: row[4],
+ preview: row[4],
+ createdAt: row[5],
}))
})
diff --git a/lib/models/wiki_talk.ts b/lib/models/wiki_talk.ts
@@ -3,7 +3,16 @@ import { createThread, Thread } from '@/lib/models/thread'
import { OkPacket, RowDataPacket } from 'mysql2'
const SQL_LIST_WIKI_TALKS = `
- select t.id, t.author_id, u.nickname, t.title, t.created_at
+ select t.id,
+ t.author_id,
+ u.nickname,
+ t.title,
+ (select content
+ from thread_comments
+ where thread_id = t.id
+ order by id
+ limit 1) as preview,
+ t.created_at
from threads t
inner join wiki_talks wt on t.id = wt.thread_id
left join user_profiles u on u.login_id = t.author_id
@@ -25,7 +34,8 @@ export const listWikiTalks = modelBehaviour<
authorId: row[1],
authorName: row[2],
title: row[3],
- createdAt: row[4],
+ preview: row[4],
+ createdAt: row[5],
}))
})
diff --git a/pages/talk/[slug]/[...path].tsx b/pages/talk/[slug]/[...path].tsx
@@ -7,6 +7,7 @@ import Container from '@/components/layout/Container'
import Divider from '@/components/layout/Divider'
import Hero from '@/components/layout/Hero'
import Section from '@/components/layout/Section'
+import ThreadList from '@/components/threads/ThreadList'
import { ApiError } from '@/lib/apierror'
import { Thread } from '@/lib/models/thread'
import { getWikiAndPageACLViaPath } from '@/lib/models/wiki_page'
@@ -64,7 +65,7 @@ export default function TestPage (props: TestPageProps) {
{props.talks.length === 0 ? (
<div>No talks</div>
) : (
- <div>asdf</div>
+ <ThreadList threads={props.talks} />
)}
</Section>