import { App, Modal, Notice, Setting } from "obsidian"; import { ParsedReminderInput } from "../types"; export class ReminderModal extends Modal { private title = ""; private body = ""; private year = ""; private month = ""; private day = ""; private hour = ""; private minute = ""; private channels = ""; constructor( app: App, private defaultChannels: string[], private onSubmit: (input: ParsedReminderInput) => Promise, initialTitle = "", ) { super(app); const initialDate = new Date(Date.now() + 60 * 60 * 1000); this.title = initialTitle; this.body = initialTitle; this.channels = defaultChannels.join(", "); this.year = String(initialDate.getFullYear()); this.month = String(initialDate.getMonth() + 1).padStart(2, "0"); this.day = String(initialDate.getDate()).padStart(2, "0"); this.hour = String(initialDate.getHours()).padStart(2, "0"); this.minute = String(Math.ceil(initialDate.getMinutes() / 5) * 5).padStart(2, "0"); if (this.minute === "60") { this.minute = "00"; this.hour = String((initialDate.getHours() + 1) % 24).padStart(2, "0"); } } onOpen(): void { const { contentEl } = this; contentEl.empty(); contentEl.createEl("h2", { text: "Create reminder" }); this.renderReminderRow(contentEl); new Setting(contentEl) .setName("Details") .setDesc("Optional longer message.") .addTextArea((text) => { text.setValue(this.body); text.inputEl.rows = 3; text.onChange((value) => { this.body = value; }); }); new Setting(contentEl) .setName("Channels") .setDesc("Comma separated provider IDs or types. Leave empty to use all enabled providers.") .addText((text) => text .setValue(this.channels) .onChange((value) => { this.channels = value; })); new Setting(contentEl) .addButton((button) => button .setButtonText("Create") .setCta() .onClick(async () => { const dueAt = this.getSelectedDate(); if (!this.title.trim() || !dueAt) { new Notice("Title and valid due date are required."); return; } await this.onSubmit({ title: this.title.trim(), body: this.body.trim(), dueAt: dueAt.toISOString(), channels: this.channels.split(",").map((value) => value.trim()).filter(Boolean), }); this.close(); })); } private renderReminderRow(container: HTMLElement): void { const setting = new Setting(container) .setName("Reminder") .setDesc("Text, date, and time."); setting.addText((text) => { text.setPlaceholder("Reminder text"); text.setValue(this.title); text.onChange((value) => { this.title = value; if (!this.body || this.body === this.title) { this.body = value; } }); }); setting.addDropdown((dropdown) => { for (const year of this.range(new Date().getFullYear(), new Date().getFullYear() + 3)) { dropdown.addOption(String(year), String(year)); } dropdown.setValue(this.year); dropdown.onChange((value) => { this.year = value; this.ensureValidDay(); }); }); setting.addDropdown((dropdown) => { for (const month of this.range(1, 12)) { const value = String(month).padStart(2, "0"); dropdown.addOption(value, value); } dropdown.setValue(this.month); dropdown.onChange((value) => { this.month = value; this.ensureValidDay(); }); }); setting.addDropdown((dropdown) => { for (const day of this.range(1, 31)) { const value = String(day).padStart(2, "0"); dropdown.addOption(value, value); } dropdown.setValue(this.day); dropdown.onChange((value) => { this.day = value; }); }); setting.addDropdown((dropdown) => { for (const hour of this.range(0, 23)) { const value = String(hour).padStart(2, "0"); dropdown.addOption(value, value); } dropdown.setValue(this.hour); dropdown.onChange((value) => { this.hour = value; }); }); setting.addDropdown((dropdown) => { for (const minute of this.range(0, 55, 5)) { const value = String(minute).padStart(2, "0"); dropdown.addOption(value, value); } dropdown.setValue(this.minute); dropdown.onChange((value) => { this.minute = value; }); }); } private getSelectedDate(): Date | null { const date = new Date(`${this.year}-${this.month}-${this.day}T${this.hour}:${this.minute}:00`); return Number.isNaN(date.getTime()) ? null : date; } private ensureValidDay(): void { const maxDay = new Date(Number(this.year), Number(this.month), 0).getDate(); if (Number(this.day) > maxDay) { this.day = String(maxDay).padStart(2, "0"); } } private range(from: number, to: number, step = 1): number[] { const values: number[] = []; for (let value = from; value <= to; value += step) { values.push(value); } return values; } }