import type { Hono } from "hono";
import { html } from "../pages/html.ts";
import { relativeTime } from "../pages/layout.ts";
import { NOTIFICATION_KINDS, type NotificationKind, setting } from "../server/db.ts";
import { type AdminContext, adminPage, formField, textFields } from "./console.ts";
import { saveSettings } from "./store.ts";
type Notification = { id: number; text: string; href: string; created_at: number; read: number };
export const LABELS: Record = {
issue_filed: "an issue is filed",
issue_followup: "someone replies to an issue",
sync_failed: "a repo's sync starts failing",
backup_failed: "a backup starts failing",
};
/** §24's Notifications section: the list, opening one, mark all read, and which kinds to raise. */
export function registerNotifications(app: Hono, ctx: AdminContext): void {
app.get("/admin/notifications", (c) => {
if (c.req.query("format") === "json") {
const unread =
ctx.kepr
.query<{ n: number }, []>("SELECT COUNT(*) AS n FROM notifications WHERE read = 0")
.get()?.n ?? 0;
const recent = ctx.kepr
.query(
"SELECT id, text, href, created_at, read FROM notifications ORDER BY id DESC LIMIT 5",
)
.all();
return c.json({
unread,
notifications: recent.map(({ id, text, created_at, read }) => ({
id,
text,
read,
when: relativeTime(created_at),
})),
});
}
const rows = ctx.kepr
.query(
"SELECT id, text, href, created_at, read FROM notifications ORDER BY id DESC",
)
.all();
const row = (n: Notification) =>
html`
${n.text}
${n.read ? "" : "unread · "}${relativeTime(n.created_at)}`;
const kind = (k: NotificationKind) =>
html``;
const body = html`
${rows.length > 0 ? rows.map(row) : html`nothing yet
`}
`;
return c.html(adminPage(c, ctx, "notifications", body));
});
app.get("/admin/notifications/:id{[0-9]{1,9}}", (c) => {
const found = ctx.kepr
.query<{ href: string }, [number]>(
"UPDATE notifications SET read = 1 WHERE id = ? RETURNING href",
)
.get(Number(c.req.param("id")));
return found ? c.redirect(found.href, 303) : c.notFound();
});
app.post("/admin/notifications", async (c) => {
const fields = await textFields(c);
if (fields.action === "read_all") {
ctx.kepr.run("UPDATE notifications SET read = 1 WHERE read = 0");
} else if (fields.action === "preferences") {
// A box left unticked is a kind switched off; the setting records only that.
saveSettings(
ctx.kepr,
Object.fromEntries(
NOTIFICATION_KINDS.map((k) => [`notify_off_${k}`, fields[k] === "1" ? "" : "1"]),
),
);
} else return c.text("Unknown action.", 400);
return c.redirect("/admin/notifications?saved=1", 303);
});
}