diff --git a/packages/insomnia/package-lock.json b/packages/insomnia/package-lock.json index 3c35e8f186..04a52c692d 100644 --- a/packages/insomnia/package-lock.json +++ b/packages/insomnia/package-lock.json @@ -73,6 +73,7 @@ "@types/fs-extra": "^5.1.0", "@types/hapi__hawk": "^8.0.2", "@types/js-yaml": "^4.0.1", + "@types/jshint": "^2.12.1", "@types/license-checker": "^25.0.1", "@types/marked": "^4.0.2", "@types/mime-types": "^2.1.1", @@ -4675,12 +4676,6 @@ "integrity": "sha512-xdOvNmXmrZqqPy3kuCQ+fz6wA0xU5pji9cd1nDrflWaAWtYLLGk5ykW0H6yg5TVyehHP1pfmuuSaZkhP+kspVA==", "dev": true }, - "node_modules/@types/jshint": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/@types/jshint/-/jshint-2.12.1.tgz", - "integrity": "sha512-Sen/qexrVc74JFfqPfaRB5zF/aIuziEmENKFQgpUNU23Ebja/aAFXdnPpiB9qumE8QWLCHFinuVbx5THc5tWkw==", - "dev": true - }, "node_modules/@types/jsdom": { "version": "16.2.14", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.14.tgz", @@ -4692,6 +4687,12 @@ "@types/tough-cookie": "*" } }, + "node_modules/@types/jshint": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@types/jshint/-/jshint-2.12.1.tgz", + "integrity": "sha512-Sen/qexrVc74JFfqPfaRB5zF/aIuziEmENKFQgpUNU23Ebja/aAFXdnPpiB9qumE8QWLCHFinuVbx5THc5tWkw==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -24041,12 +24042,6 @@ "integrity": "sha512-xdOvNmXmrZqqPy3kuCQ+fz6wA0xU5pji9cd1nDrflWaAWtYLLGk5ykW0H6yg5TVyehHP1pfmuuSaZkhP+kspVA==", "dev": true }, - "@types/jshint": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/@types/jshint/-/jshint-2.12.1.tgz", - "integrity": "sha512-Sen/qexrVc74JFfqPfaRB5zF/aIuziEmENKFQgpUNU23Ebja/aAFXdnPpiB9qumE8QWLCHFinuVbx5THc5tWkw==", - "dev": true - }, "@types/jsdom": { "version": "16.2.14", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.14.tgz", @@ -24058,6 +24053,12 @@ "@types/tough-cookie": "*" } }, + "@types/jshint": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@types/jshint/-/jshint-2.12.1.tgz", + "integrity": "sha512-Sen/qexrVc74JFfqPfaRB5zF/aIuziEmENKFQgpUNU23Ebja/aAFXdnPpiB9qumE8QWLCHFinuVbx5THc5tWkw==", + "dev": true + }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", diff --git a/packages/insomnia/src/ui/components/dropdowns/request-group-actions-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/request-group-actions-dropdown.tsx index d8137bda8e..b59cf6d83c 100644 --- a/packages/insomnia/src/ui/components/dropdowns/request-group-actions-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/request-group-actions-dropdown.tsx @@ -1,10 +1,8 @@ -import { autoBindMethodsForReact } from 'class-autobind-decorator'; import classnames from 'classnames'; import { HotKeyRegistry } from 'insomnia-common'; -import React, { PureComponent } from 'react'; -import { connect } from 'react-redux'; +import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react'; +import { useSelector } from 'react-redux'; -import { AUTOBIND_CFG } from '../../../common/constants'; import { hotKeyRefs } from '../../../common/hotkeys'; import { RENDER_PURPOSE_NO_RENDER } from '../../../common/render'; import * as models from '../../../models'; @@ -12,8 +10,8 @@ import type { RequestGroup } from '../../../models/request-group'; import type { RequestGroupAction } from '../../../plugins'; import { getRequestGroupActions } from '../../../plugins'; import * as pluginContexts from '../../../plugins/context/index'; -import { RootState } from '../../redux/modules'; -import { selectActiveEnvironment, selectActiveProject } from '../../redux/selectors'; +import { createRequest, CreateRequestType } from '../../hooks/create-request'; +import { selectActiveEnvironment, selectActiveProject, selectActiveWorkspace } from '../../redux/selectors'; import { Dropdown, DropdownProps } from '../base/dropdown/dropdown'; import { DropdownButton } from '../base/dropdown/dropdown-button'; import { DropdownDivider } from '../base/dropdown/dropdown-divider'; @@ -23,154 +21,160 @@ import { PromptButton } from '../base/prompt-button'; import { showError, showModal } from '../modals'; import { EnvironmentEditModal } from '../modals/environment-edit-modal'; -type ReduxProps = ReturnType; - -const mapStateToProps = (state: RootState) => ({ - activeProject: selectActiveProject(state), - activeEnvironment: selectActiveEnvironment(state), -}); - -interface Props extends ReduxProps, Partial { +interface Props extends Partial { requestGroup: RequestGroup; hotKeyRegistry: HotKeyRegistry; - handleCreateRequest: (id: string) => any; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => any; handleShowSettings: (requestGroup: RequestGroup) => any; handleCreateRequestGroup: (requestGroup: string) => any; } -interface State { - actionPlugins: RequestGroupAction[]; - loadingActions: Record; +interface RequestGroupActionsDropdownHandle { + show: () => void; } -@autoBindMethodsForReact(AUTOBIND_CFG) -export class UnconnectedRequestGroupActionsDropdown extends PureComponent { - _dropdown: Dropdown | null = null; - state: State = { - actionPlugins: [], - loadingActions: {}, - }; +export const RequestGroupActionsDropdown = forwardRef(({ + requestGroup, + hotKeyRegistry, + handleShowSettings, + handleDuplicateRequestGroup, + handleCreateRequestGroup, + ...other +}, ref) => { + const [actionPlugins, setActionPlugins] = useState([]); + const [loadingActions, setLoadingActions] = useState< Record>({}); + const dropdownRef = useRef(null); - _setDropdownRef(dropdown: Dropdown) { - this._dropdown = dropdown; - } + const activeProject = useSelector(selectActiveProject); + const activeEnvironment = useSelector(selectActiveEnvironment); + const activeWorkspace = useSelector(selectActiveWorkspace); + const activeWorkspaceId = activeWorkspace?._id; - async _handleRequestCreate() { - this.props.handleCreateRequest(this.props.requestGroup._id); - } - - _handleRequestGroupDuplicate() { - this.props.handleDuplicateRequestGroup(this.props.requestGroup); - } - - async _handleRequestGroupCreate() { - this.props.handleCreateRequestGroup(this.props.requestGroup._id); - } - - async _handleDeleteFolder() { - await models.stats.incrementDeletedRequestsForDescendents(this.props.requestGroup); - models.requestGroup.remove(this.props.requestGroup); - } - - _handleEditEnvironment() { - showModal(EnvironmentEditModal, this.props.requestGroup); - } - - async onOpen() { - const plugins = await getRequestGroupActions(); - this.setState({ - actionPlugins: plugins, - }); - } - - async show() { - this._dropdown?.show(); - } - - async _handlePluginClick(p: RequestGroupAction) { - this.setState(state => ({ - loadingActions: { ...state.loadingActions, [p.label]: true }, - })); - - try { - const { activeEnvironment, requestGroup, activeProject } = this.props; - const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null; - const context = { - ...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER) as Record), - ...pluginContexts.data.init(activeProject._id), - ...(pluginContexts.store.init(p.plugin) as Record), - ...(pluginContexts.network.init(activeEnvironmentId) as Record), - }; - const requests = await models.request.findByParentId(requestGroup._id); - requests.sort((a, b) => a.metaSortKey - b.metaSortKey); - await p.action(context, { - requestGroup, - requests, - }); - } catch (err) { - showError({ - title: 'Plugin Action Failed', - error: err, - }); + const create = useCallback((requestType: CreateRequestType) => { + if (activeWorkspaceId) { + createRequest({ parentId: requestGroup._id, requestType, workspaceId: activeWorkspaceId }); } + }, [activeWorkspaceId, requestGroup._id]); - this.setState(state => ({ - loadingActions: { ...state.loadingActions, [p.label]: false }, - })); - this._dropdown?.hide(); - } + useImperativeHandle(ref, () => ({ + show: () => { + dropdownRef.current?.show(); + }, + })); - render() { - const { - // eslint-disable-line @typescript-eslint/no-unused-vars - requestGroup, - // eslint-disable-line @typescript-eslint/no-unused-vars - hotKeyRegistry, - ...other - } = this.props; - const { actionPlugins, loadingActions } = this.state; - return ( - - - - - this._handleRequestCreate()}> - New Request - - - - New Folder - - - - - Duplicate - - - Environment - - - Delete - - {actionPlugins.length > 0 && Plugins} - {actionPlugins.map((p: RequestGroupAction) => ( - this._handlePluginClick(p)} stayOpenAfterClick> - {loadingActions[p.label] ? ( - - ) : ( - - )} - {p.label} - - ))} - - - Settings - - - ); - } -} + const onOpen = useCallback(() => { + getRequestGroupActions().then(actionPlugins => { + setActionPlugins(actionPlugins); + }); + }, []); -export const RequestGroupActionsDropdown = connect(mapStateToProps, null, null, { forwardRef: true })(UnconnectedRequestGroupActionsDropdown); + const handleRequestGroupDuplicate = useCallback(() => { + handleDuplicateRequestGroup(requestGroup); + }, [requestGroup, handleDuplicateRequestGroup]); + + const handleRequestGroupCreate = useCallback(() => { + handleCreateRequestGroup(requestGroup._id); + }, [handleCreateRequestGroup, requestGroup._id]); + + const handleDeleteFolder = useCallback(() => { + models.stats.incrementDeletedRequestsForDescendents(requestGroup).then(() => { + models.requestGroup.remove(requestGroup); + }); + }, [requestGroup]); + + const handleEditEnvironment = useCallback(() => { + showModal(EnvironmentEditModal, requestGroup); + }, [requestGroup]); + + const handlePluginClick = useCallback(({ label, plugin, action }: RequestGroupAction) => { + const fn = async () => { + setLoadingActions({ ...loadingActions, [label]: true }); + + try { + const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null; + const context = { + ...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER) as Record), + ...pluginContexts.data.init(activeProject._id), + ...(pluginContexts.store.init(plugin) as Record), + ...(pluginContexts.network.init(activeEnvironmentId) as Record), + }; + const requests = await models.request.findByParentId(requestGroup._id); + requests.sort((a, b) => a.metaSortKey - b.metaSortKey); + await action(context, { + requestGroup, + requests, + }); + } catch (err) { + showError({ + title: 'Plugin Action Failed', + error: err, + }); + } + + setLoadingActions({ + ...loadingActions, + [label]: false, + }); + + dropdownRef.current?.hide(); + }; + fn(); + }, [dropdownRef, loadingActions, activeEnvironment, requestGroup, activeProject]); + + return ( + + + + + + + New HTTP Request + + + + + New GraphQL Request + + + + New gRPC Request + + + + New Folder + + + + + + + Duplicate + + + + Environment + + + + Delete + + + {actionPlugins.length > 0 && Plugins} + {actionPlugins.map((requestGroupAction: RequestGroupAction) => ( + handlePluginClick(requestGroupAction)} stayOpenAfterClick> + {loadingActions[requestGroupAction.label] ? ( + + ) : ( + + )} + {requestGroupAction.label} + + ))} + + + Settings + + + ); +}); +RequestGroupActionsDropdown.displayName = 'RequestGroupActionsDropdown'; diff --git a/packages/insomnia/src/ui/components/panes/placeholder-request-pane.tsx b/packages/insomnia/src/ui/components/panes/placeholder-request-pane.tsx index 80de945588..7d612e206e 100644 --- a/packages/insomnia/src/ui/components/panes/placeholder-request-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/placeholder-request-pane.tsx @@ -2,24 +2,29 @@ import React, { FC, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { hotKeyRefs } from '../../../common/hotkeys'; +import { createRequest } from '../../hooks/create-request'; import { ForceToWorkspace } from '../../redux/modules/helpers'; import { importFile } from '../../redux/modules/import'; import { selectActiveWorkspace, selectSettings } from '../../redux/selectors'; import { Hotkey } from '../hotkey'; import { Pane, PaneBody, PaneHeader } from './pane'; -interface Props { - handleCreateRequest: () => void; -} - -export const PlaceholderRequestPane: FC = ({ - handleCreateRequest, -}) => { +export const PlaceholderRequestPane: FC = () => { const dispatch = useDispatch(); const { hotKeyRegistry } = useSelector(selectSettings); const workspaceId = useSelector(selectActiveWorkspace)?._id; const handleImportFile = useCallback(() => dispatch(importFile({ workspaceId, forceToWorkspace: ForceToWorkspace.current })), [workspaceId, dispatch]); + const createHttpRequest = useCallback(() => { + if (workspaceId) { + createRequest({ + requestType: 'HTTP', + parentId: workspaceId, + workspaceId: workspaceId, + }); + } + }, [workspaceId]); + return ( @@ -67,8 +72,8 @@ export const PlaceholderRequestPane: FC = ({ - diff --git a/packages/insomnia/src/ui/components/panes/request-pane.tsx b/packages/insomnia/src/ui/components/panes/request-pane.tsx index 8e18135b93..50a6bd687b 100644 --- a/packages/insomnia/src/ui/components/panes/request-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/request-pane.tsx @@ -37,7 +37,6 @@ interface Props { forceRefreshCounter: number; forceUpdateRequest: (r: Request, patch: Partial) => Promise; forceUpdateRequestHeaders: (r: Request, headers: RequestHeader[]) => Promise; - handleCreateRequest: () => void; handleGenerateCode: Function; handleImport: Function; handleSend: () => void; @@ -64,7 +63,6 @@ export const RequestPane: FC = ({ forceRefreshCounter, forceUpdateRequest, forceUpdateRequestHeaders, - handleCreateRequest, handleGenerateCode, handleImport, handleSend, @@ -145,9 +143,7 @@ export const RequestPane: FC = ({ if (!request) { return ( - + ); } diff --git a/packages/insomnia/src/ui/components/sidebar/dnd.tsx b/packages/insomnia/src/ui/components/sidebar/dnd.tsx index bc406f1c8d..1cccdc1995 100644 --- a/packages/insomnia/src/ui/components/sidebar/dnd.tsx +++ b/packages/insomnia/src/ui/components/sidebar/dnd.tsx @@ -27,8 +27,14 @@ export const targetCollect = (connect: DropTargetConnector, monitor: DropTargetM }); export const isAbove = (monitor: DropTargetMonitor, component: any) => { - const hoveredNode = ReactDOM.findDOMNode(component); - // @ts-expect-error -- TSCONVERSION + let hoveredNode; + try { + // Try to find the node if it's a class component + hoveredNode = ReactDOM.findDOMNode(component); + } catch (error: unknown) { + // Try to find the component if it's a function component + hoveredNode = component.node; + } const hoveredTop = hoveredNode.getBoundingClientRect().top; const draggedTop = monitor.getSourceClientOffset()?.y; return draggedTop && hoveredTop > draggedTop; diff --git a/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx b/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx index cd25fea3a2..b21372f924 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-children.tsx @@ -35,7 +35,6 @@ const mapStateToProps = (state: RootState) => ({ interface Props extends ReduxProps { handleActivateRequest: Function; - handleCreateRequest: (id: string) => any; handleCreateRequestGroup: (parentId: string) => void; handleSetRequestPinned: Function; handleSetRequestGroupCollapsed: Function; @@ -53,7 +52,6 @@ class UnconnectedSidebarChildren extends PureComponent { _renderChildren(children: Child[], isInPinnedList: boolean) { const { filter, - handleCreateRequest, handleCreateRequestGroup, handleSetRequestPinned, handleSetRequestGroupCollapsed, @@ -81,7 +79,6 @@ class UnconnectedSidebarChildren extends PureComponent { handleDuplicateRequest={handleDuplicateRequest} handleGenerateCode={handleGenerateCode} handleCopyAsCurl={handleCopyAsCurl} - requestCreate={handleCreateRequest} isActive={child.doc._id === activeRequestId} isPinned={child.pinned} disableDragAndDrop={isInPinnedList} @@ -120,7 +117,6 @@ class UnconnectedSidebarChildren extends PureComponent { handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed} handleDuplicateRequestGroup={handleDuplicateRequestGroup} isCollapsed={child.collapsed} - handleCreateRequest={handleCreateRequest} handleCreateRequestGroup={handleCreateRequestGroup} requestGroup={requestGroup} hotKeyRegistry={hotKeyRegistry} @@ -141,13 +137,6 @@ class UnconnectedSidebarChildren extends PureComponent { ); } - _handleCreateRequest() { - const { handleCreateRequest, workspace } = this.props; - if (workspace) { - handleCreateRequest(workspace._id); - } - } - _handleCreateRequestGroup() { const { handleCreateRequestGroup, workspace } = this.props; if (workspace) { @@ -161,7 +150,6 @@ class UnconnectedSidebarChildren extends PureComponent { const contextMenuPortal = ReactDOM.createPortal(
diff --git a/packages/insomnia/src/ui/components/sidebar/sidebar-create-dropdown.tsx b/packages/insomnia/src/ui/components/sidebar/sidebar-create-dropdown.tsx index ed88af4a9c..d386590a4f 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-create-dropdown.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-create-dropdown.tsx @@ -1,29 +1,38 @@ import { HotKeyRegistry } from 'insomnia-common'; import React, { FC, useCallback } from 'react'; +import { useSelector } from 'react-redux'; import { hotKeyRefs } from '../../../common/hotkeys'; import { RequestGroup } from '../../../models/request-group'; +import { createRequest, CreateRequestType } from '../../hooks/create-request'; +import { selectActiveWorkspace } from '../../redux/selectors'; import { Dropdown } from '../base/dropdown/dropdown'; import { DropdownButton } from '../base/dropdown/dropdown-button'; import { DropdownHint } from '../base/dropdown/dropdown-hint'; import { DropdownItem } from '../base/dropdown/dropdown-item'; interface Props { - handleCreateRequest: (id: string) => any; handleCreateRequestGroup: (requestGroup: RequestGroup) => any; hotKeyRegistry: HotKeyRegistry; right?: boolean; } export const SidebarCreateDropdown: FC = ({ - handleCreateRequest, handleCreateRequestGroup, hotKeyRegistry, right, }) => { - const create = useCallback((value: string) => { - handleCreateRequest(value); - }, [handleCreateRequest]); + const activeWorkspace = useSelector(selectActiveWorkspace); + const activeWorkspaceId = activeWorkspace?._id; + const create = useCallback((value: CreateRequestType) => { + if (activeWorkspaceId) { + createRequest({ + requestType: value, + parentId: activeWorkspaceId, + workspaceId: activeWorkspaceId, + }); + } + }, [activeWorkspaceId]); return ( diff --git a/packages/insomnia/src/ui/components/sidebar/sidebar-filter.tsx b/packages/insomnia/src/ui/components/sidebar/sidebar-filter.tsx index cf8a60e551..0293422d69 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-filter.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-filter.tsx @@ -11,7 +11,6 @@ import { SidebarSortDropdown } from './sidebar-sort-dropdown'; interface Props { onChange: (value: string) => Promise; - requestCreate: (requestType?: string) => void; requestGroupCreate: () => void; sidebarSort: (sortOrder: SortOrder) => void; filter: string; @@ -51,10 +50,6 @@ export class SidebarFilter extends PureComponent { this.props.requestGroupCreate(); } - _handleRequestCreate(requestType?: string) { - this.props.requestCreate(requestType); - } - _handleKeydown(event: KeyboardEvent) { executeHotKey(event, hotKeyRefs.SIDEBAR_FOCUS_FILTER, () => { this._input?.focus(); @@ -82,7 +77,6 @@ export class SidebarFilter extends PureComponent {
diff --git a/packages/insomnia/src/ui/components/sidebar/sidebar-request-group-row.tsx b/packages/insomnia/src/ui/components/sidebar/sidebar-request-group-row.tsx index ebd706bd66..cd7174afdf 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-request-group-row.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-request-group-row.tsx @@ -2,7 +2,7 @@ import { autoBindMethodsForReact } from 'class-autobind-decorator'; import classnames from 'classnames'; import { HotKeyRegistry } from 'insomnia-common'; import { noop } from 'ramda-adjunct'; -import React, { PureComponent } from 'react'; +import React, { ElementRef, MouseEvent, PureComponent } from 'react'; import { PropsWithChildren } from 'react'; import { createRef } from 'react'; import { DragSource, DragSourceSpec, DropTarget, DropTargetMonitor, DropTargetSpec } from 'react-dnd'; @@ -13,7 +13,7 @@ import { RequestGroup } from '../../../models/request-group'; import { RootState } from '../../redux/modules'; import { selectActiveEnvironment, selectActiveRequest } from '../../redux/selectors'; import { Highlight } from '../base/highlight'; -import { RequestGroupActionsDropdown, UnconnectedRequestGroupActionsDropdown } from '../dropdowns/request-group-actions-dropdown'; +import { RequestGroupActionsDropdown } from '../dropdowns/request-group-actions-dropdown'; import { showModal } from '../modals'; import { RequestGroupSettingsModal } from '../modals/request-group-settings-modal'; import { DnDDragProps, DnDDropProps, DnDProps, DragObject, dropHandleCreator, hoverHandleCreator, sourceCollect, targetCollect } from './dnd'; @@ -30,7 +30,6 @@ interface Props extends DnDProps, ReduxProps, PropsWithChildren<{}> { handleSetRequestGroupCollapsed: Function; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => any; handleActivateRequest: Function; - handleCreateRequest: (id: string) => any; handleCreateRequestGroup: (requestGroup: string) => any; filter: string; isActive: boolean; @@ -49,7 +48,7 @@ class UnconnectedSidebarRequestGroupRow extends PureComponent { dragDirection: 0, }; - private dropdownRef = createRef(); + private dropdownRef = createRef>(); private expandTagRef = createRef(); getExpandTag() { @@ -61,7 +60,7 @@ class UnconnectedSidebarRequestGroupRow extends PureComponent { handleSetRequestGroupCollapsed(requestGroup._id, !isCollapsed); } - _handleShowActions(event) { + _handleShowActions(event: MouseEvent) { event.preventDefault(); this.dropdownRef.current?.show(); @@ -90,7 +89,6 @@ class UnconnectedSidebarRequestGroupRow extends PureComponent { requestGroup, isCollapsed, isActive, - handleCreateRequest, handleCreateRequestGroup, handleDuplicateRequestGroup, isDragging, @@ -145,7 +143,6 @@ class UnconnectedSidebarRequestGroupRow extends PureComponent {
{ handleSetRequestPinned={noop} isActive={false} requestGroup={requestGroup} - requestCreate={handleCreateRequest} filter={filter} hotKeyRegistry={hotKeyRegistry} isPinned={false} // Necessary so that plugin actions work 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 e3ccf782a6..6e63a8fd88 100644 --- a/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx +++ b/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx @@ -1,18 +1,18 @@ -import { autoBindMethodsForReact } from 'class-autobind-decorator'; import classnames from 'classnames'; import { HotKeyRegistry } from 'insomnia-common'; -import React, { FC, PureComponent } from 'react'; +import React, { FC, forwardRef, MouseEvent, ReactElement, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { DragSource, DragSourceSpec, DropTarget, DropTargetSpec } from 'react-dnd'; import { useSelector } from 'react-redux'; -import { AUTOBIND_CFG, CONTENT_TYPE_GRAPHQL } from '../../../common/constants'; +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 { RequestGroup } from '../../../models/request-group'; import { useNunjucks } from '../../context/nunjucks/use-nunjucks'; -import { selectActiveEnvironment, selectActiveProject } from '../../redux/selectors'; +import { createRequest } from '../../hooks/create-request'; +import { selectActiveEnvironment, selectActiveProject, selectActiveWorkspace } from '../../redux/selectors'; import { Editable } from '../base/editable'; import { Highlight } from '../base/highlight'; import { RequestActionsDropdown } from '../dropdowns/request-actions-dropdown'; @@ -23,15 +23,12 @@ import { GrpcTag } from '../tags/grpc-tag'; import { MethodTag } from '../tags/method-tag'; import { DnDProps, DragObject, dropHandleCreator, hoverHandleCreator, sourceCollect, targetCollect } from './dnd'; -type DerivedProps = ReturnType; - interface RawProps { handleActivateRequest: Function; handleSetRequestPinned: Function; handleDuplicateRequest: Function; handleGenerateCode: Function; handleCopyAsCurl: Function; - requestCreate: Function; filter: string; isActive: boolean; isPinned: boolean; @@ -42,307 +39,255 @@ interface RawProps { disableDragAndDrop?: boolean; } -type Props = RawProps & DnDProps & DerivedProps; - -interface State { - dragDirection: number; - isEditing: boolean; - renderedUrl: string; -} - -@autoBindMethodsForReact(AUTOBIND_CFG) -class UnconnectedSidebarRequestRow extends PureComponent { - state: State = { - dragDirection: 0, - isEditing: false, - renderedUrl: '', - }; - - _urlUpdateInterval: NodeJS.Timeout | null = null; - _requestActionsDropdown: RequestActionsDropdown | null = null; - - _setRequestActionsDropdownRef(requestActionsDropdown: RequestActionsDropdown) { - this._requestActionsDropdown = requestActionsDropdown; - } - - _handleShowRequestActions(event) { - event.preventDefault(); - - this._requestActionsDropdown?.show(); - } - - _handleEditStart() { - this.setState({ - isEditing: true, - }); - } - - async _handleRequestUpdateName(name) { - const { request } = this.props; - const patch = { - name, - }; - // @ts-expect-error -- TSCONVERSION skip this if request is undefined - await requestOperations.update(request, patch); - this.setState({ - isEditing: false, - }); - } - - _handleRequestCreateFromEmpty() { - const parentId = this.props.requestGroup?._id; - this.props.requestCreate(parentId); - } - - _handleRequestActivate() { - const { isActive, request, handleActivateRequest } = this.props; - - if (isActive) { - return; - } - - // @ts-expect-error -- TSCONVERSION skip this if request is undefined - handleActivateRequest(request._id); - } - - _handleShowRequestSettings() { - showModal(RequestSettingsModal, { - request: this.props.request, - }); - } - - _getMethodOverrideHeaderValue() { - const { request } = this.props; - // @ts-expect-error -- TSCONVERSION skip this if request is undefined or grpc - const header = getMethodOverrideHeader(request.headers); - - if (header) { - return header.value; - } - - // If no override, use GraphQL as override if it's a gql request - // @ts-expect-error -- TSCONVERSION skip this if request is undefined or grpc - if (request.body && request.body.mimeType === CONTENT_TYPE_GRAPHQL) { - return 'GQL'; - } - - return null; - } - - async _debouncedUpdateRenderedUrl(props: Props) { - if (this._urlUpdateInterval !== null) { - clearTimeout(this._urlUpdateInterval); - } - this._urlUpdateInterval = setTimeout(() => { - this._updateRenderedUrl(props); - }, 300); - } - - async _updateRenderedUrl(props: Props) { - let renderedUrl; - - try { - // @ts-expect-error -- TSCONVERSION skip this if request is undefined or grpc - renderedUrl = await props.handleRender(props.request.url); - } catch (error) { - // Certain things, such as invalid variable tags and Prompts - // without titles will result in a failure to parse. Can't do - // much else, so let's just give them the unrendered URL - // @ts-expect-error -- TSCONVERSION skip this if request is undefined - renderedUrl = props.request.url; - } - - this.setState({ - renderedUrl, - }); - } - - setDragDirection(dragDirection) { - if (dragDirection !== this.state.dragDirection) { - this.setState({ - dragDirection, - }); - } - } - - componentDidMount() { - const { request } = this.props; - - if (request && !request.name) { - this._debouncedUpdateRenderedUrl(this.props); - } - } - - // eslint-disable-next-line camelcase - UNSAFE_componentWillUpdate(nextProps) { - if (!nextProps.request) { - return; - } - - const requestUrl = this.props.request ? this.props.request.url : ''; - - if (nextProps.request.url !== requestUrl) { - this._debouncedUpdateRenderedUrl(nextProps); - } - } - - componentWillUnmount() { - if (this._urlUpdateInterval !== null) { - clearTimeout(this._urlUpdateInterval); - } - } - - render() { - const { - activeEnvironment, - connectDragSource, - connectDropTarget, - disableDragAndDrop, - filter, - handleCopyAsCurl, - handleDuplicateRequest, - handleGenerateCode, - handleSetRequestPinned, - hotKeyRegistry, - isActive, - isDragging, - isDraggingOver, - isPinned, - request, - requestGroup, - activeProject, - } = this.props; - const { dragDirection } = this.state; - let node; - const classes = classnames('sidebar__row', { - 'sidebar__row--dragging': isDragging, - 'sidebar__row--dragging-above': isDraggingOver && dragDirection > 0, - 'sidebar__row--dragging-below': isDraggingOver && dragDirection < 0, - }); - - if (!request) { - node = ( -
  • -
    - -
    -
  • - ); - } else { - const methodTag = - isGrpcRequest(request) ? ( - - ) : ( - - ); - node = ( -
  • -
    - -
    - -
    - {isPinned && ( -
    - -
    - )} -
    -
  • - ); - } - - if (disableDragAndDrop) { - return node; - } else if (!this.state.isEditing) { - return connectDragSource(connectDropTarget(node)); - } else { - return connectDropTarget(node); - } - } -} +type Props = RawProps & DnDProps; const dragSource: DragSourceSpec = { - beginDrag(props) { + beginDrag({ request }) { return { - item: props.request, + item: request, }; }, }; -const dropHandle = dropHandleCreator({ - getParentId: props => props.requestGroup?._id || props.request?.parentId, - getTargetId: props => props.request?._id, -}); - -const hoverHandle = hoverHandleCreator(); - const dragTarget: DropTargetSpec = { - drop: dropHandle, - hover: hoverHandle, + drop: dropHandleCreator({ + getParentId: props => props.requestGroup?._id || props.request?.parentId, + getTargetId: props => props.request?._id, + }), + hover: hoverHandleCreator(), }; -const source = DragSource('SIDEBAR_REQUEST_ROW', dragSource, sourceCollect)(UnconnectedSidebarRequestRow); -const Target = DropTarget('SIDEBAR_REQUEST_ROW', dragTarget, targetCollect)(source); - -const useDerivedProps = () => { +export const _SidebarRequestRow: FC = forwardRef(({ + connectDragSource, + connectDropTarget, + disableDragAndDrop, + filter, + handleActivateRequest, + handleCopyAsCurl, + handleDuplicateRequest, + handleGenerateCode, + handleSetRequestPinned, + hotKeyRegistry, + isActive, + isDragging, + isDraggingOver, + isPinned, + request, + requestGroup, +}, ref) => { const { handleRender } = useNunjucks(); const activeProject = useSelector(selectActiveProject); const activeEnvironment = useSelector(selectActiveEnvironment); + const activeWorkspace = useSelector(selectActiveWorkspace); + const activeWorkspaceId = activeWorkspace?._id; + const [dragDirection, setDragDirection] = useState(0); + const [isEditing, setIsEditing] = useState(false); - return { - handleRender, - activeProject, - activeEnvironment, - }; -}; + const nodeRef = useRef(null); + useImperativeHandle(ref, () => ({ + setDragDirection, + node: nodeRef.current, + })); -export const SidebarRequestRow: FC = props => { - const derivedProps = useDerivedProps(); - return ; -}; + const startEditing = useCallback(() => { + setIsEditing(true); + }, []); + + const stopEditing = useCallback(() => { + setIsEditing(false); + }, []); + + const [renderedUrl, setRenderedUrl] = useState(''); + + const requestActionsDropdown = useRef(null); + + const handleShowRequestActions = useCallback((event: MouseEvent) => { + event.preventDefault(); + + requestActionsDropdown.current?.show(); + }, [requestActionsDropdown]); + + const handleRequestUpdateName = useCallback((name: string) => { + if (!request) { + return; + } + + requestOperations.update(request, { name }).then(() => { + stopEditing(); + }); + }, [request, stopEditing]); + + const handleRequestCreateFromEmpty = useCallback(() => { + if (!requestGroup?._id || !activeWorkspaceId) { + return; + } + createRequest({ + requestType: 'HTTP', + parentId: requestGroup?._id, + workspaceId: activeWorkspaceId, + }); + }, [requestGroup?._id, activeWorkspaceId]); + + const handleRequestActivate = useCallback(() => { + if (isActive) { + return; + } + + handleActivateRequest(request?._id); + }, [isActive, request?._id, handleActivateRequest]); + + const handleShowRequestSettings = useCallback(() => { + showModal(RequestSettingsModal, { request }); + }, [request]); + + const [methodOverrideValue, setMethodOverrideValue] = useState(null); + + useEffect(() => { + if (!request) { + return; + } + + if (isGrpcRequest(request)) { + return; + } + + const header = getMethodOverrideHeader(request.headers); + if (header) { + setMethodOverrideValue(header.value); + return; + } + + // If no override, use GraphQL as override if it's a GraphQL request + if (request.body && request.body.mimeType === CONTENT_TYPE_GRAPHQL) { + setMethodOverrideValue('GQL'); + return; + } + + setMethodOverrideValue(null); + return; + }, [request]); + + useEffect(() => { + if (!request) { + return; + } + + if (isGrpcRequest(request)) { + return; + } + + if (request.name) { + setRenderedUrl(request.name); + return; + } + + handleRender(request.url) + .then(requestUrl => { + setRenderedUrl(requestUrl); + }) + .catch(() => { + // Certain things, such as invalid variable tags and Prompts + // without titles will result in a failure to parse. Can't do + // much else, so let's just give them the unrendered URL + setRenderedUrl(request.url); + }); + + }, [handleRender, request, request?.url]); + + let node: ReactElement | null = null; + const classes = classnames('sidebar__row', { + 'sidebar__row--dragging': isDragging, + 'sidebar__row--dragging-above': isDraggingOver && dragDirection > 0, + 'sidebar__row--dragging-below': isDraggingOver && dragDirection < 0, + }); + + if (!request) { + node = ( +
  • +
    + +
    +
  • + ); + } else { + const methodTag = + isGrpcRequest(request) ? ( + + ) : ( + + ); + node = ( +
  • +
    + +
    + +
    + {isPinned && ( +
    + +
    + )} +
    +
  • + ); + } + + if (disableDragAndDrop) { + return node; + } else if (!isEditing) { + return connectDragSource(connectDropTarget(node)); + } else { + return connectDropTarget(node); + } +}); + +_SidebarRequestRow.displayName = 'SidebarRequestRow'; + +const source = DragSource('SIDEBAR_REQUEST_ROW', dragSource, sourceCollect)(_SidebarRequestRow); +export const SidebarRequestRow = DropTarget('SIDEBAR_REQUEST_ROW', dragTarget, targetCollect)(source); diff --git a/packages/insomnia/src/ui/components/wrapper-debug.tsx b/packages/insomnia/src/ui/components/wrapper-debug.tsx index 9a9695b368..b18c7f64c9 100644 --- a/packages/insomnia/src/ui/components/wrapper-debug.tsx +++ b/packages/insomnia/src/ui/components/wrapper-debug.tsx @@ -35,7 +35,6 @@ interface Props extends ReturnType { handleForceUpdateRequest: (r: Request, patch: Partial) => Promise; handleForceUpdateRequestHeaders: (r: Request, headers: RequestHeader[]) => Promise; handleImport: Function; - handleRequestCreate: () => void; handleRequestGroupCreate: () => void; handleSendAndDownloadRequestWithActiveEnvironment: (filepath?: string) => Promise; handleSendRequestWithActiveEnvironment: () => void; @@ -101,7 +100,6 @@ class UnconnectedWrapperDebug extends PureComponent { activeWorkspace, environments, handleChangeEnvironment, - handleRequestCreate, handleRequestGroupCreate, handleSidebarSort, settings, @@ -111,7 +109,6 @@ class UnconnectedWrapperDebug extends PureComponent { const { handleActivateRequest, handleCopyAsCurl, - handleCreateRequest, handleCreateRequestGroup, handleDuplicateRequest, handleDuplicateRequestGroup, @@ -146,7 +143,6 @@ class UnconnectedWrapperDebug extends PureComponent { { { settings, } = this.props; const { - handleCreateRequestForWorkspace, handleGenerateCodeForActiveRequest, handleUpdateDownloadPath, handleUpdateRequestMimeType, @@ -229,7 +223,6 @@ class UnconnectedWrapperDebug extends PureComponent { forceRefreshCounter={forceRefreshKey} forceUpdateRequest={handleForceUpdateRequest} forceUpdateRequestHeaders={handleForceUpdateRequestHeaders} - handleCreateRequest={handleCreateRequestForWorkspace} handleGenerateCode={handleGenerateCodeForActiveRequest} handleImport={handleImport} handleSend={handleSendRequestWithActiveEnvironment} diff --git a/packages/insomnia/src/ui/components/wrapper.tsx b/packages/insomnia/src/ui/components/wrapper.tsx index f0d8f725f4..b88a2d3b94 100644 --- a/packages/insomnia/src/ui/components/wrapper.tsx +++ b/packages/insomnia/src/ui/components/wrapper.tsx @@ -128,14 +128,12 @@ export type WrapperProps = AppProps & { handleSetSidebarFilter: (value: string) => Promise; handleShowSettingsModal: Function; handleSetActiveEnvironment: (environmentId: string | null) => Promise; - handleCreateRequest: (id: string, requestType?: string) => void; handleDuplicateRequest: Function; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => void; handleCreateRequestGroup: (parentId: string) => void; handleGenerateCodeForActiveRequest: Function; handleGenerateCode: Function; handleCopyAsCurl: Function; - handleCreateRequestForWorkspace: (requestType?: string) => void; requestPaneRef: Ref; responsePaneRef: Ref; handleSetResponsePreviewMode: Function; @@ -434,16 +432,6 @@ export class Wrapper extends PureComponent { this.props.handleSetResponseFilter(activeRequestId, filter); } - _handleCreateRequestInWorkspace(requestType?: string) { - const { activeWorkspace, handleCreateRequest } = this.props; - - if (!activeWorkspace) { - return; - } - - handleCreateRequest(activeWorkspace._id, requestType); - } - _handleCreateRequestGroupInWorkspace() { const { activeWorkspace, handleCreateRequestGroup } = this.props; @@ -680,45 +668,24 @@ export class Wrapper extends PureComponent { handleDeleteResponse={this._handleDeleteResponse} handleDeleteResponses={this._handleDeleteResponses} handleForceUpdateRequest={this._handleForceUpdateRequest} - handleForceUpdateRequestHeaders={ - this._handleForceUpdateRequestHeaders - } + handleForceUpdateRequestHeaders={this._handleForceUpdateRequestHeaders} handleImport={this._handleImport} - handleRequestCreate={this._handleCreateRequestInWorkspace} - handleRequestGroupCreate={ - this._handleCreateRequestGroupInWorkspace - } - handleSendAndDownloadRequestWithActiveEnvironment={ - this._handleSendAndDownloadRequestWithActiveEnvironment - } - handleSendRequestWithActiveEnvironment={ - this._handleSendRequestWithActiveEnvironment - } + handleRequestGroupCreate={this._handleCreateRequestGroupInWorkspace} + handleSendAndDownloadRequestWithActiveEnvironment={this._handleSendAndDownloadRequestWithActiveEnvironment} + handleSendRequestWithActiveEnvironment={this._handleSendRequestWithActiveEnvironment} handleSetActiveResponse={this._handleSetActiveResponse} handleSetPreviewMode={this._handleSetPreviewMode} handleSetResponseFilter={this._handleSetResponseFilter} - handleShowRequestSettingsModal={ - this._handleShowRequestSettingsModal - } + handleShowRequestSettingsModal={this._handleShowRequestSettingsModal} handleSidebarSort={handleSidebarSort} - handleUpdateRequestAuthentication={ - Wrapper._handleUpdateRequestAuthentication - } + handleUpdateRequestAuthentication={Wrapper._handleUpdateRequestAuthentication} handleUpdateRequestBody={Wrapper._handleUpdateRequestBody} - handleUpdateRequestHeaders={ - Wrapper._handleUpdateRequestHeaders - } + handleUpdateRequestHeaders={Wrapper._handleUpdateRequestHeaders} handleUpdateRequestMethod={Wrapper._handleUpdateRequestMethod} - handleUpdateRequestParameters={ - Wrapper._handleUpdateRequestParameters - } + handleUpdateRequestParameters={Wrapper._handleUpdateRequestParameters} handleUpdateRequestUrl={Wrapper._handleUpdateRequestUrl} - handleUpdateSettingsUseBulkHeaderEditor={ - this._handleUpdateSettingsUseBulkHeaderEditor - } - handleUpdateSettingsUseBulkParametersEditor={ - this._handleUpdateSettingsUseBulkParametersEditor - } + handleUpdateSettingsUseBulkHeaderEditor={this._handleUpdateSettingsUseBulkHeaderEditor} + handleUpdateSettingsUseBulkParametersEditor={this._handleUpdateSettingsUseBulkParametersEditor} wrapperProps={this.props} /> diff --git a/packages/insomnia/src/ui/containers/app.tsx b/packages/insomnia/src/ui/containers/app.tsx index 822b4a2f18..19849bd6c7 100644 --- a/packages/insomnia/src/ui/containers/app.tsx +++ b/packages/insomnia/src/ui/containers/app.tsx @@ -14,8 +14,6 @@ import { ACTIVITY_HOME, AUTOBIND_CFG, COLLAPSE_SIDEBAR_REMS, - CONTENT_TYPE_GRAPHQL, - CONTENT_TYPE_JSON, DEFAULT_PANE_HEIGHT, DEFAULT_PANE_WIDTH, DEFAULT_SIDEBAR_WIDTH, @@ -24,8 +22,6 @@ import { MAX_PANE_HEIGHT, MAX_PANE_WIDTH, MAX_SIDEBAR_REMS, - METHOD_GET, - METHOD_POST, MIN_PANE_HEIGHT, MIN_PANE_WIDTH, MIN_SIDEBAR_REMS, @@ -70,7 +66,6 @@ import { AskModal } from '../components/modals/ask-modal'; import { showCookiesModal } from '../components/modals/cookies-modal'; import { GenerateCodeModal } from '../components/modals/generate-code-modal'; import { showAlert, showModal, showPrompt } from '../components/modals/index'; -import ProtoFilesModal from '../components/modals/proto-files-modal'; import { RequestRenderErrorModal } from '../components/modals/request-render-error-modal'; import { RequestSettingsModal } from '../components/modals/request-settings-modal'; import RequestSwitcherModal from '../components/modals/request-switcher-modal'; @@ -200,19 +195,13 @@ class App extends PureComponent { }; this._savePaneWidth = debounce(paneWidth => - this._updateActiveWorkspaceMeta({ - paneWidth, - }), + this._updateActiveWorkspaceMeta({ paneWidth }), ); this._savePaneHeight = debounce(paneHeight => - this._updateActiveWorkspaceMeta({ - paneHeight, - }), + this._updateActiveWorkspaceMeta({ paneHeight }), ); this._saveSidebarWidth = debounce(sidebarWidth => - this._updateActiveWorkspaceMeta({ - sidebarWidth, - }), + this._updateActiveWorkspaceMeta({ sidebarWidth }), ); this._globalKeyMap = null; this._updateVCSLock = null; @@ -400,51 +389,6 @@ class App extends PureComponent { }); } - async _requestCreate(parentId: string, requestType?: string) { - if (requestType === 'gRPC') { - showModal(ProtoFilesModal, { - onSave: async (protoFileId: string) => { - const createdRequest = await models.grpcRequest.create({ - parentId, - name: 'New Request', - protoFileId, - }); - models.stats.incrementCreatedRequests(); - trackSegmentEvent(SegmentEvent.requestCreate, { requestType }); - this._handleSetActiveRequest(createdRequest._id); - }, - }); - return; - } - if (requestType === 'GraphQL') { - const request = await models.request.create({ - parentId, - method: METHOD_POST, - headers:[{ - name: 'Content-Type', - value: CONTENT_TYPE_JSON, - }], - body:{ - mimeType: CONTENT_TYPE_GRAPHQL, - text: '', - }, - name: 'New Request', - }); - models.stats.incrementCreatedRequests(); - trackSegmentEvent(SegmentEvent.requestCreate, { requestType }); - this._handleSetActiveRequest(request._id); - return; - } - const request = await models.request.create({ - parentId, - method: METHOD_GET, - name: 'New Request', - }); - models.stats.incrementCreatedRequests(); - trackSegmentEvent(SegmentEvent.requestCreate, { requestType: 'HTTP' }); - this._handleSetActiveRequest(request._id); - } - async _recalculateMetaSortKey(docs: (RequestGroup | Request | GrpcRequest)[]) { function __updateDoc(doc, metaSortKey) { // @ts-expect-error -- TSCONVERSION the fetched model will only ever be a RequestGroup, Request, or GrpcRequest @@ -901,12 +845,6 @@ class App extends PureComponent { } } - _requestCreateForWorkspace(requestType?: string) { - if (this.props.activeWorkspace) { - this._requestCreate(this.props.activeWorkspace._id, requestType); - } - } - _startDragSidebar() { this.setState({ draggingSidebar: true, @@ -1508,7 +1446,6 @@ class App extends PureComponent { paneWidth={paneWidth} paneHeight={paneHeight} sidebarWidth={sidebarWidth} - handleCreateRequestForWorkspace={this._requestCreateForWorkspace} handleSetRequestPinned={this._handleSetRequestPinned} handleSetRequestGroupCollapsed={this._handleSetRequestGroupCollapsed} handleActivateRequest={this._handleSetActiveRequest} @@ -1521,7 +1458,6 @@ class App extends PureComponent { handleStartDragPaneVertical={this._startDragPaneVertical} handleResetDragPaneHorizontal={this._resetDragPaneHorizontal} handleResetDragPaneVertical={this._resetDragPaneVertical} - handleCreateRequest={this._requestCreate} handleDuplicateRequest={this._requestDuplicate} handleDuplicateRequestGroup={App._requestGroupDuplicate} handleCreateRequestGroup={this._requestGroupCreate} diff --git a/packages/insomnia/src/ui/hooks/create-request.ts b/packages/insomnia/src/ui/hooks/create-request.ts new file mode 100644 index 0000000000..3f5e5310a9 --- /dev/null +++ b/packages/insomnia/src/ui/hooks/create-request.ts @@ -0,0 +1,124 @@ +import { unreachableCase } from 'ts-assert-unreachable'; + +import { SegmentEvent, trackSegmentEvent } from '../../common/analytics'; +import { + CONTENT_TYPE_GRAPHQL, + CONTENT_TYPE_JSON, + METHOD_GET, + METHOD_POST, +} from '../../common/constants'; +import * as models from '../../models'; +import { isGrpcRequestId } from '../../models/grpc-request'; +import { GrpcRequestMeta } from '../../models/grpc-request-meta'; +import { RequestMeta } from '../../models/request-meta'; +import { WorkspaceMeta } from '../../models/workspace-meta'; +import { showModal } from '../components/modals'; +import ProtoFilesModal from '../components/modals/proto-files-modal'; + +export const updateActiveWorkspaceMeta = async ( + patch: Partial, + workspaceId: string +) => { + const activeWorkspaceMeta = await models.workspaceMeta.getByParentId( + workspaceId + ); + + if (activeWorkspaceMeta) { + await models.workspaceMeta.update(activeWorkspaceMeta, patch); + } +}; + +export const updateRequestMetaByParentId = async ( + requestId: string, + patch: Partial | Partial +) => { + if (isGrpcRequestId(requestId)) { + await models.grpcRequestMeta.updateOrCreateByParentId(requestId, patch); + return; + } + + await models.requestMeta.updateOrCreateByParentId(requestId, patch); +}; + +export const setActiveRequest = async ( + activeRequestId: string, + workspaceId: string +) => { + await updateActiveWorkspaceMeta({ activeRequestId }, workspaceId); + await updateRequestMetaByParentId(activeRequestId, { + lastActive: Date.now(), + }); +}; + +export type CreateRequestType = 'HTTP' | 'gRPC' | 'GraphQL'; +type RequestCreator = (input: { + parentId: string; + requestType: CreateRequestType; + workspaceId: string; +}) => Promise; + +export const createRequest: RequestCreator = async ({ + parentId, + requestType, + workspaceId, +}) => { + let requestId = ''; + + switch (requestType) { + case 'gRPC': { + showModal(ProtoFilesModal, { + onSave: async (protoFileId: string) => { + const createdRequest = await models.grpcRequest.create({ + parentId, + name: 'New Request', + protoFileId, + }); + models.stats.incrementCreatedRequests(); + requestId = createdRequest._id; + }, + }); + break; + } + + case 'GraphQL': { + const request = await models.request.create({ + parentId, + method: METHOD_POST, + headers: [ + { + name: 'Content-Type', + value: CONTENT_TYPE_JSON, + }, + ], + body: { + mimeType: CONTENT_TYPE_GRAPHQL, + text: '', + }, + name: 'New Request', + }); + models.stats.incrementCreatedRequests(); + requestId = request._id; + break; + } + + case 'HTTP': { + const request = await models.request.create({ + parentId, + method: METHOD_GET, + name: 'New Request', + }); + models.stats.incrementCreatedRequests(); + requestId = request._id; + break; + } + + default: + unreachableCase( + requestType, + "tried to create a request but didn't specify the type" + ); + } + + trackSegmentEvent(SegmentEvent.requestCreate, { requestType }); + setActiveRequest(requestId, workspaceId); +};