diff --git a/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx index 3a1da85f251..7cfaf37f7f2 100644 --- a/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx @@ -60,7 +60,7 @@ export const ResponseHistoryDropdown = ({ const handleDeleteResponses = useCallback(async () => { if (isWebSocketResponse(activeResponse)) { - window.main.webSocket.closeAll(); + window.main.webSocket.close({ requestId }); } fetcher.submit({}, { action: `/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/debug/request/${requestId}/response/delete-all`, diff --git a/packages/insomnia/src/ui/components/environment-picker.tsx b/packages/insomnia/src/ui/components/environment-picker.tsx index 222aad93d89..ad010078412 100644 --- a/packages/insomnia/src/ui/components/environment-picker.tsx +++ b/packages/insomnia/src/ui/components/environment-picker.tsx @@ -5,6 +5,7 @@ import { useFetcher, useNavigate, useParams, useRouteLoaderData } from 'react-ro import { fuzzyMatch } from '../../common/misc'; import { isRemoteProject } from '../../models/project'; +import uiEventBus from '../eventBus'; import { useOrganizationPermissions } from '../hooks/use-organization-features'; import type { WorkspaceLoaderData } from '../routes/workspace'; import { Icon } from './icon'; @@ -239,7 +240,6 @@ export const EnvironmentPicker = ({ return; } const [environmentId] = keys.values(); - setActiveEnvironmentFetcher.submit( { environmentId, @@ -249,6 +249,7 @@ export const EnvironmentPicker = ({ action: `/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/environment/set-active`, } ); + uiEventBus.emit('CHANGE_ACTIVE_ENV', workspaceId); }} className="p-2 select-none text-sm overflow-y-auto focus:outline-none" > diff --git a/packages/insomnia/src/ui/context/app/runner-context.tsx b/packages/insomnia/src/ui/context/app/runner-context.tsx index d3646cc5dfa..ff9e8052901 100644 --- a/packages/insomnia/src/ui/context/app/runner-context.tsx +++ b/packages/insomnia/src/ui/context/app/runner-context.tsx @@ -2,7 +2,7 @@ import React, { createContext, type FC, type PropsWithChildren, useCallback, use import type { Selection } from 'react-aria-components'; import type { UploadDataType } from '../../components/modals/upload-runner-data-modal'; -import uiEventBus, { UIEventType } from '../../eventBus'; +import uiEventBus from '../../eventBus'; import useStateRef from '../../hooks/use-state-ref'; import type { RequestRow } from '../../routes/runner'; diff --git a/packages/insomnia/src/ui/eventBus.ts b/packages/insomnia/src/ui/eventBus.ts index 973c6819347..deb6104346a 100644 --- a/packages/insomnia/src/ui/eventBus.ts +++ b/packages/insomnia/src/ui/eventBus.ts @@ -1,10 +1,10 @@ type EventHandler = (...args: any[]) => void; -type UIEventType = 'CLOSE_TAB'; - +type UIEventType = 'CLOSE_TAB' | 'CHANGE_ACTIVE_ENV'; class EventBus { private events: Record = { CLOSE_TAB: [], + CHANGE_ACTIVE_ENV: [], }; // Subscribe to event diff --git a/packages/insomnia/src/ui/hooks/use-close-connection.ts b/packages/insomnia/src/ui/hooks/use-close-connection.ts new file mode 100644 index 00000000000..ce08e153b41 --- /dev/null +++ b/packages/insomnia/src/ui/hooks/use-close-connection.ts @@ -0,0 +1,71 @@ +import { useCallback, useEffect } from 'react'; + +import * as models from '../../models'; +import { isGrpcRequestId } from '../../models/grpc-request'; +import { isEventStreamRequest, isGraphqlSubscriptionRequest, isRequestId } from '../../models/request'; +import { isWebSocketRequestId } from '../../models/websocket-request'; +import { useInsomniaTabContext } from '../context/app/insomnia-tab-context'; +import uiEventBus from '../eventBus'; + +// this hook is use for control when to close connections(websocket & SSE & grpc stream & graphql subscription) +export const useCloseConnection = ({ organizationId }: { organizationId: string }) => { + + const closeConnectionById = async (id: string) => { + if (isGrpcRequestId(id)) { + window.main.grpc.cancel(id); + } else if (isWebSocketRequestId(id)) { + window.main.webSocket.close({ requestId: id }); + } else if (isRequestId(id)) { + const request = await models.request.getById(id); + if (request && isEventStreamRequest(request)) { + window.main.curl.close({ requestId: id }); + } else if (request && isGraphqlSubscriptionRequest(request)) { + window.main.webSocket.close({ requestId: id }); + } + } + }; + + // close websocket&grpc&SSE connections + const handleTabClose = useCallback((_: string, ids: 'all' | string[]) => { + if (ids === 'all') { + window.main.webSocket.closeAll(); + window.main.grpc.closeAll(); + window.main.curl.closeAll(); + return; + } + + ids.forEach(async id => { + await closeConnectionById(id); + }); + }, []); + + const { currentOrgTabs } = useInsomniaTabContext(); + + const handleActiveEnvironmentChange = useCallback((workspaceId: string) => { + const { tabList } = currentOrgTabs; + const tabs = tabList.filter(tab => tab.workspaceId === workspaceId); + tabs.forEach(async tab => { + const id = tab.id; + await closeConnectionById(id); + }); + }, [currentOrgTabs]); + + useEffect(() => { + uiEventBus.on('CLOSE_TAB', handleTabClose); + uiEventBus.on('CHANGE_ACTIVE_ENV', handleActiveEnvironmentChange); + + return () => { + uiEventBus.off('CLOSE_TAB', handleTabClose); + uiEventBus.off('CHANGE_ACTIVE_ENV', handleActiveEnvironmentChange); + }; + }, [handleTabClose, handleActiveEnvironmentChange]); + + // close all connections when organizationId change + useEffect(() => { + return () => { + window.main.webSocket.closeAll(); + window.main.grpc.closeAll(); + window.main.curl.closeAll(); + }; + }, [organizationId]); +}; diff --git a/packages/insomnia/src/ui/routes/debug.tsx b/packages/insomnia/src/ui/routes/debug.tsx index f29d70a797c..058d74783c0 100644 --- a/packages/insomnia/src/ui/routes/debug.tsx +++ b/packages/insomnia/src/ui/routes/debug.tsx @@ -105,6 +105,7 @@ import { getMethodShortHand } from '../components/tags/method-tag'; import { RealtimeResponsePane } from '../components/websockets/realtime-response-pane'; import { WebSocketRequestPane } from '../components/websockets/websocket-request-pane'; import { INSOMNIA_TAB_HEIGHT } from '../constant'; +import { useCloseConnection } from '../hooks/use-close-connection'; import { useExecutionState } from '../hooks/use-execution-state'; import { useInsomniaTab } from '../hooks/use-insomnia-tab'; import { useReadyState } from '../hooks/use-ready-state'; @@ -452,13 +453,10 @@ export const Debug: FC = () => { } }, }); - // Close all websocket connections when the active environment changes - useEffect(() => { - return () => { - window.main.webSocket.closeAll(); - window.main.grpc.closeAll(); - }; - }, [activeEnvironment?._id]); + + useCloseConnection({ + organizationId, + }); const isRealtimeRequest = activeRequest &&