diff --git a/packages/insomnia-smoke-test/tests/websocket.test.ts b/packages/insomnia-smoke-test/tests/websocket.test.ts index 590221ba04..25eac87b3c 100644 --- a/packages/insomnia-smoke-test/tests/websocket.test.ts +++ b/packages/insomnia-smoke-test/tests/websocket.test.ts @@ -49,4 +49,13 @@ test('can make websocket connection', async ({ app, page }) => { await page.click('[data-testid="response-pane"] >> [role="tab"]:has-text("Timeline")'); await expect(responseBody).toContainText('WebSocket connection established'); + const webSocketActiveConnections = page.locator('[data-testid="WebSocketSpinner__Connected"]'); + + // Basic auth, Bearer auth, and Redirect connections are displayed as open + await expect(webSocketActiveConnections).toHaveCount(3); + + // Can disconnect from all connections + await page.locator('button[name="DisconnectDropdown__DropdownButton"]').click(); + await page.locator('text=Disconnect all requests').click(); + await expect(webSocketActiveConnections).toHaveCount(0); }); diff --git a/packages/insomnia/src/ui/components/base/dropdown/dropdown-item.tsx b/packages/insomnia/src/ui/components/base/dropdown/dropdown-item.tsx index 93d01b8345..58af3867fc 100644 --- a/packages/insomnia/src/ui/components/base/dropdown/dropdown-item.tsx +++ b/packages/insomnia/src/ui/components/base/dropdown/dropdown-item.tsx @@ -1,6 +1,6 @@ import { autoBindMethodsForReact } from 'class-autobind-decorator'; import classnames from 'classnames'; -import React, { createElement, PureComponent, ReactNode } from 'react'; +import React, { ButtonHTMLAttributes, createElement, PureComponent, ReactNode } from 'react'; import { AUTOBIND_CFG } from '../../../../common/constants'; @@ -15,6 +15,7 @@ interface Props { children: ReactNode; className?: string; color?: string; + unsetStyles?: boolean; } @autoBindMethodsForReact(AUTOBIND_CFG) @@ -44,6 +45,7 @@ export class DropdownItem extends PureComponent { className, color, onClick, + unsetStyles, // eslint-disable-line @typescript-eslint/no-unused-vars stayOpenAfterClick, // eslint-disable-line @typescript-eslint/no-unused-vars @@ -61,11 +63,16 @@ export class DropdownItem extends PureComponent { ); - const buttonProps = { + const buttonProps: ButtonHTMLAttributes = { type: 'button', onClick: this._handleClick, ...props, }; + + if (unsetStyles) { + buttonProps.className = 'dropdown__item-button-unset'; + } + return createElement(buttonClass || 'button', buttonProps, inner); } } diff --git a/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx b/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx index 7e6fa17daf..4b2f786a15 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx @@ -337,5 +337,5 @@ export const SidebarRequestRow = DropTarget('SIDEBAR_REQUEST_ROW', dragTarget, t const WebSocketSpinner = ({ requestId }: { requestId: string }) => { const readyState = useWSReadyState(requestId); - return readyState === ReadyState.OPEN ? : null; + return readyState === ReadyState.OPEN ? : null; }; diff --git a/packages/insomnia/src/ui/components/websockets/action-bar.tsx b/packages/insomnia/src/ui/components/websockets/action-bar.tsx index 5813a76560..f258a8d0ee 100644 --- a/packages/insomnia/src/ui/components/websockets/action-bar.tsx +++ b/packages/insomnia/src/ui/components/websockets/action-bar.tsx @@ -10,6 +10,7 @@ import { OneLineEditor } from '../codemirror/one-line-editor'; import { useDocBodyKeyboardShortcuts } from '../keydown-binder'; import { showAlert, showModal } from '../modals'; import { RequestRenderErrorModal } from '../modals/request-render-error-modal'; +import { DisconnectButton } from './disconnect-button'; const Button = styled.button<{ warning?: boolean }>(({ warning }) => ({ paddingRight: 'var(--padding-md)', @@ -161,7 +162,7 @@ export const WebSocketActionBar: FC = ({ request, workspaceId, e {isConnectingOrClosed ? - : } + : } ); diff --git a/packages/insomnia/src/ui/components/websockets/disconnect-button.tsx b/packages/insomnia/src/ui/components/websockets/disconnect-button.tsx new file mode 100644 index 0000000000..265012e641 --- /dev/null +++ b/packages/insomnia/src/ui/components/websockets/disconnect-button.tsx @@ -0,0 +1,100 @@ +import React, { FC, useRef } from 'react'; +import styled from 'styled-components'; + +import { Dropdown as OriginalDropdown, DropdownHandle } from '../base/dropdown/dropdown'; +import { DROPDOWN_BUTTON_DISPLAY_NAME, DropdownButton as OriginalDropdownButton } from '../base/dropdown/dropdown-button'; +import { DropdownItem } from '../base/dropdown/dropdown-item'; + +const SplitButton = styled.div({ + display: 'flex', + color: 'var(--color-font-surprise)', +}); +const Dropdown = styled(OriginalDropdown)({ + display: 'flex', + textAlign: 'center', + borderLeft: '1px solid var(--hl-md)', + background: 'var(--color-danger)', + ':hover': { + opacity: 0.9, + }, +}); +const DropdownButton = styled(OriginalDropdownButton)({ + paddingRight: 'var(--padding-xs)', + paddingLeft: 'var(--padding-xs)', +}); +DropdownButton.displayName = DROPDOWN_BUTTON_DISPLAY_NAME; +const ActionButton = styled.button({ + paddingRight: 'var(--padding-md)', + paddingLeft: 'var(--padding-md)', + background: 'var(--color-danger)', + ':hover': { + opacity: 0.9, + }, +}); +const Connections = styled.div({ + display: 'flex', + justifyContent: 'space-evenly', + width: 25, +}); +const Connection = styled.div<{ size?: number }>(({ size = 10 }) => ({ + borderRadius: '50%', + width: size, + height: size, + background: 'var(--color-success)', +})); +const TextWrapper = styled.div({ + textAlign: 'left', + width: '100%', + paddingLeft: 'var(--padding-xs)', +}); + +export const DisconnectButton: FC<{ requestId: string }> = ({ requestId }) => { + const dropdownRef = useRef(); + const handleCloseThisRequest = () => { + window.main.webSocket.close({ requestId }); + }; + const handleCloseAllRequests = () => { + window.main.webSocket.closeAll(); + }; + return ( + + + Disconnect + + + dropdownRef.current?.show()} + > + + + + + + + + Disconnect this request + + + + + + + + + + Disconnect all requests + + + + + ); +}; diff --git a/packages/insomnia/src/ui/css/components/dropdown.less b/packages/insomnia/src/ui/css/components/dropdown.less index 4eee85f3b8..49f31c8e61 100644 --- a/packages/insomnia/src/ui/css/components/dropdown.less +++ b/packages/insomnia/src/ui/css/components/dropdown.less @@ -126,7 +126,12 @@ text-align: center; } } - + li > .dropdown__item-button-unset { + min-width: unset; + .dropdown__text > * { + margin-left: unset; + } + } li > button:hover:not(:disabled), li.active > button:not(:disabled) { background: var(--hl-sm);