diff --git a/frontend/src/components/ImageCanvas/CommentLabel.tsx b/frontend/src/components/ImageCanvas/CommentLabel.tsx index 03e29d6..32b544b 100644 --- a/frontend/src/components/ImageCanvas/CommentLabel.tsx +++ b/frontend/src/components/ImageCanvas/CommentLabel.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { Group, Rect, Text, Image } from 'react-konva'; import { CommentResponse } from '@/types'; import useImage from 'use-image'; @@ -9,13 +9,22 @@ import toggleDownIconSrc from '@/assets/icons/chevron-down.svg'; import Konva from 'konva'; interface CommentLabelProps { + stage: Konva.Stage; comment: CommentResponse & { isOpen?: boolean }; updateComment: (comment: CommentResponse) => void; deleteComment: (commentId: number) => void; toggleComment: (commentId: number) => void; } -export default function CommentLabel({ comment, updateComment, deleteComment, toggleComment }: CommentLabelProps) { +export default function CommentLabel({ + stage, + comment, + updateComment, + deleteComment, + toggleComment, +}: CommentLabelProps) { + const groupRef = useRef(null); + // const stage = groupRef.current?.getStage(); const [content, setContent] = useState(comment.content); const [deleteIcon] = useImage(deleteIconSrc); const [toggleUpIcon] = useImage(toggleUpIconSrc); @@ -31,7 +40,7 @@ export default function CommentLabel({ comment, updateComment, deleteComment, to const handleDelete = (e: Konva.KonvaEventObject) => { e.cancelBubble = true; - deleteComment(comment.id); + confirm('정말 삭제하시겠습니까?') && deleteComment(comment.id); }; const handleToggle = (e: Konva.KonvaEventObject) => { @@ -40,66 +49,58 @@ export default function CommentLabel({ comment, updateComment, deleteComment, to }; return ( - { - const newX = e.target.x(); - const newY = e.target.y(); - updateComment({ ...comment, positionX: newX, positionY: newY }); - }} - > - - {comment.isOpen && ( - { + const newX = e.target.x(); + const newY = e.target.y(); + updateComment({ ...comment, positionX: newX, positionY: newY }); + }} + strokeScaleEnabled={false} + scale={{ x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y }} + perfectDrawEnabled={false} + shadowForStrokeEnabled={false} + > + + {comment.isOpen && ( + + )} + - )} - {deleteIcon && ( - )} - - {comment.isOpen - ? toggleUpIcon && ( - - ) - : toggleDownIcon && ( - - )} - + + ) ); } diff --git a/frontend/src/components/ImageCanvas/PolygonTransformer.tsx b/frontend/src/components/ImageCanvas/PolygonTransformer.tsx index 9b34b01..d3a53a6 100644 --- a/frontend/src/components/ImageCanvas/PolygonTransformer.tsx +++ b/frontend/src/components/ImageCanvas/PolygonTransformer.tsx @@ -1,3 +1,4 @@ +import { TRANSFORM_CHANGE_STR } from '@/constants'; import Konva from 'konva'; import { Vector2d } from 'konva/lib/types'; import { useEffect, useRef } from 'react'; @@ -10,19 +11,6 @@ interface PolygonTransformerProps { dragLayer: Konva.Layer; } -const TRANSFORM_CHANGE_STR = [ - 'widthChange', - 'heightChange', - 'scaleXChange', - 'skewXChange', - 'skewYChange', - 'rotationChange', - 'offsetXChange', - 'offsetYChange', - 'transformsEnabledChange', - 'strokeWidthChange', -]; - export default function PolygonTransformer({ coordinates, setCoordinates, stage, dragLayer }: PolygonTransformerProps) { const anchorsRef = useRef(null); const scale: Vector2d = { x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y }; @@ -99,7 +87,10 @@ export default function PolygonTransformer({ coordinates, setCoordinates, stage, shadowForStrokeEnabled={false} listening={false} /> - + {coordinates.map((point, index) => { return ( { + const { x, y } = stageRef.current!.getRelativePointerPosition()!; + + createCommentMutation.mutate({ + content: '', + positionX: x, + positionY: y, + }); + setDrawState('pointer'); + }; const handleClick = (e: Konva.KonvaEventObject) => { e.evt.preventDefault(); @@ -216,37 +224,7 @@ export default function ImageCanvas() { const isRightClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 2; if (drawState === 'comment' && isLeftClicked) { - const stage = stageRef.current; - if (!stage) return; - - const pointerPosition = stage.getRelativePointerPosition(); - if (!pointerPosition) return; - - if (!profile) { - console.error('User profile is not available'); - return; - } - - const x = pointerPosition.x; - const y = pointerPosition.y; - - createCommentMutation.mutate({ - id: 0, - content: '', - positionX: x, - positionY: y, - memberId: profile.id, - memberNickname: profile.nickname, - memberProfileImage: profile.profileImage, - createTime: new Date().toISOString(), - author: { - id: profile.id, - nickname: profile.nickname, - profileImage: profile.profileImage, - email: profile.email, - }, - }); - return; + addComment(); } if (drawState !== 'pointer' && (isLeftClicked || isRightClicked)) { @@ -436,6 +414,7 @@ export default function ImageCanvas() { {comments.map((comment) => ( { updateCommentMutation.mutate({ diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts index 0c297f4..4f3b1cd 100644 --- a/frontend/src/constants.ts +++ b/frontend/src/constants.ts @@ -80,3 +80,16 @@ export const LABEL_CATEGORY = [ 'hair drier', 'toothbrush', ]; + +export const TRANSFORM_CHANGE_STR = [ + 'widthChange', + 'heightChange', + 'scaleXChange', + 'skewXChange', + 'skewYChange', + 'rotationChange', + 'offsetXChange', + 'offsetYChange', + 'transformsEnabledChange', + 'strokeWidthChange', +]; diff --git a/frontend/src/queries/comments/useCreateCommentQuery.ts b/frontend/src/queries/comments/useCreateCommentQuery.ts index f0d7153..9ff3910 100644 --- a/frontend/src/queries/comments/useCreateCommentQuery.ts +++ b/frontend/src/queries/comments/useCreateCommentQuery.ts @@ -1,12 +1,12 @@ import { createComment } from '@/api/commentAPi'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { CommentResponse } from '@/types'; +import { CommentRequest } from '@/types'; export default function useCreateCommentQuery(projectId: number, imageId: number) { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (commentData: CommentResponse) => createComment(projectId, imageId, commentData), + mutationFn: (commentData: CommentRequest) => createComment(projectId, imageId, commentData), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['commentList', projectId, imageId] }); }, diff --git a/frontend/src/queries/comments/useUpdateCommentQuery.ts b/frontend/src/queries/comments/useUpdateCommentQuery.ts index 185d279..147e2ed 100644 --- a/frontend/src/queries/comments/useUpdateCommentQuery.ts +++ b/frontend/src/queries/comments/useUpdateCommentQuery.ts @@ -1,6 +1,6 @@ import { updateComment } from '@/api/commentAPi'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; import { CommentRequest } from '@/types'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; export default function useUpdateCommentQuery(projectId: number) { const queryClient = useQueryClient(); diff --git a/frontend/src/types/commentTypes.ts b/frontend/src/types/commentTypes.ts index cf2a1c5..a952e0b 100644 --- a/frontend/src/types/commentTypes.ts +++ b/frontend/src/types/commentTypes.ts @@ -1,6 +1,5 @@ import { MemberResponse } from './memberTypes'; -// 댓글 관련 DTO export interface CommentRequest { content: string; positionX: number; @@ -9,16 +8,9 @@ export interface CommentRequest { export interface CommentResponse { id: number; - memberId: number; - memberNickname: string; - memberProfileImage: string; positionX: number; positionY: number; content: string; createTime: string; // 작성 일자 (ISO 8601 형식) author: MemberResponse; // 추가됨 } - -export interface CommentListResponse { - commentResponses: CommentResponse[]; -}