From 990e2364b071e85d71db4f442d3a47b932d1d1ed Mon Sep 17 00:00:00 2001 From: manNomi Date: Wed, 3 Jun 2026 16:32:19 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=B0=A8=EB=8B=A8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[boardCode]/CommunityPageContent.tsx | 9 +-- .../[boardCode]/[postId]/CommentSection.tsx | 69 +++++++++++++----- .../[boardCode]/[postId]/KebabMenu.tsx | 24 ++++++- .../community/_hooks/useBlockCommunityUser.ts | 70 +++++++++++++++++++ .../_hooks/useSelectReportHandler.ts | 27 +------ .../src/components/ui/ReportPanel/index.tsx | 5 +- apps/web/src/types/community.ts | 1 + 7 files changed, 151 insertions(+), 54 deletions(-) create mode 100644 apps/web/src/app/community/_hooks/useBlockCommunityUser.ts diff --git a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx index 0474b524..e31942e2 100644 --- a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx +++ b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx @@ -7,18 +7,11 @@ import { COMMUNITY_INITIAL_CATEGORY } from "@/apis/community/postListQuery"; import ButtonTab from "@/components/ui/ButtonTab"; import { COMMUNITY_BOARDS, COMMUNITY_CATEGORIES } from "@/constants/community"; import useReportedPostsStore from "@/lib/zustand/useReportedPostsStore"; -import type { ListPost } from "@/types/community"; import { CommunityPostListSkeleton } from "./CommunityPageSkeleton"; import CommunityRegionSelector from "./CommunityRegionSelector"; import PostCards from "./PostCards"; import PostWriteButton from "./PostWriteButton"; -type ListPostWithAuthor = ListPost & { - postFindSiteUserResponse?: { - id: number; - }; -}; - interface CommunityPageContentProps { boardCode: string; } @@ -47,7 +40,7 @@ const CommunityPageContent = ({ boardCode }: CommunityPageContentProps) => { return false; } - const authorId = (post as ListPostWithAuthor).postFindSiteUserResponse?.id; + const authorId = post.postFindSiteUserResponse?.id; if (typeof authorId === "number" && blockedUserIdSet.has(authorId)) { return false; diff --git a/apps/web/src/app/community/[boardCode]/[postId]/CommentSection.tsx b/apps/web/src/app/community/[boardCode]/[postId]/CommentSection.tsx index 8899caa3..d14d3605 100644 --- a/apps/web/src/app/community/[boardCode]/[postId]/CommentSection.tsx +++ b/apps/web/src/app/community/[boardCode]/[postId]/CommentSection.tsx @@ -1,11 +1,13 @@ "use client"; import clsx from "clsx"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { useDeleteComment } from "@/apis/community"; +import useBlockCommunityUser from "@/app/community/_hooks/useBlockCommunityUser"; import Dropdown from "@/components/ui/Dropdown"; import Image from "@/components/ui/FallbackImage"; import { DEFAULT_PROFILE_IMAGE } from "@/constants/profile"; +import useReportedPostsStore from "@/lib/zustand/useReportedPostsStore"; import { IconMoreVertFilled, IconSubComment } from "@/public/svgs"; import type { Comment as CommentType, CommunityUser } from "@/types/community"; import { normalizeImageUrlToUploadCdn } from "@/utils/cdnUrl"; @@ -21,9 +23,20 @@ type CommentSectionProps = { const CommentSection = ({ comments, postId, refresh }: CommentSectionProps) => { const [curSelectedComment, setCurSelectedComment] = useState(null); const [activeDropdown, setActiveDropdown] = useState(null); + const blockedUserIds = useReportedPostsStore((state) => state.blockedUserIds); const deleteCommentMutation = useDeleteComment(); + const visibleComments = useMemo(() => { + if (blockedUserIds.length === 0) { + return comments; + } + + const blockedUserIdSet = new Set(blockedUserIds); + + return comments.filter((comment) => !blockedUserIdSet.has(comment.postFindSiteUserResponse.id)); + }, [comments, blockedUserIds]); + const deleteComment = (commentId: number) => { if (!window.confirm("정말 삭제하시겠습니까?")) return; deleteCommentMutation.mutate({ commentId, postId }); @@ -31,7 +44,7 @@ const CommentSection = ({ comments, postId, refresh }: CommentSectionProps) => { return (
- {comments?.map((comment) => ( + {visibleComments?.map((comment) => ( - {comment.isOwner && ( + {!isDeleted && ( )} @@ -151,32 +168,52 @@ const CommentProfile = ({ user }: { user: CommunityUser }) => { const CommentDropdown = ({ commentId, + isOwner, + authorId, + authorNickname, activeDropdown, toggleDropdown, + setActiveDropdown, deleteComment, }: { commentId: number; + isOwner: boolean; + authorId: number; + authorNickname: string; activeDropdown: number | null; toggleDropdown: (commentId: number) => void; + setActiveDropdown: (commentId: number | null) => void; deleteComment: (commentId: number) => void; }) => { + const { handleBlockUser } = useBlockCommunityUser({ + onBlocked: () => setActiveDropdown(null), + }); + + const options = isOwner + ? [ + { + label: "삭제하기", + action: () => { + deleteComment(commentId); + }, + }, + ] + : [ + { + label: "차단하기", + action: () => { + setActiveDropdown(null); + void handleBlockUser({ userId: authorId, nickname: authorNickname }); + }, + }, + ]; + return ( -
+
event.stopPropagation()}> - {activeDropdown === commentId && ( - { - deleteComment(commentId); - }, - }, - ]} - /> - )} + {activeDropdown === commentId && }
); }; diff --git a/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx b/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx index 0b1d4f64..235eab4c 100644 --- a/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx +++ b/apps/web/src/app/community/[boardCode]/[postId]/KebabMenu.tsx @@ -1,8 +1,10 @@ "use client"; +import { Ban } from "lucide-react"; import { useRouter } from "next/navigation"; import { type RefObject, useEffect, useRef, useState } from "react"; import { useDeletePost } from "@/apis/community"; +import useBlockCommunityUser from "@/app/community/_hooks/useBlockCommunityUser"; import ReportPanel from "@/components/ui/ReportPanel"; import { showIconToast } from "@/lib/toast/showIconToast"; import { IconSetting } from "@/public/svgs/mentor"; @@ -52,6 +54,9 @@ const KebabMenu = ({ postId, boardCode, isOwner = false, authorId }: KebabMenuPr const dropdownRef = useRef(null); const { mutate: deletePost } = useDeletePost(); const router = useRouter(); + const { handleBlockUser, isBlocking } = useBlockCommunityUser({ + onBlocked: () => router.replace(`/community/${boardCode}`), + }); const [isDropdownOpen, setIsDropdownOpen] = useState(false); @@ -87,8 +92,25 @@ const KebabMenu = ({ postId, boardCode, isOwner = false, authorId }: KebabMenuPr
  • - +
  • + {!isOwner && authorId && ( +
  • + +
  • + )}