Skip to content

Commit 9e2172a

Browse files
yaoweiprccwangsmvCopilot
authored andcommitted
Fix/konnect project filter [INS-2805] (#10098)
* Fix the bug that filter does not work under Konnect tab * fix: ensure active filter handles undefined konnectFilter correctly * fix: improve konnect filter handling and debounce logic * fix unsynced workspace filter * fix: remove proxy defaults check in upsertProjectEnvVars function * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Kent Wang <kent.wang@konghq.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> (cherry picked from commit f7792fd)
1 parent f36547f commit 9e2172a

1 file changed

Lines changed: 58 additions & 29 deletions

File tree

packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/project-navigation-sidebar.tsx

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,31 @@ const ProjectNavigationSidebarInner = (
245245
const nonKonnectProjects = projects.filter(p => !p.konnectControlPlaneId);
246246
const konnectProjects = projects.filter(p => p.konnectControlPlaneId != null);
247247
const [filterInputValue, setFilterInputValue] = useState(projectNavigationSidebarFilter || '');
248+
const [konnectFilterInputValue, setKonnectFilterInputValue] = useState(konnectFilter || '');
249+
250+
useEffect(() => {
251+
// Keep input state aligned with storage only when organization context switches.
252+
// Read directly from localStorage to bypass react-use's stale state on key change.
253+
const readLocalStorageString = (key: string): string => {
254+
try {
255+
const raw = localStorage.getItem(key);
256+
if (raw == null) {
257+
return '';
258+
}
259+
const parsed: unknown = JSON.parse(raw);
260+
return typeof parsed === 'string' ? parsed : '';
261+
} catch {
262+
return '';
263+
}
264+
};
265+
setFilterInputValue(readLocalStorageString(`${organizationId}:project-navigation-sidebar-filter`));
266+
setKonnectFilterInputValue(readLocalStorageString(`${organizationId}:project-navigation-konnect-filter`));
267+
}, [organizationId]);
268+
248269
// Debounce update filter
249270
reactUse.useDebounce(() => setProjectNavigationSidebarFilter(filterInputValue), 300, [filterInputValue]);
271+
reactUse.useDebounce(() => setKonnectFilter(konnectFilterInputValue), 300, [konnectFilterInputValue]);
272+
const activeFilter = ((isProjectTabActive ? projectNavigationSidebarFilter : konnectFilter) || '').trim();
250273
// ref to cache queried workspaces by project id
251274
const cachedWorkspacesRef = useRef<Map<string, Workspace[]>>(new Map());
252275
// ref to cache queried collection children (request & requestGroups) data and meta by workspace id
@@ -444,12 +467,12 @@ const ProjectNavigationSidebarInner = (
444467
};
445468

446469
useEffect(() => {
447-
if (projectNavigationSidebarFilter) {
470+
if (projectNavigationSidebarFilter || konnectFilter) {
448471
window.main.trackAnalyticsEvent({
449472
event: AnalyticsEvent.projectListFiltered,
450473
});
451474
}
452-
}, [projectNavigationSidebarFilter]);
475+
}, [projectNavigationSidebarFilter, konnectFilter]);
453476

454477
useEffect(() => {
455478
getAllRemoteFilesByProjectId();
@@ -492,6 +515,7 @@ const ProjectNavigationSidebarInner = (
492515
const buildWorkspaceAndCollectionData = async () => {
493516
const items: FlatItem[] = [];
494517
// Array of project and collection workspace ids that should get data from db
518+
const activeFilterLower = activeFilter.toLowerCase();
495519

496520
const projectIds = projectsWithPresence.map(p => p._id);
497521
const collectionWorkspaceIds: string[] = [];
@@ -502,7 +526,7 @@ const ProjectNavigationSidebarInner = (
502526
if (
503527
wk.scope === 'collection' &&
504528
// Fetch collection children and meta if 1) the workspace is expanded or 2) there is an active filter
505-
(!!projectNavigationSidebarFilter || (expandedProjectAndWorkspaceIds || []).includes(wk._id))
529+
(!!activeFilter || (expandedProjectAndWorkspaceIds || []).includes(wk._id))
506530
) {
507531
collectionWorkspaceIds.push(wk._id);
508532
}
@@ -544,7 +568,7 @@ const ProjectNavigationSidebarInner = (
544568
: [];
545569
const allWorkspaces = [...sortedWorkspaces, ...unsyncedWorkspaces];
546570
// If there is no workspace under the project, show an empty workspace if no active filter
547-
if (allWorkspaces.length === 0 && !projectNavigationSidebarFilter) {
571+
if (allWorkspaces.length === 0 && !activeFilter) {
548572
items.push({
549573
kind: 'emptyProject',
550574
organizationId,
@@ -556,6 +580,15 @@ const ProjectNavigationSidebarInner = (
556580

557581
for (const workspace of allWorkspaces) {
558582
if (workspace.scope === 'unsynced') {
583+
// When a filter is active, show the unsynced workspace only if its name matches the filter.
584+
const unsyncedWorkspaceMatchesFilter =
585+
!activeFilter ||
586+
Boolean(
587+
fuzzyMatchAll(activeFilterLower, [workspace.name?.toLowerCase() || ''], {
588+
splitSpace: true,
589+
loose: true,
590+
})?.indexes,
591+
);
559592
items.push({
560593
kind: 'unsyncedWorkspace',
561594
organizationId,
@@ -566,7 +599,7 @@ const ProjectNavigationSidebarInner = (
566599
...workspace,
567600
},
568601
collapsed: false,
569-
hidden: isProjectCollapsed,
602+
hidden: activeFilter ? !unsyncedWorkspaceMatchesFilter : isProjectCollapsed,
570603
});
571604
} else {
572605
const { scope, _id: workspaceId } = workspace as Workspace;
@@ -589,7 +622,7 @@ const ProjectNavigationSidebarInner = (
589622
// build collection children if it's a collection workspace and parent workspace and project are not collapsed or there is an active filter
590623
const shouldHideCollectionChildren = isWorkspaceCollapsed || isProjectCollapsed;
591624
let collectionChildren =
592-
(!shouldHideCollectionChildren || !!projectNavigationSidebarFilter) && allRequestsAndMetaInWorkspace
625+
(!shouldHideCollectionChildren || !!activeFilter) && allRequestsAndMetaInWorkspace
593626
? flattenCollectionChildren(
594627
workspaceId,
595628
shouldHideCollectionChildren,
@@ -598,13 +631,13 @@ const ProjectNavigationSidebarInner = (
598631
)
599632
: [];
600633

601-
if (projectNavigationSidebarFilter) {
634+
if (activeFilter) {
602635
// apply filter to collection children first
603-
collectionChildren = filterCollection(collectionChildren, projectNavigationSidebarFilter);
636+
collectionChildren = filterCollection(collectionChildren, activeFilter);
604637
const collectionChildMatchesFilter = collectionChildren.some(child => !child.hidden);
605638
const workspaceMatchesFilter = Boolean(
606639
fuzzyMatchAll(
607-
projectNavigationSidebarFilter.toLowerCase(),
640+
activeFilterLower,
608641
// Todo: support remote files (cloud sync) in filter
609642
[workspace.name?.toLowerCase() || ''],
610643
{ splitSpace: true, loose: true },
@@ -618,9 +651,7 @@ const ProjectNavigationSidebarInner = (
618651
const pinnedCollectionChildren = shouldHideCollectionChildren
619652
? []
620653
: // Filter out pinned requests by pinned attribute. Besides, when there is an active filter, also filter out un-matched requests.
621-
collectionChildren.filter(
622-
child => child.pinned && !(projectNavigationSidebarFilter ? child.hidden : false),
623-
);
654+
collectionChildren.filter(child => child.pinned && !(activeFilter ? child.hidden : false));
624655

625656
if (pinnedCollectionChildren.length > 0) {
626657
items.push({
@@ -665,7 +696,7 @@ const ProjectNavigationSidebarInner = (
665696
if (
666697
models.requestGroup.isRequestGroupId(child.doc._id) &&
667698
child.children?.length === 0 &&
668-
!projectNavigationSidebarFilter
699+
!activeFilter
669700
) {
670701
// If there is a request group with no children, add an empty folder node
671702
items.push({
@@ -681,7 +712,7 @@ const ProjectNavigationSidebarInner = (
681712
}
682713
});
683714

684-
if (collectionChildren.length === 0 && !shouldHideCollectionChildren && !projectNavigationSidebarFilter) {
715+
if (collectionChildren.length === 0 && !shouldHideCollectionChildren && !activeFilter) {
685716
items.push({
686717
kind: 'emptyCollection',
687718
organizationId,
@@ -695,20 +726,18 @@ const ProjectNavigationSidebarInner = (
695726
}
696727

697728
// If project or any of its descendant workspace/collection child matches the filter, show the project; otherwise hide
698-
if (projectNavigationSidebarFilter) {
699-
const projectMatchesFilter = project.name
700-
?.toLowerCase()
701-
.includes(projectNavigationSidebarFilter.toLowerCase());
729+
if (activeFilter) {
730+
const projectMatchesFilter = project.name?.toLowerCase().includes(activeFilterLower);
702731
const hasVisibleWorkspace = items.some(
703-
i => i.kind === 'workspace' && i.project._id === projectId && !i.hidden,
732+
i => (i.kind === 'workspace' || i.kind === 'unsyncedWorkspace') && i.project._id === projectId && !i.hidden,
704733
);
705734
const shouldHideProject = !projectMatchesFilter && !hasVisibleWorkspace;
706735
items.find(i => i.kind === 'project' && i.doc._id === projectId)!.hidden = shouldHideProject;
707736
}
708737
}
709738

710739
// If there is an active filter, expand all items to show matched results and their ancestors
711-
if (projectNavigationSidebarFilter) {
740+
if (activeFilter) {
712741
items.forEach(item => {
713742
if ('collapsed' in item) {
714743
item.collapsed = false;
@@ -720,13 +749,13 @@ const ProjectNavigationSidebarInner = (
720749
};
721750
buildWorkspaceAndCollectionData();
722751
}, [
752+
activeFilter,
723753
collectionSortOrders,
724754
projectWorkspaceSortOrder,
725755
expandedProjectAndWorkspaceIds,
726756
isProjectTabActive,
727757
localWorkspaceOrders,
728758
organizationId,
729-
projectNavigationSidebarFilter,
730759
projectsWithPresence,
731760
unsyncedFilesByProjectId,
732761
]);
@@ -776,21 +805,21 @@ const ProjectNavigationSidebarInner = (
776805
const toggleProjectOrWorkspace = useCallback(
777806
(projectOrWorkspaceId: string) => {
778807
// Do not update toggle state if there is an active filter
779-
if (!projectNavigationSidebarFilter) {
808+
if (!activeFilter) {
780809
const expandedIds = expandedProjectAndWorkspaceIds || [];
781810
const isExpanded = expandedIds.includes(projectOrWorkspaceId);
782811
setExpandedProjectAndWorkspaceIds(
783812
isExpanded ? expandedIds.filter(id => id !== projectOrWorkspaceId) : [...expandedIds, projectOrWorkspaceId],
784813
);
785814
}
786815
},
787-
[expandedProjectAndWorkspaceIds, projectNavigationSidebarFilter, setExpandedProjectAndWorkspaceIds],
816+
[expandedProjectAndWorkspaceIds, activeFilter, setExpandedProjectAndWorkspaceIds],
788817
);
789818

790819
const expandProjectOrWorkspaces = useCallback(
791820
(projectOrWorkspaceIds: string[]) => {
792821
// Do not update toggle state if there is an active filter
793-
if (!projectNavigationSidebarFilter) {
822+
if (!activeFilter) {
794823
const expandedIds = expandedProjectAndWorkspaceIds || [];
795824
const newExpandedIds = Array.from(new Set([...expandedIds, ...projectOrWorkspaceIds]));
796825
// Avoid updating state if there is no change in expanded ids to prevent unnecessary re-render
@@ -800,7 +829,7 @@ const ProjectNavigationSidebarInner = (
800829
}
801830
}
802831
},
803-
[expandedProjectAndWorkspaceIds, projectNavigationSidebarFilter, setExpandedProjectAndWorkspaceIds],
832+
[expandedProjectAndWorkspaceIds, activeFilter, setExpandedProjectAndWorkspaceIds],
804833
);
805834

806835
useImperativeHandle(
@@ -819,7 +848,7 @@ const ProjectNavigationSidebarInner = (
819848
return;
820849
}
821850

822-
if (projectNavigationSidebarFilter) {
851+
if (activeFilter) {
823852
return;
824853
}
825854

@@ -926,7 +955,7 @@ const ProjectNavigationSidebarInner = (
926955
}, previousFlatItems),
927956
);
928957
},
929-
[projectNavigationSidebarFilter],
958+
[activeFilter],
930959
);
931960

932961
const parentRef = useRef<HTMLDivElement>(null);
@@ -996,9 +1025,9 @@ const ProjectNavigationSidebarInner = (
9961025
<>
9971026
<div className="flex justify-between gap-1 p-(--padding-sm)">
9981027
<SidebarSearchField
999-
value={isProjectTabActive ? filterInputValue : (konnectFilter ?? '')}
1028+
value={isProjectTabActive ? filterInputValue : konnectFilterInputValue}
10001029
isDisabled={projects.length === 0}
1001-
onChange={isProjectTabActive ? setFilterInputValue : setKonnectFilter}
1030+
onChange={isProjectTabActive ? setFilterInputValue : setKonnectFilterInputValue}
10021031
/>
10031032
{isProjectTabActive ? (
10041033
!isScratchPad && <NewProjectButton onPress={onCreateProject} isDisabled={projects.length === 0} />

0 commit comments

Comments
 (0)