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
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {

import { StyledText } from '@components/Text/StyledText';

import { CommentItemProps } from './dto';
import type { CommentItemProps } from './dto';

import More from '@assets/default/more.svg';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { MenuButtonListProps } from './dto';
import type { MenuButtonListProps } from './dto';
import { MenuListWrapper, MenuListContainer, MenuButtonItem } from './styles';
import { StyledText } from '@components/Text/StyledText';

Expand Down
142 changes: 71 additions & 71 deletions src/pages/Post/PostBase/LikeCommentBottomSheetContent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import { StyledText } from '@components/Text/StyledText';
import theme from '@styles/theme';
import Loading from '@components/Loading';
import Modal from '@components/Modal';
import CommentItem from './CommentItem';
import MenuButtonList from './MenuButtonList';
import CommentItem from './CommentItem/index';
import MenuButtonList from './MenuButtonList/index';

import { LikeCommentBottomSheetProps } from '../dto';
import { ModalProps } from '@components/Modal/dto';
import { GetPostLikeListResponse } from '@apis/post-like/dto';
import { Comment, GetCommentListResponse } from '@apis/post-comment/dto';
import type { LikeCommentBottomSheetProps } from '../dto';
import type { ModalProps } from '@components/Modal/dto';
import type { GetPostLikeListResponse } from '@apis/post-like/dto';
import type { Comment, GetCommentListResponse } from '@apis/post-comment/dto';

import Delete from '@assets/default/delete.svg';
import Block from '@assets/default/block.svg';
Expand All @@ -36,15 +36,11 @@ import { getCurrentUserId } from '@utils/getCurrentUserId';

const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({ tab, likeCount, commentCount }) => {
const [activeTab, setActiveTab] = useState<'likes' | 'comments'>(tab);
const { postId } = useParams<{ postId: string }>();

const [likes, setLikes] = useState<GetPostLikeListResponse['data']['likes']>([]);
const [postLikeCount, setPostLikeCount] = useState(likeCount);
const [comments, setComments] = useState<GetCommentListResponse['data']['comments']>([]);
const [postCommentCount, setPostCommentCount] = useState(commentCount);
const [isBlockConfirmationModalOpen, setIsBlockConfirmationModalOpen] = useState(false);
const [isStatusModalOpen, setIsStatusModalOpen] = useState(false);
const [modalContent, setModalContent] = useState('알 수 없는 오류입니다.\n관리자에게 문의해 주세요.');

const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(1);
Expand All @@ -60,50 +56,43 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
const [isMenuVisible, setIsMenuVisible] = useState(false);
const [menuPosition, setMenuPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 });

const [isBlockConfirmationModalOpen, setIsBlockConfirmationModalOpen] = useState(false);
const [isStatusModalOpen, setIsStatusModalOpen] = useState(false);
const [isCommentDeleteConfirmationModalOpen, setIsCommentDeleteConfirmationModalOpen] = useRecoilState(
IsCommentDeleteConfirmationModalOpenAtom,
);
const [, setIsCommentReportModalOpen] = useRecoilState(IsCommentReportModalOpenAtom);
const [modalContent, setModalContent] = useState('알 수 없는 오류입니다.\n관리자에게 문의해 주세요.');

const { postId } = useParams<{ postId: string }>();
const nav = useNavigate();

useEffect(() => {
setPage(1);
setReachedEnd(false);
setLikes([]);
setComments([]);

if (activeTab === 'likes') {
getPostLikeList(1);
} else if (activeTab === 'comments') {
getPostCommentList();
}
}, [activeTab]);

// IntersectionObserver를 활용하여 무한 스크롤 감지
useEffect(() => {
if (observerRef.current) observerRef.current.disconnect();

const handleIntersection = (entries: IntersectionObserverEntry[]) => {
const [entry] = entries;
if (entry.isIntersecting && !isLoading) {
console.log('호출');
getPostLikeList(page);
}
};
// 댓글 메뉴 클릭한 경우
const handleMenuOpen = (comment: Comment, event: React.MouseEvent<HTMLButtonElement>) => {
setSelectedComment(comment);
const rect = event.currentTarget.getBoundingClientRect();
setMenuPosition({ top: rect.bottom + window.scrollY - 90, left: rect.left + window.scrollX - 100 });
setIsMenuVisible(true);
};

observerRef.current = new IntersectionObserver(handleIntersection, {
root: null,
rootMargin: '0px',
threshold: 1.0,
});
// 유저 클릭한 경우
const handleUserClick = (userId: number) => {
// 로컬 스토리지에서 사용자 ID 가져오기
const myUserId = getCurrentUserId(); // 로컬 스토리지에 저장된 사용자 ID를 가져옴

if (loadMoreRef.current) observerRef.current.observe(loadMoreRef.current);
if (String(myUserId) === String(userId)) {
// 나인 경우
nav(`/profile/${userId}`);
} else {
// 다른 유저인 경우
nav(`/users/${userId}`);
}
};

return () => {
if (observerRef.current) observerRef.current.disconnect();
};
}, [page, reachedEnd, loadMoreRef.current, activeTab]);
// 댓글 작성 Input
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
setInputValue(e.target.value);
}, []);

