@@ -78,11 +78,16 @@ type MonkeyAroundUninstaller = () => void
7878
7979type ContextMenuProvider = ( item : MenuItem ) => void
8080
81- enum FileExplorerStateError {
82- DoesNotExist ,
81+ enum FileExplorerState {
82+ DoesNotExist = 1 ,
8383 DeferredView
8484}
8585
86+ interface FileExplorerStateError {
87+ state : FileExplorerState
88+ fileExplorerInDeferredState ?: FileExplorerLeaf
89+ }
90+
8691type FileExplorerLeafOrError = ValueOrError < FileExplorerLeaf , FileExplorerStateError >
8792
8893export default class CustomSortPlugin
@@ -188,20 +193,25 @@ export default class CustomSortPlugin
188193
189194 if ( fileExplorer ) {
190195 if ( fileExplorer . isDeferred ) {
191- return fileExplorerOrError . setError ( FileExplorerStateError . DeferredView )
196+ return fileExplorerOrError . setError ( {
197+ state : FileExplorerState . DeferredView ,
198+ fileExplorerInDeferredState : fileExplorer
199+ } )
192200 } else {
193201 return fileExplorerOrError . setValue ( fileExplorer )
194202 }
195203 } else {
196- return fileExplorerOrError . setError ( FileExplorerStateError . DoesNotExist )
204+ return fileExplorerOrError . setError ( {
205+ state : FileExplorerState . DoesNotExist
206+ } )
197207 }
198208 }
199209
200210 checkFileExplorerIsAvailableAndPatchable ( logWarning : boolean = true ) : FileExplorerLeafOrError {
201211 let fileExplorerOrError = this . getFileExplorer ( )
202- if ( fileExplorerOrError . e === FileExplorerStateError . DeferredView ) {
212+ if ( fileExplorerOrError . e && fileExplorerOrError . e . state === FileExplorerState . DeferredView ) {
203213 if ( logWarning ) {
204- this . logWarningFileExplorerDeferred ( )
214+ this . logDeferredFileExplorerInfo ( )
205215 }
206216 return fileExplorerOrError
207217 }
@@ -269,12 +279,17 @@ export default class CustomSortPlugin
269279 }
270280 }
271281
272- logWarningFileExplorerDeferred ( ) {
273- const msg = `${ PLUGIN_ID } v${ this . manifest . version } : failed to get active File Explorer view.\n`
282+ logDeferredFileExplorerInfo ( ) {
283+ const msg = `${ PLUGIN_ID } v${ this . manifest . version } : File Explorer is not displayed yet (Obsidian deferred view detected) .\n`
274284 + `Until the File Explorer is visible, the custom-sort plugin cannot apply the custom order.\n`
275285 console . warn ( msg )
276286 }
277287
288+ logDeferredFileExplorerWatcherSetupInfo ( ) {
289+ const msg = `${ PLUGIN_ID } v${ this . manifest . version } : A watcher was set up to apply custom sort automatically when the File Explorer is displayed.\n`
290+ console . warn ( msg )
291+ }
292+
278293 logWarningFileExplorerNotAvailable ( ) {
279294 const msg = `${ PLUGIN_ID } v${ this . manifest . version } : failed to locate File Explorer. The 'Files' core plugin can be disabled.\n`
280295 + `Some community plugins can also disable it.\n`
@@ -583,7 +598,7 @@ export default class CustomSortPlugin
583598 const plugin = this
584599 this . app . workspace . onLayoutReady ( ( ) => {
585600 setTimeout ( ( ) => {
586- plugin . initialDelayedApplicationOfCustomSorting . apply ( this )
601+ plugin . delayedApplicationOfCustomSorting . apply ( this )
587602 } ,
588603 plugin . settings . delayForInitialApplication )
589604 } )
@@ -682,7 +697,39 @@ export default class CustomSortPlugin
682697 return false
683698 }
684699
685- initialDelayedApplicationOfCustomSorting ( ) {
700+ setWatcherForDelayedFileExplorerView ( fileExplorerInDeferredState ?: FileExplorerLeaf ) {
701+ const self = this
702+
703+ let workspaceLeafContentElementParentToObserve : HTMLElement | Element | null | undefined = fileExplorerInDeferredState ?. view ?. containerEl ?. parentElement
704+ if ( ! workspaceLeafContentElementParentToObserve ) {
705+ // Fallback for the case when DOM is not available for deferred file explorer
706+ // (did not happen in practice, but just in case)
707+ workspaceLeafContentElementParentToObserve = document . querySelector ( ".workspace" ) ;
708+ }
709+ if ( workspaceLeafContentElementParentToObserve ) {
710+ const fullyFledgedFileExplorerElementSelector =
711+ ( ) => workspaceLeafContentElementParentToObserve . querySelector ( '[data-type="file-explorer"] .nav-files-container' ) ;
712+
713+ const mutationObserver = new MutationObserver ( ( _ , observerInstance ) => {
714+ const fullyFledgedFileExplorerElement = fullyFledgedFileExplorerElementSelector ( ) ;
715+ if ( fullyFledgedFileExplorerElement ) {
716+ observerInstance . disconnect ( ) ;
717+ self . delayedApplicationOfCustomSorting ( self . FROM_DOM_WATCHER )
718+ }
719+ } ) ;
720+
721+ mutationObserver . observe ( workspaceLeafContentElementParentToObserve , {
722+ childList : true ,
723+ subtree : false
724+ } ) ;
725+ }
726+ }
727+
728+ // Entering this method for the first time after initial delay after plugin loaded (via setTimeout()),
729+ // and if first attempt is unsuccessful, then entering this method again from DOM watcher, when
730+ // the File Explorer view gets transformed from delayed view into fully-fledged active view
731+ FROM_DOM_WATCHER : boolean = true
732+ delayedApplicationOfCustomSorting ( fromDOMwatcher ?: boolean ) {
686733 if ( ! this ?. isThePluginStillInstalledAndEnabled ( ) ) {
687734 console . log ( `${ PLUGIN_ID } v${ this . manifest . version } - delayed handler skipped, plugin no longer active.` )
688735 return
@@ -695,7 +742,25 @@ export default class CustomSortPlugin
695742 return
696743 }
697744
698- this . switchPluginStateTo ( true )
745+ if ( ! fromDOMwatcher ) {
746+ // Only for the first delayed invocation:
747+ // If file explorer is delayed, configure the watcher
748+ // NOTE: Do not configure the watcher if the file explorer is not available
749+ let fileExplorerOrError : FileExplorerLeafOrError = this . checkFileExplorerIsAvailableAndPatchable ( )
750+ if ( fileExplorerOrError . e && fileExplorerOrError . e . state === FileExplorerState . DeferredView ) {
751+ this . logDeferredFileExplorerWatcherSetupInfo ( )
752+ this . setWatcherForDelayedFileExplorerView ( fileExplorerOrError . e . fileExplorerInDeferredState )
753+ } else if ( fileExplorerOrError . e ) {
754+ // file explorer other error - does not exist
755+ // force the plugin switch state to report error and show the error icon
756+ this . switchPluginStateTo ( true )
757+ }
758+ else { // file explorer is available
759+ this . switchPluginStateTo ( true )
760+ }
761+ } else {
762+ this . switchPluginStateTo ( true )
763+ }
699764 }
700765
701766 setRibbonIconToEnabled ( ) {
0 commit comments