Skip to content

Commit b2439a7

Browse files
Add centralized query cache invalidation utility
1 parent 72d8450 commit b2439a7

6 files changed

Lines changed: 56 additions & 33 deletions

File tree

frontend/src/components/settings/McpManager.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AddMcpServerDialog } from './AddMcpServerDialog'
88
import { McpServerCard } from './McpServerCard'
99
import { useMcpServers } from '@/hooks/useMcpServers'
1010
import { useMutation, useQueryClient } from '@tanstack/react-query'
11+
import { invalidateConfigCaches } from '@/lib/queryInvalidation'
1112

1213
interface McpServerConfig {
1314
type: 'local' | 'remote'
@@ -61,8 +62,7 @@ export function McpManager({ config, onUpdate, onConfigUpdate }: McpManagerProps
6162
await onUpdate(updatedConfig)
6263
},
6364
onSuccess: () => {
64-
queryClient.invalidateQueries({ queryKey: ['opencode-config'] })
65-
queryClient.invalidateQueries({ queryKey: ['mcp-status'] })
65+
invalidateConfigCaches(queryClient)
6666
setDeleteConfirmServer(null)
6767
},
6868
})

frontend/src/components/settings/OpenCodeConfigManager.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'
1818
import { useServerHealth } from '@/hooks/useServerHealth'
1919
import { parseJsonc, hasJsoncComments } from '@/lib/jsonc'
2020
import { showToast } from '@/lib/toast'
21+
import { invalidateConfigCaches } from '@/lib/queryInvalidation'
2122
import type { OpenCodeConfig } from '@/api/types/settings'
2223

2324
interface Command {
@@ -86,7 +87,7 @@ export function OpenCodeConfigManager() {
8687
return await settingsApi.reloadOpenCodeConfig()
8788
},
8889
onSuccess: () => {
89-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
90+
invalidateConfigCaches(queryClient)
9091
},
9192
})
9293

@@ -95,7 +96,7 @@ export function OpenCodeConfigManager() {
9596
return await settingsApi.restartOpenCodeServer()
9697
},
9798
onSuccess: () => {
98-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
99+
invalidateConfigCaches(queryClient)
99100
},
100101
})
101102

