generated from tpl/obsidian-sample-plugin
			Use reading log for books and pages chart when filtering by month
This commit is contained in:
		
							parent
							
								
									d9cfb3df36
								
							
						
					
					
						commit
						31cfa881c7
					
				| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
		plugin: BookTrackerPlugin;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let { plugin }: Props = $props();
 | 
			
		||||
	const { plugin }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	function bookUri(book: string) {
 | 
			
		||||
		const v = encodeURIComponent(plugin.app.vault.getName());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,20 +23,32 @@
 | 
			
		|||
	import type BookTrackerPlugin from "@src/main";
 | 
			
		||||
	import BookCountStat from "@ui/components/stats/BookCountStat.svelte";
 | 
			
		||||
	import { ALL_TIME } from "@ui/stores/date-filter.svelte";
 | 
			
		||||
	import {
 | 
			
		||||
		createReadingLog,
 | 
			
		||||
		setReadingLogContext,
 | 
			
		||||
	} from "@ui/stores/reading-log.svelte";
 | 
			
		||||
 | 
			
		||||
	interface Props {
 | 
			
		||||
		plugin: BookTrackerPlugin;
 | 
			
		||||
		source: string;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let { plugin, source }: Props = $props();
 | 
			
		||||
	const { plugin, source }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	let settingsStore = createSettings(plugin);
 | 
			
		||||
	const settingsStore = createSettings(plugin);
 | 
			
		||||
	setSettingsContext(settingsStore);
 | 
			
		||||
 | 
			
		||||
	let metadataStore = createMetadata(plugin);
 | 
			
		||||
	const metadataStore = createMetadata(plugin);
 | 
			
		||||
	setMetadataContext(metadataStore);
 | 
			
		||||
 | 
			
		||||
	const readingLogStore = createReadingLog(plugin.readingLog);
 | 
			
		||||
	setReadingLogContext(readingLogStore);
 | 
			
		||||
 | 
			
		||||
	$effect(() => {
 | 
			
		||||
		readingLogStore.filterYear = metadataStore.filterYear;
 | 
			
		||||
		readingLogStore.filterMonth = metadataStore.filterMonth;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	let sections = $state<ReadingStatsSection[]>([]);
 | 
			
		||||
	let error = $state<string | null>(null);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +80,10 @@
 | 
			
		|||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	onDestroy(() => metadataStore.destroy());
 | 
			
		||||
	onDestroy(() => {
 | 
			
		||||
		metadataStore.destroy();
 | 
			
		||||
		readingLogStore.destroy();
 | 
			
		||||
	});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="obt-reading-stats">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@
 | 
			
		|||
		color?: "rainbow" | ColorName;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let {
 | 
			
		||||
	const {
 | 
			
		||||
		property,
 | 
			
		||||
		horizontal,
 | 
			
		||||
		sortByLabel = false,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,28 +2,46 @@
 | 
			
		|||
	import { chart } from "@ui/directives/chart";
 | 
			
		||||
	import { ALL_TIME } from "@ui/stores/date-filter.svelte";
 | 
			
		||||
	import { getMetadataContext } from "@ui/stores/metadata.svelte";
 | 
			
		||||
	import { getReadingLogContext } from "@ui/stores/reading-log.svelte";
 | 
			
		||||
	import { getSettingsContext } from "@ui/stores/settings.svelte";
 | 
			
		||||
	import { Color } from "@utils/color";
 | 
			
		||||
	import type { ChartConfiguration } from "chart.js";
 | 
			
		||||
 | 
			
		||||
	const settings = getSettingsContext().settings;
 | 
			
		||||
	const settingsStore = getSettingsContext();
 | 
			
		||||
	const store = getMetadataContext();
 | 
			
		||||
	const config = $derived.by(() => {
 | 
			
		||||
		const items = store.metadata.map((f) => ({
 | 
			
		||||
			pageCount: f.frontmatter[settings.pageCountProperty],
 | 
			
		||||
			date: f.frontmatter[settings.endDateProperty],
 | 
			
		||||
		}));
 | 
			
		||||
	const readingLog = getReadingLogContext();
 | 
			
		||||
 | 
			
		||||
	const isMonthly = $derived(
 | 
			
		||||
		store.filterYear !== ALL_TIME && store.filterMonth !== ALL_TIME,
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	const items = $derived(
 | 
			
		||||
		isMonthly
 | 
			
		||||
			? readingLog.entries.map((entry) => ({
 | 
			
		||||
					pageCount: entry.pagesRead,
 | 
			
		||||
					date: entry.createdAt,
 | 
			
		||||
				}))
 | 
			
		||||
			: store.metadata.map((f) => ({
 | 
			
		||||
					pageCount:
 | 
			
		||||
						f.frontmatter[settingsStore.settings.pageCountProperty],
 | 
			
		||||
					// @ts-expect-error Moment is provided by Obsidian
 | 
			
		||||
					date: moment(
 | 
			
		||||
						f.frontmatter[settingsStore.settings.endDateProperty],
 | 
			
		||||
					),
 | 
			
		||||
				})),
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	const config = $derived.by(() => {
 | 
			
		||||
		const books = new Map<number, number>();
 | 
			
		||||
		const pages = new Map<number, number>();
 | 
			
		||||
		for (const item of items) {
 | 
			
		||||
			// @ts-expect-error Moment is provided by Obsidian
 | 
			
		||||
			const date = moment(item.date);
 | 
			
		||||
			let key: number;
 | 
			
		||||
			if (store.filterYear === ALL_TIME) {
 | 
			
		||||
				key = date.year();
 | 
			
		||||
				key = item.date.year();
 | 
			
		||||
			} else if (store.filterMonth === ALL_TIME) {
 | 
			
		||||
				key = item.date.month();
 | 
			
		||||
			} else {
 | 
			
		||||
				key = date.month();
 | 
			
		||||
				key = item.date.date();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const pageCount = pages.get(key) ?? 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,10 +51,26 @@
 | 
			
		|||
			books.set(key, bookCount + 1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		console.log(pages);
 | 
			
		||||
		if (isMonthly && typeof store.filterMonth === "number") {
 | 
			
		||||
			// @ts-expect-error Moment is provided by Obsidian
 | 
			
		||||
			const daysInMonth = moment()
 | 
			
		||||
				.month(store.filterMonth - 1)
 | 
			
		||||
				.daysInMonth();
 | 
			
		||||
 | 
			
		||||
			for (let i = 1; i <= daysInMonth; i++) {
 | 
			
		||||
				if (!pages.has(i)) {
 | 
			
		||||
					books.set(i, 0);
 | 
			
		||||
					pages.set(i, 0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		console.log(pages);
 | 
			
		||||
 | 
			
		||||
		const labels = Array.from(books.keys())
 | 
			
		||||
			.sort((a, b) => a - b)
 | 
			
		||||
			.map((key) =>
 | 
			
		||||
				store.filterYear === ALL_TIME
 | 
			
		||||
				store.filterYear === ALL_TIME || isMonthly
 | 
			
		||||
					? key
 | 
			
		||||
					: // @ts-expect-error Moment is provided by Obsidian
 | 
			
		||||
						moment().month(key).format("MMM"),
 | 
			
		||||
| 
						 | 
				
			
			@ -48,26 +82,30 @@
 | 
			
		|||
			.sort((a, b) => a[0] - b[0])
 | 
			
		||||
			.map((p) => p[1]);
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			type: "line",
 | 
			
		||||
			data: {
 | 
			
		||||
				labels,
 | 
			
		||||
				datasets: [
 | 
			
		||||
					{
 | 
			
		||||
						label: "Books",
 | 
			
		||||
						data: sortedBooks,
 | 
			
		||||
						borderColor: Color.fromName("red").hex,
 | 
			
		||||
						backgroundColor: Color.fromName("red").alpha(0.5).rgba,
 | 
			
		||||
						yAxisID: "y",
 | 
			
		||||
					},
 | 
			
		||||
		let datasets = [
 | 
			
		||||
			{
 | 
			
		||||
				label: "Pages",
 | 
			
		||||
				data: sortedPages,
 | 
			
		||||
				borderColor: Color.fromName("blue").hex,
 | 
			
		||||
				backgroundColor: Color.fromName("blue").alpha(0.5).rgba,
 | 
			
		||||
						yAxisID: "y1",
 | 
			
		||||
				yAxisID: isMonthly ? "y" : "y1",
 | 
			
		||||
			},
 | 
			
		||||
				],
 | 
			
		||||
		];
 | 
			
		||||
		if (!isMonthly) {
 | 
			
		||||
			datasets.push({
 | 
			
		||||
				label: "Books",
 | 
			
		||||
				data: sortedBooks,
 | 
			
		||||
				borderColor: Color.fromName("red").hex,
 | 
			
		||||
				backgroundColor: Color.fromName("red").alpha(0.5).rgba,
 | 
			
		||||
				yAxisID: "y",
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			type: "line",
 | 
			
		||||
			data: {
 | 
			
		||||
				labels,
 | 
			
		||||
				datasets,
 | 
			
		||||
			},
 | 
			
		||||
			options: {
 | 
			
		||||
				scales: {
 | 
			
		||||
| 
						 | 
				
			
			@ -75,16 +113,13 @@
 | 
			
		|||
						type: "linear",
 | 
			
		||||
						display: true,
 | 
			
		||||
						position: "left",
 | 
			
		||||
						ticks: { beginAtZero: true },
 | 
			
		||||
					},
 | 
			
		||||
					y1: {
 | 
			
		||||
						type: "linear",
 | 
			
		||||
						display: true,
 | 
			
		||||
						display: !isMonthly,
 | 
			
		||||
						position: "right",
 | 
			
		||||
 | 
			
		||||
						// grid line settings
 | 
			
		||||
						grid: {
 | 
			
		||||
							drawOnChartArea: false, // only want the grid lines for one axis to show up
 | 
			
		||||
						},
 | 
			
		||||
						grid: { drawOnChartArea: false },
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@
 | 
			
		|||
		color?: PieChartColor;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let { property, groups, unit, unitPlural, responsive, color }: Props =
 | 
			
		||||
	const { property, groups, unit, unitPlural, responsive, color }: Props =
 | 
			
		||||
		$props();
 | 
			
		||||
 | 
			
		||||
	const store = createPropertyStore(property);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,7 @@
 | 
			
		|||
		property: string;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let { label, property }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	const { label, property }: Props = $props();
 | 
			
		||||
	const store = createPropertyStore(property);
 | 
			
		||||
	const avg = $derived.by(() => {
 | 
			
		||||
		if (store.propertyData.length === 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,8 +6,7 @@
 | 
			
		|||
		label: string;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let { label }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	const { label }: Props = $props();
 | 
			
		||||
	const store = getMetadataContext();
 | 
			
		||||
	const count = $derived(store.metadata.length);
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,7 @@
 | 
			
		|||
		property: string;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let { label, property }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	const { label, property }: Props = $props();
 | 
			
		||||
	const store = createPropertyStore(property);
 | 
			
		||||
	const count = $derived(store.propertyData.length);
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,10 @@
 | 
			
		|||
<script lang="ts">
 | 
			
		||||
	import type { Readable } from "svelte/store";
 | 
			
		||||
 | 
			
		||||
	type Props = {
 | 
			
		||||
		label: string;
 | 
			
		||||
		value: number;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let { label, value }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	const { label, value }: Props = $props();
 | 
			
		||||
	const numberFormatter = new Intl.NumberFormat("en-US", {
 | 
			
		||||
		maximumFractionDigits: 2,
 | 
			
		||||
	});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,7 @@
 | 
			
		|||
		property: string;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	let { label, property }: Props = $props();
 | 
			
		||||
 | 
			
		||||
	const { label, property }: Props = $props();
 | 
			
		||||
	const store = createPropertyStore(property);
 | 
			
		||||
	const total = $derived(
 | 
			
		||||
		store.propertyData.reduce((acc, f) => acc + f.value, 0),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,6 +63,9 @@ export function chart<
 | 
			
		|||
				| ChartConfigurationCustomTypesPerDataset<Type, Data, Label>
 | 
			
		||||
		) => {
 | 
			
		||||
			chart.data = config.data;
 | 
			
		||||
			if (config.options) {
 | 
			
		||||
				chart.options = config.options;
 | 
			
		||||
			}
 | 
			
		||||
			chart.update();
 | 
			
		||||
		},
 | 
			
		||||
		destroy: () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ export function createDateFilter<T>(
 | 
			
		|||
		data().forEach((item) => {
 | 
			
		||||
			years.add(selector(item).year());
 | 
			
		||||
		});
 | 
			
		||||
		return Array.from(years).sort((a, b) => a - b);
 | 
			
		||||
		return Array.from(years).sort((a, b) => b - a);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const filterMonths = $derived.by(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue