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}
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) + '';
+}