diff --git a/server/store/store.go b/server/store/store.go index 588d027..e2c5f1e 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -1538,6 +1538,10 @@ func (subsMapper) Get(tenantID types.TenantID, topic string, user types.Uid, kee // Update values of topic's subscriptions. func (subsMapper) Update(tenantID types.TenantID, topic string, user types.Uid, update map[string]any) error { + if update == nil { + update = map[string]any{} + } + invalidateUnread := subscriptionUpdateAffectsUnread(update) update["UpdatedAt"] = types.TimeNow() tenantAdp, err := tenantBusinessAdapter(tenantID) if err != nil { @@ -1547,6 +1551,9 @@ func (subsMapper) Update(tenantID types.TenantID, topic string, user types.Uid, return err } invalidateSubscriptionCache(tenantID, topic, user) + if invalidateUnread { + DeleteCachedUserUnreadCount(tenantID, user) + } return nil } @@ -1561,9 +1568,20 @@ func (subsMapper) Delete(tenantID types.TenantID, topic string, user types.Uid) return err } invalidateSubscriptionCache(tenantID, topic, user) + DeleteCachedUserUnreadCount(tenantID, user) return nil } +func subscriptionUpdateAffectsUnread(update map[string]any) bool { + for key := range update { + switch key { + case "ReadSeqId", "DelId", "ModeWant", "ModeGiven", "DeletedAt": + return true + } + } + return false +} + func subscriptionCacheID(topic string, user types.Uid, keepDeleted bool) string { if keepDeleted { return topic + ":" + user.String() + ":any" @@ -3596,6 +3614,7 @@ func (messagesMapper) PersistPrepared(tenantID types.TenantID, msg *types.Messag logs.Warn.Printf("topic[%s]: failed to mark message (seq: %d) read by sender - err: %+v", msg.Topic, msg.SeqId, subErr) } else { invalidateSubscriptionCache(tenantID, msg.Topic, fromUid) + DeleteCachedUserUnreadCount(tenantID, fromUid) markedReadBySender = true } } diff --git a/server/store/subscription_unread_test.go b/server/store/subscription_unread_test.go new file mode 100644 index 0000000..ed32e41 --- /dev/null +++ b/server/store/subscription_unread_test.go @@ -0,0 +1,28 @@ +package store + +import "testing" + +func TestSubscriptionUpdateAffectsUnread(t *testing.T) { + tests := []struct { + name string + update map[string]any + want bool + }{ + {name: "read seq", update: map[string]any{"ReadSeqId": int64(12)}, want: true}, + {name: "delete marker", update: map[string]any{"DelId": 4}, want: true}, + {name: "want mode", update: map[string]any{"ModeWant": 1}, want: true}, + {name: "given mode", update: map[string]any{"ModeGiven": 1}, want: true}, + {name: "deleted", update: map[string]any{"DeletedAt": "now"}, want: true}, + {name: "recv only", update: map[string]any{"RecvSeqId": int64(12)}, want: false}, + {name: "private only", update: map[string]any{"Private": map[string]any{"mute": true}}, want: false}, + {name: "empty", update: map[string]any{}, want: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := subscriptionUpdateAffectsUnread(tc.update); got != tc.want { + t.Fatalf("subscriptionUpdateAffectsUnread() = %v, want %v", got, tc.want) + } + }) + } +}