home.Service aggregates the Home screen from the services that own each value, at "now" in the household's zone: the date label, tonight's dinner from the current week's plan (null with no plan), the pending Side quest count from the deck, the Supply run status of the upcoming week or else the current one, and recentUpdates.unread. unread is whether a notification was stored in notification_outbox after household_settings.updates_seen_at (or at all while it is null), read through the new notify.StoredSince over the HasNotificationSince query. Nothing writes updates_seen_at yet; the updates task adds that. Tests under America/Chicago show tonight's dinner flips at local midnight, not UTC midnight, and cover no plan, no suggestions, the Supply run choice and unread. Maestro task 9ccfc3a6-cc7b-493a-aa4b-2b455d26f4df. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
3.1 KiB
SQL
77 lines
3.1 KiB
SQL
-- name: GetNotificationPref :one
|
|
SELECT * FROM notification_prefs
|
|
WHERE event = ?;
|
|
|
|
-- name: InsertNotification :execrows
|
|
-- Stores a pending notification for one channel, due at once. A row already
|
|
-- stored for the event key and channel, in any status, is left unchanged and
|
|
-- no row is inserted.
|
|
INSERT INTO notification_outbox (
|
|
id, event_key, channel, payload, status, attempts, next_attempt_at, created_at, updated_at
|
|
) VALUES (
|
|
sqlc.arg(id), sqlc.arg(event_key), sqlc.arg(channel), sqlc.arg(payload), 'pending', 0, sqlc.arg(next_attempt_at), sqlc.arg(now), sqlc.arg(now)
|
|
)
|
|
ON CONFLICT (event_key, channel) DO NOTHING;
|
|
|
|
-- name: ClaimDueNotification :one
|
|
-- Leases the pending notification due longest ago by moving its
|
|
-- next_attempt_at to lease_until, and returns it. A claimed row is not due
|
|
-- again until the lease ends, so no other claim takes it while it is being
|
|
-- delivered. Returns sql.ErrNoRows when none is due.
|
|
UPDATE notification_outbox
|
|
SET next_attempt_at = sqlc.arg(lease_until), updated_at = sqlc.arg(now)
|
|
WHERE id = (
|
|
SELECT id FROM notification_outbox
|
|
WHERE status = 'pending' AND next_attempt_at <= sqlc.arg(now)
|
|
ORDER BY next_attempt_at, id
|
|
LIMIT 1
|
|
)
|
|
RETURNING *;
|
|
|
|
-- name: MarkNotificationSent :execrows
|
|
-- Records a delivered notification; updated_at is when it was sent. The
|
|
-- lease must still be the claim's.
|
|
UPDATE notification_outbox
|
|
SET status = 'sent', attempts = attempts + 1, next_attempt_at = NULL, updated_at = sqlc.arg(now)
|
|
WHERE id = sqlc.arg(id) AND status = 'pending' AND next_attempt_at = sqlc.arg(lease_until);
|
|
|
|
-- name: RetryNotification :execrows
|
|
-- Records a failed delivery and when to try again. The lease must still be
|
|
-- the claim's.
|
|
UPDATE notification_outbox
|
|
SET attempts = attempts + 1, next_attempt_at = sqlc.arg(next_attempt_at), last_error = sqlc.arg(last_error), updated_at = sqlc.arg(now)
|
|
WHERE id = sqlc.arg(id) AND status = 'pending' AND next_attempt_at = sqlc.arg(lease_until);
|
|
|
|
-- name: FailNotification :execrows
|
|
-- Gives up on a notification. attempts counts the delivery attempt that
|
|
-- failed, if one was made. The lease must still be the claim's.
|
|
UPDATE notification_outbox
|
|
SET status = 'failed', attempts = attempts + sqlc.arg(attempted), next_attempt_at = NULL, last_error = sqlc.arg(last_error), updated_at = sqlc.arg(now)
|
|
WHERE id = sqlc.arg(id) AND status = 'pending' AND next_attempt_at = sqlc.arg(lease_until);
|
|
|
|
-- name: NextNotificationDue :one
|
|
-- Returns when the next pending notification is due. Returns sql.ErrNoRows
|
|
-- when none is pending.
|
|
SELECT next_attempt_at FROM notification_outbox
|
|
WHERE status = 'pending'
|
|
ORDER BY next_attempt_at
|
|
LIMIT 1;
|
|
|
|
-- name: ListNotificationPrefs :many
|
|
SELECT * FROM notification_prefs;
|
|
|
|
-- name: UpdateNotificationPref :execrows
|
|
-- Sets which channels an event goes to; affects no row when the event has
|
|
-- no preference row.
|
|
UPDATE notification_prefs
|
|
SET push = ?, chat = ?, updated_at = ?
|
|
WHERE event = ?;
|
|
|
|
-- name: HasNotificationSince :one
|
|
-- Reports whether a notification was stored after since, or at all when
|
|
-- since is NULL.
|
|
SELECT EXISTS (
|
|
SELECT 1 FROM notification_outbox
|
|
WHERE CAST(sqlc.narg(since) AS TEXT) IS NULL OR created_at > CAST(sqlc.narg(since) AS TEXT)
|
|
) AS found;
|