79 lines
2.4 KiB
JavaScript
79 lines
2.4 KiB
JavaScript
// reminders.js — native OS notifications for upcoming appointments.
|
||
// Checks once a minute; fires once per appointment when it enters the lead window
|
||
// (default 30 min before start_time, configurable via the "reminderLeadMinutes" setting).
|
||
"use strict";
|
||
|
||
const { Notification, BrowserWindow } = require("electron");
|
||
const db = require("./db");
|
||
|
||
const CHECK_INTERVAL_MS = 60 * 1000;
|
||
const DEFAULT_LEAD_MINUTES = 30;
|
||
|
||
const notified = new Set(); // appointment ids already notified
|
||
|
||
function pad(n) {
|
||
return String(n).padStart(2, "0");
|
||
}
|
||
function isoToday() {
|
||
const d = new Date();
|
||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||
}
|
||
|
||
function leadMinutes() {
|
||
const raw = db.getSetting("reminderLeadMinutes");
|
||
const n = raw ? Number(raw) : NaN;
|
||
return Number.isFinite(n) && n > 0 ? n : DEFAULT_LEAD_MINUTES;
|
||
}
|
||
|
||
function checkNow() {
|
||
if (!Notification.isSupported()) return;
|
||
let appointments;
|
||
try {
|
||
appointments = db.listAppointments(isoToday(), isoToday());
|
||
} catch {
|
||
return; // db not ready yet
|
||
}
|
||
const lead = leadMinutes();
|
||
const now = new Date();
|
||
for (const appt of appointments) {
|
||
if (!appt.start_time || notified.has(appt.id)) continue;
|
||
const [h, m] = appt.start_time.split(":").map(Number);
|
||
if (Number.isNaN(h) || Number.isNaN(m)) continue;
|
||
const start = new Date();
|
||
start.setHours(h, m, 0, 0);
|
||
const minutesUntil = (start - now) / 60000;
|
||
// Fire once the appointment enters the lead window, and up to 2 min after its
|
||
// start (covers the case where the app was closed/asleep right at the moment).
|
||
if (minutesUntil <= lead && minutesUntil >= -2) {
|
||
notified.add(appt.id);
|
||
const who = appt.animalName ? ` – ${appt.animalName}${appt.ownerName ? " (" + appt.ownerName + ")" : ""}` : "";
|
||
const title = minutesUntil > 1 ? `Termin in ${Math.round(minutesUntil)} Min.` : "Termin jetzt";
|
||
const n = new Notification({
|
||
title,
|
||
body: `${appt.title}${who}${appt.location ? "\n" + appt.location : ""}`,
|
||
});
|
||
n.on("click", () => {
|
||
const win = BrowserWindow.getAllWindows()[0];
|
||
if (win) {
|
||
win.show();
|
||
win.focus();
|
||
}
|
||
});
|
||
n.show();
|
||
}
|
||
}
|
||
}
|
||
|
||
let timer = null;
|
||
function start() {
|
||
if (timer) return;
|
||
checkNow();
|
||
timer = setInterval(checkNow, CHECK_INTERVAL_MS);
|
||
}
|
||
function stop() {
|
||
if (timer) clearInterval(timer);
|
||
timer = null;
|
||
}
|
||
|
||
module.exports = { start, stop };
|