Skip to content

Commit

Permalink
fix(project-data): Move remote file fetching outside the main path (#…
Browse files Browse the repository at this point in the history
…7378)

* move project fetches outside the projectId loader

* update types

* use a separate loader for listing files

* feedback

* close the palette when the env changes

* cleanup and comments

* clean up naming

* filter our mock server as move target

---------

Co-authored-by: jackkav <jackkav@gmail.com>
  • Loading branch information
gatzjames and jackkav authored May 7, 2024
1 parent 5f08604 commit 74fdf4e
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 222 deletions.
345 changes: 180 additions & 165 deletions packages/insomnia/src/ui/components/command-palette.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isNotNullOrUndefined } from '../../../common/misc';
import type { RequestGroup } from '../../../models/request-group';
import { invariant } from '../../../utils/invariant';
import { useRequestGroupPatcher } from '../../hooks/use-request';
import { ProjectLoaderData } from '../../routes/project';
import { ListWorkspacesLoaderData } from '../../routes/project';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
import { ModalHeader } from '../base/modal-header';
Expand All @@ -27,15 +27,15 @@ export const RequestGroupSettingsModal = ({ requestGroup, onHide }: ModalProps &
const modalRef = useRef<ModalHandle>(null);
const editorRef = useRef<CodeEditorHandle>(null);
const { organizationId, projectId, workspaceId } = useParams() as { organizationId: string; projectId: string; workspaceId: string };
const workspacesFetcher = useFetcher();
const workspacesFetcher = useFetcher<ListWorkspacesLoaderData>();
useEffect(() => {
const isIdleAndUninitialized = workspacesFetcher.state === 'idle' && !workspacesFetcher.data;
if (isIdleAndUninitialized) {
workspacesFetcher.load(`/organization/${organizationId}/project/${projectId}`);
workspacesFetcher.load(`/organization/${organizationId}/project/${projectId}/list-workspaces`);
}
}, [organizationId, projectId, workspacesFetcher]);
const projectLoaderData = workspacesFetcher?.data as ProjectLoaderData;
const workspacesForActiveProject = projectLoaderData?.files.map(w => w.workspace).filter(isNotNullOrUndefined) || [];
const projectLoaderData = workspacesFetcher?.data;
const workspacesForActiveProject = projectLoaderData?.files.map(w => w.workspace).filter(isNotNullOrUndefined).filter(w => w.scope !== 'mock-server') || [];
const [state, setState] = useState<State>({
activeWorkspaceIdToCopyTo: '',
defaultPreviewMode: !!requestGroup.description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { isRequest, Request } from '../../../models/request';
import { isWebSocketRequest, WebSocketRequest } from '../../../models/websocket-request';
import { invariant } from '../../../utils/invariant';
import { useRequestPatcher } from '../../hooks/use-request';
import { ProjectLoaderData } from '../../routes/project';
import { ListWorkspacesLoaderData } from '../../routes/project';
import { Modal, type ModalHandle, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
import { ModalHeader } from '../base/modal-header';
Expand All @@ -31,15 +31,15 @@ export const RequestSettingsModal = ({ request, onHide }: ModalProps & RequestSe
const modalRef = useRef<ModalHandle>(null);
const editorRef = useRef<CodeEditorHandle>(null);
const { organizationId, projectId, workspaceId } = useParams() as { organizationId: string; projectId: string; workspaceId: string };
const workspacesFetcher = useFetcher();
const workspacesFetcher = useFetcher<ListWorkspacesLoaderData>();
useEffect(() => {
const isIdleAndUninitialized = workspacesFetcher.state === 'idle' && !workspacesFetcher.data;
if (isIdleAndUninitialized && !isScratchpadOrganizationId(organizationId)) {
workspacesFetcher.load(`/organization/${organizationId}/project/${projectId}`);
workspacesFetcher.load(`/organization/${organizationId}/project/${projectId}/list-workspaces`);
}
}, [organizationId, projectId, workspacesFetcher]);
const projectLoaderData = workspacesFetcher?.data as ProjectLoaderData;
const workspacesForActiveProject = projectLoaderData?.files.map(w => w.workspace).filter(isNotNullOrUndefined) || [];
const projectLoaderData = workspacesFetcher?.data;
const workspacesForActiveProject = projectLoaderData?.files.map(w => w.workspace).filter(isNotNullOrUndefined).filter(w => w.scope !== 'mock-server') || [];
const [state, setState] = useState<State>({
defaultPreviewMode: !!request?.description,
activeWorkspaceIdToCopyTo: '',
Expand Down
4 changes: 2 additions & 2 deletions packages/insomnia/src/ui/components/present-users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { useParams, useRouteLoaderData } from 'react-router-dom';

import { useInsomniaEventStreamContext } from '../context/app/insomnia-event-stream-context';
import { ProjectLoaderData } from '../routes/project';
import { ProjectIdLoaderData } from '../routes/project';
import { useRootLoaderData } from '../routes/root';
import { WorkspaceLoaderData } from '../routes/workspace';
import { AvatarGroup } from './avatar';
Expand All @@ -11,7 +11,7 @@ export const PresentUsers = () => {
const { presence } = useInsomniaEventStreamContext();
const { workspaceId } = useParams() as { workspaceId: string };
const { userSession } = useRootLoaderData();
const projectData = useRouteLoaderData('/project/:projectId') as ProjectLoaderData | null;
const projectData = useRouteLoaderData('/project/:projectId') as ProjectIdLoaderData | null;
const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | null;
const remoteId = projectData?.activeProject?.remoteId || workspaceData?.activeProject.remoteId;

Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/ui/components/request-url-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(({
const [currentInterval, setCurrentInterval] = useState<number | null>(null);
const [currentTimeout, setCurrentTimeout] = useState<number | undefined>(undefined);
const fetcher = useFetcher();
// TODO: unpick this loading hack
// TODO: unpick this loading hack. This could be simplified if submit provides a way to update state when it finishes. https://github.com/remix-run/remix/discussions/9020
useEffect(() => {
if (fetcher.state !== 'idle') {
setLoading(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Project } from '../../../models/project';
import { isScratchpad, Workspace } from '../../../models/workspace';
import { SegmentEvent } from '../../analytics';
import { useOrganizationLoaderData } from '../../routes/organization';
import { ProjectLoaderData } from '../../routes/project';
import { ListWorkspacesLoaderData } from '../../routes/project';
import { useRootLoaderData } from '../../routes/root';
import { UntrackedProjectsLoaderData } from '../../routes/untracked-projects';
import { WorkspaceLoaderData } from '../../routes/workspace';
Expand Down Expand Up @@ -251,14 +251,14 @@ export const ImportExport: FC<Props> = ({ hideSettingsModal }) => {
const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | undefined;
const activeWorkspaceName = workspaceData?.activeWorkspace.name;
const { workspaceCount, userSession } = useRootLoaderData();
const workspacesFetcher = useFetcher();
const workspacesFetcher = useFetcher<ListWorkspacesLoaderData>();
useEffect(() => {
const isIdleAndUninitialized = workspacesFetcher.state === 'idle' && !workspacesFetcher.data;
if (isIdleAndUninitialized && organizationId && !isScratchpadOrganizationId(organizationId)) {
workspacesFetcher.load(`/organization/${organizationId}/project/${projectId}`);
workspacesFetcher.load(`/organization/${organizationId}/project/${projectId}/list-workspaces`);
}
}, [organizationId, projectId, workspacesFetcher]);
const projectLoaderData = workspacesFetcher?.data as ProjectLoaderData | undefined;
const projectLoaderData = workspacesFetcher?.data;
const workspacesForActiveProject = projectLoaderData?.files.map(w => w.workspace).filter(isNotNullOrUndefined) || [];
const projectName = projectLoaderData?.activeProject?.name ?? getProductName();
const projects = projectLoaderData?.projects || [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { createContext, FC, PropsWithChildren, useContext, useEffect, use
import { useFetcher, useParams, useRevalidator, useRouteLoaderData } from 'react-router-dom';

import { insomniaFetch } from '../../../ui/insomniaFetch';
import { ProjectLoaderData } from '../../routes/project';
import { ProjectIdLoaderData } from '../../routes/project';
import { useRootLoaderData } from '../../routes/root';
import { WorkspaceLoaderData } from '../../routes/workspace';

Expand Down Expand Up @@ -77,7 +77,7 @@ export const InsomniaEventStreamProvider: FC<PropsWithChildren> = ({ children })
};

const { userSession } = useRootLoaderData();
const projectData = useRouteLoaderData('/project/:projectId') as ProjectLoaderData | null;
const projectData = useRouteLoaderData('/project/:projectId') as ProjectIdLoaderData | null;
const workspaceData = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData | null;
const remoteId = projectData?.activeProject?.remoteId || workspaceData?.activeProject.remoteId;

Expand Down
24 changes: 18 additions & 6 deletions packages/insomnia/src/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,25 @@ async function renderApp() {
path: ':projectId',
id: '/project/:projectId',
loader: async (...args) =>
(await import('./routes/project')).loader(...args),
element: (
<Suspense fallback={<AppLoadingIndicator />}>
<Project />
</Suspense>
),
(await import('./routes/project')).projectIdLoader(...args),
children: [
{
index: true,
loader: async (...args) =>
(await import('./routes/project')).loader(...args),
element: (
<Suspense fallback={<AppLoadingIndicator />}>
<Project />
</Suspense>
),
},
{
path: 'list-workspaces',
loader: async (...args) =>
(
await import('./routes/project')
).listWorkspacesLoader(...args),
},
{
path: 'delete',
action: async (...args) =>
Expand Down
99 changes: 67 additions & 32 deletions packages/insomnia/src/ui/routes/project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
useLoaderData,
useNavigate,
useParams,
useSearchParams,
} from 'react-router-dom';
import { useLocalStorage } from 'react-use';

Expand Down Expand Up @@ -288,6 +287,10 @@ export interface InsomniaFile {
apiSpec?: ApiSpec;
}

export interface ProjectIdLoaderData {
activeProject?: Project;
}

export interface ProjectLoaderData {
files: InsomniaFile[];
allFilesCount: number;
Expand All @@ -305,6 +308,7 @@ export interface ProjectLoaderData {
url: string;
};
}

async function getAllLocalFiles({
projectId,
}: {
Expand Down Expand Up @@ -448,9 +452,47 @@ async function getAllRemoteFiles({
return [];
}

export interface ListWorkspacesLoaderData {
files: InsomniaFile[];
activeProject?: Project;
projects: Project[];
}

export const listWorkspacesLoader: LoaderFunction = async ({ params }): Promise<ListWorkspacesLoaderData> => {
const { organizationId, projectId } = params;
invariant(organizationId, 'Organization ID is required');
invariant(projectId, 'Project ID is required');

const project = await models.project.getById(projectId);
invariant(project, `Project was not found ${projectId}`);
const organizationProjects = await database.find<Project>(models.project.type, {
parentId: organizationId,
}) || [];

const projects = sortProjects(organizationProjects);
const files = await getAllLocalFiles({ projectId });

return {
files,
activeProject: project,
projects,
};
};

export const projectIdLoader: LoaderFunction = async ({ params }): Promise<ProjectIdLoaderData> => {
const { projectId } = params;
invariant(projectId, 'Project ID is required');

const project = await models.project.getById(projectId);
invariant(project, `Project was not found ${projectId}`);

return {
activeProject: project,
};
};

export const loader: LoaderFunction = async ({
params,
request,
}): Promise<ProjectLoaderData> => {
const { organizationId, projectId } = params;
invariant(organizationId, 'Organization ID is required');
Expand Down Expand Up @@ -480,9 +522,8 @@ export const loader: LoaderFunction = async ({
await logout();
throw redirect('/auth/login');
}
const search = new URL(request.url).searchParams;

invariant(projectId, 'projectId parameter is required');
const projectName = search.get('projectName') || '';

const project = await models.project.getById(projectId);
invariant(project, `Project was not found ${projectId}`);
Expand All @@ -495,9 +536,7 @@ export const loader: LoaderFunction = async ({
parentId: organizationId,
}) || [];

const projects = sortProjects(organizationProjects).filter(p =>
p.name?.toLowerCase().includes(projectName.toLowerCase())
);
const projects = sortProjects(organizationProjects);

let learningFeature = fallbackLearningFeature;
const lastFetchedString = window.localStorage.getItem('learning-feature-last-fetch');
Expand Down Expand Up @@ -588,22 +627,23 @@ const ProjectRoute: FC = () => {
},
storage: 'cloud_plus_local',
};
const [scope, setScope] = useLocalStorage(`${projectId}:project-dashboard-scope`, 'all');
const [sortOrder, setSortOrder] = useLocalStorage(`${projectId}:project-dashboard-sort-order`, 'modified-desc');
const [filter, setFilter] = useLocalStorage(`${projectId}:project-dashboard-filter`, '');
const [projectListFilter, setProjectListFilter] = useLocalStorage(`${organizationId}:project-list-filter`, '');
const [workspaceListFilter, setWorkspaceListFilter] = useLocalStorage(`${projectId}:workspace-list-filter`, '');
const [workspaceListScope, setWorkspaceListScope] = useLocalStorage(`${projectId}:workspace-list-scope`, 'all');
const [workspaceListSortOrder, setWorkspaceListSortOrder] = useLocalStorage(`${projectId}:workspace-list-sort-order`, 'modified-desc');
const [importModalType, setImportModalType] = useState<'file' | 'clipboard' | 'uri' | null>(null);
const [isNewProjectModalOpen, setIsNewProjectModalOpen] = useState(false);
const organization = organizations.find(o => o.id === organizationId);
const isUserOwner = organization && userSession.accountId && isOwnerOfOrganization({ organization, accountId: userSession.accountId });
const isPersonalOrg = organization && isPersonalOrganization(organization);

const filteredFiles = files
.filter(w => (scope !== 'all' ? w.scope === scope : true))
.filter(w => (workspaceListScope !== 'all' ? w.scope === workspaceListScope : true))
.filter(workspace =>
filter
workspaceListFilter
? Boolean(
fuzzyMatchAll(
filter,
workspaceListFilter,
// Use the filter string to match against these properties
[
workspace.name,
Expand All @@ -618,7 +658,7 @@ const ProjectRoute: FC = () => {
)
: true
)
.sort((a, b) => sortMethodMap[sortOrder as DashboardSortOrder](a, b));
.sort((a, b) => sortMethodMap[workspaceListSortOrder as DashboardSortOrder](a, b));

const filesWithPresence = filteredFiles.map(file => {
const workspacePresence = presence
Expand All @@ -638,7 +678,9 @@ const ProjectRoute: FC = () => {
};
});

const projectsWithPresence = projects.map(project => {
const projectsWithPresence = projects.filter(p =>
projectListFilter ? p.name?.toLowerCase().includes(projectListFilter.toLowerCase()) : true
).map(project => {
const projectPresence = presence
.filter(p => p.project === project.remoteId)
.filter(p => p.acct !== userSession.accountId)
Expand All @@ -655,7 +697,6 @@ const ProjectRoute: FC = () => {
};
});

const [searchParams, setSearchParams] = useSearchParams();
const [isGitRepositoryCloneModalOpen, setIsGitRepositoryCloneModalOpen] =
useState(false);
const [isMockServerSettingsModalOpen, setIsMockServerSettingsModalOpen] = useState(false);
Expand Down Expand Up @@ -934,13 +975,8 @@ const ProjectRoute: FC = () => {
<SearchField
aria-label="Projects filter"
className="group relative flex-1"
defaultValue={searchParams.get('filter')?.toString() ?? ''}
onChange={projectName => {
setSearchParams({
...Object.fromEntries(searchParams.entries()),
projectName,
});
}}
value={projectListFilter}
onChange={setProjectListFilter}
>
<Input
placeholder="Filter"
Expand Down Expand Up @@ -973,7 +1009,6 @@ const ProjectRoute: FC = () => {
const value = keys.values().next().value;
navigate({
pathname: `/organization/${organizationId}/project/${value}`,
search: searchParams.toString(),
});
}
}}
Expand Down Expand Up @@ -1013,13 +1048,13 @@ const ProjectRoute: FC = () => {
items={scopeActionList}
className="overflow-y-auto flex-shrink-0 flex-1 data-[empty]:py-0 py-[--padding-sm]"
disallowEmptySelection
selectedKeys={[scope || 'all']}
selectedKeys={[workspaceListScope || 'all']}
selectionMode="single"
onSelectionChange={keys => {
if (keys !== 'all') {
const value = keys.values().next().value;

setScope(value);
setWorkspaceListScope(value);
}
}}
>
Expand Down Expand Up @@ -1110,8 +1145,8 @@ const ProjectRoute: FC = () => {
<SearchField
aria-label="Files filter"
className="group relative flex-1"
value={filter}
onChange={filter => setFilter(filter)}
value={workspaceListFilter}
onChange={filter => setWorkspaceListFilter(filter)}
>
<Input
placeholder="Filter"
Expand All @@ -1126,8 +1161,8 @@ const ProjectRoute: FC = () => {
<Select
aria-label="Sort order"
className="h-full aspect-square"
selectedKey={sortOrder}
onSelectionChange={order => setSortOrder(order as DashboardSortOrder)}
selectedKey={workspaceListSortOrder}
onSelectionChange={order => setWorkspaceListSortOrder(order as DashboardSortOrder)}
>
<Button
aria-label="Select sort order"
Expand Down Expand Up @@ -1235,11 +1270,11 @@ const ProjectRoute: FC = () => {
);
}}
renderEmptyState={() => {
if (filter) {
if (workspaceListFilter) {
return (
<div className="w-full h-full flex items-center justify-center">
<p className="notice subtle">
No documents found for <strong>{filter}</strong>
No documents found for <strong>{workspaceListFilter}</strong>
</p>
</div>
);
Expand Down

0 comments on commit 74fdf4e

Please sign in to comment.