// 좋아요 리스트 불러오기 api
const getPostLikeList = async (currentPage: number) => {
Expand Down Expand Up @@ -156,11 +145,6 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
}
};

// 댓글 작성 Input
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
setInputValue(e.target.value);
}, []);

// 댓글 작성 api
const createComment = async () => {
if (isSubmitting) return; // 중복 요청 방지
Expand Down Expand Up @@ -233,6 +217,44 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
}
};

useEffect(() => {
setPage(1);
setReachedEnd(false);
setLikes([]);
setComments([]);

if (activeTab === 'likes') {
getPostLikeList(1);
} else if (activeTab === 'comments') {
getPostCommentList();
}
}, [activeTab]);

// IntersectionObserver를 활용하여 무한 스크롤 감지
useEffect(() => {
if (observerRef.current) observerRef.current.disconnect();

const handleIntersection = (entries: IntersectionObserverEntry[]) => {
const [entry] = entries;
if (entry.isIntersecting && !isLoading) {
console.log('호출');
getPostLikeList(page);
}
};

observerRef.current = new IntersectionObserver(handleIntersection, {
root: null,
rootMargin: '0px',
threshold: 1.0,
});

if (loadMoreRef.current) observerRef.current.observe(loadMoreRef.current);

return () => {
if (observerRef.current) observerRef.current.disconnect();
};
}, [page, reachedEnd, loadMoreRef.current, activeTab]);

