Skip to content

Commit

Permalink
[#486] [책 상세 / 모임 상세] 코멘트 목록 컴포넌트 (#487)
Browse files Browse the repository at this point in the history
* refactor: writer 타입 정의

* feat: CommentList 컴포넌트 구현 (presentaional)

* feat: 책 코멘트 query에 suspense 적용

- useBookComments 쿼리 추가

* feat: BookGroupCommentList 구현 (contaier)

* feat: BookCommentList 구현 (container)

* feat: CommentList 컴포넌트 스토리북 작성

* chore: 사용되지 않는 이전 CommentList 컴포넌트 삭제

* refactor: book comments query key 추가

* refactor: 코멘트 메뉴 관련 props 수정

* feat: 코멘트목록 name, 이벤트 핸들러 props 추가

* refactor: 변수, prop 이름 수정

* fix: storybook props 수정
  • Loading branch information
gxxrxn authored Feb 20, 2024
1 parent ff66f15 commit 9267ba6
Show file tree
Hide file tree
Showing 11 changed files with 407 additions and 93 deletions.
10 changes: 6 additions & 4 deletions src/app/group/[groupId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
'use client';

import Link from 'next/link';

import { isAuthed } from '@/utils/helpers';
import { KAKAO_LOGIN_URL } from '@/constants/url';

import SSRSafeSuspense from '@/components/SSRSafeSuspense';
import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo';
import CommentList from '@/v1/bookGroup/detail/CommentList';
import BookGroupCommentList from '@/v1/comment/BookGroupCommentList';
import BookGroupNavigation from '@/v1/bookGroup/BookGroupNavigation';
import JoinBookGroupButton from '@/v1/bookGroup/detail/JoinBookGroupButton';
import BottomActionButton from '@/v1/base/BottomActionButton';
import { isAuthed } from '@/utils/helpers';
import { KAKAO_LOGIN_URL } from '@/constants/url';

const DetailBookGroupPage = ({
params: { groupId },
Expand All @@ -30,7 +32,7 @@ const DetailBookGroupPage = ({
<Divider />
<div className="flex flex-col gap-[1rem]">
<Heading text="게시글" />
<CommentList groupId={groupId} />
<BookGroupCommentList groupId={groupId} />
</div>
</div>
{isAuthed() ? (
Expand Down
2 changes: 2 additions & 0 deletions src/queries/book/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const bookKeys = {
bestSeller: () => [...bookKeys.all, 'bestSeller'],
bookmark: (bookId: APIBook['bookId']) =>
[...bookKeys.detail(bookId), 'bookmark'] as const,
comments: (bookId: APIBook['bookId']) =>
[...bookKeys.detail(bookId), 'comments'] as const,
};

export default bookKeys;
46 changes: 38 additions & 8 deletions src/queries/book/useBookCommentsQuery.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,49 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';
import { UseQueryOptions } from '@tanstack/react-query';

import type {
APIBook,
APIBookCommentPagination,
BookComment,
} from '@/types/book';
import bookAPI from '@/apis/book';
import type { APIBook } from '@/types/book';
import useQueryWithSuspense from '@/hooks/useQueryWithSuspense';
import bookKeys from './key';

const useBookCommentsQuery = (
const useBookCommentsQuery = <TData = APIBookCommentPagination>(
bookId: APIBook['bookId'],
options?: Pick<
UseQueryOptions<Awaited<ReturnType<typeof bookAPI.getComments>>['data']>,
'onSuccess' | 'onError'
options?: UseQueryOptions<
Awaited<ReturnType<typeof bookAPI.getComments>>['data'],
unknown,
TData
>
) =>
useQuery(
['bookComments', bookId],
useQueryWithSuspense(
bookKeys.comments(bookId),
() => bookAPI.getComments(bookId).then(({ data }) => data),
options
);

export default useBookCommentsQuery;

const transformBookCommentsData = ({
bookComments,
}: APIBookCommentPagination) => {
return bookComments.map(
({ contents, createdAt, commentId, userId, userProfileImage, nickname }) =>
({
id: commentId,
writer: {
id: userId,
profileImageSrc: userProfileImage,
name: nickname,
},
createdAt,
content: contents,
} as BookComment)
);
};

export const useBookComments = (bookId: APIBook['bookId']) =>
useBookCommentsQuery(bookId, {
select: transformBookCommentsData,
});
58 changes: 58 additions & 0 deletions src/stories/comment/CommentList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Meta, StoryObj } from '@storybook/react';
import CommentList from '@/v1/comment/CommentList';

const meta: Meta<typeof CommentList> = {
title: 'comment/CommentList',
component: CommentList,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof CommentList>;

const comments = [
{
id: 1,
writer: {
id: 2,
profileImageSrc: 'https://bit.ly/kent-c-dodds',
name: 'Kent C. Dodds',
},
createdAt: '2023.02.05',
content: '추천해요!',
},
{
id: 2,
writer: {
id: 3,
profileImageSrc: 'https://i.pravatar.cc/300',
name: '김계란',
},
createdAt: '2023.02.07',
content: '읽고 또 읽어도 새로워요. 🫠',
},
];

export const Default: Story = {
args: {
comments,
isEditableComment: ({ writer }) => writer.id === 3,
},
};

export const Hidden: Story = {
args: {
comments,
isHidden: true,
hiddenText: '멤버만 볼 수 있어요 🥲',
},
};

export const Empty: Story = {
args: {
comments: [],
emptyText: `아직 코멘트가 없어요.
첫 코멘트의 주인공이 되어보세요!`,
},
};
8 changes: 7 additions & 1 deletion src/types/book.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BookSearchPagination, Pagination } from './common';
import { APIJobGroup } from './job';
import { APIUser } from './user';
import { APIUser, Writer } from './user';

export interface APIBook {
bookId: number;
Expand Down Expand Up @@ -82,6 +82,12 @@ export interface APIBookCommentPagination extends Pagination {
bookComments: APIBookComment[];
}

export type BookComment = {
id: APIBook['bookId'];
writer: Writer;
createdAt: APIBookComment['createdAt'];
content: APIBookComment['contents'];
};
export interface APIBestSeller {
isbn: string;
title: string;
Expand Down
8 changes: 2 additions & 6 deletions src/types/group.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIBook } from './book';
import { APIUser } from './user';
import { APIUser, Writer } from './user';
import { Pagination } from './common';

type APIGroupOwner = {
Expand Down Expand Up @@ -91,11 +91,7 @@ export type BookGroupDetail = {

export type BookGroupComment = {
id: APIGroup['bookGroupId'];
writer: {
id: APIUser['userId'];
profileImageSrc: APIUser['profileImage'];
name: APIUser['nickname'];
};
writer: Writer;
createdAt: APIGroupComment['createdAt'];
content: APIGroupComment['contents'];
};
6 changes: 6 additions & 0 deletions src/types/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ export interface APIMyProfile extends Omit<APIUserProfile, 'nickname'> {
}

export type APIUser = APIUserProfile & { name: string | null };

export type Writer = {
id: APIUser['userId'];
profileImageSrc: APIUser['profileImage'];
name: APIUser['nickname'];
};
74 changes: 0 additions & 74 deletions src/v1/bookGroup/detail/CommentList.tsx

This file was deleted.

22 changes: 22 additions & 0 deletions src/v1/comment/BookCommentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useMyProfileId } from '@/queries/user/useMyProfileQuery';
import { useBookComments } from '@/queries/book/useBookCommentsQuery';
import { isAuthed } from '@/utils/helpers';

import CommentList from './CommentList';

const BookCommentList = ({ bookId }: { bookId: number }) => {
const { data: comments } = useBookComments(bookId);
const { data: myId } = useMyProfileId({ enabled: isAuthed() });

return (
<CommentList
name={'코멘트'}
comments={comments}
isEditableComment={({ writer }) => writer.id === myId}
emptyText={`아직 코멘트가 없어요.
가장 먼저 코멘트를 남겨보세요!`}
/>
);
};

export default BookCommentList;
29 changes: 29 additions & 0 deletions src/v1/comment/BookGroupCommentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useBookGroupComments } from '@/queries/group/useBookGroupCommentsQuery';
import { useMyProfileId } from '@/queries/user/useMyProfileQuery';
import { useBookGroup } from '@/queries/group/useBookGroupQuery';
import { isAuthed } from '@/utils/helpers';

import CommentList from './CommentList';

const BookGroupCommentList = ({ groupId }: { groupId: number }) => {
const { data: bookGroupInfo } = useBookGroup(groupId);
const { data: comments } = useBookGroupComments(groupId);
const { data: myId } = useMyProfileId({ enabled: isAuthed() });
const { isPublic, isMember } = bookGroupInfo;

const isHidden = !isPublic && !isMember;

return (
<CommentList
name={'게시글'}
comments={comments}
isEditableComment={({ writer }) => writer.id === myId}
isHidden={isHidden}
hiddenText={`멤버만 볼 수 있어요 🥲`}
emptyText={`아직 게시글이 없어요.
가장 먼저 게시글을 남겨보세요!`}
/>
);
};

export default BookGroupCommentList;
Loading

0 comments on commit 9267ba6

Please sign in to comment.