Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -994,11 +994,13 @@
"filter": {
"add": "Add filter",
"add-title": "Add Notification Filter",
"allow-permanent": "Allow permanent",
"allowed-urgencies-label": "Allowed urgencies",
"flag": {
"history": "history",
"sound": "sound",
"toast": "toast"
"toast": "toast",
"permanent": "permanent"
},
"flags-label": "When matched",
"match-label": "App",
Expand Down Expand Up @@ -1697,6 +1699,10 @@
}
},
"notifications": {
"allow-permanent": {
"description": "When disabled permanent notifications expire",
"label": "Allow Permanent"
},
"allowed-urgencies": {
"description": "Only show external notifications at the selected urgency levels",
"label": "Allowed Urgencies"
Expand Down
10 changes: 6 additions & 4 deletions src/app/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1542,11 +1542,13 @@ void Application::initUi() {

m_notificationToast.initialize(m_wayland, &m_configService, &m_notificationManager, &m_renderContext, &m_httpClient);
m_configService.addReloadCallback([this]() { m_notificationToast.onConfigReload(); });
auto applyNotificationFilterConfig = [this]() {
m_notificationManager.setFilters(m_configService.config().notification.filters);
auto applyNotificationConfig = [this]() {
const auto notification = m_configService.config().notification;
m_notificationManager.setFilters(notification.filters);
m_notificationManager.setAllowPermanent(notification.allowPermanent);
};
applyNotificationFilterConfig();
m_configService.addReloadCallback(applyNotificationFilterConfig);
applyNotificationConfig();
m_configService.addReloadCallback(applyNotificationConfig);
m_configService.setNotificationManager(&m_notificationManager);
m_notificationManager.setSoundPlayer(m_soundPlayer.get());

Expand Down
1 change: 1 addition & 0 deletions src/config/config_overrides.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ namespace {
row.insert_or_assign("show_toast", item.showToast);
row.insert_or_assign("save_history", item.saveHistory);
row.insert_or_assign("play_sound", item.playSound);
row.insert_or_assign("allow_permanent", item.allowPermanent);
if (!item.allowedUrgencies.empty()) {
toml::array urgencies;
for (const auto& urgency : item.allowedUrgencies) {
Expand Down
2 changes: 2 additions & 0 deletions src/config/config_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ struct NotificationFilterConfig {
bool showToast = true;
bool saveHistory = true;
bool playSound = true;
bool allowPermanent = true;
/// Empty = allow low, normal, and critical. Otherwise only listed urgencies pass this filter.
std::vector<std::string> allowedUrgencies;

Expand Down Expand Up @@ -607,6 +608,7 @@ struct NotificationConfig {
int offsetY = 8; // absolute vertical margin from the screen edge
std::vector<std::string> monitors;
bool collapseOnDismiss = true;
bool allowPermanent = true;
std::vector<NotificationFilterConfig> filters;

bool operator==(const NotificationConfig&) const = default;
Expand Down
2 changes: 2 additions & 0 deletions src/config/schema/config_schema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ namespace noctalia::config::schema {
field(&NotificationFilterConfig::showToast, "show_toast"),
field(&NotificationFilterConfig::saveHistory, "save_history"),
field(&NotificationFilterConfig::playSound, "play_sound"),
field(&NotificationFilterConfig::allowPermanent, "allow_permanent"),
field(&NotificationFilterConfig::allowedUrgencies, "allowed_urgencies"),
custom<NotificationFilterConfig>(
"allow_critical", [](const toml::table&, NotificationFilterConfig&, std::string_view, Diagnostics&) {},
Expand All @@ -229,6 +230,7 @@ namespace noctalia::config::schema {
field(&NotificationConfig::offsetY, "offset_y"),
field(&NotificationConfig::monitors, "monitors"),
field(&NotificationConfig::collapseOnDismiss, "collapse_on_dismiss"),
field(&NotificationConfig::allowPermanent, "allow_permanent"),
custom<NotificationConfig>(
"blacklist",
[](const toml::table& tbl, NotificationConfig& out, std::string_view, Diagnostics&) {
Expand Down
1 change: 1 addition & 0 deletions src/notification/notification_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ ResolvedNotificationFilter resolveNotificationFilter(
.showToast = filter.showToast,
.saveHistory = filter.saveHistory,
.playSound = filter.playSound,
.allowPermanent = filter.allowPermanent,
.allowedUrgencies = normalizeAllowedUrgencies(filter.allowedUrgencies),
.matched = true,
};
Expand Down
1 change: 1 addition & 0 deletions src/notification/notification_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct ResolvedNotificationFilter {
bool showToast = true;
bool saveHistory = true;
bool playSound = true;
bool allowPermanent = true;
/// Empty = all urgencies allowed for this filter.
std::unordered_set<Urgency> allowedUrgencies;
bool matched = false;
Expand Down
22 changes: 22 additions & 0 deletions src/notification/notification_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,25 @@ uint32_t NotificationManager::addOrReplace(
);
};

if (timeout == 0) {
const auto resolved = resolveNotificationFilter(
m_filters,
NotificationFilterFields{
.appName = appName,
.category = category.has_value() ? std::optional<std::string_view>{*category} : std::nullopt,
.desktopEntry = desktopEntry.has_value() ? std::optional<std::string_view>{*desktopEntry} : std::nullopt,
}
);

kLog.debug(
"notification allowPermanent global-allow={} filter-matched={} filter-allow={}", m_allowPermanent,
resolved.matched, resolved.allowPermanent
);
if ((resolved.matched && !resolved.allowPermanent) || (!resolved.matched && !m_allowPermanent)) {
timeout = kDefaultNotificationTimeout;
}
}

const ExternalNotificationDispatch externalDispatch = origin == NotificationOrigin::External
? evaluateExternalDispatch(urgency, appName, category, desktopEntry, transient)
: ExternalNotificationDispatch{};
Expand Down Expand Up @@ -304,6 +323,7 @@ uint32_t NotificationManager::addOrReplace(

const auto& n = m_notifications.back();
logNotification(n, "added");

if (origin == NotificationOrigin::External ? externalDispatch.saveHistory
: shouldTrackHistory(n.origin, n.urgency, n.transient)) {
const bool hadUnreadBefore = computeHasUnreadNotificationHistory();
Expand Down Expand Up @@ -634,6 +654,8 @@ void NotificationManager::setStateCallback(StateCallback callback) { m_stateCall

void NotificationManager::setSoundPlayer(SoundPlayer* soundPlayer) { m_soundPlayer = soundPlayer; }

void NotificationManager::setAllowPermanent(bool allowPermanent) { m_allowPermanent = allowPermanent; }

bool NotificationManager::hasUnreadNotificationHistory() const noexcept {
return computeHasUnreadNotificationHistory();
}
Expand Down
2 changes: 2 additions & 0 deletions src/notification/notification_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class NotificationManager {
[[nodiscard]] bool toggleDoNotDisturb();
void setStateCallback(StateCallback callback);
void setSoundPlayer(class SoundPlayer* soundPlayer);
void setAllowPermanent(bool allowPermanent);

// Bar indicator: true when at least one notification was added since the user last
// viewed the notification history (control center notifications tab).
Expand Down Expand Up @@ -158,5 +159,6 @@ class NotificationManager {
uint32_t m_nextId{1};
std::uint64_t m_changeSerial{0};
bool m_doNotDisturb = false;
bool m_allowPermanent = true;
class SoundPlayer* m_soundPlayer = nullptr;
};
3 changes: 3 additions & 0 deletions src/shell/settings/settings_content_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ namespace settings {
if (filter.playSound) {
parts.emplace_back(i18n::tr("settings.notifications.filter.flag.sound"));
}
if (filter.allowPermanent) {
parts.emplace_back(i18n::tr("settings.notifications.filter.flag.permanent"));
}
if (!filter.allowedUrgencies.empty()) {
std::vector<std::string> urgencyLabels;
urgencyLabels.reserve(filter.allowedUrgencies.size());
Expand Down
7 changes: 7 additions & 0 deletions src/shell/settings/settings_content_notification_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ namespace settings {
persist();
}
);
addToggleRow(
*flagsBlock, scale, i18n::tr("settings.notifications.filter.allow-permanent"), row.allowPermanent,
[&row, persist](bool value) {
row.allowPermanent = value;
persist();
}
);
body->addChild(std::move(flagsBlock));

parent.addChild(std::move(body));
Expand Down
5 changes: 5 additions & 0 deletions src/shell/settings/settings_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2157,6 +2157,11 @@ namespace settings {
tr("settings.schema.notifications.collapse-on-dismiss.description"), {"notification", "collapse_on_dismiss"},
ToggleSetting{cfg.notification.collapseOnDismiss}, "reorder stack slide"
));
entries.push_back(makeEntry(
SettingsSection::Notifications, "general", tr("settings.schema.notifications.allow-permanent.label"),
tr("settings.schema.notifications.allow-permanent.description"), {"notification", "allow_permanent"},
ToggleSetting{cfg.notification.allowPermanent}, "allow permanent notifications"
));
entries.push_back(makeEntry(
SettingsSection::Notifications, "toasts", tr("settings.schema.notifications.monitors.label"),
tr("settings.schema.notifications.monitors.description"), {"notification", "monitors"},
Expand Down
1 change: 1 addition & 0 deletions src/shell/settings/settings_window_popups.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,7 @@ void SettingsWindow::openNotificationFilterCreateEditor() {
.showToast = true,
.saveHistory = true,
.playSound = true,
.allowPermanent = true,
.allowedUrgencies = {},
});

Expand Down
2 changes: 2 additions & 0 deletions tests/config_schema_roundtrip_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,15 @@ location = "https://example.invalid/bad"
6,
{"DP-2"},
false,
true,
{NotificationFilterConfig{
.name = "discord",
.enabled = true,
.match = "discord",
.showToast = false,
.saveHistory = false,
.playSound = false,
.allowPermanent = false,
.allowedUrgencies = {"normal", "critical"},
}},
};
Expand Down
Loading