generated from tpl/obsidian-sample-plugin
Move commands into classes
This commit is contained in:
parent
e4a416ec2f
commit
5a492f558c
|
@ -0,0 +1,99 @@
|
||||||
|
import type {
|
||||||
|
Editor,
|
||||||
|
Hotkey,
|
||||||
|
MarkdownFileInfo,
|
||||||
|
MarkdownView,
|
||||||
|
Command as ObsidianCommand,
|
||||||
|
} from "obsidian";
|
||||||
|
|
||||||
|
export abstract class Command implements ObsidianCommand {
|
||||||
|
private _icon?: string;
|
||||||
|
public get icon(): string | undefined {
|
||||||
|
return this._icon;
|
||||||
|
}
|
||||||
|
protected setIcon(icon: string) {
|
||||||
|
this._icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _mobileOnly?: boolean;
|
||||||
|
public get mobileOnly(): boolean | undefined {
|
||||||
|
return this._mobileOnly;
|
||||||
|
}
|
||||||
|
protected setMobileOnly(mobileOnly: boolean) {
|
||||||
|
this._mobileOnly = mobileOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _repeatable?: boolean;
|
||||||
|
public get repeatable(): boolean | undefined {
|
||||||
|
return this._repeatable;
|
||||||
|
}
|
||||||
|
protected setRepeatable(repeatable: boolean) {
|
||||||
|
this._repeatable = repeatable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hotkeys: Hotkey[] = [];
|
||||||
|
public get hotkeys(): Hotkey[] {
|
||||||
|
return this._hotkeys;
|
||||||
|
}
|
||||||
|
protected addHotkey(hotkey: Hotkey) {
|
||||||
|
this._hotkeys.push(hotkey);
|
||||||
|
}
|
||||||
|
protected removeHotkey(hotkey: Hotkey) {
|
||||||
|
this._hotkeys = this._hotkeys.filter(
|
||||||
|
(h) => h.key !== hotkey.key && h.modifiers !== h.modifiers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
protected clearHotkeys() {
|
||||||
|
this._hotkeys = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(public id: string, public name: string) {}
|
||||||
|
|
||||||
|
callback?(): any;
|
||||||
|
checkCallback?(checking: boolean): boolean;
|
||||||
|
editorCallback?(
|
||||||
|
editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean;
|
||||||
|
editorCheckCallback?(
|
||||||
|
checking: boolean,
|
||||||
|
editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean | void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class CheckCommand extends Command {
|
||||||
|
checkCallback(checking: boolean): boolean {
|
||||||
|
if (!this.check()) return false;
|
||||||
|
if (!checking) {
|
||||||
|
this.run();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract check(): boolean;
|
||||||
|
protected abstract run(): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class EditorCheckCommand extends Command {
|
||||||
|
editorCheckCallback(
|
||||||
|
checking: boolean,
|
||||||
|
editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean | void {
|
||||||
|
if (!this.check(editor, ctx)) return false;
|
||||||
|
if (!checking) {
|
||||||
|
this.run(editor, ctx);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract check(
|
||||||
|
editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean;
|
||||||
|
protected abstract run(
|
||||||
|
editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): void | Promise<void>;
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
import {
|
||||||
|
type Editor,
|
||||||
|
type MarkdownView,
|
||||||
|
type MarkdownFileInfo,
|
||||||
|
type App,
|
||||||
|
Notice,
|
||||||
|
TFile,
|
||||||
|
} from "obsidian";
|
||||||
|
import { EditorCheckCommand } from "./Command";
|
||||||
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
|
import { RatingModal } from "@ui/modals";
|
||||||
|
import type { ReadingLog } from "@utils/ReadingLog";
|
||||||
|
import { READ_STATE } from "@src/const";
|
||||||
|
|
||||||
|
export class LogReadingFinishedCommand extends EditorCheckCommand {
|
||||||
|
constructor(
|
||||||
|
private readonly app: App,
|
||||||
|
private readonly readingLog: ReadingLog,
|
||||||
|
private readonly settings: BookTrackerPluginSettings
|
||||||
|
) {
|
||||||
|
super("log-reading-finished", "Log Reading Finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPageCount(file: TFile): number {
|
||||||
|
return (
|
||||||
|
(this.app.metadataCache.getFileCache(file)?.frontmatter?.[
|
||||||
|
this.settings.pageCountProperty
|
||||||
|
] as number | undefined) ?? 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected check(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean {
|
||||||
|
return !(
|
||||||
|
ctx.file === null ||
|
||||||
|
this.settings.statusProperty === "" ||
|
||||||
|
this.settings.endDateProperty === "" ||
|
||||||
|
this.settings.ratingProperty === "" ||
|
||||||
|
this.settings.pageCountProperty === "" ||
|
||||||
|
this.getPageCount(ctx.file) <= 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async run(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): Promise<void> {
|
||||||
|
const file = ctx.file!;
|
||||||
|
const fileName = file.basename;
|
||||||
|
const pageCount = this.getPageCount(file);
|
||||||
|
|
||||||
|
const rating = await RatingModal.createAndOpen(
|
||||||
|
this.app,
|
||||||
|
this.settings.spiceProperty !== ""
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.readingLog.addEntry(fileName, pageCount, pageCount);
|
||||||
|
|
||||||
|
// @ts-expect-error Moment is provided by Obsidian
|
||||||
|
const endDate = moment().format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||||
|
frontMatter[this.settings.statusProperty] = READ_STATE;
|
||||||
|
frontMatter[this.settings.endDateProperty] = endDate;
|
||||||
|
frontMatter[this.settings.ratingProperty] = rating;
|
||||||
|
if (this.settings.spiceProperty !== "") {
|
||||||
|
frontMatter[this.settings.spiceProperty] = rating;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new Notice("Reading finished for " + fileName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import {
|
||||||
|
type Editor,
|
||||||
|
type MarkdownView,
|
||||||
|
type MarkdownFileInfo,
|
||||||
|
type App,
|
||||||
|
Notice,
|
||||||
|
TFile,
|
||||||
|
} from "obsidian";
|
||||||
|
import { Command, EditorCheckCommand } from "./Command";
|
||||||
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
|
import { ReadingProgressModal } from "@ui/modals";
|
||||||
|
import type { ReadingLog } from "@utils/ReadingLog";
|
||||||
|
|
||||||
|
export class LogReadingProgressCommand extends EditorCheckCommand {
|
||||||
|
constructor(
|
||||||
|
private readonly app: App,
|
||||||
|
private readonly readingLog: ReadingLog,
|
||||||
|
private readonly settings: BookTrackerPluginSettings
|
||||||
|
) {
|
||||||
|
super("log-reading-progress", "Log Reading Progress");
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPageCount(file: TFile): number {
|
||||||
|
return (
|
||||||
|
(this.app.metadataCache.getFileCache(file)?.frontmatter?.[
|
||||||
|
this.settings.pageCountProperty
|
||||||
|
] as number | undefined) ?? 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected check(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean {
|
||||||
|
return !(
|
||||||
|
ctx.file === null ||
|
||||||
|
this.settings.pageCountProperty === "" ||
|
||||||
|
this.getPageCount(ctx.file) <= 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async run(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): Promise<void> {
|
||||||
|
const file = ctx.file!;
|
||||||
|
const fileName = file.basename;
|
||||||
|
const pageCount = this.getPageCount(file);
|
||||||
|
const pageNumber = await ReadingProgressModal.createAndOpen(
|
||||||
|
this.app,
|
||||||
|
pageCount
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pageNumber <= 0 || pageNumber > pageCount) {
|
||||||
|
new Notice(
|
||||||
|
`Invalid page number: ${pageNumber}. It must be between 1 and ${pageCount}.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.readingLog.addEntry(fileName, pageNumber, pageCount);
|
||||||
|
new Notice(
|
||||||
|
`Logged reading progress for ${fileName}: Page ${pageNumber} of ${pageCount}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {
|
||||||
|
type Editor,
|
||||||
|
type MarkdownView,
|
||||||
|
type MarkdownFileInfo,
|
||||||
|
type App,
|
||||||
|
Notice,
|
||||||
|
} from "obsidian";
|
||||||
|
import { EditorCheckCommand } from "./Command";
|
||||||
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
|
import { IN_PROGRESS_STATE } from "@src/const";
|
||||||
|
|
||||||
|
export class LogReadingStartedCommand extends EditorCheckCommand {
|
||||||
|
constructor(
|
||||||
|
private readonly app: App,
|
||||||
|
private readonly settings: BookTrackerPluginSettings
|
||||||
|
) {
|
||||||
|
super("log-reading-started", "Log Reading Started");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected check(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean {
|
||||||
|
return !(
|
||||||
|
ctx.file === null ||
|
||||||
|
this.settings.statusProperty === "" ||
|
||||||
|
this.settings.startDateProperty === ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async run(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): Promise<void> {
|
||||||
|
const file = ctx.file!;
|
||||||
|
|
||||||
|
// @ts-expect-error Moment is provided by Obsidian
|
||||||
|
const startDate = moment().format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||||
|
frontMatter[this.settings.statusProperty] = IN_PROGRESS_STATE;
|
||||||
|
frontMatter[this.settings.startDateProperty] = startDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
new Notice("Reading started for " + file.basename);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
import {
|
||||||
|
Notice,
|
||||||
|
type App,
|
||||||
|
type Editor,
|
||||||
|
type MarkdownFileInfo,
|
||||||
|
type MarkdownView,
|
||||||
|
} from "obsidian";
|
||||||
|
import { Command, EditorCheckCommand } from "./Command";
|
||||||
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
|
import type { ReadingLog } from "@utils/ReadingLog";
|
||||||
|
import { TO_BE_READ_STATE } from "@src/const";
|
||||||
|
|
||||||
|
export class ResetReadingStatusCommand extends EditorCheckCommand {
|
||||||
|
constructor(
|
||||||
|
private readonly app: App,
|
||||||
|
private readonly readingLog: ReadingLog,
|
||||||
|
private readonly settings: BookTrackerPluginSettings
|
||||||
|
) {
|
||||||
|
super("reset-reading-status", "Reset Reading Status");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected check(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): boolean {
|
||||||
|
return !(
|
||||||
|
ctx.file === null ||
|
||||||
|
this.settings.statusProperty === "" ||
|
||||||
|
this.settings.startDateProperty === "" ||
|
||||||
|
this.settings.endDateProperty === ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected run(
|
||||||
|
_editor: Editor,
|
||||||
|
ctx: MarkdownView | MarkdownFileInfo
|
||||||
|
): void | Promise<void> {
|
||||||
|
const file = ctx.file!;
|
||||||
|
|
||||||
|
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||||
|
frontMatter[this.settings.statusProperty] = TO_BE_READ_STATE;
|
||||||
|
frontMatter[this.settings.startDateProperty] = "";
|
||||||
|
frontMatter[this.settings.endDateProperty] = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
this.readingLog.removeEntries(file.basename);
|
||||||
|
|
||||||
|
new Notice("Reading status reset for " + file.basename);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { getBookByLegacyId, type SearchResult } from "@data-sources/goodreads";
|
||||||
|
import { GoodreadsSearchModal, GoodreadsSearchSuggestModal } from "@ui/modals";
|
||||||
|
import { App, Notice } from "obsidian";
|
||||||
|
import { Command } from "./Command";
|
||||||
|
import type { Book } from "@src/types";
|
||||||
|
|
||||||
|
export class SearchGoodreadsCommand extends Command {
|
||||||
|
constructor(
|
||||||
|
private readonly app: App,
|
||||||
|
private readonly cb: (book: Book) => void
|
||||||
|
) {
|
||||||
|
super("search-goodreads", "Search Goodreads");
|
||||||
|
}
|
||||||
|
|
||||||
|
async callback() {
|
||||||
|
let results: SearchResult[];
|
||||||
|
try {
|
||||||
|
results = await GoodreadsSearchModal.createAndOpen(this.app);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to search Goodreads:", error);
|
||||||
|
new Notice(
|
||||||
|
"Failed to search Goodreads. Check console for details."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedResult = await GoodreadsSearchSuggestModal.createAndOpen(
|
||||||
|
this.app,
|
||||||
|
results
|
||||||
|
);
|
||||||
|
if (!selectedResult) {
|
||||||
|
new Notice("No book selected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let book: Book;
|
||||||
|
try {
|
||||||
|
book = await getBookByLegacyId(selectedResult.legacyId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get book:", error);
|
||||||
|
new Notice("Failed to get book. Check console for details.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cb(book);
|
||||||
|
}
|
||||||
|
}
|
283
src/main.ts
283
src/main.ts
|
@ -4,23 +4,17 @@ import {
|
||||||
DEFAULT_SETTINGS,
|
DEFAULT_SETTINGS,
|
||||||
BookTrackerSettingTab,
|
BookTrackerSettingTab,
|
||||||
} from "@ui/settings";
|
} from "@ui/settings";
|
||||||
import { getBookByLegacyId, type SearchResult } from "@data-sources/goodreads";
|
|
||||||
import { Templater } from "@utils/Templater";
|
import { Templater } from "@utils/Templater";
|
||||||
import {
|
import { CONTENT_TYPE_EXTENSIONS } from "./const";
|
||||||
GoodreadsSearchModal,
|
|
||||||
GoodreadsSearchSuggestModal,
|
|
||||||
ReadingProgressModal,
|
|
||||||
RatingModal,
|
|
||||||
} from "@ui/modals";
|
|
||||||
import {
|
|
||||||
CONTENT_TYPE_EXTENSIONS,
|
|
||||||
IN_PROGRESS_STATE,
|
|
||||||
READ_STATE,
|
|
||||||
TO_BE_READ_STATE,
|
|
||||||
} from "./const";
|
|
||||||
import { Storage } from "@utils/Storage";
|
import { Storage } from "@utils/Storage";
|
||||||
import { ReadingLog } from "@utils/ReadingLog";
|
import { ReadingLog } from "@utils/ReadingLog";
|
||||||
import { registerReadingLogCodeBlockProcessor } from "@ui/code-blocks";
|
import { registerReadingLogCodeBlockProcessor } from "@ui/code-blocks";
|
||||||
|
import type { Book } from "./types";
|
||||||
|
import { SearchGoodreadsCommand } from "@commands/SearchGoodreadsCommand";
|
||||||
|
import { LogReadingStartedCommand } from "@commands/LogReadingStartedCommand";
|
||||||
|
import { LogReadingProgressCommand } from "@commands/LogReadingProgressCommand";
|
||||||
|
import { LogReadingFinishedCommand } from "@commands/LogReadingFinishedCommand";
|
||||||
|
import { ResetReadingStatusCommand } from "@commands/ResetReadingStatusCommand";
|
||||||
|
|
||||||
export default class BookTrackerPlugin extends Plugin {
|
export default class BookTrackerPlugin extends Plugin {
|
||||||
settings: BookTrackerPluginSettings;
|
settings: BookTrackerPluginSettings;
|
||||||
|
@ -35,35 +29,31 @@ export default class BookTrackerPlugin extends Plugin {
|
||||||
this.storage = new Storage(this.app, this);
|
this.storage = new Storage(this.app, this);
|
||||||
this.readingLog = new ReadingLog(this.storage);
|
this.readingLog = new ReadingLog(this.storage);
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand(
|
||||||
id: "search-goodreads",
|
new SearchGoodreadsCommand(this.app, this.createEntry.bind(this))
|
||||||
name: "Search Goodreads",
|
);
|
||||||
callback: () => this.searchGoodreads(),
|
this.addCommand(new LogReadingStartedCommand(this.app, this.settings));
|
||||||
});
|
this.addCommand(
|
||||||
|
new LogReadingProgressCommand(
|
||||||
this.addCommand({
|
this.app,
|
||||||
id: "log-reading-started",
|
this.readingLog,
|
||||||
name: "Log Reading Started",
|
this.settings
|
||||||
callback: () => this.logReadingStarted(),
|
)
|
||||||
});
|
);
|
||||||
|
this.addCommand(
|
||||||
this.addCommand({
|
new LogReadingFinishedCommand(
|
||||||
id: "log-reading-progress",
|
this.app,
|
||||||
name: "Log Reading Progress",
|
this.readingLog,
|
||||||
callback: () => this.logReadingProgress(),
|
this.settings
|
||||||
});
|
)
|
||||||
|
);
|
||||||
this.addCommand({
|
this.addCommand(
|
||||||
id: "log-reading-completed",
|
new ResetReadingStatusCommand(
|
||||||
name: "Log Reading Completed",
|
this.app,
|
||||||
callback: () => this.logReadingFinished(),
|
this.readingLog,
|
||||||
});
|
this.settings
|
||||||
|
)
|
||||||
this.addCommand({
|
);
|
||||||
id: "reset-reading-status",
|
|
||||||
name: "Reset Reading Status",
|
|
||||||
callback: () => this.resetReadingStatus(),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addSettingTab(new BookTrackerSettingTab(this));
|
this.addSettingTab(new BookTrackerSettingTab(this));
|
||||||
|
|
||||||
|
@ -124,10 +114,8 @@ export default class BookTrackerPlugin extends Plugin {
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntryFromGoodreads(legacyId: number): Promise<void> {
|
async createEntry(book: Book): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const book = await getBookByLegacyId(legacyId);
|
|
||||||
|
|
||||||
const fileName = this.templater
|
const fileName = this.templater
|
||||||
.renderTemplate(this.settings.fileNameFormat, {
|
.renderTemplate(this.settings.fileNameFormat, {
|
||||||
title: book.title,
|
title: book.title,
|
||||||
|
@ -159,209 +147,4 @@ export default class BookTrackerPlugin extends Plugin {
|
||||||
console.error("Failed to create book entry:", error);
|
console.error("Failed to create book entry:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchGoodreads(): Promise<void> {
|
|
||||||
let results: SearchResult[];
|
|
||||||
try {
|
|
||||||
results = await GoodreadsSearchModal.createAndOpen(this.app);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to search Goodreads:", error);
|
|
||||||
new Notice(
|
|
||||||
"Failed to search Goodreads. Check console for details."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const selectedBook = await GoodreadsSearchSuggestModal.createAndOpen(
|
|
||||||
this.app,
|
|
||||||
results
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selectedBook) {
|
|
||||||
await this.createEntryFromGoodreads(selectedBook.legacyId);
|
|
||||||
} else {
|
|
||||||
new Notice("No book selected.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logReadingStarted(): void {
|
|
||||||
const activeFile = this.app.workspace.getActiveFile();
|
|
||||||
if (!activeFile) {
|
|
||||||
new Notice("No active file to mark as currently reading.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeFile.extension !== "md") {
|
|
||||||
new Notice("Active file is not a markdown file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.statusProperty) {
|
|
||||||
new Notice("Status property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.startDateProperty) {
|
|
||||||
new Notice("Start date property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-expect-error Moment is provided by Obsidian
|
|
||||||
const startDate = moment().format("YYYY-MM-DD");
|
|
||||||
|
|
||||||
this.app.fileManager.processFrontMatter(activeFile, (frontMatter) => {
|
|
||||||
frontMatter[this.settings.statusProperty] = IN_PROGRESS_STATE;
|
|
||||||
frontMatter[this.settings.startDateProperty] = startDate;
|
|
||||||
});
|
|
||||||
|
|
||||||
new Notice("Reading started for " + activeFile.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async logReadingProgress() {
|
|
||||||
const activeFile = this.app.workspace.getActiveFile();
|
|
||||||
if (!activeFile) {
|
|
||||||
new Notice("No active file to log reading progress.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeFile.extension !== "md") {
|
|
||||||
new Notice("Active file is not a markdown file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = activeFile.basename;
|
|
||||||
if (!this.settings.pageCountProperty) {
|
|
||||||
new Notice("Page count property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageCount =
|
|
||||||
(this.app.metadataCache.getFileCache(activeFile)?.frontmatter?.[
|
|
||||||
this.settings.pageCountProperty
|
|
||||||
] as number | undefined) ?? 0;
|
|
||||||
|
|
||||||
if (pageCount <= 0) {
|
|
||||||
new Notice(
|
|
||||||
"Page length property is not set or is invalid in the active file."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageNumber = await ReadingProgressModal.createAndOpen(
|
|
||||||
this.app,
|
|
||||||
pageCount
|
|
||||||
);
|
|
||||||
|
|
||||||
if (pageNumber <= 0 || pageNumber > pageCount) {
|
|
||||||
new Notice(
|
|
||||||
`Invalid page number: ${pageNumber}. It must be between 1 and ${pageCount}.`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.readingLog.addEntry(fileName, pageNumber, pageCount);
|
|
||||||
new Notice(
|
|
||||||
`Logged reading progress for ${fileName}: Page ${pageNumber} of ${pageCount}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async logReadingFinished() {
|
|
||||||
const activeFile = this.app.workspace.getActiveFile();
|
|
||||||
if (!activeFile) {
|
|
||||||
new Notice("No active file to mark as finished reading.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeFile.extension !== "md") {
|
|
||||||
new Notice("Active file is not a markdown file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.statusProperty) {
|
|
||||||
new Notice("Status property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.endDateProperty) {
|
|
||||||
new Notice("End date property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.ratingProperty) {
|
|
||||||
new Notice("Rating property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageCount =
|
|
||||||
(this.app.metadataCache.getFileCache(activeFile)?.frontmatter?.[
|
|
||||||
this.settings.pageCountProperty
|
|
||||||
] as number | undefined) ?? 0;
|
|
||||||
|
|
||||||
if (pageCount <= 0) {
|
|
||||||
new Notice(
|
|
||||||
"Page count property is not set or is invalid in the active file."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rating = await RatingModal.createAndOpen(
|
|
||||||
this.app,
|
|
||||||
this.settings.spiceProperty !== ""
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.readingLog.addEntry(
|
|
||||||
activeFile.basename,
|
|
||||||
pageCount,
|
|
||||||
pageCount
|
|
||||||
);
|
|
||||||
|
|
||||||
// @ts-expect-error Moment is provided by Obsidian
|
|
||||||
const endDate = moment().format("YYYY-MM-DD");
|
|
||||||
|
|
||||||
this.app.fileManager.processFrontMatter(activeFile, (frontMatter) => {
|
|
||||||
frontMatter[this.settings.statusProperty] = READ_STATE;
|
|
||||||
frontMatter[this.settings.endDateProperty] = endDate;
|
|
||||||
frontMatter[this.settings.ratingProperty] = rating;
|
|
||||||
if (this.settings.spiceProperty !== "") {
|
|
||||||
frontMatter[this.settings.spiceProperty] = rating;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
new Notice("Reading finished for " + activeFile.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetReadingStatus(): any {
|
|
||||||
const activeFile = this.app.workspace.getActiveFile();
|
|
||||||
if (!activeFile) {
|
|
||||||
new Notice("No active file to reset reading status.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeFile.extension !== "md") {
|
|
||||||
new Notice("Active file is not a markdown file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.statusProperty) {
|
|
||||||
new Notice("Status property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.startDateProperty) {
|
|
||||||
new Notice("Start date property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.settings.endDateProperty) {
|
|
||||||
new Notice("End date property is not set in settings.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.app.fileManager.processFrontMatter(activeFile, (frontMatter) => {
|
|
||||||
frontMatter[this.settings.statusProperty] = TO_BE_READ_STATE;
|
|
||||||
frontMatter[this.settings.startDateProperty] = "";
|
|
||||||
frontMatter[this.settings.endDateProperty] = "";
|
|
||||||
});
|
|
||||||
|
|
||||||
new Notice("Reading status reset for " + activeFile.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"ES7"
|
"ES7"
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@commands/*": ["src/commands/*"],
|
||||||
"@data-sources/*": ["src/data-sources/*"],
|
"@data-sources/*": ["src/data-sources/*"],
|
||||||
"@ui/*": ["src/ui/*"],
|
"@ui/*": ["src/ui/*"],
|
||||||
"@utils/*": ["src/utils/*"],
|
"@utils/*": ["src/utils/*"],
|
||||||
|
|
Loading…
Reference in New Issue