diff --git a/packages/insomnia/src/ui/components/dropdowns/sidebar-shortcut-actions-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/sidebar-shortcut-actions-dropdown.tsx index b2f4452e19..8cf538313b 100644 --- a/packages/insomnia/src/ui/components/dropdowns/sidebar-shortcut-actions-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/sidebar-shortcut-actions-dropdown.tsx @@ -26,9 +26,17 @@ interface Props { isOpen: boolean; onOpenChange: (isOpen: boolean) => void; triggerRef: RefObject; + onWorkspaceCreated: (workspaceId: string) => void; } -export const SidebarShortcutActionsDropdown = ({ target, storageRules, isOpen, onOpenChange, triggerRef }: Props) => { +export const SidebarShortcutActionsDropdown = ({ + target, + storageRules, + isOpen, + onOpenChange, + triggerRef, + onWorkspaceCreated, +}: Props) => { const [newWorkspaceModalState, setNewWorkspaceModalState] = useState<{ scope: 'collection' | 'design' | 'mock-server' | 'environment' | 'mcp'; isOpen: boolean; @@ -166,6 +174,8 @@ export const SidebarShortcutActionsDropdown = ({ target, storageRules, isOpen, o project={target.kind === 'project' ? target.doc : target.project} storageRules={storageRules} scope={newWorkspaceModalState.scope} + redirectAfterCreate={false} + onCreateWorkspace={onWorkspaceCreated} onOpenChange={nextIsOpen => { setNewWorkspaceModalState(prev => prev diff --git a/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/components/index.ts b/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/components/index.ts new file mode 100644 index 0000000000..107576960d --- /dev/null +++ b/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/components/index.ts @@ -0,0 +1,3 @@ +export { NewProjectButton } from './new-project-button'; +export { SidebarSearchField } from './sidebar-search-field'; +export { SideBarTabList } from './sidebar-tab-list'; diff --git a/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/components/new-project-button.tsx b/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/components/new-project-button.tsx new file mode 100644 index 0000000000..ac50dd6480 --- /dev/null +++ b/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/components/new-project-button.tsx @@ -0,0 +1,15 @@ +import { Button as BasicButton } from '~/basic-components/button'; + +import { Icon } from '../../../icon'; + +export const NewProjectButton = ({ onPress, isDisabled }: { onPress: () => void; isDisabled?: boolean }) => ( + + + New Project + +); diff --git a/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/project-navigation-sidebar.tsx b/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/project-navigation-sidebar.tsx index 27bf82f6ec..94f4d3d48d 100644 --- a/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/project-navigation-sidebar.tsx +++ b/packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/project-navigation-sidebar.tsx @@ -16,7 +16,7 @@ import { useState, } from 'react'; import { Button, GridList, GridListItem, type Selection, Tabs, Tooltip, TooltipTrigger } from 'react-aria-components'; -import { useNavigate, useParams, useSearchParams } from 'react-router'; +import { href, useNavigate, useParams, useSearchParams } from 'react-router'; import * as reactUse from 'react-use'; import { Button as BasicButton } from '~/basic-components/button'; @@ -33,14 +33,17 @@ import { AnalyticsEvent } from '~/ui/analytics'; import type { WorkspaceSortOrder } from '~/ui/components/dropdowns/sidebar-project-dropdown'; import { SidebarShortcutActionsDropdown } from '~/ui/components/dropdowns/sidebar-shortcut-actions-dropdown'; import { SidebarWorkspaceDropdown } from '~/ui/components/dropdowns/sidebar-workspace-dropdown'; -import { useDocBodyKeyboardShortcuts } from '~/ui/components/keydown-binder'; +import { useKeyboardShortcuts } from '~/ui/components/keydown-binder'; import { KongLogo } from '~/ui/components/kong-logo'; import { showModal } from '~/ui/components/modals'; import { AlertModal } from '~/ui/components/modals/alert-modal'; import { AskModal } from '~/ui/components/modals/ask-modal'; import { KonnectSettingsModal } from '~/ui/components/modals/konnect-settings-modal'; -import { SidebarSearchField } from '~/ui/components/sidebar/project-navigation-sidebar/components/sidebar-search-field'; -import { SideBarTabList } from '~/ui/components/sidebar/project-navigation-sidebar/components/sidebar-tab-list'; +import { + NewProjectButton, + SidebarSearchField, + SideBarTabList, +} from '~/ui/components/sidebar/project-navigation-sidebar/components'; import { EmptyNode } from '~/ui/components/sidebar/project-navigation-sidebar/empty-node'; import { KonnectEnvOnboarding } from '~/ui/components/sidebar/project-navigation-sidebar/konnect-env-onboarding'; import { KonnectSyncIntro } from '~/ui/components/sidebar/project-navigation-sidebar/konnect-sync-intro/konnect-sync-intro'; @@ -101,17 +104,11 @@ function getRelativeTimeString(timestamp: number, now: number = Date.now()): str return `${days}d ${hours % 24}h ago`; } -const NewProjectButton = ({ onPress, isDisabled }: { onPress: () => void; isDisabled?: boolean }) => ( - - - New Project - -); +const workspaceManualSortMethod = (a: Workspace, b: Workspace, localOrder: string[]) => { + const aIndex = localOrder.indexOf(a._id); + const bIndex = localOrder.indexOf(b._id); + return (aIndex === -1 ? Infinity : aIndex) - (bIndex === -1 ? Infinity : bIndex); +}; const ProjectNavigationSidebarInner = ( { storageRules, konnectSyncEnabled, onCreateProject, activeTab, setActiveTab }: ProjectNavigationSidebarProps, @@ -431,16 +428,9 @@ const ProjectNavigationSidebarInner = ( let sortedWorkspaces: Workspace[] = []; if (workspaceOrder === 'type-manual') { const localOrder = localWorkspaceOrders?.[projectId]; - if (localOrder) { - const orderIndexByWorkspaceId = new Map(localOrder.map((workspaceId, index) => [workspaceId, index])); - sortedWorkspaces = [...workspaces].sort((a, b) => { - const ai = orderIndexByWorkspaceId.get(a._id) ?? Infinity; - const bi = orderIndexByWorkspaceId.get(b._id) ?? Infinity; - return ai - bi; - }); - } else { - sortedWorkspaces = [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b)); - } + sortedWorkspaces = localOrder + ? [...workspaces].sort((a, b) => workspaceManualSortMethod(a, b, localOrder)) + : [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b)); } else { sortedWorkspaces = [...workspaces].sort((a, b) => sortMethodMap[workspaceOrder](a, b)); } @@ -712,11 +702,13 @@ const ProjectNavigationSidebarInner = ( const workspaces = organizationWorkspaces.filter(w => w.parentId === targetProjectId); const currentWorkspaceSortOrder = projectWorkspaceSortOrder[targetProjectId] || 'type-manual'; // Get the base order of workspace before re-order + const localOrder = localWorkspaceOrders?.[targetProjectId]; const baseOrder = // if current order is manual, use the current order in local state or default sort by created time; otherwise use current order to sort currentWorkspaceSortOrder === 'type-manual' - ? localWorkspaceOrders?.[targetProjectId] || - [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b)).map(w => w._id) + ? localOrder + ? [...workspaces].sort((a, b) => workspaceManualSortMethod(a, b, localOrder)).map(w => w._id) + : [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b)).map(w => w._id) : [...workspaces].sort((a, b) => sortMethodMap[currentWorkspaceSortOrder](a, b)).map(w => w._id); const reordered = (baseOrder as string[]).filter((id: string) => id !== draggedId); @@ -997,7 +989,7 @@ const ProjectNavigationSidebarInner = ( } }, [selectedItemId]); - useDocBodyKeyboardShortcuts({ + useKeyboardShortcuts(() => parentRef.current!, { sidebar_showCreateDropdown: event => { if (!isProjectTabActive) { return; @@ -1204,32 +1196,32 @@ const ProjectNavigationSidebarInner = ( ) : (
{syncing ? ( - - ) : ( - - - - - - )} + ) : ( + + + + + + + )}
{shortcutTargetItem && ( @@ -1484,6 +1478,19 @@ const ProjectNavigationSidebarInner = ( isOpen={isShortcutCreateOpen} onOpenChange={setIsShortcutCreateOpen} triggerRef={shortcutCreateTriggerRef} + onWorkspaceCreated={newWorkspaceId => { + const projectId = + shortcutTargetItem.kind === 'workspace' ? shortcutTargetItem.project._id : shortcutTargetItem.doc._id; + const targetWorkspaceId = shortcutTargetItem.kind === 'workspace' ? shortcutTargetItem.doc._id : null; + handleLocalWorkspaceReorder(projectId, projectId, newWorkspaceId, targetWorkspaceId, 'after'); + return navigate( + href(`/organization/:organizationId/project/:projectId/workspace/:workspaceId`, { + organizationId, + projectId, + workspaceId: newWorkspaceId, + }), + ); + }} /> )} diff --git a/packages/insomnia/src/ui/context/app/insomnia-app-data-context.tsx b/packages/insomnia/src/ui/context/app/insomnia-app-data-context.tsx index 4ee485470f..c2dd6b7cf9 100644 --- a/packages/insomnia/src/ui/context/app/insomnia-app-data-context.tsx +++ b/packages/insomnia/src/ui/context/app/insomnia-app-data-context.tsx @@ -11,6 +11,7 @@ const createAppDataQueryClient = () => staleTime: Infinity, gcTime: Infinity, retry: false, + // Local database queries should always be fetched regardless of network connectivity. networkMode: 'always', }, },