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
2 changes: 1 addition & 1 deletion src/apis/post-comment/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface Comment {

// 댓글 관련 User 정보
export interface CommentUser {
userId: number;
id: number;
nickname: string;
profilePictureUrl: string;
}
Expand Down
1 change: 0 additions & 1 deletion src/components/ClothingInfoItem/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ export interface ClothingInfo extends PostClothing {}
export interface ClothingInfoItemProps {
clothingObj: ClothingInfo;
onDelete?: (clothingObj: ClothingInfo) => void;
hasRightMargin?: boolean;
}
4 changes: 2 additions & 2 deletions src/components/ClothingInfoItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import Right from '../../assets/arrow/right.svg';
import { ClothingInfoItemProps } from './dto';
import { ClothingInfoItemContainer, ClothingInfoLeft, ClothingImage, ClothingInfoRight, ClothingModel } from './styles';

const ClothingInfoItem: React.FC<ClothingInfoItemProps> = ({ clothingObj, onDelete, hasRightMargin = false }) => {
const ClothingInfoItem: React.FC<ClothingInfoItemProps> = ({ clothingObj, onDelete }) => {
const handleClick = () => {
if (clothingObj.url) {
window.location.href = clothingObj.url;
}
};

return (
<ClothingInfoItemContainer style={{ marginRight: hasRightMargin ? '0.75rem' : '0' }}>
<ClothingInfoItemContainer>
<ClothingInfoLeft onClick={handleClick}>
<ClothingImage>
<img src={clothingObj.imageUrl} alt="ClothingImg" />
Expand Down
27 changes: 10 additions & 17 deletions src/components/ClothingInfoItem/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,13 @@ export const ClothingInfoItemContainer = styled.li`
align-items: center;
border: 0.0625rem solid ${({ theme }) => theme.colors.pink2};
border-radius: 0.5rem;
padding: 0.5rem;
padding: 10px;
min-width: 20.9375rem;
margin-bottom: 0.9375rem;
box-shadow:
0px 1px 2px 0px rgba(0, 0, 0, 0.12),
0px 0px 1px 0px rgba(0, 0, 0, 0.08),
0px 0px 1px 0px rgba(0, 0, 0, 0.08);
cursor: pointer;

/* Post 안에 있을 때 첫 번째 아이템에만 margin-left 적용 */
.post-mode > & {
&:first-child {
margin-left: 1.25rem;
}
}
`;

export const ClothingInfoLeft = styled.div`
Expand All @@ -35,7 +27,7 @@ export const ClothingInfoLeft = styled.div`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 70%;
width: 75%;
display: flex;
flex-direction: column;
justify-content: center;
Expand All @@ -53,25 +45,26 @@ export const ClothingInfoLeft = styled.div`
`;

export const ClothingImage = styled.div`
width: 4.625rem;
height: 4.625rem;
width: 62px;
height: 62px;
border-radius: 0.5rem;
margin-right: 0.9375rem;
margin-right: 10px;

> img {
width: 4.625rem;
height: 4.625rem;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 0.5rem;
}
`;

export const ClothingModel = styled(StyledText)`
display: -webkit-box;
-webkit-line-clamp: 2; /* 두 줄로 제한 */
-webkit-box-orient: vertical;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 100%;
display: inline-block; /* 텍스트 클리핑을 적용하기 위해 inline-block으로 설정 */
`;

export const ClothingInfoRight = styled.div`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { Comment } from '../../../../apis/post-comment/dto';
export interface CommentItemProps {
comment: Comment;
handleUserClick: (userId: number) => void;
getPostCommentList: () => void;
handleMenuOpen: (comment: Comment, event: React.MouseEvent<HTMLButtonElement>) => void;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import React, { useEffect, useState } from 'react';

import { useRecoilState } from 'recoil';
import dayjs from 'dayjs';
import 'dayjs/locale/ko';

import { IsBlockConfirmationModalOpenAtom, UserBlockAtom } from '../../../../recoil/Home/BlockBottomSheetAtom';
import {
IsCommentDeleteConfirmationModalOpenAtom,
IsCommentReportModalOpenAtom,
} from '../../../../recoil/Post/PostCommentAtom';

import theme from '../../../../styles/theme';
import {
StyledBigUserProfile,
Expand All @@ -20,133 +13,25 @@ import {
} from './styles';

import { StyledText } from '../../../Text/StyledText';
import MenuButtonList from '../MenuButtonList';
import Modal from '../../../Modal';

import { ModalProps } from '../../../Modal/dto';
import { CommentItemProps } from './dto';

import More from '../../../../assets/default/more.svg';
import Delete from '../../../../assets/default/delete.svg';
import Block from '../../../../assets/default/block.svg';
import Report from '../../../../assets/default/report.svg';
import X from '../../../../assets/default/x.svg';

import { deleteCommentApi } from '../../../../apis/post-comment';

const CommentItem: React.FC<CommentItemProps> = ({ comment, handleUserClick, getPostCommentList }) => {
const [showCommentMenuId, setShowCommentMenuId] = useState<number | null>(null);
const [isCommentDeleteConfirmationModalOpen, setIsCommentDeleteConfirmationModalOpen] = useRecoilState(
IsCommentDeleteConfirmationModalOpenAtom,
);
const [, setIsCommentReportModalOpen] = useRecoilState(IsCommentReportModalOpenAtom);
const [, setUserBlockAtom] = useRecoilState(UserBlockAtom);
const [, setIsBlockConfirmationModalOpen] = useRecoilState(IsBlockConfirmationModalOpenAtom);
const CommentItem: React.FC<CommentItemProps> = ({ comment, handleUserClick, handleMenuOpen }) => {
const [timeAgo, setTimeAgo] = useState<string | null>();

//const [isMenuVisible, setIsMenuVisible] = useState(false);
const [, setIsMenuVisible] = useState(false);
//const [menuPosition, setMenuPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 });

/*
useEffect(() => {
// 메뉴 위치 초기화
setMenuPosition({ top: 0, left: 0 });
}, [isMenuVisible]);

const handleMenuOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
const rect = event.currentTarget.getBoundingClientRect();
setMenuPosition({ top: rect.bottom + window.scrollY, left: rect.left + window.scrollX });
setIsMenuVisible((prev) => !prev);
};
*/

useEffect(() => {
setTimeAgo(dayjs(comment.createdAt).locale('ko').fromNow());
}, [comment]);

// 댓글 삭제
const deleteComment = async () => {
try {
await deleteCommentApi(comment.id); // 댓글 삭제 API 호출
setIsCommentDeleteConfirmationModalOpen(false); // 모달 닫기
getPostCommentList(); // 댓글 목록 갱신
} catch (error) {
console.error('댓글 삭제 중 에러 발생:', error);
}
};

const deleteConfirmationModalProps: ModalProps = {
isCloseButtonVisible: true,
onClose: () => setIsCommentDeleteConfirmationModalOpen(false),
content: '정말 댓글을 삭제하시겠습니까?',
button: {
content: '삭제',
onClick: deleteComment,
},
};

// 댓글 메뉴 클릭한 경우
const handleCommentMenuClick = (commentId: number) => {
if (!commentId) return;
setShowCommentMenuId((prevId) => (prevId === commentId ? null : commentId));
};

const menuItems = [
...(comment.isCommentWriter
? [
{
text: '삭제',
action: () => {
setIsCommentDeleteConfirmationModalOpen(true);
},
icon: Delete,
color: 'red',
},
]
: [
{
text: '신고하기',
action: () => {
setIsCommentReportModalOpen(true);
},
icon: Report,
},
{
text: '차단하기',
action: () => {
const storedUserId = localStorage.getItem('id');
if (storedUserId) {
setUserBlockAtom({
userId: Number(storedUserId),
friendId: comment.user.userId,
friendName: comment.user.nickname,
action: 'toggle',
});
setIsBlockConfirmationModalOpen(true);
}
},
icon: Block,
},
]),
{
text: '취소',
action: () => setShowCommentMenuId(null),
icon: X,
},
];

return (
<StyledCommentItem key={comment.id}>
<StyledBigUserProfile>
<img
src={comment.user.profilePictureUrl}
onClick={() => handleUserClick(comment.user.userId)}
alt="user avatar"
/>
<img src={comment.user.profilePictureUrl} onClick={() => handleUserClick(comment.user.id)} alt="user avatar" />
</StyledBigUserProfile>
<CommentContent>
<StyledText onClick={() => handleUserClick(comment.user.userId)} $textTheme={{ style: 'body2-medium' }}>
<StyledText onClick={() => handleUserClick(comment.user.id)} $textTheme={{ style: 'body2-medium' }}>
{comment.user.nickname}
</StyledText>
<StyledText $textTheme={{ style: 'body2-regular' }}>{comment.content}</StyledText>
Expand All @@ -155,18 +40,10 @@ const CommentItem: React.FC<CommentItemProps> = ({ comment, handleUserClick, get
<StyledText className="timeAgo" $textTheme={{ style: 'caption2-regular' }} color={theme.colors.gray3}>
{timeAgo}
</StyledText>
<MenuBtn onClick={() => handleCommentMenuClick(comment.id)}>
<MenuBtn onClick={(event) => handleMenuOpen(comment, event)}>
<img src={More} alt="more" />
</MenuBtn>
</RightContainer>
{showCommentMenuId === comment.id && (
<MenuButtonList
items={menuItems}
isVisible={showCommentMenuId === comment.id}
onClose={() => setIsMenuVisible(false)}
/>
)}
{isCommentDeleteConfirmationModalOpen && <Modal {...deleteConfirmationModalProps} />}
</StyledCommentItem>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ export const CommentItem = styled.div`
`;

export const StyledBigUserProfile = styled(BigUserProfile)`
width: 52px;
height: 52px;
width: 36px;
height: 36px;
margin-bottom: auto;
`;

export const CommentContent = styled.div`
margin-left: 8px;
display: flex;
flex-direction: column;
max-width: calc(100% - 60px);
max-width: calc(100% - 44px);
`;

export const RightContainer = styled.div`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export interface MenuButtonListProps {
items: MenuButtonProps[];
onClose: () => void;
position: { top: number; left: number };
}

export interface MenuButtonProps {
text: string;
action: () => void;
icon: string;
color?: string;
}

export interface MenuButtonListProps {
items: MenuButtonProps[];
isVisible: boolean;
onClose: () => void;
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import React, { useEffect, useRef } from 'react';
import React from 'react';
import ReactDOM from 'react-dom';
import { MenuButtonListProps } from './dto';
import { MenuListWrapper, MenuListContainer, MenuButtonItem } from './styles';
import { StyledText } from '../../../Text/StyledText';

const MenuButtonList: React.FC<MenuButtonListProps> = ({ items, isVisible, onClose }) => {
const containerRef = useRef<HTMLDivElement>(null);
const MenuButtonList: React.FC<MenuButtonListProps> = ({ items, onClose, position }) => {
const handleWrapperClick = () => {
onClose(); // Wrapper 클릭 시 닫기
};

// 외부 클릭 감지
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
onClose();
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [onClose]);

if (!isVisible) return null;
const handleContainerClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation(); // Container 클릭 시 이벤트 중단
};

return ReactDOM.createPortal(
<MenuListWrapper>
<MenuListContainer ref={containerRef}>
<MenuListWrapper onClick={handleWrapperClick}>
<MenuListContainer $position={position} onClick={handleContainerClick}>
{items.map((item, index) => (
<MenuButtonItem key={index} onClick={item.action}>
<StyledText $textTheme={{ style: 'body4-regular' }} color={item.color}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ export const MenuListWrapper = styled.div`
width: 100%;
height: 100%;
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
`;

export const MenuListContainer = styled.div`
z-index: 1000;
export const MenuListContainer = styled.div<{ $position: { top: number; left: number } }>`
position: absolute;
right: 0;
top: 40;
top: ${({ $position }) => `${$position.top}px`};
left: ${({ $position }) => `${$position.left}px`};
z-index: 1000;
width: 120px;
display: flex;
flex-direction: column;
border-radius: 10px;
background-color: ${({ theme }) => theme.colors.gray1};
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
`;

export const MenuButtonItem = styled.button<{ $color?: string }>`
Expand All @@ -37,4 +40,8 @@ export const MenuButtonItem = styled.button<{ $color?: string }>`
width: 16px;
height: 16px;
}

&:last-child {
border-bottom: none;
}
`;
Loading