Repository: st3v3nmw/obsidian-spaced-repetition
Pull request #1145 (2024-10-20 17:28:59)

fix(sidebar): duplicate icons & reviewQueueView undefined exception
Fixes https://github.com/st3v3nmw/obsidian-spaced-repetition/discussions/1135.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/st3v3nmw/obsidian-spaced-repetition/pull/1145?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Stephen+Mwangi) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 93.15%. Comparing base [(`e313a51`)](https://app.codecov.io/gh/st3v3nmw/obsidian-spaced-repetition/commit/e313a5128a10c072567ab35bcc01f6cf33efc718?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Stephen+Mwangi) to head [(`8f976c2`)](https://app.codecov.io/gh/st3v3nmw/obsidian-spaced-repetition/commit/8f976c2b78dbc2a62ba5be141e39aef58b63822d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Stephen+Mwangi).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1145      +/-   ##
==========================================
- Coverage   93.19%   93.15%   -0.05%     
==========================================
  Files          42       42              
  Lines        4558     4558              
  Branches      650      648       -2     
==========================================
- Hits         4248     4246       -2     
- Misses        304      310       +6     
+ Partials        6        2       -4     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/st3v3nmw/obsidian-spaced-repetition/pull/1145?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Stephen+Mwangi).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Stephen+Mwangi).


