generated from tpl/obsidian-sample-plugin
			Add A to Z Challenge
This commit is contained in:
		
							parent
							
								
									ffb8cc8d9c
								
							
						
					
					
						commit
						891041c965
					
				| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"id": "obsidian-book-tracker",
 | 
						"id": "obsidian-book-tracker",
 | 
				
			||||||
	"name": "Book Tracker",
 | 
						"name": "Book Tracker",
 | 
				
			||||||
	"version": "1.1.0",
 | 
						"version": "1.2.0",
 | 
				
			||||||
	"minAppVersion": "0.15.0",
 | 
						"minAppVersion": "0.15.0",
 | 
				
			||||||
	"description": "Simplifies tracking your reading progress and managing your book collection in Obsidian.",
 | 
						"description": "Simplifies tracking your reading progress and managing your book collection in Obsidian.",
 | 
				
			||||||
	"author": "FiFiTiDo",
 | 
						"author": "FiFiTiDo",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"name": "obsidian-book-tracker",
 | 
						"name": "obsidian-book-tracker",
 | 
				
			||||||
	"version": "1.1.0",
 | 
						"version": "1.2.0",
 | 
				
			||||||
	"description": "Simplifies tracking your reading progress and managing your book collection in Obsidian.",
 | 
						"description": "Simplifies tracking your reading progress and managing your book collection in Obsidian.",
 | 
				
			||||||
	"main": "main.js",
 | 
						"main": "main.js",
 | 
				
			||||||
	"scripts": {
 | 
						"scripts": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ import { CreateBookFromGoodreadsUrlCommand } from "@commands/CreateBookFromGoodr
 | 
				
			||||||
import { registerShelfCodeBlockProcessor } from "@ui/code-blocks/ShelfCodeBlock";
 | 
					import { registerShelfCodeBlockProcessor } from "@ui/code-blocks/ShelfCodeBlock";
 | 
				
			||||||
import { ReloadReadingLogCommand } from "@commands/ReloadReadingLogCommand";
 | 
					import { ReloadReadingLogCommand } from "@commands/ReloadReadingLogCommand";
 | 
				
			||||||
import { registerReadingCalendarCodeBlockProcessor } from "@ui/code-blocks/ReadingCalendarCodeBlock";
 | 
					import { registerReadingCalendarCodeBlockProcessor } from "@ui/code-blocks/ReadingCalendarCodeBlock";
 | 
				
			||||||
 | 
					import { registerAToZChallengeCodeBlockProcessor } from "@ui/code-blocks/AToZChallengeCodeBlock";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class BookTrackerPlugin extends Plugin {
 | 
					export default class BookTrackerPlugin extends Plugin {
 | 
				
			||||||
	public settings: BookTrackerPluginSettings;
 | 
						public settings: BookTrackerPluginSettings;
 | 
				
			||||||
| 
						 | 
					@ -91,6 +92,7 @@ export default class BookTrackerPlugin extends Plugin {
 | 
				
			||||||
		registerReadingStatsCodeBlockProcessor(this);
 | 
							registerReadingStatsCodeBlockProcessor(this);
 | 
				
			||||||
		registerShelfCodeBlockProcessor(this);
 | 
							registerShelfCodeBlockProcessor(this);
 | 
				
			||||||
		registerReadingCalendarCodeBlockProcessor(this);
 | 
							registerReadingCalendarCodeBlockProcessor(this);
 | 
				
			||||||
 | 
							registerAToZChallengeCodeBlockProcessor(this);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onunload() {}
 | 
						onunload() {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					import { registerCodeBlockRenderer } from ".";
 | 
				
			||||||
 | 
					import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer";
 | 
				
			||||||
 | 
					import AToZChallengeCodeBlockView from "./AToZChallengeCodeBlockView.svelte";
 | 
				
			||||||
 | 
					import type BookTrackerPlugin from "@src/main";
 | 
				
			||||||
 | 
					import z from "zod/v4";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function registerAToZChallengeCodeBlockProcessor(
 | 
				
			||||||
 | 
						plugin: BookTrackerPlugin
 | 
				
			||||||
 | 
					): void {
 | 
				
			||||||
 | 
						registerCodeBlockRenderer(
 | 
				
			||||||
 | 
							plugin,
 | 
				
			||||||
 | 
							"a-to-z-challenge",
 | 
				
			||||||
 | 
							(source, el) =>
 | 
				
			||||||
 | 
								new SvelteCodeBlockRenderer(
 | 
				
			||||||
 | 
									AToZChallengeCodeBlockView,
 | 
				
			||||||
 | 
									plugin,
 | 
				
			||||||
 | 
									source,
 | 
				
			||||||
 | 
									el
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const AToZChallengeSettingsSchema = z.object({
 | 
				
			||||||
 | 
						coverProperty: z.string(),
 | 
				
			||||||
 | 
						titleProperty: z.string(),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,194 @@
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import {
 | 
				
			||||||
 | 
							createSettings,
 | 
				
			||||||
 | 
							setSettingsContext,
 | 
				
			||||||
 | 
						} from "@ui/stores/settings.svelte";
 | 
				
			||||||
 | 
						import type { SvelteCodeBlockProps } from "./SvelteCodeBlockRenderer";
 | 
				
			||||||
 | 
						import { createMetadata } from "@ui/stores/metadata.svelte";
 | 
				
			||||||
 | 
						import { STATUS_READ } from "@src/const";
 | 
				
			||||||
 | 
						import { ALL_TIME } from "@ui/stores/date-filter.svelte";
 | 
				
			||||||
 | 
						import { AToZChallengeSettingsSchema } from "./AToZChallengeCodeBlock";
 | 
				
			||||||
 | 
						import { parseYaml, TFile } from "obsidian";
 | 
				
			||||||
 | 
						import OpenFileLink from "@ui/components/OpenFileLink.svelte";
 | 
				
			||||||
 | 
						import type { Moment } from "moment";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { plugin, source }: SvelteCodeBlockProps = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const settings = AToZChallengeSettingsSchema.parse(parseYaml(source));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const settingsStore = createSettings(plugin);
 | 
				
			||||||
 | 
						setSettingsContext(settingsStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const metadataStore = createMetadata(plugin, {
 | 
				
			||||||
 | 
							statusFilter: STATUS_READ,
 | 
				
			||||||
 | 
							initialYear: true,
 | 
				
			||||||
 | 
							disableMonthFilter: true,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const getSortValue = (value: string) => {
 | 
				
			||||||
 | 
							if (value.startsWith("A ")) {
 | 
				
			||||||
 | 
								return value.slice(2) + ", A";
 | 
				
			||||||
 | 
							} else if (value.startsWith("An ")) {
 | 
				
			||||||
 | 
								return value.slice(3) + ", An";
 | 
				
			||||||
 | 
							} else if (value.startsWith("The ")) {
 | 
				
			||||||
 | 
								return value.slice(4) + ", The";
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return value;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const items = $derived(
 | 
				
			||||||
 | 
							metadataStore.metadata.reduce(
 | 
				
			||||||
 | 
								(acc, item) => {
 | 
				
			||||||
 | 
									const title = item.frontmatter[settings.titleProperty];
 | 
				
			||||||
 | 
									const firstLetter = getSortValue(title).charAt(0).toUpperCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!firstLetter.match(/[A-Z]/)) {
 | 
				
			||||||
 | 
										return acc;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!acc[firstLetter]) {
 | 
				
			||||||
 | 
										const coverPath = item.frontmatter[
 | 
				
			||||||
 | 
											settings.coverProperty
 | 
				
			||||||
 | 
										] as string;
 | 
				
			||||||
 | 
										const coverFile = plugin.app.vault.getFileByPath(coverPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										let coverSrc: string = "";
 | 
				
			||||||
 | 
										if (coverFile) {
 | 
				
			||||||
 | 
											coverSrc = plugin.app.vault.getResourcePath(coverFile);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										const coverAlt = item.frontmatter[settings.titleProperty];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										acc[firstLetter] = {
 | 
				
			||||||
 | 
											file: item.file,
 | 
				
			||||||
 | 
											// @ts-expect-error Moment is provided by Obsidian
 | 
				
			||||||
 | 
											startDate: moment(
 | 
				
			||||||
 | 
												item.frontmatter[
 | 
				
			||||||
 | 
													settingsStore.settings.startDateProperty
 | 
				
			||||||
 | 
												],
 | 
				
			||||||
 | 
											),
 | 
				
			||||||
 | 
											// @ts-expect-error Moment is provided by Obsidian
 | 
				
			||||||
 | 
											endDate: moment(
 | 
				
			||||||
 | 
												item.frontmatter[
 | 
				
			||||||
 | 
													settingsStore.settings.endDateProperty
 | 
				
			||||||
 | 
												],
 | 
				
			||||||
 | 
											),
 | 
				
			||||||
 | 
											coverSrc,
 | 
				
			||||||
 | 
											coverAlt,
 | 
				
			||||||
 | 
										};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return acc;
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{} as Record<
 | 
				
			||||||
 | 
									string,
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										file: TFile;
 | 
				
			||||||
 | 
										startDate: Moment;
 | 
				
			||||||
 | 
										endDate: Moment;
 | 
				
			||||||
 | 
										coverSrc: string;
 | 
				
			||||||
 | 
										coverAlt: string;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								>,
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const startDate = $derived(
 | 
				
			||||||
 | 
							Object.values(items)
 | 
				
			||||||
 | 
								.map((item) => item.startDate)
 | 
				
			||||||
 | 
								.sort((a, b) => a.diff(b))[0],
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const endDate = $derived.by(() => {
 | 
				
			||||||
 | 
							const dates = Object.values(items)
 | 
				
			||||||
 | 
								.map((item) => item.endDate)
 | 
				
			||||||
 | 
								.sort((a, b) => b.diff(a));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (dates.length !== 26) {
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return dates[0];
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="reading-bingo">
 | 
				
			||||||
 | 
						<div class="top-info">
 | 
				
			||||||
 | 
							<select class="year-filter" bind:value={metadataStore.filterYear}>
 | 
				
			||||||
 | 
								{#each metadataStore.filterYears as year}
 | 
				
			||||||
 | 
									<option value={year}>{year}</option>
 | 
				
			||||||
 | 
								{/each}
 | 
				
			||||||
 | 
							</select>
 | 
				
			||||||
 | 
							<p>Started: {startDate.format("YYYY-MM-DD")}</p>
 | 
				
			||||||
 | 
							<p>Ended: {endDate?.format("YYYY-MM-DD") ?? "N/A"}</p>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="bingo">
 | 
				
			||||||
 | 
							{#each alphabet as letter}
 | 
				
			||||||
 | 
								<div class="bingo-item">
 | 
				
			||||||
 | 
									{#if items[letter]}
 | 
				
			||||||
 | 
										{@const item = items[letter]}
 | 
				
			||||||
 | 
										<OpenFileLink file={item.file}>
 | 
				
			||||||
 | 
											<img src={item.coverSrc} alt={item.coverAlt} />
 | 
				
			||||||
 | 
										</OpenFileLink>
 | 
				
			||||||
 | 
									{:else}
 | 
				
			||||||
 | 
										<div class="placeholder">{letter}</div>
 | 
				
			||||||
 | 
									{/if}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							{/each}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
						.reading-bingo {
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							flex-direction: column;
 | 
				
			||||||
 | 
							gap: var(--size-4-6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.top-info {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								align-items: center;
 | 
				
			||||||
 | 
								gap: var(--size-4-4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								p {
 | 
				
			||||||
 | 
									padding: 0;
 | 
				
			||||||
 | 
									margin: 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.bingo {
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								flex-wrap: wrap;
 | 
				
			||||||
 | 
								justify-content: center;
 | 
				
			||||||
 | 
								gap: var(--size-4-4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.bingo-item {
 | 
				
			||||||
 | 
									min-width: 150px;
 | 
				
			||||||
 | 
									max-width: 300px;
 | 
				
			||||||
 | 
									width: calc(20% - var(--size-4-4) * 2);
 | 
				
			||||||
 | 
									aspect-ratio: 2 / 3;
 | 
				
			||||||
 | 
									background-color: var(--background-secondary);
 | 
				
			||||||
 | 
									border-radius: var(--radius-l);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:has(.placeholder) {
 | 
				
			||||||
 | 
										display: grid;
 | 
				
			||||||
 | 
										place-items: center;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									.placeholder {
 | 
				
			||||||
 | 
										font-size: 2rem;
 | 
				
			||||||
 | 
										font-weight: bold;
 | 
				
			||||||
 | 
										font-style: italic;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									img {
 | 
				
			||||||
 | 
										width: 100%;
 | 
				
			||||||
 | 
										height: 100%;
 | 
				
			||||||
 | 
										object-fit: cover;
 | 
				
			||||||
 | 
										border-radius: var(--radius-l);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ export function registerReadingCalendarCodeBlockProcessor(
 | 
				
			||||||
): void {
 | 
					): void {
 | 
				
			||||||
	registerCodeBlockRenderer(
 | 
						registerCodeBlockRenderer(
 | 
				
			||||||
		plugin,
 | 
							plugin,
 | 
				
			||||||
		"readingcalendar",
 | 
							"reading-calendar",
 | 
				
			||||||
		(source, el) =>
 | 
							(source, el) =>
 | 
				
			||||||
			new SvelteCodeBlockRenderer(
 | 
								new SvelteCodeBlockRenderer(
 | 
				
			||||||
				ReadingCalendarCodeBlockView,
 | 
									ReadingCalendarCodeBlockView,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,9 @@
 | 
				
			||||||
	const settingsStore = createSettings(plugin);
 | 
						const settingsStore = createSettings(plugin);
 | 
				
			||||||
	setSettingsContext(settingsStore);
 | 
						setSettingsContext(settingsStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const metadataStore = createMetadata(plugin, null);
 | 
						const metadataStore = createMetadata(plugin, {
 | 
				
			||||||
 | 
							statusFilter: null,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
	setMetadataContext(metadataStore);
 | 
						setMetadataContext(metadataStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const readingLog = createReadingLog(plugin.readingLog);
 | 
						const readingLog = createReadingLog(plugin.readingLog);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ export function registerReadingLogCodeBlockProcessor(
 | 
				
			||||||
): void {
 | 
					): void {
 | 
				
			||||||
	registerCodeBlockRenderer(
 | 
						registerCodeBlockRenderer(
 | 
				
			||||||
		plugin,
 | 
							plugin,
 | 
				
			||||||
		"readinglog",
 | 
							"reading-log",
 | 
				
			||||||
		(source, el) =>
 | 
							(source, el) =>
 | 
				
			||||||
			new SvelteCodeBlockRenderer(
 | 
								new SvelteCodeBlockRenderer(
 | 
				
			||||||
				ReadingLogCodeBlockView,
 | 
									ReadingLogCodeBlockView,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ export function registerReadingStatsCodeBlockProcessor(
 | 
				
			||||||
): void {
 | 
					): void {
 | 
				
			||||||
	registerCodeBlockRenderer(
 | 
						registerCodeBlockRenderer(
 | 
				
			||||||
		plugin,
 | 
							plugin,
 | 
				
			||||||
		"readingstats",
 | 
							"reading-stats",
 | 
				
			||||||
		(source, el) =>
 | 
							(source, el) =>
 | 
				
			||||||
			new SvelteCodeBlockRenderer(
 | 
								new SvelteCodeBlockRenderer(
 | 
				
			||||||
				ReadingStatsCodeBlockView,
 | 
									ReadingStatsCodeBlockView,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,10 @@
 | 
				
			||||||
	const settingsStore = createSettings(plugin);
 | 
						const settingsStore = createSettings(plugin);
 | 
				
			||||||
	setSettingsContext(settingsStore);
 | 
						setSettingsContext(settingsStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const metadataStore = createMetadata(plugin, settings.statusFilter, true);
 | 
						const metadataStore = createMetadata(plugin, {
 | 
				
			||||||
 | 
							statusFilter: settings.statusFilter,
 | 
				
			||||||
 | 
							initialMonth: true,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
	setMetadataContext(metadataStore);
 | 
						setMetadataContext(metadataStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let view = $state(settings.defaultView);
 | 
						let view = $state(settings.defaultView);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,9 @@
 | 
				
			||||||
	const settingsStore = createSettings(plugin);
 | 
						const settingsStore = createSettings(plugin);
 | 
				
			||||||
	setSettingsContext(settingsStore);
 | 
						setSettingsContext(settingsStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const metadataStore = createMetadata(plugin, null);
 | 
						const metadataStore = createMetadata(plugin, {
 | 
				
			||||||
 | 
							statusFilter: null,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let editMode = $derived(entry !== undefined);
 | 
						let editMode = $derived(entry !== undefined);
 | 
				
			||||||
	let book = $state(entry?.book ?? "");
 | 
						let book = $state(entry?.book ?? "");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,17 +9,56 @@ export interface DateFilterStore {
 | 
				
			||||||
	get filterMonths(): { label: string; value: number }[];
 | 
						get filterMonths(): { label: string; value: number }[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface DateFilterStoreOptions {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * If true, the filter month will be set to the current month
 | 
				
			||||||
 | 
						 * If a number is provided, the filter month will be set to the provided month
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @default false
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						initialMonth?: boolean | number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * If true, the filter year will be set to the current year
 | 
				
			||||||
 | 
						 * If a number is provided, the filter year will be set to the provided year
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @default true
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						initialYear?: boolean | number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * If true, the month filter will be disabled
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @default false
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						disableMonthFilter?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function createDateFilter<T>(
 | 
					export function createDateFilter<T>(
 | 
				
			||||||
	data: () => T[],
 | 
						data: () => T[],
 | 
				
			||||||
	selector: (item: T) => Moment,
 | 
						selector: (item: T) => Moment,
 | 
				
			||||||
	initialMonth?: boolean
 | 
						{
 | 
				
			||||||
 | 
							initialMonth = false,
 | 
				
			||||||
 | 
							initialYear = true,
 | 
				
			||||||
 | 
							disableMonthFilter = false,
 | 
				
			||||||
 | 
						}: DateFilterStoreOptions
 | 
				
			||||||
): DateFilterStore & {
 | 
					): DateFilterStore & {
 | 
				
			||||||
	filteredData: T[];
 | 
						filteredData: T[];
 | 
				
			||||||
} {
 | 
					} {
 | 
				
			||||||
	const today = new Date();
 | 
						const today = new Date();
 | 
				
			||||||
	let filterYear: number | typeof ALL_TIME = $state(today.getFullYear());
 | 
						let filterYear: number | typeof ALL_TIME = $state(
 | 
				
			||||||
 | 
							typeof initialYear === "number"
 | 
				
			||||||
 | 
								? initialYear
 | 
				
			||||||
 | 
								: initialYear
 | 
				
			||||||
 | 
								? today.getFullYear()
 | 
				
			||||||
 | 
								: ALL_TIME
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
	let filterMonth: number | typeof ALL_TIME = $state(
 | 
						let filterMonth: number | typeof ALL_TIME = $state(
 | 
				
			||||||
		initialMonth ? today.getMonth() + 1 : ALL_TIME
 | 
							typeof initialMonth === "number"
 | 
				
			||||||
 | 
								? initialMonth
 | 
				
			||||||
 | 
								: initialMonth
 | 
				
			||||||
 | 
								? today.getMonth() + 1
 | 
				
			||||||
 | 
								: ALL_TIME
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	const filteredData = $derived.by(() => {
 | 
						const filteredData = $derived.by(() => {
 | 
				
			||||||
		return data()
 | 
							return data()
 | 
				
			||||||
| 
						 | 
					@ -30,7 +69,7 @@ export function createDateFilter<T>(
 | 
				
			||||||
						return false;
 | 
											return false;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (filterMonth !== ALL_TIME) {
 | 
										if (filterMonth !== ALL_TIME && !disableMonthFilter) {
 | 
				
			||||||
						if (date.month() !== filterMonth - 1) {
 | 
											if (date.month() !== filterMonth - 1) {
 | 
				
			||||||
							return false;
 | 
												return false;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,11 @@ import {
 | 
				
			||||||
	setSettingsContext,
 | 
						setSettingsContext,
 | 
				
			||||||
} from "./settings.svelte";
 | 
					} from "./settings.svelte";
 | 
				
			||||||
import type BookTrackerPlugin from "@src/main";
 | 
					import type BookTrackerPlugin from "@src/main";
 | 
				
			||||||
import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
 | 
					import {
 | 
				
			||||||
 | 
						createDateFilter,
 | 
				
			||||||
 | 
						type DateFilterStore,
 | 
				
			||||||
 | 
						type DateFilterStoreOptions,
 | 
				
			||||||
 | 
					} from "./date-filter.svelte";
 | 
				
			||||||
import type { ReadingState } from "@src/types";
 | 
					import type { ReadingState } from "@src/types";
 | 
				
			||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
					import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,10 +53,16 @@ function getMetadata(
 | 
				
			||||||
	return metadata;
 | 
						return metadata;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface MetadataStoreOptions extends DateFilterStoreOptions {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * The reading state to filter by
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						statusFilter?: ReadingState | null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function createMetadata(
 | 
					export function createMetadata(
 | 
				
			||||||
	plugin: BookTrackerPlugin,
 | 
						plugin: BookTrackerPlugin,
 | 
				
			||||||
	statusFilter: ReadingState | null = STATUS_READ,
 | 
						{ statusFilter = STATUS_READ, ...dateFilterOpts }: MetadataStoreOptions = {}
 | 
				
			||||||
	initialMonth?: boolean
 | 
					 | 
				
			||||||
): MetadataStore {
 | 
					): MetadataStore {
 | 
				
			||||||
	let settingsStore = getSettingsContext();
 | 
						let settingsStore = getSettingsContext();
 | 
				
			||||||
	if (!settingsStore) {
 | 
						if (!settingsStore) {
 | 
				
			||||||
| 
						 | 
					@ -97,7 +107,7 @@ export function createMetadata(
 | 
				
			||||||
				f.frontmatter[settingsStore.settings.endDateProperty]
 | 
									f.frontmatter[settingsStore.settings.endDateProperty]
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		initialMonth
 | 
							dateFilterOpts
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ export function createReadingLog(readingLog: ReadingLog): ReadingLogStore {
 | 
				
			||||||
	const dateFilter = createDateFilter(
 | 
						const dateFilter = createDateFilter(
 | 
				
			||||||
		() => entries,
 | 
							() => entries,
 | 
				
			||||||
		(entry) => entry.createdAt,
 | 
							(entry) => entry.createdAt,
 | 
				
			||||||
		true
 | 
							{ initialMonth: true }
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async function addEntry(entry: ReadingLogEntry): Promise<void> {
 | 
						async function addEntry(entry: ReadingLogEntry): Promise<void> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"1.0.0": "0.15.0",
 | 
						"1.0.0": "0.15.0",
 | 
				
			||||||
	"1.1.0": "0.15.0"
 | 
						"1.1.0": "0.15.0",
 | 
				
			||||||
 | 
						"1.2.0": "0.15.0"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue