77#include " util/file_utils.h"
88#include " util/string_utils.h"
99
10+ #include < algorithm>
1011#include < filesystem>
1112#include < string_view>
1213
@@ -99,13 +100,16 @@ void NotificationManager::rebuildHistoryIndex() {
99100
100101void NotificationManager::upsertHistory (const Notification& notification, bool active,
101102 std::optional<CloseReason> closeReason) {
103+ bool seen = false ;
102104 if (const auto it = m_historyIndex.find (notification.id ); it != m_historyIndex.end ()) {
105+ seen = m_history[it->second ].seen ;
103106 m_history.erase (m_history.begin () + static_cast <std::ptrdiff_t >(it->second ));
104107 }
105108
106109 m_history.push_back (NotificationHistoryEntry{
107110 .notification = notification,
108111 .active = active,
112+ .seen = seen,
109113 .closeReason = closeReason,
110114 .eventSerial = ++m_changeSerial,
111115 });
@@ -131,6 +135,20 @@ void NotificationManager::removeEventCallback(int token) {
131135 std::erase_if (m_eventCallbacks, [token](const auto & pair) { return pair.first == token; });
132136}
133137
138+ bool NotificationManager::computeHasUnreadNotificationHistory () const noexcept {
139+ return std::any_of (m_history.begin (), m_history.end (),
140+ [](const NotificationHistoryEntry& entry) { return !entry.seen ; });
141+ }
142+
143+ void NotificationManager::notifyUnreadStateChangedIfNeeded (bool previousUnreadState) {
144+ if (m_stateCallback == nullptr ) {
145+ return ;
146+ }
147+ if (computeHasUnreadNotificationHistory () != previousUnreadState) {
148+ m_stateCallback ();
149+ }
150+ }
151+
134152uint32_t NotificationManager::addOrReplace (uint32_t replacesId, std::string appName, std::string summary,
135153 std::string body, Urgency urgency, int32_t timeout,
136154 NotificationOrigin origin, std::vector<std::string> actions,
@@ -174,7 +192,9 @@ uint32_t NotificationManager::addOrReplace(uint32_t replacesId, std::string appN
174192
175193 logNotification (n, " updated" );
176194 if (shouldTrackHistory (n.origin , n.urgency )) {
195+ const bool hadUnreadBefore = computeHasUnreadNotificationHistory ();
177196 upsertHistory (n, true , std::nullopt );
197+ notifyUnreadStateChangedIfNeeded (hadUnreadBefore);
178198 } else {
179199 removeHistoryEntry (n.id );
180200 }
@@ -223,8 +243,9 @@ uint32_t NotificationManager::addOrReplace(uint32_t replacesId, std::string appN
223243 const auto & n = m_notifications.back ();
224244 logNotification (n, " added" );
225245 if (shouldTrackHistory (n.origin , n.urgency )) {
246+ const bool hadUnreadBefore = computeHasUnreadNotificationHistory ();
226247 upsertHistory (n, true , std::nullopt );
227- m_unreadSinceHistoryVisit = true ;
248+ notifyUnreadStateChangedIfNeeded (hadUnreadBefore) ;
228249 }
229250
230251 for (auto & [token, cb] : m_eventCallbacks) {
@@ -338,6 +359,9 @@ bool NotificationManager::close(uint32_t id, CloseReason reason) {
338359
339360 const size_t index = it->second ;
340361 const Notification closed = m_notifications[index];
362+ const bool hadUnreadBefore = computeHasUnreadNotificationHistory ();
363+ const bool historyHandledUnreadChange =
364+ shouldTrackHistory (closed.origin , closed.urgency ) && reason == CloseReason::Dismissed;
341365 const char * reasonStr = (reason == CloseReason::Expired) ? " expired"
342366 : (reason == CloseReason::Dismissed) ? " dismissed"
343367 : " closed" ;
@@ -368,6 +392,10 @@ bool NotificationManager::close(uint32_t id, CloseReason reason) {
368392 m_closeCallback (id, reason);
369393 }
370394
395+ if (!historyHandledUnreadChange) {
396+ notifyUnreadStateChangedIfNeeded (hadUnreadBefore);
397+ }
398+
371399 return true ;
372400}
373401
@@ -383,13 +411,15 @@ void NotificationManager::removeHistoryEntry(uint32_t id, std::optional<CloseRea
383411 return ;
384412 }
385413
414+ const bool hadUnreadBefore = computeHasUnreadNotificationHistory ();
386415 const CloseReason reason =
387416 dbusCloseReason.value_or (m_history[it->second ].closeReason .value_or (CloseReason::Dismissed));
388417 emitPendingDBusClose (id, reason);
389418 m_history.erase (m_history.begin () + static_cast <std::ptrdiff_t >(it->second ));
390419 ++m_changeSerial;
391420 rebuildHistoryIndex ();
392421 schedulePersistHistory ();
422+ notifyUnreadStateChangedIfNeeded (hadUnreadBefore);
393423}
394424
395425void NotificationManager::clearHistory () {
@@ -486,16 +516,20 @@ void NotificationManager::setStateCallback(StateCallback callback) { m_stateCall
486516
487517void NotificationManager::setSoundPlayer (SoundPlayer* soundPlayer) { m_soundPlayer = soundPlayer; }
488518
489- bool NotificationManager::hasUnreadNotificationHistory () const noexcept { return m_unreadSinceHistoryVisit; }
519+ bool NotificationManager::hasUnreadNotificationHistory () const noexcept {
520+ return computeHasUnreadNotificationHistory ();
521+ }
490522
491523void NotificationManager::markNotificationHistorySeen () {
492- if (!m_unreadSinceHistoryVisit) {
524+ const bool hadUnreadBefore = computeHasUnreadNotificationHistory ();
525+ if (!hadUnreadBefore) {
493526 return ;
494527 }
495- m_unreadSinceHistoryVisit = false ;
496- if (m_stateCallback) {
497- m_stateCallback ();
528+ for (auto & entry : m_history) {
529+ entry.seen = true ;
498530 }
531+ schedulePersistHistory ();
532+ notifyUnreadStateChangedIfNeeded (hadUnreadBefore);
499533}
500534
501535void NotificationManager::schedulePersistHistory () {
0 commit comments