feat: notifications say what changed and alert once: a grouped notice names the tracks, grows without re-alerting, and a phone pile lists its titles (M542 #6146-#6151)
release / govulncheck (push) Successful in 14s
release / web (push) Successful in 1m22s
release / go (push) Successful in 1m33s
release / android (push) Failing after 1m42s
release / integration (push) Failing after 4m42s
release / Build signed APK (releases and dev) (push) Successful in 4m55s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 14s
release / web (push) Successful in 1m22s
release / go (push) Successful in 1m33s
release / android (push) Failing after 1m42s
release / integration (push) Failing after 4m42s
release / Build signed APK (releases and dev) (push) Successful in 4m55s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
- An update to a grouped notice keeps its arrival time and email state, so the phone and the email no longer go off again each time it grows. - Notices carry the names of what changed (newest ten), shown as three and "and N more": tracks still missing after the scan's move/replacement fold, duplicate groups new to a sweep, songs merged automatically, and the track and listener a playback error was for. - Duplicates found counts only the groups new to that sweep; playback errors count reports, not the unresolved total. - A scan failure says what stopped and links to Admin > Library. - Android: a pile of more than three alerts lists the newest five titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,30 +13,29 @@ RETURNING id;
|
||||
|
||||
-- name: UpsertCoalescedNotification :one
|
||||
-- One unread row per (user, coalesce_key). A new event while that row is
|
||||
-- unread updates it in place and moves it back to the top of the inbox.
|
||||
-- unread updates it in place: its `count` adds to the row's, and its `items`
|
||||
-- (the names of what changed) go in front of the row's, keeping the newest
|
||||
-- ten. Every coalesced kind's event is "N more happened", never a restated
|
||||
-- total, so the row always says what is new since it was last read.
|
||||
--
|
||||
-- sum_count: the payload's `count` adds to the unread row's count rather than
|
||||
-- replacing it. For kinds whose event is "N more happened" (tracks marked
|
||||
-- missing). Kinds whose event states the whole current total (pending
|
||||
-- duplicate groups) pass false and the payload simply replaces.
|
||||
--
|
||||
-- emailed_at is cleared so the newer state goes out in the next batch; an
|
||||
-- emailed-but-unread row that keeps growing is news the user hasn't seen.
|
||||
-- Unless email_wanted is false, as in InsertNotification.
|
||||
-- created_at and emailed_at are left alone (M542): an update changes what the
|
||||
-- notice says, not when it arrived. Moving created_at made the phone announce
|
||||
-- it again and clearing emailed_at sent it again, so a notice the hourly
|
||||
-- duplicate pass kept growing alerted every hour.
|
||||
INSERT INTO user_notifications (user_id, kind, payload, coalesce_key, emailed_at)
|
||||
VALUES (sqlc.arg(user_id), sqlc.arg(kind), sqlc.arg(payload), sqlc.arg(coalesce_key),
|
||||
CASE WHEN sqlc.arg(email_wanted)::boolean THEN NULL ELSE now() END)
|
||||
ON CONFLICT (user_id, coalesce_key) WHERE read_at IS NULL AND coalesce_key IS NOT NULL
|
||||
DO UPDATE SET
|
||||
payload = CASE
|
||||
WHEN sqlc.arg(sum_count)::boolean THEN
|
||||
EXCLUDED.payload || jsonb_build_object('count',
|
||||
COALESCE((user_notifications.payload->>'count')::bigint, 0)
|
||||
+ COALESCE((EXCLUDED.payload->>'count')::bigint, 0))
|
||||
ELSE EXCLUDED.payload
|
||||
END,
|
||||
created_at = now(),
|
||||
emailed_at = EXCLUDED.emailed_at
|
||||
payload = EXCLUDED.payload || jsonb_build_object(
|
||||
'count', COALESCE((user_notifications.payload->>'count')::bigint, 0)
|
||||
+ COALESCE((EXCLUDED.payload->>'count')::bigint, 0),
|
||||
'items', (SELECT COALESCE(jsonb_agg(e.item ORDER BY e.n), '[]'::jsonb)
|
||||
FROM jsonb_array_elements(
|
||||
COALESCE(EXCLUDED.payload->'items', '[]'::jsonb)
|
||||
|| COALESCE(user_notifications.payload->'items', '[]'::jsonb)
|
||||
) WITH ORDINALITY AS e(item, n)
|
||||
WHERE e.n <= 10))
|
||||
RETURNING id;
|
||||
|
||||
-- name: ListNotifications :many
|
||||
@@ -65,8 +64,9 @@ UPDATE user_notifications
|
||||
-- name: MarkAllNotificationsRead :execrows
|
||||
-- up_to, when set, limits it to what existed when the user asked: a "mark
|
||||
-- all read" queued offline and replayed later must not mark notices that
|
||||
-- arrived in between, which the user never saw. A coalesced row updated
|
||||
-- since then carries a newer created_at, so it stays unread too.
|
||||
-- arrived in between, which the user never saw. A coalesced row that grew
|
||||
-- since then is marked read with it: it arrived before they asked, and its
|
||||
-- growth is a quiet update to a notice they had seen (M542).
|
||||
UPDATE user_notifications
|
||||
SET read_at = now()
|
||||
WHERE user_id = sqlc.arg(user_id)
|
||||
|
||||
Reference in New Issue
Block a user