Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 63 additions & 6 deletions src/page/post-detail-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import {
import { getMyMemberId } from '@shared/utils/auth';
import { COMMENT_QUERY_OPTIONS } from '@shared/api/domain/comments/query';
import { queryClient } from '@app/providers/query-client';
import { COMMENT_MUTATION_OPTIONS } from '@shared/api/domain/comments/query';
import { useState } from 'react';
import XIcon from '@shared/assets/icon/x.svg?react';

const handleShare = async () => {
const url = window.location.href;
Expand All @@ -40,6 +43,10 @@ const PostDetailPage = () => {
const navigate = useNavigate();
const { feedId } = useParams();
const numericFeedId = Number(feedId);
const [comment, setComment] = useState('');
const [parentId, setParentId] = useState<number | null>(null);
const [replyTarget, setReplyTarget] = useState<string | null>(null);

const { mutate: applyParticipation } = useMutation({
...PARTICIPATION_MUTATION_OPTIONS.APPLY(),
onSuccess: () => {
Expand Down Expand Up @@ -91,6 +98,18 @@ const PostDetailPage = () => {
});
},
});
const { mutate: createComment } = useMutation({
...COMMENT_MUTATION_OPTIONS.CREATE(),
onSuccess: () => {
setComment('');
setParentId(null);
setReplyTarget(null);

queryClient.invalidateQueries({
queryKey: ['comments', numericFeedId],
});
},
});

const { data, isLoading } = useQuery(
FEED_QUERY_OPTIONS.DETAIL(numericFeedId),
Expand Down Expand Up @@ -142,6 +161,10 @@ const PostDetailPage = () => {
comments={commentsData ?? []}
participants={participants}
isOwner={isOwner}
onReply={(commentId, nickname) => {
setParentId(commentId);
setReplyTarget(nickname ?? null);
}}
onChangeApproval={(participationId, status) => {
if (status === 'approved') {
approveParticipation(participationId);
Expand All @@ -159,12 +182,46 @@ const PostDetailPage = () => {
</Button>
</div>
)}
<div className="flex w-full gap-[1.6rem] py-[1.4rem] px-[2.4rem]">
<Input inputSize="sm" placeholder="댓글을 입력해주세요" />
<FloatingActionButton
mode="inline"
icon={<SendIcon width={'2rem'} height={'2rem'} />}
/>
<div className="flex flex-col px-[2.4rem] py-[1.4rem] gap-[0.6rem]">
{replyTarget && (
<div className="flex justify-between items-center pr-[6rem] text-gray-400 typo-caption">
<span>↳{replyTarget}님에게 답글 작성중...</span>

<button
onClick={() => {
setParentId(null);
setReplyTarget(null);
}}
>
<XIcon width={'2rem'} height={'2rem'} />
</button>
</div>
)}

<div className="flex w-full gap-[1.6rem]">
<Input
inputSize="sm"
placeholder="댓글을 입력해주세요"
value={comment}
onChange={(e) => setComment(e.target.value)}
/>

<FloatingActionButton
mode="inline"
icon={<SendIcon width={'2rem'} height={'2rem'} />}
onClick={() => {
if (!comment.trim()) return;

createComment({
feedId: numericFeedId,
body: {
description: comment,
parentId: parentId ?? undefined,
},
});
}}
/>
</div>
</div>
</div>
);
Expand Down
26 changes: 24 additions & 2 deletions src/shared/api/domain/comments/query.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
import { queryOptions } from '@tanstack/react-query';
import { queryOptions, mutationOptions } from '@tanstack/react-query';
import { api } from '@shared/api/config/instance';
import { END_POINT } from '@shared/api/end-point';
import type { GetCommentsResponse } from '@shared/types/comments/type';
import type {
CreateCommentRequest,
GetCommentsResponse,
} from '@shared/types/comments/type';

const getComments = async (feedId: number) => {
return api.get(END_POINT.FEED.COMMENTS(feedId)).json<GetCommentsResponse>();
};

const createComment = async (feedId: number, body: CreateCommentRequest) => {
return api.post(END_POINT.FEED.COMMENTS(feedId), {
json: body,
});
};

export const COMMENT_QUERY_OPTIONS = {
LIST: (feedId: number) =>
queryOptions({
queryKey: ['comments', feedId],
queryFn: () => getComments(feedId),
}),
};

export const COMMENT_MUTATION_OPTIONS = {
CREATE: () =>
mutationOptions({
mutationFn: ({
feedId,
body,
}: {
feedId: number;
body: CreateCommentRequest;
}) => createComment(feedId, body),
}),
};
3 changes: 2 additions & 1 deletion src/shared/api/end-point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ export const END_POINT = {
LIST: 'api/feeds',
DETAIL: (feedId: number) => `api/feeds/${feedId}`,
PARTICIPATION: (feedId: number) => `api/feeds/${feedId}/participations`,
COMMENTS: (feedId: number) => `api/feeds/${feedId}/comments`, // ✅ 추가
COMMENTS: (feedId: number) => `api/feeds/${feedId}/comments`,
CREATE: (feedId: number) => `api/feeds/${feedId}/comments`,
},
PARTICIPATION: {
APPROVE: (id: number) => `api/participations/${id}/approve`,
Expand Down
3 changes: 3 additions & 0 deletions src/shared/types/comments/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ import type { paths } from '@shared/types/schema';

export type GetCommentsResponse =
paths['/api/feeds/{feedId}/comments']['get']['responses']['200']['content']['*/*'];

export type CreateCommentRequest =
paths['/api/feeds/{feedId}/comments']['post']['requestBody']['content']['application/json'];
15 changes: 12 additions & 3 deletions src/widgets/postDetail/comment/comment-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface CommentItemProps {
memberId?: number;
participationId?: number;
createdAt?: string;
children?: CommentItemProps[];
}

export interface Participant {
Expand All @@ -21,6 +22,7 @@ export interface Participant {
interface CommentItemUIProps extends CommentItemProps {
isOwner?: boolean;
participants?: Participant[];
onReply?: (commentId: number, nickname?: string) => void;
onChangeApproval?: (
participationId: number,
status: Exclude<ApprovalStatus, 'pending'>,
Expand All @@ -32,7 +34,11 @@ export function CommentItem({
description,
commentType = 'USER',
isOwner = false,
depth,
onChangeApproval,
onReply,
commentId,
children,
participationId,
participants,
}: CommentItemUIProps) {
Expand Down Expand Up @@ -77,10 +83,13 @@ export function CommentItem({
<div className="mt-[0.4rem]">
<p className="typo-body1 whitespace-pre-line">{description}</p>

{!isSystem && (
<button className="mt-[0.6rem] flex gap-[0.4rem] items-center typo-caption text-gray-500">
{depth === 0 && (
<button
onClick={() => onReply?.(commentId!, nickname)}
className="mt-[0.6rem] flex gap-[0.4rem] items-center typo-caption text-gray-500"
>
<MessageIcon width={'1.8rem'} height={'1.8rem'} />
답글 쓰기
{children?.length ?? 0} 답글 쓰기
</button>
)}
</div>
Expand Down
16 changes: 10 additions & 6 deletions src/widgets/postDetail/comment/comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface CommentProps {
comments: CommentItemProps[];
participants?: Participant[];
isOwner?: boolean;
onReply?: (commentId: number, nickname?: string) => void; // 추가
onChangeApproval?: (
participationId: number,
status: Exclude<ApprovalStatus, 'pending'>,
Expand All @@ -19,6 +20,7 @@ export function Comment({
comments,
participants,
isOwner,
onReply,
onChangeApproval,
}: CommentProps) {
const systemRoot = comments.filter(
Expand All @@ -40,23 +42,24 @@ export function Comment({
{...comment}
participants={participants}
isOwner={isOwner}
onReply={onReply}
onChangeApproval={onChangeApproval}
/>
))}

{userRoot.map((comment) => (
<div className="flex flex-col gap-[2rem]" key={comment.commentId}>
<div className="flex flex-col" key={comment.commentId}>
<CommentItem
{...comment}
participants={participants}
isOwner={isOwner}
onReply={onReply}
onChangeApproval={onChangeApproval}
/>

<div className="flex flex-col gap-[1rem] pl-[4.4rem]">
{comments
.filter((c) => c.parentId === comment.commentId)
.map((reply) => (
{comment.children && comment.children.length > 0 && (
<div className="flex flex-col gap-[1rem] pl-[4.4rem] pt-[2rem]">
{comment.children.map((reply) => (
<CommentItem
key={reply.commentId}
{...reply}
Expand All @@ -65,7 +68,8 @@ export function Comment({
onChangeApproval={onChangeApproval}
/>
))}
</div>
</div>
)}
</div>
))}
</div>
Expand Down
Loading