nixos/quickshell/NotificationsState.qml
2026-04-15 10:38:34 -03:00

142 lines
4.2 KiB
QML

pragma Singleton
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
Singleton {
id: root
property bool panelOpen: false
property int unreadCount: 0
property ListModel notifications: ListModel {}
property var _refs: ({}) // maps nid to the live Notification object
property ListModel toasts: ListModel {}
// Runs every 100ms while toasts are active, decrementing progress (1.0 to 0.0 over 5s)
// and removing any toast that has expired
Timer {
id: toastTick
interval: 100
repeat: true
running: root.toasts.count > 0
onTriggered: {
let i = 0
while (i < root.toasts.count) {
const p = root.toasts.get(i).progress - 0.02
if (p <= 0) {
root.toasts.remove(i, 1)
} else {
root.toasts.setProperty(i, "progress", p)
i++
}
}
}
}
NotificationServer {
keepOnReload: true
onNotification: notif => {
const ts = Date.now()
const day = root._dayLabel(ts)
root.notifications.insert(0, {
nid: notif.id,
appName: notif.appName || "Unknown",
summary: notif.summary || "",
body: notif.body || "",
appIcon: notif.appIcon || "",
urgency: notif.urgency || 1,
timestamp: ts,
day: day
})
const r = root._refs
r[notif.id] = notif
root._refs = r
if (!root.panelOpen) root.unreadCount++
root.toasts.insert(0, {
tid: notif.id,
appName: notif.appName || "Unknown",
summary: notif.summary || "",
body: notif.body || "",
appIcon: notif.appIcon || "",
urgency: notif.urgency || 1,
progress: 1.0
})
}
}
function dismiss(index) {
if (index < 0 || index >= notifications.count) return
const entry = notifications.get(index)
const ref = _refs[entry.nid]
if (ref) {
try { ref.close() } catch (_) {}
const r = _refs
delete r[entry.nid]
_refs = r
}
notifications.remove(index, 1)
}
function dismissAll() {
for (let i = 0; i < notifications.count; i++) {
const ref = _refs[notifications.get(i).nid]
if (ref) { try { ref.close() } catch (_) {} }
}
notifications.clear()
_refs = {}
unreadCount = 0
}
// Invokes the "default" action on a notification, falling back to the first action
function invokeDefault(nid) {
const ref = _refs[nid]
if (!ref) return
const actions = ref.actions
if (!actions || actions.length === 0) return
for (let i = 0; i < actions.length; i++) {
if (actions[i].identifier === "default") {
try { actions[i].invoke() } catch (_) {}
return
}
}
try { actions[0].invoke() } catch (_) {}
}
function dismissToast(tid) {
for (let i = 0; i < toasts.count; i++) {
if (toasts.get(i).tid === tid) {
toasts.remove(i, 1)
return
}
}
}
function togglePanel() {
panelOpen = !panelOpen
if (panelOpen) unreadCount = 0
}
function formatTime(ts) {
return Qt.formatDateTime(new Date(ts), "hh:mm")
}
function _dayLabel(ts) {
const now = new Date()
const date = new Date(ts)
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const yesterday = new Date(today.getTime() - 86400000)
const day = new Date(date.getFullYear(), date.getMonth(), date.getDate())
if (day.getTime() === today.getTime()) return "Today"
if (day.getTime() === yesterday.getTime()) return "Yesterday"
return Qt.formatDateTime(date, "dd/MM/yyyy")
}
}