Files
fellakte/main/reminders.js
T
2026-09-14 18:57:05 +00:00

79 lines
2.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 };