From 6ed11ffe5e5ca8afe270ddde79903976232d51e2 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 2 Sep 2026 15:53:14 +1000 Subject: [PATCH] fix(desktop): restore the Projects sidebar row and overview route The channel-first migration removed the Projects row from the sidebar's primary menu and turned /projects into a redirect to home, leaving the projects overview (ProjectsScreen) unreachable: the sidebar Projects section's "Browse all" action and every other goProjects() caller silently landed on the Inbox. Bring back the entry point without reverting the migration: - /projects renders ProjectsScreen again (lazy, in Suspense) instead of redirecting to /. The usePreviewFeatureWarning("projects") call from the pre-promotion route stays gone. - The primary menu shows Projects under Inbox again, wired through onSelectProjects and projectsOverviewActive exactly as before, but with no FeatureGate: Projects was promoted out of preview features on this branch and the "projects" feature id no longer exists, so the old gate would only re-flood the console with unknown-feature warnings. - channel-project-features.spec.ts pinned the row's absence; it now asserts the row is visible alongside the sidebar Projects section. badge.spec.ts already required the row (primary rows share the same inactive emphasis) and passes again without changes. Verified: desktop unit suite 5943 passing; tsc, biome clean; the channel-project-features and badge smoke specs pass (20 tests). Co-Authored-By: Claude Fable 5 Signed-off-by: Matt Toohey --- desktop/src/app/AppShell.tsx | 5 +++++ desktop/src/app/routes/projects.tsx | 22 +++++++++++++++---- .../src/features/sidebar/ui/AppSidebar.tsx | 4 ++++ .../features/sidebar/ui/AppSidebar.types.ts | 2 ++ .../sidebar/ui/AppSidebarPinnedHeader.tsx | 18 ++++++++++++++- .../e2e/channel-project-features.spec.ts | 2 +- 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/desktop/src/app/AppShell.tsx b/desktop/src/app/AppShell.tsx index 48ddb8a10f..eb5ab5a95d 100644 --- a/desktop/src/app/AppShell.tsx +++ b/desktop/src/app/AppShell.tsx @@ -147,6 +147,7 @@ export function AppShell() { goChannel, goHome, goNewMessage, + goProjects, goPulse, goSettings, goWorkflows, @@ -885,6 +886,7 @@ export function AppShell() { scopeSearchFocusRequest, ]} onSelectHome={() => void goHome()} + onSelectProjects={() => void goProjects()} onSelectPulse={() => void goPulse()} onSelectSettings={handleOpenSettings} onSelectWorkflows={() => void goWorkflows()} @@ -901,6 +903,9 @@ export function AppShell() { }) } profile={profileQuery.data} + projectsOverviewActive={ + location.pathname === "/projects" + } selfUserStatus={ deferredPubkey ? (selfStatusQuery.data?.[ diff --git a/desktop/src/app/routes/projects.tsx b/desktop/src/app/routes/projects.tsx index 75871c03b4..bd6a7064a9 100644 --- a/desktop/src/app/routes/projects.tsx +++ b/desktop/src/app/routes/projects.tsx @@ -1,7 +1,21 @@ -import { createFileRoute, redirect } from "@tanstack/react-router"; +import * as React from "react"; +import { createFileRoute } from "@tanstack/react-router"; + +import { ViewLoadingFallback } from "@/shared/ui/ViewLoadingFallback"; + +const ProjectsScreen = React.lazy(async () => { + const module = await import("@/features/projects/ui/ProjectsScreen"); + return { default: module.ProjectsScreen }; +}); export const Route = createFileRoute("/projects")({ - beforeLoad: () => { - throw redirect({ to: "/" }); - }, + component: ProjectsRouteComponent, }); + +function ProjectsRouteComponent() { + return ( + }> + + + ); +} diff --git a/desktop/src/features/sidebar/ui/AppSidebar.tsx b/desktop/src/features/sidebar/ui/AppSidebar.tsx index bb68e75328..6d9a678659 100644 --- a/desktop/src/features/sidebar/ui/AppSidebar.tsx +++ b/desktop/src/features/sidebar/ui/AppSidebar.tsx @@ -91,6 +91,7 @@ export function AppSidebar({ isCreatingChannel, isCreatingForum, profile, + projectsOverviewActive, relayConnectionCard, selfPresenceStatus, errorMessage, @@ -116,6 +117,7 @@ export function AppSidebar({ onRemoveCommunity, onCreateAgent, onSelectAgents, + onSelectProjects, onSelectPulse, onSelectWorkflows, onSelectHome, @@ -542,8 +544,10 @@ export function AppSidebar({ homeBadgeCount={homeBadgeCount} onSelectAgents={onSelectAgents} onSelectHome={onSelectHome} + onSelectProjects={onSelectProjects} onSelectPulse={onSelectPulse} onSelectWorkflows={onSelectWorkflows} + projectsOverviewActive={projectsOverviewActive} selectedView={selectedView} /> diff --git a/desktop/src/features/sidebar/ui/AppSidebar.types.ts b/desktop/src/features/sidebar/ui/AppSidebar.types.ts index c5c93b7b84..ab35b8b598 100644 --- a/desktop/src/features/sidebar/ui/AppSidebar.types.ts +++ b/desktop/src/features/sidebar/ui/AppSidebar.types.ts @@ -32,6 +32,7 @@ export type AppSidebarProps = { isCreatingChannel: boolean; isCreatingForum: boolean; profile?: Profile; + projectsOverviewActive: boolean; relayConnectionCard: ReturnType; selfPresenceStatus: PresenceStatus; errorMessage?: string; @@ -82,6 +83,7 @@ export type AppSidebarProps = { onRemoveCommunity: (id: string) => Promise; onCreateAgent: () => void; onSelectAgents: () => void; + onSelectProjects: () => void; onSelectPulse: () => void; onSelectWorkflows: () => void; onSelectHome: () => void; diff --git a/desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx b/desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx index f1a76824a0..cad45ca405 100644 --- a/desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx +++ b/desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx @@ -1,4 +1,4 @@ -import { Activity, Bot, Inbox, Zap } from "lucide-react"; +import { Activity, Bot, Folders, Inbox, Zap } from "lucide-react"; import { TopbarSearch } from "@/features/search/ui/TopbarSearch"; import { FeatureGate } from "@/shared/features"; @@ -41,8 +41,10 @@ type AppSidebarPrimaryMenuProps = { homeBadgeCount: number; onSelectAgents: () => void; onSelectHome: () => void; + onSelectProjects: () => void; onSelectPulse: () => void; onSelectWorkflows: () => void; + projectsOverviewActive: boolean; selectedView: SidebarSelectedView; }; @@ -89,8 +91,10 @@ export function AppSidebarPrimaryMenu({ homeBadgeCount, onSelectAgents, onSelectHome, + onSelectProjects, onSelectPulse, onSelectWorkflows, + projectsOverviewActive, selectedView, }: AppSidebarPrimaryMenuProps) { return ( @@ -134,6 +138,18 @@ export function AppSidebarPrimaryMenu({ + + + + Projects + + (await acceptedProjectEvents(page)).length) .toBe(0); - await expect(page.getByTestId("open-projects-view")).toHaveCount(0); + await expect(page.getByTestId("open-projects-view")).toBeVisible(); const projectsSection = page.getByTestId("sidebar-projects-section"); await expect(projectsSection).toBeVisible();