(threadParentById: Record<string, string>)
| 20 | }; |
| 21 | |
| 22 | export function useThreadRows(threadParentById: Record<string, string>) { |
| 23 | const cacheRef = useRef( |
| 24 | new WeakMap< |
| 25 | ThreadSummary[], |
| 26 | Map<string, ThreadRowCacheEntry> |
| 27 | >(), |
| 28 | ); |
| 29 | const cacheParentRef = useRef(threadParentById); |
| 30 | if (cacheParentRef.current !== threadParentById) { |
| 31 | cacheParentRef.current = threadParentById; |
| 32 | cacheRef.current = new WeakMap< |
| 33 | ThreadSummary[], |
| 34 | Map<string, ThreadRowCacheEntry> |
| 35 | >(); |
| 36 | } |
| 37 | |
| 38 | const getThreadRows = useCallback( |
| 39 | ( |
| 40 | threads: ThreadSummary[], |
| 41 | isExpanded: boolean, |
| 42 | workspaceId: string, |
| 43 | getPinTimestamp: (workspaceId: string, threadId: string) => number | null, |
| 44 | pinVersion = 0, |
| 45 | ): ThreadRowResult => { |
| 46 | const cacheKey = `${workspaceId}:${isExpanded ? "1" : "0"}`; |
| 47 | const threadCache = cacheRef.current.get(threads); |
| 48 | const cachedEntry = threadCache?.get(cacheKey); |
| 49 | if (cachedEntry && cachedEntry.pinVersion === pinVersion) { |
| 50 | return cachedEntry.result; |
| 51 | } |
| 52 | |
| 53 | const visibleThreads = threads.filter((thread) => { |
| 54 | if (!thread.isSubagent) { |
| 55 | return true; |
| 56 | } |
| 57 | return Boolean(threadParentById[thread.id]); |
| 58 | }); |
| 59 | const threadIds = new Set(visibleThreads.map((thread) => thread.id)); |
| 60 | const childrenByParent = new Map<string, ThreadSummary[]>(); |
| 61 | const roots: ThreadSummary[] = []; |
| 62 | const resolveVisibleParentId = (threadId: string) => { |
| 63 | let current = threadParentById[threadId]; |
| 64 | const visited = new Set<string>([threadId]); |
| 65 | while (current && !visited.has(current)) { |
| 66 | if (threadIds.has(current)) { |
| 67 | return current; |
| 68 | } |
| 69 | visited.add(current); |
| 70 | current = threadParentById[current]; |
| 71 | } |
| 72 | return null; |
| 73 | }; |
| 74 | |
| 75 | visibleThreads.forEach((thread) => { |
| 76 | const parentId = resolveVisibleParentId(thread.id); |
| 77 | if (parentId) { |
| 78 | const list = childrenByParent.get(parentId) ?? []; |
| 79 | list.push(thread); |
no test coverage detected