// 본인 댓글 메뉴 항목
const MyCommentMenuItems = [
{
Expand Down Expand Up @@ -307,28 +329,6 @@ const LikeCommentBottomSheetContent: React.FC<LikeCommentBottomSheetProps> = ({
},
};

// 댓글 메뉴 클릭한 경우
const handleMenuOpen = (comment: Comment, event: React.MouseEvent<HTMLButtonElement>) => {
setSelectedComment(comment);
const rect = event.currentTarget.getBoundingClientRect();
setMenuPosition({ top: rect.bottom + window.scrollY - 90, left: rect.left + window.scrollX - 100 });
setIsMenuVisible(true);
};

// 유저 클릭한 경우
const handleUserClick = (userId: number) => {
// 로컬 스토리지에서 사용자 ID 가져오기
const myUserId = getCurrentUserId(); // 로컬 스토리지에 저장된 사용자 ID를 가져옴

if (String(myUserId) === String(userId)) {
// 나인 경우
nav(`/profile/${userId}`);
} else {
// 다른 유저인 경우
nav(`/users/${userId}`);
}
};

return (
<>
<TabContainer>
Expand Down
115 changes: 58 additions & 57 deletions src/pages/Post/PostBase/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import TopBar from '@components/TopBar';
import NavBar from '@components/NavBar';
import BottomSheet from '@components/BottomSheet';
import ClothingInfoItem from '@components/ClothingInfoItem';
import ImageSwiper from './ImageSwiper';
import LikeCommentBottomSheetContent from './LikeCommentBottomSheetContent';
import ImageSwiper from './ImageSwiper/index';
import LikeCommentBottomSheetContent from './LikeCommentBottomSheetContent/index';

import {
PostLayout,
Expand All @@ -36,22 +36,21 @@ import {
ClothingInfoList,
} from './styles';

import Left from '../../../assets/arrow/left.svg';
import Like from '../../../assets/default/like.svg';
import LikeFill from '../../../assets/default/like-fill.svg';
import Message from '../../../assets/default/message.svg';
import More from '../../../assets/default/more.svg';
import Left from '@assets/arrow/left.svg';
import Like from '@assets/default/like.svg';
import LikeFill from '@assets/default/like-fill.svg';
import Message from '@assets/default/message.svg';
import More from '@assets/default/more.svg';

import { BottomSheetProps } from '@components/BottomSheet/dto';
import { PostBaseProps } from './dto';
import { GetPostDetailResponse } from '@apis/post/dto';
import type { BottomSheetProps } from '@components/BottomSheet/dto';
import type { PostBaseProps } from './dto';
import type { GetPostDetailResponse } from '@apis/post/dto';

import { getPostDetailApi } from '@apis/post';
import { togglePostLikeStatusApi } from '@apis/post-like';
import { getCurrentUserId } from '@utils/getCurrentUserId';

const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
const { postId } = useParams<{ postId: string }>();
const [, setPostId] = useRecoilState(postIdAtom);
const [post, setPost] = useState<GetPostDetailResponse['data']>();
const [user, setUser] = useRecoilState(userAtom);
Expand All @@ -62,9 +61,56 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
const [isLikeCommentBottomSheetOpen, setIsLikeCommentBottomSheetOpen] = useState(false);
const [activeTab, setActiveTab] = useState<'likes' | 'comments'>('likes'); // activeTab state

const nav = useNavigate();
const { postId } = useParams<{ postId: string }>();
const userId = getCurrentUserId();

const nav = useNavigate();

const handleLikeCommentOpen = (tab: 'likes' | 'comments') => {
setActiveTab(tab); // 클릭한 버튼에 따라 activeTab 설정
setIsLikeCommentBottomSheetOpen(true);
};

const handleUserClick = () => {
if (post?.isPostWriter) {
// 내 게시물인 경우
nav(`/profile/${userId}`);
} else {
// 다른 유저의 게시물인 경우
nav(`/users/${post?.user.id}`);
}
};

const contentRef = useRef<HTMLDivElement>(null);

const toggleTextDisplay = () => {
setShowFullText((prev) => !prev);
};

// 게시글 좋아요 누르기/취소하기 api
const togglePostLikeStatus = async () => {
if (!post || !postId) return;

const prevPost = { ...post }; // 현재 상태 저장
setPost({
...post,
isPostLike: !post.isPostLike,
postLikesCount: post.isPostLike ? post.postLikesCount - 1 : post.postLikesCount + 1,
}); //사용자가 좋아요를 누르면 먼저 클라이언트에서 post 상태를 변경(낙관적 업데이트)

try {
const response = await togglePostLikeStatusApi(Number(postId));
setPost({
...post,
isPostLike: response.data.isPostLike,
postLikesCount: response.data.postLikesCount,
}); // 서버로 요청 후 성공하면 그대로 유지
} catch (error) {
console.error('Error toggling like status:', error);
setPost(prevPost); // 실패하면 원래 상태로 롤백
}
};

useEffect(() => {
setPostId(Number(postId));

Expand All @@ -85,8 +131,6 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
getPost();
}, [postId]);

const contentRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (contentRef.current) {
// 실제 높이와 줄 제한 높이 비교
Expand All @@ -95,25 +139,6 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
}
}, [post?.content]);

const toggleTextDisplay = () => {
setShowFullText((prev) => !prev);
};

const handleUserClick = () => {
if (post?.isPostWriter) {
// 내 게시물인 경우
nav(`/profile/${userId}`);
} else {
// 다른 유저의 게시물인 경우
nav(`/users/${post?.user.id}`);
}
};

const handleLikeCommentOpen = (tab: 'likes' | 'comments') => {
setActiveTab(tab); // 클릭한 버튼에 따라 activeTab 설정
setIsLikeCommentBottomSheetOpen(true);
};

const likeCommentbottomSheetProps: BottomSheetProps = {
isOpenBottomSheet: isLikeCommentBottomSheetOpen,
isHandlerVisible: true,
Expand All @@ -128,30 +153,6 @@ const PostBase: React.FC<PostBaseProps> = ({ onClickMenu }) => {
},
};

// 게시글 좋아요 누르기/취소하기
const togglePostLikeStatus = async () => {
if (!post || !postId) return;

const prevPost = { ...post }; // 현재 상태 저장
setPost({
...post,
isPostLike: !post.isPostLike,
postLikesCount: post.isPostLike ? post.postLikesCount - 1 : post.postLikesCount + 1,
}); //사용자가 좋아요를 누르면 먼저 클라이언트에서 post 상태를 변경(낙관적 업데이트)

try {
const response = await togglePostLikeStatusApi(Number(postId));
setPost({
...post,
isPostLike: response.data.isPostLike,
postLikesCount: response.data.postLikesCount,
}); // 서버로 요청 후 성공하면 그대로 유지
} catch (error) {
console.error('Error toggling like status:', error);
setPost(prevPost); // 실패하면 원래 상태로 롤백
}
};

return (
<OODDFrame>
<TopBar LeftButtonSrc={Left} />
Expand Down
Loading
Loading