Merge branch 'fe/refactor/258-comment-api' into 'fe/develop'

Refactor: 코멘트 api 리팩토링 - S11P21S002-258

See merge request s11-s-project/S11P21S002!245
This commit is contained in:
정현조 2024-09-30 14:20:52 +09:00
commit a7d5a54f7a
7 changed files with 88 additions and 112 deletions

View File

@ -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<Konva.Group>(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<MouseEvent>) => {
e.cancelBubble = true;
deleteComment(comment.id);
confirm('정말 삭제하시겠습니까?') && deleteComment(comment.id);
};
const handleToggle = (e: Konva.KonvaEventObject<MouseEvent>) => {
@ -40,66 +49,58 @@ export default function CommentLabel({ comment, updateComment, deleteComment, to
};
return (
<Group
x={comment.positionX}
y={comment.positionY}
draggable
onDragEnd={(e) => {
const newX = e.target.x();
const newY = e.target.y();
updateComment({ ...comment, positionX: newX, positionY: newY });
}}
>
<Rect
width={comment.isOpen ? 200 : 50}
height={comment.isOpen ? 100 : 30}
fill="white"
stroke="black"
onClick={handleEdit}
/>
{comment.isOpen && (
<Text
stage && (
<Group
ref={groupRef}
x={comment.positionX}
y={comment.positionY}
draggable
onDragEnd={(e) => {
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}
>
<Rect
width={comment.isOpen ? 200 : 60}
height={comment.isOpen ? 100 : 30}
fill="white"
stroke="#080808"
strokeWidth={1}
cornerRadius={5}
/>
{comment.isOpen && (
<Text
x={10}
y={35}
width={190}
text={content || '내용 없음'}
fontSize={16}
fill="#080808"
onClick={handleEdit}
/>
)}
<Image
image={comment.isOpen ? toggleUpIcon : toggleDownIcon}
x={5}
y={5}
width={190}
text={content || '내용 없음'}
fontSize={16}
fill="black"
onClick={handleEdit}
width={20}
height={20}
onClick={handleToggle}
/>
)}
{deleteIcon && (
<Image
image={deleteIcon}
x={comment.isOpen ? 175 : 25}
x={35}
y={5}
width={20}
height={20}
onClick={handleDelete}
/>
)}
{comment.isOpen
? toggleUpIcon && (
<Image
image={toggleUpIcon}
x={comment.isOpen ? 150 : 0}
y={5}
width={20}
height={20}
onClick={handleToggle}
/>
)
: toggleDownIcon && (
<Image
image={toggleDownIcon}
x={comment.isOpen ? 150 : 0}
y={5}
width={20}
height={20}
onClick={handleToggle}
/>
)}
</Group>
</Group>
)
);
}

View File

@ -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<Konva.Group>(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}
/>
<Group ref={anchorsRef}>
<Group
ref={anchorsRef}
// scale={scale}
>
{coordinates.map((point, index) => {
return (
<Circle

View File

@ -19,7 +19,6 @@ import { useQueryClient } from '@tanstack/react-query';
import useSaveImageLabelsQuery from '@/queries/projects/useSaveImageLabelsQuery';
import { useToast } from '@/hooks/use-toast';
import CommentLabel from './CommentLabel';
import useAuthStore from '@/stores/useAuthStore';
export default function ImageCanvas() {
const { project, folderId, categories } = useProjectStore();
@ -40,7 +39,6 @@ export default function ImageCanvas() {
const queryClient = useQueryClient();
const { toast } = useToast();
const { profile } = useAuthStore();
const { comments, setComments } = useCommentStore();
const { data: commentList } = useCommentListQuery(project!.id, imageId);
const createCommentMutation = useCreateCommentQuery(project!.id, imageId);
@ -208,6 +206,16 @@ export default function ImageCanvas() {
setDrawState('pointer');
setSelectedLabelId(id);
};
const addComment = () => {
const { x, y } = stageRef.current!.getRelativePointerPosition()!;
createCommentMutation.mutate({
content: '',
positionX: x,
positionY: y,
});
setDrawState('pointer');
};
const handleClick = (e: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
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) => (
<CommentLabel
key={comment.id}
stage={stageRef.current as Konva.Stage}
comment={comment}
updateComment={(updatedComment) => {
updateCommentMutation.mutate({

View File

@ -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',
];

View File

@ -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] });
},

View File

@ -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();

View File

@ -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[];
}