diff --git a/packages/insomnia/src/models/helpers/request-operations.ts b/packages/insomnia/src/models/helpers/request-operations.ts index d0b9b3c9eb2..9bc80d391ba 100644 --- a/packages/insomnia/src/models/helpers/request-operations.ts +++ b/packages/insomnia/src/models/helpers/request-operations.ts @@ -1,6 +1,7 @@ import { GrpcRequest, isGrpcRequest, isGrpcRequestId } from '../grpc-request'; import * as models from '../index'; import { Request } from '../request'; +import { isWebSocketRequest, WebSocketRequest } from '../websocket-request'; export function getById(requestId: string): Promise { return isGrpcRequestId(requestId) @@ -8,10 +9,15 @@ export function getById(requestId: string): Promise(request: T, patch: Partial = {}): Promise { diff --git a/packages/insomnia/src/models/websocket-request.ts b/packages/insomnia/src/models/websocket-request.ts index b774481f1ee..af1b78ca83e 100644 --- a/packages/insomnia/src/models/websocket-request.ts +++ b/packages/insomnia/src/models/websocket-request.ts @@ -14,9 +14,10 @@ export const canSync = false; export interface BaseWebSocketRequest { name: string; url: string; + metaSortKey: number; } -export type WebSocketRequest = BaseWebSocketRequest & BaseModel; +export type WebSocketRequest = BaseModel & BaseWebSocketRequest & { type: typeof type }; export const isWebSocketRequest = (model: Pick): model is WebSocketRequest => ( model.type === type @@ -25,6 +26,7 @@ export const isWebSocketRequest = (model: Pick): model is Web export const init = (): BaseWebSocketRequest => ({ name: 'New WebSocket Request', url: '', + metaSortKey: -1 * Date.now(), }); export const migrate = (doc: WebSocketRequest) => doc; diff --git a/packages/insomnia/src/ui/components/dropdowns/auth-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/auth-dropdown.tsx index 296b37766a5..f685fffe56a 100644 --- a/packages/insomnia/src/ui/components/dropdowns/auth-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/auth-dropdown.tsx @@ -17,6 +17,7 @@ import { } from '../../../common/constants'; import * as models from '../../../models'; import { update } from '../../../models/helpers/request-operations'; +import { isRequest } from '../../../models/request'; import { selectActiveRequest } from '../../redux/selectors'; import { Dropdown } from '../base/dropdown/dropdown'; import { DropdownButton } from '../base/dropdown/dropdown-button'; @@ -46,8 +47,7 @@ export const AuthDropdown: FC = () => { return; } - if (!('authentication' in activeRequest)) { - // gRPC Requests don't have `authentication` + if (!isRequest(activeRequest)) { return; } @@ -86,7 +86,7 @@ export const AuthDropdown: FC = () => { if (!activeRequest) { return false; } - if (!('authentication' in activeRequest)) { + if (!isRequest(activeRequest)) { return false; } return type === (activeRequest.authentication.type || AUTH_NONE); diff --git a/packages/insomnia/src/ui/components/dropdowns/content-type-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/content-type-dropdown.tsx index 4cff98bdf6b..7be60a8f986 100644 --- a/packages/insomnia/src/ui/components/dropdowns/content-type-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/content-type-dropdown.tsx @@ -15,6 +15,7 @@ import { CONTENT_TYPE_YAML, getContentTypeName, } from '../../../common/constants'; +import { isWebSocketRequest } from '../../../models/websocket-request'; import { selectActiveRequest } from '../../redux/selectors'; import { Dropdown } from '../base/dropdown/dropdown'; import { DropdownButton } from '../base/dropdown/dropdown-button'; @@ -38,7 +39,9 @@ const MimeTypeItem: FC<{ mimeType, onChange, }) => { - const activeRequest = useSelector(selectActiveRequest); + const request = useSelector(selectActiveRequest); + const activeRequest = request && !isWebSocketRequest(request) ? request : null; + const handleChangeMimeType = useCallback(async (mimeType: string | null) => { if (!activeRequest) { return; @@ -91,7 +94,8 @@ const MimeTypeItem: FC<{ MimeTypeItem.displayName = DropdownItem.name; export const ContentTypeDropdown: FC = ({ onChange }) => { - const activeRequest = useSelector(selectActiveRequest); + const request = useSelector(selectActiveRequest); + const activeRequest = request && !isWebSocketRequest(request) ? request : null; if (!activeRequest) { return null; diff --git a/packages/insomnia/src/ui/components/sidebar/dnd.tsx b/packages/insomnia/src/ui/components/sidebar/dnd.tsx index d4071db3b99..348602282ec 100644 --- a/packages/insomnia/src/ui/components/sidebar/dnd.tsx +++ b/packages/insomnia/src/ui/components/sidebar/dnd.tsx @@ -7,13 +7,14 @@ import * as models from '../../../models'; import { GrpcRequest } from '../../../models/grpc-request'; import { Request } from '../../../models/request'; import { RequestGroup } from '../../../models/request-group'; +import { WebSocketRequest } from '../../../models/websocket-request'; export type DnDDragProps = ReturnType; export type DnDDropProps = ReturnType; export type DnDProps = DnDDragProps & DnDDropProps; export interface DragObject { - item?: GrpcRequest | Request | RequestGroup; + item?: GrpcRequest | Request | WebSocketRequest | RequestGroup; } export const sourceCollect = (connect: DragSourceConnector, monitor: DragSourceMonitor) => ({ diff --git a/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx b/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx index 6b77b2f7928..b053fce3654 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx @@ -2,10 +2,11 @@ import React, { FC, Fragment } from 'react'; import ReactDOM from 'react-dom'; import { useSelector } from 'react-redux'; -import { GrpcRequest, isGrpcRequest } from '../../../models/grpc-request'; +import { GrpcRequest } from '../../../models/grpc-request'; import * as models from '../../../models/index'; -import { isRequest, Request } from '../../../models/request'; -import type { RequestGroup } from '../../../models/request-group'; +import { Request } from '../../../models/request'; +import { isRequestGroup, RequestGroup } from '../../../models/request-group'; +import { WebSocketRequest } from '../../../models/websocket-request'; import { updateRequestMetaByParentId } from '../../hooks/create-request'; import { selectActiveRequest, selectActiveWorkspaceMeta } from '../../redux/selectors'; import { selectSidebarChildren } from '../../redux/sidebar-selectors'; @@ -14,7 +15,7 @@ import { SidebarRequestGroupRow } from './sidebar-request-group-row'; import { SidebarRequestRow } from './sidebar-request-row'; export interface Child { - doc: Request | GrpcRequest | RequestGroup; + doc: Request | GrpcRequest | WebSocketRequest | RequestGroup; children: Child[]; collapsed: boolean; hidden: boolean; @@ -50,16 +51,37 @@ export const SidebarChildren: FC = ({ updateRequestMetaByParentId(requestId, { lastActive: Date.now() }); }; - const RecursiveSidebarRows: FC = ({ rows, isInPinnedList }) => { + const RecursiveSidebarRows: FC = ({ + rows, + isInPinnedList, + }) => { const activeRequest = useSelector(selectActiveRequest); const activeRequestId = activeRequest ? activeRequest._id : 'n/a'; return ( <> - {rows.map(row => (!isInPinnedList && row.hidden) - ? null - : (isRequest(row.doc) || isGrpcRequest(row.doc)) - ? ( + {rows + .filter(row => !(!isInPinnedList && row.hidden)) + .map(row => { + if (isRequestGroup(row.doc)) { + return ( + + {row.children.filter(Boolean).length > 0 ? ( + + ) : null} + + ); + } + return ( = ({ disableDragAndDrop={isInPinnedList} request={row.doc} /> - ) : ( - - {row.children.filter(Boolean).length > 0 ? : null} - - ))} - ); + ); + })} + + ); }; + const { all, pinned } = sidebarChildren; const showSeparator = sidebarChildren.pinned.length > 0; const contextMenuPortal = ReactDOM.createPortal( 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 47402dd8ddf..71eda7b2e3e 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx @@ -7,8 +7,9 @@ import { CONTENT_TYPE_GRAPHQL } from '../../../common/constants'; import { getMethodOverrideHeader } from '../../../common/misc'; import { GrpcRequest, isGrpcRequest } from '../../../models/grpc-request'; import * as requestOperations from '../../../models/helpers/request-operations'; -import { Request } from '../../../models/request'; +import { isRequest, Request } from '../../../models/request'; import { RequestGroup } from '../../../models/request-group'; +import { isWebSocketRequest, WebSocketRequest } from '../../../models/websocket-request'; import { useNunjucks } from '../../context/nunjucks/use-nunjucks'; import { createRequest } from '../../hooks/create-request'; import { selectActiveEnvironment, selectActiveProject, selectActiveWorkspace } from '../../redux/selectors'; @@ -21,6 +22,7 @@ import { showModal } from '../modals/index'; import { RequestSettingsModal } from '../modals/request-settings-modal'; import { GrpcTag } from '../tags/grpc-tag'; import { MethodTag } from '../tags/method-tag'; +import { WebSocketTag } from '../tags/websocket-tag'; import { DnDProps, DragObject, dropHandleCreator, hoverHandleCreator, sourceCollect, targetCollect } from './dnd'; interface RawProps { @@ -30,7 +32,7 @@ interface RawProps { handleDuplicateRequest: Function; isActive: boolean; isPinned: boolean; - request?: Request | GrpcRequest; + request?: Request | GrpcRequest | WebSocketRequest; requestGroup?: RequestGroup; } @@ -138,7 +140,7 @@ export const _SidebarRequestRow: FC = forwardRef(({ return; } - if (isGrpcRequest(request)) { + if (!isRequest(request)) { return; } @@ -203,12 +205,17 @@ export const _SidebarRequestRow: FC = forwardRef(({ ); } else { - const methodTag = - isGrpcRequest(request) ? ( - - ) : ( - - ); + + let methodTag = null; + + if (isGrpcRequest(request)) { + methodTag = ; + } else if (isWebSocketRequest(request)) { + methodTag = ; + } else if (isRequest(request)) { + methodTag = ; + } + node = (
  • = forwardRef(({
    - + {!isWebSocketRequest(request) && ( + + )}
    {isPinned && (
    diff --git a/packages/insomnia/src/ui/components/tags/websocket-tag.tsx b/packages/insomnia/src/ui/components/tags/websocket-tag.tsx new file mode 100644 index 00000000000..96f757b375f --- /dev/null +++ b/packages/insomnia/src/ui/components/tags/websocket-tag.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const WebSocketTag = () => ( +
    + WS +
    +); diff --git a/packages/insomnia/src/ui/containers/app.tsx b/packages/insomnia/src/ui/containers/app.tsx index 8ab58a773c6..56b98df9723 100644 --- a/packages/insomnia/src/ui/containers/app.tsx +++ b/packages/insomnia/src/ui/containers/app.tsx @@ -29,6 +29,7 @@ import { isNotDefaultProject } from '../../models/project'; import { Request, updateMimeType } from '../../models/request'; import { type RequestGroupMeta } from '../../models/request-group-meta'; import { getByParentId as getRequestMetaByParentId } from '../../models/request-meta'; +import { isWebSocketRequest, WebSocketRequest } from '../../models/websocket-request'; import { isWorkspace } from '../../models/workspace'; import * as plugins from '../../plugins'; import * as themes from '../../plugins/misc'; @@ -266,7 +267,7 @@ class App extends PureComponent { ]; } - _requestDuplicate(request?: Request | GrpcRequest) { + _requestDuplicate(request?: Request | GrpcRequest | WebSocketRequest) { if (!request) { return; } @@ -353,6 +354,11 @@ class App extends PureComponent { return null; } + if (isWebSocketRequest(this.props.activeRequest)) { + console.warn('Tried to update request mime-type on WebSocket request'); + return null; + } + const requestMeta = await models.requestMeta.getOrCreateByParentId( this.props.activeRequest._id, ); diff --git a/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts b/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts index 913b26544d5..06a7c016757 100644 --- a/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts +++ b/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts @@ -2,6 +2,7 @@ import { useCallback } from 'react'; import { useSelector } from 'react-redux'; import { getRenderContext, getRenderContextAncestors, HandleGetRenderContext, HandleRender, render } from '../../../common/render'; +import { isWebSocketRequest } from '../../../models/websocket-request'; import { NUNJUCKS_TEMPLATE_GLOBAL_PROPERTY_NAME } from '../../../templating'; import { getKeys } from '../../../templating/utils'; import { selectActiveEnvironment, selectActiveRequest, selectActiveWorkspace } from '../../redux/selectors'; @@ -19,7 +20,8 @@ initializeNunjucksRenderPromiseCache(); */ export const useNunjucks = () => { const environmentId = useSelector(selectActiveEnvironment)?._id; - const request = useSelector(selectActiveRequest); + const activeRequest = useSelector(selectActiveRequest); + const request = activeRequest && isWebSocketRequest(activeRequest) ? null : activeRequest; const workspace = useSelector(selectActiveWorkspace); const fetchRenderContext = useCallback(async () => { diff --git a/packages/insomnia/src/ui/hooks/use-active-request.ts b/packages/insomnia/src/ui/hooks/use-active-request.ts index 7115e633729..7c372cec0d9 100644 --- a/packages/insomnia/src/ui/hooks/use-active-request.ts +++ b/packages/insomnia/src/ui/hooks/use-active-request.ts @@ -2,8 +2,7 @@ import { useCallback } from 'react'; import { useSelector } from 'react-redux'; import * as models from '../../models'; -import { isGrpcRequest } from '../../models/grpc-request'; -import { Request } from '../../models/request'; +import { isRequest, Request } from '../../models/request'; import { selectActiveRequest } from '../redux/selectors'; export const useActiveRequest = () => { @@ -13,8 +12,8 @@ export const useActiveRequest = () => { throw new Error('Tried to load null request'); } - if (isGrpcRequest(activeRequest)) { - throw new Error('Loaded GrpcRequest, expected to load Request'); + if (!isRequest(activeRequest)) { + throw new Error('Expected to load Request'); } const patchRequest = useCallback(async (patch: Partial) => { diff --git a/packages/insomnia/src/ui/redux/sidebar-selectors.ts b/packages/insomnia/src/ui/redux/sidebar-selectors.ts index ed281e6a3fe..54587c33796 100644 --- a/packages/insomnia/src/ui/redux/sidebar-selectors.ts +++ b/packages/insomnia/src/ui/redux/sidebar-selectors.ts @@ -6,6 +6,7 @@ import type { BaseModel } from '../../models'; import { GrpcRequest, isGrpcRequest } from '../../models/grpc-request'; import { isRequest, Request } from '../../models/request'; import { isRequestGroup, RequestGroup } from '../../models/request-group'; +import { isWebSocketRequest } from '../../models/websocket-request'; import { selectActiveWorkspace, selectActiveWorkspaceMeta, @@ -17,7 +18,7 @@ import { type SidebarModel = Request | GrpcRequest | RequestGroup; export const shouldShowInSidebar = (model: BaseModel): boolean => - isRequest(model) || isGrpcRequest(model) || isRequestGroup(model); + isRequest(model) || isWebSocketRequest(model) || isGrpcRequest(model) || isRequestGroup(model); export const shouldIgnoreChildrenOf = (model: SidebarModel): boolean => isRequest(model) || isGrpcRequest(model);