@@ -57,6 +57,7 @@ const useDynamicTreeState = (
5757 isLoading : isLoadingLocalFileTree ,
5858 error : localFileTreeError ,
5959 } = useLocalFileTree ( ) ;
60+ const [ remoteFileTreeError , setRemoteFileTreeError ] = useState < Error | null > ( null ) ;
6061
6162 // If the site was just created and if there is no rewind_id yet,
6263 // then all options are pre-checked to allow only a full sync
@@ -73,6 +74,7 @@ const useDynamicTreeState = (
7374 if ( type === 'pull' && remoteSiteId ) {
7475 let isCancelled = false ;
7576 const loadRemoteTree = async ( ) => {
77+ setRemoteFileTreeError ( null ) ;
7678 try {
7779 if ( rewindId ) {
7880 const remoteTree = await fetchChildren ( remoteSiteId , rewindId , '/wp-content/' , false ) ;
@@ -83,7 +85,10 @@ const useDynamicTreeState = (
8385 }
8486 }
8587 } catch ( error ) {
86- console . error ( 'Failed to load remote file tree:' , error ) ;
88+ const errorObj = error instanceof Error ? error : new Error ( 'Unknown error occurred' ) ;
89+ if ( ! isCancelled ) {
90+ setRemoteFileTreeError ( errorObj ) ;
91+ }
8792 }
8893 } ;
8994 void loadRemoteTree ( ) ;
@@ -125,6 +130,7 @@ const useDynamicTreeState = (
125130 isErrorRewindId,
126131 isLoadingLocalFileTree,
127132 localFileTreeError,
133+ remoteFileTreeError,
128134 } ;
129135} ;
130136
@@ -162,6 +168,7 @@ export function SyncDialog( {
162168 isErrorRewindId,
163169 isLoadingLocalFileTree,
164170 localFileTreeError,
171+ remoteFileTreeError,
165172 } = useDynamicTreeState ( type , localSite . id , remoteSite . id , setTreeState ) ;
166173
167174 const [ wpVersion ] = useGetWpVersion ( localSite ) ;
@@ -225,8 +232,23 @@ export function SyncDialog( {
225232 }
226233
227234 if ( type === 'pull' && rewindId && node . path && node . children ?. length === 0 ) {
228- const children = await fetchChildren ( remoteSite . id , rewindId , node . path , node . checked ) ;
229- setTreeState ( ( prev ) => updateNodeById ( prev , node . id , { children } ) ) ;
235+ // Set loading state for the node
236+ setTreeState ( ( prev ) => updateNodeById ( prev , node . id , { loading : true } ) ) ;
237+
238+ try {
239+ const children = await fetchChildren ( remoteSite . id , rewindId , node . path , node . checked ) ;
240+ setTreeState ( ( prev ) =>
241+ updateNodeById ( prev , node . id , { children, loading : false , hasError : false } )
242+ ) ;
243+ } catch ( error ) {
244+ setTreeState ( ( prev ) =>
245+ updateNodeById ( prev , node . id , {
246+ children : [ ] ,
247+ loading : false ,
248+ hasError : true ,
249+ } )
250+ ) ;
251+ }
230252 }
231253 // For push operations, children are already loaded - no async fetching needed
232254 } ,
@@ -361,7 +383,7 @@ export function SyncDialog( {
361383 }
362384 return null ;
363385 } }
364- renderEmptyContent = { ( nodeId ) => {
386+ renderEmptyContent = { ( nodeId , node ) => {
365387 if ( nodeId === 'wp-content' && type === 'push' && localFileTreeError ) {
366388 return (
367389 < div className = "text-gray-500 italic" >
@@ -371,6 +393,18 @@ export function SyncDialog( {
371393 </ div >
372394 ) ;
373395 }
396+ if (
397+ ( nodeId === 'wp-content' && type === 'pull' && remoteFileTreeError ) ||
398+ node . hasError
399+ ) {
400+ return (
401+ < div className = "text-gray-500 italic" >
402+ { __ (
403+ 'Error retrieving remote files and directories. Please close and reopen this dialog to try again.'
404+ ) }
405+ </ div >
406+ ) ;
407+ }
374408 return (
375409 < div className = "text-gray-500 italic" aria-label = { __ ( 'Empty folder' ) } >
376410 { __ ( 'Empty' ) }
0 commit comments