@@ -10,16 +10,7 @@ import { EnvironmentId, ThreadId, type SidebarProjectGroupingMode } from "@t3too
1010import { useAtomValue } from "@effect/atom-react" ;
1111import { useFocusEffect } from "@react-navigation/native" ;
1212import { NavigationContext , NavigationRouteContext , useNavigation } from "@react-navigation/native" ;
13- import {
14- use ,
15- useDeferredValue ,
16- useEffect ,
17- useEffectEvent ,
18- useMemo ,
19- useRef ,
20- useState ,
21- type ReactNode ,
22- } from "react" ;
13+ import { use , useDeferredValue , useEffect , useMemo , useRef , useState , type ReactNode } from "react" ;
2314import { Keyboard , Platform , useWindowDimensions , View } from "react-native" ;
2415import { KeyboardController , useKeyboardState } from "react-native-keyboard-controller" ;
2516import Animated , {
@@ -45,6 +36,7 @@ import { scopedThreadKey } from "../../lib/scopedEntities";
4536import type { ThreadListFilters } from "../../persistence/mobile-preferences" ;
4637import { appAtomRegistry } from "../../state/atom-registry" ;
4738import { useProjects } from "../../state/entities" ;
39+ import { useThreadCwd } from "../../state/use-selected-thread-worktree" ;
4840import { environmentThreadShells } from "../../state/threads" ;
4941import { mobilePreferencesAtom } from "../../state/preferences" ;
5042import {
@@ -128,46 +120,20 @@ export function useRegisterWorkspaceInspector(render: (() => ReactNode) | undefi
128120 ) ;
129121 } ) ( ) ;
130122
131- const focusedRef = useRef ( false ) ;
132- const deactivateRef = useRef < ( ( ) => void ) | null > ( null ) ;
133-
134- const syncRegistration = useEffectEvent ( ( ) => {
135- if ( ! focusedRef . current || wrappedRender === undefined ) {
136- deactivateRef . current ?.( ) ;
137- return ;
138- }
139- deactivateRef . current = registerWorkspaceInspector ( wrappedRender ) ;
140- } ) ;
141-
142- useFocusEffect ( ( ) => {
143- focusedRef . current = true ;
144- syncRegistration ( ) ;
145- return ( ) => {
146- focusedRef . current = false ;
147- syncRegistration ( ) ;
148- } ;
149- } ) ;
150-
151- useEffect ( ( ) => {
152- if ( focusedRef . current ) {
153- syncRegistration ( ) ;
154- }
155- // oxlint-disable-next-line react/exhaustive-effect-dependencies -- Changed pane content replaces its existing native workspace registration.
156- } , [ wrappedRender ] ) ;
157-
158- useEffect (
159- ( ) => ( ) => {
160- deactivateRef . current ?.( ) ;
161- deactivateRef . current = null ;
162- } ,
163- [ ] ,
123+ // useFocusEffect re-runs when the renderer changes while focused, so the
124+ // pane always shows this render's content, and its cleanup deactivates on
125+ // blur and unmount.
126+ useFocusEffect ( ( ) =>
127+ wrappedRender === undefined ? undefined : registerWorkspaceInspector ( wrappedRender ) ,
164128 ) ;
165129}
166130
167131export function AdaptiveWorkspaceLayout ( props : {
168132 readonly children : ReactNode ;
169133 readonly pathname : string ;
170134 readonly workspaceRouteKey : string | undefined ;
135+ readonly workspaceRouteName : string | undefined ;
136+ readonly workspaceRouteKeys : ReadonlyArray < string > ;
171137 readonly selectedPullRequestKey ?: string | null ;
172138} ) {
173139 const preferencesResult = useAtomValue ( mobilePreferencesAtom ) ;
@@ -194,6 +160,8 @@ function AdaptiveWorkspaceLayoutContent(
194160 readonly children : ReactNode ;
195161 readonly pathname : string ;
196162 readonly workspaceRouteKey : string | undefined ;
163+ readonly workspaceRouteName : string | undefined ;
164+ readonly workspaceRouteKeys : ReadonlyArray < string > ;
197165 readonly selectedPullRequestKey ?: string | null ;
198166 } & {
199167 readonly projectGroupingMode : SidebarProjectGroupingMode ;
@@ -348,16 +316,52 @@ function AdaptiveWorkspaceLayoutContent(
348316 const activeThread = parseActiveThreadPath ( pathname ) ;
349317 const environmentId = activeThread ?. environmentId ?? null ;
350318 const threadId = activeThread ?. threadId ?? null ;
351- const selectedThreadKey = ( ( ) => {
319+ const selectedThreadRef = ( ( ) => {
352320 if ( environmentId === null || threadId === null ) {
353321 return null ;
354322 }
355323 try {
356- return scopedThreadKey ( EnvironmentId . make ( environmentId ) , ThreadId . make ( threadId ) ) ;
324+ return {
325+ environmentId : EnvironmentId . make ( environmentId ) ,
326+ threadId : ThreadId . make ( threadId ) ,
327+ } ;
357328 } catch {
358329 return null ;
359330 }
360331 } ) ( ) ;
332+ const selectedThreadKey =
333+ selectedThreadRef === null
334+ ? null
335+ : scopedThreadKey ( selectedThreadRef . environmentId , selectedThreadRef . threadId ) ;
336+ const isFilesRoute = props . workspaceRouteName === "ThreadFiles" ;
337+ // Same resolution the Files screen uses: without a cwd it shows "Files unavailable" and
338+ // registers no inspector, so revealing would only hide the sidebar beside that screen.
339+ const filesCwd = useThreadCwd ( isFilesRoute && fileInspector . supported ? selectedThreadRef : null ) ;
340+ // Opening a review or the files route reveals its inspector once per route and thread.
341+ // Routes still on the stack remember their reveal, so going back to one keeps the user's choice.
342+ const revealsInspector =
343+ props . workspaceRouteName === "ThreadReview" || ( isFilesRoute && filesCwd !== null ) ;
344+ const [ revealedInspectors , setRevealedInspectors ] = useState < ReadonlyMap < string , string > > (
345+ ( ) => new Map ( ) ,
346+ ) ;
347+ if (
348+ revealsInspector &&
349+ props . workspaceRouteKey !== undefined &&
350+ selectedThreadKey !== null &&
351+ revealedInspectors . get ( props . workspaceRouteKey ) !== selectedThreadKey
352+ ) {
353+ const next = new Map < string , string > ( ) ;
354+ for ( const routeKey of props . workspaceRouteKeys ) {
355+ const revealedThreadKey = revealedInspectors . get ( routeKey ) ;
356+ if ( revealedThreadKey !== undefined ) {
357+ next . set ( routeKey , revealedThreadKey ) ;
358+ }
359+ }
360+ next . set ( props . workspaceRouteKey , selectedThreadKey ) ;
361+ setRevealedInspectors ( next ) ;
362+ setFocusedAuxiliaryPaneRole ( "inspector" ) ;
363+ setFileInspectorPreferredVisible ( true ) ;
364+ }
361365 const [ workspaceInspector , setWorkspaceInspector ] = useState < {
362366 readonly render : ( ) => ReactNode ;
363367 readonly active : boolean ;
0 commit comments