diff options
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml')
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml new file mode 100644 index 00000000..001ffcf5 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml | |||
@@ -0,0 +1,151 @@ | |||
1 | pragma Singleton | ||
2 | |||
3 | import QtQml | ||
4 | import Quickshell | ||
5 | import Quickshell.Services.Notifications | ||
6 | |||
7 | Singleton { | ||
8 | id: root | ||
9 | |||
10 | readonly property bool active: !lockscreenActive && !displayInhibited | ||
11 | property bool lockscreenActive: false | ||
12 | property bool displayInhibited: false | ||
13 | property alias trackedNotifications: server.trackedNotifications | ||
14 | readonly property var groups: { | ||
15 | function matchesGroupKey(notif, groupKey) { | ||
16 | var matches = true; | ||
17 | for (const prop in groupKey.test) { | ||
18 | if (notif[prop] !== groupKey.test[prop]) { | ||
19 | matches = false; | ||
20 | break; | ||
21 | } | ||
22 | } | ||
23 | return matches; | ||
24 | } | ||
25 | |||
26 | var groups = new Map(); | ||
27 | var notifs = new Array(); | ||
28 | for (const [ix, notif] of server.trackedNotifications.values.entries()) { | ||
29 | var didGroup = false; | ||
30 | for (const groupKey of root.groupKeys) { | ||
31 | if (!matchesGroupKey(notif, groupKey)) | ||
32 | continue; | ||
33 | |||
34 | const key = JSON.stringify({ | ||
35 | "key": groupKey, | ||
36 | "values": Object.assign({}, ...(Array.from(groupKey["group-by"]).map(prop => { | ||
37 | var res = {}; | ||
38 | res[prop] = notif[prop]; | ||
39 | return res; | ||
40 | }))) | ||
41 | }); | ||
42 | if (!groups.has(key)) | ||
43 | groups.set(key, new Array()); | ||
44 | groups.get(key).push({ "ix": ix, "notif": notif }); | ||
45 | didGroup = true; | ||
46 | break; | ||
47 | } | ||
48 | |||
49 | if (!didGroup) | ||
50 | notifs.push([{ "ix": ix, "notif": notif }]); | ||
51 | } | ||
52 | notifs.push(...groups.values()); | ||
53 | notifs.sort((as, bs) => Math.min(...(as.map(o => o.ix))) - Math.min(...(bs.map(o => o.ix)))); | ||
54 | return notifs.map(ns => ns.map(n => n.notif)); | ||
55 | } | ||
56 | |||
57 | property var groupKeys: [ | ||
58 | { "test": { "appName": "Element" }, "group-by": [ "summary" ] } | ||
59 | ]; | ||
60 | |||
61 | property var history: [] | ||
62 | |||
63 | Component { | ||
64 | id: expirationTimer | ||
65 | |||
66 | QtObject { | ||
67 | id: timer | ||
68 | |||
69 | required property QtObject parent | ||
70 | required property int expirationTime | ||
71 | property list<QtObject> data: [ | ||
72 | Timer { | ||
73 | running: root.active | ||
74 | interval: timer.expirationTime | ||
75 | onTriggered: timer.parent.expire() | ||
76 | } | ||
77 | ] | ||
78 | } | ||
79 | } | ||
80 | |||
81 | Component { | ||
82 | id: notificationLock | ||
83 | |||
84 | RetainableLock {} | ||
85 | } | ||
86 | |||
87 | readonly property SystemClock clock: SystemClock { | ||
88 | precision: SystemClock.Minutes | ||
89 | } | ||
90 | |||
91 | function formatTime(time) { | ||
92 | const now = root.clock.date; | ||
93 | const diff = now - time; | ||
94 | const minutes = Math.ceil(diff / 60000); | ||
95 | const hours = Math.floor(minutes / 60); | ||
96 | |||
97 | if (hours < 1) { | ||
98 | if (minutes < 1) | ||
99 | return "now"; | ||
100 | if (minutes == 1) | ||
101 | return "1 minute"; | ||
102 | return `${minutes} minutes`; | ||
103 | } | ||
104 | |||
105 | const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()) | ||
106 | const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()) | ||
107 | const days = Math.floor((nowDate - timeDate) / (1000 * 86400)) | ||
108 | |||
109 | const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm"); | ||
110 | |||
111 | if (days === 0) | ||
112 | return timeStr; | ||
113 | if (days === 1) | ||
114 | return `yesterday ${timeStr}`; | ||
115 | |||
116 | const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD"); | ||
117 | return `${dateStr} ${timeStr}`; | ||
118 | } | ||
119 | |||
120 | NotificationServer { | ||
121 | id: server | ||
122 | |||
123 | bodySupported: true | ||
124 | actionsSupported: true | ||
125 | actionIconsSupported: true | ||
126 | imageSupported: true | ||
127 | bodyMarkupSupported: true | ||
128 | bodyImagesSupported: true | ||
129 | |||
130 | onNotification: notification => { | ||
131 | var timeout = notification.expireTimeout * 1000; | ||
132 | if (notification.appName == "poweralertd") | ||
133 | timeout = 2000; | ||
134 | if (timeout > 0) { | ||
135 | Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true }); | ||
136 | notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout }); | ||
137 | } | ||
138 | Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true }); | ||
139 | notification.receivedTime = root.clock.date; | ||
140 | notification.closed.connect((reason) => server.onNotificationClosed(notification, reason)); | ||
141 | notification.tracked = true; | ||
142 | } | ||
143 | |||
144 | function onNotificationClosed(notification, reason) { | ||
145 | root.history.push({ | ||
146 | lock: notificationLock.createObject(root, { locked: true, object: notification }), | ||
147 | notification: notification | ||
148 | }); | ||
149 | } | ||
150 | } | ||
151 | } | ||