diff --git a/README.md b/README.md index ac9ee16..7668334 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Follow the steps below to install Tasks. - `Daily notes` core plugin - this plugin intends to be a replacement for it, the only difference now is the next/previous note command that will be added in next release. Notes created through Daily notes will not be connected to any journal (can be done manually editing frontmatter properties). - `Periodic Notes` community plugin - this plugin was intially inspired by Periodic notes that seem to abandoned and aims to be a replacement for it. -- `Calendar` community plugin - there is no integration as for now and plun is to create a calendar view in this plugin making Calendar plugin not needed. +- `Calendar` community plugin - there is no integration as for now and plan is to create a calendar view in this plugin making Calendar plugin not needed. - `Templater` community plugin - it is planned to add support for Templater templates and plugin, and also some helpers ## Supported variables diff --git a/src/calendar-journal/calendar-index.ts b/src/calendar-journal/calendar-index.ts index eb0f9d1..7a9e7d1 100644 --- a/src/calendar-journal/calendar-index.ts +++ b/src/calendar-journal/calendar-index.ts @@ -20,6 +20,18 @@ export class CalendarIndex { this.intervalTree.insert(interval, value); } + findNextNote(endDate: MomentDate, granularity: CalendarGranularity): string | null { + const list = this.intervalTree.search([endDate.toDate().getTime(), Infinity]) as IndexEntry[]; + const [note] = list.filter((entry) => entry.granularity === granularity); + return note?.path ?? null; + } + + findPreviousNote(startDate: MomentDate, granularity: CalendarGranularity): string | null { + const list = this.intervalTree.search([0, startDate.toDate().getTime()]) as IndexEntry[]; + const note = list.filter((entry) => entry.granularity === granularity).pop(); + return note?.path ?? null; + } + clearForPath(path: string): void { if (!this.intervalTree.size) return; for (const [key, entry] of this.intervalTree.iterate(undefined, (value, key) => [key, value])) { diff --git a/src/calendar-journal/calendar-journal-section.ts b/src/calendar-journal/calendar-journal-section.ts index 96d35d8..32d2cc2 100644 --- a/src/calendar-journal/calendar-journal-section.ts +++ b/src/calendar-journal/calendar-journal-section.ts @@ -81,6 +81,13 @@ export class CalendarJournalSection { return await this.openDate(this.getRangeStart(date), this.getRangeEnd(date)); } + async openPath(filePath: string): Promise { + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!file) return; + if (!(file instanceof TFile)) return; + await this.openFile(file); + } + async openNext(date?: string): Promise { const startDate = this.getRangeStart(date).add(1, this.granularity); const endDate = startDate.clone().endOf(this.granularity); @@ -115,6 +122,10 @@ export class CalendarJournalSection { private async openDate(startDate: MomentDate, endDate: MomentDate): Promise { const file = await this.ensureDateNote(startDate, endDate); + await this.openFile(file); + } + + private async openFile(file: TFile): Promise { const mode = this.config.openMode === "active" ? undefined : this.config.openMode; const leaf = this.app.workspace.getLeaf(mode); await leaf.openFile(file, { active: true }); diff --git a/src/calendar-journal/calendar-journal.ts b/src/calendar-journal/calendar-journal.ts index c447186..7db88a3 100644 --- a/src/calendar-journal/calendar-journal.ts +++ b/src/calendar-journal/calendar-journal.ts @@ -145,12 +145,27 @@ export class CalendarJournal implements Journal { await this.year.autoCreateNote(); } + async findNextNote(data: CalerndatFrontMatter): Promise { + return this.index.findNextNote(this.calendar.date(data.end_date).add(1, "day").startOf("day"), data.granularity); + } + async findPreviousNote(data: CalerndatFrontMatter): Promise { + return this.index.findPreviousNote( + this.calendar.date(data.start_date).subtract(1, "day").endOf("day"), + data.granularity, + ); + } + async openStartupNote(): Promise { if (!this.config.openOnStartup || !this.config.startupSection) return; const section = this.config.startupSection; await this[section].open(); } + async openPath(path: string, frontmatter: CalerndatFrontMatter): Promise { + const section = frontmatter.granularity; + await this[section].openPath(path); + } + parseFrontMatter(frontmatter: FrontMatterCache): CalerndatFrontMatter { return { type: "calendar", diff --git a/src/contracts/journal.types.ts b/src/contracts/journal.types.ts index a4b1cbd..1422ba5 100644 --- a/src/contracts/journal.types.ts +++ b/src/contracts/journal.types.ts @@ -5,6 +5,7 @@ export interface Journal { name: string; autoCreateNotes(): Promise; openStartupNote(): Promise; + openPath(path: string, frontmatter: JournalFrontMatter): Promise; configureRibbonIcons(plugin: Plugin): void; parseFrontMatter(frontmatter: FrontMatterCache): JournalFrontMatter; indexNote(frontmatter: JournalFrontMatter, path: string): void; @@ -12,6 +13,9 @@ export interface Journal { supportsCommand(id: string): boolean; execCommand(id: string): Promise; + findNextNote(data: JournalFrontMatter): Promise; + findPreviousNote(data: JournalFrontMatter): Promise; + clearNotes(): Promise; deleteNotes(): Promise; } diff --git a/src/interval-journal/interval-journal.ts b/src/interval-journal/interval-journal.ts index bb7e2a5..8b7e580 100644 --- a/src/interval-journal/interval-journal.ts +++ b/src/interval-journal/interval-journal.ts @@ -1,5 +1,5 @@ import { App, FrontMatterCache, Plugin, TFile, normalizePath } from "obsidian"; -import { IntervalConfig, IntervalFrontMatter } from "../contracts/config.types"; +import { IntervalConfig, IntervalFrontMatter, JournalFrontMatter } from "../contracts/config.types"; import { CalendarHelper } from "../utils/calendar"; import { Journal } from "../contracts/journal.types"; import { @@ -61,6 +61,20 @@ export class IntervalJournal implements Journal { return this.config.ribbon.tooltip || `Open current ${this.config.name} note`; } + async findNextNote(data: IntervalFrontMatter): Promise { + return this.intervals.findNextNote(this.calendar.date(data.end_date).add(1, "day").startOf("day")); + } + async findPreviousNote(data: IntervalFrontMatter): Promise { + return this.intervals.findPreviousNote(this.calendar.date(data.start_date).subtract(1, "day").endOf("day")); + } + + async openPath(path: string, _frontmatter: JournalFrontMatter): Promise { + const file = this.app.vault.getAbstractFileByPath(path); + if (!file) return; + if (!(file instanceof TFile)) return; + await this.openFile(file); + } + findInterval(date?: string): Interval { return this.intervals.findInterval(date); } @@ -207,6 +221,10 @@ export class IntervalJournal implements Journal { private async openInterval(interval: Interval): Promise { const file = await this.ensureIntervalNote(interval); + await this.openFile(file); + } + + private async openFile(file: TFile): Promise { const mode = this.config.openMode === "active" ? undefined : this.config.openMode; const leaf = this.app.workspace.getLeaf(mode); await leaf.openFile(file, { active: true }); diff --git a/src/interval-journal/interval-manager.ts b/src/interval-journal/interval-manager.ts index bf70448..71fc54f 100644 --- a/src/interval-journal/interval-manager.ts +++ b/src/interval-journal/interval-manager.ts @@ -71,6 +71,18 @@ export class IntervalManager { this.keysMap.set(this.getIntervalKey(interval), interval); } + findNextNote(endDate: MomentDate): string | null { + const list = this.intervalTree.search([endDate.toDate().getTime(), Infinity]) as Interval[]; + const note = list.pop(); + return note?.path ?? null; + } + + findPreviousNote(startDate: MomentDate): string | null { + const list = this.intervalTree.search([0, startDate.toDate().getTime()]) as Interval[]; + const [note] = list; + return note?.path ?? null; + } + clearForPath(path: string): void { if (!this.intervalTree.size) return; for (const [key, entry] of this.intervalTree.iterate(undefined, (value, key) => [key, value])) { diff --git a/src/journal-manager.ts b/src/journal-manager.ts index 5ab4233..f4e2772 100644 --- a/src/journal-manager.ts +++ b/src/journal-manager.ts @@ -1,4 +1,4 @@ -import { App, Component, Plugin, TAbstractFile, TFile } from "obsidian"; +import { App, Component, Notice, Plugin, TAbstractFile, TFile } from "obsidian"; import { CalendarJournal, calendarCommands } from "./calendar-journal/calendar-journal"; import { FRONTMATTER_ID_KEY } from "./constants"; import { deepCopy } from "./utils"; @@ -94,6 +94,56 @@ export class JournalManager extends Component { }, }); } + this.plugin.addCommand({ + id: "journal:open-next", + name: "Open next note", + editorCallback: async (editor, ctx) => { + const file = ctx.file; + if (file) { + const data = await this.getJournalData(file.path); + if (data) { + const journal = this.journals.get(data.id); + if (journal) { + const notePath = await journal.findNextNote(data); + if (notePath) { + await journal.openPath(notePath, data); + } else { + new Notice("There is no next note after this one."); + } + } else { + new Notice("Unknown journal id."); + } + } else { + new Notice("This note is not connected to any journal."); + } + } + }, + }); + this.plugin.addCommand({ + id: "journal:open-prev", + name: "Open previous note", + editorCallback: async (editor, ctx) => { + const file = ctx.file; + if (file) { + const data = await this.getJournalData(file.path); + if (data) { + const journal = this.journals.get(data.id); + if (journal) { + const notePath = await journal.findPreviousNote(data); + if (notePath) { + await journal.openPath(notePath, data); + } else { + new Notice("There is no previous note before this one."); + } + } else { + new Notice("Unknown journal id."); + } + } else { + new Notice("This note is not connected to any journal."); + } + } + }, + }); } configureRibbonIcons() {