diff --git a/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx b/apps/web/src/app/community/[boardCode]/CommunityPageContent.tsx index 0474b524..9d741f93 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; } @@ -28,6 +21,7 @@ const CommunityPageContent = ({ boardCode }: CommunityPageContentProps) => { const [category, setCategory] = useState(COMMUNITY_INITIAL_CATEGORY); const reportedPostIds = useReportedPostsStore((state) => state.reportedPostIds); const blockedUserIds = useReportedPostsStore((state) => state.blockedUserIds); + const blockedPostIds = useReportedPostsStore((state) => state.blockedPostIds); const { data: posts = [], isPending } = useGetPostList({ boardCode, @@ -35,19 +29,20 @@ const CommunityPageContent = ({ boardCode }: CommunityPageContentProps) => { }); const visiblePosts = useMemo(() => { - if (reportedPostIds.length === 0 && blockedUserIds.length === 0) { + if (reportedPostIds.length === 0 && blockedUserIds.length === 0 && blockedPostIds.length === 0) { return posts; } const reportedIdSet = new Set(reportedPostIds); const blockedUserIdSet = new Set(blockedUserIds); + const blockedPostIdSet = new Set(blockedPostIds); return posts.filter((post) => { - if (reportedIdSet.has(post.id)) { + if (reportedIdSet.has(post.id) || blockedPostIdSet.has(post.id)) { return false; } - const authorId = (post as ListPostWithAuthor).postFindSiteUserResponse?.id; + const authorId = post.postFindSiteUserResponse?.id; if (typeof authorId === "number" && blockedUserIdSet.has(authorId)) { return false; @@ -55,7 +50,7 @@ const CommunityPageContent = ({ boardCode }: CommunityPageContentProps) => { return true; }); - }, [posts, reportedPostIds, blockedUserIds]); + }, [posts, reportedPostIds, blockedUserIds, blockedPostIds]); const handleBoardChange = (newBoard: string) => { router.push(`/community/${newBoard}`); 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..88ae377a 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 && ( +
  • + +
  • + )}