aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 09:16:16 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 09:16:16 +0200
commit73d7aff4248a5ab05cec55dbd7feb0d17b97c1df (patch)
tree31f7c449033f87cf189566640655400f182fa140
parent1e70e552db6790f2fd07fdd1721bf6cf76e0c6b3 (diff)
downloadnotifyd-73d7aff4248a5ab05cec55dbd7feb0d17b97c1df.tar.gz
notifyd-73d7aff4248a5ab05cec55dbd7feb0d17b97c1df.zip
feat(notify): make the queue and history caps settable
A lowered cap applies at the next add rather than at once, so reloading the config never drops a notification the user has not seen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--internal/notify/store.go25
-rw-r--r--internal/notify/store_test.go23
2 files changed, 40 insertions, 8 deletions
diff --git a/internal/notify/store.go b/internal/notify/store.go
index 7a1a745..3648983 100644
--- a/internal/notify/store.go
+++ b/internal/notify/store.go
@@ -36,11 +36,6 @@ type live struct {
Closed bool
}
-const (
- liveCap = 20
- historyCap = 20
-)
-
// Store holds the live queue and the history ring. Time, signals and file
// writes are injected, so the whole thing is tested without a bus or a clock.
type Store struct {
@@ -49,6 +44,8 @@ type Store struct {
order []uint32
entries map[uint32]*live
history []*live
+ liveCap int
+ historyCap int
emit func(id, reason uint32)
publish func(live, history []Popup)
removeImage func(string)
@@ -58,6 +55,8 @@ func NewStore(emit func(id, reason uint32), publish func(live, history []Popup),
return &Store{
nextID: 1,
entries: map[uint32]*live{},
+ liveCap: DefaultConfig().LiveCap,
+ historyCap: DefaultConfig().HistoryCap,
emit: emit,
publish: publish,
removeImage: removeImage,
@@ -168,6 +167,16 @@ func (s *Store) ClearHistory() {
s.publishLocked()
}
+// SetCaps changes the queue and history bounds. Nothing is trimmed here: a
+// reload must not silently drop live notifications, so a lowered cap applies
+// at the next add.
+func (s *Store) SetCaps(live, history int) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.liveCap = live
+ s.historyCap = history
+}
+
// Actionable reports whether a client is still listening on the id.
func (s *Store) Actionable(id uint32) bool {
s.mu.Lock()
@@ -223,13 +232,13 @@ func (s *Store) fileLocked(n *live, reason uint32) {
s.emit(n.ID, reason)
}
s.history = append([]*live{n}, s.history...)
- if len(s.history) > historyCap {
- s.history = s.history[:historyCap]
+ if len(s.history) > s.historyCap {
+ s.history = s.history[:s.historyCap]
}
}
func (s *Store) evictLocked() {
- for len(s.order) > liveCap {
+ for len(s.order) > s.liveCap {
id := s.order[len(s.order)-1]
n := s.entries[id]
s.removeLocked(id)
diff --git a/internal/notify/store_test.go b/internal/notify/store_test.go
index 13f78dc..cc351be 100644
--- a/internal/notify/store_test.go
+++ b/internal/notify/store_test.go
@@ -164,3 +164,26 @@ func TestStoreRemovesImageOnReplace(t *testing.T) {
t.Fatalf("replace removed %v", removed)
}
}
+
+func TestSetCapsAppliesAtNextAdd(t *testing.T) {
+ s, _ := newTestStore()
+ for i := 0; i < 5; i++ {
+ id, _ := s.Add(popup("a"), "", 0)
+ s.Dismiss(id, 2)
+ }
+ for i := 0; i < 4; i++ {
+ s.Add(popup("a"), "", 0)
+ }
+ s.SetCaps(2, 3)
+ // Lowering a cap drops nothing by itself.
+ if got := len(s.historySnapshot()); got != 5 {
+ t.Fatalf("history after SetCaps = %d, want 5", got)
+ }
+ s.Add(popup("a"), "", 0)
+ if got := len(s.order); got != 2 {
+ t.Errorf("live = %d, want 2", got)
+ }
+ if got := len(s.historySnapshot()); got != 3 {
+ t.Errorf("history = %d, want 3", got)
+ }
+}