Skip to content

Commit be7e64b

Browse files
refactor: extract session cache lookup and add permission navigation
1 parent a77d9e0 commit be7e64b

3 files changed

Lines changed: 76 additions & 37 deletions

File tree

frontend/src/components/notifications/PendingActionsGroup.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { PendingActionBadge } from '@/components/ui/pending-action-badge'
33
import { usePermissions, useQuestions } from '@/contexts/EventContext'
44

55
export function PendingActionsGroup() {
6-
const { pendingCount: permissionCount, setShowDialog } = usePermissions()
6+
const { pendingCount: permissionCount, setShowDialog, navigateToCurrent: navigateToPermission } = usePermissions()
77
const { pendingCount: questionCount, navigateToCurrent } = useQuestions()
88

99
return (
@@ -12,7 +12,10 @@ export function PendingActionsGroup() {
1212
count={permissionCount}
1313
icon={Bell}
1414
color="orange"
15-
onClick={() => setShowDialog(true)}
15+
onClick={() => {
16+
navigateToPermission()
17+
setShowDialog(true)
18+
}}
1619
label="permission"
1720
/>
1821
<PendingActionBadge

frontend/src/components/ui/header.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ function HeaderActions({ children, className }: { children: ReactNode; className
173173

174174
function HeaderMobileDropdown({ children, className }: { children: ReactNode; className?: string }) {
175175
const isMobile = useMobile();
176-
const { pendingCount: permissionCount, setShowDialog } = usePermissions();
176+
const { pendingCount: permissionCount, setShowDialog, navigateToCurrent: navigateToPermission } = usePermissions();
177177
const { pendingCount: questionCount, navigateToCurrent } = useQuestions();
178178
const { open } = useSettingsDialog();
179179

@@ -202,7 +202,7 @@ function HeaderMobileDropdown({ children, className }: { children: ReactNode; cl
202202
</DropdownMenuTrigger>
203203
<DropdownMenuContent align="end">
204204
{permissionCount > 0 && (
205-
<DropdownMenuItem onClick={() => setShowDialog(true)} className="gap-2">
205+
<DropdownMenuItem onClick={() => { navigateToPermission(); setShowDialog(true); }} className="gap-2">
206206
<Bell className="w-4 h-4 text-orange-500" />
207207
<span>{permissionCount} pending permission{permissionCount > 1 ? 's' : ''}</span>
208208
</DropdownMenuItem>

frontend/src/contexts/EventContext.tsx

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ interface EventContextValue {
9696
hasForSession: (sessionID: string) => boolean
9797
showDialog: boolean
9898
setShowDialog: (show: boolean) => void
99+
navigateToCurrent: () => void
99100
}
100101
questions: {
101102
current: QuestionRequest | null
@@ -157,59 +158,64 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
157158
const currentPermission = allPermissions[0] ?? null
158159
const currentQuestion = allQuestions[0] ?? null
159160

160-
const getRepoIdForSession = useCallback((sessionID: string): number | null => {
161-
if (!repos) return null
162-
161+
const findSessionInCache = useCallback((sessionID: string): { url: string; directory: string } | null => {
163162
const cache = queryClient.getQueryCache()
164163
const queries = cache.getAll()
165-
164+
166165
for (const query of queries) {
167166
const key = query.queryKey
168167
if (key[0] === 'opencode' && key[1] === 'session' && key.length >= 5) {
169168
const sessionData = query.state.data as { id: string } | undefined
170169
if (sessionData?.id === sessionID) {
171-
const directory = key[4] as string | undefined
172-
if (directory) {
173-
const repo = repos.find(r => r.fullPath === directory)
174-
if (repo) return repo.id
175-
}
170+
const url = key[2] as string
171+
const directory = key[4] as string
172+
if (url && directory) return { url, directory }
176173
}
177174
}
178175
}
179-
return null
180-
}, [repos, queryClient])
181-
182-
const getClient = useCallback((sessionID: string): OpenCodeClient | null => {
183-
const cache = queryClient.getQueryCache()
184-
const queries = cache.getAll()
185176

186177
for (const query of queries) {
187178
const key = query.queryKey
188-
if (key[0] === 'opencode' && key[1] === 'session' && key.length >= 5) {
189-
const sessionData = query.state.data as { id: string } | undefined
190-
if (sessionData?.id === sessionID) {
179+
if (key[0] === 'opencode' && key[1] === 'sessions' && key.length >= 4) {
180+
const sessionsList = query.state.data as Array<{ id: string }> | undefined
181+
if (!sessionsList) continue
182+
const found = sessionsList.find(s => s.id === sessionID)
183+
if (found) {
191184
const url = key[2] as string
192-
const directory = key[4] as string | undefined
193-
194-
if (!url || typeof url !== 'string') continue
195-
196-
const clientKey = `${url}|${directory ?? ''}`
197-
let client = clientsRef.current.get(clientKey)
198-
if (!client) {
199-
if (clientsRef.current.size >= MAX_CACHED_CLIENTS) {
200-
const firstKey = clientsRef.current.keys().next().value
201-
if (firstKey) clientsRef.current.delete(firstKey)
202-
}
203-
client = new OpenCodeClient(url, directory)
204-
clientsRef.current.set(clientKey, client)
205-
}
206-
return client
185+
const directory = key[3] as string
186+
if (url && directory) return { url, directory }
207187
}
208188
}
209189
}
190+
210191
return null
211192
}, [queryClient])
212193

194+
const getRepoIdForSession = useCallback((sessionID: string): number | null => {
195+
if (!repos) return null
196+
const result = findSessionInCache(sessionID)
197+
if (!result) return null
198+
const repo = repos.find(r => r.fullPath === result.directory)
199+
return repo?.id ?? null
200+
}, [repos, findSessionInCache])
201+
202+
const getClient = useCallback((sessionID: string): OpenCodeClient | null => {
203+
const result = findSessionInCache(sessionID)
204+
if (!result) return null
205+
206+
const clientKey = `${result.url}|${result.directory}`
207+
let client = clientsRef.current.get(clientKey)
208+
if (!client) {
209+
if (clientsRef.current.size >= MAX_CACHED_CLIENTS) {
210+
const firstKey = clientsRef.current.keys().next().value
211+
if (firstKey) clientsRef.current.delete(firstKey)
212+
}
213+
client = new OpenCodeClient(result.url, result.directory)
214+
clientsRef.current.set(clientKey, client)
215+
}
216+
return client
217+
}, [findSessionInCache])
218+
213219
const addPermission = useCallback((permission: PermissionRequest) => {
214220
addToSessionKeyedState(setPermissionsBySession, permission)
215221
}, [])
@@ -322,6 +328,17 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
322328
}
323329
}, [currentQuestion, getRepoIdForSession, navigate, location.pathname])
324330

331+
const navigateToCurrentPermission = useCallback(() => {
332+
if (!currentPermission) return
333+
const repoId = getRepoIdForSession(currentPermission.sessionID)
334+
if (repoId) {
335+
const targetPath = `/repos/${repoId}/sessions/${currentPermission.sessionID}`
336+
if (location.pathname !== targetPath) {
337+
navigate(targetPath)
338+
}
339+
}
340+
}, [currentPermission, getRepoIdForSession, navigate, location.pathname])
341+
325342
const fetchInitialPendingData = useCallback(async () => {
326343
if (!repos || repos.length === 0) return
327344

@@ -386,6 +403,23 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
386403
})
387404
}
388405
break
406+
case 'session.updated':
407+
if ('info' in event.properties) {
408+
const sessionInfo = event.properties.info as { id: string }
409+
const cache = queryClient.getQueryCache()
410+
for (const query of cache.getAll()) {
411+
const key = query.queryKey
412+
if (key[0] === 'opencode' && key[1] === 'sessions' && key.length >= 4) {
413+
const currentList = query.state.data as Array<{ id: string }> | undefined
414+
if (!currentList) continue
415+
const exists = currentList.some(s => s.id === sessionInfo.id)
416+
if (!exists) {
417+
queryClient.setQueryData(key, [...currentList, sessionInfo])
418+
}
419+
}
420+
}
421+
}
422+
break
389423
case 'lsp.updated':
390424
queryClient.invalidateQueries({
391425
queryKey: ['opencode', 'lsp']
@@ -443,6 +477,7 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
443477
hasForSession: hasPermissionsForSession,
444478
showDialog: showPermissionDialog,
445479
setShowDialog: setShowPermissionDialog,
480+
navigateToCurrent: navigateToCurrentPermission,
446481
},
447482
questions: {
448483
current: currentQuestion,
@@ -466,6 +501,7 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
466501
getPermissionForCallID,
467502
hasPermissionsForSession,
468503
showPermissionDialog,
504+
navigateToCurrentPermission,
469505
currentQuestion,
470506
allQuestions.length,
471507
replyToQuestion,

0 commit comments

Comments
 (0)