From 9857ee8712e005503bbdd11cab5c5ff903e7d043 Mon Sep 17 00:00:00 2001 From: James Gatz Date: Fri, 16 Sep 2022 14:56:41 +0200 Subject: [PATCH] Force refresh cleanup (#5184) * remove forceRefreshCounter from app.tsx * simplify refresh counter for ws request pane * remove forceRefreshKey from remaining panes * remove unused nunjucks key * cleanup forceUpdate drills * undrill handleSetActiveResponse * put restore back in * more uniqueness Co-authored-by: jackkav --- .../insomnia/src/models/websocket-response.ts | 28 +++++++- .../dropdowns/response-history-dropdown.tsx | 38 ++++++++-- .../components/editors/body/body-editor.tsx | 17 ++--- .../ui/components/modals/nunjucks-modal.tsx | 5 +- .../panes/grpc-request-pane/index.tsx | 13 ++-- .../components/panes/grpc-response-pane.tsx | 15 ++-- .../src/ui/components/panes/request-pane.tsx | 27 ++++--- .../src/ui/components/panes/response-pane.tsx | 3 - .../websockets/websocket-request-pane.tsx | 24 ++++--- .../websockets/websocket-response-pane.tsx | 14 +--- .../src/ui/components/wrapper-debug.tsx | 22 +----- .../insomnia/src/ui/components/wrapper.tsx | 70 +------------------ packages/insomnia/src/ui/containers/app.tsx | 53 ++------------ .../insomnia/src/ui/hooks/use-vcs-version.ts | 35 ++++++++++ 14 files changed, 161 insertions(+), 203 deletions(-) create mode 100644 packages/insomnia/src/ui/hooks/use-vcs-version.ts diff --git a/packages/insomnia/src/models/websocket-response.ts b/packages/insomnia/src/models/websocket-response.ts index 74bab0207e..1e31587fb6 100644 --- a/packages/insomnia/src/models/websocket-response.ts +++ b/packages/insomnia/src/models/websocket-response.ts @@ -1,6 +1,6 @@ import fs from 'fs'; -import { database as db } from '../common/database'; +import { database as db, Query } from '../common/database'; import * as requestOperations from './helpers/request-operations'; import type { BaseModel } from './index'; import * as models from './index'; @@ -144,6 +144,32 @@ export async function create(patch: Partial = {}, maxResponse return db.docCreate(type, patch); } +async function _findRecentForRequest( + requestId: string, + environmentId: string | null, + limit: number, +) { + const query: Query = { + parentId: requestId, + }; + + // Filter responses by environment if setting is enabled + if ((await models.settings.getOrCreate()).filterResponsesByEnv) { + query.environmentId = environmentId; + } + + return db.findMostRecentlyModified(type, query, limit); +} + +export async function getLatestForRequest( + requestId: string, + environmentId: string | null, +) { + const responses = await _findRecentForRequest(requestId, environmentId, 1); + const response = responses[0] as WebSocketResponse | null | undefined; + return response || null; +} + export function getLatestByParentId(parentId: string) { return db.getMostRecentlyModified(type, { parentId, 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 064ccd06d2..c7d6219c97 100644 --- a/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx +++ b/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx @@ -8,6 +8,7 @@ import { decompressObject } from '../../../common/misc'; import * as models from '../../../models/index'; import { Response } from '../../../models/response'; import { isWebSocketResponse, WebSocketResponse } from '../../../models/websocket-response'; +import { updateRequestMetaByParentId } from '../../hooks/create-request'; import { selectActiveEnvironment, selectActiveRequest, selectActiveRequestResponses, selectRequestVersions } from '../../redux/selectors'; import { type DropdownHandle, Dropdown } from '../base/dropdown/dropdown'; import { DropdownButton } from '../base/dropdown/dropdown-button'; @@ -23,14 +24,12 @@ import { TimeFromNow } from '../time-from-now'; interface Props { activeResponse: GenericResponse; - handleSetActiveResponse: (requestId: string, activeResponse: GenericResponse | null) => void; className?: string; requestId: string; } export const ResponseHistoryDropdown = ({ activeResponse, - handleSetActiveResponse, className, requestId, }: Props) => { @@ -48,29 +47,53 @@ export const ResponseHistoryDropdown = { + if (isWebSocketResponse(activeResponse)) { + window.main.webSocket.close({ requestId }); + } + + if (activeResponse.requestVersionId) { + await models.requestVersion.restore(activeResponse.requestVersionId); + } + + await updateRequestMetaByParentId(requestId, { activeResponseId: activeResponse._id }); + }, []); + const handleDeleteResponses = useCallback(async () => { const environmentId = activeEnvironment ? activeEnvironment._id : null; if (isWebSocketResponse(activeResponse)) { + window.main.webSocket.closeAll(); await models.webSocketResponse.removeForRequest(requestId, environmentId); } else { await models.response.removeForRequest(requestId, environmentId); } - if (activeRequest && activeRequest._id === requestId) { - handleSetActiveResponse(requestId, null); + await updateRequestMetaByParentId(requestId, { activeResponseId: null }); } - }, [activeEnvironment, activeRequest, activeResponse, handleSetActiveResponse, requestId]); + }, [activeEnvironment, activeRequest, activeResponse, requestId]); const handleDeleteResponse = useCallback(async () => { + let response: Response | WebSocketResponse | null = null; if (activeResponse) { if (isWebSocketResponse(activeResponse)) { + window.main.webSocket.close({ requestId }); await models.webSocketResponse.remove(activeResponse); + const environmentId = activeEnvironment?._id || null; + response = await models.webSocketResponse.getLatestForRequest(requestId, environmentId); } else { await models.response.remove(activeResponse); + const environmentId = activeEnvironment?._id || null; + response = await models.response.getLatestForRequest(requestId, environmentId); } + + if (response?.requestVersionId) { + // Deleting a response restores latest request body + await models.requestVersion.restore(response.requestVersionId); + } + + await updateRequestMetaByParentId(requestId, { activeResponseId: response?._id || null }); } - handleSetActiveResponse(requestId, null); - }, [activeResponse, handleSetActiveResponse, requestId]); + }, [activeEnvironment?._id, activeResponse, requestId]); responses.forEach(response => { const responseTime = new Date(response.created); @@ -103,6 +126,7 @@ export const ResponseHistoryDropdown = _id === response.requestVersionId); const request = requestVersion ? decompressObject(requestVersion.compressedRequest) : null; + return ( Promise; request: Request; workspace: Workspace; settings: Settings; @@ -45,7 +43,6 @@ interface Props { } export const BodyEditor: FC = ({ - onChangeHeaders, request, workspace, settings, @@ -54,28 +51,28 @@ export const BodyEditor: FC = ({ const handleRawChange = useCallback((rawValue: string) => { const oldContentType = request.body.mimeType || ''; const body = newBodyRaw(rawValue, oldContentType); - update(request, { body }); + models.request.update(request, { body }); }, [request]); const handleGraphQLChange = useCallback((content: string) => { const body = newBodyRaw(content, CONTENT_TYPE_GRAPHQL); - update(request, { body }); + models.request.update(request, { body }); }, [request]); const handleFormUrlEncodedChange = useCallback((parameters: RequestBodyParameter[]) => { const body = newBodyFormUrlEncoded(parameters); - update(request, { body }); + models.request.update(request, { body }); }, [request]); const handleFormChange = useCallback((parameters: RequestBodyParameter[]) => { const body = newBodyForm(parameters); - update(request, { body }); + models.request.update(request, { body }); }, [request]); const handleFileChange = async (path: string) => { const headers = clone(request.headers); const body = newBodyFile(path); - const newRequest = await update(request, { body }); + const newRequest = await models.request.update(request, { body }); let contentTypeHeader = getContentTypeHeader(headers); if (!contentTypeHeader) { @@ -100,7 +97,7 @@ export const BodyEditor: FC = ({

, onDone: (saidYes: boolean) => { if (saidYes) { - onChangeHeaders(newRequest, headers); + models.request.update(newRequest, { headers }); } }, }); diff --git a/packages/insomnia/src/ui/components/modals/nunjucks-modal.tsx b/packages/insomnia/src/ui/components/modals/nunjucks-modal.tsx index f782ae71b5..998e3e4b7f 100644 --- a/packages/insomnia/src/ui/components/modals/nunjucks-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/nunjucks-modal.tsx @@ -11,7 +11,6 @@ import { TagEditor } from '../templating/tag-editor'; import { VariableEditor } from '../templating/variable-editor'; interface Props { - uniqueKey: string; workspace: Workspace; } @@ -66,7 +65,7 @@ export class NunjucksModal extends PureComponent { } render() { - const { uniqueKey, workspace } = this.props; + const { workspace } = this.props; const { defaultTemplate } = this.state; let editor: JSX.Element | null = null; let title = ''; @@ -80,7 +79,7 @@ export class NunjucksModal extends PureComponent { } return ( - + Edit {title}
{editor}
diff --git a/packages/insomnia/src/ui/components/panes/grpc-request-pane/index.tsx b/packages/insomnia/src/ui/components/panes/grpc-request-pane/index.tsx index 8faf4bfc32..ef01d0a7b6 100644 --- a/packages/insomnia/src/ui/components/panes/grpc-request-pane/index.tsx +++ b/packages/insomnia/src/ui/components/panes/grpc-request-pane/index.tsx @@ -1,5 +1,6 @@ import { SvgIcon } from 'insomnia-components'; import React, { FunctionComponent, useCallback } from 'react'; +import { useSelector } from 'react-redux'; import { Tab, TabList, TabPanel, Tabs } from 'react-tabs'; import styled from 'styled-components'; @@ -10,6 +11,8 @@ import { executeHotKey } from '../../../../common/hotkeys-listener'; import type { GrpcRequest } from '../../../../models/grpc-request'; import type { Settings } from '../../../../models/settings'; import { useGrpc } from '../../../context/grpc'; +import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../../hooks/use-vcs-version'; +import { selectActiveEnvironment } from '../../../redux/selectors'; import { GrpcSendButton } from '../../buttons/grpc-send-button'; import { OneLineEditor } from '../../codemirror/one-line-editor'; import { GrpcMethodDropdown } from '../../dropdowns/grpc-method-dropdown/grpc-method-dropdown'; @@ -26,7 +29,6 @@ import useProtoFileReload from './use-proto-file-reload'; import useSelectedMethod from './use-selected-method'; interface Props { - forceRefreshKey: number; activeRequest: GrpcRequest; environmentId: string; workspaceId: string; @@ -55,7 +57,6 @@ export const GrpcRequestPane: FunctionComponent = ({ activeRequest, environmentId, workspaceId, - forceRefreshKey, }) => { const [state, dispatch] = useGrpc(activeRequest._id); const { requestMessages, running, methods } = state; @@ -66,9 +67,11 @@ export const GrpcRequestPane: FunctionComponent = ({ // @ts-expect-error -- TSCONVERSION methodType can be undefined const handleAction = useActionHandlers(activeRequest._id, environmentId, methodType, dispatch); const getExistingGrpcUrls = useExistingGrpcUrls(workspaceId, activeRequest._id); - // Used to refresh input fields to their default value when switching between requests. - // This is a common pattern in this codebase. - const uniquenessKey = `${forceRefreshKey}::${activeRequest._id}`; + const gitVersion = useGitVCSVersion(); + const activeRequestSyncVersion = useActiveRequestSyncVCSVersion(); + const activeEnvironment = useSelector(selectActiveEnvironment); + // Reset the response pane state when we switch requests, the environment gets modified, or the (Git|Sync)VCS version changes + const uniquenessKey = `${activeEnvironment?.modified}::${activeRequest?._id}::${gitVersion}::${activeRequestSyncVersion}`; const { start } = handleAction; const _handleKeyDown = useCallback((event: KeyboardEvent) => { diff --git a/packages/insomnia/src/ui/components/panes/grpc-response-pane.tsx b/packages/insomnia/src/ui/components/panes/grpc-response-pane.tsx index f6546bded8..8de56e25c0 100644 --- a/packages/insomnia/src/ui/components/panes/grpc-response-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/grpc-response-pane.tsx @@ -1,21 +1,26 @@ import React, { FunctionComponent } from 'react'; +import { useSelector } from 'react-redux'; import type { GrpcRequest } from '../../../models/grpc-request'; import { useGrpcRequestState } from '../../context/grpc'; +import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/use-vcs-version'; +import { selectActiveEnvironment } from '../../redux/selectors'; import { GrpcSpinner } from '../grpc-spinner'; import { GrpcStatusTag } from '../tags/grpc-status-tag'; import { GrpcTabbedMessages } from '../viewers/grpc-tabbed-messages'; import { Pane, PaneBody, PaneHeader } from './pane'; interface Props { - forceRefreshKey: number; activeRequest: GrpcRequest; } -export const GrpcResponsePane: FunctionComponent = ({ activeRequest, forceRefreshKey }) => { - // Used to refresh input fields to their default value when switching between requests. - // This is a common pattern in this codebase. - const uniquenessKey = `${forceRefreshKey}::${activeRequest._id}`; +export const GrpcResponsePane: FunctionComponent = ({ activeRequest }) => { + const gitVersion = useGitVCSVersion(); + const activeRequestSyncVersion = useActiveRequestSyncVCSVersion(); + const activeEnvironment = useSelector(selectActiveEnvironment); + // Force re-render when we switch requests, the environment gets modified, or the (Git|Sync)VCS version changes + const uniquenessKey = `${activeEnvironment?.modified}::${activeRequest?._id}::${gitVersion}::${activeRequestSyncVersion}`; + const { responseMessages, status, error } = useGrpcRequestState(activeRequest._id); return ( diff --git a/packages/insomnia/src/ui/components/panes/request-pane.tsx b/packages/insomnia/src/ui/components/panes/request-pane.tsx index 38053f6a86..437501c779 100644 --- a/packages/insomnia/src/ui/components/panes/request-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/request-pane.tsx @@ -1,6 +1,7 @@ import classnames from 'classnames'; import { deconstructQueryStringToParams, extractQueryStringFromUrl } from 'insomnia-url'; import React, { FC, useCallback, useEffect, useRef } from 'react'; +import { useSelector } from 'react-redux'; import { Tab, TabList, TabPanel, Tabs } from 'react-tabs'; import { useMount } from 'react-use'; @@ -8,12 +9,11 @@ import { getContentTypeFromHeaders } from '../../../common/constants'; import * as models from '../../../models'; import { queryAllWorkspaceUrls } from '../../../models/helpers/query-all-workspace-urls'; import { update } from '../../../models/helpers/request-operations'; -import type { - Request, - RequestHeader, -} from '../../../models/request'; +import type { Request } from '../../../models/request'; import type { Settings } from '../../../models/settings'; import type { Workspace } from '../../../models/workspace'; +import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/use-vcs-version'; +import { selectActiveEnvironment, selectActiveRequestMeta } from '../../redux/selectors'; import { AuthDropdown } from '../dropdowns/auth-dropdown'; import { ContentTypeDropdown } from '../dropdowns/content-type-dropdown'; import { AuthWrapper } from '../editors/auth/auth-wrapper'; @@ -31,9 +31,6 @@ import { PlaceholderRequestPane } from './placeholder-request-pane'; interface Props { environmentId: string; - forceRefreshCounter: number; - forceUpdateRequest: (r: Request, patch: Partial) => Promise; - forceUpdateRequestHeaders: (r: Request, headers: RequestHeader[]) => Promise; handleImport: Function; request?: Request | null; settings: Settings; @@ -42,9 +39,6 @@ interface Props { export const RequestPane: FC = ({ environmentId, - forceRefreshCounter, - forceUpdateRequest, - forceUpdateRequestHeaders, handleImport, request, settings, @@ -99,12 +93,12 @@ export const RequestPane: FC = ({ // Only update if url changed if (url !== request.url) { - forceUpdateRequest(request, { + models.request.update(request, { url, parameters, }); } - }, [request, forceUpdateRequest]); + }, [request]); const requestUrlBarRef = useRef(null); useMount(() => { @@ -116,6 +110,13 @@ export const RequestPane: FC = ({ request?._id, // happens when the user switches requests settings.hasPromptedAnalytics, // happens when the user dismisses the analytics modal ]); + const gitVersion = useGitVCSVersion(); + const activeRequestSyncVersion = useActiveRequestSyncVCSVersion(); + const activeEnvironment = useSelector(selectActiveEnvironment); + // const activeResponse = useSelector(selectActiveResponse); + const activeRequestMeta = useSelector(selectActiveRequestMeta); + // Force re-render when we switch requests, the environment gets modified, or the (Git|Sync)VCS version changes + const uniqueKey = `${activeEnvironment?.modified}::${request?._id}::${gitVersion}::${activeRequestSyncVersion}::${activeRequestMeta?.activeResponseId}`; if (!request) { return ( @@ -139,7 +140,6 @@ export const RequestPane: FC = ({ const numParameters = request.parameters.filter(p => !p.disabled).length; const numHeaders = request.headers.filter(h => !h.disabled).length; const urlHasQueryParameters = request.url.indexOf('?') >= 0; - const uniqueKey = `${forceRefreshCounter}::${request._id}`; const contentType = getContentTypeFromHeaders(request.headers) || request.body.mimeType; return ( @@ -195,7 +195,6 @@ export const RequestPane: FC = ({ workspace={workspace} environmentId={environmentId} settings={settings} - onChangeHeaders={forceUpdateRequestHeaders} /> diff --git a/packages/insomnia/src/ui/components/panes/response-pane.tsx b/packages/insomnia/src/ui/components/panes/response-pane.tsx index fef468a45c..e7fff444a8 100644 --- a/packages/insomnia/src/ui/components/panes/response-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/response-pane.tsx @@ -33,12 +33,10 @@ import { PlaceholderResponsePane } from './placeholder-response-pane'; interface Props { handleSetFilter: (filter: string) => void; - handleSetActiveResponse: (requestId: string, activeResponse: Response | null) => void; request?: Request | null; } export const ResponsePane: FC = ({ handleSetFilter, - handleSetActiveResponse, request, }) => { const response = useSelector(selectActiveResponse) as Response | null; @@ -135,7 +133,6 @@ export const ResponsePane: FC = ({ diff --git a/packages/insomnia/src/ui/components/websockets/websocket-request-pane.tsx b/packages/insomnia/src/ui/components/websockets/websocket-request-pane.tsx index 4d7ba35c11..cb9ceb564b 100644 --- a/packages/insomnia/src/ui/components/websockets/websocket-request-pane.tsx +++ b/packages/insomnia/src/ui/components/websockets/websocket-request-pane.tsx @@ -6,9 +6,11 @@ import styled from 'styled-components'; import { AuthType, CONTENT_TYPE_JSON } from '../../../common/constants'; import { getRenderContext, render, RENDER_PURPOSE_SEND } from '../../../common/render'; import * as models from '../../../models'; +import { Environment } from '../../../models/environment'; import { WebSocketRequest } from '../../../models/websocket-request'; import { ReadyState, useWSReadyState } from '../../context/websocket-client/use-ws-ready-state'; -import { selectSettings } from '../../redux/selectors'; +import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/use-vcs-version'; +import { selectActiveRequestMeta, selectSettings } from '../../redux/selectors'; import { CodeEditor, UnconnectedCodeEditor } from '../codemirror/code-editor'; import { AuthDropdown } from '../dropdowns/auth-dropdown'; import { WebSocketPreviewModeDropdown } from '../dropdowns/websocket-preview-mode'; @@ -152,15 +154,14 @@ const WebSocketRequestForm: FC = ({ interface Props { request: WebSocketRequest; workspaceId: string; - environmentId: string; - forceRefreshKey: number; + environment: Environment | null; } // requestId is something we can read from the router params in the future. // essentially we can lift up the states and merge request pane and response pane into a single page and divide the UI there. // currently this is blocked by the way page layout divide the panes with dragging functionality // TODO: @gatzjames discuss above assertion in light of request and settings drills -export const WebSocketRequestPane: FC = ({ request, workspaceId, environmentId, forceRefreshKey }) => { +export const WebSocketRequestPane: FC = ({ request, workspaceId, environment }) => { const readyState = useWSReadyState(request._id); const { useBulkParametersEditor } = useSelector(selectSettings); @@ -205,7 +206,12 @@ export const WebSocketRequestPane: FC = ({ request, workspaceId, environm } }; - const uniqueKey = `${forceRefreshKey}::${request._id}`; + const gitVersion = useGitVCSVersion(); + const activeRequestSyncVersion = useActiveRequestSyncVCSVersion(); + const activeRequestMeta = useSelector(selectActiveRequestMeta); + + // Reset the response pane state when we switch requests, the environment gets modified, or the (Git|Sync)VCS version changes + const uniqueKey = `${environment?.modified}::${request?._id}::${gitVersion}::${activeRequestSyncVersion}::${activeRequestMeta?.activeResponseId}`; return ( @@ -214,7 +220,7 @@ export const WebSocketRequestPane: FC = ({ request, workspaceId, environm key={uniqueKey} request={request} workspaceId={workspaceId} - environmentId={environmentId} + environmentId={environment?._id || ''} defaultValue={request.url} readyState={readyState} onChange={handleOnChange} @@ -249,12 +255,12 @@ export const WebSocketRequestPane: FC = ({ request, workspaceId, environm key={uniqueKey} request={request} previewMode={previewMode} - environmentId={environmentId} + environmentId={environment?._id || ''} /> @@ -285,7 +291,7 @@ export const WebSocketRequestPane: FC = ({ request, workspaceId, environm void }> = +export const WebSocketResponsePane: FC<{ requestId: string }> = ({ requestId, - handleSetActiveResponse, }) => { const response = useSelector(selectActiveResponse) as WebSocketResponse | null; if (!response) { @@ -72,13 +71,12 @@ export const WebSocketResponsePane: FC<{ requestId: string; handleSetActiveRespo ); } - return ; + return ; }; -const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketResponse; handleSetActiveResponse: (requestId: string, activeResponse: WebSocketResponse | null) => void }> = ({ +const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketResponse }> = ({ requestId, response, - handleSetActiveResponse, }) => { const [selectedEvent, setSelectedEvent] = useState(null); const [timeline, setTimeline] = useState([]); @@ -87,11 +85,6 @@ const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketRe setSelectedEvent((selected: WebSocketEvent | null) => selected?._id === event._id ? null : event); }; - const setActiveResponseAndDisconnect = (requestId: string, response: WebSocketResponse | null) => { - handleSetActiveResponse(requestId, response); - window.main.webSocket.close({ requestId }); - }; - useEffect(() => { setSelectedEvent(null); }, [response._id]); @@ -123,7 +116,6 @@ const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketRe diff --git a/packages/insomnia/src/ui/components/wrapper-debug.tsx b/packages/insomnia/src/ui/components/wrapper-debug.tsx index 9f15f7f805..6d18fb41ab 100644 --- a/packages/insomnia/src/ui/components/wrapper-debug.tsx +++ b/packages/insomnia/src/ui/components/wrapper-debug.tsx @@ -3,10 +3,7 @@ import { useSelector } from 'react-redux'; import { isGrpcRequest } from '../../models/grpc-request'; import { isRemoteProject } from '../../models/project'; -import { Request, RequestHeader } from '../../models/request'; -import type { Response } from '../../models/response'; import { isWebSocketRequest } from '../../models/websocket-request'; -import { WebSocketResponse } from '../../models/websocket-response'; import { isCollection, isDesign } from '../../models/workspace'; import { VCS } from '../../sync/vcs/vcs'; import { @@ -35,25 +32,17 @@ import { WorkspacePageHeader } from './workspace-page-header'; import type { HandleActivityChange } from './wrapper'; interface Props { - forceRefreshKey: number; gitSyncDropdown: ReactNode; handleActivityChange: HandleActivityChange; handleSetActiveEnvironment: (id: string | null) => void; - handleSetActiveResponse: (requestId: string, activeResponse: Response | WebSocketResponse | null) => void; - handleForceUpdateRequest: (r: Request, patch: Partial) => Promise; - handleForceUpdateRequestHeaders: (r: Request, headers: RequestHeader[]) => Promise; handleImport: Function; handleSetResponseFilter: (filter: string) => void; vcs: VCS | null; } export const WrapperDebug: FC = ({ - forceRefreshKey, gitSyncDropdown, handleActivityChange, handleSetActiveEnvironment, - handleSetActiveResponse, - handleForceUpdateRequest, - handleForceUpdateRequestHeaders, handleImport, handleSetResponseFilter, vcs, @@ -121,24 +110,18 @@ export const WrapperDebug: FC = ({ activeRequest={activeRequest} environmentId={activeEnvironment ? activeEnvironment._id : ''} workspaceId={activeWorkspace._id} - forceRefreshKey={forceRefreshKey} settings={settings} /> ) : ( isWebSocketRequest(activeRequest) ? ( ) : ( = ({ isGrpcRequest(activeRequest) ? ( ) : ( isWebSocketRequest(activeRequest) ? ( ) : ( ) ) diff --git a/packages/insomnia/src/ui/components/wrapper.tsx b/packages/insomnia/src/ui/components/wrapper.tsx index 0ff00daeed..fc7fda60fb 100644 --- a/packages/insomnia/src/ui/components/wrapper.tsx +++ b/packages/insomnia/src/ui/components/wrapper.tsx @@ -15,20 +15,15 @@ import { } from '../../common/constants'; import { importRaw } from '../../common/import'; import { initializeSpectral, isLintError } from '../../common/spectral'; -import { update } from '../../models/helpers/request-operations'; +import * as requestOperations from '../../models/helpers/request-operations'; import * as models from '../../models/index'; import { isRequest, - Request, - RequestHeader, } from '../../models/request'; -import { Response } from '../../models/response'; -import { WebSocketResponse } from '../../models/websocket-response'; import { GitVCS } from '../../sync/git/git-vcs'; import { VCS } from '../../sync/vcs/vcs'; import { CookieModifyModal } from '../components/modals/cookie-modify-modal'; import { GrpcDispatchModalWrapper } from '../context/grpc'; -import { updateRequestMetaByParentId } from '../hooks/create-request'; import { RootState } from '../redux/modules'; import { setActiveActivity } from '../redux/modules/global'; import { selectActiveActivity, selectActiveApiSpec, selectActiveCookieJar, selectActiveEnvironment, selectActiveGitRepository, selectActiveRequest, selectActiveResponse, selectActiveWorkspace, selectActiveWorkspaceMeta, selectSettings } from '../redux/selectors'; @@ -131,29 +126,15 @@ export type HandleActivityChange = (options: { }) => Promise; interface State { - forceRefreshKey: number; activeGitBranch: string; } @autoBindMethodsForReact(AUTOBIND_CFG) export class WrapperClass extends PureComponent { state: State = { - forceRefreshKey: Date.now(), activeGitBranch: 'no-vcs', }; - // Request updaters - async _handleForceUpdateRequest(r: Request, patch: Partial) { - const newRequest = await update(r, patch); - this._forceRequestPaneRefreshAfterDelay(); - - return newRequest; - } - - _handleForceUpdateRequestHeaders(r: Request, headers: RequestHeader[]) { - return this._handleForceUpdateRequest(r, { headers }); - } - async _handleImport(text: string) { const { activeRequest } = this.props; @@ -166,7 +147,7 @@ export class WrapperClass extends PureComponent { if (r && r._type === 'request' && activeRequest && isRequest(activeRequest)) { // Only pull fields that we want to update - return this._handleForceUpdateRequest(activeRequest, { + return requestOperations.update(activeRequest, { url: r.url, method: r.method, headers: r.headers, @@ -184,31 +165,6 @@ export class WrapperClass extends PureComponent { return null; } - async handleSetActiveResponse(requestId: string, activeResponse: Response | WebSocketResponse | null = null) { - const { activeEnvironment } = this.props; - const activeResponseId = activeResponse ? activeResponse._id : null; - await updateRequestMetaByParentId(requestId, { - activeResponseId, - }); - - let response: Response | null; - if (activeResponseId) { - response = await models.response.getById(activeResponseId); - } else { - const environmentId = activeEnvironment ? activeEnvironment._id : null; - response = await models.response.getLatestForRequest(requestId, environmentId); - } - if (!response || !response.requestVersionId) { - return; - } - const request = await models.requestVersion.restore(response.requestVersionId); - if (!request) { - return; - } - // Refresh app to reflect changes. Using timeout because we need to - // wait for the request update to propagate. - setTimeout(() => this._forceRequestPaneRefresh(), 500); - } async _handleWorkspaceActivityChange({ workspaceId, nextActivity }: Parameters[0]): ReturnType { const { activeActivity, activeApiSpec, handleSetActiveActivity } = this.props; @@ -275,19 +231,6 @@ export class WrapperClass extends PureComponent { this.props.handleSetResponseFilter(activeRequestId, filter); } - _forceRequestPaneRefreshAfterDelay(): void { - // Give it a second for the app to render first. If we don't wait, it will refresh - // on the old request and won't catch the newest one. - // TODO: Move this refresh key into redux store so we don't need timeout - window.setTimeout(this._forceRequestPaneRefresh, 100); - } - - _forceRequestPaneRefresh() { - this.setState({ - forceRefreshKey: Date.now(), - }); - } - _handleGitBranchChanged(branch: string) { this.setState({ activeGitBranch: branch || 'no-vcs', @@ -298,10 +241,6 @@ export class WrapperClass extends PureComponent { if (this.props.activeWorkspaceMeta) { await models.workspaceMeta.update(this.props.activeWorkspaceMeta, { activeEnvironmentId }); } - // Give it time to update and re-render - setTimeout(() => { - this._forceRequestPaneRefresh(); - }, 300); } render() { @@ -366,7 +305,6 @@ export class WrapperClass extends PureComponent { : null} @@ -480,15 +418,11 @@ export class WrapperClass extends PureComponent { element={ }> diff --git a/packages/insomnia/src/ui/containers/app.tsx b/packages/insomnia/src/ui/containers/app.tsx index e1191e5bd0..4b42c03d2d 100644 --- a/packages/insomnia/src/ui/containers/app.tsx +++ b/packages/insomnia/src/ui/containers/app.tsx @@ -21,7 +21,6 @@ import { generateId, } from '../../common/misc'; import * as models from '../../models'; -import { isEnvironment } from '../../models/environment'; import { GrpcRequest, isGrpcRequest } from '../../models/grpc-request'; import { getByParentId as getGrpcRequestMetaByParentId } from '../../models/grpc-request-meta'; import * as requestOperations from '../../models/helpers/request-operations'; @@ -55,7 +54,7 @@ import { SyncMergeModal } from '../components/modals/sync-merge-modal'; import { WorkspaceEnvironmentsEditModal } from '../components/modals/workspace-environments-edit-modal'; import { WorkspaceSettingsModal } from '../components/modals/workspace-settings-modal'; import { Toast } from '../components/toast'; -import { type WrapperClass, Wrapper } from '../components/wrapper'; +import { Wrapper } from '../components/wrapper'; import withDragDropContext from '../context/app/drag-drop-context'; import { GrpcProvider } from '../context/grpc'; import { NunjucksEnabledProvider } from '../context/nunjucks/nunjucks-enabled-context'; @@ -89,7 +88,6 @@ export type AppProps = ReturnType & ReturnType { private _globalKeyMap: any; private _updateVCSLock: any; - private _wrapper: WrapperClass | null = null; private _responseFilterHistorySaveTimeout: NodeJS.Timeout | null = null; constructor(props: AppProps) { @@ -106,7 +103,6 @@ class App extends PureComponent { this.state = { vcs: null, gitVCS: null, - forceRefreshCounter: 0, isMigratingChildren: false, }; @@ -356,10 +352,6 @@ class App extends PureComponent { showModal(SettingsModal, tabIndex); } - _setWrapperRef(wrapper: WrapperClass) { - this._wrapper = wrapper; - } - async _handleReloadPlugins() { const { settings } = this.props; await plugins.reloadPlugins(); @@ -406,13 +398,6 @@ class App extends PureComponent { this._ensureWorkspaceChildren(); - // Force app refresh if login state changes - if (prevProps.isLoggedIn !== this.props.isLoggedIn) { - this.setState(state => ({ - forceRefreshCounter: state.forceRefreshCounter + 1, - })); - } - // Check on VCS things const { activeWorkspace, activeProject, activeGitRepository } = this.props; const changingWorkspace = prevProps.activeWorkspace?._id !== activeWorkspace?._id; @@ -540,38 +525,16 @@ class App extends PureComponent { } } - async _handleDbChange(changes: ChangeBufferEvent[]) { - let needsRefresh = false; - + async listenforWorkspaceDelete(changes: ChangeBufferEvent[]) { for (const change of changes) { - const [type, doc, fromSync] = change; + const [type, doc] = change; const { vcs } = this.state; - const { activeRequest } = this.props; - - // Force refresh if environment changes - // TODO: Only do this for environments in this workspace (not easy because they're nested) - if (isEnvironment(doc)) { - console.log('[App] Forcing update from environment change', change); - needsRefresh = true; - } - - // Force refresh if sync changes the active request - if (fromSync && activeRequest && doc._id === activeRequest._id) { - needsRefresh = true; - console.log('[App] Forcing update from request change', change); - } // Delete VCS project if workspace deleted if (vcs && isWorkspace(doc) && type === db.CHANGE_REMOVE) { await vcs.removeBackendProjectsForRoot(doc._id); } } - - if (needsRefresh) { - setTimeout(() => { - this._wrapper?._forceRequestPaneRefresh(); - }, 300); - } } async componentDidMount() { @@ -584,7 +547,7 @@ class App extends PureComponent { // Update VCS await this._updateVCS(); await this._updateGitVCS(); - db.onChange(this._handleDbChange); + db.onChange(this.listenforWorkspaceDelete); ipcRenderer.on('toggle-preferences', () => { App._handleShowSettingsModal(); }); @@ -703,7 +666,7 @@ class App extends PureComponent { } componentWillUnmount() { - db.offChange(this._handleDbChange); + db.offChange(this.listenforWorkspaceDelete); } async _ensureWorkspaceChildren() { @@ -768,13 +731,12 @@ class App extends PureComponent { return null; } - const { activeWorkspace } = this.props; + const { activeWorkspace, isLoggedIn } = this.props; const { gitVCS, vcs, - forceRefreshCounter, } = this.state; - const uniquenessKey = `${forceRefreshCounter}::${activeWorkspace?._id || 'n/a'}`; + const uniquenessKey = `${isLoggedIn}::${activeWorkspace?._id || 'n/a'}`; return ( @@ -784,7 +746,6 @@ class App extends PureComponent {
{ + database.onChange(changes => { + for (const change of changes) { + const [, doc, fromSync] = change; + + // Force refresh if sync changes the active request + if (activeRequest?._id === doc._id && fromSync) { + setVersion(v => v + 1); + } + } + }); + }, [activeRequest?._id]); + + return version; +} + +// We use this hook to determine if the active workspace has been updated from the Git VCS +// For example, by pulling a new version from the remote, switching branches, etc. +export function useGitVCSVersion() { + const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta); + + return ((activeWorkspaceMeta?.cachedGitLastCommitTime + '') + activeWorkspaceMeta?.cachedGitRepositoryBranch) + ''; +}