Initial Obsidian reminder notifier plugin
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
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<void>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user