Refactor: 코멘트 api 리팩토링 - S11P21S002-258
This commit is contained in:
parent
4eed4889b3
commit
3fa8abfb34
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { Group, Rect, Text, Image } from 'react-konva';
|
import { Group, Rect, Text, Image } from 'react-konva';
|
||||||
import { CommentResponse } from '@/types';
|
import { CommentResponse } from '@/types';
|
||||||
import useImage from 'use-image';
|
import useImage from 'use-image';
|
||||||
@ -9,13 +9,22 @@ import toggleDownIconSrc from '@/assets/icons/chevron-down.svg';
|
|||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
|
||||||
interface CommentLabelProps {
|
interface CommentLabelProps {
|
||||||
|
stage: Konva.Stage;
|
||||||
comment: CommentResponse & { isOpen?: boolean };
|
comment: CommentResponse & { isOpen?: boolean };
|
||||||
updateComment: (comment: CommentResponse) => void;
|
updateComment: (comment: CommentResponse) => void;
|
||||||
deleteComment: (commentId: number) => void;
|
deleteComment: (commentId: number) => void;
|
||||||
toggleComment: (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 [content, setContent] = useState(comment.content);
|
||||||
const [deleteIcon] = useImage(deleteIconSrc);
|
const [deleteIcon] = useImage(deleteIconSrc);
|
||||||
const [toggleUpIcon] = useImage(toggleUpIconSrc);
|
const [toggleUpIcon] = useImage(toggleUpIconSrc);
|
||||||
@ -31,7 +40,7 @@ export default function CommentLabel({ comment, updateComment, deleteComment, to
|
|||||||
|
|
||||||
const handleDelete = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
const handleDelete = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
||||||
e.cancelBubble = true;
|
e.cancelBubble = true;
|
||||||
deleteComment(comment.id);
|
confirm('정말 삭제하시겠습니까?') && deleteComment(comment.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggle = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
const handleToggle = (e: Konva.KonvaEventObject<MouseEvent>) => {
|
||||||
@ -40,7 +49,9 @@ export default function CommentLabel({ comment, updateComment, deleteComment, to
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
stage && (
|
||||||
<Group
|
<Group
|
||||||
|
ref={groupRef}
|
||||||
x={comment.positionX}
|
x={comment.positionX}
|
||||||
y={comment.positionY}
|
y={comment.positionY}
|
||||||
draggable
|
draggable
|
||||||
@ -49,57 +60,47 @@ export default function CommentLabel({ comment, updateComment, deleteComment, to
|
|||||||
const newY = e.target.y();
|
const newY = e.target.y();
|
||||||
updateComment({ ...comment, positionX: newX, positionY: newY });
|
updateComment({ ...comment, positionX: newX, positionY: newY });
|
||||||
}}
|
}}
|
||||||
|
strokeScaleEnabled={false}
|
||||||
|
scale={{ x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y }}
|
||||||
|
perfectDrawEnabled={false}
|
||||||
|
shadowForStrokeEnabled={false}
|
||||||
>
|
>
|
||||||
<Rect
|
<Rect
|
||||||
width={comment.isOpen ? 200 : 50}
|
width={comment.isOpen ? 200 : 60}
|
||||||
height={comment.isOpen ? 100 : 30}
|
height={comment.isOpen ? 100 : 30}
|
||||||
fill="white"
|
fill="white"
|
||||||
stroke="black"
|
stroke="#080808"
|
||||||
onClick={handleEdit}
|
strokeWidth={1}
|
||||||
|
cornerRadius={5}
|
||||||
/>
|
/>
|
||||||
{comment.isOpen && (
|
{comment.isOpen && (
|
||||||
<Text
|
<Text
|
||||||
x={5}
|
x={10}
|
||||||
y={5}
|
y={35}
|
||||||
width={190}
|
width={190}
|
||||||
text={content || '내용 없음'}
|
text={content || '내용 없음'}
|
||||||
fontSize={16}
|
fontSize={16}
|
||||||
fill="black"
|
fill="#080808"
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{deleteIcon && (
|
<Image
|
||||||
|
image={comment.isOpen ? toggleUpIcon : toggleDownIcon}
|
||||||
|
x={5}
|
||||||
|
y={5}
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
onClick={handleToggle}
|
||||||
|
/>
|
||||||
<Image
|
<Image
|
||||||
image={deleteIcon}
|
image={deleteIcon}
|
||||||
x={comment.isOpen ? 175 : 25}
|
x={35}
|
||||||
y={5}
|
y={5}
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
onClick={handleDelete}
|
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>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { TRANSFORM_CHANGE_STR } from '@/constants';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { Vector2d } from 'konva/lib/types';
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
@ -10,19 +11,6 @@ interface PolygonTransformerProps {
|
|||||||
dragLayer: Konva.Layer;
|
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) {
|
export default function PolygonTransformer({ coordinates, setCoordinates, stage, dragLayer }: PolygonTransformerProps) {
|
||||||
const anchorsRef = useRef<Konva.Group>(null);
|
const anchorsRef = useRef<Konva.Group>(null);
|
||||||
const scale: Vector2d = { x: 1 / stage.getAbsoluteScale().x, y: 1 / stage.getAbsoluteScale().y };
|
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}
|
shadowForStrokeEnabled={false}
|
||||||
listening={false}
|
listening={false}
|
||||||
/>
|
/>
|
||||||
<Group ref={anchorsRef}>
|
<Group
|
||||||
|
ref={anchorsRef}
|
||||||
|
// scale={scale}
|
||||||
|
>
|
||||||
{coordinates.map((point, index) => {
|
{coordinates.map((point, index) => {
|
||||||
return (
|
return (
|
||||||
<Circle
|
<Circle
|
||||||
|
@ -19,7 +19,6 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||||||
import useSaveImageLabelsQuery from '@/queries/projects/useSaveImageLabelsQuery';
|
import useSaveImageLabelsQuery from '@/queries/projects/useSaveImageLabelsQuery';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import CommentLabel from './CommentLabel';
|
import CommentLabel from './CommentLabel';
|
||||||
import useAuthStore from '@/stores/useAuthStore';
|
|
||||||
|
|
||||||
export default function ImageCanvas() {
|
export default function ImageCanvas() {
|
||||||
const { project, folderId, categories } = useProjectStore();
|
const { project, folderId, categories } = useProjectStore();
|
||||||
@ -40,7 +39,6 @@ export default function ImageCanvas() {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { profile } = useAuthStore();
|
|
||||||
const { comments, setComments } = useCommentStore();
|
const { comments, setComments } = useCommentStore();
|
||||||
const { data: commentList } = useCommentListQuery(project!.id, imageId);
|
const { data: commentList } = useCommentListQuery(project!.id, imageId);
|
||||||
const createCommentMutation = useCreateCommentQuery(project!.id, imageId);
|
const createCommentMutation = useCreateCommentQuery(project!.id, imageId);
|
||||||
@ -208,6 +206,16 @@ export default function ImageCanvas() {
|
|||||||
setDrawState('pointer');
|
setDrawState('pointer');
|
||||||
setSelectedLabelId(id);
|
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>) => {
|
const handleClick = (e: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
|
||||||
e.evt.preventDefault();
|
e.evt.preventDefault();
|
||||||
@ -216,37 +224,7 @@ export default function ImageCanvas() {
|
|||||||
const isRightClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 2;
|
const isRightClicked = e.evt.type === 'mousedown' && (e.evt as MouseEvent).button === 2;
|
||||||
|
|
||||||
if (drawState === 'comment' && isLeftClicked) {
|
if (drawState === 'comment' && isLeftClicked) {
|
||||||
const stage = stageRef.current;
|
addComment();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drawState !== 'pointer' && (isLeftClicked || isRightClicked)) {
|
if (drawState !== 'pointer' && (isLeftClicked || isRightClicked)) {
|
||||||
@ -436,6 +414,7 @@ export default function ImageCanvas() {
|
|||||||
{comments.map((comment) => (
|
{comments.map((comment) => (
|
||||||
<CommentLabel
|
<CommentLabel
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
|
stage={stageRef.current as Konva.Stage}
|
||||||
comment={comment}
|
comment={comment}
|
||||||
updateComment={(updatedComment) => {
|
updateComment={(updatedComment) => {
|
||||||
updateCommentMutation.mutate({
|
updateCommentMutation.mutate({
|
||||||
|
@ -80,3 +80,16 @@ export const LABEL_CATEGORY = [
|
|||||||
'hair drier',
|
'hair drier',
|
||||||
'toothbrush',
|
'toothbrush',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const TRANSFORM_CHANGE_STR = [
|
||||||
|
'widthChange',
|
||||||
|
'heightChange',
|
||||||
|
'scaleXChange',
|
||||||
|
'skewXChange',
|
||||||
|
'skewYChange',
|
||||||
|
'rotationChange',
|
||||||
|
'offsetXChange',
|
||||||
|
'offsetYChange',
|
||||||
|
'transformsEnabledChange',
|
||||||
|
'strokeWidthChange',
|
||||||
|
];
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { createComment } from '@/api/commentAPi';
|
import { createComment } from '@/api/commentAPi';
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { CommentResponse } from '@/types';
|
import { CommentRequest } from '@/types';
|
||||||
|
|
||||||
export default function useCreateCommentQuery(projectId: number, imageId: number) {
|
export default function useCreateCommentQuery(projectId: number, imageId: number) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (commentData: CommentResponse) => createComment(projectId, imageId, commentData),
|
mutationFn: (commentData: CommentRequest) => createComment(projectId, imageId, commentData),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['commentList', projectId, imageId] });
|
queryClient.invalidateQueries({ queryKey: ['commentList', projectId, imageId] });
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { updateComment } from '@/api/commentAPi';
|
import { updateComment } from '@/api/commentAPi';
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { CommentRequest } from '@/types';
|
import { CommentRequest } from '@/types';
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
export default function useUpdateCommentQuery(projectId: number) {
|
export default function useUpdateCommentQuery(projectId: number) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { MemberResponse } from './memberTypes';
|
import { MemberResponse } from './memberTypes';
|
||||||
|
|
||||||
// 댓글 관련 DTO
|
|
||||||
export interface CommentRequest {
|
export interface CommentRequest {
|
||||||
content: string;
|
content: string;
|
||||||
positionX: number;
|
positionX: number;
|
||||||
@ -9,16 +8,9 @@ export interface CommentRequest {
|
|||||||
|
|
||||||
export interface CommentResponse {
|
export interface CommentResponse {
|
||||||
id: number;
|
id: number;
|
||||||
memberId: number;
|
|
||||||
memberNickname: string;
|
|
||||||
memberProfileImage: string;
|
|
||||||
positionX: number;
|
positionX: number;
|
||||||
positionY: number;
|
positionY: number;
|
||||||
content: string;
|
content: string;
|
||||||
createTime: string; // 작성 일자 (ISO 8601 형식)
|
createTime: string; // 작성 일자 (ISO 8601 형식)
|
||||||
author: MemberResponse; // 추가됨
|
author: MemberResponse; // 추가됨
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommentListResponse {
|
|
||||||
commentResponses: CommentResponse[];
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user