// Generated, do not edit. sha256:2e1c42ef1941376d221fab1a61b5e60c9be06530764fa9486b752263252805ed import * as db from '../db.js'; import { ENUMS } from '../../shared/config.js'; import { nowIso, todayIso } from './_shared.js'; import { listPendingAiProposals } from './ai-proposals.js'; import { listPendingProposals } from './proposals.js'; import { dueCreditsAll } from './interest-credits.js'; import { listOpenValuationDiffs } from './valuations.js'; import { lightningBalanceDiffs } from './positions.js'; import { listDueReservations } from './reservations.js'; const dateToInt = d => Number(String(d).slice(0, 10).replace(/-/g, '')); const NOTIFICATION_SOURCES = { reconcile: { action: true, state() { const p = listPendingProposals(); return { count: p.length, highWater: p.reduce((m, x) => Math.max(m, x.id), 0) }; } }, interest: { action: true, state() { const due = dueCreditsAll(); return { count: due.length, highWater: due.reduce((m, d) => Math.max(m, dateToInt(d.periodEnd)), 0) }; } }, valuation: { action: true, state() { const diffs = listOpenValuationDiffs(); return { count: diffs.length + lightningBalanceDiffs().length, highWater: diffs.reduce((m, v) => Math.max(m, v.id), 0) }; } }, schedule_ended: { action: false, state() { const rows = db.all(`SELECT id, endsOn FROM InterestSchedule WHERE endsOn < ?`, [todayIso()]); return { count: rows.length, highWater: rows.reduce((m, r) => Math.max(m, dateToInt(r.endsOn)), 0) }; } }, source_proposal: { action: true, state() { const rows = db.all(`SELECT assetId, MAX(id) AS mid FROM PriceSource WHERE status = 'proposed' GROUP BY assetId`); return { count: rows.length, highWater: rows.reduce((m, r) => Math.max(m, r.mid), 0) }; } }, ai_proposal: { action: true, state() { const rows = listPendingAiProposals(); return { count: rows.length, highWater: rows.reduce((m, r) => Math.max(m, r.id), 0) }; } }, reservation: { action: true, state() { const rows = listDueReservations(); return { count: rows.length, highWater: rows.reduce((m, r) => Math.max(m, dateToInt(r.occurredAt)), 0) }; } }, }; export async function refreshNotifications() { let changed = false; for (const kind of ENUMS.NOTIFICATION_KIND) { const src = NOTIFICATION_SOURCES[kind]; if (!src) continue; const { count, highWater } = src.state(); const active = db.one(`SELECT id, count, highWater FROM Notification WHERE kind = ? AND dismissedAt IS NULL ORDER BY id DESC LIMIT 1`, [kind]); if (count === 0) { if (src.action && active) { db.run(`DELETE FROM Notification WHERE id = ?`, [active.id]); changed = true; } continue; } if (active) { if (active.count !== count || active.highWater !== highWater) { db.run(`UPDATE Notification SET count = ?, highWater = ? WHERE id = ?`, [count, highWater, active.id]); changed = true; } continue; } const last = db.one(`SELECT highWater FROM Notification WHERE kind = ? ORDER BY id DESC LIMIT 1`, [kind]); if (!last || highWater > last.highWater) { db.run(`INSERT INTO Notification (kind, count, highWater, createdAt) VALUES (?,?,?,?)`, [kind, count, highWater, nowIso()]); changed = true; } } if (changed) await db.persist(); } export function listActiveNotifications() { return db.all(`SELECT id, kind, count, highWater, createdAt FROM Notification WHERE dismissedAt IS NULL ORDER BY id DESC`); } export async function currentNotifications() { await refreshNotifications(); return listActiveNotifications(); } export function notificationDetail(n) { if (n.kind !== 'schedule_ended') return null; const rows = db.all(` SELECT s.accountId, ac.name AS account, po.symbol AS asset FROM InterestSchedule s JOIN Account ac ON ac.id = s.accountId JOIN Asset po ON po.id = s.payoutAssetId WHERE s.endsOn < ? ORDER BY s.endsOn DESC, s.id DESC`, [todayIso()]); if (!rows.length) return null; return { accountId: rows[0].accountId, account: rows[0].account, asset: rows[0].asset, extra: rows.length - 1 }; } export async function dismissNotification(id) { db.run(`UPDATE Notification SET dismissedAt = ? WHERE id = ? AND dismissedAt IS NULL`, [nowIso(), id]); await db.persist(); }