@@ -5,7 +5,14 @@ import { useArchivedTaskIds } from "@posthog/ui/features/archive/useArchivedTask
55import { useCloudPrUrl } from "@posthog/ui/features/git-interaction/useCloudPrUrl" ;
66import { useTaskPrStatus } from "@posthog/ui/features/sidebar/useTaskPrStatus" ;
77import { Flex , Spinner , Text } from "@radix-ui/themes" ;
8- import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
8+ import {
9+ type ReactNode ,
10+ useCallback ,
11+ useEffect ,
12+ useMemo ,
13+ useRef ,
14+ useState ,
15+ } from "react" ;
916import { VList , type VListHandle } from "virtua" ;
1017import {
1118 REVIEW_LIST_BUFFER_PX ,
@@ -16,6 +23,8 @@ import { REVIEW_HOST, type ReviewHost } from "../reviewHost";
1623import { useReviewNavigationStore } from "../reviewNavigationStore" ;
1724import type { ReviewListItem , ReviewShellProps } from "../reviewShellParts" ;
1825import {
26+ buildItemIndex ,
27+ filterReviewItemsByFilePaths ,
1928 findActiveScrollKey ,
2029 findRenderedScrollAnchor ,
2130 isFileViewed ,
@@ -113,6 +122,7 @@ export function ReviewShell({
113122 isEmpty,
114123 items,
115124 itemIndexByFilePath,
125+ commentedFilePaths,
116126 currentSignatures,
117127 viewedRecord,
118128 onToggleViewed,
@@ -135,6 +145,26 @@ export function ReviewShell({
135145 const lastActiveRef = useRef < string | null > ( null ) ;
136146 const pendingNavigationRef = useRef < string | null > ( null ) ;
137147 const navigationFrameRef = useRef < number | null > ( null ) ;
148+ const [ showCommentedFilesOnly , setShowCommentedFilesOnly ] = useState ( false ) ;
149+ const isCommentFilterActive =
150+ showCommentedFilesOnly && commentedFilePaths !== undefined ;
151+
152+ const commentedItems = useMemo (
153+ ( ) =>
154+ commentedFilePaths
155+ ? filterReviewItemsByFilePaths ( items , commentedFilePaths )
156+ : [ ] ,
157+ [ commentedFilePaths , items ] ,
158+ ) ;
159+ const visibleItems = isCommentFilterActive ? commentedItems : items ;
160+ const visibleItemIndexByFilePath = useMemo (
161+ ( ) => buildItemIndex ( visibleItems ) ,
162+ [ visibleItems ] ,
163+ ) ;
164+ const commentedFileCount = useMemo (
165+ ( ) => commentedItems . filter ( ( item ) => item . filePaths ) . length ,
166+ [ commentedItems ] ,
167+ ) ;
138168
139169 const workerFactory = useCallback (
140170 ( ) => reviewHost . diffWorkerFactory ( ) ,
@@ -147,12 +177,20 @@ export function ReviewShell({
147177 const isExpanded = reviewMode === "expanded" ;
148178
149179 const viewedCount = useMemo ( ( ) => {
180+ const visibleKeys = isCommentFilterActive
181+ ? new Set (
182+ visibleItems . flatMap ( ( item ) =>
183+ item . scrollKey ? [ item . scrollKey ] : [ ] ,
184+ ) ,
185+ )
186+ : null ;
150187 let count = 0 ;
151188 for ( const [ key , sig ] of currentSignatures ) {
189+ if ( visibleKeys && ! visibleKeys . has ( key ) ) continue ;
152190 if ( isFileViewed ( viewedRecord [ key ] , sig ) ) count ++ ;
153191 }
154192 return count ;
155- } , [ currentSignatures , viewedRecord ] ) ;
193+ } , [ currentSignatures , isCommentFilterActive , viewedRecord , visibleItems ] ) ;
156194
157195 // Collapse already-viewed files on first open per task (mirrors GitHub).
158196 // Skips on re-opens: seededTaskRef prevents re-collapsing files the user
@@ -219,8 +257,13 @@ export function ReviewShell({
219257
220258 useEffect ( ( ) => {
221259 if ( ! scrollRequest ) return ;
222- const targetIndex = itemIndexByFilePath . get ( scrollRequest ) ;
223- if ( targetIndex === undefined ) return ;
260+ const targetIndex = visibleItemIndexByFilePath . get ( scrollRequest ) ;
261+ if ( targetIndex === undefined ) {
262+ if ( isCommentFilterActive && itemIndexByFilePath . has ( scrollRequest ) ) {
263+ setShowCommentedFilesOnly ( false ) ;
264+ }
265+ return ;
266+ }
224267
225268 const currentSignature = currentSignatures . get ( scrollRequest ) ;
226269 const viewed =
@@ -264,7 +307,9 @@ export function ReviewShell({
264307 onUncollapseFile ,
265308 scrollRequest ,
266309 setActiveFilePath ,
310+ isCommentFilterActive ,
267311 taskId ,
312+ visibleItemIndexByFilePath ,
268313 viewedRecord ,
269314 ] ) ;
270315
@@ -293,6 +338,40 @@ export function ReviewShell({
293338 [ ] ,
294339 ) ;
295340
341+ let reviewContent : ReactNode ;
342+ if ( isLoading ) {
343+ reviewContent = (
344+ < Flex align = "center" justify = "center" className = "min-h-0 flex-1" >
345+ < Spinner size = "2" />
346+ </ Flex >
347+ ) ;
348+ } else if ( isEmpty || visibleItems . length === 0 ) {
349+ reviewContent = (
350+ < Flex align = "center" justify = "center" className = "min-h-0 flex-1" >
351+ < Text color = "gray" className = "text-sm" >
352+ { isCommentFilterActive
353+ ? "No files with comments"
354+ : "No file changes to review" }
355+ </ Text >
356+ </ Flex >
357+ ) ;
358+ } else {
359+ reviewContent = (
360+ < VList
361+ ref = { listRef }
362+ bufferSize = { REVIEW_LIST_BUFFER_PX }
363+ itemSize = { REVIEW_LIST_ESTIMATED_ITEM_SIZE }
364+ className = "pierre-scroll-root scrollbar-overlay-y min-h-0 flex-1 overflow-auto bg-(--gray-2)"
365+ shift = { false }
366+ style = { { scrollbarGutter : "stable" } }
367+ onScroll = { handleScroll }
368+ data = { visibleItems }
369+ >
370+ { renderItem }
371+ </ VList >
372+ ) ;
373+ }
374+
296375 return (
297376 < WorkerPoolContextProvider
298377 // poolSize: each highlighter worker is a full V8 isolate with shiki
@@ -325,6 +404,13 @@ export function ReviewShell({
325404 taskId = { taskId }
326405 fileCount = { fileCount }
327406 viewedCount = { viewedCount }
407+ commentedFileCount = { commentedFileCount }
408+ showCommentedFilesOnly = { isCommentFilterActive }
409+ onToggleCommentedFilesOnly = {
410+ commentedFilePaths
411+ ? ( ) => setShowCommentedFilesOnly ( ( current ) => ! current )
412+ : undefined
413+ }
328414 linesAdded = { linesAdded }
329415 linesRemoved = { linesRemoved }
330416 allExpanded = { allExpanded }
@@ -343,38 +429,7 @@ export function ReviewShell({
343429 direction = "column"
344430 className = "min-w-0 flex-1"
345431 >
346- { isLoading ? (
347- < Flex
348- align = "center"
349- justify = "center"
350- className = "min-h-0 flex-1"
351- >
352- < Spinner size = "2" />
353- </ Flex >
354- ) : isEmpty ? (
355- < Flex
356- align = "center"
357- justify = "center"
358- className = "min-h-0 flex-1"
359- >
360- < Text color = "gray" className = "text-sm" >
361- No file changes to review
362- </ Text >
363- </ Flex >
364- ) : (
365- < VList
366- ref = { listRef }
367- bufferSize = { REVIEW_LIST_BUFFER_PX }
368- itemSize = { REVIEW_LIST_ESTIMATED_ITEM_SIZE }
369- className = "pierre-scroll-root scrollbar-overlay-y min-h-0 flex-1 overflow-auto bg-(--gray-2)"
370- shift = { false }
371- style = { { scrollbarGutter : "stable" } }
372- onScroll = { handleScroll }
373- data = { items }
374- >
375- { renderItem }
376- </ VList >
377- ) }
432+ { reviewContent }
378433 < PendingReviewBar taskId = { taskId } />
379434 </ Flex >
380435
0 commit comments