Skip to content

Commit 90e3066

Browse files
committed
feat: 유튜브 요약 API 연동
1 parent 9f42b03 commit 90e3066

6 files changed

Lines changed: 193 additions & 77 deletions

File tree

apps/extension/src/constants/content.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

apps/extension/src/lib/tanstack/mutation/content.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ import { successToast } from "@/utils/toast";
22
import { useMutation } from "@tanstack/react-query";
33

44
import {
5-
detailSaveContent,
6-
finishDetailSaveContent,
7-
quickSaveContent,
5+
detailSaveWebContent,
6+
detailSaveYoutubeContent,
7+
finishDetailSaveWebContent,
8+
finishDetailSaveYoutubeContent,
9+
quickSaveWebContent,
10+
quickSaveYoutubeContent,
811
} from "../../../services/content";
912

1013
export const useQuickSaveContent = () => {
1114
return useMutation({
12-
mutationFn: quickSaveContent,
15+
mutationFn: quickSaveWebContent,
1316
onSuccess: (data) => {
1417
if (data.isSuccess) {
1518
successToast("저장에 성공했어요.");
@@ -21,9 +24,29 @@ export const useQuickSaveContent = () => {
2124
});
2225
};
2326

27+
export const useQuickSaveYoutubeContent = () => {
28+
return useMutation({
29+
mutationFn: quickSaveYoutubeContent,
30+
onSuccess: (data) => {
31+
if (data.isSuccess) {
32+
successToast("저장에 성공했어요.");
33+
}
34+
},
35+
});
36+
};
37+
2438
export const useDetailSaveContent = () => {
2539
return useMutation({
26-
mutationFn: detailSaveContent,
40+
mutationFn: detailSaveWebContent,
41+
onError: (error) => {
42+
console.error(error);
43+
},
44+
});
45+
};
46+
47+
export const useDetailSaveYoutubeContent = () => {
48+
return useMutation({
49+
mutationFn: detailSaveYoutubeContent,
2750
onError: (error) => {
2851
console.error(error);
2952
},
@@ -32,7 +55,16 @@ export const useDetailSaveContent = () => {
3255

3356
export const useFinishDetailSaveContent = () => {
3457
return useMutation({
35-
mutationFn: finishDetailSaveContent,
58+
mutationFn: finishDetailSaveWebContent,
59+
onError: (error) => {
60+
console.error(error);
61+
},
62+
});
63+
};
64+
65+
export const useFinishDetailSaveYoutubeContent = () => {
66+
return useMutation({
67+
mutationFn: finishDetailSaveYoutubeContent,
3668
onError: (error) => {
3769
console.error(error);
3870
},

apps/extension/src/pages/create-content.tsx

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import Logo from "@/assets/logo.svg?react";
44
import { Button } from "@/components/ui/button";
55
import { Input } from "@/components/ui/input";
66
import { useReplaceNavigate } from "@/hooks/use-replace-navigate";
7-
import { useFinishDetailSaveContent } from "@/lib/tanstack/mutation/content";
7+
import {
8+
useFinishDetailSaveContent,
9+
useFinishDetailSaveYoutubeContent,
10+
} from "@/lib/tanstack/mutation/content";
811
import { useGetCategories } from "@/lib/tanstack/query/category";
912
import { contentSchema, type ContentSchemaType } from "@/schemas/content";
1013
import { infoToast, successToast } from "@/utils/toast";
@@ -24,7 +27,6 @@ interface CreateContentState {
2427
tags: string[];
2528
category: string;
2629
htmlFile: File;
27-
type: string;
2830
}
2931

3032
export default function CreateContent() {
@@ -41,7 +43,14 @@ export default function CreateContent() {
4143
setIsCategoryOpen(false);
4244
});
4345

44-
const { mutateAsync, isPending } = useFinishDetailSaveContent();
46+
const {
47+
mutateAsync: mutateFinishDetailSaveContent,
48+
isPending: isPendingFinishDetailSaveContent,
49+
} = useFinishDetailSaveContent();
50+
const {
51+
mutateAsync: mutateFinishDetailSaveYoutubeContent,
52+
isPending: isPendingFinishDetailSaveYoutubeContent,
53+
} = useFinishDetailSaveYoutubeContent();
4554

4655
const { data: userCategories } = useGetCategories();
4756

@@ -63,31 +72,53 @@ export default function CreateContent() {
6372
const categories = [state?.category || "", ...(userCategories || [])];
6473

6574
const watchedTags = watch("tags");
75+
const watchedCategory = watch("category");
76+
const watchedThumbnail = watch("thumbnail");
77+
78+
const isYoutubeUrl = state.url.includes("youtube.com");
79+
const isPending = isPendingFinishDetailSaveContent || isPendingFinishDetailSaveYoutubeContent;
6680

6781
const onGoBack = () => {
6882
navigate("/search-content");
6983
};
7084

7185
const onSave = handleSubmit(async (data) => {
72-
const formData = new FormData();
73-
formData.append("title", data.title);
74-
formData.append("url", data.url);
75-
formData.append("thumbnail", data.thumbnail || "");
76-
formData.append("memo", data.memo || "");
77-
formData.append("summary", data.summary || "");
78-
formData.append("category", data.category);
79-
for (const tag of data.tags) {
80-
formData.append("tags", tag);
86+
if (isYoutubeUrl) {
87+
await mutateFinishDetailSaveYoutubeContent(
88+
{
89+
...data,
90+
memo: data.memo || "",
91+
summary: data.summary || "",
92+
transcript: "",
93+
thumbnail: data.thumbnail || "",
94+
},
95+
{
96+
onSuccess: () => {
97+
successToast("저장에 성공했어요.");
98+
navigate("/search-content");
99+
},
100+
}
101+
);
102+
} else {
103+
const formData = new FormData();
104+
formData.append("title", data.title);
105+
formData.append("url", data.url);
106+
formData.append("thumbnail", data.thumbnail || "");
107+
formData.append("memo", data.memo || "");
108+
formData.append("summary", data.summary || "");
109+
formData.append("category", data.category);
110+
for (const tag of data.tags) {
111+
formData.append("tags", tag);
112+
}
113+
formData.append("htmlFile", htmlFile);
114+
115+
await mutateFinishDetailSaveContent(formData, {
116+
onSuccess: () => {
117+
successToast("저장에 성공했어요.");
118+
navigate("/search-content");
119+
},
120+
});
81121
}
82-
formData.append("htmlFile", htmlFile);
83-
formData.append("type", state.type);
84-
85-
await mutateAsync(formData, {
86-
onSuccess: () => {
87-
successToast("저장에 성공했어요.");
88-
navigate("/search-content");
89-
},
90-
});
91122
});
92123

93124
const onAddTag = (e: React.FormEvent<HTMLFormElement>) => {
@@ -121,9 +152,6 @@ export default function CreateContent() {
121152
setIsCategoryOpen(false);
122153
};
123154

124-
const watchedCategory = watch("category");
125-
const watchedThumbnail = watch("thumbnail");
126-
127155
return (
128156
<>
129157
{/* 헤더 */}

apps/extension/src/pages/search-content.tsx

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Button } from "@/components/ui/button";
2-
import { CONTENT_TYPE } from "@/constants/content";
32
import { useReplaceNavigate } from "@/hooks/use-replace-navigate";
43
import { useTabStore } from "@/lib/zustand/tab";
54
import { useUserStore } from "@/lib/zustand/user";
@@ -17,7 +16,12 @@ import {
1716
import { LogOut, Save, Sparkles } from "lucide-react";
1817
import { useNavigate } from "react-router-dom";
1918

20-
import { useDetailSaveContent, useQuickSaveContent } from "../lib/tanstack/mutation/content";
19+
import {
20+
useDetailSaveContent,
21+
useDetailSaveYoutubeContent,
22+
useQuickSaveContent,
23+
useQuickSaveYoutubeContent,
24+
} from "../lib/tanstack/mutation/content";
2125

2226
const getHtmlContent = async () => {
2327
const html = await getHtmlText();
@@ -79,61 +83,84 @@ export default function SearchContent() {
7983
useQuickSaveContent();
8084
const { mutateAsync: mutateDetailSaveContent, isPending: isPendingDetailSaveContent } =
8185
useDetailSaveContent();
86+
const {
87+
mutateAsync: mutateQuickSaveYoutubeContent,
88+
isPending: isPendingQuickSaveYoutubeContent,
89+
} = useQuickSaveYoutubeContent();
90+
const {
91+
mutateAsync: mutateDetailSaveYoutubeContent,
92+
isPending: isPendingDetailSaveYoutubeContent,
93+
} = useDetailSaveYoutubeContent();
8294

8395
const saveDisabledd =
84-
isFindingExistPath || isPendingQuickSaveContent || isPendingDetailSaveContent;
96+
isFindingExistPath ||
97+
isPendingQuickSaveContent ||
98+
isPendingDetailSaveContent ||
99+
isPendingQuickSaveYoutubeContent ||
100+
isPendingDetailSaveYoutubeContent;
85101

86-
const currentContentType = currentTab.url.includes("youtube.com")
87-
? CONTENT_TYPE.YOUTUBE
88-
: CONTENT_TYPE.WEB;
102+
const isYoutubeUrl = currentTab.url.includes("youtube.com");
89103

90104
const onSaveOnly = async () => {
91-
const { htmlContent, thumbnail } = await getHtmlContent();
92-
if (!htmlContent || !thumbnail) return;
105+
if (isYoutubeUrl) {
106+
await mutateQuickSaveYoutubeContent({
107+
title: currentTab.title,
108+
url: currentTab.url,
109+
});
110+
} else {
111+
const { htmlContent, thumbnail } = await getHtmlContent();
112+
if (!htmlContent || !thumbnail) return;
93113

94-
const htmlBlob = new Blob([htmlContent], { type: "text/html" });
95-
const htmlFile = new File([htmlBlob], "content.html", { type: "text/html" });
114+
const htmlBlob = new Blob([htmlContent], { type: "text/html" });
115+
const htmlFile = new File([htmlBlob], "content.html", { type: "text/html" });
96116

97-
const formData = new FormData();
117+
const formData = new FormData();
98118

99-
formData.append("htmlFile", htmlFile);
100-
formData.append("title", currentTab.title);
101-
formData.append("url", currentTab.url);
102-
formData.append("thumbnail", thumbnail);
119+
formData.append("htmlFile", htmlFile);
120+
formData.append("title", currentTab.title);
121+
formData.append("url", currentTab.url);
122+
formData.append("thumbnail", thumbnail);
103123

104-
await mutateQuickSaveContent(formData);
124+
await mutateQuickSaveContent(formData);
125+
}
105126
};
106127

107128
const onSaveWithSummary = async () => {
108-
const { htmlContent, thumbnail } = await getHtmlContent();
109-
if (!htmlContent || !thumbnail) return;
129+
if (isYoutubeUrl) {
130+
const res = await mutateDetailSaveYoutubeContent({
131+
url: currentTab.url,
132+
});
133+
navigate("/create-content", {
134+
state: {
135+
...res.result,
136+
...currentTab,
137+
thumbnail: "",
138+
transcript: "",
139+
},
140+
});
141+
} else {
142+
const { htmlContent, thumbnail } = await getHtmlContent();
143+
if (!htmlContent || !thumbnail) return;
110144

111-
const htmlBlob = new Blob([htmlContent], { type: "text/html" });
112-
const htmlFile = new File([htmlBlob], "content.html", { type: "text/html" });
145+
const htmlBlob = new Blob([htmlContent], { type: "text/html" });
146+
const htmlFile = new File([htmlBlob], "content.html", { type: "text/html" });
113147

114-
const formData = new FormData();
115-
formData.append("htmlFile", htmlFile);
116-
formData.append("type", currentContentType);
148+
const formData = new FormData();
149+
formData.append("htmlFile", htmlFile);
117150

118-
await mutateDetailSaveContent(
119-
{
151+
const res = await mutateDetailSaveContent({
120152
url: currentTab.url,
121153
formData,
122-
},
123-
{
124-
onSuccess: (data) => {
125-
navigate("/create-content", {
126-
state: {
127-
...data.result,
128-
...currentTab,
129-
thumbnail,
130-
htmlFile,
131-
type: currentContentType,
132-
},
133-
});
154+
});
155+
navigate("/create-content", {
156+
state: {
157+
...res.result,
158+
...currentTab,
159+
thumbnail,
160+
htmlFile,
134161
},
135-
}
136-
);
162+
});
163+
}
137164
};
138165

139166
return (

apps/extension/src/schemas/content.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { z } from "zod";
33
export const contentSchema = z.object({
44
title: z.string().min(1, "제목을 입력해주세요."),
55
url: z.string().min(1, "URL을 입력해주세요."),
6-
thumbnail: z.string().min(1, "썸네일을 입력해주세요."),
6+
thumbnail: z.string().optional(),
77
category: z.string().min(1, "카테고리를 선택해주세요."),
88
summary: z.string().optional(),
99
memo: z.string().optional(),

0 commit comments

Comments
 (0)