Files
minstrel/internal/api/notify_producers.go
T
bvandeusenandClaude Opus 5.5 854c307834
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
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)
- 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>
2026-10-19 22:44:19 -04:00

86 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package api
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
"git.fabledsword.com/bvandeusen/minstrel/internal/library"
"git.fabledsword.com/bvandeusen/minstrel/internal/notifications"
"git.fabledsword.com/bvandeusen/minstrel/internal/requests"
)
// Helpers for the handlers that produce notifications (M489). Every call is
// NotifyLogged: a notification never fails the action that caused it.
// userLabel is how a user is named to someone else: their display name, or
// their username when they have none.
func userLabel(u dbq.User) string {
if u.DisplayName != nil && *u.DisplayName != "" {
return *u.DisplayName
}
return u.Username
}
func requestPayload(row dbq.Request) notifications.Payload {
return notifications.Payload{
RequestID: uuidToString(row.ID),
RequestKind: string(row.Kind),
Name: requests.DisplayName(row),
}
}
// notifyRequestDecided tells the requester an admin approved or declined
// their request. An admin deciding their own request needs no notice.
func (h *handlers) notifyRequestDecided(ctx context.Context, kind notifications.Kind, admin dbq.User, row dbq.Request, reason string) {
if row.UserID == admin.ID {
return
}
p := requestPayload(row)
p.Reason = reason
h.notifier.NotifyLogged(ctx, kind, notifications.ToUser(row.UserID), p.Map())
}
// quarantineReasonLabel is a flag reason as the flag popover words it, in
// lower case to sit inside a sentence ("flagged X: bad rip").
func quarantineReasonLabel(reason string) string {
switch reason {
case "bad_rip":
return "bad rip"
case "wrong_file":
return "wrong file"
case "wrong_tags":
return "wrong tags"
case "duplicate":
return "duplicate"
default:
return ""
}
}
// trackLabel names a track "Artist – Title" for a notification, falling back
// to the title alone, or to nothing, rather than failing the caller.
func (h *handlers) trackLabel(ctx context.Context, trackID pgtype.UUID) string {
q := dbq.New(h.pool)
t, err := q.GetTrackByID(ctx, trackID)
if err != nil {
return ""
}
if a, aerr := q.GetArtistByID(ctx, t.ArtistID); aerr == nil {
return library.TrackLabel(a.Name, t.Title)
}
return t.Title
}
// notifyPlaybackErrors tells admins a track failed to play, naming it and who
// it failed for (M542). The notice coalesces: while it is unread each report
// adds one to its count and its name to the front of the list.
func (h *handlers) notifyPlaybackErrors(ctx context.Context, trackID pgtype.UUID, user dbq.User) {
p := notifications.Payload{Count: 1}
if name := h.trackLabel(ctx, trackID); name != "" {
p.Items = []string{name + " (" + userLabel(user) + ")"}
}
h.notifier.NotifyLogged(ctx, notifications.KindPlaybackErrors, notifications.ToAdmins(pgtype.UUID{}), p.Map())
}