Skip to content

Commit 72bc2a4

Browse files
committed
perf(sidebar): let the palette fetch its own lists, so closed routes never register them
1 parent 245df29 commit 72bc2a4

4 files changed

Lines changed: 114 additions & 77 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: 87 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,83 @@ 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+
/**
193+
* The hidden-tab checks are repeated here, not left to `enabled`. A disabled query still
194+
* returns whatever is already cached — another surface may have filled it, or the permission
195+
* config may have flipped after it did — so gating only the fetch would let the palette list
196+
* entities a permission group hides.
197+
*/
198+
const tables = useMemo(
199+
() =>
200+
permissionConfig.hideTablesTab
201+
? []
202+
: fetchedTables.map((t) => ({
203+
id: t.id,
204+
name: t.name,
205+
href: `/workspace/${workspaceId}/tables/${t.id}`,
206+
folderPath: getFolderPathNames(tableFolderMap, t.folderId),
207+
})),
208+
[fetchedTables, tableFolderMap, workspaceId, permissionConfig.hideTablesTab]
209+
)
210+
211+
const files = useMemo(
212+
() =>
213+
permissionConfig.hideFilesTab
214+
? []
215+
: fetchedFiles.map((f) => ({
216+
id: f.id,
217+
name: f.name,
218+
href: `/workspace/${workspaceId}/files/${f.id}`,
219+
folderPath: f.folderPath
220+
? parseWorkspaceFileFolderDisplayPath(f.folderPath)
221+
: undefined,
222+
})),
223+
[fetchedFiles, workspaceId, permissionConfig.hideFilesTab]
224+
)
225+
226+
const knowledgeBases = useMemo(
227+
() =>
228+
permissionConfig.hideKnowledgeBaseTab
229+
? []
230+
: fetchedKnowledgeBases.map((kb) => ({
231+
id: kb.id,
232+
name: kb.name,
233+
href: `/workspace/${workspaceId}/knowledge/${kb.id}`,
234+
folderPath: getFolderPathNames(knowledgeBaseFolderMap, kb.folderId),
235+
})),
236+
[
237+
fetchedKnowledgeBases,
238+
knowledgeBaseFolderMap,
239+
workspaceId,
240+
permissionConfig.hideKnowledgeBaseTab,
241+
]
242+
)
243+
160244
const openHelpModal = useCallback(() => {
161245
window.dispatchEvent(new CustomEvent('open-help-modal'))
162246
}, [])

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 & 67 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,16 +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.
605-
const { data: tableFolderMap = EMPTY_FOLDER_MAP } = useFolderMap(
606-
permissionConfig.hideTablesTab ? undefined : workspaceId,
607-
'table'
608-
)
609-
const { data: knowledgeBaseFolderMap = EMPTY_FOLDER_MAP } = useFolderMap(
610-
permissionConfig.hideKnowledgeBaseTab ? undefined : workspaceId,
611-
'knowledge_base'
612-
)
613599
const updateWorkflowMutation = useUpdateWorkflow()
614600

615601
const folderTree = useMemo(
@@ -903,56 +889,6 @@ export const Sidebar = memo(function Sidebar({
903889
[fetchedChats, workspaceId]
904890
)
905891

906-
const { data: fetchedTables = [] } = useTablesList(workspaceId)
907-
const { data: fetchedFiles = [] } = useWorkspaceFiles(workspaceId)
908-
const { data: fetchedKnowledgeBases = [] } = useKnowledgeBasesQuery(workspaceId)
909-
910-
const searchModalTables = useMemo(
911-
() =>
912-
permissionConfig.hideTablesTab
913-
? []
914-
: fetchedTables.map((t) => ({
915-
id: t.id,
916-
name: t.name,
917-
href: `/workspace/${workspaceId}/tables/${t.id}`,
918-
folderPath: getFolderPathNames(tableFolderMap, t.folderId),
919-
})),
920-
[fetchedTables, tableFolderMap, workspaceId, permissionConfig.hideTablesTab]
921-
)
922-
923-
const searchModalFiles = useMemo(
924-
() =>
925-
permissionConfig.hideFilesTab
926-
? []
927-
: fetchedFiles.map((f) => ({
928-
id: f.id,
929-
name: f.name,
930-
href: `/workspace/${workspaceId}/files/${f.id}`,
931-
folderPath: f.folderPath
932-
? parseWorkspaceFileFolderDisplayPath(f.folderPath)
933-
: undefined,
934-
})),
935-
[fetchedFiles, workspaceId, permissionConfig.hideFilesTab]
936-
)
937-
938-
const searchModalKnowledgeBases = useMemo(
939-
() =>
940-
permissionConfig.hideKnowledgeBaseTab
941-
? []
942-
: fetchedKnowledgeBases.map((kb) => ({
943-
id: kb.id,
944-
name: kb.name,
945-
href: `/workspace/${workspaceId}/knowledge/${kb.id}`,
946-
folderPath: getFolderPathNames(knowledgeBaseFolderMap, kb.folderId),
947-
})),
948-
[
949-
fetchedKnowledgeBases,
950-
knowledgeBaseFolderMap,
951-
workspaceId,
952-
permissionConfig.hideKnowledgeBaseTab,
953-
]
954-
)
955-
956892
const chatIds = useMemo(() => chats.map((t) => t.id), [chats])
957893

958894
const { selectedChats, handleChatClick } = useChatSelection({ chatIds })
@@ -1928,9 +1864,6 @@ export const Sidebar = memo(function Sidebar({
19281864
workflows={searchModalWorkflows}
19291865
workspaces={searchModalWorkspaces}
19301866
chats={chats}
1931-
tables={searchModalTables}
1932-
files={searchModalFiles}
1933-
knowledgeBases={searchModalKnowledgeBases}
19341867
logs={searchModalLogs}
19351868
integrations={searchModalIntegrations}
19361869
connectedAccounts={searchModalConnectedAccounts}

0 commit comments

Comments
 (0)