@@ -12,7 +12,13 @@ import {
1212 sanitizeEpisodeListLimit ,
1313 volume ,
1414} from "src/store" ;
15- import { Notice , Plugin , type Editor , type WorkspaceLeaf } from "obsidian" ;
15+ import {
16+ Notice ,
17+ Platform ,
18+ Plugin ,
19+ type Editor ,
20+ type WorkspaceLeaf ,
21+ } from "obsidian" ;
1622import { API } from "src/API/API" ;
1723import type { IAPI } from "src/API/IAPI" ;
1824import { DEFAULT_SETTINGS , VIEW_TYPE } from "src/constants" ;
@@ -64,7 +70,7 @@ export default class PodNotes extends Plugin implements IPodNotes {
6470 public settings ! : IPodNotesSettings ;
6571 public override app ! : PartialAppExtension ;
6672
67- private view : MainView | null = null ;
73+ private views = new Set < MainView > ( ) ;
6874
6975 private playedEpisodeController ?: StoreController < {
7076 [ episodeName : string ] : PlayedEpisode ;
@@ -89,12 +95,17 @@ export default class PodNotes extends Plugin implements IPodNotes {
8995
9096 private maxLayoutReadyAttempts = 10 ;
9197 private layoutReadyAttempts = 0 ;
98+ private layoutReadyRetry : ReturnType < typeof setTimeout > | null = null ;
99+ private isUnloaded = false ;
100+ private podcastViewMountEnabled = ! Platform . isMobileApp ;
92101 private isReady = false ;
93102 private pendingSave : IPodNotesSettings | null = null ;
94103 private saveScheduled = false ;
95104 private saveChain : Promise < void > = Promise . resolve ( ) ;
96105
97106 override async onload ( ) {
107+ this . isUnloaded = false ;
108+ this . podcastViewMountEnabled = ! Platform . isMobileApp ;
98109 plugin . set ( this ) ;
99110
100111 await this . loadSettings ( ) ;
@@ -446,8 +457,9 @@ export default class PodNotes extends Plugin implements IPodNotes {
446457 this . addSettingTab ( new PodNotesSettingsTab ( this . app , this ) ) ;
447458
448459 this . registerView ( VIEW_TYPE , ( leaf : WorkspaceLeaf ) => {
449- this . view = new MainView ( leaf , this ) ;
450- return this . view ;
460+ const view = new MainView ( leaf , this ) ;
461+ this . views . add ( view ) ;
462+ return view ;
451463 } ) ;
452464
453465 // Persistent, discoverable entry point in the left ribbon. The right
@@ -460,39 +472,62 @@ export default class PodNotes extends Plugin implements IPodNotes {
460472
461473 this . app . workspace . onLayoutReady ( this . onLayoutReady . bind ( this ) ) ;
462474
463- this . registerObsidianProtocolHandler ( "podnotes" , ( action ) =>
464- podNotesURIHandler ( action , this . api ) ,
465- ) ;
475+ this . registerObsidianProtocolHandler ( "podnotes" , ( action ) => {
476+ this . enablePodcastViewMount ( ) ;
477+ return podNotesURIHandler ( action , this . api ) ;
478+ } ) ;
466479
467480 this . registerEvent ( getContextMenuHandler ( this . app ) ) ;
468481
469482 this . isReady = true ;
470483 }
471484
472485 onLayoutReady ( ) : void {
486+ if ( this . isUnloaded ) {
487+ return ;
488+ }
489+
490+ if ( Platform . isMobileApp ) {
491+ // Mobile startup is sensitive to creating plugin-owned side panes; keep
492+ // PodNotes dormant until the user opens it with the command or ribbon.
493+ this . clearLayoutReadyRetry ( ) ;
494+ this . layoutReadyAttempts = 0 ;
495+ return ;
496+ }
497+
473498 if ( ! this . app . workspace || ! this . app . workspace . layoutReady ) {
474499 // Workspace is not ready, schedule a retry
475500 this . layoutReadyAttempts ++ ;
476- if ( this . layoutReadyAttempts < this . maxLayoutReadyAttempts ) {
477- setTimeout ( ( ) => this . onLayoutReady ( ) , 100 ) ;
478- } else {
501+ if ( this . layoutReadyAttempts >= this . maxLayoutReadyAttempts ) {
479502 console . error (
480503 "Failed to initialize PodNotes layout after maximum attempts" ,
481504 ) ;
505+ } else if ( ! this . layoutReadyRetry ) {
506+ this . layoutReadyRetry = setTimeout ( ( ) => {
507+ this . layoutReadyRetry = null ;
508+ this . onLayoutReady ( ) ;
509+ } , 100 ) ;
482510 }
483511 return ;
484512 }
485513
514+ this . clearLayoutReadyRetry ( ) ;
515+ this . layoutReadyAttempts = 0 ;
516+
486517 if ( this . app . workspace . getLeavesOfType ( VIEW_TYPE ) . length ) {
487518 return ;
488519 }
489520
490521 const leaf = this . app . workspace . getRightLeaf ( false ) ;
491522
492523 if ( leaf ) {
493- leaf . setViewState ( {
494- type : VIEW_TYPE ,
495- } ) ;
524+ void leaf
525+ . setViewState ( {
526+ type : VIEW_TYPE ,
527+ } )
528+ . catch ( ( error ) => {
529+ console . error ( "PodNotes: failed to initialize startup view" , error ) ;
530+ } ) ;
496531 }
497532 }
498533
@@ -501,6 +536,8 @@ export default class PodNotes extends Plugin implements IPodNotes {
501536 // makes "Show PodNotes" and the ribbon icon reliably surface the view even
502537 // when it is already open but hidden in a collapsed/overflowing sidebar (#55).
503538 async activateView ( ) : Promise < void > {
539+ this . enablePodcastViewMount ( ) ;
540+
504541 const { workspace } = this . app ;
505542
506543 const existing = workspace . getLeavesOfType ( VIEW_TYPE ) ;
@@ -516,6 +553,21 @@ export default class PodNotes extends Plugin implements IPodNotes {
516553 }
517554 }
518555
556+ shouldMountPodcastView ( ) : boolean {
557+ return this . podcastViewMountEnabled ;
558+ }
559+
560+ enablePodcastViewMount ( ) : void {
561+ this . podcastViewMountEnabled = true ;
562+ for ( const view of this . views ) {
563+ view . mountPodcastView ( ) ;
564+ }
565+ }
566+
567+ unregisterPodcastView ( view : MainView ) : void {
568+ this . views . delete ( view ) ;
569+ }
570+
519571 private getTranscriptionService ( ) : TranscriptionService {
520572 if ( ! this . transcriptionService ) {
521573 this . transcriptionService = new TranscriptionService ( this ) ;
@@ -525,6 +577,8 @@ export default class PodNotes extends Plugin implements IPodNotes {
525577 }
526578
527579 override onunload ( ) {
580+ this . isUnloaded = true ;
581+ this . clearLayoutReadyRetry ( ) ;
528582 this . playedEpisodeController ?. off ( ) ;
529583 this . savedFeedsController ?. off ( ) ;
530584 this . playlistController ?. off ( ) ;
@@ -536,11 +590,19 @@ export default class PodNotes extends Plugin implements IPodNotes {
536590 this . hidePlayedEpisodesController ?. off ( ) ;
537591 this . volumeUnsubscribe ?.( ) ;
538592 this . localFilesMirrorUnsubscribe ?.( ) ;
593+ this . views . clear ( ) ;
539594
540595 // Detach all leaves of this view type to prevent duplicates on reload
541596 this . app . workspace . detachLeavesOfType ( VIEW_TYPE ) ;
542597 }
543598
599+ private clearLayoutReadyRetry ( ) : void {
600+ if ( ! this . layoutReadyRetry ) return ;
601+
602+ clearTimeout ( this . layoutReadyRetry ) ;
603+ this . layoutReadyRetry = null ;
604+ }
605+
544606 async loadSettings ( ) {
545607 const loadedData = await this . loadData ( ) ;
546608
0 commit comments