Skip to content

Commit a8c4295

Browse files
authored
배포 최신화
배포 최신화
2 parents 4e8fb53 + 2bf15c6 commit a8c4295

41 files changed

Lines changed: 982 additions & 666 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/extension/public/icon128.png

2.68 KB
Loading

apps/extension/src/assets/menu.svg

Lines changed: 3 additions & 0 deletions
Loading

apps/extension/src/components/loading/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ import { Spinner } from "@repo/ui";
33

44
interface LoadingProps {
55
title: string;
6+
description?: string;
67
children: React.ReactNode;
78
}
89

9-
const Loading = ({ title, children }: LoadingProps) => {
10+
const Loading = ({ title, description, children }: LoadingProps) => {
1011
const isLoading = useLoadingStore((state) => state.isLoading);
1112

1213
return isLoading ? (
1314
<div className="flex h-full flex-col items-center justify-center gap-10 text-notification">
14-
{title}
15+
<div className="text-center">
16+
<h1>{title}</h1>
17+
<p className="text-description">{description}</p>
18+
</div>
1519
<Spinner />
1620
</div>
1721
) : (

apps/extension/src/hooks/use-detect-path.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useEffect } from "react";
22

33
import { useStarStore } from "@/state/zustand/star";
44
import { useTabStore } from "@/state/zustand/tab";
5+
import { useUserStore } from "@/state/zustand/user";
56
import { updateCurrentTab } from "@/utils/chrome";
67

78
import { useReplaceNavigate } from "./use-replace-navigate";
@@ -24,6 +25,7 @@ export function useDetectPath() {
2425
const { currentTab, setCurrentTab, setIsFindingExistPath } = useTabStore();
2526

2627
const stars = useStarStore((state) => state.stars);
28+
const isLoggedIn = useUserStore((state) => state.isLoggedIn);
2729

2830
const navigate = useReplaceNavigate();
2931

@@ -36,8 +38,8 @@ export function useDetectPath() {
3638
};
3739

3840
const onTabUpdated = (_: number, changeInfo: TabChangeInfoProps) => {
39-
setIsFindingExistPath(true);
40-
if (changeInfo.status === "complete") {
41+
if (changeInfo.url && changeInfo.status === "loading") {
42+
setIsFindingExistPath(true);
4143
updateCurrentTab(setCurrentTab);
4244
}
4345
};
@@ -52,6 +54,10 @@ export function useDetectPath() {
5254
}, []);
5355

5456
useEffect(() => {
57+
if (!isLoggedIn) {
58+
return navigate("/");
59+
}
60+
5561
const findStar = stars?.starListDto.find((star) => encodeURI(star.siteUrl) === currentTab.url);
5662

5763
if (findStar) {
@@ -60,7 +66,7 @@ export function useDetectPath() {
6066
navigate("/bookmark");
6167
}
6268
setIsFindingExistPath(false);
63-
}, [stars, currentTab]);
69+
}, [stars, currentTab, isLoggedIn]);
6470

6571
return { currentTab };
6672
}

apps/extension/src/index.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,10 @@ textarea {
5353
}
5454
outline: none;
5555
}
56+
57+
button {
58+
cursor: pointer;
59+
&:disabled {
60+
cursor: not-allowed;
61+
}
62+
}

apps/extension/src/pages/bookmark.tsx

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,26 @@
1-
import { useMemo } from "react";
1+
import { useState } from "react";
22

3+
import Logo from "@/assets/logo.svg?react";
4+
import Menu from "@/assets/menu.svg?react";
35
import Loading from "@/components/loading";
46
import { useCreateStar } from "@/state/mutation/star";
5-
import { useStarStore } from "@/state/zustand/star";
67
import { useTabStore } from "@/state/zustand/tab";
78
import { getHtmlText } from "@/utils/chrome";
8-
import { Graph2D } from "@repo/ui";
9-
import { RectangleButton } from "@repo/ui";
9+
import { Modal, RectangleButton, useOutsideClick } from "@repo/ui";
1010

1111
import { useNavigate } from "react-router-dom";
1212

1313
const url = import.meta.env.VITE_BASE_URL;
1414

1515
const Bookmark = () => {
1616
const { currentTab, isFindingExistPath } = useTabStore();
17+
const [isMenuOpen, setIsMenuOpen] = useState(false);
18+
const [isLogoutModalOpen, setIsLogoutModalOpen] = useState(false);
19+
20+
const [menuContainerRef] = useOutsideClick<HTMLDivElement>(() => setIsMenuOpen(false));
1721

1822
const navigate = useNavigate();
1923
const { mutateAsync } = useCreateStar();
20-
const stars = useStarStore((state) => state.stars);
21-
22-
const graphData = useMemo(() => {
23-
if (!stars?.starListDto || !stars?.linkListDto) return { nodes: [], links: [] };
24-
25-
return {
26-
nodes: stars.starListDto.map((star) => ({
27-
id: star.starId,
28-
name: star.title,
29-
val: Math.min(star.views, 10),
30-
url: star.siteUrl,
31-
})),
32-
links: stars.linkListDto.map((link) => ({
33-
source: link.linkedNodeIdList[0],
34-
target: link.linkedNodeIdList[1],
35-
})),
36-
};
37-
}, [stars]);
3824

3925
const onClickLogout = async () => {
4026
await chrome.cookies.remove({
@@ -65,13 +51,38 @@ const Bookmark = () => {
6551
});
6652
};
6753

54+
const onToggleMenu = (e: React.MouseEvent<HTMLButtonElement>) => {
55+
e.stopPropagation();
56+
setIsMenuOpen((prev) => !prev);
57+
};
58+
59+
const onOpenLogoutModal = (e: React.MouseEvent<HTMLButtonElement>) => {
60+
e.stopPropagation();
61+
setIsMenuOpen(false);
62+
setIsLogoutModalOpen(true);
63+
};
64+
6865
return (
69-
<Loading title="페이지를 요약하고 있어요!">
66+
<Loading title="페이지를 요약하고 있어요!" description="페이지를 벗어나면 요약이 취소돼요.">
67+
<header className="flex items-center justify-between">
68+
<Logo />
69+
<div className="relative">
70+
<button onClick={onToggleMenu}>
71+
<Menu />
72+
</button>
73+
{isMenuOpen && (
74+
<div ref={menuContainerRef} className="absolute bottom-0 right-0 translate-y-full">
75+
<button
76+
onClick={onOpenLogoutModal}
77+
className="w-max rounded-sm border border-black px-2.5 py-1"
78+
>
79+
로그아웃
80+
</button>
81+
</div>
82+
)}
83+
</div>
84+
</header>
7085
<main className="flex h-full flex-col justify-center gap-28 overflow-x-hidden">
71-
<section>
72-
<Graph2D graphData={graphData} />
73-
<h1 className="text-notification">현재 페이지 정보</h1>
74-
</section>
7586
<section className="space-y-4">
7687
<div className="text-body space-y-2">
7788
<h2>URL</h2>
@@ -82,15 +93,24 @@ const Bookmark = () => {
8293
<p className="text-gray7">{currentTab.title}</p>
8394
</div>
8495
</section>
85-
<div className="flex flex-col gap-2">
86-
<RectangleButton onClick={onClickAdd} disabled={isFindingExistPath}>
87-
{isFindingExistPath ? "기존 북마크(추가 불가)" : "북마크에 추가하기"}
88-
</RectangleButton>
89-
<RectangleButton variation="outline" onClick={onClickLogout}>
90-
로그아웃
91-
</RectangleButton>
92-
</div>
96+
<RectangleButton className="flex-none" onClick={onClickAdd} disabled={isFindingExistPath}>
97+
{isFindingExistPath ? "기존 북마크(추가 불가)" : "북마크에 추가하기"}
98+
</RectangleButton>
9399
</main>
100+
{isLogoutModalOpen && (
101+
<Modal
102+
title="로그아웃"
103+
subTitle="로그아웃 하시겠습니까?"
104+
callback={() => setIsLogoutModalOpen(false)}
105+
>
106+
<div className="flex w-full gap-2">
107+
<RectangleButton onClick={() => setIsLogoutModalOpen(false)} variation="outline">
108+
취소
109+
</RectangleButton>
110+
<RectangleButton onClick={onClickLogout}>로그아웃</RectangleButton>
111+
</div>
112+
</Modal>
113+
)}
94114
</Loading>
95115
);
96116
};

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

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import AISummary from "@/assets/ai-summary.svg?react";
44
import Logo from "@/assets/logo.svg?react";
55
import CardWrapper from "@/components/create-bookmark/card-wrapper";
66
import Loading from "@/components/loading";
7+
import { useReplaceNavigate } from "@/hooks/use-replace-navigate";
78
import { useCreateCategory } from "@/state/mutation/category";
89
import { useCompleteCreateStar, useUpdateStar } from "@/state/mutation/star";
910
import { useGetKeywords } from "@/state/query/keyword";
1011
import { useGetStarById } from "@/state/query/star";
1112
import { useStarStore } from "@/state/zustand/star";
12-
import { useTabStore } from "@/state/zustand/tab";
1313
import { CategoryListProps } from "@/types/category";
1414
import { CompleteSummarizeStarProps } from "@/types/star";
15-
import { Graph2D } from "@repo/ui";
15+
import { cn, Graph2D } from "@repo/ui";
1616
import { Keyword, RectangleButton, Spinner, Textarea } from "@repo/ui";
1717

1818
import { Navigate, useLocation, useSearchParams } from "react-router-dom";
@@ -41,9 +41,10 @@ const CreateBookmark = () => {
4141
const [searchParams] = useSearchParams();
4242
const id = searchParams.get("id");
4343

44+
// 추후 AI 요약 기능 추가 시 tanstack query의 isPending 사용
45+
const [isAISummaryPending, setIsAISummaryPending] = useState(false);
4446
const [bookmark, setBookmark] = useState<StateProps>(Object.assign(DEFAULT_BOOKMARK, state));
4547
const stars = useStarStore((state) => state.stars);
46-
const setIsFindingExistPath = useTabStore((state) => state.setIsFindingExistPath);
4748

4849
const { mutateAsync: mutateAsyncCompleteCreateStar } = useCompleteCreateStar();
4950
const { mutateAsync: mutateAsyncUpdateStar } = useUpdateStar();
@@ -52,6 +53,8 @@ const CreateBookmark = () => {
5253
const { data: keywords } = useGetKeywords();
5354
const { data: star, isLoading: isLoadingGetStar } = useGetStarById(id);
5455

56+
const navigate = useReplaceNavigate();
57+
5558
useEffect(() => {
5659
if (!isLoadingGetStar && star?.result) {
5760
setBookmark((prev) => ({
@@ -77,13 +80,31 @@ const CreateBookmark = () => {
7780
// 관련된 노드만 필터링
7881
const relatedNodes = stars.starListDto.filter((star) => relatedNodeIds.has(star.starId));
7982

83+
// 현재 북마크 정보 가져오기
84+
const currentStar = stars.starListDto.find((star) => star.starId === id);
85+
86+
// 연관된 노드가 없으면 현재 북마크만 포함
87+
const nodes =
88+
relatedNodes.length > 0
89+
? relatedNodes.map((star) => ({
90+
id: star.starId,
91+
name: star.title,
92+
val: Math.min(star.views, 10),
93+
url: star.siteUrl,
94+
}))
95+
: currentStar
96+
? [
97+
{
98+
id: currentStar.starId,
99+
name: currentStar.title,
100+
val: 10,
101+
url: currentStar.siteUrl,
102+
},
103+
]
104+
: [];
105+
80106
return {
81-
nodes: relatedNodes.map((star) => ({
82-
id: star.starId,
83-
name: star.title,
84-
val: Math.min(star.views, 10),
85-
url: star.siteUrl,
86-
})),
107+
nodes,
87108
links: relatedLinks.map((link) => ({
88109
source: link.linkedNodeIdList[0],
89110
target: link.linkedNodeIdList[1],
@@ -100,8 +121,6 @@ const CreateBookmark = () => {
100121
return <Navigate to="/bad-request" replace />;
101122
}
102123

103-
const saveDisabled = bookmark.categoryName.trim() === "";
104-
105124
const onSelectCategory = (category: string) => {
106125
setBookmark((prev) => ({ ...prev, categoryName: category }));
107126
};
@@ -144,38 +163,22 @@ const CreateBookmark = () => {
144163
};
145164

146165
const onClickSave = async () => {
147-
// id가 있으면 수정하기 API 요청
148-
const categoryName = bookmark.categories.find(
149-
(category) => category.name === bookmark.categoryName
150-
)?.name;
151-
152-
if (!categoryName) {
153-
return;
154-
}
155-
156166
const body = {
157167
...bookmark,
158168
keywordList: bookmark.keywords,
159169
};
160170

161171
if (id) {
162-
await mutateAsyncUpdateStar(
163-
{ id, body },
164-
{
165-
onSuccess: () => {
166-
setIsFindingExistPath(true);
167-
},
168-
}
169-
);
172+
await mutateAsyncUpdateStar({ id, body });
170173
} else {
171-
await mutateAsyncCompleteCreateStar(body, {
172-
onSuccess: () => {
173-
setIsFindingExistPath(true);
174-
},
175-
});
174+
await mutateAsyncCompleteCreateStar(body);
176175
}
177176
};
178177

178+
const onClickCancel = () => {
179+
navigate("/bookmark");
180+
};
181+
179182
if (isLoadingGetStar) {
180183
return (
181184
<div className="flex h-full flex-col items-center justify-center gap-10 text-notification">
@@ -188,15 +191,9 @@ const CreateBookmark = () => {
188191
return (
189192
<Loading title="북마크를 저장하고 있어요!">
190193
<main className="flex h-full flex-col gap-6">
191-
<header className="flex items-center justify-between">
192-
<div className="flex items-center gap-1">
193-
<Logo />
194-
<p className="text-description font-bold">Nebula</p>
195-
</div>
196-
<button className="flex items-center gap-1">
197-
<AISummary />
198-
<p className="text-xs font-semibold">Nebula AI</p>
199-
</button>
194+
<header className="flex items-center gap-1">
195+
<Logo />
196+
<p className="text-description font-bold">Nebula</p>
200197
</header>
201198
{id && (
202199
<section>
@@ -220,6 +217,17 @@ const CreateBookmark = () => {
220217
value={bookmark.summaryAI}
221218
onChange={onChangeText}
222219
placeholder="북마크에 대한 요약을 작성할 수 있어요."
220+
rightElement={
221+
<button
222+
className={cn("flex items-center gap-1", isAISummaryPending && "animate-pulse")}
223+
onClick={() => setIsAISummaryPending(true)}
224+
>
225+
<AISummary />
226+
<p className="text-xs font-semibold">
227+
{isAISummaryPending ? "요약 중..." : "인공지능 요약"}
228+
</p>
229+
</button>
230+
}
223231
/>
224232
<Textarea
225233
id="userMemo"
@@ -239,11 +247,11 @@ const CreateBookmark = () => {
239247
onUpdateKeyword={onUpdateKeyword}
240248
/>
241249
</section>
242-
<section className="flex gap-3">
243-
<RectangleButton variation="outline">취소</RectangleButton>
244-
<RectangleButton disabled={saveDisabled} onClick={onClickSave}>
245-
저장
250+
<section className="flex gap-3 pb-5">
251+
<RectangleButton variation="outline" onClick={onClickCancel}>
252+
취소
246253
</RectangleButton>
254+
<RectangleButton onClick={onClickSave}>저장</RectangleButton>
247255
</section>
248256
</main>
249257
</Loading>

0 commit comments

Comments
 (0)