Skip to content

Commit d5af752

Browse files
authored
Merge pull request #2785 from Ape/pr/notification-per-entry-seen
notification: track unread history with per-entry seen state
2 parents f19b25c + 3fd174d commit d5af752

3 files changed

Lines changed: 45 additions & 7 deletions

File tree

src/notification/notification_history_store.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ bool loadNotificationHistoryFromFile(const std::filesystem::path& path, std::deq
650650
NotificationHistoryEntry he;
651651
he.notification = notificationFromJson(item.at("notification"), path);
652652
he.active = item.value("active", false);
653+
he.seen = item.value("seen", true);
653654
if (item.contains("close_reason") && !item["close_reason"].is_null()) {
654655
const auto crs = item["close_reason"].get<std::string>();
655656
he.closeReason = closeReasonFrom(crs);
@@ -686,6 +687,7 @@ bool saveNotificationHistoryToFile(const std::filesystem::path& path,
686687
nlohmann::json je;
687688
je["notification"] = notificationToJson(he.notification, path);
688689
je["active"] = he.active;
690+
je["seen"] = he.seen;
689691
if (he.closeReason.has_value()) {
690692
je["close_reason"] = std::string(closeReasonStr(*he.closeReason));
691693
} else {

src/notification/notification_manager.cpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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

100101
void 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+
134152
uint32_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

395425
void NotificationManager::clearHistory() {
@@ -486,16 +516,20 @@ void NotificationManager::setStateCallback(StateCallback callback) { m_stateCall
486516

487517
void 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

491523
void 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

501535
void NotificationManager::schedulePersistHistory() {

src/notification/notification_manager.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ enum class NotificationEvent {
1919
struct NotificationHistoryEntry {
2020
Notification notification;
2121
bool active = true;
22+
bool seen = false;
2223
std::optional<CloseReason> closeReason;
2324
std::uint64_t eventSerial = 0;
2425
};
@@ -120,6 +121,8 @@ class NotificationManager {
120121
void schedulePersistHistory();
121122
void persistHistoryToDisk();
122123
void emitPendingDBusClose(uint32_t id, CloseReason reason);
124+
[[nodiscard]] bool computeHasUnreadNotificationHistory() const noexcept;
125+
void notifyUnreadStateChangedIfNeeded(bool previousUnreadState);
123126

124127
bool m_persistScheduled = false;
125128

@@ -137,6 +140,5 @@ class NotificationManager {
137140
uint32_t m_nextId{1};
138141
std::uint64_t m_changeSerial{0};
139142
bool m_doNotDisturb = false;
140-
bool m_unreadSinceHistoryVisit = false;
141143
class SoundPlayer* m_soundPlayer = nullptr;
142144
};

0 commit comments

Comments
 (0)