@@ -11,6 +11,8 @@ import {
1111 type AddProjectRemoteSource ,
1212} from "@t3tools/client-runtime/operations/projects" ;
1313import {
14+ canPreloadBrowsePath ,
15+ createBrowseNavigationCoordinator ,
1416 filterFilesystemBrowseEntries ,
1517 getFilesystemBrowsePath ,
1618} from "@t3tools/client-runtime/state/filesystem" ;
@@ -43,7 +45,10 @@ import { useThemeColor } from "../../lib/useThemeColor";
4345import { uuidv4 } from "../../lib/uuid" ;
4446import { useAtomCommand } from "../../state/use-atom-command" ;
4547import { useAtomQueryRunner } from "../../state/use-atom-query-runner" ;
46- import { useSavedRemoteConnections } from "../../state/use-remote-environment-registry" ;
48+ import {
49+ useRemoteEnvironmentRuntime ,
50+ useSavedRemoteConnections ,
51+ } from "../../state/use-remote-environment-registry" ;
4752
4853interface EnvironmentOption {
4954 readonly environmentId : EnvironmentId ;
@@ -221,6 +226,63 @@ function ProjectPathInput(props: {
221226 ) ;
222227}
223228
229+ function useBrowsePathInput ( environment : EnvironmentOption | null ) {
230+ const [ pathInput , commitPathInput ] = useState ( ( ) =>
231+ getAddProjectInitialQuery ( environment ?. baseDirectory ) ,
232+ ) ;
233+ const environmentRuntime = useRemoteEnvironmentRuntime ( environment ?. environmentId ?? null ) ;
234+ const loadBrowsePath = useAtomQueryRunner ( filesystemEnvironment . browse , {
235+ reportFailure : false ,
236+ reportDefect : false ,
237+ } ) ;
238+ const [ browseNavigation ] = useState ( createBrowseNavigationCoordinator ) ;
239+ const [ isBrowseNavigating , setIsBrowseNavigating ] = useState ( false ) ;
240+ const setPathInput = useCallback (
241+ ( path : string ) => {
242+ browseNavigation . invalidate ( ) ;
243+ setIsBrowseNavigating ( false ) ;
244+ commitPathInput ( path ) ;
245+ } ,
246+ [ browseNavigation ] ,
247+ ) ;
248+ const navigateToBrowsePath = useCallback (
249+ async ( path : string ) => {
250+ setIsBrowseNavigating ( true ) ;
251+ const committed = await browseNavigation . run (
252+ async ( ) => {
253+ if ( environment && canPreloadBrowsePath ( environmentRuntime ?. connectionState ) ) {
254+ await loadBrowsePath ( {
255+ environmentId : environment . environmentId ,
256+ input : { partialPath : path } ,
257+ } ) ;
258+ }
259+ } ,
260+ ( ) => commitPathInput ( path ) ,
261+ ) ;
262+ if ( committed ) {
263+ setIsBrowseNavigating ( false ) ;
264+ }
265+ return committed ;
266+ } ,
267+ [ browseNavigation , environment , environmentRuntime ?. connectionState , loadBrowsePath ] ,
268+ ) ;
269+
270+ useEffect ( ( ) => {
271+ if ( environment ) {
272+ setPathInput ( getAddProjectInitialQuery ( environment . baseDirectory ) ) ;
273+ }
274+ } , [ environment , setPathInput ] ) ;
275+
276+ useEffect (
277+ ( ) => ( ) => {
278+ browseNavigation . invalidate ( ) ;
279+ } ,
280+ [ browseNavigation ] ,
281+ ) ;
282+
283+ return { isBrowseNavigating, pathInput, setPathInput, navigateToBrowsePath } ;
284+ }
285+
224286function useEnvironmentOptions ( ) : ReadonlyArray < EnvironmentOption > {
225287 const serverConfigByEnvironmentId = useServerConfigs ( ) ;
226288 const { savedConnectionsById } = useSavedRemoteConnections ( ) ;
@@ -585,6 +647,7 @@ function FolderBrowser(props: {
585647 readonly environment : EnvironmentOption ;
586648 readonly pathInput : string ;
587649 readonly setPathInput : ( path : string ) => void ;
650+ readonly navigateToBrowsePath : ( path : string ) => Promise < boolean > ;
588651} ) {
589652 const accentColor = useThemeColor ( "--color-icon-muted" ) ;
590653 const browsePath = useMemo (
@@ -633,7 +696,7 @@ function FolderBrowser(props: {
633696 right = { null }
634697 onPress = { ( ) => {
635698 if ( browsePath . parentPath ) {
636- props . setPathInput ( browsePath . parentPath ) ;
699+ void props . navigateToBrowsePath ( browsePath . parentPath ) ;
637700 }
638701 } }
639702 />
@@ -650,7 +713,7 @@ function FolderBrowser(props: {
650713 browsePath . directoryPath . length > 0
651714 ? appendBrowsePathSegment ( browsePath . directoryPath , entry . name )
652715 : ensureBrowseDirectoryPath ( entry . fullPath ) ;
653- props . setPathInput ( nextPath ) ;
716+ void props . navigateToBrowsePath ( nextPath ) ;
654717 } }
655718 />
656719 ) ) }
@@ -662,19 +725,13 @@ function FolderBrowser(props: {
662725export function AddProjectLocalFolderScreen ( props : { readonly environmentId ?: string | string [ ] } ) {
663726 const environment = useEnvironmentFromParam ( props . environmentId ) ;
664727 const createProject = useCreateProject ( environment ) ;
665- const [ pathInput , setPathInput ] = useState ( ( ) =>
666- getAddProjectInitialQuery ( environment ?. baseDirectory ) ,
667- ) ;
728+ const { isBrowseNavigating, navigateToBrowsePath, pathInput, setPathInput } =
729+ useBrowsePathInput ( environment ) ;
668730 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
669731 const [ error , setError ] = useState < string | null > ( null ) ;
670732
671- useEffect ( ( ) => {
672- if ( ! environment ) return ;
673- setPathInput ( getAddProjectInitialQuery ( environment . baseDirectory ) ) ;
674- } , [ environment ] ) ;
675-
676733 const submitPath = useCallback ( async ( ) => {
677- if ( ! environment || isSubmitting ) return ;
734+ if ( ! environment || isBrowseNavigating || isSubmitting ) return ;
678735 setError ( null ) ;
679736 const resolved = resolveAddProjectPath ( {
680737 rawPath : pathInput ,
@@ -692,7 +749,7 @@ export function AddProjectLocalFolderScreen(props: { readonly environmentId?: st
692749 setError ( errorMessage ( Cause . squash ( result . cause ) ) ) ;
693750 }
694751 setIsSubmitting ( false ) ;
695- } , [ createProject , environment , isSubmitting , pathInput ] ) ;
752+ } , [ createProject , environment , isBrowseNavigating , isSubmitting , pathInput ] ) ;
696753
697754 return (
698755 < AddProjectShell >
@@ -706,12 +763,13 @@ export function AddProjectLocalFolderScreen(props: { readonly environmentId?: st
706763 />
707764 < PrimaryActionButton
708765 label = "Add project"
709- disabled = { isSubmitting }
766+ disabled = { isBrowseNavigating || isSubmitting }
710767 onPress = { ( ) => void submitPath ( ) }
711768 loading = { isSubmitting }
712769 />
713770 < FolderBrowser
714771 environment = { environment }
772+ navigateToBrowsePath = { navigateToBrowsePath }
715773 pathInput = { pathInput }
716774 setPathInput = { setPathInput }
717775 />
@@ -735,19 +793,13 @@ export function AddProjectDestinationScreen(props: {
735793 const createProject = useCreateProject ( environment ) ;
736794 const remoteUrl = stringParam ( props . remoteUrl ) ;
737795 const repositoryTitle = stringParam ( props . repositoryTitle ) ;
738- const [ pathInput , setPathInput ] = useState ( ( ) =>
739- getAddProjectInitialQuery ( environment ?. baseDirectory ) ,
740- ) ;
796+ const { isBrowseNavigating, navigateToBrowsePath, pathInput, setPathInput } =
797+ useBrowsePathInput ( environment ) ;
741798 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
742799 const [ error , setError ] = useState < string | null > ( null ) ;
743800
744- useEffect ( ( ) => {
745- if ( ! environment ) return ;
746- setPathInput ( getAddProjectInitialQuery ( environment . baseDirectory ) ) ;
747- } , [ environment ] ) ;
748-
749801 const submitPath = useCallback ( async ( ) => {
750- if ( ! environment || ! remoteUrl || isSubmitting ) return ;
802+ if ( ! environment || ! remoteUrl || isBrowseNavigating || isSubmitting ) return ;
751803 setError ( null ) ;
752804 const resolved = resolveAddProjectPath ( {
753805 rawPath : pathInput ,
@@ -776,7 +828,15 @@ export function AddProjectDestinationScreen(props: {
776828 }
777829 }
778830 setIsSubmitting ( false ) ;
779- } , [ cloneRepository , createProject , environment , isSubmitting , pathInput , remoteUrl ] ) ;
831+ } , [
832+ cloneRepository ,
833+ createProject ,
834+ environment ,
835+ isBrowseNavigating ,
836+ isSubmitting ,
837+ pathInput ,
838+ remoteUrl ,
839+ ] ) ;
780840
781841 return (
782842 < AddProjectShell >
@@ -798,12 +858,13 @@ export function AddProjectDestinationScreen(props: {
798858 />
799859 < PrimaryActionButton
800860 label = "Clone project"
801- disabled = { isSubmitting || ! remoteUrl }
861+ disabled = { isBrowseNavigating || isSubmitting || ! remoteUrl }
802862 onPress = { ( ) => void submitPath ( ) }
803863 loading = { isSubmitting }
804864 />
805865 < FolderBrowser
806866 environment = { environment }
867+ navigateToBrowsePath = { navigateToBrowsePath }
807868 pathInput = { pathInput }
808869 setPathInput = { setPathInput }
809870 />
0 commit comments