diff --git a/desktop/src/app/App.tsx b/desktop/src/app/App.tsx index 4d71da8fa..62a5a5e9f 100644 --- a/desktop/src/app/App.tsx +++ b/desktop/src/app/App.tsx @@ -4,6 +4,7 @@ import { RouterProvider } from "@tanstack/react-router"; import { useCallback, useLayoutEffect } from "react"; import { router } from "@/app/router"; +import { UpdaterProvider } from "@/features/settings/hooks/UpdaterProvider"; import { useAppOnboardingState } from "@/features/onboarding/hooks"; import { OnboardingFlow } from "@/features/onboarding/ui/OnboardingFlow"; import { useWorkspaceInit } from "@/features/workspaces/useWorkspaceInit"; @@ -92,5 +93,9 @@ export function App() { return ; } - return ; + return ( + + + + ); } diff --git a/desktop/src/app/AppShell.tsx b/desktop/src/app/AppShell.tsx index 6fc34148d..bd93673da 100644 --- a/desktop/src/app/AppShell.tsx +++ b/desktop/src/app/AppShell.tsx @@ -51,7 +51,6 @@ import { SidebarProvider, SidebarTrigger, } from "@/shared/ui/sidebar"; -import { UpdaterProvider } from "@/features/settings/hooks/UpdaterProvider"; import { UpdateIndicator } from "@/features/settings/UpdateIndicator"; type AppView = "home" | "channel" | "agents" | "workflows" | "pulse"; @@ -453,209 +452,205 @@ export function AppShell() { }, [handleCloseSettings, handleOpenSettings, settingsOpen]); return ( - - - { - setIsChannelManagementOpen(true); - }, - }} - > - -
- -
- - - -
-
- handleOpenSettings("updates")} - /> -
- { - const id = workspacesHook.addWorkspace(workspace); - workspacesHook.switchWorkspace(id); - }} - onAddWorkspaceOpenChange={setIsAddWorkspaceOpen} - onNewDmOpenChange={setIsNewDmOpen} - onOpenAddWorkspace={() => setIsAddWorkspaceOpen(true)} - onUpdateWorkspace={workspacesHook.updateWorkspace} - onRemoveWorkspace={workspacesHook.removeWorkspace} - onSwitchWorkspace={workspacesHook.switchWorkspace} - selfPresenceStatus={presenceSession.currentStatus} - workspaces={workspacesHook.workspaces} - onCreateChannel={async ({ - description, - name, - visibility, - ttlSeconds, - }) => { - const createdChannel = - await createChannelMutation.mutateAsync({ - name, - description, - channelType: "stream", - visibility, - ttlSeconds, - }); - - await goChannel(createdChannel.id); - }} - onCreateForum={async ({ - description, - name, - visibility, - ttlSeconds, - }) => { - const createdForum = await createForumMutation.mutateAsync({ + + { + setIsChannelManagementOpen(true); + }, + }} + > + +
+ +
+ + + +
+
+ handleOpenSettings("updates")} + /> +
+ { + const id = workspacesHook.addWorkspace(workspace); + workspacesHook.switchWorkspace(id); + }} + onAddWorkspaceOpenChange={setIsAddWorkspaceOpen} + onNewDmOpenChange={setIsNewDmOpen} + onOpenAddWorkspace={() => setIsAddWorkspaceOpen(true)} + onUpdateWorkspace={workspacesHook.updateWorkspace} + onRemoveWorkspace={workspacesHook.removeWorkspace} + onSwitchWorkspace={workspacesHook.switchWorkspace} + selfPresenceStatus={presenceSession.currentStatus} + workspaces={workspacesHook.workspaces} + onCreateChannel={async ({ + description, + name, + visibility, + ttlSeconds, + }) => { + const createdChannel = + await createChannelMutation.mutateAsync({ name, description, - channelType: "forum", + channelType: "stream", visibility, ttlSeconds, }); - await goChannel(createdForum.id); - }} - onHideDm={handleHideDm} - onOpenBrowseChannels={handleOpenBrowseChannels} - onOpenBrowseForums={handleOpenBrowseForums} - onOpenDm={async ({ pubkeys }) => { - const directMessage = await openDmMutation.mutateAsync({ - pubkeys, - }); - await goChannel(directMessage.id); - }} - onOpenSearch={handleOpenSearch} - onSelectAgents={() => { - void goAgents(); - }} - onSelectChannel={(channelId) => { - void goChannel(channelId); - }} - onSelectHome={() => { - void goHome(); - }} - onSelectPulse={() => { - void goPulse(); - }} - onSelectSettings={handleOpenSettings} - onSelectWorkflows={() => { - void goWorkflows(); - }} - onSetPresenceStatus={(status) => - presenceSession.setStatus(status) - } - profile={profileQuery.data} - selectedChannelId={selectedChannelId} - selectedView={selectedView} - unreadChannelIds={unreadChannelIds} - /> + await goChannel(createdChannel.id); + }} + onCreateForum={async ({ + description, + name, + visibility, + ttlSeconds, + }) => { + const createdForum = await createForumMutation.mutateAsync({ + name, + description, + channelType: "forum", + visibility, + ttlSeconds, + }); - - - + await goChannel(createdForum.id); + }} + onHideDm={handleHideDm} + onOpenBrowseChannels={handleOpenBrowseChannels} + onOpenBrowseForums={handleOpenBrowseForums} + onOpenDm={async ({ pubkeys }) => { + const directMessage = await openDmMutation.mutateAsync({ + pubkeys, + }); + await goChannel(directMessage.id); + }} + onOpenSearch={handleOpenSearch} + onSelectAgents={() => { + void goAgents(); + }} + onSelectChannel={(channelId) => { + void goChannel(channelId); + }} + onSelectHome={() => { + void goHome(); + }} + onSelectPulse={() => { + void goPulse(); + }} + onSelectSettings={handleOpenSettings} + onSelectWorkflows={() => { + void goWorkflows(); + }} + onSetPresenceStatus={(status) => + presenceSession.setStatus(status) + } + profile={profileQuery.data} + selectedChannelId={selectedChannelId} + selectedView={selectedView} + unreadChannelIds={unreadChannelIds} + /> - { - setIsChannelManagementOpen(false); - void goHome({ replace: true }); - }} - onOpenSearchResult={handleOpenSearchResult} - onSearchOpenChange={setIsSearchOpen} - onSelectChannel={(channelId) => { - void goChannel(channelId); - }} - /> + + + - {settingsOpen ? ( - - - - ) : null} -
- -
-
-
-
- + { + setIsChannelManagementOpen(false); + void goHome({ replace: true }); + }} + onOpenSearchResult={handleOpenSearchResult} + onSearchOpenChange={setIsSearchOpen} + onSelectChannel={(channelId) => { + void goChannel(channelId); + }} + /> + + {settingsOpen ? ( + + + + ) : null} +
+ +
+
+
+
); } diff --git a/desktop/src/features/settings/UpdateChecker.tsx b/desktop/src/features/settings/UpdateChecker.tsx index 3efb5e784..9a35fbace 100644 --- a/desktop/src/features/settings/UpdateChecker.tsx +++ b/desktop/src/features/settings/UpdateChecker.tsx @@ -1,10 +1,8 @@ import { useUpdaterContext } from "./hooks/UpdaterProvider"; import { Button } from "@/shared/ui/button"; -import { Badge } from "@/shared/ui/badge"; export function UpdateChecker() { - const { status, checkForUpdate, downloadAndInstall, relaunch } = - useUpdaterContext(); + const { status, checkForUpdate, relaunch } = useUpdaterContext(); return (
@@ -44,14 +42,7 @@ export function UpdateChecker() { )} {status.state === "available" && ( -
-

- Version {status.version} is available. -

- -
+

Preparing update...

)} {status.state === "downloading" && ( diff --git a/desktop/src/features/settings/UpdateIndicator.tsx b/desktop/src/features/settings/UpdateIndicator.tsx index 7c9590053..3aed48ab5 100644 --- a/desktop/src/features/settings/UpdateIndicator.tsx +++ b/desktop/src/features/settings/UpdateIndicator.tsx @@ -1,4 +1,4 @@ -import { Download, RefreshCw } from "lucide-react"; +import { Download, Loader2, RefreshCw } from "lucide-react"; import { Button } from "@/shared/ui/button"; @@ -11,23 +11,47 @@ const indicatorButtonClass = const iconClass = "h-3.5 w-3.5"; const variants: Record< - "available" | "ready", - { Icon: typeof Download; label: string; badgeColor: string } + "available" | "downloading" | "installing" | "ready", + { + Icon: typeof Download; + label: string; + badgeColor: string; + iconClass: string; + } > = { available: { Icon: Download, label: "Update available", badgeColor: "bg-primary", + iconClass: iconClass, + }, + downloading: { + Icon: Loader2, + label: "Downloading update\u2026", + badgeColor: "bg-primary", + iconClass: `${iconClass} animate-spin`, + }, + installing: { + Icon: Loader2, + label: "Installing update\u2026", + badgeColor: "bg-primary", + iconClass: `${iconClass} animate-spin`, }, ready: { Icon: RefreshCw, label: "Restart to update", badgeColor: "bg-emerald-500", + iconClass: iconClass, }, }; function getVariant(state: UpdateStatus["state"]) { - if (state === "available" || state === "ready") { + if ( + state === "available" || + state === "downloading" || + state === "installing" || + state === "ready" + ) { return variants[state]; } return null; @@ -45,7 +69,7 @@ export function UpdateIndicator({ return null; } - const { Icon, label, badgeColor } = variant; + const { Icon, label, badgeColor, iconClass: variantIconClass } = variant; return (