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 @@
+
+
+
+
+
+
+
+
+
+
+
+ Date |
+ Book |
+ Pages Read |
+ Percent Complete |
+
+
+
+ {#each filteredEntries as entry}
+
+ {formatDate(entry.createdAt)} |
+ {entry.book} |
+ {entry.pagesRead} |
+
+ {Math.round(
+ (entry.pagesReadTotal /
+ (entry.pagesReadTotal + entry.pagesRemaining)) *
+ 100,
+ )}%
+ |
+
+ {/each}
+
+
+
+
+
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));
+ };
+}