import { defineStore } from "pinia"; import { ref } from "vue"; import { repo } from "../adapters"; import router from "../router"; import { useUiStore } from "./ui"; import type { Note } from "./notes"; // Foreground reminder delivery — NO Web Push / service worker (per operator). While // the app is open, poll for due reminders and surface each once as a toast (and, if // the user opted in, a page-context OS notification). Real background delivery is a // native-client concern. const POLL_MS = 45_000; const RECENT_MS = 15 * 60 * 1000; // on first load, only announce reminders due this recently export const useReminderStore = defineStore("reminders", () => { const osSupported = typeof Notification !== "undefined"; const osEnabled = ref(osSupported && Notification.permission === "granted"); // note id → the remind_at we already fired, so each occurrence notifies at most once. const notified = new Map(); let timer: ReturnType | undefined; let primed = false; // Single owner of the reminders endpoint — the RemindersView reads its list through // this too, so the URL + response shape live in one place. async function fetchReminders(): Promise { return repo.notes.reminders(); } async function check(): Promise { const ui = useUiStore(); let notes: Note[]; try { notes = await fetchReminders(); } catch { return; } const now = Date.now(); for (const n of notes) { if (!n.remind_at) continue; const due = Date.parse(n.remind_at); if (due > now || notified.get(n.id) === n.remind_at) continue; notified.set(n.id, n.remind_at); // Silently prime a stale backlog on first load; only announce recently-due ones. if (!primed && now - due > RECENT_MS) continue; const title = n.display_title || "Reminder"; ui.showToast(`⏰ ${title}`, { label: "Open", run: () => void router.push({ path: "/", query: { open: n.id } }), }); if (osEnabled.value) { try { new Notification("ThoughtSync reminder", { body: title }); } catch { /* notifications unavailable — the toast still fired */ } } } primed = true; } function start(): void { if (timer) return; void check(); timer = setInterval(() => void check(), POLL_MS); } function stop(): void { if (timer) clearInterval(timer); timer = undefined; } async function enableOs(): Promise { if (!osSupported) return; osEnabled.value = (await Notification.requestPermission()) === "granted"; } return { osSupported, osEnabled, start, stop, enableOs, fetchReminders }; });