Patch:
diff --git a/src/core.ts b/src/core.ts
index e934b4b..439ecd4 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -74,11 +74,12 @@ export class OsrCore {
         osrNoteLinkInfoFinder: IOsrVaultNoteLinkInfoFinder,
         settings: SRSettings,
         dataChangedHandler: () => void,
+        noteReviewQueue: NoteReviewQueue,
     ): void {
         this.settings = settings;
         this.osrNoteLinkInfoFinder = osrNoteLinkInfoFinder;
         this.dataChangedHandler = dataChangedHandler;
-        this._noteReviewQueue = new NoteReviewQueue();
+        this._noteReviewQueue = noteReviewQueue;
         this._questionPostponementList = questionPostponementList;
         this._dueDateFlashcardHistogram = new CardDueDateHistogram();
         this._dueDateNoteHistogram = new NoteDueDateHistogram();
diff --git a/src/gui/review-queue-list-view.tsx b/src/gui/review-queue-list-view.tsx
index 8564cec..23a7005 100644
--- a/src/gui/review-queue-list-view.tsx
+++ b/src/gui/review-queue-list-view.tsx
@@ -1,4 +1,4 @@
-import { App, ItemView, Menu, TFile, WorkspaceLeaf } from "obsidian";
+import { ItemView, Menu, TFile, WorkspaceLeaf } from "obsidian";
 
 import { COLLAPSE_ICON, TICKS_PER_DAY } from "src/constants";
 import { t } from "src/lang/helpers";
@@ -18,7 +18,6 @@ export class ReviewQueueListView extends ItemView {
 
     constructor(
         leaf: WorkspaceLeaf,
-        app: App,
         nextNoteReviewHandler: NextNoteReviewHandler,
         settings: SRSettings,
     ) {
@@ -26,8 +25,11 @@ export class ReviewQueueListView extends ItemView {
 
         this.nextNoteReviewHandler = nextNoteReviewHandler;
         this.settings = settings;
-        this.registerEvent(this.app.workspace.on("file-open", () => this.redraw()));
-        this.registerEvent(this.app.vault.on("rename", () => this.redraw()));
+
+        if (this.settings.enableNoteReviewPaneOnStartup) {
+            this.registerEvent(this.app.workspace.on("file-open", () => this.redraw()));
+            this.registerEvent(this.app.vault.on("rename", () => this.redraw()));
+        }
     }
 
     public getViewType(): string {
@@ -53,6 +55,8 @@ export class ReviewQueueListView extends ItemView {
     }
 
     public redraw(): void {
+        if (!this.noteReviewQueue.reviewDecks) return;
+
         const activeFile: TFile | null = this.app.workspace.getActiveFile();
 
         const rootEl: HTMLElement = createDiv("tree-item nav-folder mod-root");
diff --git a/src/gui/sidebar.tsx b/src/gui/sidebar.tsx
index 1f3d399..667d26f 100644
--- a/src/gui/sidebar.tsx
+++ b/src/gui/sidebar.tsx
@@ -25,52 +25,39 @@ export class OsrSidebar {
     }
 
     redraw(): void {
-        if (this.getActiveLeaf(REVIEW_QUEUE_VIEW_TYPE)) this.reviewQueueListView.redraw();
+        this.reviewQueueListView.redraw();
     }
 
     private getActiveLeaf(type: string): WorkspaceLeaf | null {
         const leaves = this.app.workspace.getLeavesOfType(type);
         if (leaves.length == 0) {
-            return null;
+            return this.app.workspace.getRightLeaf(false);
         }
 
         return leaves[0];
     }
 
-    async init(): Promise<void> {
+    init(): void {
         this.plugin.registerView(REVIEW_QUEUE_VIEW_TYPE, (leaf) => {
             return (this.reviewQueueListView = new ReviewQueueListView(
                 leaf,
-                this.app,
                 this.nextNoteReviewHandler,
                 this.settings,
             ));
         });
-
-        if (
-            this.settings.enableNoteReviewPaneOnStartup &&
-            this.getActiveLeaf(REVIEW_QUEUE_VIEW_TYPE) == null
-        ) {
-            await this.activateReviewQueueViewPanel();
-        }
     }
 
-    private async activateReviewQueueViewPanel(): Promise<void> {
-        await this.app.workspace.getRightLeaf(false).setViewState({
-            type: REVIEW_QUEUE_VIEW_TYPE,
-            active: true,
-        });
+    async activateReviewQueueViewPanel(): Promise<void> {
+        if (this.settings.enableNoteReviewPaneOnStartup) {
+            await this.getActiveLeaf(REVIEW_QUEUE_VIEW_TYPE).setViewState({
+                type: REVIEW_QUEUE_VIEW_TYPE,
+                active: true,
+            });
+        }
     }
 
     async openReviewQueueView(): Promise<void> {
-        let reviewQueueLeaf = this.getActiveLeaf(REVIEW_QUEUE_VIEW_TYPE);
-        if (reviewQueueLeaf == null) {
-            await this.activateReviewQueueViewPanel();
-            reviewQueueLeaf = this.getActiveLeaf(REVIEW_QUEUE_VIEW_TYPE);
-        }
-
-        if (reviewQueueLeaf !== null) {
-            this.app.workspace.revealLeaf(reviewQueueLeaf);
-        }
+        const reviewQueueLeaf = this.getActiveLeaf(REVIEW_QUEUE_VIEW_TYPE);
+        this.app.workspace.revealLeaf(reviewQueueLeaf);
     }
 }
diff --git a/src/gui/statistics.tsx b/src/gui/statistics.tsx
index 2fccecc..fecb382 100644
--- a/src/gui/statistics.tsx
+++ b/src/gui/statistics.tsx
@@ -143,10 +143,7 @@ export class StatisticsView {
             t("INTERVALS_DESC"),
             Object.keys(cardStats.intervals.dict),
             Object.values(cardStats.intervals.dict),
-            t("INTERVALS_SUMMARY", {
-                avg: averageInterval,
-                longest: longestInterval,
-            }),
+            t("INTERVALS_SUMMARY", { avg: averageInterval, longest: longestInterval }),
             t("COUNT"),
             t("DAYS"),
             t("NUMBER_OF_CARDS"),
diff --git a/src/main.ts b/src/main.ts
index dfadb84..75c8e8e 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -32,6 +32,7 @@ import { t } from "src/lang/helpers";
 import { NextNoteReviewHandler } from "src/next-note-review-handler";
 import { Note } from "src/note";
 import { NoteFileLoader } from "src/note-file-loader";
+import { NoteReviewQueue } from "src/note-review-queue";
 import { setDebugParser } from "src/parser";
 import { DEFAULT_DATA, PluginData } from "src/plugin-data";
 import { QuestionPostponementList } from "src/question-postponement-list";
@@ -57,12 +58,24 @@ export default class SRPlugin extends Plugin {
     async onload(): Promise<void> {
         await this.loadPluginData();
 
-        this.initLogicClasses();
+        const noteReviewQueue = new NoteReviewQueue();
+        this.nextNoteReviewHandler = new NextNoteReviewHandler(
+            this.app,
+            this.data.settings,
+            noteReviewQueue,
+        );
 
-        this.initGuiItems();
-    }
+        this.osrSidebar = new OsrSidebar(this, this.data.settings, this.nextNoteReviewHandler);
+        this.osrSidebar.init();
+        this.app.workspace.onLayoutReady(async () => {
+            await this.osrSidebar.activateReviewQueueViewPanel();
+            setTimeout(async () => {
+                if (!this.osrAppCore.syncLock) {
+                    await this.sync();
+                }
+            }, 2000);
+        });
 
-    private initLogicClasses() {
         const questionPostponementList: QuestionPostponementList = new QuestionPostponementList(
             this,
             this.data.settings,
@@ -78,16 +91,9 @@ export default class SRPlugin extends Plugin {
             osrNoteLinkInfoFinder,
             this.data.settings,
             this.onOsrVaultDataChanged.bind(this),
+            noteReviewQueue,
         );
-    }
 
-    private initGuiItems() {
-        this.nextNoteReviewHandler = new NextNoteReviewHandler(
-            this.app,
-            this.data.settings,
-            this.app.workspace,
-            this.osrAppCore.noteReviewQueue,
-        );
         appIcon();
 
         this.showStatusBar(this.data.settings.showStatusBar);
@@ -99,16 +105,6 @@ export default class SRPlugin extends Plugin {
         this.addPluginCommands();
 
         this.addSettingTab(new SRSettingTab(this.app, this));
-
-        this.osrSidebar = new OsrSidebar(this, this.data.settings, this.nextNoteReviewHandler);
-        this.app.workspace.onLayoutReady(async () => {
-            await this.osrSidebar.init();
-            setTimeout(async () => {
-                if (!this.osrAppCore.syncLock) {
-                    await this.sync();
-                }
-            }, 2000);
-        });
     }
 
     showFileMenuItems(status: boolean) {
@@ -355,7 +351,8 @@ export default class SRPlugin extends Plugin {
                 ),
             }),
         );
-        this.osrSidebar.redraw();
+
+        if (this.data.settings.enableNoteReviewPaneOnStartup) this.osrSidebar.redraw();
     }
 
     async loadNote(noteFile: TFile): Promise<Note> {
diff --git a/src/next-note-review-handler.ts b/src/next-note-review-handler.ts
index beccad2..1ecb75b 100644
--- a/src/next-note-review-handler.ts
+++ b/src/next-note-review-handler.ts
@@ -1,4 +1,4 @@
-import { App, Notice, TFile, Workspace } from "obsidian";
+import { App, Notice, TFile } from "obsidian";
 
 import { ReviewDeckSelectionModal } from "src/gui/review-deck-selection-modal";
 import { t } from "src/lang/helpers";
@@ -8,7 +8,6 @@ import { SRSettings } from "src/settings";
 export class NextNoteReviewHandler {
     private app: App;
     private settings: SRSettings;
-    private workspace: Workspace;
     private _noteReviewQueue: NoteReviewQueue;
     private _lastSelectedReviewDeck: string;
 
@@ -20,15 +19,9 @@ export class NextNoteReviewHandler {
         return this._noteReviewQueue;
     }
 
-    constructor(
-        app: App,
-        settings: SRSettings,
-        workspace: Workspace,
-        noteReviewQueue: NoteReviewQueue,
-    ) {
+    constructor(app: App, settings: SRSettings, noteReviewQueue: NoteReviewQueue) {
         this.app = app;
         this.settings = settings;
-        this.workspace = workspace;
         this._noteReviewQueue = noteReviewQueue;
     }
 


Test patch:
diff --git a/tests/unit/helpers/unit-test-core.ts b/tests/unit/helpers/unit-test-core.ts
index 77f017d..8b25a34 100644
--- a/tests/unit/helpers/unit-test-core.ts
+++ b/tests/unit/helpers/unit-test-core.ts
@@ -2,6 +2,7 @@ import * as fs from "fs";
 import * as path from "path";
 
 import { OsrCore } from "src/core";
+import { NoteReviewQueue } from "src/note-review-queue";
 import { QuestionPostponementList } from "src/question-postponement-list";
 import { SRSettings } from "src/settings";
 
@@ -24,7 +25,13 @@ export class UnitTestOsrCore extends OsrCore {
             settings,
             this.buryList,
         );
-        this.init(questionPostponementList, this.infoFinder, settings, () => {});
+        this.init(
+            questionPostponementList,
+            this.infoFinder,
+            settings,
+            () => {},
+            new NoteReviewQueue(),
+        );
     }
 
     // Needed for unit testing: Setup fileMap and the link "info finder"