import { FormMessage, SettingRow } from "./design-system/WorkspacePanel"; import { useEffect, useRef, useState } from "react"; import { habitRequest } from "../lib/dashboard"; import { allWeekdays, defaultReminder, entries, type ReminderEntry, type ReminderResponse } from "../reminders/contracts"; import { Button, Checkbox } from "./design-system/primitives"; import { Field } from "./design-system/Field"; const deliveryLabels: Record = { sent: "Delivered", deferred: "Waiting to retry", pending: "Delivery unconfirmed", uncertain: "Delivery unconfirmed; check Discord. This reminder will not be retried.", failed: "Discord could not deliver it. Check your DM permissions and that you share a server with the bot.", skipped: "Skipped because your check-ins or settings changed", }; export function ReminderSettings({ timezone, onBusyChange, disabled = false }: { timezone: string; onBusyChange?: (busy: boolean) => void; disabled?: boolean }) { const [settings, setSettings] = useState(); const [draft, setDraft] = useState({ ...defaultReminder, reminders: entries(defaultReminder) }); const [error, setError] = useState(""); const [notice, setNotice] = useState(""); const [busy, setBusy] = useState(false); const [attempt, setAttempt] = useState(0); const saving = useRef(false); useEffect(() => { const controller = new AbortController(); setError(""); habitRequest("/reminders", { signal: controller.signal }) .then(data => { if (!controller.signal.aborted) { setSettings(data); setDraft({ enabled: data.enabled, time: data.time ?? "20:00", reminders: entries(data), quietStart: data.quietStart, quietEnd: data.quietEnd }); } }) .catch(error => { if (!controller.signal.aborted) setError(error.message); }); return () => controller.abort(); }, [attempt]); const changeEntry = (index: number, entry: ReminderEntry) => setDraft(value => ({ ...value, reminders: value.reminders.map((previous, position) => position === index ? entry : previous) })); return

A private Discord nudge when you still have habits left today. Named reminders include their name in the message. No habit names are sent.

Times in {timezone}

}> {!settings ? error ? :

Loading reminders…

:
{ event.preventDefault(); if (saving.current || disabled) return; const form = event.currentTarget; const submitted = { enabled: draft.enabled, reminders: draft.reminders.map((entry, index) => ({ ...entry, time: (form.elements.namedItem(`reminder-time-${index}`) as HTMLInputElement).value, ...(entry.type === "date" ? { date: (form.elements.namedItem(`reminder-date-${index}`) as HTMLInputElement).value } : {}) })), quietStart: draft.quietStart, quietEnd: draft.quietEnd, ...Object.fromEntries((["quietStart", "quietEnd"] as const).map(key => [key, (form.elements.namedItem(key) as HTMLInputElement).value])) }; saving.current = true; setBusy(true); onBusyChange?.(true); setError(""); setNotice(""); try { await habitRequest("/reminders", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(submitted) }); setDraft(value => ({ ...value, ...submitted })); setNotice(submitted.enabled ? "Discord reminders saved." : "Reminders turned off."); } catch (error) { setError(error instanceof Error ? error.message : "Could not save reminders. Try again."); } finally { saving.current = false; setBusy(false); onBusyChange?.(false); } }}> {!settings.available &&

Discord reminders are not configured on this server.

}
setDraft(value => ({ ...value, enabled: event.target.checked }))} /> {draft.reminders.map((entry, index) =>
{id => changeEntry(index, { ...entry, name: event.target.value })} />}
{id => changeEntry(index, { ...entry, time: event.currentTarget.value })} />} {id => }
{entry.type === "date" ? {id => changeEntry(index, { ...entry, date: event.currentTarget.value })} />} :
{[[1, "Mon"], [2, "Tue"], [3, "Wed"], [4, "Thu"], [5, "Fri"], [6, "Sat"], [0, "Sun"]].map(([day, label]) => changeEntry(index, { ...entry, days: event.target.checked ? [...entry.days, Number(day)] : entry.days.filter(value => value !== day) })} />)}
}
)}
{([['quietStart', 'Quiet hours start'], ['quietEnd', 'Quiet hours end']] as const).map(([key, label]) => {id => })}
{settings.lastDelivery &&

LAST REMINDER{settings.lastDelivery.date}: {deliveryLabels[settings.lastDelivery.status] ?? "Unknown delivery status"}

}
} {error} {notice}
; }