diff --git a/desktop/src/features/agents/lib/sortProviders.ts b/desktop/src/features/agents/lib/sortProviders.ts new file mode 100644 index 0000000000..2bc9f3fd8b --- /dev/null +++ b/desktop/src/features/agents/lib/sortProviders.ts @@ -0,0 +1,18 @@ +import type { AcpProvider } from "@/shared/api/types"; + +/** + * Sort ACP providers with "goose" first, then alphabetically by label. + * Used by any surface that presents a provider list to the user. + */ +export function sortProviders( + providers: readonly AcpProvider[], +): AcpProvider[] { + return [...providers].sort((left, right) => { + const leftPriority = left.id === "goose" ? 0 : 1; + const rightPriority = right.id === "goose" ? 0 : 1; + if (leftPriority !== rightPriority) { + return leftPriority - rightPriority; + } + return left.label.localeCompare(right.label); + }); +} diff --git a/desktop/src/features/channels/ui/ChannelMembersBar.tsx b/desktop/src/features/channels/ui/ChannelMembersBar.tsx index 500685f23f..8017924c31 100644 --- a/desktop/src/features/channels/ui/ChannelMembersBar.tsx +++ b/desktop/src/features/channels/ui/ChannelMembersBar.tsx @@ -9,6 +9,7 @@ import { useManagedAgentsQuery, useRelayAgentsQuery, } from "@/features/agents/hooks"; +import { sortProviders } from "@/features/agents/lib/sortProviders"; import { useChannelMembersQuery } from "@/features/channels/hooks"; import type { Channel } from "@/shared/api/types"; import { normalizePubkey } from "@/shared/lib/pubkey"; @@ -19,6 +20,8 @@ import { QuickAddAgentPopover } from "./QuickAddAgentPopover"; type ChannelMembersBarProps = { channel: Channel; currentPubkey?: string; + isAddBotDialogOpen?: boolean; + onAddBotDialogOpenChange?: (open: boolean) => void; onManageChannel: () => void; onToggleMembers: () => void; }; @@ -26,10 +29,15 @@ type ChannelMembersBarProps = { export function ChannelMembersBar({ channel, currentPubkey, + isAddBotDialogOpen, + onAddBotDialogOpenChange, onManageChannel, onToggleMembers, }: ChannelMembersBarProps) { - const [isAddBotOpen, setIsAddBotOpen] = React.useState(false); + // Dialog state: controlled externally if props provided, otherwise local. + const [localIsAddBotOpen, setLocalIsAddBotOpen] = React.useState(false); + const isAddBotOpen = isAddBotDialogOpen ?? localIsAddBotOpen; + const setIsAddBotOpen = onAddBotDialogOpenChange ?? setLocalIsAddBotOpen; const [isQuickAddOpen, setIsQuickAddOpen] = React.useState(false); const { startHuddle, isStarting: isStartingHuddle } = useHuddle(); const queryClient = useQueryClient(); @@ -41,16 +49,7 @@ export function ChannelMembersBar({ const members = membersQuery.data ?? []; const memberCount = membersQuery.data?.length ?? channel.memberCount; const providers = React.useMemo( - () => - [...(providersQuery.data ?? [])].sort((left, right) => { - const leftPriority = left.id === "goose" ? 0 : 1; - const rightPriority = right.id === "goose" ? 0 : 1; - if (leftPriority !== rightPriority) { - return leftPriority - rightPriority; - } - - return left.label.localeCompare(right.label); - }), + () => sortProviders(providersQuery.data ?? []), [providersQuery.data], ); const normalizedCurrentPubkey = currentPubkey @@ -76,7 +75,7 @@ export function ChannelMembersBar({ previousChannelIdRef.current = channel.id; setIsAddBotOpen(false); setIsQuickAddOpen(false); - }, [channel.id]); + }, [channel.id, setIsAddBotOpen]); const dialogErrorMessage = providersQuery.error instanceof Error diff --git a/desktop/src/features/channels/ui/ChannelScreen.tsx b/desktop/src/features/channels/ui/ChannelScreen.tsx index 64902e2fe0..870e4bead2 100644 --- a/desktop/src/features/channels/ui/ChannelScreen.tsx +++ b/desktop/src/features/channels/ui/ChannelScreen.tsx @@ -85,6 +85,7 @@ export function ChannelScreen({ string | null >(null); const [isMembersSidebarOpen, setIsMembersSidebarOpen] = React.useState(false); + const [isAddBotDialogOpen, setIsAddBotDialogOpen] = React.useState(false); const [openThreadHeadId, setOpenThreadHeadId] = React.useState( null, ); @@ -436,7 +437,9 @@ export function ChannelScreen({ activeChannelTitle={activeChannelTitle} activeDmPresenceStatus={activeDmPresenceStatus} currentPubkey={currentPubkey} + isAddBotDialogOpen={isAddBotDialogOpen} isJoining={joinChannelMutation.isPending} + onAddBotDialogOpenChange={setIsAddBotDialogOpen} onJoinChannel={joinChannelMutation.mutateAsync} onManageChannel={openChannelManagement} onToggleMembers={() => setIsMembersSidebarOpen((prev) => !prev)} @@ -530,6 +533,7 @@ export function ChannelScreen({ currentPubkey={currentPubkey} open={isMembersSidebarOpen} onOpenChange={setIsMembersSidebarOpen} + onOpenAddBotDialog={() => setIsAddBotDialogOpen(true)} onViewActivity={handleOpenAgentSession} /> diff --git a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx index 4673aa5586..23338b22a2 100644 --- a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx +++ b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx @@ -14,7 +14,9 @@ type ChannelScreenHeaderProps = { activeChannelTitle: string; activeDmPresenceStatus: PresenceStatus | null; currentPubkey?: string; + isAddBotDialogOpen?: boolean; isJoining?: boolean; + onAddBotDialogOpenChange?: (open: boolean) => void; onJoinChannel?: () => Promise; onManageChannel: () => void; onToggleMembers: () => void; @@ -26,7 +28,9 @@ export function ChannelScreenHeader({ activeChannelTitle, activeDmPresenceStatus, currentPubkey, + isAddBotDialogOpen, isJoining = false, + onAddBotDialogOpenChange, onJoinChannel, onManageChannel, onToggleMembers, @@ -56,6 +60,8 @@ export function ChannelScreenHeader({ diff --git a/desktop/src/features/channels/ui/MembersSidebar.tsx b/desktop/src/features/channels/ui/MembersSidebar.tsx index 36b0b64fe2..f3fa835926 100644 --- a/desktop/src/features/channels/ui/MembersSidebar.tsx +++ b/desktop/src/features/channels/ui/MembersSidebar.tsx @@ -5,13 +5,7 @@ import { useAddChannelMembersMutation, useChannelMembersQuery, } from "@/features/channels/hooks"; -import { - useAcpProvidersQuery, - useBackendProvidersQuery, - useManagedAgentsQuery, - useRelayAgentsQuery, - useUpdateManagedAgentMutation, -} from "@/features/agents/hooks"; +import { useUpdateManagedAgentMutation } from "@/features/agents/hooks"; import { CreateAgentRespondToField } from "@/features/agents/ui/RespondToField"; import { useClassifiedMembers } from "@/features/channels/lib/useClassifiedMembers"; import { @@ -48,7 +42,6 @@ import { MembersSidebarAgentControls } from "./MembersSidebarAgentControls"; import { ChannelMemberInviteCard } from "./ChannelMemberInviteCard"; import { MembersSidebarMemberCard } from "./MembersSidebarMemberCard"; import { useMembersSidebarActions } from "./useMembersSidebarActions"; -import { AddChannelBotDialog } from "./AddChannelBotDialog"; import { QuickAddAgentPopover } from "./QuickAddAgentPopover"; type MembersSidebarProps = { @@ -56,6 +49,7 @@ type MembersSidebarProps = { currentPubkey?: string; open: boolean; onOpenChange: (open: boolean) => void; + onOpenAddBotDialog?: () => void; onViewActivity?: (pubkey: string) => void; }; @@ -64,37 +58,13 @@ export function MembersSidebar({ currentPubkey, open, onOpenChange, + onOpenAddBotDialog, onViewActivity, }: MembersSidebarProps) { const channelId = channel?.id ?? null; const queryClient = useQueryClient(); const membersQuery = useChannelMembersQuery(channelId, open); const addMembersMutation = useAddChannelMembersMutation(channelId); - const providersQuery = useAcpProvidersQuery(); - const backendProvidersQuery = useBackendProvidersQuery(); - const sidebarManagedAgentsQuery = useManagedAgentsQuery(); - const sidebarRelayAgentsQuery = useRelayAgentsQuery(); - const sidebarProviders = React.useMemo( - () => - [...(providersQuery.data ?? [])].sort((left, right) => { - const leftPriority = left.id === "goose" ? 0 : 1; - const rightPriority = right.id === "goose" ? 0 : 1; - if (leftPriority !== rightPriority) { - return leftPriority - rightPriority; - } - return left.label.localeCompare(right.label); - }), - [providersQuery.data], - ); - const sidebarDialogErrorMessage = - providersQuery.error instanceof Error - ? providersQuery.error.message - : sidebarManagedAgentsQuery.error instanceof Error - ? sidebarManagedAgentsQuery.error.message - : sidebarRelayAgentsQuery.error instanceof Error - ? sidebarRelayAgentsQuery.error.message - : null; - const changeRoleMutation = useMutation({ mutationFn: async ({ pubkey, role }: { pubkey: string; role: string }) => { if (!channelId) throw new Error("No channel selected."); @@ -139,8 +109,6 @@ export function MembersSidebar({ const [isSidebarQuickAddOpen, setIsSidebarQuickAddOpen] = React.useState(false); - const [isSidebarFullDialogOpen, setIsSidebarFullDialogOpen] = - React.useState(false); const managedAgentByPubkey = React.useMemo( () => @@ -347,7 +315,10 @@ export function MembersSidebar({ channelId={channelId} open={isSidebarQuickAddOpen} onOpenChange={setIsSidebarQuickAddOpen} - onMoreOptions={() => setIsSidebarFullDialogOpen(true)} + onMoreOptions={() => { + setIsSidebarQuickAddOpen(false); + onOpenAddBotDialog?.(); + }} >