Skip to content

Commit cbb634c

Browse files
committed
perf(sidebar): let the palette fetch its own lists, so closed routes never register them
1 parent 79ccbfc commit cbb634c

4 files changed

Lines changed: 95 additions & 90 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.test.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,25 @@ vi.mock('@/hooks/use-permission-config', () => ({
7272
}),
7373
}))
7474

75+
/**
76+
* The palette owns these reads now — it mounts only while open, so the queries exist only then.
77+
* `mockTables` lets a test drive the Tables section the way the `tables` prop used to.
78+
*/
79+
const mockTables = vi.hoisted(() => ({ current: [] as unknown[] }))
80+
81+
vi.mock('@/hooks/queries/tables', () => ({
82+
useTablesList: () => ({ data: mockTables.current }),
83+
}))
84+
vi.mock('@/hooks/queries/workspace-files', () => ({
85+
useWorkspaceFiles: () => ({ data: [] }),
86+
}))
87+
vi.mock('@/hooks/queries/kb/knowledge', () => ({
88+
useKnowledgeBasesQuery: () => ({ data: [] }),
89+
}))
90+
vi.mock('@/hooks/queries/folders', () => ({
91+
useFolderMap: () => ({ data: {} }),
92+
}))
93+
7594
vi.mock('@/hooks/use-settings-navigation', () => ({
7695
useSettingsNavigation: () => ({ navigateToSettings: vi.fn() }),
7796
}))
@@ -518,11 +537,9 @@ describe('SearchModal', () => {
518537
})
519538

