fancy notifs
This commit is contained in:
@@ -5,6 +5,14 @@ QtObject {
|
||||
id: root
|
||||
|
||||
property ListModel notifications: ListModel {}
|
||||
property var groupedNotifications: []
|
||||
property var groupExpansion: ({})
|
||||
property int versionCounter: 0
|
||||
|
||||
function nextVersion() {
|
||||
versionCounter += 1;
|
||||
return versionCounter;
|
||||
}
|
||||
|
||||
function indexOfId(notificationId) {
|
||||
for (let i = 0; i < notifications.count; i++) {
|
||||
@@ -15,16 +23,104 @@ QtObject {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function timeoutMsFor(notificationObject) {
|
||||
const isCritical = notificationObject.urgency === NotificationUrgency.Critical;
|
||||
if (isCritical)
|
||||
return -1;
|
||||
function normalizedString(value) {
|
||||
if (value === undefined || value === null)
|
||||
return "";
|
||||
return String(value);
|
||||
}
|
||||
|
||||
const rawSeconds = Number(notificationObject.expireTimeout);
|
||||
if (!Number.isFinite(rawSeconds) || rawSeconds <= 0)
|
||||
return 5000;
|
||||
function hintString(notificationObject, name) {
|
||||
if (!notificationObject || !notificationObject.hints)
|
||||
return "";
|
||||
const value = notificationObject.hints[name];
|
||||
return value === undefined || value === null ? "" : String(value);
|
||||
}
|
||||
|
||||
return Math.max(1000, Math.round(rawSeconds * 1000));
|
||||
function groupKeyFor(notificationObject) {
|
||||
const desktopEntry = normalizedString(hintString(notificationObject, "desktop-entry")).trim().toLowerCase();
|
||||
if (desktopEntry.length > 0)
|
||||
return "desktop:" + desktopEntry;
|
||||
|
||||
const appName = normalizedString(notificationObject ? notificationObject.appName : "").trim().toLowerCase();
|
||||
if (appName.length > 0)
|
||||
return "app:" + appName;
|
||||
|
||||
const appIcon = normalizedString(notificationObject ? notificationObject.appIcon : "").trim().toLowerCase();
|
||||
if (appIcon.length > 0)
|
||||
return "icon:" + appIcon;
|
||||
|
||||
const image = normalizedString(notificationObject ? notificationObject.image : "").trim().toLowerCase();
|
||||
if (image.length > 0)
|
||||
return "image:" + image;
|
||||
|
||||
return "id:" + String(notificationObject ? notificationObject.id : Date.now());
|
||||
}
|
||||
|
||||
function sortRowsNewestFirst(left, right) {
|
||||
return (right.createdAt || 0) - (left.createdAt || 0);
|
||||
}
|
||||
|
||||
function groupSortNewestFirst(left, right) {
|
||||
return (right.newestAt || 0) - (left.newestAt || 0);
|
||||
}
|
||||
|
||||
function rebuildGroups() {
|
||||
const grouped = ({});
|
||||
|
||||
for (let i = 0; i < notifications.count; i++) {
|
||||
const row = notifications.get(i);
|
||||
const key = row.rowGroupKey;
|
||||
if (!grouped[key]) {
|
||||
grouped[key] = {
|
||||
groupKey: key,
|
||||
appName: row.rowAppName,
|
||||
appIcon: row.rowAppIcon,
|
||||
desktopEntry: row.rowDesktopEntry,
|
||||
newestAt: row.rowCreatedAt,
|
||||
notifications: []
|
||||
};
|
||||
}
|
||||
|
||||
grouped[key].notifications.push({
|
||||
notificationId: row.rowNotificationId,
|
||||
notifObject: row.rowObject,
|
||||
notifVersion: row.rowVersion,
|
||||
createdAt: row.rowCreatedAt,
|
||||
appName: row.rowAppName,
|
||||
appIcon: row.rowAppIcon,
|
||||
image: row.rowImage
|
||||
});
|
||||
|
||||
if (row.rowCreatedAt > grouped[key].newestAt)
|
||||
grouped[key].newestAt = row.rowCreatedAt;
|
||||
}
|
||||
|
||||
const groups = [];
|
||||
for (const groupKey in grouped) {
|
||||
if (!grouped.hasOwnProperty(groupKey))
|
||||
continue;
|
||||
|
||||
const group = grouped[groupKey];
|
||||
group.notifications.sort(sortRowsNewestFirst);
|
||||
groups.push(group);
|
||||
}
|
||||
|
||||
groups.sort(groupSortNewestFirst);
|
||||
groupedNotifications = groups;
|
||||
}
|
||||
|
||||
function groupIsExpanded(groupKey) {
|
||||
return Boolean(groupExpansion[groupKey]);
|
||||
}
|
||||
|
||||
function setGroupExpanded(groupKey, expanded) {
|
||||
const next = Object.assign({}, groupExpansion);
|
||||
next[groupKey] = Boolean(expanded);
|
||||
groupExpansion = next;
|
||||
}
|
||||
|
||||
function toggleGroupExpanded(groupKey) {
|
||||
setGroupExpanded(groupKey, !groupIsExpanded(groupKey));
|
||||
}
|
||||
|
||||
function addNotification(notificationObject) {
|
||||
@@ -33,32 +129,50 @@ QtObject {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingIndex = indexOfId(notificationObject.id);
|
||||
if (existingIndex >= 0)
|
||||
notifications.remove(existingIndex);
|
||||
|
||||
notificationObject.tracked = true;
|
||||
|
||||
const idCopy = notificationObject.id;
|
||||
const objectCopy = notificationObject;
|
||||
notificationObject.closed.connect(function () {
|
||||
const rowIndex = indexOfId(idCopy);
|
||||
if (rowIndex >= 0)
|
||||
if (rowIndex >= 0 && notifications.get(rowIndex).rowObject === objectCopy) {
|
||||
notifications.remove(rowIndex);
|
||||
rebuildGroups();
|
||||
}
|
||||
});
|
||||
|
||||
const nowMs = Date.now();
|
||||
const groupKey = groupKeyFor(notificationObject);
|
||||
|
||||
const existingIndex = indexOfId(notificationObject.id);
|
||||
if (existingIndex >= 0) {
|
||||
notifications.set(existingIndex, {
|
||||
rowNotificationId: notificationObject.id,
|
||||
rowObject: notificationObject,
|
||||
rowVersion: nextVersion(),
|
||||
rowCreatedAt: nowMs,
|
||||
rowGroupKey: groupKey,
|
||||
rowAppName: normalizedString(notificationObject.appName),
|
||||
rowAppIcon: normalizedString(notificationObject.appIcon),
|
||||
rowImage: normalizedString(notificationObject.image),
|
||||
rowDesktopEntry: hintString(notificationObject, "desktop-entry")
|
||||
});
|
||||
rebuildGroups();
|
||||
return;
|
||||
}
|
||||
|
||||
notifications.append({
|
||||
rowNotificationId: notificationObject.id,
|
||||
rowTitle: String(notificationObject.summary || ""),
|
||||
rowBody: String(notificationObject.body || ""),
|
||||
rowUrgency: NotificationUrgency.toString(notificationObject.urgency),
|
||||
rowAppName: String(notificationObject.appName || ""),
|
||||
rowAppIcon: String(notificationObject.appIcon || ""),
|
||||
rowImage: String(notificationObject.image || ""),
|
||||
rowTimeoutMs: timeoutMsFor(notificationObject),
|
||||
rowCritical: notificationObject.urgency === NotificationUrgency.Critical,
|
||||
rowHints: notificationObject.hints || ({}),
|
||||
rowObject: notificationObject
|
||||
rowObject: notificationObject,
|
||||
rowVersion: nextVersion(),
|
||||
rowCreatedAt: nowMs,
|
||||
rowGroupKey: groupKey,
|
||||
rowAppName: normalizedString(notificationObject.appName),
|
||||
rowAppIcon: normalizedString(notificationObject.appIcon),
|
||||
rowImage: normalizedString(notificationObject.image),
|
||||
rowDesktopEntry: hintString(notificationObject, "desktop-entry")
|
||||
});
|
||||
rebuildGroups();
|
||||
}
|
||||
|
||||
function closeById(notificationId, reason) {
|
||||
@@ -70,13 +184,15 @@ QtObject {
|
||||
const notifObject = row.rowObject;
|
||||
|
||||
if (notifObject) {
|
||||
if (reason === "timeout")
|
||||
if (reason === "timeout") {
|
||||
notifObject.expire();
|
||||
else
|
||||
} else if (reason === "click" || reason === "dismiss" || reason === "drag") {
|
||||
notifObject.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
notifications.remove(rowIndex);
|
||||
rebuildGroups();
|
||||
}
|
||||
|
||||
property NotificationServer server: NotificationServer {
|
||||
@@ -84,6 +200,7 @@ QtObject {
|
||||
bodySupported: true
|
||||
bodyMarkupSupported: false
|
||||
bodyHyperlinksSupported: false
|
||||
actionsSupported: true
|
||||
|
||||
// NotificationServer is Quickshell's DBus implementation for
|
||||
// org.freedesktop.Notifications, so this is the notification source.
|
||||
|
||||
Reference in New Issue
Block a user