diff --git a/src/component/DailyNoteEditorView.svelte b/src/component/DailyNoteEditorView.svelte index 1eda446..9c874cc 100644 --- a/src/component/DailyNoteEditorView.svelte +++ b/src/component/DailyNoteEditorView.svelte @@ -5,149 +5,68 @@ import { TFile, moment } from "obsidian"; import DailyNote from "./DailyNote.svelte"; import { inview } from "svelte-inview"; - import { - getAllDailyNotes, - getDailyNote, - createDailyNote, - getDateFromFile, - getDailyNoteSettings, - DEFAULT_DAILY_NOTE_FORMAT - } from 'obsidian-daily-notes-interface'; + import { TimeRange, SelectionMode, TimeField } from "../types/time"; import { onMount } from "svelte"; - import { TimeRange } from "../types/time"; + import { FileManager, FileManagerOptions } from "../utils/fileManager"; export let plugin: DailyNoteViewPlugin; export let leaf: WorkspaceLeaf; export let selectedRange: TimeRange = "all"; export let customRange: { start: Date; end: Date } | null = null; + export let selectionMode: SelectionMode = "daily"; + export let target: string = ""; + export let timeField: TimeField = "mtime"; // 默认使用修改时间 + const size = 1; let intervalId; - let cacheDailyNotes: Record; - let allDailyNotes: TFile[] = []; - let renderedDailyNotes: TFile[] = []; - let filteredDailyNotes: TFile[] = []; + let renderedFiles: TFile[] = []; + let filteredFiles: TFile[] = []; let hasMore = true; - let hasFetch = false; - let hasCurrentDay: boolean = true; - let firstLoaded = true; let loaderRef: HTMLDivElement; - $: if (hasMore && !hasFetch) { - cacheDailyNotes = getAllDailyNotes(); - // Build notes list by date in descending order. - for (const string of Object.keys(cacheDailyNotes).sort().reverse()) { - allDailyNotes.push(cacheDailyNotes[string]); - } - hasFetch = true; - checkDailyNote(); - filterNotesByRange(); - } + // Create the file manager + let fileManager: FileManager; + + $: fileManagerOptions = { + mode: selectionMode, + target: target, + timeRange: selectedRange, + customRange: customRange, + app: plugin.app, + timeField: timeField + } as FileManagerOptions; - $: if (selectedRange && hasFetch) { - filterNotesByRange(); - } - - onMount(() => { - checkDailyNote(); - startFillViewport(); - }); - - function filterNotesByRange() { - const now = moment(); - const fileFormat = getDailyNoteSettings().format || DEFAULT_DAILY_NOTE_FORMAT; + $: if (fileManager && (selectedRange !== fileManager.options.timeRange || + customRange !== fileManager.options.customRange || + selectionMode !== fileManager.options.mode || + target !== fileManager.options.target || + timeField !== fileManager.options.timeField)) { + fileManager.updateOptions({ + timeRange: selectedRange, + customRange: customRange, + mode: selectionMode, + target: target, + timeField: timeField + }); - // Reset filtered notes - filteredDailyNotes = []; - - // Filter notes based on selected range - if (selectedRange === 'all') { - filteredDailyNotes = [...allDailyNotes]; - } else { - filteredDailyNotes = allDailyNotes.filter(file => { - const fileDate = moment(file.basename, fileFormat); - - switch (selectedRange) { - case 'week': - return fileDate.isSame(now, 'week'); - case 'month': - return fileDate.isSame(now, 'month'); - case 'year': - return fileDate.isSame(now, 'year'); - case 'last-week': - return fileDate.isBetween( - moment().subtract(1, 'week').startOf('week'), - moment().subtract(1, 'week').endOf('week'), - null, - '[]' - ); - case 'last-month': - return fileDate.isBetween( - moment().subtract(1, 'month').startOf('month'), - moment().subtract(1, 'month').endOf('month'), - null, - '[]' - ); - case 'last-year': - return fileDate.isBetween( - moment().subtract(1, 'year').startOf('year'), - moment().subtract(1, 'year').endOf('year'), - null, - '[]' - ); - case 'quarter': - return fileDate.isSame(now, 'quarter'); - case 'last-quarter': - return fileDate.isBetween( - moment().subtract(1, 'quarter').startOf('quarter'), - moment().subtract(1, 'quarter').endOf('quarter'), - null, - '[]' - ); - case 'custom': - if (customRange) { - const startDate = moment(customRange.start); - const endDate = moment(customRange.end); - return fileDate.isBetween(startDate, endDate, null, '[]'); - } - return false; - default: - return true; - } - }); - } - - // Reset rendered notes and start filling viewport again - renderedDailyNotes = []; - hasMore = filteredDailyNotes.length > 0; + // Reset rendered files and start filling viewport again + renderedFiles = []; + filteredFiles = fileManager.getFilteredFiles(); + hasMore = filteredFiles.length > 0; firstLoaded = true; startFillViewport(); } - function checkDailyNote() { - // @ts-ignore - const currentDate = moment(); - const currentDailyNote = getDailyNote(currentDate, cacheDailyNotes); - - // console.log(currentDate, cacheDailyNotes); - if (!currentDailyNote) { - hasCurrentDay = false; - } - } - - async function createNewDailyNote() { - const currentDate = moment(); - if (!hasCurrentDay) { - const currentDailyNote: any = await createDailyNote(currentDate); - renderedDailyNotes.push(currentDailyNote); - - renderedDailyNotes = sortDailyNotes(renderedDailyNotes); - hasCurrentDay = true; - } - } + onMount(() => { + fileManager = new FileManager(fileManagerOptions); + filteredFiles = fileManager.getFilteredFiles(); + hasMore = filteredFiles.length > 0; + startFillViewport(); + }); function startFillViewport() { if (!intervalId) { @@ -161,13 +80,13 @@ } function infiniteHandler() { - if (!hasFetch || !hasMore) return; - if (filteredDailyNotes.length === 0 && hasFetch) { + if (!fileManager || !hasMore) return; + if (filteredFiles.length === 0) { hasMore = false; } else { - renderedDailyNotes = [ - ...renderedDailyNotes, - ...filteredDailyNotes.splice(0, size) + renderedFiles = [ + ...renderedFiles, + ...filteredFiles.splice(0, size) ]; if (firstLoaded) { window.setTimeout(() => { @@ -184,71 +103,51 @@ } } + async function createNewDailyNote() { + const newNote = await fileManager.createNewDailyNote(); + if (newNote) { + renderedFiles = [newNote, ...renderedFiles]; + } + } + export function tick() { - renderedDailyNotes = renderedDailyNotes; + renderedFiles = renderedFiles; } export function check() { - checkDailyNote(); - } - - function sortDailyNotes(notes: TFile[]): TFile[] { - const fileFormat = getDailyNoteSettings().format || DEFAULT_DAILY_NOTE_FORMAT; - - return notes.sort((a, b) => { - return moment(b.basename, fileFormat).valueOf() - moment(a.basename, fileFormat).valueOf(); - }); + fileManager.checkDailyNote(); } export function fileCreate(file: TFile) { - const fileDate = getDateFromFile(file as any, 'day'); - const fileFormat = getDailyNoteSettings().format || DEFAULT_DAILY_NOTE_FORMAT; - if (!fileDate) return; - - if (renderedDailyNotes.length === 0) { - allDailyNotes.push(file); - allDailyNotes = sortDailyNotes(allDailyNotes); - filterNotesByRange(); - return; + fileManager.fileCreate(file); + + // Update the rendered files if needed + if (selectionMode === "daily") { + // For daily notes, we need to check if the file should be added to the rendered files + const filteredFiles = fileManager.getFilteredFiles(); + if (filteredFiles.some(f => f.basename === file.basename) && + !renderedFiles.some(f => f.basename === file.basename)) { + renderedFiles = [file, ...renderedFiles]; + } + } else { + // For folder and tag modes, we can simply update the rendered files + renderedFiles = fileManager.getFilteredFiles().slice(0, renderedFiles.length); } - - const lastRenderedDailyNote = renderedDailyNotes[renderedDailyNotes.length - 1]; - const firstRenderedDailyNote = renderedDailyNotes[0]; - const lastRenderedDailyNoteDate = moment(lastRenderedDailyNote.basename, fileFormat); - const firstRenderedDailyNoteDate = moment(firstRenderedDailyNote.basename, fileFormat); - - if (fileDate.isBetween(lastRenderedDailyNoteDate, firstRenderedDailyNoteDate)) { - renderedDailyNotes.push(file); - renderedDailyNotes = sortDailyNotes(renderedDailyNotes); - } else if (fileDate.isBefore(lastRenderedDailyNoteDate)) { - allDailyNotes.push(file); - allDailyNotes = sortDailyNotes(allDailyNotes); - filterNotesByRange(); - } else if (fileDate.isAfter(firstRenderedDailyNoteDate)) { - renderedDailyNotes.push(file); - renderedDailyNotes = sortDailyNotes(renderedDailyNotes); - } - - if (fileDate.isSame(moment(), 'day')) hasCurrentDay = true; } - export function fileDelete(file: TFile) { - if (!getDateFromFile(file as any, 'day')) return; - renderedDailyNotes = renderedDailyNotes.filter((dailyNote) => { + fileManager.fileDelete(file); + + // Remove the file from rendered files if it exists + renderedFiles = renderedFiles.filter((dailyNote) => { return dailyNote.basename !== file.basename; }); - allDailyNotes = allDailyNotes.filter((dailyNote) => { - return dailyNote.basename !== file.basename; - }); - filterNotesByRange(); - checkDailyNote(); }
- {#if selectedRange !== 'all'} + {#if selectionMode === "daily" && selectedRange !== 'all'}
{#if selectedRange === 'custom' && customRange} Showing notes from: {moment(customRange.start).format('YYYY-MM-DD')} to {moment(customRange.end).format('YYYY-MM-DD')} @@ -256,19 +155,37 @@ Showing notes for: {selectedRange} {/if}
+ {:else if selectionMode === "folder"} +
+ Showing files from folder: {target} + {#if selectedRange !== 'all'} + + ({timeField === 'ctime' ? 'created' : 'modified'} {selectedRange}) + + {/if} +
+ {:else if selectionMode === "tag"} +
+ Showing files with tag: {target} + {#if selectedRange !== 'all'} + + ({timeField === 'ctime' ? 'created' : 'modified'} {selectedRange}) + + {/if} +
{/if}
- {#if renderedDailyNotes.length === 0} + {#if renderedFiles.length === 0}
{/if} - {#if !hasCurrentDay && (selectedRange === 'all' || selectedRange === 'week' || selectedRange === 'month' || selectedRange === 'year' || selectedRange === 'quarter')} + {#if selectionMode === "daily" && !fileManager?.hasCurrentDayNote() && (selectedRange === 'all' || selectedRange === 'week' || selectedRange === 'month' || selectedRange === 'year' || selectedRange === 'quarter')} {/if} - {#each renderedDailyNotes as file (file)} + {#each renderedFiles as file (file)} {/each}
diff --git a/src/dailyNoteViewIndex.ts b/src/dailyNoteViewIndex.ts index 752d6b9..27a642e 100644 --- a/src/dailyNoteViewIndex.ts +++ b/src/dailyNoteViewIndex.ts @@ -25,7 +25,7 @@ import { DailyNoteSettingTab, DEFAULT_SETTINGS, } from "./dailyNoteSettings"; -import { TimeRange } from "./types/time"; +import { TimeRange, TimeField } from "./types/time"; import { getAllDailyNotes, getDailyNote, @@ -45,6 +45,9 @@ class DailyNoteView extends ItemView { scope: Scope; selectedDaysRange: TimeRange = "all"; + selectionMode: "daily" | "folder" | "tag" = "daily"; + target: string = ""; + timeField: TimeField = "mtime"; // 默认使用修改时间 customRange: { start: Date; @@ -63,11 +66,25 @@ class DailyNoteView extends ItemView { } getDisplayText(): string { - return "Daily Note"; + if (this.selectionMode === "daily") { + return "Daily Notes"; + } else if (this.selectionMode === "folder") { + return `Folder: ${this.target}`; + } else if (this.selectionMode === "tag") { + return `Tag: ${this.target}`; + } + return "Notes"; } getIcon(): string { - return "daily-note"; + if (this.selectionMode === "daily") { + return "daily-note"; + } else if (this.selectionMode === "folder") { + return "folder"; + } else if (this.selectionMode === "tag") { + return "tag"; + } + return "document"; } onFileCreate = (file: TAbstractFile) => { @@ -92,6 +109,25 @@ class DailyNoteView extends ItemView { } } + setSelectionMode(mode: "daily" | "folder" | "tag", target: string = "") { + this.selectionMode = mode; + this.target = target; + + if (this.view) { + this.view.$set({ + selectionMode: mode, + target: target, + }); + } + } + + setTimeField(field: TimeField) { + this.timeField = field; + if (this.view) { + this.view.$set({ timeField: field }); + } + } + openDailyNoteEditor() { this.plugin.openDailyNoteEditor(); } @@ -100,6 +136,65 @@ class DailyNoteView extends ItemView { this.scope.register(["Mod"], "f", (e) => { // do-nothing }); + + // Add action for selecting view mode + this.addAction("layers-2", "Select view mode", (e) => { + const menu = new Menu(); + + // Add mode selection options + const addModeOption = ( + title: string, + mode: "daily" | "folder" | "tag" + ) => { + menu.addItem((item) => { + item.setTitle(title); + item.setChecked(this.selectionMode === mode); + item.onClick(() => { + if (mode === "daily") { + this.setSelectionMode(mode); + } else { + // For folder and tag modes, we need to prompt for the target + const modal = new SelectTargetModal( + this.plugin.app, + mode, + (target: string) => { + this.setSelectionMode(mode, target); + } + ); + modal.open(); + } + }); + }); + }; + + addModeOption("Daily Notes", "daily"); + addModeOption("Folder", "folder"); + addModeOption("Tag", "tag"); + + menu.showAtMouseEvent(e); + }); + + // Add action for selecting time field (for folder and tag modes) + this.addAction("clock", "Select time field", (e) => { + const menu = new Menu(); + + // Add time field selection options + const addTimeFieldOption = (title: string, field: TimeField) => { + menu.addItem((item) => { + item.setTitle(title); + item.setChecked(this.timeField === field); + item.onClick(() => { + this.setTimeField(field); + }); + }); + }; + + addTimeFieldOption("Creation Time", "ctime"); + addTimeFieldOption("Modification Time", "mtime"); + + menu.showAtMouseEvent(e); + }); + this.addAction("calendar-range", "Select date range", (e) => { const menu = new Menu(); // Add range selection options @@ -146,6 +241,9 @@ class DailyNoteView extends ItemView { leaf: this.leaf, selectedRange: this.selectedDaysRange, customRange: this.customRange, + selectionMode: this.selectionMode, + target: this.target, + timeField: this.timeField, }, }); this.app.vault.on("create", this.onFileCreate); @@ -239,6 +337,106 @@ class CustomRangeModal extends Modal { } } +class SelectTargetModal extends Modal { + saveCallback: (target: string) => void; + mode: "folder" | "tag"; + targetInput: HTMLInputElement; + + constructor( + app: App, + mode: "folder" | "tag", + saveCallback: (target: string) => void + ) { + super(app); + this.mode = mode; + this.saveCallback = saveCallback; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + + contentEl.createEl("h2", { + text: this.mode === "folder" ? "Select Folder" : "Select Tag", + }); + + const form = contentEl.createEl("form"); + form.addEventListener("submit", (e) => { + e.preventDefault(); + this.save(); + }); + + const targetSetting = form.createDiv(); + targetSetting.addClass("setting-item"); + + const targetSettingInfo = targetSetting.createDiv(); + targetSettingInfo.addClass("setting-item-info"); + + targetSettingInfo.createEl("div", { + text: this.mode === "folder" ? "Folder Path" : "Tag Name", + cls: "setting-item-name", + }); + + targetSettingInfo.createEl("div", { + text: + this.mode === "folder" + ? "Enter the path to the folder (e.g., 'folder/subfolder')" + : "Enter the tag name without the '#' (e.g., 'tag')", + cls: "setting-item-description", + }); + + const targetSettingControl = targetSetting.createDiv(); + targetSettingControl.addClass("setting-item-control"); + + this.targetInput = targetSettingControl.createEl("input", { + type: "text", + value: "", + }); + this.targetInput.addClass("target-input"); + + const footerEl = contentEl.createDiv(); + footerEl.addClass("modal-button-container"); + + footerEl + .createEl("button", { + text: "Cancel", + cls: "mod-warning", + attr: { + type: "button", + }, + }) + .addEventListener("click", () => { + this.close(); + }); + + footerEl + .createEl("button", { + text: "Save", + cls: "mod-cta", + attr: { + type: "submit", + }, + }) + .addEventListener("click", (e) => { + e.preventDefault(); + this.save(); + }); + } + + save() { + const target = this.targetInput.value.trim(); + if (target) { + this.saveCallback(target); + this.close(); + } + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +} + export default class DailyNoteViewPlugin extends Plugin { private view: DailyNoteView; lastActiveFile: TFile; @@ -295,6 +493,32 @@ export default class DailyNoteViewPlugin extends Plugin { workspace.revealLeaf(leaf); } + async openFolderView(folderPath: string, timeField: TimeField = "mtime") { + const workspace = this.app.workspace; + const leaf = workspace.getLeaf(true); + await leaf.setViewState({ type: DAILY_NOTE_VIEW_TYPE }); + + // Get the view and set the selection mode to folder + const view = leaf.view as DailyNoteView; + view.setSelectionMode("folder", folderPath); + view.setTimeField(timeField); + + workspace.revealLeaf(leaf); + } + + async openTagView(tagName: string, timeField: TimeField = "mtime") { + const workspace = this.app.workspace; + const leaf = workspace.getLeaf(true); + await leaf.setViewState({ type: DAILY_NOTE_VIEW_TYPE }); + + // Get the view and set the selection mode to tag + const view = leaf.view as DailyNoteView; + view.setSelectionMode("tag", tagName); + view.setTimeField(timeField); + + workspace.revealLeaf(leaf); + } + async ensureTodaysDailyNoteExists() { try { const currentDate = moment(); @@ -353,7 +577,7 @@ export default class DailyNoteViewPlugin extends Plugin { // 0.14.x doesn't have WorkspaceContainer; this can just be an instanceof check once 15.x is mandatory: if ( - parent === app.workspace.rootSplit || + parent === this.app.workspace.rootSplit || (WorkspaceContainer && parent instanceof WorkspaceContainer) ) { diff --git a/src/style/index.css b/src/style/index.css index 06fba46..1a7033d 100644 --- a/src/style/index.css +++ b/src/style/index.css @@ -42,7 +42,6 @@ padding: 0 !important; overflow-y: clip; } - .daily-note-view .daily-note-container::before { content: ""; display: block; @@ -54,7 +53,11 @@ background-color: var(--color-base-25); } -.daily-note-view .daily-note-container:first-child::before { +.daily-note-view .daily-note-container:first-of-type::before { + height: 0px; +} + +.daily-note-view .dn-range-indicator + .daily-note-container::before { height: 0px; } diff --git a/src/types/time.d.ts b/src/types/time.d.ts index f43a3c3..494c989 100644 --- a/src/types/time.d.ts +++ b/src/types/time.d.ts @@ -1 +1,15 @@ -export type TimeRange = "week" | "month" | "year" | "all" | "last-week" | "last-month" | "last-year" | "quarter" | "last-quarter" | "custom"; +export type TimeRange = + | "week" + | "month" + | "year" + | "all" + | "last-week" + | "last-month" + | "last-year" + | "quarter" + | "last-quarter" + | "custom"; + +export type SelectionMode = "daily" | "folder" | "tag"; + +export type TimeField = "ctime" | "mtime"; diff --git a/src/utils/fileManager.ts b/src/utils/fileManager.ts new file mode 100644 index 0000000..9b779f8 --- /dev/null +++ b/src/utils/fileManager.ts @@ -0,0 +1,431 @@ +import { TFile, moment, App } from "obsidian"; +import { + getAllDailyNotes, + getDailyNote, + createDailyNote, + getDateFromFile, + getDailyNoteSettings, + DEFAULT_DAILY_NOTE_FORMAT, +} from "obsidian-daily-notes-interface"; +import { TimeRange, TimeField } from "../types/time"; + +export interface FileManagerOptions { + mode: "daily" | "folder" | "tag"; + target?: string; + timeRange?: TimeRange; + customRange?: { start: Date; end: Date } | null; + app?: App; + timeField?: TimeField; +} + +export class FileManager { + private allFiles: TFile[] = []; + private filteredFiles: TFile[] = []; + private hasFetched: boolean = false; + private hasCurrentDay: boolean = true; + private cacheDailyNotes: Record = {}; + + // Make options public so it can be accessed from outside + public options: FileManagerOptions; + + constructor(options: FileManagerOptions) { + this.options = options; + this.fetchFiles(); + } + + public fetchFiles(): void { + if (this.hasFetched) return; + + switch (this.options.mode) { + case "daily": + this.fetchDailyNotes(); + break; + case "folder": + this.fetchFolderFiles(); + break; + case "tag": + this.fetchTaggedFiles(); + break; + } + + this.hasFetched = true; + this.checkDailyNote(); + this.filterFilesByRange(); + } + + private fetchDailyNotes(): void { + this.cacheDailyNotes = getAllDailyNotes(); + // Build notes list by date in descending order. + for (const string of Object.keys(this.cacheDailyNotes) + .sort() + .reverse()) { + this.allFiles.push(this.cacheDailyNotes[string]); + } + } + + private fetchFolderFiles(): void { + if (!this.options.target || !this.options.app) return; + + // Get all files in the vault + const allFiles = this.options.app.vault.getFiles(); + + // Filter files by folder path + this.allFiles = allFiles.filter((file) => { + const folderPath = file.parent?.path || ""; + return ( + folderPath === this.options.target || + folderPath.startsWith(this.options.target + "/") + ); + }); + + // Sort files by modification time (newest first) + this.allFiles = this.allFiles.sort((a, b) => { + return b.stat.mtime - a.stat.mtime; + }); + } + + private fetchTaggedFiles(): void { + if (!this.options.target || !this.options.app) return; + + // Get all files with the specified tag + const allFiles = this.options.app.vault.getFiles(); + const targetTag = this.options.target.startsWith("#") + ? this.options.target + : "#" + this.options.target; + + this.allFiles = allFiles.filter((file) => { + // Check if the file has the target tag in its cache + const fileCache = + this.options.app?.metadataCache.getFileCache(file); + if (!fileCache || !fileCache.tags) return false; + + return fileCache.tags.some((tag) => tag.tag === targetTag); + }); + + // Sort files by modification time (newest first) + this.allFiles = this.allFiles.sort((a, b) => { + return b.stat.mtime - a.stat.mtime; + }); + } + + public filterFilesByRange(): TFile[] { + // If no time range is specified, return all files + if (!this.options.timeRange) { + this.filteredFiles = [...this.allFiles]; + return this.filteredFiles; + } + + // Reset the filtered files list + this.filteredFiles = []; + + // If the time range is "all", return all files + if (this.options.timeRange === "all") { + this.filteredFiles = [...this.allFiles]; + return this.filteredFiles; + } + + // Use different filtering methods based on different modes + if (this.options.mode === "daily") { + // Daily mode: filter daily notes by date + this.filterDailyNotesByRange(); + } else { + // Folder and tag modes: filter files by creation or modification time + this.filterFilesByTimeRange(); + } + + return this.filteredFiles; + } + + /** + * Filter files by time range + * Applicable to folder and tag modes + */ + private filterFilesByTimeRange(): void { + const now = moment(); + const timeField = this.options.timeField || "mtime"; // 默认使用修改时间 + + // Filter files by creation or modification time + this.filteredFiles = this.allFiles.filter((file) => { + // Get the time of the file based on the timeField option + const fileDate = moment(file.stat[timeField]); + + return this.isDateInRange(fileDate, now); + }); + } + + /** + * Filter daily notes by date + * Applicable to daily mode + */ + private filterDailyNotesByRange(): void { + const now = moment(); + const fileFormat = + getDailyNoteSettings().format || DEFAULT_DAILY_NOTE_FORMAT; + + this.filteredFiles = this.allFiles.filter((file) => { + const fileDate = moment(file.basename, fileFormat); + + return this.isDateInRange(fileDate, now); + }); + } + + /** + * Check if the file date is in the range + * @param fileDate file date + * @param now current date + * @returns whether in the range + */ + private isDateInRange( + fileDate: moment.Moment, + now: moment.Moment + ): boolean { + switch (this.options.timeRange) { + case "week": + return fileDate.isSame(now, "week"); + case "month": + return fileDate.isSame(now, "month"); + case "year": + return fileDate.isSame(now, "year"); + case "last-week": + return fileDate.isBetween( + moment().subtract(1, "week").startOf("week"), + moment().subtract(1, "week").endOf("week"), + null, + "[]" + ); + case "last-month": + return fileDate.isBetween( + moment().subtract(1, "month").startOf("month"), + moment().subtract(1, "month").endOf("month"), + null, + "[]" + ); + case "last-year": + return fileDate.isBetween( + moment().subtract(1, "year").startOf("year"), + moment().subtract(1, "year").endOf("year"), + null, + "[]" + ); + case "quarter": + return fileDate.isSame(now, "quarter"); + case "last-quarter": + return fileDate.isBetween( + moment().subtract(1, "quarter").startOf("quarter"), + moment().subtract(1, "quarter").endOf("quarter"), + null, + "[]" + ); + case "custom": + if (this.options.customRange) { + const startDate = moment(this.options.customRange.start); + const endDate = moment(this.options.customRange.end); + return fileDate.isBetween(startDate, endDate, null, "[]"); + } + return false; + default: + return true; + } + } + + public checkDailyNote(): boolean { + if (this.options.mode !== "daily") { + this.hasCurrentDay = true; + return true; + } + + // @ts-ignore + const currentDate = moment(); + const currentDailyNote = getDailyNote( + currentDate, + this.cacheDailyNotes + ); + + if (!currentDailyNote) { + this.hasCurrentDay = false; + return false; + } + + this.hasCurrentDay = true; + return true; + } + + public async createNewDailyNote(): Promise { + if (this.options.mode !== "daily" || this.hasCurrentDay) { + return null; + } + + const currentDate = moment(); + const currentDailyNote: any = await createDailyNote(currentDate); + + if (currentDailyNote) { + this.allFiles.push(currentDailyNote); + this.allFiles = this.sortDailyNotes(this.allFiles); + this.hasCurrentDay = true; + this.filterFilesByRange(); + return currentDailyNote; + } + + return null; + } + + public fileCreate(file: TFile): void { + if (this.options.mode === "daily") { + this.handleDailyNoteCreate(file); + } else if (this.options.mode === "folder") { + this.handleFolderFileCreate(file); + } else if (this.options.mode === "tag") { + this.handleTaggedFileCreate(file); + } + } + + private handleDailyNoteCreate(file: TFile): void { + const fileDate = getDateFromFile(file as any, "day"); + const fileFormat = + getDailyNoteSettings().format || DEFAULT_DAILY_NOTE_FORMAT; + if (!fileDate) return; + + if (this.filteredFiles.length === 0) { + this.allFiles.push(file); + this.allFiles = this.sortDailyNotes(this.allFiles); + this.filterFilesByRange(); + return; + } + + const lastFilteredFile = + this.filteredFiles[this.filteredFiles.length - 1]; + const firstFilteredFile = this.filteredFiles[0]; + const lastFilteredFileDate = moment( + lastFilteredFile.basename, + fileFormat + ); + const firstFilteredFileDate = moment( + firstFilteredFile.basename, + fileFormat + ); + + if (fileDate.isBetween(lastFilteredFileDate, firstFilteredFileDate)) { + this.filteredFiles.push(file); + this.filteredFiles = this.sortDailyNotes(this.filteredFiles); + } else if (fileDate.isBefore(lastFilteredFileDate)) { + this.allFiles.push(file); + this.allFiles = this.sortDailyNotes(this.allFiles); + this.filterFilesByRange(); + } else if (fileDate.isAfter(firstFilteredFileDate)) { + this.filteredFiles.push(file); + this.filteredFiles = this.sortDailyNotes(this.filteredFiles); + } + + if (fileDate.isSame(moment(), "day")) this.hasCurrentDay = true; + } + + private handleFolderFileCreate(file: TFile): void { + if (!this.options.target) return; + + // Check if the file belongs to the target folder + const folderPath = file.parent?.path || ""; + if ( + folderPath === this.options.target || + folderPath.startsWith(this.options.target + "/") + ) { + // Add the file to the collections + this.allFiles.push(file); + + // Sort files by modification time (newest first) + this.allFiles = this.allFiles.sort((a, b) => { + return b.stat.mtime - a.stat.mtime; + }); + + // Update filtered files + this.filterFilesByRange(); + } + } + + private handleTaggedFileCreate(file: TFile): void { + if (!this.options.target || !this.options.app) return; + + // Check if the file has the target tag + const targetTag = this.options.target.startsWith("#") + ? this.options.target + : "#" + this.options.target; + + const fileCache = this.options.app.metadataCache.getFileCache(file); + if (!fileCache || !fileCache.tags) return; + + if (fileCache.tags.some((tag) => tag.tag === targetTag)) { + // Add the file to the collections + this.allFiles.push(file); + + // Sort files by modification time (newest first) + this.allFiles = this.allFiles.sort((a, b) => { + return b.stat.mtime - a.stat.mtime; + }); + + // Update filtered files + this.filterFilesByRange(); + } + } + + public fileDelete(file: TFile): void { + if ( + this.options.mode === "daily" && + getDateFromFile(file as any, "day") + ) { + this.filteredFiles = this.filteredFiles.filter((f) => { + return f.basename !== file.basename; + }); + this.allFiles = this.allFiles.filter((f) => { + return f.basename !== file.basename; + }); + this.filterFilesByRange(); + this.checkDailyNote(); + } else { + // Handle deletion for folder and tag modes + this.filteredFiles = this.filteredFiles.filter((f) => { + return f.basename !== file.basename; + }); + this.allFiles = this.allFiles.filter((f) => { + return f.basename !== file.basename; + }); + } + } + + private sortDailyNotes(notes: TFile[]): TFile[] { + const fileFormat = + getDailyNoteSettings().format || DEFAULT_DAILY_NOTE_FORMAT; + + return notes.sort((a, b) => { + return ( + moment(b.basename, fileFormat).valueOf() - + moment(a.basename, fileFormat).valueOf() + ); + }); + } + + public getAllFiles(): TFile[] { + return this.allFiles; + } + + public getFilteredFiles(): TFile[] { + return this.filteredFiles; + } + + public hasCurrentDayNote(): boolean { + return this.hasCurrentDay; + } + + public updateOptions(options: Partial): void { + this.options = { ...this.options, ...options }; + + if (options.timeRange || options.customRange) { + this.filterFilesByRange(); + } + + if (options.mode || options.target) { + this.allFiles = []; + this.filteredFiles = []; + this.hasFetched = false; + this.fetchFiles(); + } + } +} diff --git a/styles.css b/styles.css index 6feccf1..2df19f6 100644 --- a/styles.css +++ b/styles.css @@ -1,4 +1,4 @@ -.daily-note.svelte-18gimyz.svelte-18gimyz{margin-bottom:var(--size-4-5);padding-bottom:var(--size-4-5)}.daily-note.svelte-18gimyz:has(.is-readable-line-width) .daily-note-title.svelte-18gimyz{max-width:var(--file-line-width);margin-left:auto;margin-right:auto}.daily-note-title.svelte-18gimyz.svelte-18gimyz{display:flex;justify-content:space-between;margin-top:var(--size-4-12)}.daily-node-icon.svelte-18gimyz.svelte-18gimyz{width:var(--size-4-8);height:var(--size-4-8);cursor:pointer}.daily-node-icon.svelte-18gimyz.svelte-18gimyz:hover{background-color:var(--background-modifier-hover)}.dn-stock.svelte-i23r02{height:1000px;width:100%}.no-more-text.svelte-i23r02{margin-left:auto;margin-right:auto;text-align:center}.dn-blank-day.svelte-i23r02{display:flex;margin-left:auto;margin-right:auto;max-width:var(--file-line-width);color:var(--color-base-40);padding-top:20px;padding-bottom:20px;transition:all 300ms}.dn-blank-day.svelte-i23r02:hover{padding-top:40px;padding-bottom:40px;transition:padding 300ms}.dn-blank-day-text.svelte-i23r02{margin-left:auto;margin-right:auto;text-align:center}.dn-range-indicator.svelte-i23r02{margin-left:auto;margin-right:auto;max-width:var(--file-line-width);padding:5px 0}.dn-range-text.svelte-i23r02{font-size:0.8em;color:var(--color-base-50);text-align:center;font-style:italic}.dn-editor { +.daily-note.svelte-18gimyz.svelte-18gimyz{margin-bottom:var(--size-4-5);padding-bottom:var(--size-4-5)}.daily-note.svelte-18gimyz:has(.is-readable-line-width) .daily-note-title.svelte-18gimyz{max-width:var(--file-line-width);margin-left:auto;margin-right:auto}.daily-note-title.svelte-18gimyz.svelte-18gimyz{display:flex;justify-content:space-between;margin-top:var(--size-4-12)}.daily-node-icon.svelte-18gimyz.svelte-18gimyz{width:var(--size-4-8);height:var(--size-4-8);cursor:pointer}.daily-node-icon.svelte-18gimyz.svelte-18gimyz:hover{background-color:var(--background-modifier-hover)}.dn-stock.svelte-gpxxkc{height:1000px;width:100%}.no-more-text.svelte-gpxxkc{margin-left:auto;margin-right:auto;text-align:center}.dn-blank-day.svelte-gpxxkc{display:flex;margin-left:auto;margin-right:auto;max-width:var(--file-line-width);color:var(--color-base-40);padding-top:20px;padding-bottom:20px;transition:all 300ms}.dn-blank-day.svelte-gpxxkc:hover{padding-top:40px;padding-bottom:40px;transition:padding 300ms}.dn-blank-day-text.svelte-gpxxkc{margin-left:auto;margin-right:auto;text-align:center}.dn-range-indicator.svelte-gpxxkc{margin-left:auto;margin-right:auto;max-width:var(--file-line-width);padding:5px 0}.dn-range-text.svelte-gpxxkc{font-size:0.8em;color:var(--color-base-50);text-align:center;font-style:italic}.dn-time-field.svelte-gpxxkc{font-size:0.9em;color:var(--color-base-40)}.dn-editor { /*overflow-y: hidden;*/ } @@ -42,7 +42,6 @@ padding: 0 !important; overflow-y: clip; } - .daily-note-view .daily-note-container::before { content: ""; display: block; @@ -54,7 +53,11 @@ background-color: var(--color-base-25); } -.daily-note-view .daily-note-container:first-child::before { +.daily-note-view .daily-note-container:first-of-type::before { + height: 0px; +} + +.daily-note-view .dn-range-indicator + .daily-note-container::before { height: 0px; }