@@ -104,17 +105,15 @@ export function OpenCodeConfigManager() {
104105
return await settingsApi.upgradeOpenCode()
105106
},
106107
onSuccess: (data) => {
107-
queryClient.invalidateQueries({ queryKey: ['health'] })
108-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
108+
invalidateConfigCaches(queryClient)
109109
if (data.upgraded) {
110110
showToast.success(`Upgraded to v${data.newVersion} and server restarted`, { id: 'upgrade-opencode' })
111111
} else {
112112
showToast.success('OpenCode is already up to date', { id: 'upgrade-opencode' })
113113
}
114114
},
115115
onError: (error) => {
116-
queryClient.invalidateQueries({ queryKey: ['health'] })
117-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
116+
invalidateConfigCaches(queryClient)
118117

119118
const defaultMessage = 'Failed to upgrade OpenCode'
120119

@@ -186,9 +185,8 @@ export function OpenCodeConfigManager() {
186185
}
187186
} else {
188187
showToast.success('Configuration updated')
188+
invalidateConfigCaches(queryClient)
189189
}
190-
191-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
192190
} catch (error) {
193191
console.error('Failed to update config:', error)
194192
showToast.error(getApiErrorMessage(error, 'Failed to update config'), { id: 'update-restart' })
@@ -242,7 +240,7 @@ export function OpenCodeConfigManager() {
242240
showToast.success('Configuration created', { id: 'create-config' })
243241
}
244242

245-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
243+
invalidateConfigCaches(queryClient)
246244
} catch (error) {
247245
console.error('Failed to create config:', error)
248246
showToast.error(getApiErrorMessage(error, 'Failed to create configuration'), { id: 'create-config' })
@@ -259,9 +257,11 @@ export function OpenCodeConfigManager() {
259257
setIsUpdating(true)
260258
await settingsApi.deleteOpenCodeConfig(config.name)
261259
setDeleteConfirmConfig(null)
260+
if (selectedConfig?.id === config.id) {
261+
setSelectedConfig(null)
262+
}
262263
fetchConfigs()
263-
// Invalidate agents cache in case deleted config had agents
264-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
264+
invalidateConfigCaches(queryClient)
265265
} catch (error) {
266266
console.error('Failed to delete config:', error)
267267
} finally {
@@ -501,7 +501,7 @@ export function OpenCodeConfigManager() {
501501
? 'Configuration saved and server reloaded'
502502
: 'Configuration saved'
503503
showToast.success(successMsg, { id: 'edit-config' })
504-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
504+
invalidateConfigCaches(queryClient)
505505
} catch (error) {
506506
showToast.error(getApiErrorMessage(error, 'Failed to save configuration'), { id: 'edit-config' })
507507
throw error
@@ -586,15 +586,15 @@ export function OpenCodeConfigManager() {
586586
<div className="flex items-center gap-3 min-w-0">
587587
<h4 className="text-sm font-medium truncate">Commands</h4>
588588
<span className="text-xs text-muted-foreground">
589-
{Object.keys(selectedConfig.content.command as Record<string, Command> || {}).length} configured
589+
{Object.keys((selectedConfig.content?.command as Record<string, Command> | undefined) ?? {}).length} configured
590590
</span>
591591
</div>
592592
<Edit className={`h-4 w-4 transition-transform ${expandedSections.commands ? 'rotate-90' : ''}`} />
593593
</button>
594594
<div className={`${expandedSections.commands ? 'block' : 'hidden'} border-t border-border`}>
595595
<div className="p-1 sm:p-4 max-h-[50vh] overflow-y-auto">
596596
<CommandsEditor
597-
commands={(selectedConfig.content.command as Record<string, Command>) || {}}
597+
commands={(selectedConfig.content?.command as Record<string, Command> | undefined) ?? {}}
598598
onChange={(commands) => {
599599
const updatedContent = {
600600
...selectedConfig.content,
@@ -623,15 +623,15 @@ export function OpenCodeConfigManager() {
623623
<div className="flex items-center gap-3 min-w-0">
624624
<h4 className="text-sm font-medium truncate">Agents</h4>
625625
<span className="text-xs text-muted-foreground">
626-
{Object.keys(selectedConfig.content.agent as Record<string, Agent> || {}).length} configured
626+
{Object.keys((selectedConfig.content?.agent as Record<string, Agent> | undefined) ?? {}).length} configured
627627
</span>
628628
</div>
629629
<Edit className={`h-4 w-4 transition-transform ${expandedSections.agents ? 'rotate-90' : ''}`} />
630630
</button>
631631
<div className={`${expandedSections.agents ? 'block' : 'hidden'} border-t border-border`}>
632632
<div className="p-4 max-h-[50vh] overflow-y-auto">
633633
<AgentsEditor
634-
agents={(selectedConfig.content.agent as Record<string, Agent>) || {}}
634+
agents={(selectedConfig.content?.agent as Record<string, Agent> | undefined) ?? {}}
635635
onChange={(agents) => {
636636
const updatedContent = {
637637
...selectedConfig.content,
@@ -660,7 +660,7 @@ export function OpenCodeConfigManager() {
660660
<div className="flex items-center gap-3 min-w-0">
661661
<h4 className="text-sm font-medium truncate">MCP Servers</h4>
662662
<span className="text-xs text-muted-foreground">
663-
{Object.keys((selectedConfig.content.mcp as Record<string, unknown>) || {}).length} configured
663+
{Object.keys((selectedConfig.content?.mcp as Record<string, unknown> | undefined) ?? {}).length} configured
664664
</span>
665665
</div>
666666
<Edit className={`h-4 w-4 transition-transform ${expandedSections.mcp ? 'rotate-90' : ''}`} />

frontend/src/hooks/useContextUsage.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useMemo } from 'react'
22
import { useMessages } from './useOpenCode'
33
import { useQuery } from '@tanstack/react-query'
4-
import { useSessionAgent } from './useSessionAgent'
54
import { useModelSelection } from './useModelSelection'
65

76
interface ContextUsage {
@@ -43,11 +42,8 @@ async function fetchProviders(opcodeUrl: string): Promise<ProvidersResponse> {
4342

4443
export const useContextUsage = (opcodeUrl: string | null | undefined, sessionID: string | undefined, directory?: string): ContextUsage => {
4544
const { data: messages, isLoading: messagesLoading } = useMessages(opcodeUrl, sessionID, directory)
46-
const sessionAgent = useSessionAgent(opcodeUrl, sessionID, directory)
4745
const { modelString: globalModelString } = useModelSelection(opcodeUrl, directory)
48-
const modelString = sessionAgent.model
49-
? `${sessionAgent.model.providerID}/${sessionAgent.model.modelID}`
50-
: globalModelString
46+
const modelString = globalModelString
5147

5248
const { data: providersData } = useQuery({
5349
queryKey: ['providers', opcodeUrl],

frontend/src/hooks/useServerHealth.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { useQuery } from '@tanstack/react-query'
1+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
22
import { toast } from 'sonner'
33
import { settingsApi } from '@/api/settings'
4-
import { useMutation } from '@tanstack/react-query'
5-
import { useQueryClient } from '@tanstack/react-query'
4+
import { invalidateConfigCaches, invalidateSettingsCaches } from '@/lib/queryInvalidation'
65

76
interface HealthResponse {
87
status: 'healthy' | 'degraded' | 'unhealthy'
@@ -34,8 +33,7 @@ export function useServerHealth(enabled = true) {
3433
return await settingsApi.reloadOpenCodeConfig()
3534
},
3635
onSuccess: () => {
37-
queryClient.invalidateQueries({ queryKey: ['health'] })
38-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
36+
invalidateConfigCaches(queryClient)
3937
toast.success('Server configuration reloaded successfully')
4038
},
4139
onError: (error: unknown) => {
@@ -53,9 +51,7 @@ export function useServerHealth(enabled = true) {
5351
return await settingsApi.rollbackOpenCodeConfig()
5452
},
5553
onSuccess: (data) => {
56-
queryClient.invalidateQueries({ queryKey: ['health'] })
57-
queryClient.invalidateQueries({ queryKey: ['settings'] })
58-
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
54+
invalidateSettingsCaches(queryClient)
5955
toast.success(data.message)
6056
},
6157
onError: () => {

frontend/src/hooks/useSettings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
22
import { settingsApi } from '@/api/settings'
33
import type { UserPreferences } from '@/api/types/settings'
4+
import { invalidateConfigCaches } from '@/lib/queryInvalidation'
45

56
export function useSettings(userId = 'default') {
67
const queryClient = useQueryClient()
@@ -43,8 +44,8 @@ export function useSettings(userId = 'default') {
4344
}
4445
},
4546
onSettled: () => {
46-
// Refetch to ensure server state
4747
queryClient.invalidateQueries({ queryKey: ['settings', userId] })
48+
invalidateConfigCaches(queryClient)
4849
},
4950
})
5051

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { QueryClient } from '@tanstack/react-query'
2+
3+
export function invalidateConfigCaches(queryClient: QueryClient) {
4+
queryClient.invalidateQueries({ queryKey: ['opencode', 'config'] })
5+
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
6+
queryClient.invalidateQueries({ queryKey: ['opencode-config'] })
7+
queryClient.invalidateQueries({ queryKey: ['health'] })
8+
queryClient.invalidateQueries({ queryKey: ['mcp-status'] })
9+
queryClient.invalidateQueries({ queryKey: ['providers'] })
10+
}
11+
12+
export function invalidateSettingsCaches(queryClient: QueryClient, userId = 'default') {
13+
queryClient.invalidateQueries({ queryKey: ['settings', userId] })
14+
invalidateConfigCaches(queryClient)
15+
}
16+
17+
export function invalidateSessionCaches(queryClient: QueryClient) {
18+
queryClient.invalidateQueries({
19+
predicate: (query) =>
20+
query.queryKey[0] === 'opencode' &&
21+
(query.queryKey[1] === 'sessions' ||
22+
query.queryKey[1] === 'session' ||
23+
query.queryKey[1] === 'messages'),
24+
})
25+
}
26+
27+
export function invalidateAllConfigRelatedCaches(queryClient: QueryClient, userId = 'default') {
28+
invalidateSettingsCaches(queryClient, userId)
29+
invalidateSessionCaches(queryClient)
30+
}

0 commit comments

Comments
 (0)