86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
import { App, Notice, TFile } from 'obsidian';
|
|
import type { Task } from './types';
|
|
import { DAILY_NOTE_TASKS_HEADING } from './constants';
|
|
|
|
// obsidian-daily-notes-interface re-exports moment from Obsidian's bundle.
|
|
// We import it this way so esbuild treats moment as external.
|
|
declare const moment: (date?: unknown) => {
|
|
format(fmt: string): string;
|
|
};
|
|
|
|
interface DailyNotesInterface {
|
|
appHasDailyNotesPluginLoaded(app: App): boolean;
|
|
getDailyNote(
|
|
date: ReturnType<typeof moment>,
|
|
notes: Record<string, TFile>
|
|
): TFile | null;
|
|
createDailyNote(date: ReturnType<typeof moment>): Promise<TFile>;
|
|
getAllDailyNotes(): Record<string, TFile>;
|
|
}
|
|
|
|
let dni: DailyNotesInterface | null = null;
|
|
|
|
async function getDni(): Promise<DailyNotesInterface> {
|
|
if (!dni) {
|
|
dni = await import('obsidian-daily-notes-interface') as unknown as DailyNotesInterface;
|
|
}
|
|
return dni;
|
|
}
|
|
|
|
/**
|
|
* Append a completed task (and its notes) to the `#### Tasks` section
|
|
* of today's daily note, creating the section and/or the note if needed.
|
|
*/
|
|
export async function appendTaskToDailyNote(task: Task, app: App): Promise<void> {
|
|
const lib = await getDni();
|
|
|
|
if (!lib.appHasDailyNotesPluginLoaded(app)) {
|
|
new Notice(
|
|
'YAOTP: Daily Notes plugin is not enabled. ' +
|
|
'Please enable it in Settings → Core plugins.'
|
|
);
|
|
return;
|
|
}
|
|
|
|
const all = lib.getAllDailyNotes();
|
|
// moment is available as a global in Obsidian's runtime
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const today = (window as any).moment();
|
|
let file: TFile | null = lib.getDailyNote(today, all);
|
|
|
|
if (!file) {
|
|
file = await lib.createDailyNote(today);
|
|
}
|
|
|
|
const existing = await app.vault.read(file);
|
|
const appended = buildAppendedContent(existing, task);
|
|
await app.vault.modify(file, appended);
|
|
}
|
|
|
|
function buildAppendedContent(existing: string, task: Task): string {
|
|
const taskLine = `- [x] ${task.text}`;
|
|
const notesBlock =
|
|
task.notes.length > 0 ? '\n\n' + task.notes.join('\n') : '';
|
|
const entry = taskLine + notesBlock;
|
|
|
|
const headingIndex = existing.lastIndexOf(DAILY_NOTE_TASKS_HEADING);
|
|
|
|
if (headingIndex === -1) {
|
|
// Section doesn't exist — append it at the end
|
|
const base = existing.trimEnd();
|
|
return base + (base.length > 0 ? '\n\n' : '') + DAILY_NOTE_TASKS_HEADING + '\n\n' + entry + '\n';
|
|
}
|
|
|
|
// Section exists — insert after heading and any existing items
|
|
const afterHeading = existing.slice(headingIndex + DAILY_NOTE_TASKS_HEADING.length);
|
|
// Find where the next same-or-higher-level heading starts (if any)
|
|
const nextHeadingMatch = afterHeading.match(/\n#{1,4} /);
|
|
const insertionRelative = nextHeadingMatch?.index ?? afterHeading.length;
|
|
|
|
const before = existing.slice(0, headingIndex + DAILY_NOTE_TASKS_HEADING.length);
|
|
const sectionContent = afterHeading.slice(0, insertionRelative).trimEnd();
|
|
const after = afterHeading.slice(insertionRelative);
|
|
|
|
return before + sectionContent + '\n\n' + entry + (after.length > 0 ? '\n' + after : '\n');
|
|
}
|