From 7886fdb350dc09e67d203cdcd121dca532ee8c07 Mon Sep 17 00:00:00 2001
From: Jack Kavanagh
Date: Tue, 25 Jul 2023 16:15:00 +0200
Subject: [PATCH] redux->remix workspace route (#6191)
* checkpoint
* workspace meta
* select environment
* fix sidebar layout
* clean up
* absorb apispec into workspace route
* client certs
* remove redux tests
* update data docs
* remove active project
* revert selector change
* fix selector
* remove activeworkspace
* remove activity
---
DEVELOPMENT.md | 4 +-
.../create-mock-store-with-active-request.ts | 2 -
.../src/__jest__/redux-state-for-test.ts | 2 -
.../models/helpers/workspace-operations.ts | 2 +-
.../insomnia/src/ui/components/app-header.tsx | 8 +-
.../request-group-actions-dropdown.tsx | 24 +-
.../dropdowns/response-history-dropdown.tsx | 14 +-
.../ui/components/dropdowns/sync-dropdown.tsx | 19 +-
.../dropdowns/workspace-dropdown.tsx | 27 +-
.../components/modals/cookie-modify-modal.tsx | 7 +-
.../ui/components/modals/cookies-modal.tsx | 6 +-
.../modals/request-switcher-modal.tsx | 32 +--
.../components/modals/sync-delete-modal.tsx | 8 +-
.../workspace-environments-edit-modal.tsx | 8 +-
.../modals/workspace-settings-modal.tsx | 44 ++--
.../ui/components/panes/grpc-request-pane.tsx | 12 +-
.../components/panes/grpc-response-pane.tsx | 11 +-
.../panes/placeholder-request-pane.tsx | 6 +-
.../src/ui/components/panes/request-pane.tsx | 10 +-
.../src/ui/components/request-url-bar.tsx | 24 +-
.../ui/components/settings/import-export.tsx | 19 +-
.../src/ui/components/sidebar-layout.tsx | 27 +-
.../sidebar/sidebar-create-dropdown.tsx | 24 +-
.../ui/components/sidebar/sidebar-filter.tsx | 23 +-
.../sidebar/sidebar-request-row.tsx | 20 +-
.../src/ui/components/sync-pull-button.tsx | 11 +-
.../nunjucks/__tests__/use-nunjucks.test.ts | 195 --------------
.../src/ui/context/nunjucks/use-nunjucks.ts | 18 +-
.../src/ui/hooks/use-document-title.ts | 24 +-
.../ui/hooks/use-global-keyboard-shortcuts.ts | 10 +-
.../insomnia/src/ui/hooks/use-vcs-version.ts | 15 +-
packages/insomnia/src/ui/index.tsx | 46 +---
.../src/ui/redux/__tests__/selectors.test.ts | 190 --------------
.../ui/redux/modules/__tests__/global.test.ts | 27 --
.../redux/modules/__tests__/workspace.test.ts | 140 ----------
.../insomnia/src/ui/redux/modules/global.tsx | 41 +--
.../src/ui/redux/modules/workspace.ts | 50 ----
packages/insomnia/src/ui/redux/selectors.ts | 246 ++----------------
.../src/ui/redux/sidebar-selectors.ts | 18 --
packages/insomnia/src/ui/routes/debug.tsx | 57 ++--
packages/insomnia/src/ui/routes/modals.tsx | 22 +-
packages/insomnia/src/ui/routes/workspace.tsx | 19 +-
42 files changed, 316 insertions(+), 1196 deletions(-)
delete mode 100644 packages/insomnia/src/ui/context/nunjucks/__tests__/use-nunjucks.test.ts
delete mode 100644 packages/insomnia/src/ui/redux/__tests__/selectors.test.ts
delete mode 100644 packages/insomnia/src/ui/redux/modules/__tests__/workspace.test.ts
delete mode 100644 packages/insomnia/src/ui/redux/modules/workspace.ts
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 2ad86dd9b7..e5663e5ef7 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -45,8 +45,8 @@ There are a few notable directories inside it:
Insomnia stores data in a few places:
- A local in-memory NeDB database stores data for data models (requests, folder, workspaces, etc.).
-- A local Redux store contains an in-memory copy of all database entities.
-- Multiple React Context stores, defined in `/src/ui/context`.
+- localstorage
+- a fake localstorage api that writes to file and is used for window sizing
> Note: NeDB is officially unmaintained (even for critical security bugs) and was last published in February 2016. Due to this, we hope to move away from it, however doing so is tricky because of how deeply tied it is to our architecture.
diff --git a/packages/insomnia/src/__jest__/create-mock-store-with-active-request.ts b/packages/insomnia/src/__jest__/create-mock-store-with-active-request.ts
index fbc29dbff1..f7fef81e67 100644
--- a/packages/insomnia/src/__jest__/create-mock-store-with-active-request.ts
+++ b/packages/insomnia/src/__jest__/create-mock-store-with-active-request.ts
@@ -1,7 +1,6 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
-import { ACTIVITY_DEBUG } from '../common/constants';
import * as models from '../models';
import { Request } from '../models/request';
import { RequestMeta } from '../models/request-meta';
@@ -21,7 +20,6 @@ export const createMockStoreWithRequest = async ({ requestPatch, requestMetaPatc
const store = mockStore(await reduxStateForTest({
activeProjectId: projectId,
activeWorkspaceId: workspaceId,
- activeActivity: ACTIVITY_DEBUG,
}));
return {
diff --git a/packages/insomnia/src/__jest__/redux-state-for-test.ts b/packages/insomnia/src/__jest__/redux-state-for-test.ts
index 9ba5223492..739a5a6313 100644
--- a/packages/insomnia/src/__jest__/redux-state-for-test.ts
+++ b/packages/insomnia/src/__jest__/redux-state-for-test.ts
@@ -1,4 +1,3 @@
-import { ACTIVITY_HOME } from '../common/constants';
import { database } from '../common/database';
import { DEFAULT_PROJECT_ID, type } from '../models/project';
import { RootState } from '../ui/redux/modules';
@@ -36,7 +35,6 @@ export const reduxStateForTest = async (global: Partial = {}): Prom
entities: entities.reducer(entities.initialEntitiesState, entities.initializeWith(allDocs)),
global: {
activeWorkspaceId: null,
- activeActivity: ACTIVITY_HOME,
activeProjectId: DEFAULT_PROJECT_ID,
dashboardSortOrder: 'modified-desc',
isLoggedIn: false,
diff --git a/packages/insomnia/src/models/helpers/workspace-operations.ts b/packages/insomnia/src/models/helpers/workspace-operations.ts
index fe0492d09c..3c0800a9ad 100644
--- a/packages/insomnia/src/models/helpers/workspace-operations.ts
+++ b/packages/insomnia/src/models/helpers/workspace-operations.ts
@@ -3,7 +3,7 @@ import type { ApiSpec } from '../api-spec';
import * as models from '../index';
import { isDesign, Workspace } from '../workspace';
-export async function rename(name: string, workspace: Workspace, apiSpec?: ApiSpec) {
+export async function rename(name: string, workspace: Workspace, apiSpec?: ApiSpec | null) {
if (isDesign(workspace) && apiSpec) {
await models.apiSpec.update(apiSpec, {
fileName: name,
diff --git a/packages/insomnia/src/ui/components/app-header.tsx b/packages/insomnia/src/ui/components/app-header.tsx
index 5b6a05233b..c407a59486 100644
--- a/packages/insomnia/src/ui/components/app-header.tsx
+++ b/packages/insomnia/src/ui/components/app-header.tsx
@@ -1,12 +1,10 @@
import classNames from 'classnames';
import React, { FC, Fragment, ReactNode } from 'react';
-import { useSelector } from 'react-redux';
import styled from 'styled-components';
-import { selectIsLoggedIn } from '../redux/selectors';
+import * as session from '../../account/session';
import { GitHubStarsButton } from './github-stars-button';
import { InsomniaAILogo } from './insomnia-icon';
-
const LogoWrapper = styled.div({
display: 'flex',
justifyContent: 'center',
@@ -75,8 +73,6 @@ export const AppHeader: FC = ({
gridCenter,
gridRight,
}) => {
- const isLoggedIn = useSelector(selectIsLoggedIn);
-
return (
= ({
- {!isLoggedIn ? : null}
+ {!session.isLoggedIn() ? : null}
)}
gridCenter={gridCenter}
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 2af17fbc04..46ec8c9073 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,5 +1,6 @@
import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { toKebabCase } from '../../../common/misc';
import { RENDER_PURPOSE_NO_RENDER } from '../../../common/render';
@@ -11,11 +12,11 @@ import { getRequestGroupActions } from '../../../plugins';
import * as pluginContexts from '../../../plugins/context/index';
import { createRequest, CreateRequestType } from '../../hooks/create-request';
import { createRequestGroup } from '../../hooks/create-request-group';
-import { selectActiveEnvironment, selectActiveProject, selectActiveWorkspace, selectHotKeyRegistry } from '../../redux/selectors';
+import { selectHotKeyRegistry } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, type DropdownHandle, DropdownItem, type DropdownProps, DropdownSection, ItemContent } from '../base/dropdown';
import { showError, showModal, showPrompt } from '../modals';
import { EnvironmentEditModal } from '../modals/environment-edit-modal';
-
interface Props extends Partial {
requestGroup: RequestGroup;
handleShowSettings: (requestGroup: RequestGroup) => any;
@@ -30,24 +31,24 @@ export const RequestGroupActionsDropdown = forwardRef {
+ const {
+ activeWorkspace,
+ activeEnvironment,
+ activeProject,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const hotKeyRegistry = useSelector(selectHotKeyRegistry);
const [actionPlugins, setActionPlugins] = useState([]);
const [loadingActions, setLoadingActions] = useState>({});
const dropdownRef = useRef(null);
- const activeProject = useSelector(selectActiveProject);
- const activeEnvironment = useSelector(selectActiveEnvironment);
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceId = activeWorkspace?._id;
-
const create = useCallback((requestType: CreateRequestType) => {
- if (activeWorkspaceId) {
+ if (activeWorkspace._id) {
createRequest({
parentId: requestGroup._id,
- requestType, workspaceId: activeWorkspaceId,
+ requestType, workspaceId: activeWorkspace._id,
});
}
- }, [activeWorkspaceId, requestGroup._id]);
+ }, [activeWorkspace._id, requestGroup._id]);
useImperativeHandle(ref, () => ({
show: () => {
@@ -102,12 +103,11 @@ export const RequestGroupActionsDropdown = forwardRef),
...pluginContexts.data.init(activeProject._id),
...(pluginContexts.store.init(plugin) as Record),
- ...(pluginContexts.network.init(activeEnvironmentId) as Record),
+ ...(pluginContexts.network.init(activeEnvironment._id) as Record),
};
const requests = await models.request.findByParentId(requestGroup._id);
requests.sort((a, b) => a.metaSortKey - b.metaSortKey);
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 8440a8b1c3..42f8da759b 100644
--- a/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx
+++ b/packages/insomnia/src/ui/components/dropdowns/response-history-dropdown.tsx
@@ -1,6 +1,7 @@
import { differenceInHours, differenceInMinutes, isThisWeek, isToday } from 'date-fns';
import React, { useCallback, useRef } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { decompressObject } from '../../../common/misc';
import * as models from '../../../models/index';
@@ -9,7 +10,8 @@ import { Response } from '../../../models/response';
import { WebSocketRequest } from '../../../models/websocket-request';
import { isWebSocketResponse, WebSocketResponse } from '../../../models/websocket-response';
import { updateRequestMetaByParentId } from '../../hooks/create-request';
-import { selectActiveEnvironment, selectActiveRequest, selectActiveRequestResponses, selectRequestVersions } from '../../redux/selectors';
+import { selectActiveRequest, selectActiveRequestResponses, selectRequestVersions } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, type DropdownHandle, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
import { SizeTag } from '../tags/size-tag';
@@ -17,7 +19,6 @@ import { StatusTag } from '../tags/status-tag';
import { TimeTag } from '../tags/time-tag';
import { URLTag } from '../tags/url-tag';
import { TimeFromNow } from '../time-from-now';
-
interface Props {
activeResponse: GenericResponse;
className?: string;
@@ -30,7 +31,9 @@ export const ResponseHistoryDropdown = ) => {
const dropdownRef = useRef(null);
- const activeEnvironment = useSelector(selectActiveEnvironment);
+ const {
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const responses = useSelector(selectActiveRequestResponses) as GenericResponse[];
const activeRequest = useSelector(selectActiveRequest);
const requestVersions = useSelector(selectRequestVersions);
@@ -56,12 +59,11 @@ export const ResponseHistoryDropdown = {
- const environmentId = activeEnvironment ? activeEnvironment._id : null;
if (isWebSocketResponse(activeResponse)) {
window.main.webSocket.closeAll();
- await models.webSocketResponse.removeForRequest(requestId, environmentId);
+ await models.webSocketResponse.removeForRequest(requestId, activeEnvironment._id);
} else {
- await models.response.removeForRequest(requestId, environmentId);
+ await models.response.removeForRequest(requestId, activeEnvironment._id);
}
if (activeRequest && activeRequest._id === requestId) {
await updateRequestMetaByParentId(requestId, { activeResponseId: null });
diff --git a/packages/insomnia/src/ui/components/dropdowns/sync-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/sync-dropdown.tsx
index b9c153f9df..2e64f9d14c 100644
--- a/packages/insomnia/src/ui/components/dropdowns/sync-dropdown.tsx
+++ b/packages/insomnia/src/ui/components/dropdowns/sync-dropdown.tsx
@@ -1,6 +1,7 @@
import classnames from 'classnames';
import React, { FC, Fragment, useCallback, useEffect, useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useNavigate, useParams, useRouteLoaderData } from 'react-router-dom';
import { useInterval, useMount } from 'react-use';
import * as session from '../../../account/session';
@@ -18,8 +19,8 @@ import { BackendProjectWithTeam } from '../../../sync/vcs/normalize-backend-proj
import { pullBackendProject } from '../../../sync/vcs/pull-backend-project';
import { interceptAccessError } from '../../../sync/vcs/util';
import { VCS } from '../../../sync/vcs/vcs';
-import { activateWorkspace } from '../../redux/modules/workspace';
-import { selectActiveWorkspaceMeta, selectRemoteProjects, selectSyncItems } from '../../redux/selectors';
+import { selectRemoteProjects, selectSyncItems } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { Link } from '../base/link';
import { HelpTooltip } from '../help-tooltip';
@@ -32,7 +33,6 @@ import { SyncHistoryModal } from '../modals/sync-history-modal';
import { SyncStagingModal } from '../modals/sync-staging-modal';
import { Button } from '../themed-button';
import { Tooltip } from '../tooltip';
-
// TODO: handle refetching logic in one place not here in a component
// Refresh dropdown periodically
@@ -79,10 +79,13 @@ export const SyncDropdown: FC = ({ vcs, workspace, project }) => {
},
remoteBackendProjects: [],
});
- const dispatch = useDispatch();
+ const { organizationId, projectId } = useParams<{ organizationId: string; projectId: string }>();
+ const navigate = useNavigate();
const remoteProjects = useSelector(selectRemoteProjects);
const syncItems = useSelector(selectSyncItems);
- const workspaceMeta = useSelector(selectActiveWorkspaceMeta);
+ const {
+ activeWorkspaceMeta,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const refetchRemoteBranch = useCallback(async () => {
if (session.isLoggedIn()) {
try {
@@ -136,7 +139,7 @@ export const SyncDropdown: FC = ({ vcs, workspace, project }) => {
try {
// NOTE pushes the first snapshot automatically
- await pushSnapshotOnInitialize({ vcs, workspace, workspaceMeta, project });
+ await pushSnapshotOnInitialize({ vcs, workspace, workspaceMeta: activeWorkspaceMeta, project });
await refreshVCSAndRefetchRemote();
} catch (err) {
console.log('[sync_menu] Error refreshing sync state', err);
@@ -167,7 +170,7 @@ export const SyncDropdown: FC = ({ vcs, workspace, project }) => {
const pulledIntoProject = await pullBackendProject({ vcs, backendProject, remoteProjects });
if (pulledIntoProject.project._id !== project._id) {
// If pulled into a different project, reactivate the workspace
- dispatch(activateWorkspace({ workspaceId: workspace._id }));
+ navigate(`/organization/${organizationId}/project/${projectId}/workspace/${workspace._id}`);
logCollectionMovedToProject(workspace, pulledIntoProject.project);
}
await refreshVCSAndRefetchRemote();
diff --git a/packages/insomnia/src/ui/components/dropdowns/workspace-dropdown.tsx b/packages/insomnia/src/ui/components/dropdowns/workspace-dropdown.tsx
index deaecd7996..4b3bd05849 100644
--- a/packages/insomnia/src/ui/components/dropdowns/workspace-dropdown.tsx
+++ b/packages/insomnia/src/ui/components/dropdowns/workspace-dropdown.tsx
@@ -1,10 +1,12 @@
import React, { FC, useCallback, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { isLoggedIn } from '../../../account/session';
import { database as db } from '../../../common/database';
import { getWorkspaceLabel } from '../../../common/get-workspace-label';
import { RENDER_PURPOSE_NO_RENDER } from '../../../common/render';
+import { workspace } from '../../../models';
import { isRequest } from '../../../models/request';
import { isRequestGroup } from '../../../models/request-group';
import { isDesign, Workspace } from '../../../models/workspace';
@@ -12,20 +14,23 @@ import type { WorkspaceAction } from '../../../plugins';
import { getWorkspaceActions } from '../../../plugins';
import * as pluginContexts from '../../../plugins/context';
import { useAIContext } from '../../context/app/ai-context';
-import { selectActiveApiSpec, selectActiveEnvironment, selectActiveProject, selectActiveWorkspace, selectActiveWorkspaceName, selectSettings } from '../../redux/selectors';
+import { selectSettings } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, type DropdownHandle, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { InsomniaAI } from '../insomnia-ai-icon';
import { showError, showModal } from '../modals';
import { configGenerators, showGenerateConfigModal } from '../modals/generate-config-modal';
import { SettingsModal, TAB_INDEX_EXPORT } from '../modals/settings-modal';
import { WorkspaceSettingsModal } from '../modals/workspace-settings-modal';
-
export const WorkspaceDropdown: FC = () => {
- const activeEnvironment = useSelector(selectActiveEnvironment);
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceName = useSelector(selectActiveWorkspaceName);
- const activeApiSpec = useSelector(selectActiveApiSpec);
- const activeProject = useSelector(selectActiveProject);
+ const {
+ activeWorkspace,
+ activeEnvironment,
+ activeProject,
+ activeApiSpec,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
+ const activeWorkspaceName = workspace.name;
+
const settings = useSelector(selectSettings);
const { hotKeyRegistry } = settings;
const [actionPlugins, setActionPlugins] = useState([]);
@@ -41,12 +46,11 @@ export const WorkspaceDropdown: FC = () => {
const handlePluginClick = useCallback(async ({ action, plugin, label }: WorkspaceAction, workspace: Workspace) => {
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),
+ ...(pluginContexts.network.init(activeEnvironment._id) as Record),
};
const docs = await db.withDescendants(workspace);
@@ -94,11 +98,6 @@ export const WorkspaceDropdown: FC = () => {
});
}, [activeApiSpec]);
- if (!activeWorkspace) {
- console.error('warning: tried to render WorkspaceDropdown without an activeWorkspace');
- return null;
- }
-
return (
((_, ref) => {
const modalRef = useRef(null);
const [cookie, setCookie] = useState(null);
- const activeCookieJar = useSelector(selectActiveCookieJar);
+ const { activeCookieJar } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
+
useImperativeHandle(ref, () => ({
hide: () => {
modalRef.current?.hide();
diff --git a/packages/insomnia/src/ui/components/modals/cookies-modal.tsx b/packages/insomnia/src/ui/components/modals/cookies-modal.tsx
index 8548c2ccb8..e229b505e2 100644
--- a/packages/insomnia/src/ui/components/modals/cookies-modal.tsx
+++ b/packages/insomnia/src/ui/components/modals/cookies-modal.tsx
@@ -1,11 +1,11 @@
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
-import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { fuzzyMatch } from '../../../common/misc';
import * as models from '../../../models';
import type { Cookie } from '../../../models/cookie-jar';
import { useNunjucks } from '../../context/nunjucks/use-nunjucks';
-import { selectActiveCookieJar } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
import { ModalFooter } from '../base/modal-footer';
@@ -21,7 +21,7 @@ export const CookiesModal = forwardRef((_, ref)
const { handleRender } = useNunjucks();
const [filter, setFilter] = useState('');
const [visibleCookieIndexes, setVisibleCookieIndexes] = useState(null);
- const activeCookieJar = useSelector(selectActiveCookieJar);
+ const { activeCookieJar } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
useImperativeHandle(ref, () => ({
hide: () => {
diff --git a/packages/insomnia/src/ui/components/modals/request-switcher-modal.tsx b/packages/insomnia/src/ui/components/modals/request-switcher-modal.tsx
index ac951e254a..fe4b2c3b12 100644
--- a/packages/insomnia/src/ui/components/modals/request-switcher-modal.tsx
+++ b/packages/insomnia/src/ui/components/modals/request-switcher-modal.tsx
@@ -1,6 +1,7 @@
import classnames from 'classnames';
import React, { forwardRef, Fragment, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useNavigate, useParams, useRouteLoaderData } from 'react-router-dom';
import { METHOD_GRPC } from '../../../common/constants';
import { fuzzyMatchAll } from '../../../common/misc';
@@ -12,8 +13,8 @@ import { isWebSocketRequest, WebSocketRequest } from '../../../models/websocket-
import { Workspace } from '../../../models/workspace';
import { buildQueryStringFromParams, joinUrlAndQueryString } from '../../../utils/url/querystring';
import { updateRequestMetaByParentId } from '../../hooks/create-request';
-import { activateWorkspace } from '../../redux/modules/workspace';
-import { selectActiveRequest, selectActiveWorkspace, selectActiveWorkspaceMeta, selectGrpcRequestMetas, selectRequestMetas, selectWorkspaceRequestsAndRequestGroups, selectWorkspacesForActiveProject } from '../../redux/selectors';
+import { selectActiveRequest, selectGrpcRequestMetas, selectRequestMetas, selectWorkspaceRequestsAndRequestGroups, selectWorkspacesForActiveProject } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Highlight } from '../base/highlight';
import { Modal, ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
@@ -23,7 +24,6 @@ import { GrpcTag } from '../tags/grpc-tag';
import { MethodTag } from '../tags/method-tag';
import { WebSocketTag } from '../tags/websocket-tag';
import { wrapToIndex } from './utils';
-
interface State {
searchString: string;
workspacesForActiveProject: Workspace[];
@@ -67,10 +67,13 @@ export const RequestSwitcherModal = forwardRef();
+ const navigate = useNavigate();
const activeRequest = useSelector(selectActiveRequest);
- const workspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
+ const {
+ activeWorkspace: workspace,
+ activeWorkspaceMeta,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const workspacesForActiveProject = useSelector(selectWorkspacesForActiveProject);
const requestMetas = useSelector(selectRequestMetas);
const grpcRequestMetas = useSelector(selectGrpcRequestMetas);
@@ -163,7 +166,7 @@ export const RequestSwitcherModal = forwardRef w._id !== workspace?._id)
+ .filter(w => w._id !== workspace._id)
.filter(w => {
const name = w.name.toLowerCase();
const toMatch = searchString.toLowerCase();
@@ -180,7 +183,7 @@ export const RequestSwitcherModal = forwardRef ({
hide: () => {
@@ -206,21 +209,20 @@ export const RequestSwitcherModal = forwardRef {
+ const activateWorkspaceAndHide = useCallback((workspace: Workspace) => {
if (!workspace) {
return;
}
- dispatch(activateWorkspace({ workspace }));
+ console.log(`[app] Activating workspace "${workspace.name}"`);
+ navigate(`/organization/${organizationId}/project/${projectId}/workspace/${workspace._id}`);
modalRef.current?.hide();
- }, [dispatch]);
+ }, [navigate, organizationId, projectId]);
const activateRequestAndHide = useCallback((request?: Request | WebSocketRequest | GrpcRequest) => {
if (!request) {
return;
}
- if (activeWorkspaceMeta) {
- models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: request._id });
- }
+ models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: request._id });
updateRequestMetaByParentId(request._id, { lastActive: Date.now() });
modalRef.current?.hide();
}, [activeWorkspaceMeta]);
diff --git a/packages/insomnia/src/ui/components/modals/sync-delete-modal.tsx b/packages/insomnia/src/ui/components/modals/sync-delete-modal.tsx
index 9499be499e..0f45ec7e36 100644
--- a/packages/insomnia/src/ui/components/modals/sync-delete-modal.tsx
+++ b/packages/insomnia/src/ui/components/modals/sync-delete-modal.tsx
@@ -1,11 +1,11 @@
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
-import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { strings } from '../../../common/strings';
import { interceptAccessError } from '../../../sync/vcs/util';
import { VCS } from '../../../sync/vcs/vcs';
import { Button } from '../../components/themed-button';
-import { selectActiveWorkspace } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
import { ModalHeader } from '../base/modal-header';
@@ -31,6 +31,9 @@ export const SyncDeleteModal = forwardRef(({ vcs }
error: '',
workspaceName: '',
});
+ const {
+ activeWorkspace,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
useImperativeHandle(ref, () => ({
hide: () => modalRef.current?.hide(),
@@ -43,7 +46,6 @@ export const SyncDeleteModal = forwardRef(({ vcs }
modalRef.current?.show({ onHide });
},
}), []);
- const activeWorkspace = useSelector(selectActiveWorkspace);
const onSubmit = async (event: React.SyntheticEvent) => {
event.preventDefault();
try {
diff --git a/packages/insomnia/src/ui/components/modals/workspace-environments-edit-modal.tsx b/packages/insomnia/src/ui/components/modals/workspace-environments-edit-modal.tsx
index eba9edd063..c533987371 100644
--- a/packages/insomnia/src/ui/components/modals/workspace-environments-edit-modal.tsx
+++ b/packages/insomnia/src/ui/components/modals/workspace-environments-edit-modal.tsx
@@ -1,13 +1,11 @@
import classnames from 'classnames';
import React, { FC, forwardRef, Fragment, useImperativeHandle, useRef } from 'react';
import { ListDropTargetDelegate, ListKeyboardDelegate, mergeProps, useDraggableCollection, useDraggableItem, useDropIndicator, useDroppableCollection, useDroppableItem, useFocusRing, useListBox, useOption } from 'react-aria';
-import { useSelector } from 'react-redux';
import { useFetcher, useParams, useRouteLoaderData } from 'react-router-dom';
import { DraggableCollectionState, DroppableCollectionState, Item, ListState, useDraggableCollectionState, useDroppableCollectionState, useListState } from 'react-stately';
import { docsTemplateTags } from '../../../common/documentation';
import type { Environment } from '../../../models/environment';
-import { selectActiveWorkspaceMeta } from '../../redux/selectors';
import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, DropdownItem, ItemContent } from '../base/dropdown';
import { Editable } from '../base/editable';
@@ -30,12 +28,14 @@ interface SidebarListItemProps {
const SidebarListItem: FC = ({
environment,
}: SidebarListItemProps) => {
- const workspaceMeta = useSelector(selectActiveWorkspaceMeta);
+ const {
+ activeWorkspaceMeta,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
return (
{environment.color ? (
diff --git a/packages/insomnia/src/ui/components/modals/workspace-settings-modal.tsx b/packages/insomnia/src/ui/components/modals/workspace-settings-modal.tsx
index de8841c5c0..698fbfba41 100644
--- a/packages/insomnia/src/ui/components/modals/workspace-settings-modal.tsx
+++ b/packages/insomnia/src/ui/components/modals/workspace-settings-modal.tsx
@@ -1,9 +1,9 @@
import React, { FC, forwardRef, ReactNode, useEffect, useImperativeHandle, useRef, useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
import { useRevalidator } from 'react-router-dom';
+import { useRouteLoaderData } from 'react-router-dom';
+import { useNavigate, useParams } from 'react-router-dom';
import styled from 'styled-components';
-import { ACTIVITY_HOME } from '../../../common/constants';
import { database as db } from '../../../common/database';
import { getWorkspaceLabel } from '../../../common/get-workspace-label';
import { CaCertificate } from '../../../models/ca-certificate';
@@ -12,8 +12,7 @@ import * as workspaceOperations from '../../../models/helpers/workspace-operatio
import * as models from '../../../models/index';
import { isRequest } from '../../../models/request';
import { invariant } from '../../../utils/invariant';
-import { setActiveActivity } from '../../redux/modules/global';
-import { selectActiveApiSpec, selectActiveWorkspace, selectActiveWorkspaceClientCertificates, selectActiveWorkspaceMeta, selectActiveWorkspaceName } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { FileInputButton } from '../base/file-input-button';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
@@ -23,7 +22,6 @@ import { PanelContainer, TabItem, Tabs } from '../base/tabs';
import { HelpTooltip } from '../help-tooltip';
import { MarkdownEditor } from '../markdown-editor';
import { PasswordViewer } from '../viewers/password-viewer';
-
const CertificateFields = styled.div({
display: 'flex',
flexDirection: 'column',
@@ -89,12 +87,16 @@ export const WorkspaceSettingsModal = forwardRef(null);
useEffect(() => {
if (!workspace) {
@@ -107,13 +109,12 @@ export const WorkspaceSettingsModal = forwardRef ({
hide: () => {
modalRef.current?.hide();
},
show: () => {
- const hasDescription = !!workspace?.description;
+ const hasDescription = !!workspace.description;
setState(state => ({
...state,
showDescription: hasDescription,
@@ -122,7 +123,7 @@ export const WorkspaceSettingsModal = forwardRef {
if (!workspace) {
@@ -157,7 +158,7 @@ export const WorkspaceSettingsModal = forwardRef workspaceOperations.rename(event.target.value, workspace, apiSpec)}
+ onChange={event => workspaceOperations.rename(event.target.value, workspace, activeApiSpec)}
/>
@@ -509,20 +511,20 @@ export const WorkspaceSettingsModal = forwardRef
{
- if (workspaceMeta?.gitRepositoryId) {
- await models.workspaceMeta.update(workspaceMeta, {
+ if (activeWorkspaceMeta?.gitRepositoryId) {
+ await models.workspaceMeta.update(activeWorkspaceMeta, {
gitRepositoryId: null,
});
} else {
- invariant(workspaceMeta, 'Workspace meta not found');
+ invariant(activeWorkspaceMeta, 'Workspace meta not found');
const repo = await models.gitRepository.create({
uri: '',
});
- await models.workspaceMeta.update(workspaceMeta, {
+ await models.workspaceMeta.update(activeWorkspaceMeta, {
gitRepositoryId: repo._id,
});
}
diff --git a/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx b/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx
index 2b3bee8d89..e0c9122742 100644
--- a/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx
+++ b/packages/insomnia/src/ui/components/panes/grpc-request-pane.tsx
@@ -1,5 +1,5 @@
import React, { FunctionComponent, useState } from 'react';
-import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { useAsync } from 'react-use';
import styled from 'styled-components';
@@ -12,8 +12,8 @@ import * as models from '../../../models';
import type { GrpcRequest, GrpcRequestHeader } from '../../../models/grpc-request';
import { queryAllWorkspaceUrls } from '../../../models/helpers/query-all-workspace-urls';
import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/use-vcs-version';
-import { selectActiveEnvironment } from '../../redux/selectors';
import { GrpcRequestState } from '../../routes/debug';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { PanelContainer, TabItem, Tabs } from '../base/tabs';
import { GrpcSendButton } from '../buttons/grpc-send-button';
import { OneLineEditor } from '../codemirror/one-line-editor';
@@ -92,10 +92,12 @@ export const GrpcRequestPane: FunctionComponent = ({
const gitVersion = useGitVCSVersion();
const activeRequestSyncVersion = useActiveRequestSyncVCSVersion();
- const activeEnvironment = useSelector(selectActiveEnvironment);
- const environmentId = activeEnvironment?._id || 'n/a';
+ const {
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
+ const environmentId = activeEnvironment._id;
// 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 uniquenessKey = `${activeEnvironment.modified}::${activeRequest?._id}::${gitVersion}::${activeRequestSyncVersion}`;
const method = methods.find(c => c.fullPath === activeRequest.protoMethodName);
const methodType = method?.type;
const handleRequestSend = async () => {
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 099004581f..0c22d703c1 100644
--- a/packages/insomnia/src/ui/components/panes/grpc-response-pane.tsx
+++ b/packages/insomnia/src/ui/components/panes/grpc-response-pane.tsx
@@ -1,14 +1,13 @@
import React, { FunctionComponent } from 'react';
-import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import type { GrpcRequest } from '../../../models/grpc-request';
import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/use-vcs-version';
-import { selectActiveEnvironment } from '../../redux/selectors';
import { GrpcRequestState } from '../../routes/debug';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { GrpcStatusTag } from '../tags/grpc-status-tag';
import { GrpcTabbedMessages } from '../viewers/grpc-tabbed-messages';
import { Pane, PaneBody, PaneHeader } from './pane';
-
interface Props {
activeRequest: GrpcRequest;
grpcState: GrpcRequestState;
@@ -17,9 +16,11 @@ interface Props {
export const GrpcResponsePane: FunctionComponent = ({ activeRequest, grpcState }) => {
const gitVersion = useGitVCSVersion();
const activeRequestSyncVersion = useActiveRequestSyncVCSVersion();
- const activeEnvironment = useSelector(selectActiveEnvironment);
+ const {
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
// 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 uniquenessKey = `${activeEnvironment.modified}::${activeRequest?._id}::${gitVersion}::${activeRequestSyncVersion}`;
const { responseMessages, status, error } = grpcState;
return (
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 f32157503b..77baf4585b 100644
--- a/packages/insomnia/src/ui/components/panes/placeholder-request-pane.tsx
+++ b/packages/insomnia/src/ui/components/panes/placeholder-request-pane.tsx
@@ -1,15 +1,15 @@
import React, { FC, useCallback } from 'react';
import { useSelector } from 'react-redux';
+import { useParams } from 'react-router-dom';
import { createRequest } from '../../hooks/create-request';
-import { selectActiveWorkspace, selectSettings } from '../../redux/selectors';
+import { selectSettings } from '../../redux/selectors';
import { Hotkey } from '../hotkey';
import { Pane, PaneBody, PaneHeader } from './pane';
export const PlaceholderRequestPane: FC = () => {
const { hotKeyRegistry } = useSelector(selectSettings);
- const workspaceId = useSelector(selectActiveWorkspace)?._id;
-
+ const { workspaceId } = useParams<{ workspaceId: string }>();
const createHttpRequest = useCallback(() => {
if (workspaceId) {
createRequest({
diff --git a/packages/insomnia/src/ui/components/panes/request-pane.tsx b/packages/insomnia/src/ui/components/panes/request-pane.tsx
index 4b60d14db5..8030fb4f83 100644
--- a/packages/insomnia/src/ui/components/panes/request-pane.tsx
+++ b/packages/insomnia/src/ui/components/panes/request-pane.tsx
@@ -1,5 +1,6 @@
import React, { FC, useCallback, useEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import styled from 'styled-components';
import { version } from '../../../../package.json';
@@ -14,7 +15,8 @@ import type { Settings } from '../../../models/settings';
import { create, Workspace } from '../../../models/workspace';
import { deconstructQueryStringToParams, extractQueryStringFromUrl } from '../../../utils/url/querystring';
import { useActiveRequestSyncVCSVersion, useGitVCSVersion } from '../../hooks/use-vcs-version';
-import { selectActiveEnvironment, selectActiveRequestMeta } from '../../redux/selectors';
+import { selectActiveRequestMeta } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { PanelContainer, TabItem, Tabs } from '../base/tabs';
import { AuthDropdown } from '../dropdowns/auth-dropdown';
import { ContentTypeDropdown } from '../dropdowns/content-type-dropdown';
@@ -31,7 +33,6 @@ import { RenderedQueryString } from '../rendered-query-string';
import { RequestUrlBar, RequestUrlBarHandle } from '../request-url-bar';
import { Pane, PaneHeader } from './pane';
import { PlaceholderRequestPane } from './placeholder-request-pane';
-
const HeaderContainer = styled.div({
display: 'flex',
flexDirection: 'column',
@@ -268,7 +269,10 @@ export const RequestPane: FC = ({
}, [request]);
const gitVersion = useGitVCSVersion();
const activeRequestSyncVersion = useActiveRequestSyncVCSVersion();
- const activeEnvironment = useSelector(selectActiveEnvironment);
+
+ const {
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
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}`;
diff --git a/packages/insomnia/src/ui/components/request-url-bar.tsx b/packages/insomnia/src/ui/components/request-url-bar.tsx
index 8a31333a74..c9df544251 100644
--- a/packages/insomnia/src/ui/components/request-url-bar.tsx
+++ b/packages/insomnia/src/ui/components/request-url-bar.tsx
@@ -5,6 +5,7 @@ import { extension as mimeExtension } from 'mime-types';
import path from 'path';
import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { useInterval } from 'react-use';
import styled from 'styled-components';
@@ -16,13 +17,13 @@ import { update } from '../../models/helpers/request-operations';
import { isEventStreamRequest, isRequest, Request } from '../../models/request';
import * as network from '../../network/network';
import { convert } from '../../utils/importers/convert';
-import { invariant } from '../../utils/invariant';
import { buildQueryStringFromParams, joinUrlAndQueryString } from '../../utils/url/querystring';
import { SegmentEvent } from '../analytics';
import { updateRequestMetaByParentId } from '../hooks/create-request';
import { useReadyState } from '../hooks/use-ready-state';
import { useTimeoutWhen } from '../hooks/useTimeoutWhen';
-import { selectActiveEnvironment, selectActiveRequest, selectActiveWorkspace, selectHotKeyRegistry, selectResponseDownloadPath, selectSettings } from '../redux/selectors';
+import { selectActiveRequest, selectHotKeyRegistry, selectResponseDownloadPath, selectSettings } from '../redux/selectors';
+import { WorkspaceLoaderData } from '../routes/workspace';
import { Dropdown, DropdownButton, type DropdownHandle, DropdownItem, DropdownSection, ItemContent } from './base/dropdown';
import { OneLineEditor, OneLineEditorHandle } from './codemirror/one-line-editor';
import { MethodDropdown } from './dropdowns/method-dropdown';
@@ -61,10 +62,12 @@ export const RequestUrlBar = forwardRef(({
uniquenessKey,
setLoading,
}, ref) => {
+ const {
+ activeWorkspace,
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const downloadPath = useSelector(selectResponseDownloadPath);
const hotKeyRegistry = useSelector(selectHotKeyRegistry);
- const activeEnvironment = useSelector(selectActiveEnvironment);
- const activeWorkspace = useSelector(selectActiveWorkspace);
const activeRequest = useSelector(selectActiveRequest);
const settings = useSelector(selectSettings);
const methodDropdownRef = useRef(null);
@@ -123,7 +126,7 @@ export const RequestUrlBar = forwardRef(({
});
setLoading(true);
try {
- const responsePatch = await network.send(request._id, activeEnvironment?._id);
+ const responsePatch = await network.send(request._id, activeEnvironment._id);
const headers = responsePatch.headers || [];
const header = getContentDispositionHeader(headers);
const nameFromHeader = header ? contentDisposition.parse(header.value).parameters.filename : null;
@@ -189,7 +192,7 @@ export const RequestUrlBar = forwardRef(({
await updateRequestMetaByParentId(request._id, { activeResponseId: null });
setLoading(false);
}
- }, [activeEnvironment?._id, request, setLoading, settings.maxHistoryResponses, settings.preferredHttpVersion]);
+ }, [activeEnvironment._id, request, setLoading, settings.maxHistoryResponses, settings.preferredHttpVersion]);
const handleSend = useCallback(async () => {
if (!request) {
@@ -207,7 +210,7 @@ export const RequestUrlBar = forwardRef(({
});
setLoading(true);
try {
- const responsePatch = await network.send(request._id, activeEnvironment?._id);
+ const responsePatch = await network.send(request._id, activeEnvironment._id);
await models.response.create(responsePatch, settings.maxHistoryResponses);
} catch (err) {
if (err.type === 'render') {
@@ -232,7 +235,7 @@ export const RequestUrlBar = forwardRef(({
// Unset active response because we just made a new one
await updateRequestMetaByParentId(request._id, { activeResponseId: null });
setLoading(false);
- }, [activeEnvironment?._id, request, setLoading, settings.maxHistoryResponses, settings.preferredHttpVersion]);
+ }, [activeEnvironment._id, request, setLoading, settings.maxHistoryResponses, settings.preferredHttpVersion]);
const send = useCallback(() => {
setCurrentTimeout(undefined);
@@ -242,8 +245,7 @@ export const RequestUrlBar = forwardRef(({
}
if (isEventStreamRequest(request)) {
const startListening = async () => {
- invariant(activeWorkspace, 'activeWorkspace not found (remove with redux)');
- const environmentId = activeEnvironment?._id;
+ const environmentId = activeEnvironment._id;
const workspaceId = activeWorkspace._id;
const renderContext = await getRenderContext({ request, environmentId, purpose: RENDER_PURPOSE_SEND });
// Render any nunjucks tags in the url/headers/authentication settings/cookies
@@ -268,7 +270,7 @@ export const RequestUrlBar = forwardRef(({
return;
}
handleSend();
- }, [activeEnvironment?._id, activeWorkspace, downloadPath, handleSend, request, sendThenSetFilePath]);
+ }, [activeEnvironment._id, activeWorkspace, downloadPath, handleSend, request, sendThenSetFilePath]);
useInterval(send, currentInterval ? currentInterval : null);
useTimeoutWhen(send, currentTimeout, !!currentTimeout);
diff --git a/packages/insomnia/src/ui/components/settings/import-export.tsx b/packages/insomnia/src/ui/components/settings/import-export.tsx
index 66accf8e8b..965249bc49 100644
--- a/packages/insomnia/src/ui/components/settings/import-export.tsx
+++ b/packages/insomnia/src/ui/components/settings/import-export.tsx
@@ -1,6 +1,7 @@
import React, { FC, Fragment, useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
+import { useRouteLoaderData } from 'react-router-dom';
import { getProductName } from '../../../common/constants';
import { docsImportExport } from '../../../common/documentation';
@@ -8,7 +9,8 @@ import { exportAllToFile } from '../../../common/export';
import { getWorkspaceLabel } from '../../../common/get-workspace-label';
import { strings } from '../../../common/strings';
import { isRequestGroup } from '../../../models/request-group';
-import { selectActiveProjectName, selectActiveWorkspace, selectActiveWorkspaceName, selectWorkspaceRequestsAndRequestGroups, selectWorkspacesForActiveProject } from '../../redux/selectors';
+import { selectWorkspaceRequestsAndRequestGroups, selectWorkspacesForActiveProject } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { Link } from '../base/link';
import { AlertModal } from '../modals/alert-modal';
@@ -16,7 +18,6 @@ import { ExportRequestsModal } from '../modals/export-requests-modal';
import { ImportModal } from '../modals/import-modal';
import { showModal } from '../modals/index';
import { Button } from '../themed-button';
-
interface Props {
hideSettingsModal: () => void;
}
@@ -27,9 +28,11 @@ export const ImportExport: FC = ({ hideSettingsModal }) => {
projectId,
workspaceId,
} = useParams() as { organizationId: string; projectId: string; workspaceId?: string };
- const projectName = useSelector(selectActiveProjectName) ?? getProductName();
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceName = useSelector(selectActiveWorkspaceName);
+
+ const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | undefined;
+ const activeWorkspaceName = workspaceData?.activeWorkspace.name;
+ const projectName = workspaceData?.activeProject.name ?? getProductName();
+
const workspacesForActiveProject = useSelector(selectWorkspacesForActiveProject);
const workspaceRequestsAndRequestGroups = useSelector(selectWorkspaceRequestsAndRequestGroups);
const [isImportModalOpen, setIsImportModalOpen] = useState(false);
@@ -61,7 +64,7 @@ export const ImportExport: FC = ({ hideSettingsModal }) => {
Your format isn't supported? Add Your Own.
- {activeWorkspace ?
+ {workspaceData?.activeWorkspace ?
(
= ({ hideSettingsModal }) => {
aria-label="Choose Export Type"
title="Choose Export Type"
>
-
+
diff --git a/packages/insomnia/src/ui/components/sidebar-layout.tsx b/packages/insomnia/src/ui/components/sidebar-layout.tsx
index d45c232990..d9a0f86361 100644
--- a/packages/insomnia/src/ui/components/sidebar-layout.tsx
+++ b/packages/insomnia/src/ui/components/sidebar-layout.tsx
@@ -1,15 +1,15 @@
import React, { FC, forwardRef, ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import styled from 'styled-components';
import { COLLAPSE_SIDEBAR_REMS, DEFAULT_PANE_HEIGHT, DEFAULT_PANE_WIDTH, DEFAULT_SIDEBAR_WIDTH, MAX_PANE_HEIGHT, MAX_PANE_WIDTH, MAX_SIDEBAR_REMS, MIN_PANE_HEIGHT, MIN_PANE_WIDTH, MIN_SIDEBAR_REMS } from '../../common/constants';
import { debounce } from '../../common/misc';
import * as models from '../../models';
-import { selectActiveWorkspaceMeta, selectSettings } from '../redux/selectors';
-import { selectPaneHeight, selectPaneWidth, selectSidebarWidth } from '../redux/sidebar-selectors';
+import { selectSettings } from '../redux/selectors';
+import { WorkspaceLoaderData } from '../routes/workspace';
import { ErrorBoundary } from './error-boundary';
import { Sidebar } from './sidebar/sidebar';
-
const verticalStyles = {
'.sidebar': {
gridColumnStart: '2',
@@ -203,11 +203,8 @@ export const SidebarLayout: FC = ({
renderPageSidebar,
}) => {
const { forceVerticalLayout } = useSelector(selectSettings);
- const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
- const reduxPaneHeight = useSelector(selectPaneHeight);
- const reduxPaneWidth = useSelector(selectPaneWidth);
- const reduxSidebarWidth = useSelector(selectSidebarWidth);
-
+ const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | undefined;
+ const { activeWorkspaceMeta } = workspaceData || {};
const requestPaneRef = useRef(null);
const responsePaneRef = useRef(null);
const sidebarRef = useRef(null);
@@ -215,9 +212,9 @@ export const SidebarLayout: FC = ({
const [draggingSidebar, setDraggingSidebar] = useState(false);
const [draggingPaneHorizontal, setDraggingPaneHorizontal] = useState(false);
const [draggingPaneVertical, setDraggingPaneVertical] = useState(false);
- const [sidebarWidth, setSidebarWidth] = useState(reduxSidebarWidth || DEFAULT_SIDEBAR_WIDTH);
- const [paneWidth, setPaneWidth] = useState(reduxPaneWidth || DEFAULT_PANE_WIDTH);
- const [paneHeight, setPaneHeight] = useState(reduxPaneHeight || DEFAULT_PANE_HEIGHT);
+ const [sidebarWidth, setSidebarWidth] = useState(activeWorkspaceMeta?.sidebarWidth || DEFAULT_SIDEBAR_WIDTH);
+ const [paneWidth, setPaneWidth] = useState(activeWorkspaceMeta?.paneWidth || DEFAULT_PANE_WIDTH);
+ const [paneHeight, setPaneHeight] = useState(activeWorkspaceMeta?.paneHeight || DEFAULT_PANE_HEIGHT);
useEffect(() => {
const unsubscribe = window.main.on('toggle-sidebar', () => {
@@ -265,7 +262,7 @@ export const SidebarLayout: FC = ({
const handleMouseMove = useCallback((event: MouseEvent) => {
if (draggingPaneHorizontal) {
// Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
- const distance = reduxPaneWidth - paneWidth;
+ const distance = (activeWorkspaceMeta?.paneWidth || DEFAULT_PANE_WIDTH) - paneWidth;
if (!showDragOverlay && Math.abs(distance) > 0.02) {
setShowDragOverlay(true);
@@ -283,7 +280,7 @@ export const SidebarLayout: FC = ({
}
} else if (draggingPaneVertical) {
// Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
- const distance = reduxPaneHeight - paneHeight;
+ const distance = (activeWorkspaceMeta?.paneHeight || DEFAULT_PANE_HEIGHT) - paneHeight;
/* % */
if (!showDragOverlay && Math.abs(distance) > 0.02) {
setShowDragOverlay(true);
@@ -300,7 +297,7 @@ export const SidebarLayout: FC = ({
}
} else if (draggingSidebar) {
// Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
- const distance = reduxSidebarWidth - sidebarWidth;
+ const distance = (activeWorkspaceMeta?.sidebarWidth || DEFAULT_SIDEBAR_WIDTH) - sidebarWidth;
/* ems */
if (!showDragOverlay && Math.abs(distance) > 2) {
setShowDragOverlay(true);
@@ -317,7 +314,7 @@ export const SidebarLayout: FC = ({
handleSetSidebarWidth(localSidebarWidth);
}
}
- }, [draggingPaneHorizontal, draggingPaneVertical, draggingSidebar, handleSetPaneHeight, handleSetPaneWidth, handleSetSidebarWidth, paneHeight, paneWidth, reduxPaneHeight, reduxPaneWidth, reduxSidebarWidth, showDragOverlay, sidebarWidth]);
+ }, [activeWorkspaceMeta?.paneHeight, activeWorkspaceMeta?.paneWidth, activeWorkspaceMeta?.sidebarWidth, draggingPaneHorizontal, draggingPaneVertical, draggingSidebar, handleSetPaneHeight, handleSetPaneWidth, handleSetSidebarWidth, paneHeight, paneWidth, showDragOverlay, sidebarWidth]);
useEffect(() => {
document.addEventListener('mouseup', handleMouseUp);
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 9813ee15ce..ad00e6d4a4 100644
--- a/packages/insomnia/src/ui/components/sidebar/sidebar-create-dropdown.tsx
+++ b/packages/insomnia/src/ui/components/sidebar/sidebar-create-dropdown.tsx
@@ -1,32 +1,34 @@
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { createRequest, CreateRequestType } from '../../hooks/create-request';
import { createRequestGroup } from '../../hooks/create-request-group';
-import { selectActiveWorkspace, selectHotKeyRegistry } from '../../redux/selectors';
+import { selectHotKeyRegistry } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import { Dropdown, DropdownButton, DropdownItem, ItemContent } from '../base/dropdown';
-
export const SidebarCreateDropdown = () => {
const hotKeyRegistry = useSelector(selectHotKeyRegistry);
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceId = activeWorkspace?._id;
+ const {
+ activeWorkspace,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const create = useCallback((value: CreateRequestType) => {
- if (activeWorkspaceId) {
+ if (activeWorkspace._id) {
createRequest({
requestType: value,
- parentId: activeWorkspaceId,
- workspaceId: activeWorkspaceId,
+ parentId: activeWorkspace._id,
+ workspaceId: activeWorkspace._id,
});
}
- }, [activeWorkspaceId]);
+ }, [activeWorkspace._id]);
const createGroup = useCallback(() => {
- if (!activeWorkspaceId) {
+ if (!activeWorkspace._id) {
return;
}
- createRequestGroup(activeWorkspaceId);
- }, [activeWorkspaceId]);
+ createRequestGroup(activeWorkspace._id);
+ }, [activeWorkspace._id]);
const dataTestId = 'SidebarCreateDropdown';
return (
= ({ filter }) => {
const inputRef = useRef(null);
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
+
+ const {
+ activeWorkspace,
+ activeWorkspaceMeta,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const handleClearFilter = useCallback(async () => {
- if (activeWorkspaceMeta) {
- await models.workspaceMeta.update(activeWorkspaceMeta, { sidebarFilter: '' });
- }
+ await models.workspaceMeta.update(activeWorkspaceMeta, { sidebarFilter: '' });
if (inputRef.current) {
inputRef.current.value = '';
inputRef.current.focus();
@@ -31,9 +31,7 @@ export const SidebarFilter: FC = ({ filter }) => {
}, [activeWorkspaceMeta]);
const handleOnChange = useCallback(async (event: React.SyntheticEvent) => {
- if (activeWorkspaceMeta) {
- await models.workspaceMeta.update(activeWorkspaceMeta, { sidebarFilter: event.currentTarget.value });
- }
+ await models.workspaceMeta.update(activeWorkspaceMeta, { sidebarFilter: event.currentTarget.value });
}, [activeWorkspaceMeta]);
useDocBodyKeyboardShortcuts({
@@ -44,9 +42,6 @@ export const SidebarFilter: FC = ({ filter }) => {
const sortSidebar = async (order: SortOrder, parentId?: string) => {
let flushId: number | undefined;
- if (!activeWorkspace) {
- return;
- }
if (!parentId) {
parentId = activeWorkspace._id;
flushId = await db.bufferChanges();
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 bec584ad4c..1af880923c 100644
--- a/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx
+++ b/packages/insomnia/src/ui/components/sidebar/sidebar-request-row.tsx
@@ -1,7 +1,7 @@
import classnames from 'classnames';
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 { useRouteLoaderData } from 'react-router-dom';
import { CONTENT_TYPE_GRAPHQL } from '../../../common/constants';
import { getMethodOverrideHeader } from '../../../common/misc';
@@ -14,7 +14,7 @@ import { isWebSocketRequest, WebSocketRequest } from '../../../models/websocket-
import { useNunjucks } from '../../context/nunjucks/use-nunjucks';
import { createRequest, updateRequestMetaByParentId } from '../../hooks/create-request';
import { useReadyState } from '../../hooks/use-ready-state';
-import { selectActiveEnvironment, selectActiveProject, selectActiveWorkspace, selectActiveWorkspaceMeta } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
import type { DropdownHandle } from '../base/dropdown';
import { Editable } from '../base/editable';
import { Highlight } from '../base/highlight';
@@ -27,7 +27,6 @@ import { MethodTag } from '../tags/method-tag';
import { WebSocketTag } from '../tags/websocket-tag';
import { ConnectionCircle } from '../websockets/action-bar';
import { DnDProps, DragObject, dropHandleCreator, hoverHandleCreator, sourceCollect, targetCollect } from './dnd';
-
interface RawProps {
disableDragAndDrop?: boolean;
filter: string;
@@ -68,11 +67,14 @@ export const _SidebarRequestRow: FC = forwardRef(({
requestGroup,
}, ref) => {
const { handleRender } = useNunjucks();
- const activeProject = useSelector(selectActiveProject);
- const activeEnvironment = useSelector(selectActiveEnvironment);
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
- const activeWorkspaceId = activeWorkspace?._id;
+ const {
+ activeWorkspace,
+ activeWorkspaceMeta,
+ activeEnvironment,
+ activeProject,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
+
+ const activeWorkspaceId = activeWorkspace._id;
const [dragDirection, setDragDirection] = useState(0);
const [isEditing, setIsEditing] = useState(false);
const handleSetActiveRequest = useCallback(() => {
@@ -236,7 +238,7 @@ export const _SidebarRequestRow: FC = forwardRef(({
);
} else {
- let methodTag = null;
+ let methodTag;
if (isGrpcRequest(request)) {
methodTag = ;
diff --git a/packages/insomnia/src/ui/components/sync-pull-button.tsx b/packages/insomnia/src/ui/components/sync-pull-button.tsx
index e31be808fc..da04d45552 100644
--- a/packages/insomnia/src/ui/components/sync-pull-button.tsx
+++ b/packages/insomnia/src/ui/components/sync-pull-button.tsx
@@ -1,10 +1,9 @@
import React, { FC, ReactNode, useState } from 'react';
-import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { VCS } from '../../sync/vcs/vcs';
-import { selectActiveProject } from '../redux/selectors';
+import { WorkspaceLoaderData } from '../routes/workspace';
import { showError } from './modals';
-
interface Props {
vcs: VCS;
branch: string;
@@ -16,7 +15,9 @@ interface Props {
export const SyncPullButton: FC = props => {
const { className, children, disabled } = props;
- const project = useSelector(selectActiveProject);
+ const {
+ activeProject,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const [loading, setLoading] = useState(false);
const onClick = async () => {
const { vcs, onPull, branch } = props;
@@ -27,7 +28,7 @@ export const SyncPullButton: FC = props => {
try {
// Clone old VCS so we don't mess anything up while working on other projects
await newVCS.checkout([], branch);
- await newVCS.pull([], project.remoteId);
+ await newVCS.pull([], activeProject.remoteId);
} catch (err) {
showError({
title: 'Pull Error',
diff --git a/packages/insomnia/src/ui/context/nunjucks/__tests__/use-nunjucks.test.ts b/packages/insomnia/src/ui/context/nunjucks/__tests__/use-nunjucks.test.ts
deleted file mode 100644
index 0ac1428d64..0000000000
--- a/packages/insomnia/src/ui/context/nunjucks/__tests__/use-nunjucks.test.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-import { beforeEach, describe, expect, it, jest } from '@jest/globals';
-import { renderHook } from '@testing-library/react';
-import { mocked } from 'jest-mock';
-import configureMockStore from 'redux-mock-store';
-import thunk from 'redux-thunk';
-import type { PromiseValue } from 'type-fest';
-
-import { globalBeforeEach } from '../../../../__jest__/before-each';
-import { reduxStateForTest } from '../../../../__jest__/redux-state-for-test';
-import { withReduxStore } from '../../../../__jest__/with-redux-store';
-import { ACTIVITY_DEBUG } from '../../../../common/constants';
-import { getRenderContext, getRenderContextAncestors, render } from '../../../../common/render';
-import * as models from '../../../../models';
-import { RootState } from '../../../redux/modules';
-import { initializeNunjucksRenderPromiseCache, useNunjucks } from '../use-nunjucks';
-
-const renderMock = mocked(render);
-const getRenderContextMock = mocked(getRenderContext);
-const getRenderContextAncestorsMock = mocked(getRenderContextAncestors);
-
-jest.mock('../../../../common/render', () => ({
- render: jest.fn(),
- getRenderContext: jest.fn(),
- getRenderContextAncestors: jest.fn(),
-}));
-
-const middlewares = [thunk];
-const mockStore = configureMockStore(middlewares);
-
-const mockAncestors: PromiseValue> = [];
-const mockContext: PromiseValue> = { foo: 'bar' };
-
-describe('useNunjucks', () => {
- beforeEach(async () => {
- await globalBeforeEach();
- getRenderContextMock.mockResolvedValue(mockContext);
- getRenderContextAncestorsMock.mockResolvedValue(mockAncestors);
- initializeNunjucksRenderPromiseCache();
- });
-
- describe('handleGetRenderContext', () => {
-
- it('should return context with keys', async () => {
- const store = mockStore(await reduxStateForTest({
- activeActivity: ACTIVITY_DEBUG,
- }));
-
- const { result } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
-
- const context = await result.current.handleGetRenderContext();
-
- expect(context).toStrictEqual({
- context: mockContext,
- keys: [{
- name: '_.foo',
- value: 'bar',
- }],
- });
- });
-
- it('should get context using the active entities', async () => {
- // Arrange
- const workspace = await models.workspace.create();
- await models.workspaceMeta.getOrCreateByParentId(workspace._id);
- const environment = await models.environment.getOrCreateForParentId(workspace._id);
- const request = await models.request.create({ parentId: workspace._id });
-
- await models.workspaceMeta.updateByParentId(workspace._id, {
- activeEnvironmentId: environment._id,
- activeRequestId: request._id,
- });
-
- const store = mockStore(await reduxStateForTest({
- activeActivity: ACTIVITY_DEBUG,
- activeWorkspaceId: workspace._id,
- }));
-
- // Act
- const { result } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
- await result.current.handleGetRenderContext();
-
- // Assert
- expect(getRenderContextAncestorsMock).toBeCalledWith(request);
- expect(getRenderContextMock).toBeCalledWith({
- request,
- environmentId: environment._id,
- ancestors: mockAncestors,
- });
- });
- });
-
- describe('handleRender', () => {
- it('should render and get context once', async () => {
- // Arrange
- const store = mockStore(await reduxStateForTest());
-
- // Act
- const { result } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
- await result.current.handleRender('abc');
-
- // Assert
- expect(getRenderContextMock).toHaveBeenCalledTimes(1);
- expect(renderMock).toHaveBeenCalledTimes(1);
- });
-
- it('should render and get context twice because there is no caching', async () => {
- // Arrange
- const store = mockStore(await reduxStateForTest());
-
- // Act
- const { result } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
- await result.current.handleRender('abc');
- await result.current.handleRender('def');
-
- // Assert
- expect(getRenderContextMock).toHaveBeenCalledTimes(2);
- expect(renderMock).toHaveBeenCalledTimes(2);
- });
-
- it('should render and get context once because there is a cache', async () => {
- // Arrange
- const store = mockStore(await reduxStateForTest());
-
- // Act
- const { result } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
- const cacheKey = 'cache';
-
- await result.current.handleRender('abc', cacheKey);
- await result.current.handleRender('def', cacheKey);
-
- // Assert
- expect(getRenderContextMock).toHaveBeenCalledTimes(1);
- expect(renderMock).toHaveBeenCalledTimes(2);
- });
-
- it('should render and get context twice because there are different cache keys', async () => {
- // Arrange
- const store = mockStore(await reduxStateForTest());
-
- // Act
- const { result } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
- const cacheKeyOne = 'cache-1';
- const cacheKeyTwo = 'cache-2';
-
- await result.current.handleRender('abc', cacheKeyOne);
- await result.current.handleRender('def', cacheKeyTwo);
- await result.current.handleRender('ghi', cacheKeyOne);
- await result.current.handleRender('jkl', cacheKeyTwo);
-
- // Assert
- expect(getRenderContextMock).toHaveBeenCalledTimes(2);
- expect(renderMock).toHaveBeenCalledTimes(4);
- });
-
- it('should not change the cache during re-renders of the hook', async () => {
- // Arrange
- const store = mockStore(await reduxStateForTest());
-
- // Act
- const { result, rerender } = renderHook(useNunjucks, { wrapper: withReduxStore(store) });
- const cacheKeyOne = 'cache-1';
- const cacheKeyTwo = 'cache-2';
-
- await result.current.handleRender('abc', cacheKeyOne);
- rerender();
- await result.current.handleRender('def', cacheKeyTwo);
- rerender();
- await result.current.handleRender('ghi', cacheKeyOne);
- rerender();
- await result.current.handleRender('jkl', cacheKeyTwo);
-
- // Assert
- expect(getRenderContextMock).toHaveBeenCalledTimes(2);
- expect(renderMock).toHaveBeenCalledTimes(4);
- });
-
- it('should not change the cache during multiple renders of the hook', async () => {
- // Arrange
- const store = mockStore(await reduxStateForTest());
-
- // Act
- const cacheKeyOne = 'cache-1';
- const cacheKeyTwo = 'cache-2';
-
- await renderHook(useNunjucks, { wrapper: withReduxStore(store) }).result.current.handleRender('abc', cacheKeyOne);
- await renderHook(useNunjucks, { wrapper: withReduxStore(store) }).result.current.handleRender('def', cacheKeyTwo);
- await renderHook(useNunjucks, { wrapper: withReduxStore(store) }).result.current.handleRender('ghi', cacheKeyOne);
- await renderHook(useNunjucks, { wrapper: withReduxStore(store) }).result.current.handleRender('jkl', cacheKeyTwo);
-
- // Assert
- expect(getRenderContextMock).toHaveBeenCalledTimes(2);
- expect(renderMock).toHaveBeenCalledTimes(4);
- });
- });
-});
diff --git a/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts b/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts
index 913b26544d..281433cf10 100644
--- a/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts
+++ b/packages/insomnia/src/ui/context/nunjucks/use-nunjucks.ts
@@ -1,11 +1,12 @@
import { useCallback } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { getRenderContext, getRenderContextAncestors, HandleGetRenderContext, HandleRender, render } from '../../../common/render';
import { NUNJUCKS_TEMPLATE_GLOBAL_PROPERTY_NAME } from '../../../templating';
import { getKeys } from '../../../templating/utils';
-import { selectActiveEnvironment, selectActiveRequest, selectActiveWorkspace } from '../../redux/selectors';
-
+import { selectActiveRequest } from '../../redux/selectors';
+import { WorkspaceLoaderData } from '../../routes/workspace';
let getRenderContextPromiseCache: any = {};
export const initializeNunjucksRenderPromiseCache = () => {
@@ -18,18 +19,19 @@ initializeNunjucksRenderPromiseCache();
* Access to functions useful for Nunjucks rendering
*/
export const useNunjucks = () => {
- const environmentId = useSelector(selectActiveEnvironment)?._id;
const request = useSelector(selectActiveRequest);
- const workspace = useSelector(selectActiveWorkspace);
-
+ const {
+ activeWorkspace,
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const fetchRenderContext = useCallback(async () => {
- const ancestors = await getRenderContextAncestors(request || workspace);
+ const ancestors = await getRenderContextAncestors(request || activeWorkspace);
return getRenderContext({
request: request || undefined,
- environmentId,
+ environmentId: activeEnvironment._id,
ancestors,
});
- }, [environmentId, request, workspace]);
+ }, [activeEnvironment._id, request, activeWorkspace]);
const handleGetRenderContext: HandleGetRenderContext = useCallback(async () => {
const context = await fetchRenderContext();
diff --git a/packages/insomnia/src/ui/hooks/use-document-title.ts b/packages/insomnia/src/ui/hooks/use-document-title.ts
index 62b9cd5e29..1b1b9619d3 100644
--- a/packages/insomnia/src/ui/hooks/use-document-title.ts
+++ b/packages/insomnia/src/ui/hooks/use-document-title.ts
@@ -1,25 +1,25 @@
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
-import { ACTIVITY_HOME, getProductName } from '../../common/constants';
-import { selectActiveActivity, selectActiveEnvironment, selectActiveProject, selectActiveRequest, selectActiveWorkspace, selectActiveWorkspaceName } from '../redux/selectors';
+import { getProductName } from '../../common/constants';
+import { selectActiveRequest } from '../redux/selectors';
+import { WorkspaceLoaderData } from '../routes/workspace';
export const useDocumentTitle = () => {
- const activeActivity = useSelector(selectActiveActivity);
- const activeProject = useSelector(selectActiveProject);
- const activeWorkspaceName = useSelector(selectActiveWorkspaceName);
- const activeWorkspace = useSelector(selectActiveWorkspace);
+ const {
+ activeWorkspace,
+ activeEnvironment,
+ activeProject,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
- const activeEnvironment = useSelector(selectActiveEnvironment);
const activeRequest = useSelector(selectActiveRequest);
// Update document title
useEffect(() => {
let title;
- if (activeActivity === ACTIVITY_HOME) {
- title = getProductName();
- } else if (activeWorkspace && activeWorkspaceName) {
+ if (activeWorkspace && activeWorkspace.name) {
title = activeProject.name;
- title += ` - ${activeWorkspaceName}`;
+ title += ` - ${activeWorkspace.name}`;
if (activeEnvironment) {
title += ` (${activeEnvironment.name})`;
}
@@ -28,6 +28,6 @@ export const useDocumentTitle = () => {
}
}
document.title = title || getProductName();
- }, [activeActivity, activeEnvironment, activeProject.name, activeRequest, activeWorkspace, activeWorkspaceName]);
+ }, [activeEnvironment, activeProject.name, activeRequest, activeWorkspace]);
};
diff --git a/packages/insomnia/src/ui/hooks/use-global-keyboard-shortcuts.ts b/packages/insomnia/src/ui/hooks/use-global-keyboard-shortcuts.ts
index 22939d4613..6fd41eb8fd 100644
--- a/packages/insomnia/src/ui/hooks/use-global-keyboard-shortcuts.ts
+++ b/packages/insomnia/src/ui/hooks/use-global-keyboard-shortcuts.ts
@@ -1,4 +1,5 @@
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import * as models from '../../models';
import * as plugins from '../../plugins';
@@ -6,13 +7,12 @@ import { useDocBodyKeyboardShortcuts } from '../components/keydown-binder';
import { showModal } from '../components/modals';
import { SettingsModal, TAB_INDEX_SHORTCUTS } from '../components/modals/settings-modal';
import { WorkspaceSettingsModal } from '../components/modals/workspace-settings-modal';
-import { selectActiveWorkspace, selectActiveWorkspaceMeta, selectSettings } from '../redux/selectors';
-
+import { selectSettings } from '../redux/selectors';
+import { WorkspaceLoaderData } from '../routes/workspace';
export const useGlobalKeyboardShortcuts = () => {
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
+ const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | undefined;
const settings = useSelector(selectSettings);
-
+ const { activeWorkspace, activeWorkspaceMeta } = workspaceData || {};
useDocBodyKeyboardShortcuts({
workspace_showSettings:
() => activeWorkspace && showModal(WorkspaceSettingsModal),
diff --git a/packages/insomnia/src/ui/hooks/use-vcs-version.ts b/packages/insomnia/src/ui/hooks/use-vcs-version.ts
index dd34e6bb73..04159d34a6 100644
--- a/packages/insomnia/src/ui/hooks/use-vcs-version.ts
+++ b/packages/insomnia/src/ui/hooks/use-vcs-version.ts
@@ -1,14 +1,13 @@
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { ChangeBufferEvent, database } from '../../common/database';
import { BaseModel } from '../../models';
import {
- selectActiveApiSpec,
selectActiveRequest,
- selectActiveWorkspaceMeta,
} from '../redux/selectors';
-
+import { WorkspaceLoaderData } from '../routes/workspace';
// We use this hook to determine if the active request has been updated from the system (not the user typing)
// For example, by pulling a new version from the remote, switching branches, etc.
export function useActiveRequestSyncVCSVersion() {
@@ -27,8 +26,9 @@ export function useActiveRequestSyncVCSVersion() {
// For example, by pulling a new version from the remote, switching branches, etc.
export function useActiveApiSpecSyncVCSVersion() {
const [version, setVersion] = useState(0);
- const activeApiSpec = useSelector(selectActiveApiSpec);
-
+ const {
+ activeApiSpec,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
useEffect(() => {
const isRequestUpdatedFromSync = (changes: ChangeBufferEvent[]) => changes.find(([, doc, fromSync]) => activeApiSpec?._id === doc._id && fromSync);
database.onChange(changes => isRequestUpdatedFromSync(changes) && setVersion(v => v + 1));
@@ -40,7 +40,8 @@ export function useActiveApiSpecSyncVCSVersion() {
// 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);
-
+ const {
+ activeWorkspaceMeta,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
return ((activeWorkspaceMeta?.cachedGitLastCommitTime + '') + activeWorkspaceMeta?.cachedGitRepositoryBranch) + '';
}
diff --git a/packages/insomnia/src/ui/index.tsx b/packages/insomnia/src/ui/index.tsx
index 19224f7821..0ff2f10f0f 100644
--- a/packages/insomnia/src/ui/index.tsx
+++ b/packages/insomnia/src/ui/index.tsx
@@ -22,19 +22,17 @@ import { database } from '../common/database';
import { initializeLogging } from '../common/log';
import * as models from '../models';
import { DEFAULT_ORGANIZATION_ID } from '../models/organization';
-import { DEFAULT_PROJECT_ID, isRemoteProject } from '../models/project';
+import { DEFAULT_PROJECT_ID } from '../models/project';
import { initNewOAuthSession } from '../network/o-auth-2/get-token';
import { init as initPlugins } from '../plugins';
import { applyColorScheme } from '../plugins/misc';
import { invariant } from '../utils/invariant';
import { AppLoadingIndicator } from './components/app-loading-indicator';
-import { init as initStore, RootState } from './redux/modules';
+import { init as initStore } from './redux/modules';
import {
- setActiveActivity,
setActiveProject,
setActiveWorkspace,
} from './redux/modules/global';
-import { selectActiveProject } from './redux/selectors';
import { ErrorRoute } from './routes/error';
import Root from './routes/root';
import { initializeSentry } from './sentry';
@@ -500,7 +498,6 @@ function updateReduxNavigationState(store: Store, pathname: string) {
store.dispatch(
setActiveWorkspace(isActivityDebug?.params.workspaceId || '')
);
- store.dispatch(setActiveActivity(ACTIVITY_DEBUG));
} else if (isActivityDesign) {
currentActivity = ACTIVITY_SPEC;
store.dispatch(
@@ -509,7 +506,6 @@ function updateReduxNavigationState(store: Store, pathname: string) {
store.dispatch(
setActiveWorkspace(isActivityDesign?.params.workspaceId || '')
);
- store.dispatch(setActiveActivity(ACTIVITY_SPEC));
} else if (isActivityTest) {
currentActivity = ACTIVITY_UNIT_TEST;
store.dispatch(
@@ -518,13 +514,11 @@ function updateReduxNavigationState(store: Store, pathname: string) {
store.dispatch(
setActiveWorkspace(isActivityTest?.params.workspaceId || '')
);
- store.dispatch(setActiveActivity(ACTIVITY_UNIT_TEST));
} else {
currentActivity = ACTIVITY_HOME;
store.dispatch(
setActiveProject(isActivityHome?.params.projectId || '')
);
- store.dispatch(setActiveActivity(ACTIVITY_HOME));
}
return currentActivity;
@@ -549,46 +543,14 @@ async function renderApp() {
// Synchronizes the Redux store with the router history
// @HACK: This is temporary until we completely remove navigation through Redux
const synchronizeRouterState = () => {
- let currentActivity = (store.getState() as RootState).global.activeActivity;
let currentPathname = router.state.location.pathname;
-
- currentActivity = updateReduxNavigationState(store, router.state.location.pathname);
-
+ updateReduxNavigationState(store, router.state.location.pathname);
router.subscribe(({ location }) => {
if (location.pathname !== currentPathname) {
currentPathname = location.pathname;
- currentActivity = updateReduxNavigationState(store, location.pathname);
+ updateReduxNavigationState(store, location.pathname);
}
});
-
- store.subscribe(() => {
- const state = store.getState() as RootState;
- const activity = state.global.activeActivity;
-
- const activeProject = selectActiveProject(state);
- const organizationId = activeProject && isRemoteProject(activeProject) ? activeProject._id : DEFAULT_ORGANIZATION_ID;
-
- if (activity !== currentActivity) {
- currentActivity = activity;
- const activeProjectId = activeProject ? activeProject._id : DEFAULT_PROJECT_ID;
- if (activity === ACTIVITY_HOME) {
- router.navigate(`/organization/${organizationId}/project/${activeProject._id}`);
- } else if (activity === ACTIVITY_DEBUG) {
- router.navigate(
- `/organization/${organizationId}/project/${activeProjectId}/workspace/${state.global.activeWorkspaceId}/${ACTIVITY_DEBUG}`
- );
- } else if (activity === ACTIVITY_SPEC) {
- router.navigate(
- `/organization/${organizationId}/project/${activeProjectId}/workspace/${state.global.activeWorkspaceId}/${ACTIVITY_SPEC}`
- );
- } else if (activity === ACTIVITY_UNIT_TEST) {
- router.navigate(
- `/organization/${organizationId}/project/${state.global.activeProjectId}/workspace/${state.global.activeWorkspaceId}/test`
- );
- }
- }
-
- });
};
synchronizeRouterState();
diff --git a/packages/insomnia/src/ui/redux/__tests__/selectors.test.ts b/packages/insomnia/src/ui/redux/__tests__/selectors.test.ts
deleted file mode 100644
index 25301ed45d..0000000000
--- a/packages/insomnia/src/ui/redux/__tests__/selectors.test.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-import { beforeEach, describe, expect, it } from '@jest/globals';
-
-import { globalBeforeEach } from '../../../__jest__/before-each';
-import { reduxStateForTest } from '../../../__jest__/redux-state-for-test';
-import { ACTIVITY_DEBUG, ACTIVITY_HOME } from '../../../common/constants';
-import * as models from '../../../models';
-import { DEFAULT_PROJECT_ID, Project } from '../../../models/project';
-import { WorkspaceScopeKeys } from '../../../models/workspace';
-import { selectActiveApiSpec, selectActiveProject, selectActiveWorkspaceName, selectWorkspacesWithResolvedNameForActiveProject } from '../selectors';
-
-describe('selectors', () => {
- beforeEach(globalBeforeEach);
-
- describe('selectActiveProject', () => {
- it('should return the active project', async () => {
- // create two projects
- const projectA = await models.project.create();
- await models.project.create();
-
- // set first as selected
- const state = await reduxStateForTest({ activeProjectId: projectA._id });
-
- const project = selectActiveProject(state);
- expect(project).toStrictEqual(projectA);
- });
-
- it('should return default project if active project not found', async () => {
- // create two projects
- await models.project.create();
- await models.project.create();
-
- // set first as selected
- const state = await reduxStateForTest({ activeProjectId: 'some-other-project' });
-
- const project = selectActiveProject(state);
- expect(project).toStrictEqual(expect.objectContaining>({ _id: DEFAULT_PROJECT_ID }));
- });
-
- it('should return default project if no active project', async () => {
- // create two projects
- await models.project.create();
- await models.project.create();
-
- // set nothing as active
- const state = await reduxStateForTest({ activeProjectId: undefined });
-
- const project = selectActiveProject(state);
- expect(project).toStrictEqual(expect.objectContaining>({ _id: DEFAULT_PROJECT_ID }));
- });
- });
-
- describe('selectActiveApiSpec', () => {
- it('will return undefined when there is not an active workspace', async () => {
- const state = await reduxStateForTest({
- activeWorkspaceId: null,
- });
-
- expect(selectActiveApiSpec(state)).toBe(undefined);
- });
-
- it('will return the apiSpec for a given workspace', async () => {
- const workspace = await models.workspace.create({
- name: 'workspace.name',
- scope: WorkspaceScopeKeys.design,
- });
- const spec = await models.apiSpec.updateOrCreateForParentId(
- workspace._id,
- { fileName: 'apiSpec.fileName' },
- );
-
- const state = await reduxStateForTest({
- activeActivity: ACTIVITY_DEBUG,
- activeWorkspaceId: workspace._id,
- });
-
- expect(selectActiveApiSpec(state)).toEqual(spec);
- });
- });
-
- describe('selectActiveWorkspaceName', () => {
- it('returns workspace name for collections', async () => {
- const workspace = await models.workspace.create({
- name: 'workspace.name',
- scope: WorkspaceScopeKeys.collection,
- });
- // even though this shouldn't technically happen, we want to make sure the selector still makes the right decision (and ignores the api spec for collections)
- await models.apiSpec.updateOrCreateForParentId(
- workspace._id,
- { fileName: 'apiSpec.fileName' },
- );
- const state = await reduxStateForTest({
- activeActivity: ACTIVITY_DEBUG,
- activeWorkspaceId: workspace._id,
- });
-
- expect(selectActiveWorkspaceName(state)).toBe('workspace.name');
- });
-
- it('returns api spec name for design documents', async () => {
- const workspace = await models.workspace.create({
- name: 'workspace.name',
- scope: WorkspaceScopeKeys.design,
- });
- await models.apiSpec.updateOrCreateForParentId(
- workspace._id,
- { fileName: 'apiSpec.fileName' },
- );
- const state = await reduxStateForTest({
- activeActivity: ACTIVITY_DEBUG,
- activeWorkspaceId: workspace._id,
- });
-
- expect(selectActiveWorkspaceName(state)).toBe('apiSpec.fileName');
- });
-
- it('returns undefined when there is not an active workspace', async () => {
- await models.workspace.create({
- name: 'workspace.name',
- scope: WorkspaceScopeKeys.collection,
- });
- const state = await reduxStateForTest({
- activeActivity: ACTIVITY_DEBUG,
- activeWorkspaceId: null,
- });
-
- expect(selectActiveWorkspaceName(state)).toBe(undefined);
- });
- });
-
- describe('selectWorkspacesWithResolvedNameForActiveProject', () => {
- it('returns the workspaces with resolved names for the active project', async () => {
- const newCollectionWorkspace = await models.workspace.create({
- name: 'collectionWorkspace.name',
- scope: WorkspaceScopeKeys.collection,
- });
-
- const newDesignWorkspace = await models.workspace.create({
- name: 'designWorkspace.name',
- scope: WorkspaceScopeKeys.design,
- });
-
- const newApiSpec = await models.apiSpec.getOrCreateForParentId(
- newDesignWorkspace._id
- );
-
- // The database will update the api spec with the workspace name
- // That's why we need to explicitly update the ApiSpec name
- await models.apiSpec.update(newApiSpec, {
- fileName: 'apiSpec.name',
- });
-
- const state = await reduxStateForTest({
- activeActivity: ACTIVITY_HOME,
- activeWorkspaceId: null,
- });
-
- const workspaces = selectWorkspacesWithResolvedNameForActiveProject(state);
-
- const designWorkspace = workspaces.find(
- workspace => workspace._id === newDesignWorkspace._id
- );
-
- const collectionWorkspace = workspaces.find(
- workspace => workspace._id === newCollectionWorkspace._id
- );
-
- expect(
- designWorkspace
- ).toMatchObject(
- {
- _id: newDesignWorkspace._id,
- name: 'apiSpec.name',
- scope: WorkspaceScopeKeys.design,
- type: 'Workspace',
- },
- );
-
- expect(
- collectionWorkspace
- ).toMatchObject(
- {
- _id: newCollectionWorkspace._id,
- name: 'collectionWorkspace.name',
- scope: WorkspaceScopeKeys.collection,
- type: 'Workspace',
- },
- );
- });
- });
-});
diff --git a/packages/insomnia/src/ui/redux/modules/__tests__/global.test.ts b/packages/insomnia/src/ui/redux/modules/__tests__/global.test.ts
index a31819a41c..4fd3fa8ed9 100644
--- a/packages/insomnia/src/ui/redux/modules/__tests__/global.test.ts
+++ b/packages/insomnia/src/ui/redux/modules/__tests__/global.test.ts
@@ -1,19 +1,10 @@
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
import { globalBeforeEach } from '../../../../__jest__/before-each';
-import {
- ACTIVITY_DEBUG,
- ACTIVITY_HOME,
- ACTIVITY_SPEC,
- ACTIVITY_UNIT_TEST,
- GlobalActivity,
-} from '../../../../common/constants';
import {
LOCALSTORAGE_PREFIX,
- SET_ACTIVE_ACTIVITY,
SET_ACTIVE_PROJECT,
SET_ACTIVE_WORKSPACE,
- setActiveActivity,
setActiveProject,
setActiveWorkspace,
} from '../global';
@@ -27,24 +18,6 @@ describe('global', () => {
global.localStorage.clear();
});
- describe('setActiveActivity', () => {
- it.each([
- ACTIVITY_SPEC,
- ACTIVITY_DEBUG,
- ACTIVITY_UNIT_TEST,
- ACTIVITY_HOME,
- ])('should update local storage and track event: %s', (activity: GlobalActivity) => {
- const expectedEvent = {
- type: SET_ACTIVE_ACTIVITY,
- activity,
- };
- expect(setActiveActivity(activity)).toStrictEqual(expectedEvent);
- expect(global.localStorage.getItem(`${LOCALSTORAGE_PREFIX}::activity`)).toBe(
- JSON.stringify(activity),
- );
- });
- });
-
describe('setActiveProject', () => {
it('should update local storage', () => {
const projectId = 'id';
diff --git a/packages/insomnia/src/ui/redux/modules/__tests__/workspace.test.ts b/packages/insomnia/src/ui/redux/modules/__tests__/workspace.test.ts
deleted file mode 100644
index 3d2fada95d..0000000000
--- a/packages/insomnia/src/ui/redux/modules/__tests__/workspace.test.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-import { beforeEach, describe, expect, it, jest } from '@jest/globals';
-import configureMockStore from 'redux-mock-store';
-import thunk from 'redux-thunk';
-
-import { globalBeforeEach } from '../../../../__jest__/before-each';
-import { reduxStateForTest } from '../../../../__jest__/redux-state-for-test';
-import { ACTIVITY_DEBUG, ACTIVITY_SPEC } from '../../../../common/constants';
-import * as models from '../../../../models';
-import { SET_ACTIVE_ACTIVITY, SET_ACTIVE_PROJECT, SET_ACTIVE_WORKSPACE } from '../global';
-import { activateWorkspace } from '../workspace';
-
-jest.mock('../../../components/modals');
-jest.mock('../../../../ui/analytics');
-
-const middlewares = [thunk];
-const mockStore = configureMockStore(middlewares);
-
-describe('workspace', () => {
- beforeEach(globalBeforeEach);
- describe('activateWorkspace', () => {
- it('should do nothing if workspace cannot be found', async () => {
- const store = mockStore(await reduxStateForTest({ activeProjectId: 'abc', activeWorkspaceId: 'def' }));
-
- await store.dispatch(activateWorkspace({ workspaceId: 'DOES_NOT_EXIST' }));
-
- expect(store.getActions()).toEqual([]);
- });
-
- it('should activate project and workspace and activity using workspaceId', async () => {
- const project = await models.project.create();
- const workspace = await models.workspace.create({ scope: 'design', parentId: project._id });
- const store = mockStore(await reduxStateForTest({ activeProjectId: 'abc', activeWorkspaceId: 'def' }));
-
- await store.dispatch(activateWorkspace({ workspaceId: workspace._id }));
-
- expect(store.getActions()).toEqual([
- {
- type: SET_ACTIVE_PROJECT,
- projectId: project._id,
- },
- {
- type: SET_ACTIVE_WORKSPACE,
- workspaceId: workspace._id,
- },
- {
- type: SET_ACTIVE_ACTIVITY,
- activity: ACTIVITY_SPEC,
- },
- ]);
- });
-
- it('should activate project and workspace and activity from home', async () => {
- const project = await models.project.create();
- const workspace = await models.workspace.create({ scope: 'design', parentId: project._id });
- const store = mockStore(await reduxStateForTest({ activeProjectId: 'abc', activeWorkspaceId: 'def' }));
-
- await store.dispatch(activateWorkspace({ workspace }));
-
- expect(store.getActions()).toEqual([
- {
- type: SET_ACTIVE_PROJECT,
- projectId: project._id,
- },
- {
- type: SET_ACTIVE_WORKSPACE,
- workspaceId: workspace._id,
- },
- {
- type: SET_ACTIVE_ACTIVITY,
- activity: ACTIVITY_SPEC,
- },
- ]);
- });
-
- it('should switch to the default design activity', async () => {
- const project = await models.project.create();
- const workspace = await models.workspace.create({ scope: 'design', parentId: project._id });
- const store = mockStore(await reduxStateForTest({ activeProjectId: project._id, activeWorkspaceId: workspace._id }));
-
- await store.dispatch(activateWorkspace({ workspace }));
-
- expect(store.getActions()).toEqual([
- {
- type: SET_ACTIVE_PROJECT,
- projectId: project._id,
- },
- {
- type: SET_ACTIVE_WORKSPACE,
- workspaceId: workspace._id,
- },
- {
- type: SET_ACTIVE_ACTIVITY,
- activity: ACTIVITY_SPEC,
- },
- ]);
- });
-
- it.each([ACTIVITY_DEBUG])('should not switch activity if already in a supported collection activity: %s', async activeActivity => {
- const project = await models.project.create();
- const workspace = await models.workspace.create({ scope: 'design', parentId: project._id });
- const store = mockStore(await reduxStateForTest({ activeProjectId: project._id, activeWorkspaceId: workspace._id, activeActivity }));
-
- await store.dispatch(activateWorkspace({ workspace }));
-
- expect(store.getActions()).toEqual([
- {
- type: SET_ACTIVE_PROJECT,
- projectId: project._id,
- },
- {
- type: SET_ACTIVE_WORKSPACE,
- workspaceId: workspace._id,
- },
- ]);
- });
-
- it('should switch to the default collection activity', async () => {
- const project = await models.project.create();
- const workspace = await models.workspace.create({ scope: 'collection', parentId: project._id });
- const store = mockStore(await reduxStateForTest({ activeProjectId: project._id, activeWorkspaceId: workspace._id }));
-
- await store.dispatch(activateWorkspace({ workspace }));
-
- expect(store.getActions()).toEqual([
- {
- type: SET_ACTIVE_PROJECT,
- projectId: project._id,
- },
- {
- type: SET_ACTIVE_WORKSPACE,
- workspaceId: workspace._id,
- },
- {
- type: SET_ACTIVE_ACTIVITY,
- activity: ACTIVITY_DEBUG,
- },
- ]);
- });
- });
-});
diff --git a/packages/insomnia/src/ui/redux/modules/global.tsx b/packages/insomnia/src/ui/redux/modules/global.tsx
index bf35bf10dd..9cfc269223 100644
--- a/packages/insomnia/src/ui/redux/modules/global.tsx
+++ b/packages/insomnia/src/ui/redux/modules/global.tsx
@@ -1,10 +1,6 @@
import { combineReducers } from 'redux';
-import type { DashboardSortOrder, GlobalActivity } from '../../../common/constants';
-import {
- ACTIVITY_HOME,
- isValidActivity,
-} from '../../../common/constants';
+import type { DashboardSortOrder } from '../../../common/constants';
import { DEFAULT_PROJECT_ID } from '../../../models/project';
export const LOCALSTORAGE_PREFIX = 'insomnia::meta';
@@ -17,15 +13,6 @@ export const SET_ACTIVE_ACTIVITY = 'global/activate-activity';
// ~~~~~~~~ //
// REDUCERS //
// ~~~~~~~~ //
-function activeActivityReducer(state: string | null = null, action: any) {
- switch (action.type) {
- case SET_ACTIVE_ACTIVITY:
- return action.activity;
-
- default:
- return state;
- }
-}
function activeProjectReducer(state: string = DEFAULT_PROJECT_ID, action: any) {
switch (action.type) {
@@ -71,7 +58,6 @@ export interface GlobalState {
activeProjectId: string;
dashboardSortOrder: DashboardSortOrder;
activeWorkspaceId: string | null;
- activeActivity: GlobalActivity | null;
isLoggedIn: boolean;
}
@@ -79,7 +65,6 @@ export const reducer = combineReducers({
dashboardSortOrder: dashboardSortOrderReducer,
activeProjectId: activeProjectReducer,
activeWorkspaceId: activeWorkspaceReducer,
- activeActivity: activeActivityReducer,
isLoggedIn: loginStateChangeReducer,
});
@@ -91,19 +76,6 @@ export const loginStateChange = (loggedIn: boolean) => ({
loggedIn,
});
-/*
- Go to an explicit activity
- */
-export const setActiveActivity = (activity: GlobalActivity) => {
- activity = isValidActivity(activity) ? activity : ACTIVITY_HOME;
- window.localStorage.setItem(`${LOCALSTORAGE_PREFIX}::activity`, JSON.stringify(activity));
- window.main.trackPageView({ name: activity });
- return {
- type: SET_ACTIVE_ACTIVITY,
- activity,
- };
-};
-
export const setActiveProject = (projectId: string) => {
const key = `${LOCALSTORAGE_PREFIX}::activeProjectId`;
window.localStorage.setItem(key, JSON.stringify(projectId));
@@ -113,17 +85,6 @@ export const setActiveProject = (projectId: string) => {
};
};
-export const setDashboardSortOrder = (sortOrder: DashboardSortOrder) => {
- const key = `${LOCALSTORAGE_PREFIX}::dashboard-sort-order`;
- window.localStorage.setItem(key, JSON.stringify(sortOrder));
- return {
- type: SET_DASHBOARD_SORT_ORDER,
- payload: {
- sortOrder,
- },
- };
-};
-
export const setActiveWorkspace = (workspaceId: string | null) => {
const key = `${LOCALSTORAGE_PREFIX}::activeWorkspaceId`;
window.localStorage.setItem(key, JSON.stringify(workspaceId));
diff --git a/packages/insomnia/src/ui/redux/modules/workspace.ts b/packages/insomnia/src/ui/redux/modules/workspace.ts
deleted file mode 100644
index 8169e5f3dd..0000000000
--- a/packages/insomnia/src/ui/redux/modules/workspace.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Dispatch } from 'redux';
-import type { RequireExactlyOne } from 'type-fest';
-
-import { ACTIVITY_DEBUG, ACTIVITY_SPEC, GlobalActivity, isCollectionActivity, isDesignActivity } from '../../../common/constants';
-import * as models from '../../../models';
-import { isCollection, isDesign, Workspace } from '../../../models/workspace';
-import { selectActiveActivity, selectWorkspaces } from '../selectors';
-import { RootState } from '.';
-import { setActiveActivity, setActiveProject, setActiveWorkspace } from './global';
-
-export const activateWorkspace = ({ workspace, workspaceId }: RequireExactlyOne<{workspace: Workspace; workspaceId: string}>) => {
- return async (dispatch: Dispatch, getState: () => RootState) => {
- // If we have no workspace but we do have an id, search for it
- if (!workspace && workspaceId) {
- workspace = selectWorkspaces(getState()).find(({ _id }) => _id === workspaceId);
- }
-
- // If we still have no workspace, exit
- if (!workspace) {
- return;
- }
-
- const activeActivity = selectActiveActivity(getState()) || undefined;
-
- // Activate the correct project
- const nextProjectId = workspace.parentId;
- dispatch(setActiveProject(nextProjectId));
-
- // Activate the correct workspace
- const nextWorkspaceId = workspace._id;
- dispatch(setActiveWorkspace(nextWorkspaceId));
-
- // Activate the correct activity
- if (isCollection(workspace) && isCollectionActivity(activeActivity)) {
- // we are in a collection, and our active activity is a collection activity
- return;
- }
-
- if (isDesign(workspace) && isDesignActivity(activeActivity)) {
- // we are in a design document, and our active activity is a design activity
- return;
- }
-
- const { activeActivity: cachedActivity } = await models.workspaceMeta.getOrCreateByParentId(workspace._id);
- const nextActivity = cachedActivity as GlobalActivity || (isDesign(workspace) ? ACTIVITY_SPEC : ACTIVITY_DEBUG);
- dispatch(setActiveActivity(nextActivity));
-
- // TODO: dispatch one action to activate the project, workspace and activity in one go to avoid jumps in the UI
- };
-};
diff --git a/packages/insomnia/src/ui/redux/selectors.ts b/packages/insomnia/src/ui/redux/selectors.ts
index acc0e011fb..55ad9a8fd7 100644
--- a/packages/insomnia/src/ui/redux/selectors.ts
+++ b/packages/insomnia/src/ui/redux/selectors.ts
@@ -1,7 +1,7 @@
import { createSelector } from 'reselect';
import type { ValueOf } from 'type-fest';
-import { isWorkspaceActivity, PREVIEW_MODE_SOURCE } from '../../common/constants';
+import { PREVIEW_MODE_SOURCE } from '../../common/constants';
import * as models from '../../models';
import { BaseModel } from '../../models';
import { GrpcRequest, isGrpcRequest } from '../../models/grpc-request';
@@ -11,10 +11,8 @@ import { DEFAULT_PROJECT_ID, isRemoteProject } from '../../models/project';
import { isRequest, Request } from '../../models/request';
import { isRequestGroup, RequestGroup } from '../../models/request-group';
import { type Response } from '../../models/response';
-import { UnitTestResult } from '../../models/unit-test-result';
import { isWebSocketRequest, WebSocketRequest } from '../../models/websocket-request';
import { type WebSocketResponse } from '../../models/websocket-response';
-import { isCollection } from '../../models/workspace';
import { RootState } from './modules';
type EntitiesLists = {
@@ -24,43 +22,29 @@ type EntitiesLists = {
// ~~~~~~~~~ //
// Selectors //
// ~~~~~~~~~ //
-export const selectEntities = createSelector(
- (state: RootState) => state.entities,
- entities => entities,
-);
-
-export const selectGlobal = createSelector(
- (state: RootState) => state.global,
- global => global,
-);
-
export const selectEntitiesLists = createSelector(
- selectEntities,
+ (state: RootState) => state.entities,
entities => {
+ // transforms entities object from object keyed on id to array of entities containing id
const entitiesLists: any = {};
-
- for (const k of Object.keys(entities)) {
- const entityMap = (entities as any)[k];
- entitiesLists[k] = Object.keys(entityMap).map(id => entityMap[id]);
+ for (const [k, v] of Object.entries(entities)) {
+ entitiesLists[k] = Object.keys(v).map(id => v[id]);
}
-
return entitiesLists as EntitiesLists;
},
);
export const selectEntitiesChildrenMap = createSelector(selectEntitiesLists, entities => {
const parentLookupMap: any = {};
-
- for (const key of Object.keys(entities)) {
- for (const entity of (entities as any)[key]) {
- if (!entity.parentId) {
- continue;
- }
-
- if (parentLookupMap[entity.parentId]) {
- parentLookupMap[entity.parentId].push(entity);
- } else {
- parentLookupMap[entity.parentId] = [entity];
+ // group entities by parent
+ for (const value of Object.values(entities)) {
+ for (const entity of value) {
+ if (entity.parentId) {
+ if (parentLookupMap[entity.parentId]) {
+ parentLookupMap[entity.parentId].push(entity);
+ } else {
+ parentLookupMap[entity.parentId] = [entity];
+ }
}
}
}
@@ -96,50 +80,21 @@ export const selectRemoteProjects = createSelector(
projects => projects.filter(isRemoteProject),
);
-export const selectActiveProject = createSelector(
- selectEntities,
- (state: RootState) => state.global.activeProjectId,
- (entities, activeProjectId) => {
- return entities.projects[activeProjectId] || entities.projects[DEFAULT_PROJECT_ID];
- },
-);
-
-export const selectDashboardSortOrder = createSelector(
- selectGlobal,
- global => global.dashboardSortOrder
-);
-
-export const selectWorkspaces = createSelector(
- selectEntitiesLists,
- entities => entities.workspaces,
-);
-
export const selectWorkspacesForActiveProject = createSelector(
- selectWorkspaces,
- selectActiveProject,
- (workspaces, activeProject) => workspaces.filter(workspace => workspace.parentId === activeProject._id),
+ selectEntitiesLists,
+ (state: RootState) => state.global.activeProjectId,
+ (entities, activeProjectId) => entities.workspaces.filter(workspace => workspace.parentId === (activeProjectId || DEFAULT_PROJECT_ID)),
);
export const selectActiveWorkspace = createSelector(
selectWorkspacesForActiveProject,
(state: RootState) => state.global.activeWorkspaceId,
- (state: RootState) => state.global.activeActivity,
- (workspaces, activeWorkspaceId, activeActivity) => {
- // Only return an active workspace if we're in an activity
- if (activeActivity && isWorkspaceActivity(activeActivity)) {
- const workspace = workspaces.find(workspace => workspace._id === activeWorkspaceId);
- return workspace;
- }
-
- return undefined;
+ (workspaces, activeWorkspaceId) => {
+ const workspace = workspaces.find(workspace => workspace._id === activeWorkspaceId);
+ return workspace;
},
);
-export const selectWorkspaceMetas = createSelector(
- selectEntitiesLists,
- entities => entities.workspaceMetas,
-);
-
export const selectActiveWorkspaceMeta = createSelector(
selectActiveWorkspace,
selectEntitiesLists,
@@ -154,57 +109,6 @@ export const selectApiSpecs = createSelector(
entities => entities.apiSpecs,
);
-export const selectWorkspacesWithResolvedNameForActiveProject = createSelector(
- selectWorkspacesForActiveProject,
- selectApiSpecs,
- (workspaces, apiSpecs) => {
- return workspaces.map(workspace => {
- if (isCollection(workspace)) {
- return workspace;
- }
-
- const apiSpec = apiSpecs.find(
- apiSpec => apiSpec.parentId === workspace._id
- );
-
- return {
- ...workspace,
- name: apiSpec?.fileName || workspace.name,
- };
- });
- }
-);
-
-export const selectActiveApiSpec = createSelector(
- selectApiSpecs,
- selectActiveWorkspace,
- (apiSpecs, activeWorkspace) => {
- if (!activeWorkspace) {
- // There should never be an active api spec without an active workspace
- return undefined;
- }
- return apiSpecs.find(apiSpec => apiSpec.parentId === activeWorkspace._id);
- }
-);
-
-export const selectActiveWorkspaceName = createSelector(
- selectActiveWorkspace,
- selectActiveApiSpec,
- (activeWorkspace, activeApiSpec) => {
- if (!activeWorkspace) {
- // see above, but since the selectActiveWorkspace selector really can return undefined, we need to handle it here.
- return undefined;
- }
-
- return isCollection(activeWorkspace) ? activeWorkspace.name : activeApiSpec?.fileName;
- }
-);
-
-export const selectEnvironments = createSelector(
- selectEntitiesLists,
- entities => entities.environments,
-);
-
export const selectGitRepositories = createSelector(
selectEntitiesLists,
entities => entities.gitRepositories,
@@ -227,22 +131,16 @@ export const selectRequests = createSelector(
export const selectActiveEnvironment = createSelector(
selectActiveWorkspaceMeta,
- selectEnvironments,
- (meta, environments) => {
+ selectEntitiesLists,
+ (meta, entities) => {
if (!meta) {
return null;
}
- return environments.find(environment => environment._id === meta.activeEnvironmentId) || null;
+ return entities.environments.find(environment => environment._id === meta.activeEnvironmentId) || null;
},
);
-export const selectActiveWorkspaceClientCertificates = createSelector(
- selectEntitiesLists,
- selectActiveWorkspace,
- (entities, activeWorkspace) => entities.clientCertificates.filter(c => c.parentId === activeWorkspace?._id),
-);
-
export const selectActiveGitRepository = createSelector(
selectEntitiesLists,
selectActiveWorkspaceMeta,
@@ -333,7 +231,7 @@ export const selectWorkspaceRequestsAndRequestGroups = createSelector(
);
export const selectActiveRequest = createSelector(
- selectEntities,
+ (state: RootState) => state.entities,
selectActiveWorkspaceMeta,
(entities, workspaceMeta) => {
const id = workspaceMeta?.activeRequestId || 'n/a';
@@ -354,25 +252,6 @@ export const selectActiveRequest = createSelector(
},
);
-export const selectActiveCookieJar = createSelector(
- selectEntitiesLists,
- selectActiveWorkspace,
- (entities, workspace) => {
- const cookieJar = entities.cookieJars.find(cj => cj.parentId === workspace?._id);
- return cookieJar || null;
- },
-);
-
-export const selectUnseenWorkspaces = createSelector(
- selectEntitiesLists,
- entities => {
- const { workspaces, workspaceMetas } = entities;
- return workspaces.filter(workspace => {
- const meta = workspaceMetas.find(m => m.parentId === workspace._id);
- return !!(meta && !meta.hasSeen);
- });
- });
-
export const selectActiveRequestMeta = createSelector(
selectActiveRequest,
selectEntitiesLists,
@@ -449,84 +328,7 @@ export const selectActiveResponse = createSelector(
},
);
-export const selectActiveUnitTestResult = createSelector(
- selectEntitiesLists,
- selectActiveWorkspace,
- (entities, activeWorkspace) => {
- if (!activeWorkspace) {
- return null;
- }
-
- let recentResult: UnitTestResult | null = null;
-
- for (const r of entities.unitTestResults) {
- if (r.parentId !== activeWorkspace._id) {
- continue;
- }
-
- if (!recentResult) {
- recentResult = r;
- continue;
- }
-
- if (r.created > recentResult.created) {
- recentResult = r;
- }
- }
-
- return recentResult;
- },
-);
-
-export const selectActiveUnitTestSuite = createSelector(
- selectEntitiesLists,
- selectActiveWorkspaceMeta,
- (entities, activeWorkspaceMeta) => {
- if (!activeWorkspaceMeta) {
- return null;
- }
-
- const id = activeWorkspaceMeta.activeUnitTestSuiteId;
- return entities.unitTestSuites.find(s => s._id === id) || null;
- },
-);
-
-export const selectActiveUnitTests = createSelector(
- selectEntitiesLists,
- selectActiveUnitTestSuite,
- (entities, activeUnitTestSuite) => {
- if (!activeUnitTestSuite) {
- return [];
- }
-
- return entities.unitTests.filter(s => s.parentId === activeUnitTestSuite._id);
- },
-);
-
-export const selectActiveProjectName = createSelector(
- selectActiveProject,
- activeProject => activeProject.name,
-);
-
-export const selectActiveUnitTestSuites = createSelector(
- selectEntitiesLists,
- selectActiveWorkspace,
- (entities, activeWorkspace) => {
- return entities.unitTestSuites.filter(s => s.parentId === activeWorkspace?._id);
- },
-);
-
export const selectSyncItems = createSelector(
selectActiveWorkspaceEntities,
getStatusCandidates,
);
-
-export const selectIsLoggedIn = createSelector(
- selectGlobal,
- global => global.isLoggedIn,
-);
-
-export const selectActiveActivity = createSelector(
- selectGlobal,
- global => global.activeActivity,
-);
diff --git a/packages/insomnia/src/ui/redux/sidebar-selectors.ts b/packages/insomnia/src/ui/redux/sidebar-selectors.ts
index 69f2e1c08e..1234b64462 100644
--- a/packages/insomnia/src/ui/redux/sidebar-selectors.ts
+++ b/packages/insomnia/src/ui/redux/sidebar-selectors.ts
@@ -1,6 +1,5 @@
import { createSelector } from 'reselect';
-import { DEFAULT_PANE_HEIGHT, DEFAULT_PANE_WIDTH, DEFAULT_SIDEBAR_WIDTH } from '../../common/constants';
import { fuzzyMatchAll } from '../../common/misc';
import type { BaseModel } from '../../models';
import { GrpcRequest, isGrpcRequest } from '../../models/grpc-request';
@@ -43,27 +42,10 @@ export interface SidebarChildren {
all: Child[];
pinned: Child[];
}
-
-export const selectSidebarWidth = createSelector(
- selectActiveWorkspaceMeta,
- activeWorkspaceMeta => activeWorkspaceMeta?.sidebarWidth || DEFAULT_SIDEBAR_WIDTH,
-);
-
-export const selectPaneWidth = createSelector(
- selectActiveWorkspaceMeta,
- activeWorkspaceMeta => activeWorkspaceMeta?.paneWidth || DEFAULT_PANE_WIDTH,
-);
-
-export const selectPaneHeight = createSelector(
- selectActiveWorkspaceMeta,
- activeWorkspaceMeta => activeWorkspaceMeta?.paneHeight || DEFAULT_PANE_HEIGHT,
-);
-
export const selectSidebarFilter = createSelector(
selectActiveWorkspaceMeta,
activeWorkspaceMeta => activeWorkspaceMeta ? activeWorkspaceMeta.sidebarFilter : '',
);
-
export const selectSidebarChildren = createSelector(
selectCollapsedRequestGroups,
selectPinnedRequests,
diff --git a/packages/insomnia/src/ui/routes/debug.tsx b/packages/insomnia/src/ui/routes/debug.tsx
index 470ca8427a..35ea214b46 100644
--- a/packages/insomnia/src/ui/routes/debug.tsx
+++ b/packages/insomnia/src/ui/routes/debug.tsx
@@ -1,6 +1,7 @@
import { ServiceError, StatusObject } from '@grpc/grpc-js';
import React, { FC, Fragment, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { ChangeBufferEvent, database as db } from '../../common/database';
import { generateId } from '../../common/misc';
@@ -39,13 +40,10 @@ import { WebSocketRequestPane } from '../components/websockets/websocket-request
import { updateRequestMetaByParentId } from '../hooks/create-request';
import { createRequestGroup } from '../hooks/create-request-group';
import {
- selectActiveEnvironment,
selectActiveRequest,
- selectActiveWorkspace,
- selectActiveWorkspaceMeta,
selectSettings,
} from '../redux/selectors';
-import { selectSidebarFilter } from '../redux/sidebar-selectors';
+import { WorkspaceLoaderData } from './workspace';
export interface GrpcMessage {
id: string;
text: string;
@@ -74,9 +72,12 @@ const INITIAL_GRPC_REQUEST_STATE = {
};
export const Debug: FC = () => {
- const activeEnvironment = useSelector(selectActiveEnvironment);
+ const {
+ activeWorkspace,
+ activeWorkspaceMeta,
+ activeEnvironment,
+ } = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
const activeRequest = useSelector(selectActiveRequest);
- const activeWorkspace = useSelector(selectActiveWorkspace);
const [grpcStates, setGrpcStates] = useState([]);
useEffect(() => {
db.onChange(async (changes: ChangeBufferEvent[]) => {
@@ -90,18 +91,14 @@ export const Debug: FC = () => {
}, []);
useEffect(() => {
const fn = async () => {
- if (activeWorkspace) {
- const children = await db.withDescendants(activeWorkspace);
- const grpcRequests = children.filter(d => isGrpcRequest(d));
- setGrpcStates(grpcRequests.map(r => ({ requestId: r._id, ...INITIAL_GRPC_REQUEST_STATE })));
- }
+ const children = await db.withDescendants(activeWorkspace);
+ const grpcRequests = children.filter(d => isGrpcRequest(d));
+ setGrpcStates(grpcRequests.map(r => ({ requestId: r._id, ...INITIAL_GRPC_REQUEST_STATE })));
};
fn();
}, [activeWorkspace]);
const settings = useSelector(selectSettings);
- const sidebarFilter = useSelector(selectSidebarFilter);
- const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
const [runningRequests, setRunningRequests] = useState({});
const setLoading = (isLoading: boolean) => {
invariant(activeRequest, 'No active request');
@@ -192,27 +189,21 @@ export const Debug: FC = () => {
},
request_createHTTP:
async () => {
- if (activeWorkspace) {
- const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
- const request = await models.request.create({
- parentId,
- name: 'New Request',
- });
- if (activeWorkspaceMeta) {
- await models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: request._id });
- }
- await updateRequestMetaByParentId(request._id, {
- lastActive: Date.now(),
- });
- models.stats.incrementCreatedRequests();
- window.main.trackSegmentEvent({ event: SegmentEvent.requestCreate, properties: { requestType: 'HTTP' } });
- }
+ const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
+ const request = await models.request.create({
+ parentId,
+ name: 'New Request',
+ });
+ await models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: request._id });
+ await updateRequestMetaByParentId(request._id, {
+ lastActive: Date.now(),
+ });
+ models.stats.incrementCreatedRequests();
+ window.main.trackSegmentEvent({ event: SegmentEvent.requestCreate, properties: { requestType: 'HTTP' } });
},
request_showCreateFolder:
() => {
- if (activeWorkspace) {
- createRequestGroup(activeRequest ? activeRequest.parentId : activeWorkspace._id);
- }
+ createRequestGroup(activeRequest ? activeRequest.parentId : activeWorkspace._id);
},
request_showRecent:
() => showModal(RequestSwitcherModal, {
@@ -263,11 +254,11 @@ export const Debug: FC = () => {
diff --git a/packages/insomnia/src/ui/routes/modals.tsx b/packages/insomnia/src/ui/routes/modals.tsx
index 880e3e1e49..1316db0ae1 100644
--- a/packages/insomnia/src/ui/routes/modals.tsx
+++ b/packages/insomnia/src/ui/routes/modals.tsx
@@ -1,5 +1,5 @@
import React, { FC, Fragment } from 'react';
-import { useSelector } from 'react-redux';
+import { useRouteLoaderData } from 'react-router-dom';
import { ErrorBoundary } from '../components/error-boundary';
import { registerModal } from '../components/modals';
@@ -34,17 +34,11 @@ import { WorkspaceEnvironmentsEditModal } from '../components/modals/workspace-e
import { WorkspaceSettingsModal } from '../components/modals/workspace-settings-modal';
import { WrapperModal } from '../components/modals/wrapper-modal';
import { useVCS } from '../hooks/use-vcs';
-import {
- selectActiveCookieJar,
- selectActiveEnvironment,
- selectActiveWorkspace,
-} from '../redux/selectors';
+import { WorkspaceLoaderData } from './workspace';
const Modals: FC = () => {
- const activeCookieJar = useSelector(selectActiveCookieJar);
- const activeWorkspace = useSelector(selectActiveWorkspace);
- const activeEnvironment = useSelector(selectActiveEnvironment);
-
+ const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | undefined;
+ const { activeWorkspace, activeEnvironment, activeCookieJar } = workspaceData || {};
const vcs = useVCS({
workspaceId: activeWorkspace?._id,
});
@@ -113,6 +107,10 @@ const Modals: FC = () => {
registerModal(instance, 'WorkspaceSettingsModal')
}
/>
+
+ registerModal(instance, 'RequestSwitcherModal')}
+ />
>
) : null}
@@ -128,10 +126,6 @@ const Modals: FC = () => {
ref={instance => registerModal(instance, 'ResponseDebugModal')}
/>
- registerModal(instance, 'RequestSwitcherModal')}
- />
-
registerModal(instance, 'EnvironmentEditModal')}
/>
diff --git a/packages/insomnia/src/ui/routes/workspace.tsx b/packages/insomnia/src/ui/routes/workspace.tsx
index 15da9a4e07..98601c124a 100644
--- a/packages/insomnia/src/ui/routes/workspace.tsx
+++ b/packages/insomnia/src/ui/routes/workspace.tsx
@@ -2,6 +2,9 @@ import React from 'react';
import { LoaderFunction, Outlet, useLoaderData } from 'react-router-dom';
import * as models from '../../models';
+import { ApiSpec } from '../../models/api-spec';
+import { ClientCertificate } from '../../models/client-certificate';
+import { CookieJar } from '../../models/cookie-jar';
import { Environment } from '../../models/environment';
import { GitRepository } from '../../models/git-repository';
import { Project } from '../../models/project';
@@ -10,12 +13,15 @@ import { WorkspaceMeta } from '../../models/workspace-meta';
import { invariant } from '../../utils/invariant';
export interface WorkspaceLoaderData {
activeWorkspace: Workspace;
- activeWorkspaceMeta?: WorkspaceMeta;
+ activeWorkspaceMeta: WorkspaceMeta;
activeProject: Project;
gitRepository: GitRepository | null;
activeEnvironment: Environment;
+ activeCookieJar: CookieJar;
baseEnvironment: Environment;
subEnvironments: Environment[];
+ activeApiSpec: ApiSpec | null;
+ clientCertificates: ClientCertificate[];
}
export const workspaceLoader: LoaderFunction = async ({
@@ -38,6 +44,7 @@ export const workspaceLoader: LoaderFunction = async ({
const activeWorkspaceMeta = await models.workspaceMeta.getOrCreateByParentId(
workspaceId,
);
+ invariant(activeWorkspaceMeta, 'Workspace meta not found');
const gitRepository = await models.gitRepository.getById(
activeWorkspaceMeta.gitRepositoryId || '',
);
@@ -50,20 +57,28 @@ export const workspaceLoader: LoaderFunction = async ({
const activeEnvironment = subEnvironments.find(({ _id }) => activeWorkspaceMeta.activeEnvironmentId === _id) || baseEnvironment;
+ const activeCookieJar = await models.cookieJar.getOrCreateForParentId(workspaceId);
+ invariant(activeCookieJar, 'Cookie jar not found');
+
+ const activeApiSpec = await models.apiSpec.getByParentId(workspaceId);
+ const clientCertificates = await models.clientCertificate.findByParentId(workspaceId);
return {
activeWorkspace,
activeProject,
gitRepository,
activeWorkspaceMeta,
+ activeCookieJar,
activeEnvironment,
subEnvironments,
baseEnvironment,
+ activeApiSpec,
+ clientCertificates,
};
};
const WorkspaceRoute = () => {
const workspaceData = useLoaderData() as WorkspaceLoaderData;
- const branch = workspaceData.activeWorkspaceMeta?.cachedGitRepositoryBranch;
+ const branch = workspaceData.activeWorkspaceMeta.cachedGitRepositoryBranch;
return ;
};