mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-07 19:59:14 -04:00
refactor(Notifications): further support for duplicate notification logic
- New setting to stack or suppress identical alerts (on by default) Closes #2334
This commit is contained in:
@@ -688,6 +688,7 @@ Singleton {
|
||||
property int notificationTimeoutNormal: 5000
|
||||
property int notificationTimeoutCritical: 0
|
||||
property bool notificationCompactMode: false
|
||||
property bool notificationDedupeEnabled: true
|
||||
property int notificationPopupPosition: SettingsData.Position.Top
|
||||
property int notificationAnimationSpeed: SettingsData.AnimationSpeed.Short
|
||||
property int notificationCustomAnimationDuration: 400
|
||||
|
||||
@@ -399,6 +399,7 @@ var SPEC = {
|
||||
notificationTimeoutNormal: { def: 5000 },
|
||||
notificationTimeoutCritical: { def: 0 },
|
||||
notificationCompactMode: { def: false },
|
||||
notificationDedupeEnabled: { def: true },
|
||||
notificationPopupPosition: { def: 0 },
|
||||
notificationAnimationSpeed: { def: 1 },
|
||||
notificationCustomAnimationDuration: { def: 400 },
|
||||
|
||||
@@ -182,26 +182,30 @@ Rectangle {
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
readonly property real reservedTrailingWidth: historySeparator.implicitWidth + Math.max(historyTimeText.implicitWidth, 72) + spacing
|
||||
|
||||
StyledText {
|
||||
id: historyTitleText
|
||||
width: Math.min(implicitWidth, Math.max(0, parent.width - parent.reservedTrailingWidth))
|
||||
text: {
|
||||
let title = historyItem.summary || "";
|
||||
const appName = historyItem.appName || "";
|
||||
const prefix = appName + " • ";
|
||||
if (appName && title.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||
title = title.substring(prefix.length);
|
||||
Item {
|
||||
width: Math.max(0, parent.width - historySeparator.implicitWidth - Math.max(historyTimeText.implicitWidth, 72) - parent.spacing * 2)
|
||||
height: historyTitleText.implicitHeight
|
||||
visible: historyTitleText.text.length > 0
|
||||
|
||||
StyledText {
|
||||
id: historyTitleText
|
||||
anchors.fill: parent
|
||||
text: {
|
||||
let title = historyItem.summary || "";
|
||||
const appName = historyItem.appName || "";
|
||||
const prefix = appName + " • ";
|
||||
if (appName && title.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||
title = title.substring(prefix.length);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
return title;
|
||||
color: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
color: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
visible: text.length > 0
|
||||
}
|
||||
StyledText {
|
||||
id: historySeparator
|
||||
|
||||
@@ -273,6 +273,17 @@ Item {
|
||||
onToggled: checked => SettingsData.set("notificationCompactMode", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "notificationDedupeEnabled"
|
||||
tags: ["notification", "duplicate", "dedupe", "stack", "coalesce", "repeat"]
|
||||
text: I18n.tr("Suppress Duplicate Notifications")
|
||||
description: SettingsData.notificationDedupeEnabled
|
||||
? I18n.tr("Identical alerts show as one popup instead of stacking")
|
||||
: I18n.tr("Identical alerts stack as separate notification cards")
|
||||
checked: SettingsData.notificationDedupeEnabled
|
||||
onToggled: checked => SettingsData.set("notificationDedupeEnabled", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "notificationPopupShadowEnabled"
|
||||
tags: ["notification", "popup", "shadow", "radius", "rounded"]
|
||||
|
||||
@@ -35,6 +35,8 @@ Singleton {
|
||||
property int maxIngressPerSecond: 20
|
||||
property double _lastIngressSec: 0
|
||||
property int _ingressCountThisSec: 0
|
||||
readonly property int notificationDedupBurstMs: 5000
|
||||
property var _recentDedupKeys: []
|
||||
|
||||
property var _dismissQueue: []
|
||||
property int _dismissBatchSize: 8
|
||||
@@ -291,18 +293,58 @@ Singleton {
|
||||
return Date.now() / 1000.0;
|
||||
}
|
||||
|
||||
function _normalizeDedupText(text) {
|
||||
if (!text)
|
||||
return "";
|
||||
let normalized = text.toString();
|
||||
normalized = normalized.replace(/<img\b[^>]*>/gi, "");
|
||||
normalized = normalized.replace(/<[^>]+>/g, "");
|
||||
normalized = normalized.replace(/\s+/g, " ").trim();
|
||||
return normalized.toLowerCase();
|
||||
}
|
||||
|
||||
function _dedupAppId(source) {
|
||||
if (!source)
|
||||
return "";
|
||||
const desktopEntry = (source.desktopEntry || "").toString().trim().toLowerCase();
|
||||
if (desktopEntry)
|
||||
return desktopEntry;
|
||||
return (source.appName || "").toString().trim().toLowerCase();
|
||||
}
|
||||
|
||||
function _notificationDedupKey(source) {
|
||||
if (!source)
|
||||
return "";
|
||||
const app = (source.appName || source.desktopEntry || "").toString();
|
||||
const summary = (source.summary || "").toString();
|
||||
const body = (source.body || "").toString();
|
||||
const app = _dedupAppId(source);
|
||||
const summary = _normalizeDedupText(source.summary);
|
||||
const body = _normalizeDedupText(source.body);
|
||||
const urgency = typeof source.urgency === "number" ? source.urgency : NotificationUrgency.Normal;
|
||||
const icon = (source.appIcon || "").toString();
|
||||
if (!app && !summary && !body)
|
||||
return "";
|
||||
const sep = "";
|
||||
return app + sep + summary + sep + body + sep + urgency + sep + icon;
|
||||
return app + sep + summary + sep + body + sep + urgency;
|
||||
}
|
||||
|
||||
function _pruneRecentDedupKeys() {
|
||||
const cutoff = Date.now() - notificationDedupBurstMs;
|
||||
_recentDedupKeys = _recentDedupKeys.filter(entry => entry && entry.atMs >= cutoff);
|
||||
}
|
||||
|
||||
function _hasRecentDuplicate(key) {
|
||||
if (!key)
|
||||
return false;
|
||||
_pruneRecentDedupKeys();
|
||||
return _recentDedupKeys.some(entry => entry && entry.key === key);
|
||||
}
|
||||
|
||||
function _recordDedupKey(key) {
|
||||
if (!key)
|
||||
return;
|
||||
_pruneRecentDedupKeys();
|
||||
_recentDedupKeys.push({
|
||||
"key": key,
|
||||
"atMs": Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
function _findActiveDuplicate(notif) {
|
||||
@@ -310,17 +352,14 @@ Singleton {
|
||||
if (!key)
|
||||
return null;
|
||||
|
||||
for (const w of visibleNotifications) {
|
||||
for (const w of allWrappers) {
|
||||
if (!w || !w.notification || !w.popup)
|
||||
continue;
|
||||
if (_notificationDedupKey(w.notification) === key)
|
||||
return w;
|
||||
}
|
||||
|
||||
for (const w of notificationQueue) {
|
||||
if (!w || !w.notification)
|
||||
if (_notificationDedupKey(w.notification) !== key)
|
||||
continue;
|
||||
if (_notificationDedupKey(w.notification) === key)
|
||||
if (visibleNotifications.indexOf(w) !== -1 || notificationQueue.indexOf(w) !== -1)
|
||||
return w;
|
||||
if (w.timer && w.timer.running)
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -637,14 +676,17 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
const duplicate = _findActiveDuplicate(notif);
|
||||
if (duplicate) {
|
||||
if (duplicate.timer && duplicate.timer.running)
|
||||
duplicate.timer.restart();
|
||||
try {
|
||||
notif.dismiss();
|
||||
} catch (e) {}
|
||||
return;
|
||||
if (SettingsData.notificationDedupeEnabled) {
|
||||
const dedupKey = _notificationDedupKey(notif);
|
||||
const duplicate = _findActiveDuplicate(notif);
|
||||
if (duplicate || _hasRecentDuplicate(dedupKey)) {
|
||||
if (duplicate && duplicate.timer && duplicate.timer.running)
|
||||
duplicate.timer.restart();
|
||||
try {
|
||||
notif.dismiss();
|
||||
} catch (e) {}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_ingressAllowed(policy.urgency)) {
|
||||
@@ -686,6 +728,9 @@ Singleton {
|
||||
});
|
||||
|
||||
if (wrapper) {
|
||||
if (SettingsData.notificationDedupeEnabled)
|
||||
_recordDedupKey(_notificationDedupKey(notif));
|
||||
|
||||
root.allWrappers.push(wrapper);
|
||||
if (shouldKeepInCenter) {
|
||||
root.notifications.push(wrapper);
|
||||
|
||||
@@ -5807,6 +5807,28 @@
|
||||
],
|
||||
"description": "Use smaller notification cards"
|
||||
},
|
||||
{
|
||||
"section": "notificationDedupeEnabled",
|
||||
"label": "Suppress Duplicate Notifications",
|
||||
"tabIndex": 17,
|
||||
"category": "Notifications",
|
||||
"keywords": [
|
||||
"alert",
|
||||
"alerts",
|
||||
"coalesce",
|
||||
"dedupe",
|
||||
"duplicate",
|
||||
"duplicates",
|
||||
"messages",
|
||||
"notif",
|
||||
"notification",
|
||||
"notifications",
|
||||
"repeat",
|
||||
"stack",
|
||||
"toast"
|
||||
],
|
||||
"description": "Control whether identical alerts stack or show as a single popup"
|
||||
},
|
||||
{
|
||||
"section": "notificationHistorySaveCritical",
|
||||
"label": "Critical Priority",
|
||||
|
||||
Reference in New Issue
Block a user