520539
it('hoists a module page’s actions and its entity section directly under the Sim group', async () => {
521-
const tables = [{ id: 'table-1', name: 'Leads', href: '/workspace/workspace-1/tables/table-1' }]
540+
mockTables.current = [{ id: 'table-1', name: 'Leads', folderId: null }]
522541
await act(async () => {
523-
root.render(
524-
<SearchModal open onOpenChange={vi.fn()} pageContext='tables' canEdit tables={tables} />
525-
)
542+
root.render(<SearchModal open onOpenChange={vi.fn()} pageContext='tables' canEdit />)
526543
})
527544

528545
const headings = Array.from(document.querySelectorAll<HTMLElement>('[cmdk-group-heading]')).map(

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ import { createPortal } from 'react-dom'
4343
import { supportsAtomicBrowserPanelOcclusion } from '@/lib/browser-agent/transport'
4444
import { isChatEnabled } from '@/lib/core/config/env-flags'
4545
import { MothershipHandoffStorage } from '@/lib/core/utils/browser-storage'
46+
import { getFolderPathNames } from '@/lib/folders/tree'
4647
import { sendMothershipMessage } from '@/lib/mothership/events'
4748
import { captureEvent } from '@/lib/posthog/client'
4849
import { toSearchToken } from '@/lib/search/tokens'
4950
import { hasTriggerCapability } from '@/lib/workflows/triggers/trigger-utils'
51+
import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folder-display-path'
5052
import { useInvokeGlobalCommand } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
5153
import {
5254
CommandFadedList,
@@ -86,8 +88,13 @@ import {
8688
CMDK_SECTION_GAP_CLASS,
8789
} from '@/app/workspace/[workspaceId]/w/components/sidebar/constants'
8890
import { SIDEBAR_SCROLL_EVENT } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar'
91+
import { useFolderMap } from '@/hooks/queries/folders'
92+
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
93+
import { useTablesList } from '@/hooks/queries/tables'
94+
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
8995
import { usePermissionConfig } from '@/hooks/use-permission-config'
9096
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
97+
import type { WorkflowFolder } from '@/stores/folders/types'
9198
import { useSearchModalStore } from '@/stores/modals/search/store'
9299
import type { SearchBlockItem, SearchToolOperationItem } from '@/stores/modals/search/types'
93100

@@ -101,6 +108,9 @@ const logger = createLogger('SearchModal')
101108
export const MAX_BROWSE_RESULTS = Number.POSITIVE_INFINITY
102109
const MAX_SEARCH_RESULTS = 50
103110

111+
/** Stable empty default so a pending folder map does not remount the memos below. */
112+
const EMPTY_FOLDER_MAP: Record<string, WorkflowFolder> = {}
113+
104114
export type { SearchModalProps } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils'
105115

106116
type SearchModalContentProps = Omit<SearchModalProps, 'open'>
@@ -121,9 +131,6 @@ function SearchModalContent({
121131
workflows = [],
122132
workspaces = [],
123133
chats = [],
124-
tables = [],
125-
files = [],
126-
knowledgeBases = [],
127134
logs = [],
128135
integrations = [],
129136
connectedAccounts = [],
@@ -157,6 +164,64 @@ function SearchModalContent({
157164

158165
const { blocks, tools, triggers, toolOperations } = useSearchModalStore((state) => state.data)
159166

167+
/**
168+
* Read here rather than passed down from the sidebar. This component mounts only while the
169+
* palette is open, so these workspace-wide lists are fetched — and registered in the query
170+
* cache — only then. The sidebar renders on every workspace route, so holding them there put
171+
* three lists and two folder maps in the cache on routes that never display them, and a
172+
* registered key also blocks a page from seeding it during the server render.
173+
*/
174+
const { data: fetchedTables = [] } = useTablesList(workspaceId, 'active', {
175+
enabled: !permissionConfig.hideTablesTab,
176+
})
177+
const { data: fetchedFiles = [] } = useWorkspaceFiles(workspaceId, 'active', {
178+
enabled: !permissionConfig.hideFilesTab,
179+
})
180+
const { data: fetchedKnowledgeBases = [] } = useKnowledgeBasesQuery(workspaceId, {
181+
enabled: !permissionConfig.hideKnowledgeBaseTab,
182+
})
183+
const { data: tableFolderMap = EMPTY_FOLDER_MAP } = useFolderMap(
184+
permissionConfig.hideTablesTab ? undefined : workspaceId,
185+
'table'
186+
)
187+
const { data: knowledgeBaseFolderMap = EMPTY_FOLDER_MAP } = useFolderMap(
188+
permissionConfig.hideKnowledgeBaseTab ? undefined : workspaceId,
189+
'knowledge_base'
190+
)
191+
192+
const tables = useMemo(
193+
() =>
194+
fetchedTables.map((t) => ({
195+
id: t.id,
196+
name: t.name,
197+
href: `/workspace/${workspaceId}/tables/${t.id}`,
198+
folderPath: getFolderPathNames(tableFolderMap, t.folderId),
199+
})),
200+
[fetchedTables, tableFolderMap, workspaceId]
201+
)
202+
203+
const files = useMemo(
204+
() =>
205+
fetchedFiles.map((f) => ({
206+
id: f.id,
207+
name: f.name,
208+
href: `/workspace/${workspaceId}/files/${f.id}`,
209+
folderPath: f.folderPath ? parseWorkspaceFileFolderDisplayPath(f.folderPath) : undefined,
210+
})),
211+
[fetchedFiles, workspaceId]
212+
)
213+
214+
const knowledgeBases = useMemo(
215+
() =>
216+
fetchedKnowledgeBases.map((kb) => ({
217+
id: kb.id,
218+
name: kb.name,
219+
href: `/workspace/${workspaceId}/knowledge/${kb.id}`,
220+
folderPath: getFolderPathNames(knowledgeBaseFolderMap, kb.folderId),
221+
})),
222+
[fetchedKnowledgeBases, knowledgeBaseFolderMap, workspaceId]
223+
)
224+
160225
const openHelpModal = useCallback(() => {
161226
window.dispatchEvent(new CustomEvent('open-help-modal'))
162227
}, [])

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,12 @@ export interface SearchModalProps {
166166
workflows?: WorkflowItem[]
167167
workspaces?: WorkspaceItem[]
168168
chats?: TaskItem[]
169-
tables?: FolderedItem[]
170-
files?: FileItem[]
171-
knowledgeBases?: FolderedItem[]
169+
/**
170+
* Tables, files, and knowledge bases are NOT passed in: the content component reads them
171+
* itself, so those three workspace-wide lists are queried only while the palette is open.
172+
* The sidebar renders on every workspace route, so fetching them there registered the keys
173+
* in the cache on routes that never show them.
174+
*/
172175
logs?: LogItem[]
173176
integrations?: IntegrationSearchItem[]
174177
connectedAccounts?: IntegrationSearchItem[]

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import { isChatEnabled } from '@/lib/core/config/env-flags'
4343
import { isMacPlatform } from '@/lib/core/utils/platform'
4444
import { buildFolderTree, getFolderPathNames } from '@/lib/folders/tree'
4545
import { captureEvent } from '@/lib/posthog/client'
46-
import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folder-display-path'
4746
import { CONNECT_MODE } from '@/app/workspace/[workspaceId]/integrations/connect-route'
4847
import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
4948
import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider'
@@ -100,7 +99,6 @@ import { useImportWorkflow } from '@/app/workspace/[workspaceId]/w/hooks'
10099
import { useCustomBlockOverlayVersion } from '@/blocks/custom/client-overlay'
101100
import { useWorkspaceCredentials } from '@/hooks/queries/credentials'
102101
import { useFolderMap, useFolders } from '@/hooks/queries/folders'
103-
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'
104102
import { type LogFilters, useLogsList } from '@/hooks/queries/logs'
105103
import type { MothershipChatMetadata } from '@/hooks/queries/mothership-chats'
106104
import {
@@ -112,10 +110,8 @@ import {
112110
useRenameMothershipChat,
113111
useSetMothershipChatPinned,
114112
} from '@/hooks/queries/mothership-chats'
115-
import { useTablesList } from '@/hooks/queries/tables'
116113
import { useUpdateWorkflow } from '@/hooks/queries/workflows'
117114
import type { Workspace } from '@/hooks/queries/workspace'
118-
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
119115
import { useMothershipChatEvents } from '@/hooks/use-mothership-chat-events'
120116
import { usePermissionConfig } from '@/hooks/use-permission-config'
121117
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
@@ -600,17 +596,6 @@ export const Sidebar = memo(function Sidebar({
600596

601597
useFolders(workspaceId)
602598
const { data: folderMap = EMPTY_FOLDER_MAP } = useFolderMap(workspaceId)
603-
// Tables and knowledge bases keep their folders in the generic folder tree,
604-
// keyed by resource type, so each needs its own map to resolve a path. Both resolve
605-
// paths for search-modal rows only, so they wait for the palette like its lists do.
606-
const { data: tableFolderMap = EMPTY_FOLDER_MAP } = useFolderMap(
607-
isSearchModalOpen && !permissionConfig.hideTablesTab ? workspaceId : undefined,
608-
'table'
609-
)
610-
const { data: knowledgeBaseFolderMap = EMPTY_FOLDER_MAP } = useFolderMap(
611-
isSearchModalOpen && !permissionConfig.hideKnowledgeBaseTab ? workspaceId : undefined,
612-
'knowledge_base'
613-
)
614599
const updateWorkflowMutation = useUpdateWorkflow()
615600

616601
const folderTree = useMemo(
@@ -904,68 +889,6 @@ export const Sidebar = memo(function Sidebar({
904889
[fetchedChats, workspaceId]
905890
)
906891

907-
/**
908-
* Search-modal data only. The sidebar renders on every workspace route, so fetching
909-
* these workspace-wide lists eagerly cost three requests per cold load to populate a
910-
* palette most sessions never open. React Query keeps what it has once fetched, so
911-
* reopening the palette is served from cache.
912-
*/
913-
const { data: fetchedTables = [] } = useTablesList(workspaceId, 'active', {
914-
enabled: isSearchModalOpen && !permissionConfig.hideTablesTab,
915-
})
916-
const { data: fetchedFiles = [] } = useWorkspaceFiles(workspaceId, 'active', {
917-
enabled: isSearchModalOpen && !permissionConfig.hideFilesTab,
918-
})
919-
const { data: fetchedKnowledgeBases = [] } = useKnowledgeBasesQuery(workspaceId, {
920-
enabled: isSearchModalOpen && !permissionConfig.hideKnowledgeBaseTab,
921-
})
922-
923-
const searchModalTables = useMemo(
924-
() =>
925-
permissionConfig.hideTablesTab
926-
? []
927-
: fetchedTables.map((t) => ({
928-
id: t.id,
929-
name: t.name,
930-
href: `/workspace/${workspaceId}/tables/${t.id}`,
931-
folderPath: getFolderPathNames(tableFolderMap, t.folderId),
932-
})),
933-
[fetchedTables, tableFolderMap, workspaceId, permissionConfig.hideTablesTab]
934-
)
935-
936-
const searchModalFiles = useMemo(
937-
() =>
938-
permissionConfig.hideFilesTab
939-
? []
940-
: fetchedFiles.map((f) => ({
941-
id: f.id,
942-
name: f.name,
943-
href: `/workspace/${workspaceId}/files/${f.id}`,
944-
folderPath: f.folderPath
945-
? parseWorkspaceFileFolderDisplayPath(f.folderPath)
946-
: undefined,
947-
})),
948-
[fetchedFiles, workspaceId, permissionConfig.hideFilesTab]
949-
)
950-
951-
const searchModalKnowledgeBases = useMemo(
952-
() =>
953-
permissionConfig.hideKnowledgeBaseTab
954-
? []
955-
: fetchedKnowledgeBases.map((kb) => ({
956-
id: kb.id,
957-
name: kb.name,
958-
href: `/workspace/${workspaceId}/knowledge/${kb.id}`,
959-
folderPath: getFolderPathNames(knowledgeBaseFolderMap, kb.folderId),
960-
})),
961-
[
962-
fetchedKnowledgeBases,
963-
knowledgeBaseFolderMap,
964-
workspaceId,
965-
permissionConfig.hideKnowledgeBaseTab,
966-
]
967-
)
968-
969892
const chatIds = useMemo(() => chats.map((t) => t.id), [chats])
970893

971894
const { selectedChats, handleChatClick } = useChatSelection({ chatIds })
@@ -1941,9 +1864,6 @@ export const Sidebar = memo(function Sidebar({
19411864
workflows={searchModalWorkflows}
19421865
workspaces={searchModalWorkspaces}
19431866
chats={chats}
1944-
tables={searchModalTables}
1945-
files={searchModalFiles}
1946-
knowledgeBases={searchModalKnowledgeBases}
19471867
logs={searchModalLogs}
19481868
integrations={searchModalIntegrations}
19491869
connectedAccounts={searchModalConnectedAccounts}

0 commit comments

Comments
 (0)