diff --git a/src/commands/CreateReadingLogBackupCommand.ts b/src/commands/CreateReadingLogBackupCommand.ts index 82776be..6b4e5ae 100644 --- a/src/commands/CreateReadingLogBackupCommand.ts +++ b/src/commands/CreateReadingLogBackupCommand.ts @@ -1,5 +1,6 @@ import type { ReadingLog } from "@utils/ReadingLog"; import { Command } from "./Command"; +import moment from "@external/moment"; export class BackupReadingLogCommand extends Command { constructor(private readonly readingLog: ReadingLog) { @@ -7,7 +8,6 @@ export class BackupReadingLogCommand extends Command { } async callback() { - // @ts-expect-error Moment is provided by Obsidian const timestamp = moment().format("YYYY-MM-DD_HH-mm-ss"); const backupFilename = `reading-log-backup_${timestamp}.json`; await this.readingLog.save(backupFilename); diff --git a/src/commands/LogReadingFinishedCommand.ts b/src/commands/LogReadingFinishedCommand.ts index 4824c8d..45830d6 100644 --- a/src/commands/LogReadingFinishedCommand.ts +++ b/src/commands/LogReadingFinishedCommand.ts @@ -2,32 +2,23 @@ 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 { STATUS_READ } from "@src/const"; import { mkdirRecursive, dirname } from "@utils/fs"; +import moment from "@external/moment"; +import type BookTrackerPlugin from "@src/main"; export class LogReadingFinishedCommand extends EditorCheckCommand { - constructor( - private readonly app: App, - private readonly readingLog: ReadingLog, - private readonly settings: BookTrackerPluginSettings - ) { + constructor(private readonly plugin: BookTrackerPlugin) { 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 - ); + return this.plugin.getBookMetadata(file)?.pageCount ?? 0; } protected check( @@ -36,10 +27,10 @@ export class LogReadingFinishedCommand extends EditorCheckCommand { ): boolean { return !( ctx.file === null || - this.settings.statusProperty === "" || - this.settings.endDateProperty === "" || - this.settings.ratingProperty === "" || - this.settings.pageCountProperty === "" || + this.plugin.settings.statusProperty === "" || + this.plugin.settings.endDateProperty === "" || + this.plugin.settings.ratingProperty === "" || + this.plugin.settings.pageCountProperty === "" || this.getPageCount(ctx.file) <= 0 ); } @@ -53,12 +44,16 @@ export class LogReadingFinishedCommand extends EditorCheckCommand { const pageCount = this.getPageCount(file); const ratings = await RatingModal.createAndOpen( - this.app, - this.settings.spiceProperty !== "" + this.plugin.app, + this.plugin.settings.spiceProperty !== "" ); try { - await this.readingLog.createEntry(fileName, pageCount, pageCount); + await this.plugin.readingLog.createEntry( + fileName, + pageCount, + pageCount + ); } catch (error) { new Notice( `Failed to log reading progress for ${fileName}: ${error}` @@ -66,25 +61,23 @@ export class LogReadingFinishedCommand extends EditorCheckCommand { return; } - // @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] = STATUS_READ; - frontMatter[this.settings.endDateProperty] = endDate; - frontMatter[this.settings.ratingProperty] = ratings.rating; - if (this.settings.spiceProperty !== "") { - frontMatter[this.settings.spiceProperty] = ratings.spice; + this.plugin.app.fileManager.processFrontMatter(file, (fm) => { + fm[this.plugin.settings.statusProperty] = STATUS_READ; + fm[this.plugin.settings.endDateProperty] = endDate; + fm[this.plugin.settings.ratingProperty] = ratings.rating; + if (this.plugin.settings.spiceProperty !== "") { + fm[this.plugin.settings.spiceProperty] = ratings.spice; } }); - if (this.settings.organizeReadBooks) { - // @ts-expect-error Moment is provided by Obsidian + if (this.plugin.settings.organizeReadBooks) { const datePath = moment().format("YYYY/MMMM"); - const newPath = `${this.settings.readBooksFolder}/${datePath}/${file.name}`; + const newPath = `${this.plugin.settings.readBooksFolder}/${datePath}/${file.name}`; - await mkdirRecursive(this.app.vault, dirname(newPath)); - await this.app.vault.rename(file, newPath); + await mkdirRecursive(this.plugin.app.vault, dirname(newPath)); + await this.plugin.app.vault.rename(file, newPath); } new Notice("Reading finished for " + fileName); diff --git a/src/commands/LogReadingProgressCommand.ts b/src/commands/LogReadingProgressCommand.ts index 459bbd8..2dd2754 100644 --- a/src/commands/LogReadingProgressCommand.ts +++ b/src/commands/LogReadingProgressCommand.ts @@ -2,30 +2,20 @@ import { type Editor, type MarkdownView, type MarkdownFileInfo, - type App, Notice, TFile, } from "obsidian"; import { EditorCheckCommand } from "./Command"; -import type { BookTrackerPluginSettings } from "@ui/settings"; import { ReadingProgressModal } from "@ui/modals"; -import type { ReadingLog } from "@utils/ReadingLog"; +import type BookTrackerPlugin from "@src/main"; export class LogReadingProgressCommand extends EditorCheckCommand { - constructor( - private readonly app: App, - private readonly readingLog: ReadingLog, - private readonly settings: BookTrackerPluginSettings - ) { + constructor(private readonly plugin: BookTrackerPlugin) { 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 - ); + return this.plugin.getBookMetadata(file)?.pageCount ?? 0; } protected check( @@ -34,7 +24,7 @@ export class LogReadingProgressCommand extends EditorCheckCommand { ): boolean { return !( ctx.file === null || - this.settings.pageCountProperty === "" || + this.plugin.settings.pageCountProperty === "" || this.getPageCount(ctx.file) <= 0 ); } @@ -47,7 +37,7 @@ export class LogReadingProgressCommand extends EditorCheckCommand { const fileName = file.basename; const pageCount = this.getPageCount(file); const pageNumber = await ReadingProgressModal.createAndOpen( - this.app, + this.plugin.app, pageCount ); @@ -59,7 +49,11 @@ export class LogReadingProgressCommand extends EditorCheckCommand { } try { - await this.readingLog.createEntry(fileName, pageNumber, pageCount); + await this.plugin.readingLog.createEntry( + fileName, + pageNumber, + pageCount + ); } catch (error) { new Notice( `Failed to log reading progress for ${fileName}: ${error}` diff --git a/src/commands/LogReadingStartedCommand.ts b/src/commands/LogReadingStartedCommand.ts index 1b55467..df3618f 100644 --- a/src/commands/LogReadingStartedCommand.ts +++ b/src/commands/LogReadingStartedCommand.ts @@ -8,6 +8,7 @@ import { import { EditorCheckCommand } from "./Command"; import type { BookTrackerPluginSettings } from "@ui/settings"; import { STATUS_IN_PROGRESS } from "@src/const"; +import moment from "@external/moment"; export class LogReadingStartedCommand extends EditorCheckCommand { constructor( @@ -34,12 +35,11 @@ export class LogReadingStartedCommand extends EditorCheckCommand { ): Promise { 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] = STATUS_IN_PROGRESS; - frontMatter[this.settings.startDateProperty] = startDate; + this.app.fileManager.processFrontMatter(file, (fm) => { + fm[this.settings.statusProperty] = STATUS_IN_PROGRESS; + fm[this.settings.startDateProperty] = startDate; }); new Notice("Reading started for " + file.basename); diff --git a/src/commands/ResetReadingStatusCommand.ts b/src/commands/ResetReadingStatusCommand.ts index 690c239..948dca1 100644 --- a/src/commands/ResetReadingStatusCommand.ts +++ b/src/commands/ResetReadingStatusCommand.ts @@ -37,10 +37,10 @@ export class ResetReadingStatusCommand extends EditorCheckCommand { ): void | Promise { const file = ctx.file!; - this.app.fileManager.processFrontMatter(file, (frontMatter) => { - frontMatter[this.settings.statusProperty] = STATUS_TO_BE_READ; - frontMatter[this.settings.startDateProperty] = ""; - frontMatter[this.settings.endDateProperty] = ""; + this.app.fileManager.processFrontMatter(file, (fm) => { + fm[this.settings.statusProperty] = STATUS_TO_BE_READ; + fm[this.settings.startDateProperty] = ""; + fm[this.settings.endDateProperty] = ""; }); this.readingLog.deleteEntriesForBook(file.basename); diff --git a/src/external/moment.ts b/src/external/moment.ts new file mode 100644 index 0000000..728d6d9 --- /dev/null +++ b/src/external/moment.ts @@ -0,0 +1,4 @@ +export type { Moment } from "moment"; + +// @ts-expect-error Moment is provided by Obsidian +export default moment; diff --git a/src/main.ts b/src/main.ts index 6212094..fd5c4da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,10 @@ -import { Notice, Plugin, requestUrl, TFile } from "obsidian"; +import { + Notice, + Plugin, + requestUrl, + TFile, + type FrontMatterCache, +} from "obsidian"; import { type BookTrackerPluginSettings, DEFAULT_SETTINGS, @@ -12,7 +18,7 @@ import { registerReadingLogCodeBlockProcessor, registerReadingStatsCodeBlockProcessor, } from "@ui/code-blocks"; -import type { Book } from "./types"; +import type { Book, BookMetadata, ReadingState } from "./types"; import { SearchGoodreadsCommand } from "@commands/SearchGoodreadsCommand"; import { LogReadingStartedCommand } from "@commands/LogReadingStartedCommand"; import { LogReadingProgressCommand } from "@commands/LogReadingProgressCommand"; @@ -26,6 +32,7 @@ import { registerShelfCodeBlockProcessor } from "@ui/code-blocks/ShelfCodeBlock" import { ReloadReadingLogCommand } from "@commands/ReloadReadingLogCommand"; import { registerReadingCalendarCodeBlockProcessor } from "@ui/code-blocks/ReadingCalendarCodeBlock"; import { registerAToZChallengeCodeBlockProcessor } from "@ui/code-blocks/AToZChallengeCodeBlock"; +import moment from "@external/moment"; export default class BookTrackerPlugin extends Plugin { public settings: BookTrackerPluginSettings; @@ -49,20 +56,8 @@ export default class BookTrackerPlugin extends Plugin { ) ); this.addCommand(new LogReadingStartedCommand(this.app, this.settings)); - this.addCommand( - new LogReadingProgressCommand( - this.app, - this.readingLog, - this.settings - ) - ); - this.addCommand( - new LogReadingFinishedCommand( - this.app, - this.readingLog, - this.settings - ) - ); + this.addCommand(new LogReadingProgressCommand(this)); + this.addCommand(new LogReadingFinishedCommand(this)); this.addCommand( new ResetReadingStatusCommand( this.app, @@ -178,4 +173,72 @@ export default class BookTrackerPlugin extends Plugin { const file = await this.app.vault.create(filePath, renderedContent); await this.app.workspace.getLeaf().openFile(file); } + + getBookMetadata(file: TFile): BookMetadata | null { + const metadata = this.app.metadataCache.getFileCache(file); + if (!metadata) { + return null; + } + + return this.frontmatterToMetadata(metadata.frontmatter); + } + + frontmatterToMetadata(fm: FrontMatterCache | undefined): BookMetadata { + const getString = (key: string) => { + const value = fm?.[key]; + if (typeof value === "string") { + return value; + } + return ""; + }; + + const getStringArray = (key: string) => { + const value = fm?.[key]; + if (Array.isArray(value)) { + return value as string[]; + } + return []; + }; + + const getNumber = (key: string) => { + const value = fm?.[key]; + if (typeof value === "number") { + return value; + } else if (typeof value === "string") { + return parseFloat(value); + } + return 0; + }; + + const getDate = (key: string) => { + const value = fm?.[key]; + if (typeof value === "string" || value instanceof Date) { + return moment(value); + } + return null; + }; + + return { + title: getString(this.settings.titleProperty), + subtitle: getString(this.settings.subtitleProperty), + description: getString(this.settings.descriptionProperty), + authors: getStringArray(this.settings.authorsProperty), + seriesTitle: getString(this.settings.seriesTitleProperty), + seriesPosition: getNumber(this.settings.seriesPositionProperty), + startDate: getDate(this.settings.startDateProperty)!, + endDate: getDate(this.settings.endDateProperty)!, + status: getString(this.settings.statusProperty) as ReadingState, + rating: getNumber(this.settings.ratingProperty), + spice: getNumber(this.settings.spiceProperty), + format: getString(this.settings.formatProperty), + source: getStringArray(this.settings.sourceProperty), + categories: getStringArray(this.settings.categoriesProperty), + publisher: getString(this.settings.publisherProperty), + publishDate: getDate(this.settings.publishDateProperty)!, + pageCount: getNumber(this.settings.pageCountProperty), + isbn: getString(this.settings.isbnProperty), + coverImageUrl: getString(this.settings.coverImageUrlProperty), + localCoverPath: getString(this.settings.localCoverPathProperty), + }; + } } diff --git a/src/types.ts b/src/types.ts index 9a69b8f..ad683b5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,12 +1,10 @@ -import type { - STATUS_IN_PROGRESS, - STATUS_READ, - STATUS_TO_BE_READ, -} from "./const"; +import moment from "@external/moment"; +import { STATUS_IN_PROGRESS, STATUS_READ, STATUS_TO_BE_READ } from "./const"; +import z from "zod/v4"; export interface Author { name: string; - description: string; + description?: string; } export interface Series { @@ -33,3 +31,28 @@ export type InProgressState = typeof STATUS_IN_PROGRESS; export type ReadState = typeof STATUS_READ; export type ReadingState = ToBeReadState | InProgressState | ReadState; + +export const BookMetadataSchema = z.object({ + title: z.string(), + subtitle: z.string(), + description: z.string(), + authors: z.array(z.string()), + seriesTitle: z.string(), + seriesPosition: z.number(), + startDate: z.date().transform((date) => moment(date)), + endDate: z.date().transform((date) => moment(date)), + status: z.enum([STATUS_TO_BE_READ, STATUS_IN_PROGRESS, STATUS_READ]), + rating: z.number(), + spice: z.number(), + format: z.string(), + source: z.array(z.string()), + categories: z.array(z.string()), + publisher: z.string(), + publishDate: z.date().transform((date) => moment(date)), + pageCount: z.number(), + isbn: z.string(), + coverImageUrl: z.string(), + localCoverPath: z.string(), +}); + +export type BookMetadata = z.infer; diff --git a/src/ui/code-blocks/AToZChallengeCodeBlock.ts b/src/ui/code-blocks/AToZChallengeCodeBlock.ts index 9083b7e..e4f89e6 100644 --- a/src/ui/code-blocks/AToZChallengeCodeBlock.ts +++ b/src/ui/code-blocks/AToZChallengeCodeBlock.ts @@ -2,7 +2,6 @@ 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 @@ -19,8 +18,3 @@ export function registerAToZChallengeCodeBlockProcessor( ) ); } - -export const AToZChallengeSettingsSchema = z.object({ - coverProperty: z.string(), - titleProperty: z.string(), -}); diff --git a/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte b/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte index 404495a..c437bef 100644 --- a/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte +++ b/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte @@ -4,17 +4,16 @@ setSettingsContext, } from "@ui/stores/settings.svelte"; import type { SvelteCodeBlockProps } from "./SvelteCodeBlockRenderer"; - import { createMetadata } from "@ui/stores/metadata.svelte"; + import { + createMetadata, + type FileMetadata, + } 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"; + import DateFilter from "@ui/components/DateFilter.svelte"; + import BookCover from "@ui/components/BookCover.svelte"; - const { plugin, source }: SvelteCodeBlockProps = $props(); - - const settings = AToZChallengeSettingsSchema.parse(parseYaml(source)); + const { plugin }: SvelteCodeBlockProps = $props(); const settingsStore = createSettings(plugin); setSettingsContext(settingsStore); @@ -39,10 +38,10 @@ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); - const items = $derived( + const metadata = $derived( metadataStore.metadata.reduce( - (acc, item) => { - const title = item.frontmatter[settings.titleProperty]; + (acc, meta) => { + const title = meta.book.title; const firstLetter = getSortValue(title).charAt(0).toUpperCase(); if (!firstLetter.match(/[A-Z]/)) { @@ -50,60 +49,23 @@ } 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, - }; + acc[firstLetter] = meta; } return acc; }, - {} as Record< - string, - { - file: TFile; - startDate: Moment; - endDate: Moment; - coverSrc: string; - coverAlt: string; - } - >, + {} as Record, ), ); const startDate = $derived( - Object.values(items) - .map((item) => item.startDate) + Object.values(metadata) + .map((meta) => meta.book.startDate) .sort((a, b) => a.diff(b))[0], ); const endDate = $derived.by(() => { - const dates = Object.values(items) - .map((item) => item.endDate) + const dates = Object.values(metadata) + .map((meta) => meta.book.endDate) .sort((a, b) => b.diff(a)); if (dates.length !== 26) { @@ -116,21 +78,17 @@
- +

Started: {startDate.format("YYYY-MM-DD")}

Ended: {endDate?.format("YYYY-MM-DD") ?? "N/A"}

{#each alphabet as letter}
- {#if items[letter]} - {@const item = items[letter]} - - {item.coverAlt} + {#if metadata[letter]} + {@const meta = metadata[letter]} + + {:else}
{letter}
@@ -182,7 +140,7 @@ font-style: italic; } - img { + :global(img) { width: 100%; height: 100%; object-fit: cover; diff --git a/src/ui/code-blocks/ReadingCalendarCodeBlock.ts b/src/ui/code-blocks/ReadingCalendarCodeBlock.ts index 76daedb..280c24a 100644 --- a/src/ui/code-blocks/ReadingCalendarCodeBlock.ts +++ b/src/ui/code-blocks/ReadingCalendarCodeBlock.ts @@ -2,7 +2,6 @@ import { registerCodeBlockRenderer } from "."; import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer"; import ReadingCalendarCodeBlockView from "./ReadingCalendarCodeBlockView.svelte"; import type BookTrackerPlugin from "@src/main"; -import z from "zod/v4"; export function registerReadingCalendarCodeBlockProcessor( plugin: BookTrackerPlugin @@ -19,7 +18,3 @@ export function registerReadingCalendarCodeBlockProcessor( ) ); } - -export const ReadingCalendarSettingsSchema = z.object({ - coverProperty: z.string(), -}); diff --git a/src/ui/code-blocks/ReadingCalendarCodeBlockView.svelte b/src/ui/code-blocks/ReadingCalendarCodeBlockView.svelte index c9750cf..fa71eb9 100644 --- a/src/ui/code-blocks/ReadingCalendarCodeBlockView.svelte +++ b/src/ui/code-blocks/ReadingCalendarCodeBlockView.svelte @@ -1,6 +1,5 @@ + +{coverAlt} + + diff --git a/src/ui/components/BookshelfView.svelte b/src/ui/components/BookshelfView.svelte index 0ac4965..a00dcb8 100644 --- a/src/ui/components/BookshelfView.svelte +++ b/src/ui/components/BookshelfView.svelte @@ -7,17 +7,14 @@ getMetadataContext, type FileMetadata, } from "@ui/stores/metadata.svelte"; - import { getSettingsContext } from "@ui/stores/settings.svelte"; import { COLOR_NAMES, type ColorName } from "@utils/color"; import { randomElement, randomFloat } from "@utils/rand"; import { v4 as uuidv4 } from "uuid"; import memoize from "just-memoize"; - import type { ShelfSettings } from "@ui/code-blocks/ShelfCodeBlock"; import type { TFile } from "obsidian"; interface Props { plugin: BookTrackerPlugin; - settings: ShelfSettings; } interface BookData { @@ -37,9 +34,8 @@ books: BookData[]; } - const { plugin, settings }: Props = $props(); + const { plugin }: Props = $props(); - const settingsStore = getSettingsContext(); const metadataStore = getMetadataContext(); const designs = ["default", "dual-top-bands", "split-bands"] as const; @@ -74,29 +70,20 @@ } const getBookData = memoize( - (metadata: FileMetadata): BookData => { + (meta: FileMetadata): BookData => { const orientation = randomOrientation(); return { - id: metadata.file.path, - title: metadata.frontmatter[settings.titleProperty], - subtitle: settings.subtitleProperty - ? metadata.frontmatter[settings.subtitleProperty] - : undefined, - authors: metadata.frontmatter[settings.authorsProperty], - width: Math.min( - Math.max( - 20, - metadata.frontmatter[ - settingsStore.settings.pageCountProperty - ] / 10, - ), - 100, - ), + id: meta.file.path, + title: meta.book.title, + subtitle: + meta.book.subtitle === "" ? undefined : meta.book.subtitle, + authors: meta.book.authors, + width: Math.min(Math.max(20, meta.book.pageCount / 10), 100), color: randomColor(), design: orientation === "front" ? "default" : randomDesign(), orientation: randomOrientation(), - file: metadata.file, + file: meta.file, }; }, (metadata: FileMetadata) => metadata.file.path, diff --git a/src/ui/components/DateFilter.svelte b/src/ui/components/DateFilter.svelte index b3b9842..b730644 100644 --- a/src/ui/components/DateFilter.svelte +++ b/src/ui/components/DateFilter.svelte @@ -10,20 +10,27 @@ "filterYear" | "filterYears" | "filterMonth" | "filterMonths" >; showAllMonths?: boolean; + disableMonthFilter?: boolean; + disableAllTime?: boolean; } - const { store, showAllMonths }: Props = $props(); + const { store, showAllMonths, disableMonthFilter, disableAllTime }: Props = + $props(); -{#if store.filterYear !== ALL_TIME} +{#if store.filterYear !== ALL_TIME && !disableMonthFilter}