C1: NotifItem QtObject with owned timer, refactor service to stable identities
This commit is contained in:
parent
45704cb102
commit
88d8842064
3 changed files with 136 additions and 52 deletions
|
|
@ -12,30 +12,37 @@ QtObject {
|
|||
property var list: []
|
||||
property bool dnd: false
|
||||
|
||||
readonly property var popups: list.filter(n => n.popup)
|
||||
readonly property int count: list.length
|
||||
readonly property var popups: list.filter(n => n.popup && n.state !== "dismissed")
|
||||
readonly property int count: list.filter(n => n.state !== "dismissed").length
|
||||
|
||||
// O(1) lookup
|
||||
property var _byId: ({})
|
||||
|
||||
function dismiss(notifId) {
|
||||
const idx = list.findIndex(n => n.id === notifId);
|
||||
if (idx >= 0) {
|
||||
const n = list[idx];
|
||||
n.notification?.dismiss();
|
||||
list.splice(idx, 1);
|
||||
_changed();
|
||||
}
|
||||
const item = _byId[notifId];
|
||||
if (!item)
|
||||
return;
|
||||
item.finishDismiss();
|
||||
list = list.filter(n => n !== item);
|
||||
delete _byId[notifId];
|
||||
item.destroy();
|
||||
_saveTimer.restart();
|
||||
}
|
||||
|
||||
function dismissAll() {
|
||||
for (const n of list.slice())
|
||||
n.notification?.dismiss();
|
||||
for (const item of list.slice()) {
|
||||
item.finishDismiss();
|
||||
delete _byId[item.id];
|
||||
item.destroy();
|
||||
}
|
||||
list = [];
|
||||
_changed();
|
||||
_saveTimer.restart();
|
||||
}
|
||||
|
||||
function dismissPopup(notifId) {
|
||||
const n = list.find(n => n.id === notifId);
|
||||
if (n) {
|
||||
n.popup = false;
|
||||
const item = _byId[notifId];
|
||||
if (item) {
|
||||
item.popup = false;
|
||||
_changed();
|
||||
}
|
||||
}
|
||||
|
|
@ -46,9 +53,11 @@ QtObject {
|
|||
|
||||
function _changed() {
|
||||
list = list.slice();
|
||||
_saveTimer.restart();
|
||||
}
|
||||
|
||||
// Signal popups to animate out before removal
|
||||
signal popupExpiring(var notifId)
|
||||
|
||||
property NotificationServer _server: NotificationServer {
|
||||
actionsSupported: true
|
||||
bodyMarkupSupported: true
|
||||
|
|
@ -59,13 +68,17 @@ QtObject {
|
|||
onNotification: notif => {
|
||||
notif.tracked = true;
|
||||
|
||||
const data = {
|
||||
const isCritical = notif.urgency === NotificationUrgency.Critical;
|
||||
|
||||
const item = _itemComp.createObject(root, {
|
||||
notification: notif,
|
||||
id: notif.id,
|
||||
summary: notif.summary,
|
||||
body: notif.body,
|
||||
appName: notif.appName,
|
||||
appIcon: notif.appIcon,
|
||||
image: notif.image,
|
||||
hints: notif.hints,
|
||||
urgency: notif.urgency,
|
||||
actions: notif.actions ? notif.actions.map(a => ({
|
||||
identifier: a.identifier,
|
||||
|
|
@ -73,14 +86,13 @@ QtObject {
|
|||
invoke: () => a.invoke()
|
||||
})) : [],
|
||||
time: Date.now(),
|
||||
popup: !root.dnd,
|
||||
closed: false,
|
||||
notification: notif
|
||||
};
|
||||
popup: isCritical || !root.dnd
|
||||
});
|
||||
|
||||
root.list = [data, ...root.list];
|
||||
root._byId[item.id] = item;
|
||||
root.list = [item, ...root.list];
|
||||
|
||||
// Dismiss excess popups (keep in history, just hide popup)
|
||||
// Trim excess popups
|
||||
const max = M.Modules.notifications.maxPopups || 4;
|
||||
const currentPopups = root.list.filter(n => n.popup);
|
||||
if (currentPopups.length > max) {
|
||||
|
|
@ -89,38 +101,33 @@ QtObject {
|
|||
root._changed();
|
||||
}
|
||||
|
||||
// Auto-expire popup
|
||||
if (data.popup) {
|
||||
// Auto-expire popup (skip for critical)
|
||||
if (item.popup && !isCritical) {
|
||||
const timeout = notif.expireTimeout > 0 ? notif.expireTimeout : (M.Modules.notifications.timeout || 3000);
|
||||
Qt.callLater(() => {
|
||||
_expireTimer.createObject(root, {
|
||||
_notifId: data.id,
|
||||
interval: timeout
|
||||
});
|
||||
});
|
||||
item._expireTimer.interval = timeout;
|
||||
item._expireTimer.running = true;
|
||||
}
|
||||
|
||||
// Trim history
|
||||
const maxHistory = M.Modules.notifications.maxHistory || 50;
|
||||
while (root.list.length > maxHistory) {
|
||||
const old = root.list.pop();
|
||||
old.finishDismiss();
|
||||
delete root._byId[old.id];
|
||||
old.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Signal popups to animate out before removal
|
||||
signal popupExpiring(var notifId)
|
||||
|
||||
property Component _expireTimer: Component {
|
||||
Timer {
|
||||
property var _notifId
|
||||
running: true
|
||||
onTriggered: {
|
||||
root.popupExpiring(_notifId);
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
property Component _itemComp: Component {
|
||||
NotifItem {}
|
||||
}
|
||||
|
||||
// Persistence
|
||||
property Timer _saveTimer: Timer {
|
||||
interval: 1000
|
||||
onTriggered: {
|
||||
const data = root.list.map(n => ({
|
||||
const data = root.list.filter(n => n.state !== "dismissed").map(n => ({
|
||||
id: n.id,
|
||||
summary: n.summary,
|
||||
body: n.body,
|
||||
|
|
@ -139,16 +146,25 @@ QtObject {
|
|||
onLoaded: {
|
||||
try {
|
||||
const data = JSON.parse(text());
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const maxHistory = M.Modules.notifications.maxHistory || 50;
|
||||
for (let i = 0; i < Math.min(data.length, maxHistory); i++) {
|
||||
const n = data[i];
|
||||
// Prefix persisted IDs to avoid collision with live D-Bus IDs
|
||||
n.id = "p_" + (n.id ?? i) + "_" + n.time;
|
||||
n.popup = false;
|
||||
n.closed = false;
|
||||
n.notification = null;
|
||||
n.actions = [];
|
||||
const item = _itemComp.createObject(root, {
|
||||
id: "p_" + (n.id ?? i) + "_" + n.time,
|
||||
summary: n.summary || "",
|
||||
body: n.body || "",
|
||||
appName: n.appName || "",
|
||||
appIcon: n.appIcon || "",
|
||||
image: n.image || "",
|
||||
urgency: n.urgency ?? 1,
|
||||
time: n.time || Date.now(),
|
||||
popup: false,
|
||||
actions: []
|
||||
});
|
||||
root._byId[item.id] = item;
|
||||
root.list.push(item);
|
||||
}
|
||||
root.list = data.concat(root.list);
|
||||
root._changed();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue