Skip to content

Commit 5446971

Browse files
authored
Merge pull request #12 from boostcampwm-2024/refactor-fe-#9
lazy loading 적용
2 parents fd6eb9a + 3fa0411 commit 5446971

File tree

19 files changed

+186
-103
lines changed

19 files changed

+186
-103
lines changed

apps/frontend/src/app/App.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1+
import { lazy, Suspense } from "react";
12
import { useSyncedUsers } from "@/entities/user";
23
import { useProtectedWorkspace } from "@/features/workspace";
34
import { CanvasView } from "@/widgets/CanvasView";
4-
import { EditorView } from "@/widgets/EditorView";
5-
import { NodeToolsView } from "@/widgets/NodeToolsView";
65
import { PageSideBarView } from "@/widgets/PageSideBarView";
76
import { CanvasToolsView } from "@/widgets/CanvasToolsView";
8-
import { SideWrapper } from "@/shared/ui";
7+
import { SideWrapper, Skeleton } from "@/shared/ui";
8+
import { usePageStore } from "@/entities/page";
9+
10+
const EditorView = lazy(() => import("@/widgets/EditorView"));
11+
const NodeToolsView = lazy(() => import("@/widgets/NodeToolsView"));
912

1013
function App() {
1114
useSyncedUsers();
1215
const { isLoading } = useProtectedWorkspace();
16+
const { currentPage } = usePageStore();
1317

1418
if (isLoading) {
1519
return (
@@ -21,17 +25,31 @@ function App() {
2125

2226
return (
2327
<div className="fixed inset-0 bg-white">
24-
<SideWrapper side="right" className="z-50">
25-
<EditorView />
26-
</SideWrapper>
28+
{currentPage && (
29+
<SideWrapper side="right" className="z-50">
30+
<Suspense
31+
fallback={
32+
<Skeleton className="absolute right-4 top-4 flex h-[720px] w-[520px] flex-col rounded-lg border bg-white shadow-lg" />
33+
}
34+
>
35+
<EditorView />
36+
</Suspense>
37+
</SideWrapper>
38+
)}
2739
<CanvasView />
2840
<SideWrapper
2941
side="left"
3042
className="left-4 top-4 flex flex-row items-start gap-2"
3143
>
3244
<PageSideBarView />
3345
<CanvasToolsView />
34-
<NodeToolsView />
46+
{currentPage && (
47+
<Suspense
48+
fallback={<Skeleton className="h-[48px] w-[48px] rounded-xl" />}
49+
>
50+
<NodeToolsView />
51+
</Suspense>
52+
)}
3553
</SideWrapper>
3654
</div>
3755
);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export { CursorButton } from "./ui/CursorButton";
22
export { NodePanel } from "./ui/NodePanel";
3-
export { ProfilePanel } from "./ui/ProfilePanel";
3+
export { default } from "./ui/ProfilePanel";

apps/frontend/src/features/canvasTools/ui/ProfilePanel/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface ProfilePanelProps {
99
onClientIdChange: (clientId: string) => void;
1010
}
1111

12-
export function ProfilePanel({
12+
export default function ProfilePanel({
1313
color,
1414
clientId,
1515
onColorChange,
@@ -22,7 +22,7 @@ export function ProfilePanel({
2222
};
2323

2424
return (
25-
<div className="flex flex-row gap-4 p-4">
25+
<div className="m-2 flex flex-row gap-4 p-4">
2626
<CursorPreview
2727
defaultCoors={{ x: 90, y: 80 }}
2828
clientId={clientId}

apps/frontend/src/features/pageSidebar/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { LogoBtn } from "./ui/LogoBtn";
22
export { NoteList } from "./ui/NoteList";
33
export { Tools } from "./ui/Tools";
44
export { WorkspaceNav } from "./ui/WorkspaceNav";
5+
export { default } from "./ui/PageListPanel";

apps/frontend/src/features/pageSidebar/model/useNoteList.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,21 @@ export const useNoteList = () => {
1414
if (!canvas.provider) return;
1515
const nodesMap = canvas.provider.doc.getMap("nodes");
1616

17-
nodesMap.observe(() => {
17+
const initializePages = () => {
1818
const yNodes = Array.from(nodesMap.values()) as Node[];
1919
const data = yNodes.map((yNode) => yNode.data) as NoteNodeData[];
2020
setPages(data);
21+
};
22+
23+
initializePages();
24+
25+
nodesMap.observe(() => {
26+
initializePages();
2127
});
28+
29+
return () => {
30+
nodesMap.unobserve(initializePages);
31+
};
2232
}, [canvas.provider]);
2333

2434
const [noteIdToDelete, setNoteIdToDelete] = useState<number | null>(null);

apps/frontend/src/features/pageSidebar/ui/NoteList/index.tsx

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,33 @@ export function NoteList({ className }: NoteListProps) {
1919
onCloseModal,
2020
} = useNoteList();
2121

22-
if (!pages) {
23-
return <div>로딩중</div>;
24-
}
25-
2622
return (
2723
<div className={cn("flex flex-col gap-0.5 text-sm font-medium", className)}>
2824
<RemoveNoteModal
2925
isOpen={isModalOpen}
3026
onConfirm={onConfirm}
3127
onCloseModal={onCloseModal}
3228
/>
33-
{pages.map(({ id, title, emoji }) => (
34-
<Button
35-
onClick={() => handleNoteClick(id)}
36-
key={id}
37-
className="group flex flex-row items-center justify-between gap-1 rounded-sm px-3 py-1 hover:bg-neutral-100"
38-
>
39-
<Emoji emoji={emoji} width="w-5" height="h-5" />
40-
<div className="flex-1 truncate text-start">{title}</div>
41-
<span
42-
className="hidden text-neutral-400 transition-colors group-hover:block hover:text-red-500"
43-
onClick={(e) => {
44-
e.stopPropagation();
45-
openModal(id);
46-
}}
29+
{pages &&
30+
pages.map(({ id, title, emoji }) => (
31+
<Button
32+
onClick={() => handleNoteClick(id)}
33+
key={id}
34+
className="group flex flex-row items-center justify-between gap-1 rounded-sm px-3 py-1 hover:bg-neutral-100"
4735
>
48-
<Trash2 width={18} height={18} />
49-
</span>
50-
</Button>
51-
))}
36+
<Emoji emoji={emoji} width="w-5" height="h-5" />
37+
<div className="flex-1 truncate text-start">{title}</div>
38+
<span
39+
className="hidden text-neutral-400 transition-colors group-hover:block hover:text-red-500"
40+
onClick={(e) => {
41+
e.stopPropagation();
42+
openModal(id);
43+
}}
44+
>
45+
<Trash2 width={18} height={18} />
46+
</span>
47+
</Button>
48+
))}
5249
</div>
5350
);
5451
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { NoteList, Tools } from "@/features/pageSidebar";
2+
import { ScrollWrapper } from "@/shared/ui";
3+
4+
export default function PageListPanel() {
5+
return (
6+
<div className="flex flex-col gap-3 pb-4">
7+
<div className="w-full px-4">
8+
<Tools />
9+
</div>
10+
<ScrollWrapper className="max-h-[604px] overflow-x-clip scrollbar scrollbar-track-transparent scrollbar-thumb-[#d9d9d9]">
11+
<NoteList className="p-4 pb-0 pt-0" />
12+
</ScrollWrapper>
13+
</div>
14+
);
15+
}

apps/frontend/src/features/workspace/ui/ShareTool/SharePanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function SharePanel() {
7575
const isDisabled = isGuest || isPending;
7676

7777
return (
78-
<div className="w-full">
78+
<div className="w-full p-2">
7979
<div className="flex w-full flex-row justify-between p-1">
8080
<div className="flex flex-row items-center gap-2">
8181
<div className="flex-row text-sm text-slate-400">공개 범위</div>

apps/frontend/src/features/workspace/ui/ShareTool/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { lazy, Suspense } from "react";
12
import { Sharebutton } from "./ShareButton";
2-
import { SharePanel } from "./SharePanel";
3-
import { Popover } from "@/shared/ui";
3+
import { Popover, Skeleton } from "@/shared/ui";
4+
5+
const SharePanel = lazy(() => import("./SharePanel"));
46

57
export function ShareTool() {
68
return (
@@ -9,8 +11,10 @@ export function ShareTool() {
911
<Popover.Trigger>
1012
<Sharebutton />
1113
</Popover.Trigger>
12-
<Popover.Content className="rounded-lg border border-neutral-200 bg-white p-2 shadow-md">
13-
<SharePanel />
14+
<Popover.Content className="rounded-lg border border-neutral-200 bg-white shadow-md">
15+
<Suspense fallback={<Skeleton className="h-[92px] w-[240px]" />}>
16+
<SharePanel />
17+
</Suspense>
1418
</Popover.Content>
1519
</Popover>
1620
</div>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useState } from "react";
2+
import { UserProfile } from "@/entities/user";
3+
import { Logout, useGetUser, LoginForm } from "@/features/auth";
4+
import {
5+
WorkspaceAddButton,
6+
WorkspaceForm,
7+
WorkspaceList,
8+
} from "@/features/workspace";
9+
import { Divider } from "@/shared/ui";
10+
11+
export default function WorkspacePanel() {
12+
const { data } = useGetUser();
13+
const [isModalOpen, setIsModalOpen] = useState(false);
14+
15+
const onOpenModal = () => {
16+
setIsModalOpen(true);
17+
};
18+
19+
const onCloseModal = () => {
20+
setIsModalOpen(false);
21+
};
22+
return (
23+
<div className="w-[280px] px-4 py-4">
24+
{data ? (
25+
<div className="flex flex-col gap-1">
26+
<UserProfile nickname={data.snowflakeId ?? "123"} />
27+
<Divider direction="horizontal" className="h-0.5" />
28+
<WorkspaceList />
29+
<WorkspaceForm
30+
isModalOpen={isModalOpen}
31+
onCloseModal={onCloseModal}
32+
/>
33+
<Divider direction="horizontal" className="h-0.5" />
34+
<div className="flex w-full flex-col">
35+
<WorkspaceAddButton onClick={onOpenModal} />
36+
<Logout />
37+
</div>
38+
</div>
39+
) : (
40+
<LoginForm />
41+
)}
42+
</div>
43+
);
44+
}

0 commit comments

Comments
 (0)