From e88bbe425f3e2e25c567225daee3e18435691e6f Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:34:08 +1100 Subject: [PATCH] refactor: move nested Notifs.Notif type to NotifData --- .../dashboard/GeneralSection.qml | 1 - modules/controlcenter/launcher/Settings.qml | 1 - modules/lock/NotifGroup.qml | 2 +- modules/notifications/Content.qml | 2 +- modules/notifications/Notification.qml | 2 +- modules/sidebar/Notif.qml | 2 +- modules/sidebar/NotifActionList.qml | 2 +- modules/sidebar/NotifGroupList.qml | 2 +- services/NotifData.qml | 220 +++++++++++++++++ services/Notifs.qml | 222 +----------------- 10 files changed, 230 insertions(+), 226 deletions(-) create mode 100644 services/NotifData.qml diff --git a/modules/controlcenter/dashboard/GeneralSection.qml b/modules/controlcenter/dashboard/GeneralSection.qml index 288db2ca..3db044ee 100644 --- a/modules/controlcenter/dashboard/GeneralSection.qml +++ b/modules/controlcenter/dashboard/GeneralSection.qml @@ -2,7 +2,6 @@ import "../components" import qs.components import qs.components.controls import qs.config -import qs.services import QtQuick import QtQuick.Layouts diff --git a/modules/controlcenter/launcher/Settings.qml b/modules/controlcenter/launcher/Settings.qml index a9658435..a2ed2b12 100644 --- a/modules/controlcenter/launcher/Settings.qml +++ b/modules/controlcenter/launcher/Settings.qml @@ -5,7 +5,6 @@ import "../components" import qs.components import qs.components.controls import qs.config -import qs.services import QtQuick import QtQuick.Layouts diff --git a/modules/lock/NotifGroup.qml b/modules/lock/NotifGroup.qml index 7f2b62fb..1c848344 100644 --- a/modules/lock/NotifGroup.qml +++ b/modules/lock/NotifGroup.qml @@ -306,7 +306,7 @@ StyledRect { component NotifLine: StyledText { id: notifLine - required property Notifs.Notif modelData + required property NotifData modelData Layout.fillWidth: true textFormat: Text.MarkdownText diff --git a/modules/notifications/Content.qml b/modules/notifications/Content.qml index 42fb1fce..46075a20 100644 --- a/modules/notifications/Content.qml +++ b/modules/notifications/Content.qml @@ -68,7 +68,7 @@ Item { delegate: Item { id: wrapper - required property Notifs.Notif modelData + required property NotifData modelData required property int index readonly property alias nonAnimHeight: notif.nonAnimHeight property int idx diff --git a/modules/notifications/Notification.qml b/modules/notifications/Notification.qml index e3ed784c..d53cc7cb 100644 --- a/modules/notifications/Notification.qml +++ b/modules/notifications/Notification.qml @@ -15,7 +15,7 @@ import QtQuick.Shapes StyledRect { id: root - required property Notifs.Notif modelData + required property NotifData modelData readonly property bool hasImage: modelData.image.length > 0 readonly property bool hasAppIcon: modelData.appIcon.length > 0 readonly property int bodyTextFormat: /[<*_`#\[\]]/.test(modelData.body) ? Text.MarkdownText : Text.PlainText diff --git a/modules/sidebar/Notif.qml b/modules/sidebar/Notif.qml index 4ba76ec8..bfe7dd55 100644 --- a/modules/sidebar/Notif.qml +++ b/modules/sidebar/Notif.qml @@ -10,7 +10,7 @@ import QtQuick.Layouts StyledRect { id: root - required property Notifs.Notif modelData + required property NotifData modelData required property Props props required property bool expanded required property var visibilities diff --git a/modules/sidebar/NotifActionList.qml b/modules/sidebar/NotifActionList.qml index b95b1da8..472152ae 100644 --- a/modules/sidebar/NotifActionList.qml +++ b/modules/sidebar/NotifActionList.qml @@ -13,7 +13,7 @@ import QtQuick.Layouts Item { id: root - required property Notifs.Notif notif + required property NotifData notif Layout.fillWidth: true implicitHeight: flickable.contentHeight diff --git a/modules/sidebar/NotifGroupList.qml b/modules/sidebar/NotifGroupList.qml index 6f4a8dd1..2d9ba38b 100644 --- a/modules/sidebar/NotifGroupList.qml +++ b/modules/sidebar/NotifGroupList.qml @@ -63,7 +63,7 @@ Item { id: notif required property int index - required property Notifs.Notif modelData + required property NotifData modelData readonly property alias nonAnimHeight: notifInner.nonAnimHeight readonly property bool previewHidden: { diff --git a/services/NotifData.qml b/services/NotifData.qml new file mode 100644 index 00000000..f3bb9eb6 --- /dev/null +++ b/services/NotifData.qml @@ -0,0 +1,220 @@ +import qs.config +import qs.utils +import Caelestia +import Quickshell +import Quickshell.Services.Notifications +import QtQuick + +QtObject { + id: notif + + property bool popup + property bool closed + property var locks: new Set() + + property date time: new Date() + property string timeStr: qsTr("now") + + readonly property Timer timeStrTimer: Timer { + running: !notif.closed + repeat: true + interval: 5000 + onTriggered: notif.updateTimeStr() + } + + property Notification notification + property string id + property string summary + property string body + property string appIcon + property string appName + property string image + property var hints // Hints are not persisted across restarts + property real expireTimeout: Config.notifs.defaultExpireTimeout + property int urgency: NotificationUrgency.Normal + property bool resident + property bool hasActionIcons + property list actions + + readonly property Timer timer: Timer { + running: true + interval: notif.expireTimeout > 0 ? notif.expireTimeout : Config.notifs.defaultExpireTimeout + onTriggered: { + if (Config.notifs.expire) + notif.popup = false; + } + } + + readonly property LazyLoader dummyImageLoader: LazyLoader { + active: false + + PanelWindow { + implicitWidth: Config.notifs.sizes.image + implicitHeight: Config.notifs.sizes.image + color: "transparent" + mask: Region {} + + Image { + function tryCache(): void { + if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image) + return; + + const cacheKey = notif.appName + notif.summary + notif.id; + let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch; + for (let i = 0; i < cacheKey.length; i++) { + ch = cacheKey.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); + h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); + h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); + const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0); + + const cache = `${Paths.notifimagecache}/${hash}.png`; + CUtils.saveItem(this, Qt.resolvedUrl(cache), () => { + notif.image = cache; + notif.dummyImageLoader.active = false; + }); + } + + anchors.fill: parent + source: Qt.resolvedUrl(notif.image) + fillMode: Image.PreserveAspectCrop + cache: false + asynchronous: true + opacity: 0 + + onStatusChanged: tryCache() + onWidthChanged: tryCache() + onHeightChanged: tryCache() + } + } + } + + readonly property Connections conn: Connections { + function onClosed(): void { + notif.close(); + } + + function onSummaryChanged(): void { + notif.summary = notif.notification.summary; + } + + function onBodyChanged(): void { + notif.body = notif.notification.body; + } + + function onAppIconChanged(): void { + notif.appIcon = notif.notification.appIcon; + } + + function onAppNameChanged(): void { + notif.appName = notif.notification.appName; + } + + function onImageChanged(): void { + notif.image = notif.notification.image; + if (notif.notification?.image) + notif.dummyImageLoader.active = true; + } + + function onExpireTimeoutChanged(): void { + notif.expireTimeout = notif.notification.expireTimeout; + } + + function onUrgencyChanged(): void { + notif.urgency = notif.notification.urgency; + } + + function onResidentChanged(): void { + notif.resident = notif.notification.resident; + } + + function onHasActionIconsChanged(): void { + notif.hasActionIcons = notif.notification.hasActionIcons; + } + + function onActionsChanged(): void { + notif.actions = notif.notification.actions.map(a => ({ + identifier: a.identifier, + text: a.text, + invoke: () => a.invoke() + })); + } + + function onHintsChanged(): void { + notif.hints = notif.notification.hints; + } + + target: notif.notification + } + + function updateTimeStr(): void { + const diff = Date.now() - time.getTime(); + const m = Math.floor(diff / 60000); + + if (m < 1) { + timeStr = qsTr("now"); + timeStrTimer.interval = 5000; + } else { + const h = Math.floor(m / 60); + const d = Math.floor(h / 24); + + if (d > 0) { + timeStr = `${d}d`; + timeStrTimer.interval = 3600000; + } else if (h > 0) { + timeStr = `${h}h`; + timeStrTimer.interval = 300000; + } else { + timeStr = `${m}m`; + timeStrTimer.interval = m < 10 ? 30000 : 60000; + } + } + } + + function lock(item: Item): void { + locks.add(item); + } + + function unlock(item: Item): void { + locks.delete(item); + if (closed) + close(); + } + + function close(): void { + closed = true; + if (locks.size === 0 && Notifs.list.includes(this)) { + Notifs.list = Notifs.list.filter(n => n !== this); + notification?.dismiss(); + destroy(); + } + } + + Component.onCompleted: { + if (!notification) + return; + + id = notification.id; + summary = notification.summary; + body = notification.body; + appIcon = notification.appIcon; + appName = notification.appName; + image = notification.image; + if (notification?.image) + dummyImageLoader.active = true; + expireTimeout = notification.expireTimeout; + hints = notification.hints; + urgency = notification.urgency; + resident = notification.resident; + hasActionIcons = notification.hasActionIcons; + actions = notification.actions.map(a => ({ + identifier: a.identifier, + text: a.text, + invoke: () => a.invoke() + })); + } +} diff --git a/services/Notifs.qml b/services/Notifs.qml index c36a4f3f..ed187ce5 100644 --- a/services/Notifs.qml +++ b/services/Notifs.qml @@ -14,9 +14,9 @@ import QtQuick Singleton { id: root - property list list: [] - readonly property list notClosed: list.filter(n => !n.closed) - readonly property list popups: list.filter(n => n.popup) + property list list: [] + readonly property list notClosed: list.filter(n => !n.closed) + readonly property list popups: list.filter(n => n.popup) property alias dnd: props.dnd property bool loaded @@ -139,223 +139,9 @@ Singleton { target: "notifs" } - component Notif: QtObject { - id: notif - - property bool popup - property bool closed - property var locks: new Set() - - property date time: new Date() - property string timeStr: qsTr("now") - - readonly property Timer timeStrTimer: Timer { - running: !notif.closed - repeat: true - interval: 5000 - onTriggered: notif.updateTimeStr() - } - - property Notification notification - property string id - property string summary - property string body - property string appIcon - property string appName - property string image - property var hints // Hints are not persisted across restarts - property real expireTimeout: Config.notifs.defaultExpireTimeout - property int urgency: NotificationUrgency.Normal - property bool resident - property bool hasActionIcons - property list actions - - readonly property Timer timer: Timer { - running: true - interval: notif.expireTimeout > 0 ? notif.expireTimeout : Config.notifs.defaultExpireTimeout - onTriggered: { - if (Config.notifs.expire) - notif.popup = false; - } - } - - readonly property LazyLoader dummyImageLoader: LazyLoader { - active: false - - PanelWindow { - implicitWidth: Config.notifs.sizes.image - implicitHeight: Config.notifs.sizes.image - color: "transparent" - mask: Region {} - - Image { - function tryCache(): void { - if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image) - return; - - const cacheKey = notif.appName + notif.summary + notif.id; - let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch; - for (let i = 0; i < cacheKey.length; i++) { - ch = cacheKey.charCodeAt(i); - h1 = Math.imul(h1 ^ ch, 2654435761); - h2 = Math.imul(h2 ^ ch, 1597334677); - } - h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); - h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); - h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); - h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); - const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0); - - const cache = `${Paths.notifimagecache}/${hash}.png`; - CUtils.saveItem(this, Qt.resolvedUrl(cache), () => { - notif.image = cache; - notif.dummyImageLoader.active = false; - }); - } - - anchors.fill: parent - source: Qt.resolvedUrl(notif.image) - fillMode: Image.PreserveAspectCrop - cache: false - asynchronous: true - opacity: 0 - - onStatusChanged: tryCache() - onWidthChanged: tryCache() - onHeightChanged: tryCache() - } - } - } - - readonly property Connections conn: Connections { - function onClosed(): void { - notif.close(); - } - - function onSummaryChanged(): void { - notif.summary = notif.notification.summary; - } - - function onBodyChanged(): void { - notif.body = notif.notification.body; - } - - function onAppIconChanged(): void { - notif.appIcon = notif.notification.appIcon; - } - - function onAppNameChanged(): void { - notif.appName = notif.notification.appName; - } - - function onImageChanged(): void { - notif.image = notif.notification.image; - if (notif.notification?.image) - notif.dummyImageLoader.active = true; - } - - function onExpireTimeoutChanged(): void { - notif.expireTimeout = notif.notification.expireTimeout; - } - - function onUrgencyChanged(): void { - notif.urgency = notif.notification.urgency; - } - - function onResidentChanged(): void { - notif.resident = notif.notification.resident; - } - - function onHasActionIconsChanged(): void { - notif.hasActionIcons = notif.notification.hasActionIcons; - } - - function onActionsChanged(): void { - notif.actions = notif.notification.actions.map(a => ({ - identifier: a.identifier, - text: a.text, - invoke: () => a.invoke() - })); - } - - function onHintsChanged(): void { - notif.hints = notif.notification.hints; - } - - target: notif.notification - } - - function updateTimeStr(): void { - const diff = Date.now() - time.getTime(); - const m = Math.floor(diff / 60000); - - if (m < 1) { - timeStr = qsTr("now"); - timeStrTimer.interval = 5000; - } else { - const h = Math.floor(m / 60); - const d = Math.floor(h / 24); - - if (d > 0) { - timeStr = `${d}d`; - timeStrTimer.interval = 3600000; - } else if (h > 0) { - timeStr = `${h}h`; - timeStrTimer.interval = 300000; - } else { - timeStr = `${m}m`; - timeStrTimer.interval = m < 10 ? 30000 : 60000; - } - } - } - - function lock(item: Item): void { - locks.add(item); - } - - function unlock(item: Item): void { - locks.delete(item); - if (closed) - close(); - } - - function close(): void { - closed = true; - if (locks.size === 0 && root.list.includes(this)) { - root.list = root.list.filter(n => n !== this); - notification?.dismiss(); - destroy(); - } - } - - Component.onCompleted: { - if (!notification) - return; - - id = notification.id; - summary = notification.summary; - body = notification.body; - appIcon = notification.appIcon; - appName = notification.appName; - image = notification.image; - if (notification?.image) - dummyImageLoader.active = true; - expireTimeout = notification.expireTimeout; - hints = notification.hints; - urgency = notification.urgency; - resident = notification.resident; - hasActionIcons = notification.hasActionIcons; - actions = notification.actions.map(a => ({ - identifier: a.identifier, - text: a.text, - invoke: () => a.invoke() - })); - } - } - Component { id: notifComp - Notif {} + NotifData {} } }