Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persistent review queues #48

Merged
merged 4 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 40 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,54 @@
# Simple Note Review
Simple and flexible plugin for easy note review, resurfacing, and repetition.
Simple and flexible plugin for note review, resurfacing, and repetition.

> IMPORTANT: this plugin uses amazing [Dataview](https://github.com/blacksmithgu/obsidian-dataview) plugin as its search engine.
> IMPORTANT: this plugin uses [Dataview](https://github.com/blacksmithgu/obsidian-dataview) plugin as its search engine.
Please make sure you have Dataview plugin installed.

### Review your notes easily
Review notes based on the date they were last reviewed.
Notes with no reviewed date will be first in note set.
Mark notes as rewieved today with one command.
### Is this a spaced repitition plugin?
Not quite.

![simple-note-review-sidebar-demo](https://user-images.githubusercontent.com/36126057/230956476-a525e77d-632c-44f5-b8e3-ab0bd427155f.gif)
This is a plugin for people who want to review, resurface, and rediscover their notes, but do not want to build a rigid system with a schedule.

The philosophy is: "define a set of notes and review them at your own pace".

### Build flexible note sets for reviewing
Build custom note sets for reviewing based on tags, folders, or DataviewJS-style queries.
Customize note set's name and query logic.
## How does it work?
#### Define a note set based on tags, folders, or a dataviewJS query.
Simple Note Review can create a noteset with a flexible set of rules: tags and/or folders, creation date, or even a dataviewJS query.
A note can be in any number of note sets.

![noteset_example_1](https://user-images.githubusercontent.com/36126057/208353981-756c526e-f42a-4981-be03-fa0b479a1dbc.jpg)
![image](https://github.com/dartungar/obsidian-simple-note-review/assets/36126057/60c9501c-aa4c-4d09-bd77-4ebf2dd9bb0b)

#### Use toolbar or commands for reviewing
When you start a review of a noteset for the first time, the plugin creates a persistent queue with all the notes in this noteset.

You can proceed reviewing notes in order, or choose a random one.

### Review frequency
You can optionally set each note's review frequency:
If you want to start a fresh review, reset the noteset queue with corresponding button on the sidebar or a command.

IMPORTANT: review queue does not update automatically, so it's always best to reset the queue when starting a new review!

![sidebar](https://github.com/dartungar/obsidian-simple-note-review/assets/36126057/f85c88f9-819f-40ec-a5ee-2db623d32733)

#### Review or skip note
To review or skip a note, click button on a sidebar or use a corresponding command.
Skipping a note removes it from a current review queue; it will be back when you reset the queue.

![review-note](https://github.com/dartungar/obsidian-simple-note-review/assets/36126057/9d18dc36-2c98-43bb-8101-12d8cf7cc313)


#### Change review frequency for notes or exclude them from reviews
You can optionally set each note's review frequency in the sidebar on via command:
- high
- normal
- low
- ignore

### Review algorithms
![note-review-frequency](https://user-images.githubusercontent.com/36126057/192049630-bb1455eb-e2b1-4abd-9440-beb8dfac7818.png)
##### Default
Makes notes with earlier review dates appear first.
If review frequency is set, notes with higher review frequency will rank significantly higher than those with lower review frequency.
##### Random
Takes a random note from the current set.

### Review statistics
See note set's details:
- what notes are included in the set
- how many notes are included in the set
- how many notes are reviewed in last 7, 30 days
- how many notes are not reviewed yet

![noteset-info](https://user-images.githubusercontent.com/36126057/187531702-4de555fe-6229-4885-92a1-a591bbc33615.png)
- ignore (exclude from all reviews)

#### What does "current note set" mean?
It is a note set you are currently reviewing.
Since one note can be in any number of note sets, the plugin needs to understand which queue to use.

#### Noteset Stats
You can see noteset stats in settings or in sidebar.

![image](https://github.com/dartungar/obsidian-simple-note-review/assets/36126057/257d2a64-60fc-470d-8649-9541b47159cf)

183 changes: 89 additions & 94 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,91 +1,69 @@
import { Editor, MarkdownView, Notice, Plugin } from 'obsidian';
import { getAPI } from 'obsidian-dataview';
import { addSimpleNoteReviewIcon } from 'src/UI/icon';
import { NoteSetService } from 'src/noteSet/noteSetService';
import { SelectNoteSetModal } from 'src/UI/selectNoteSetModal';
import { DefaultSettings, SimpleNoteReviewPluginSettings } from 'src/settings/pluginSettings';
import { SimpleNoteReviewPluginSettingsTab } from 'src/UI/settingsTab';
import { ReviewFrequency } from 'src/noteSet/reviewFrequency';
import { SimpleNoteReviewSidebarView } from 'src/UI/sidebar/sidebarView';
import { Editor, MarkdownView, Notice, Plugin } from "obsidian";
import { getAPI } from "obsidian-dataview";
import { addSimpleNoteReviewIcon } from "src/UI/icon";
import { NoteSetService } from "src/noteSet/noteSetService";
import { SelectNoteSetModal } from "src/UI/selectNoteSetModal";
import {
DefaultSettings,
SimpleNoteReviewPluginSettings,
} from "src/settings/pluginSettings";
import { SimpleNoteReviewPluginSettingsTab } from "src/UI/settingsTab";
import { ReviewFrequency } from "src/noteSet/reviewFrequency";
import { SimpleNoteReviewSidebarView } from "src/UI/sidebar/sidebarView";
import { FileService } from "src/notes/fileService";
import { ReviewService } from "src/queues/reviewService";

export default class SimpleNoteReviewPlugin extends Plugin {
settings: SimpleNoteReviewPluginSettings;
service: NoteSetService = new NoteSetService(this.app, this);
noteSetService: NoteSetService = new NoteSetService(this.app, this);
reviewService: ReviewService = new ReviewService(this.app, this);
fileService: FileService = new FileService(this.app, this);

readonly openModalIconName: string = "glasses";
readonly markAsReviewedIconName: string = "checkmark";

async onload() {
this.app.workspace.onLayoutReady(() => {
if (!this.dataviewIsInstalled()) {
this.showNotice("Could not find Dataview plugin. To use Simple Note Review plugin, please install Dataview plugin first.")
this.showNotice(
"Could not find Dataview plugin. To use Simple Note Review plugin, please install Dataview plugin first."
);
}
});

await this.loadSettings();

this.service.updateNoteSetDisplayNames();
this.noteSetService.updateNoteSetDisplayNames();

this.registerView(
SimpleNoteReviewSidebarView.VIEW_TYPE,
(leaf) => new SimpleNoteReviewSidebarView(leaf, this)
);

this.addRibbonIcon(this.openModalIconName, "Simple Note Review: Open Sidebar View", (evt: MouseEvent) => {
this.activateView();
})

this.addRibbonIcon("play-circle", "Simple Note Review: Start Reviewing", (evt: MouseEvent) => {
this.startReview();
});

this.addCommands();

this.registerEvent(
this.app.workspace.on("editor-menu", (menu, editor, view) => {
menu.addItem((item) => {
item
.setTitle("Mark Note As Reviewed Today")
.setIcon(this.markAsReviewedIconName)
.onClick(async () => {
await this.service.reviewNote(view.file);
});
});
})
this.addRibbonIcon(
this.openModalIconName,
"Simple Note Review: Open Sidebar View",
(evt: MouseEvent) => {
this.activateView();
}
);

this.registerEvent(
this.app.workspace.on("file-menu", (menu, file) => {
menu.addItem((item) => {
item
.setTitle("Mark Note As Reviewed Today")
.setIcon(this.markAsReviewedIconName)
.onClick(async () => {
await this.service.reviewNote(file);
});
});
})
this.addRibbonIcon(
"play",
"Simple Note Review: Continue Review of Current Note Set",
(evt: MouseEvent) => {
this.reviewService.startReview(this.settings.currentNoteSet);
}
);


this.addSettingTab(new SimpleNoteReviewPluginSettingsTab(this, this.app));
}

onunload() {
this.addCommands();

this.addSettingTab(
new SimpleNoteReviewPluginSettingsTab(this, this.app)
);
}


public async startReview(): Promise<void> {
let currentNoteSet = this.settings.currentNoteSet;
if (!currentNoteSet) {
new SelectNoteSetModal(this.app, this).open();
return;
}

this.showNotice(`Reviewing note set "${this.settings.currentNoteSet.displayName}"`);
this.service.openNextFile(currentNoteSet);

}
onunload() {}

async loadSettings() {
this.settings = Object.assign(
Expand All @@ -99,33 +77,25 @@ export default class SimpleNoteReviewPlugin extends Plugin {
await this.saveData(this.settings);
}

public showNotice(message: string) : void {
public showNotice(message: string): void {
new Notice(message);
}

private dataviewIsInstalled(): boolean {
return !!getAPI();
}
}

private addCommands(): void {
this.addCommand({
id: "start-review",
name: "Start reviewing notes",
name: "Start reviewing notes in current note set",
callback: () => {
this.startReview();
this.reviewService.startReview(this.settings.currentNoteSet);
},
});

this.addCommand({
id: "continue-review",
name: "Continue reviewing notes",
callback: () => {
this.startReview();
}
});

this.addCommand({
id: "open-toolbar",
id: "open-sidebar",
name: "Open Sidebar View",
callback: () => {
this.activateView();
Expand All @@ -136,34 +106,46 @@ export default class SimpleNoteReviewPlugin extends Plugin {
id: "open-random-note",
name: "Open random note from the current note set",
callback: () => {
this.service.openRandomFile(this.settings.currentNoteSet);
}
this.reviewService.openRandomNoteInQueue(
this.settings.currentNoteSet
);
},
});

this.addCommand({
id: "reset-queue",
name: "reset queue for the current note set",
callback: () => {
this.reviewService.resetNotesetQueue(
this.settings.currentNoteSet
);
},
});

this.addCommand({
id: "open-modal",
name: "Select Note Set For Reviewing",
name: "Select note set for reviewing",
callback: () => {
new SelectNoteSetModal(this.app, this).open();
},
});

this.addCommand({
id: "set-reviewed-date",
name: "Mark Note As Reviewed Today",
id: "mark-current-note-as-reviewed",
name: "Mark current note as reviewed",
callback: () => {
this.service.reviewNote(
this.reviewService.reviewNote(
this.app.workspace.getActiveFile(),
this.settings.currentNoteSet
)
);
},
});

this.addCommand({
id: "set-review-frequency-high",
name: "Set review frequency to high",
callback: () => {
this.service.setReviewFrequency(
this.fileService.setReviewFrequency(
this.app.workspace.getActiveFile(),
ReviewFrequency.high
);
Expand All @@ -174,7 +156,7 @@ export default class SimpleNoteReviewPlugin extends Plugin {
id: "set-review-frequency-normal",
name: "Set review frequency to normal",
callback: () => {
this.service.setReviewFrequency(
this.fileService.setReviewFrequency(
this.app.workspace.getActiveFile(),
ReviewFrequency.normal
);
Expand All @@ -185,7 +167,7 @@ export default class SimpleNoteReviewPlugin extends Plugin {
id: "set-review-frequency-low",
name: "Set review frequency to low",
callback: () => {
this.service.setReviewFrequency(
this.fileService.setReviewFrequency(
this.app.workspace.getActiveFile(),
ReviewFrequency.low
);
Expand All @@ -194,28 +176,41 @@ export default class SimpleNoteReviewPlugin extends Plugin {

this.addCommand({
id: "set-review-frequency-ignore",
name: "Set review frequency to none (ignore this note)",
name: "Set review frequency to none (ignore this note in all reviews)",
callback: () => {
this.service.setReviewFrequency(
this.fileService.setReviewFrequency(
this.app.workspace.getActiveFile(),
ReviewFrequency.ignore
);
},
});

this.addCommand({
id: "skip-note",
name: "Skip note from current review",
callback: () => {
this.reviewService.skipNote(
this.app.workspace.getActiveFile(),
this.settings.currentNoteSet
);
},
});
}

async activateView() {
this.app.workspace.detachLeavesOfType(SimpleNoteReviewSidebarView.VIEW_TYPE);

this.app.workspace.detachLeavesOfType(
SimpleNoteReviewSidebarView.VIEW_TYPE
);

await this.app.workspace.getRightLeaf(false).setViewState({
type: SimpleNoteReviewSidebarView.VIEW_TYPE,
active: true,
type: SimpleNoteReviewSidebarView.VIEW_TYPE,
active: true,
});

this.app.workspace.revealLeaf(
this.app.workspace.getLeavesOfType(SimpleNoteReviewSidebarView.VIEW_TYPE)[0]
this.app.workspace.getLeavesOfType(
SimpleNoteReviewSidebarView.VIEW_TYPE
)[0]
);
}


}
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "simple-note-review",
"name": "Simple Note Review",
"version": "0.9.2",
"version": "1.0.1",
"minAppVersion": "1.1.0",
"description": "Simple, customizable plugin for easy note review, resurfacing & repetition.",
"author": "dartungar",
Expand Down
Loading