Skip to content

Commit 80a61fd

Browse files
committed
refactor: migrate breadcrumb management to Zustand store
- Replace route-based breadcrumb logic in layout with centralized Zustand store - Each page now sets its own breadcrumb content via useBreadcrumbStore
1 parent 0e2e073 commit 80a61fd

File tree

7 files changed

+58
-29
lines changed

7 files changed

+58
-29
lines changed

src-frontend/app/apps/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ import { AppList } from "@/components/apps/app-list";
66
import { AppDetail } from "@/components/apps/app-detail";
77
import { toast } from "@/hooks/use-toast";
88
import { uninstallApp } from "@/lib/api/apps";
9-
import { Suspense } from "react";
9+
import { Suspense, useEffect } from "react";
10+
import { useBreadcrumbStore } from "@/stores";
11+
import { AppBreadcrumb } from "@/components/apps/breadcrumb";
1012

1113
function AppsPageContent() {
1214
const router = useRouter();
1315
const [selectedApp] = useQueryState("id");
16+
const { setBreadcrumb, clearBreadcrumb } = useBreadcrumbStore();
17+
18+
useEffect(() => {
19+
setBreadcrumb(<AppBreadcrumb />);
20+
return () => clearBreadcrumb();
21+
}, [setBreadcrumb, clearBreadcrumb]);
1422

1523
const handleUninstallApp = async (appId: string): Promise<boolean> => {
1624
const appName = appId.split(".").pop();

src-frontend/app/layout.tsx

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ import {
1616
import { AnalyticsProvider, PageViewTracker } from "@/lib/analytics";
1717
import { useEffect } from "react";
1818
import { useTheme } from "next-themes";
19-
import { AppBreadcrumb } from "@/components/apps/breadcrumb";
20-
import { WorkspaceBreadcrumb } from "@/components/workspace/breadcrumb";
21-
import { MarketplaceBreadcrumb } from "@/components/marketplace/marketplace-breadcrumb";
22-
import { ScrollText } from "lucide-react";
19+
import { useBreadcrumbStore } from "@/stores";
2320

2421
const title = "SyftBox";
2522
const description = "The internet of private data!";
@@ -29,6 +26,7 @@ function MainLayout({ children }: { children: React.ReactNode }) {
2926
const sidebarExcludedPaths = ["/", "/about/", "/updates/"];
3027
const shouldShowSidebar = !sidebarExcludedPaths.includes(pathname);
3128
const { theme } = useTheme();
29+
const { breadcrumbContent } = useBreadcrumbStore();
3230

3331
useEffect(() => {
3432
if (typeof window !== "undefined" && window.__TAURI__) {
@@ -62,29 +60,6 @@ function MainLayout({ children }: { children: React.ReactNode }) {
6260
}
6361
}, []);
6462

65-
// Determine which breadcrumb to show based on the current route
66-
const getBreadcrumb = () => {
67-
if (pathname.startsWith("/apps")) {
68-
return <AppBreadcrumb />;
69-
}
70-
if (pathname.startsWith("/workspace")) {
71-
return <WorkspaceBreadcrumb />;
72-
}
73-
if (pathname.startsWith("/marketplace")) {
74-
return <MarketplaceBreadcrumb />;
75-
}
76-
if (pathname.startsWith("/logs")) {
77-
return (
78-
<span className="flex items-center gap-2 p-1 text-sm">
79-
<ScrollText className="h-4 w-4" />
80-
<span>Logs</span>
81-
</span>
82-
);
83-
}
84-
// For other routes, show nothing
85-
return null;
86-
};
87-
8863
if (!shouldShowSidebar) {
8964
return (
9065
<div className="bg-sidebar flex h-screen w-screen">
@@ -105,7 +80,7 @@ function MainLayout({ children }: { children: React.ReactNode }) {
10580
}}
10681
>
10782
<div className="bg-sidebar flex h-screen w-screen flex-col">
108-
<TitleBar>{getBreadcrumb()}</TitleBar>
83+
<TitleBar>{breadcrumbContent}</TitleBar>
10984
<div className="flex flex-1 overflow-hidden">
11085
<Sidebar className="border-none">
11186
<AppSidebar />

src-frontend/app/logs/page.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
"use client";
22

3+
import { useEffect } from "react";
34
import { Logs } from "@/components/logs";
5+
import { useBreadcrumbStore } from "@/stores";
6+
import { ScrollText } from "lucide-react";
47

58
export default function LogsPage() {
9+
const { setBreadcrumb, clearBreadcrumb } = useBreadcrumbStore();
10+
11+
useEffect(() => {
12+
setBreadcrumb(
13+
<span className="flex items-center gap-2 p-1 text-sm">
14+
<ScrollText className="h-4 w-4" />
15+
<span>Logs</span>
16+
</span>,
17+
);
18+
return () => clearBreadcrumb();
19+
}, [setBreadcrumb, clearBreadcrumb]);
20+
621
return <Logs />;
722
}

src-frontend/app/marketplace/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
import { Toolbar } from "@/components/ui/toolbar";
2525
import { toast } from "@/hooks/use-toast";
2626
import { Suspense } from "react";
27+
import { useBreadcrumbStore } from "@/stores";
28+
import { MarketplaceBreadcrumb } from "@/components/marketplace/marketplace-breadcrumb";
2729

2830
function MarketplacePageContent() {
2931
const router = useRouter();
@@ -35,6 +37,7 @@ function MarketplacePageContent() {
3537
const [isSubmitting, setIsSubmitting] = useState(false);
3638
const [marketplaceApps, setMarketplaceApps] = useState<MarketplaceApp[]>([]);
3739
const [isLoading, setIsLoading] = useState(true);
40+
const { setBreadcrumb, clearBreadcrumb } = useBreadcrumbStore();
3841

3942
useEffect(() => {
4043
const fetchApps = async () => {
@@ -57,6 +60,11 @@ function MarketplacePageContent() {
5760
fetchApps();
5861
}, []);
5962

63+
useEffect(() => {
64+
setBreadcrumb(<MarketplaceBreadcrumb />);
65+
return () => clearBreadcrumb();
66+
}, [setBreadcrumb, clearBreadcrumb]);
67+
6068
const handlePublishSubmit = async () => {
6169
if (!repoUrl.trim()) return;
6270

src-frontend/app/workspace/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@ import {
77
initializeFileSystemStore,
88
useFileSystemStore,
99
} from "@/stores/useFileSystemStore";
10+
import { useBreadcrumbStore } from "@/stores";
11+
import { WorkspaceBreadcrumb } from "@/components/workspace/breadcrumb";
1012

1113
function FilesPageContent() {
1214
const [initialPath] = useQueryState("path");
1315
const { refreshFileSystem } = useFileSystemStore();
16+
const { setBreadcrumb, clearBreadcrumb } = useBreadcrumbStore();
1417

1518
useEffect(() => {
1619
initializeFileSystemStore(initialPath);
1720
}, [initialPath]);
1821

22+
useEffect(() => {
23+
setBreadcrumb(<WorkspaceBreadcrumb />);
24+
return () => clearBreadcrumb();
25+
}, [setBreadcrumb, clearBreadcrumb]);
26+
1927
useEffect(() => {
2028
// Periodically refresh the file system
2129
const interval = setInterval(() => {

src-frontend/stores/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ export {
1414
initializeFileSystemStore,
1515
} from "./useFileSystemStore";
1616
export { useSidebarStore, type FavoriteItem } from "./useSidebarStore";
17+
export { useBreadcrumbStore } from "./useBreadcrumbStore";
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { create } from "zustand";
2+
import type { ReactNode } from "react";
3+
4+
interface BreadcrumbStore {
5+
breadcrumbContent: ReactNode | null;
6+
setBreadcrumb: (content: ReactNode | null) => void;
7+
clearBreadcrumb: () => void;
8+
}
9+
10+
export const useBreadcrumbStore = create<BreadcrumbStore>((set) => ({
11+
breadcrumbContent: null,
12+
setBreadcrumb: (content) => set({ breadcrumbContent: content }),
13+
clearBreadcrumb: () => set({ breadcrumbContent: null }),
14+
}));

0 commit comments

Comments
 (0)