Skip to content

Commit 3e88941

Browse files
authored
[FEATURE] 우클릭 시 삭제 기능 구현 (#67)
* feat: context menu에서 delete API 연동 * refactor: 토픽에서 콘텐츠 제거했을 때 성공, 실패에 대한 후속 처리 훅에서 관리하도록 리팩토링
1 parent ef096f1 commit 3e88941

6 files changed

Lines changed: 38 additions & 34 deletions

File tree

apps/web/src/components/(with-side-bar)/topic/context-menu-provider.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
ContextMenuTrigger,
99
} from "@/components/common/context-menu";
1010
import { TopicContext } from "@/context/topic-context";
11-
import { useCreateContent } from "@/lib/tanstack/mutation/topic-content";
11+
import { useCreateContent, useRemoveTopicContentById } from "@/lib/tanstack/mutation/topic-content";
1212
import type { CategoryContentDTO } from "@linkyboard/types";
1313
import type { Node } from "@xyflow/react";
1414

@@ -33,11 +33,12 @@ export default function ContextMenuProvider({
3333
throw new Error("TopicContext not found");
3434
}
3535

36-
const { id, nodes, selectedNodeIds } = topicContext;
36+
const { id, nodes, selectedNodeIds, onResetSelectedNodeIds } = topicContext;
3737

3838
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
3939

4040
const { mutateAsync: createContent } = useCreateContent(id);
41+
const { mutateAsync: removeContent } = useRemoveTopicContentById(id, onResetSelectedNodeIds);
4142

4243
const onDuplicate = useCallback(() => {
4344
selectedNodeIds.forEach(async (nodeId) => {
@@ -69,10 +70,13 @@ export default function ContextMenuProvider({
6970
// 여기에 Summary 로직 추가
7071
}, []);
7172

72-
const onDelete = useCallback(() => {
73-
console.log("Delete 실행");
74-
// 여기에 Delete 로직 추가
75-
}, []);
73+
const onDelete = useCallback(async () => {
74+
const contentIds = selectedNodeIds.map((id) => Number(id.split("-")[1]));
75+
await removeContent({
76+
topicId: id,
77+
contentIds,
78+
});
79+
}, [id, removeContent, selectedNodeIds]);
7680

7781
const onCloseContextMenu = useCallback(() => {
7882
setIsContextMenuOpen(false);

apps/web/src/components/(with-side-bar)/topic/remove-content-button.tsx

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,28 @@
1-
import { TOPIC } from "@/constants/topic";
2-
import { invalidateQueries } from "@/lib/tanstack";
31
import { useRemoveTopicContentById } from "@/lib/tanstack/mutation/topic-content";
4-
import { Button, errorToast } from "@linkyboard/components";
2+
import { Button } from "@linkyboard/components";
53

64
import { Trash2 } from "lucide-react";
75

86
interface RemoveContentButtonProps {
97
topicId: string;
108
selectedNodeIds: string[];
11-
setSelectedNodeIds: React.Dispatch<React.SetStateAction<string[]>>;
9+
onResetSelectedNodeIds: () => void;
1210
}
1311

1412
export default function RemoveContentButton({
1513
topicId,
1614
selectedNodeIds,
17-
setSelectedNodeIds,
15+
onResetSelectedNodeIds,
1816
}: RemoveContentButtonProps) {
1917
const contentIds = selectedNodeIds.map((id) => Number(id.split("-")[1]));
2018

21-
const { mutateAsync, isPending } = useRemoveTopicContentById();
19+
const { mutateAsync, isPending } = useRemoveTopicContentById(topicId, onResetSelectedNodeIds);
2220

2321
const onRemoveContent = async () => {
24-
await mutateAsync(
25-
{
26-
topicId,
27-
contentIds,
28-
},
29-
{
30-
onSuccess: () => {
31-
invalidateQueries([TOPIC.GET_TOPIC_BOARD_BY_ID, topicId]);
32-
setSelectedNodeIds([]);
33-
},
34-
onError: () => {
35-
errorToast("토픽에서 콘텐츠를 제거하지 못했어요.");
36-
},
37-
}
38-
);
22+
await mutateAsync({
23+
topicId,
24+
contentIds,
25+
});
3926
};
4027

4128
return (

apps/web/src/components/(with-side-bar)/topic/summarize-dialog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { summarizeSchema, type SummarizeSchemaType } from "../../../schemas/summ
2626
interface SummarizeDialogProps {
2727
topicId: string;
2828
selectedNodeIds: string[];
29-
setSelectedNodeIds: React.Dispatch<React.SetStateAction<string[]>>;
29+
onResetSelectedNodeIds: () => void;
3030
}
3131

3232
const DEFAULT_VALUES = {
@@ -38,7 +38,7 @@ const DEFAULT_VALUES = {
3838
function SummarizeDialogContent({
3939
topicId,
4040
selectedNodeIds,
41-
setSelectedNodeIds,
41+
onResetSelectedNodeIds,
4242
}: SummarizeDialogProps) {
4343
const [isModelDropdownOpen, setIsModelDropdownOpen] = useState(false);
4444

@@ -91,7 +91,7 @@ function SummarizeDialogContent({
9191
invalidateQueries([TOPIC.GET_TOPIC_BOARD_BY_ID, topicId]);
9292
router.push(`/topic/${topicId}/sticker?stickerId=${data.result.id}`);
9393
reset();
94-
setSelectedNodeIds([]);
94+
onResetSelectedNodeIds();
9595
},
9696
onError: (error) => {
9797
console.error(error);

apps/web/src/context/topic-context.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface TopicContextProps {
1515
onConnect: (params: Connection) => void;
1616
onEdgeClick: (e: React.MouseEvent, edge: Edge) => void;
1717
onNodeSelect: (nodeId: string) => void;
18+
onResetSelectedNodeIds: () => void;
1819
}
1920

2021
export const TopicContext = createContext<TopicContextProps | null>(null);

apps/web/src/lib/tanstack/mutation/topic-content.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
updateContentPosition,
66
updateContentSize,
77
} from "@/services/topic-content";
8-
import { infoToast } from "@linkyboard/components";
8+
import { errorToast, infoToast } from "@linkyboard/components";
99
import { useMutation } from "@tanstack/react-query";
1010

1111
import { invalidateQueries } from "..";
@@ -43,8 +43,15 @@ export const useCreateContent = (id: string) => {
4343
});
4444
};
4545

46-
export const useRemoveTopicContentById = () => {
46+
export const useRemoveTopicContentById = (topicId: string, onResetSelectedNodeIds: () => void) => {
4747
return useMutation({
4848
mutationFn: removeTopicContentById,
49+
onSuccess: () => {
50+
invalidateQueries([TOPIC.GET_TOPIC_BOARD_BY_ID, topicId]);
51+
onResetSelectedNodeIds();
52+
},
53+
onError: () => {
54+
errorToast("토픽에서 콘텐츠를 제거하지 못했어요.");
55+
},
4956
});
5057
};

apps/web/src/page/topic/[id]/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
5757
);
5858
}, []);
5959

60+
const onResetSelectedNodeIds = useCallback(() => {
61+
setSelectedNodeIds([]);
62+
}, []);
63+
6064
const onConnect = useCallback(
6165
async (params: Connection) => {
6266
// 연결 유효성 검사: 같은 노드 간에는 하나의 연결만 허용
@@ -127,12 +131,12 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
127131
<RemoveContentButton
128132
topicId={id}
129133
selectedNodeIds={selectedNodeIds}
130-
setSelectedNodeIds={setSelectedNodeIds}
134+
onResetSelectedNodeIds={onResetSelectedNodeIds}
131135
/>
132136
<SummarizeDialog
133137
topicId={id}
134138
selectedNodeIds={selectedNodeIds}
135-
setSelectedNodeIds={setSelectedNodeIds}
139+
onResetSelectedNodeIds={onResetSelectedNodeIds}
136140
/>
137141
</>
138142
)}
@@ -160,6 +164,7 @@ export default function TopicBoardPage({ id, type }: TopicBoardPageProps) {
160164
onConnect,
161165
onEdgeClick,
162166
onNodeSelect,
167+
onResetSelectedNodeIds,
163168
}}
164169
>
165170
<ReactFlowCanvas />

0 commit comments

Comments
 (0)