diff --git a/src/components/ReadingLogViewer.svelte b/src/components/ReadingLogViewer.svelte new file mode 100644 index 0000000..46e9e0b --- /dev/null +++ b/src/components/ReadingLogViewer.svelte @@ -0,0 +1,135 @@ + + +
+
+ + +
+ + + + + + + + + + + + {#each filteredEntries as entry} + + + + + + + {/each} + +
DateBookPages ReadPercent Complete
{formatDate(entry.createdAt)}{entry.book}{entry.pagesRead} + {Math.round( + (entry.pagesReadTotal / + (entry.pagesReadTotal + entry.pagesRemaining)) * + 100, + )}% +
+
+ + diff --git a/src/main.ts b/src/main.ts index 73ba368..85eeb74 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,8 @@ import { import { ReadingLog, Storage } from "@utils/storage"; import { ReadingProgressModal } from "@views/reading-progress-modal"; import { RatingModal } from "@views/rating-modal"; +import { renderCodeBlockProcessor } from "@utils/svelte"; +import ReadingLogViewer from "@components/ReadingLogViewer.svelte"; export default class BookTrackerPlugin extends Plugin { settings: BookTrackerPluginSettings; @@ -62,6 +64,14 @@ export default class BookTrackerPlugin extends Plugin { }); this.addSettingTab(new BookTrackerSettingTab(this.app, this)); + + this.registerMarkdownCodeBlockProcessor( + "readinglog", + renderCodeBlockProcessor(ReadingLogViewer, { + app: this.app, + readingLog: this.readingLog, + }) + ); } onunload() {} @@ -252,7 +262,7 @@ export default class BookTrackerPlugin extends Plugin { return; } - await this.readingLog.addEntry(fileName, pageNumber); + await this.readingLog.addEntry(fileName, pageNumber, pageLength); new Notice( `Logged reading progress for ${fileName}: Page ${pageNumber} of ${pageLength}.` ); @@ -299,7 +309,11 @@ export default class BookTrackerPlugin extends Plugin { const rating = await RatingModal.createAndOpen(this.app); - await this.readingLog.addEntry(activeFile.basename, pageLength); + await this.readingLog.addEntry( + activeFile.basename, + pageLength, + pageLength + ); // @ts-expect-error Moment is provided by Obsidian const endDate = moment().format("YYYY-MM-DD"); diff --git a/src/utils/storage.ts b/src/utils/storage.ts index 9a7ebb0..5315b2c 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -37,6 +37,7 @@ interface ReadingLogEntry { readonly book: string; readonly pagesRead: number; readonly pagesReadTotal: number; + readonly pagesRemaining: number; readonly createdAt: Date; } @@ -57,7 +58,10 @@ export class ReadingLog { "reading-log.json" ); if (entries) { - this.entries = entries; + this.entries = entries.map((entry) => ({ + ...entry, + createdAt: new Date(entry.createdAt), + })); } } @@ -65,6 +69,10 @@ export class ReadingLog { await this.plugin.storage.writeJSON("reading-log.json", this.entries); } + public getEntries(): ReadingLogEntry[] { + return this.entries; + } + public getLatestEntry(book: string): ReadingLogEntry | null { const entriesForBook = this.entries.filter( (entry) => entry.book === book @@ -75,7 +83,11 @@ export class ReadingLog { : null; } - public async addEntry(book: string, pageEnded: number): Promise { + public async addEntry( + book: string, + pageEnded: number, + pageLength: number + ): Promise { const latestEntry = this.getLatestEntry(book); const newEntry: ReadingLogEntry = { @@ -84,6 +96,7 @@ export class ReadingLog { ? pageEnded - latestEntry.pagesReadTotal : pageEnded, pagesReadTotal: pageEnded, + pagesRemaining: pageLength - pageEnded, createdAt: new Date(), }; diff --git a/src/utils/svelte.ts b/src/utils/svelte.ts new file mode 100644 index 0000000..7f7f20b --- /dev/null +++ b/src/utils/svelte.ts @@ -0,0 +1,33 @@ +import type { MarkdownPostProcessorContext } from "obsidian"; +import { mount, unmount, type Component, type ComponentProps } from "svelte"; +import { MarkdownRenderChild } from "obsidian"; + +/** + * Renders a svelte component as a code block processor. + * @param component the svelte component to render. + * @param props properties forwarded to the component. + * @param stateProvider an optional provider that handles state & state updates of the code block processor. + */ +export function renderCodeBlockProcessor( + component: C, + props: ComponentProps +) { + return ( + source: string, + containerEl: HTMLElement, + ctx: MarkdownPostProcessorContext + ) => { + const svelteComponent = mount(component, { + target: containerEl, + props, + }); + + class UnloadSvelteComponent extends MarkdownRenderChild { + onunload() { + unmount(svelteComponent); + } + } + + ctx.addChild(new UnloadSvelteComponent(containerEl)); + }; +}