mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
refactor(notif): rename internals for clarity (popup vs history)
This commit is contained in:
@@ -43,7 +43,7 @@ NIconButton {
|
|||||||
function computeUnreadCount() {
|
function computeUnreadCount() {
|
||||||
var since = NotificationService.lastSeenTs;
|
var since = NotificationService.lastSeenTs;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var model = NotificationService.historyList;
|
var model = NotificationService.historyModel;
|
||||||
for (var i = 0; i < model.count; i++) {
|
for (var i = 0; i < model.count; i++) {
|
||||||
var item = model.get(i);
|
var item = model.get(i);
|
||||||
var ts = item.timestamp instanceof Date ? item.timestamp.getTime() : item.timestamp;
|
var ts = item.timestamp instanceof Date ? item.timestamp.getTime() : item.timestamp;
|
||||||
@@ -71,8 +71,8 @@ NIconButton {
|
|||||||
colorFg: Color.resolveColorKey(iconColorKey)
|
colorFg: Color.resolveColorKey(iconColorKey)
|
||||||
border.color: Style.capsuleBorderColor
|
border.color: Style.capsuleBorderColor
|
||||||
border.width: Style.capsuleBorderWidth
|
border.width: Style.capsuleBorderWidth
|
||||||
visible: !((hideWhenZero && NotificationService.historyList.count === 0) || (hideWhenZeroUnread && count === 0))
|
visible: !((hideWhenZero && NotificationService.historyModel.count === 0) || (hideWhenZeroUnread && count === 0))
|
||||||
opacity: !((hideWhenZero && NotificationService.historyList.count === 0) || (hideWhenZeroUnread && count === 0)) ? 1.0 : 0.0
|
opacity: !((hideWhenZero && NotificationService.historyModel.count === 0) || (hideWhenZeroUnread && count === 0)) ? 1.0 : 0.0
|
||||||
|
|
||||||
NPopupContextMenu {
|
NPopupContextMenu {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Variants {
|
|||||||
|
|
||||||
required property ShellScreen modelData
|
required property ShellScreen modelData
|
||||||
|
|
||||||
property ListModel notificationModel: NotificationService.activeList
|
property ListModel notificationModel: NotificationService.popupModel
|
||||||
|
|
||||||
// Deferred activation to prevent re-entrant QML incubation crash.
|
// Deferred activation to prevent re-entrant QML incubation crash.
|
||||||
// Direct binding to notificationModel.count would activate the Loader
|
// Direct binding to notificationModel.count would activate the Loader
|
||||||
@@ -177,7 +177,7 @@ Variants {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Service fallback if delegate is already invalid
|
// Service fallback if delegate is already invalid
|
||||||
NotificationService.dismissActiveNotification(notificationId);
|
NotificationService.dismissPopup(notificationId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ Variants {
|
|||||||
interval: Style.animationSlow
|
interval: Style.animationSlow
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
NotificationService.dismissActiveNotification(notificationId);
|
NotificationService.dismissPopup(notificationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ SmartPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function moveSelection(dir) {
|
function moveSelection(dir) {
|
||||||
var m = NotificationService.historyList;
|
var m = NotificationService.historyModel;
|
||||||
if (!m || m.count === 0)
|
if (!m || m.count === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ SmartPanel {
|
|||||||
function moveAction(dir) {
|
function moveAction(dir) {
|
||||||
if (focusIndex === -1)
|
if (focusIndex === -1)
|
||||||
return;
|
return;
|
||||||
var item = NotificationService.historyList.get(focusIndex);
|
var item = NotificationService.historyModel.get(focusIndex);
|
||||||
if (!item)
|
if (!item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ SmartPanel {
|
|||||||
function activateSelection() {
|
function activateSelection() {
|
||||||
if (focusIndex === -1)
|
if (focusIndex === -1)
|
||||||
return;
|
return;
|
||||||
var item = NotificationService.historyList.get(focusIndex);
|
var item = NotificationService.historyModel.get(focusIndex);
|
||||||
if (!item)
|
if (!item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ SmartPanel {
|
|||||||
function removeSelection() {
|
function removeSelection() {
|
||||||
if (focusIndex === -1)
|
if (focusIndex === -1)
|
||||||
return;
|
return;
|
||||||
var item = NotificationService.historyList.get(focusIndex);
|
var item = NotificationService.historyModel.get(focusIndex);
|
||||||
if (!item)
|
if (!item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ SmartPanel {
|
|||||||
|
|
||||||
// Calculate content height based on header + tabs (if visible) + content
|
// Calculate content height based on header + tabs (if visible) + content
|
||||||
property real calculatedHeight: {
|
property real calculatedHeight: {
|
||||||
if (NotificationService.historyList.count === 0) {
|
if (NotificationService.historyModel.count === 0) {
|
||||||
return headerBox.implicitHeight + scrollView.implicitHeight + Style.margin2L + Style.marginM;
|
return headerBox.implicitHeight + scrollView.implicitHeight + Style.margin2L + Style.marginM;
|
||||||
}
|
}
|
||||||
return headerBox.implicitHeight + scrollView.implicitHeight + Style.margin2L + Style.marginM;
|
return headerBox.implicitHeight + scrollView.implicitHeight + Style.margin2L + Style.marginM;
|
||||||
@@ -296,7 +296,7 @@ SmartPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function recalcRangeCounts() {
|
function recalcRangeCounts() {
|
||||||
var m = NotificationService.historyList;
|
var m = NotificationService.historyModel;
|
||||||
if (!m || typeof m.count === "undefined" || m.count <= 0) {
|
if (!m || typeof m.count === "undefined" || m.count <= 0) {
|
||||||
panelContent.rangeCounts = [0, 0, 0, 0];
|
panelContent.rangeCounts = [0, 0, 0, 0];
|
||||||
return;
|
return;
|
||||||
@@ -328,7 +328,7 @@ SmartPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hasNotificationsInCurrentRange() {
|
function hasNotificationsInCurrentRange() {
|
||||||
var m = NotificationService.historyList;
|
var m = NotificationService.historyModel;
|
||||||
if (!m || m.count === 0) {
|
if (!m || m.count === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ SmartPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: NotificationService.historyList
|
target: NotificationService.historyModel
|
||||||
function onCountChanged() {
|
function onCountChanged() {
|
||||||
panelContent.recalcRangeCounts();
|
panelContent.recalcRangeCounts();
|
||||||
}
|
}
|
||||||
@@ -433,7 +433,7 @@ SmartPanel {
|
|||||||
NTabBar {
|
NTabBar {
|
||||||
id: tabsBox
|
id: tabsBox
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: NotificationService.historyList.count > 0 && panelContent.groupByDate
|
visible: NotificationService.historyModel.count > 0 && panelContent.groupByDate
|
||||||
currentIndex: panelContent.currentRange
|
currentIndex: panelContent.currentRange
|
||||||
tabHeight: Style.toOdd(Style.baseWidgetSize * 0.8)
|
tabHeight: Style.toOdd(Style.baseWidgetSize * 0.8)
|
||||||
spacing: Style.marginXS
|
spacing: Style.marginXS
|
||||||
@@ -512,20 +512,20 @@ SmartPanel {
|
|||||||
|
|
||||||
NIcon {
|
NIcon {
|
||||||
icon: "bell-off"
|
icon: "bell-off"
|
||||||
pointSize: (NotificationService.historyList.count === 0) ? 48 : Style.baseWidgetSize
|
pointSize: (NotificationService.historyModel.count === 0) ? 48 : Style.baseWidgetSize
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
text: I18n.tr("notifications.panel.no-notifications")
|
text: I18n.tr("notifications.panel.no-notifications")
|
||||||
pointSize: (NotificationService.historyList.count === 0) ? Style.fontSizeL : Style.fontSizeM
|
pointSize: (NotificationService.historyModel.count === 0) ? Style.fontSizeL : Style.fontSizeM
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
NText {
|
NText {
|
||||||
visible: NotificationService.historyList.count === 0
|
visible: NotificationService.historyModel.count === 0
|
||||||
text: I18n.tr("notifications.panel.description")
|
text: I18n.tr("notifications.panel.description")
|
||||||
pointSize: Style.fontSizeS
|
pointSize: Style.fontSizeS
|
||||||
color: Color.mOnSurfaceVariant
|
color: Color.mOnSurfaceVariant
|
||||||
@@ -552,7 +552,7 @@ SmartPanel {
|
|||||||
spacing: Style.marginM
|
spacing: Style.marginM
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: NotificationService.historyList
|
model: NotificationService.historyModel
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: notificationDelegate
|
id: notificationDelegate
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ Singleton {
|
|||||||
Logger.w("IPC", "Argument to ipc call '" + funcName + "' must be a number");
|
Logger.w("IPC", "Argument to ipc call '" + funcName + "' must be a number");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (idx < 0 || idx >= NotificationService.activeList.count) {
|
if (idx < 0 || idx >= NotificationService.popupModel.count) {
|
||||||
Logger.w("IPC", "Notification index out of range: " + idx);
|
Logger.w("IPC", "Notification index out of range: " + idx);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return NotificationService.activeList.get(idx);
|
return NotificationService.popupModel.get(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
@@ -191,7 +191,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dismissOldest() {
|
function dismissOldest() {
|
||||||
NotificationService.dismissOldestActive();
|
NotificationService.dismissOldestPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeOldestHistory() {
|
function removeOldestHistory() {
|
||||||
@@ -199,7 +199,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dismissAll() {
|
function dismissAll() {
|
||||||
NotificationService.dismissAllActive();
|
NotificationService.dismissAllPopups();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHistory(): string {
|
function getHistory(): string {
|
||||||
@@ -230,13 +230,13 @@ Singleton {
|
|||||||
|
|
||||||
var actions = JSON.parse(notif.actionsJson || "[]");
|
var actions = JSON.parse(notif.actionsJson || "[]");
|
||||||
if (actions.length === 0) {
|
if (actions.length === 0) {
|
||||||
NotificationService.dismissActiveNotification(notif.id);
|
NotificationService.dismissPopup(notif.id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionId = actions.find(a => a.identifier === "default")?.identifier ?? actions[0].identifier;
|
var actionId = actions.find(a => a.identifier === "default")?.identifier ?? actions[0].identifier;
|
||||||
var result = NotificationService.invokeAction(notif.id, actionId);
|
var result = NotificationService.invokeAction(notif.id, actionId);
|
||||||
NotificationService.dismissActiveNotification(notif.id);
|
NotificationService.dismissPopup(notif.id);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Singleton {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
property int maxVisible: 5
|
property int maxPopups: 5
|
||||||
property int maxHistory: 100
|
property int maxHistory: 100
|
||||||
property string historyFile: Quickshell.env("NOCTALIA_NOTIF_HISTORY_FILE") || (Settings.cacheDir + "notifications.json")
|
property string historyFile: Quickshell.env("NOCTALIA_NOTIF_HISTORY_FILE") || (Settings.cacheDir + "notifications.json")
|
||||||
|
|
||||||
@@ -27,11 +27,11 @@ Singleton {
|
|||||||
property bool doNotDisturb: false
|
property bool doNotDisturb: false
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
property ListModel activeList: ListModel {}
|
property ListModel popupModel: ListModel {}
|
||||||
property ListModel historyList: ListModel {}
|
property ListModel historyModel: ListModel {}
|
||||||
|
|
||||||
// Internal state
|
// Internal state
|
||||||
property var activeNotifications: ({}) // Maps internal ID to {notification, watcher, metadata}
|
property var popupState: ({}) // Maps internal ID to {notification, watcher, cachedActions, metadata}
|
||||||
property var quickshellIdToInternalId: ({})
|
property var quickshellIdToInternalId: ({})
|
||||||
|
|
||||||
// Rate limiting for notification sounds (minimum 100ms between sounds)
|
// Rate limiting for notification sounds (minimum 100ms between sounds)
|
||||||
@@ -167,19 +167,19 @@ Singleton {
|
|||||||
|
|
||||||
// Check if this is a replacement notification
|
// Check if this is a replacement notification
|
||||||
const existingInternalId = quickshellIdToInternalId[quickshellId];
|
const existingInternalId = quickshellIdToInternalId[quickshellId];
|
||||||
if (existingInternalId && activeNotifications[existingInternalId]) {
|
if (existingInternalId && popupState[existingInternalId]) {
|
||||||
updateExistingNotification(existingInternalId, notification, data);
|
updatePopup(existingInternalId, notification, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate content
|
// Check for duplicate content
|
||||||
const duplicateId = findDuplicateNotification(data);
|
const duplicateId = findDuplicateNotification(data);
|
||||||
if (duplicateId) {
|
if (duplicateId) {
|
||||||
removeNotification(duplicateId);
|
removePopup(duplicateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new notification
|
// Add new notification
|
||||||
addNewNotification(quickshellId, notification, data);
|
addPopup(quickshellId, notification, data);
|
||||||
playNotificationSound(data.urgency, notification.appName);
|
playNotificationSound(data.urgency, notification.appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,30 +277,30 @@ Singleton {
|
|||||||
return defaultSoundFile;
|
return defaultSoundFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateExistingNotification(internalId, notification, data) {
|
function updatePopup(internalId, notification, data) {
|
||||||
const index = findNotificationIndex(internalId);
|
const index = findPopupIndex(internalId);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
return;
|
return;
|
||||||
const existing = activeList.get(index);
|
const existing = popupModel.get(index);
|
||||||
const oldTimestamp = existing.timestamp;
|
const oldTimestamp = existing.timestamp;
|
||||||
const oldProgress = existing.progress;
|
const oldProgress = existing.progress;
|
||||||
|
|
||||||
// Update properties (keeping original timestamp and progress)
|
// Update properties (keeping original timestamp and progress)
|
||||||
activeList.setProperty(index, "summary", data.summary);
|
popupModel.setProperty(index, "summary", data.summary);
|
||||||
activeList.setProperty(index, "summaryMarkdown", data.summaryMarkdown);
|
popupModel.setProperty(index, "summaryMarkdown", data.summaryMarkdown);
|
||||||
activeList.setProperty(index, "body", data.body);
|
popupModel.setProperty(index, "body", data.body);
|
||||||
activeList.setProperty(index, "bodyMarkdown", data.bodyMarkdown);
|
popupModel.setProperty(index, "bodyMarkdown", data.bodyMarkdown);
|
||||||
activeList.setProperty(index, "appName", data.appName);
|
popupModel.setProperty(index, "appName", data.appName);
|
||||||
activeList.setProperty(index, "urgency", data.urgency);
|
popupModel.setProperty(index, "urgency", data.urgency);
|
||||||
activeList.setProperty(index, "expireTimeout", data.expireTimeout);
|
popupModel.setProperty(index, "expireTimeout", data.expireTimeout);
|
||||||
activeList.setProperty(index, "originalImage", data.originalImage);
|
popupModel.setProperty(index, "originalImage", data.originalImage);
|
||||||
activeList.setProperty(index, "cachedImage", data.cachedImage);
|
popupModel.setProperty(index, "cachedImage", data.cachedImage);
|
||||||
activeList.setProperty(index, "actionsJson", data.actionsJson);
|
popupModel.setProperty(index, "actionsJson", data.actionsJson);
|
||||||
activeList.setProperty(index, "timestamp", oldTimestamp);
|
popupModel.setProperty(index, "timestamp", oldTimestamp);
|
||||||
activeList.setProperty(index, "progress", oldProgress);
|
popupModel.setProperty(index, "progress", oldProgress);
|
||||||
|
|
||||||
// Update stored notification object
|
// Update stored notification object
|
||||||
const notifData = activeNotifications[internalId];
|
const notifData = popupState[internalId];
|
||||||
notifData.notification = notification;
|
notifData.notification = notification;
|
||||||
|
|
||||||
// Deep copy actions to preserve them even if QML object clears list
|
// Deep copy actions to preserve them even if QML object clears list
|
||||||
@@ -319,7 +319,7 @@ Singleton {
|
|||||||
notification.tracked = true;
|
notification.tracked = true;
|
||||||
|
|
||||||
function onClosed() {
|
function onClosed() {
|
||||||
userDismissNotification(internalId);
|
dismissPopup(internalId);
|
||||||
}
|
}
|
||||||
notification.closed.connect(onClosed);
|
notification.closed.connect(onClosed);
|
||||||
notifData.onClosed = onClosed;
|
notifData.onClosed = onClosed;
|
||||||
@@ -329,7 +329,7 @@ Singleton {
|
|||||||
notifData.metadata.duration = calculateDuration(data);
|
notifData.metadata.duration = calculateDuration(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNewNotification(quickshellId, notification, data) {
|
function addPopup(quickshellId, notification, data) {
|
||||||
// Map IDs
|
// Map IDs
|
||||||
quickshellIdToInternalId[quickshellId] = data.id;
|
quickshellIdToInternalId[quickshellId] = data.id;
|
||||||
|
|
||||||
@@ -351,7 +351,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store notification data
|
// Store notification data
|
||||||
activeNotifications[data.id] = {
|
popupState[data.id] = {
|
||||||
"notification": notification,
|
"notification": notification,
|
||||||
"watcher": watcher,
|
"watcher": watcher,
|
||||||
"cachedActions": safeActions // Cache actions
|
"cachedActions": safeActions // Cache actions
|
||||||
@@ -370,24 +370,24 @@ Singleton {
|
|||||||
notification.tracked = true;
|
notification.tracked = true;
|
||||||
|
|
||||||
function onClosed() {
|
function onClosed() {
|
||||||
userDismissNotification(data.id);
|
dismissPopup(data.id);
|
||||||
}
|
}
|
||||||
notification.closed.connect(onClosed);
|
notification.closed.connect(onClosed);
|
||||||
activeNotifications[data.id].onClosed = onClosed;
|
popupState[data.id].onClosed = onClosed;
|
||||||
|
|
||||||
// Defer list insertion to prevent re-entrant QML incubation crash.
|
// Defer list insertion to prevent re-entrant QML incubation crash.
|
||||||
// Direct insert triggers Repeater.modelUpdated synchronously, which
|
// Direct insert triggers Repeater.modelUpdated synchronously, which
|
||||||
// incubates delegates whose signal handlers can re-enter the V4 engine
|
// incubates delegates whose signal handlers can re-enter the V4 engine
|
||||||
// and crash in QV4::Object::insertMember.
|
// and crash in QV4::Object::insertMember.
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
activeList.insert(0, data);
|
popupModel.insert(0, data);
|
||||||
|
|
||||||
// Remove overflow
|
// Remove overflow
|
||||||
while (activeList.count > maxVisible) {
|
while (popupModel.count > maxPopups) {
|
||||||
const last = activeList.get(activeList.count - 1);
|
const last = popupModel.get(popupModel.count - 1);
|
||||||
// Overflow only removes from ACTIVE view, but keeps it for history
|
// Overflow only removes from ACTIVE view, but keeps it for history
|
||||||
activeNotifications[last.id]?.notification?.dismiss(); // Visually dismiss
|
popupState[last.id]?.notification?.dismiss(); // Visually dismiss
|
||||||
activeList.remove(activeList.count - 1);
|
popupModel.remove(popupModel.count - 1);
|
||||||
// DO NOT call cleanupNotification here, we want to keep it for history actions
|
// DO NOT call cleanupNotification here, we want to keep it for history actions
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -396,8 +396,8 @@ Singleton {
|
|||||||
function findDuplicateNotification(data) {
|
function findDuplicateNotification(data) {
|
||||||
const contentId = getContentId(data.summary, data.body, data.appName);
|
const contentId = getContentId(data.summary, data.body, data.appName);
|
||||||
|
|
||||||
for (var i = 0; i < activeList.count; i++) {
|
for (var i = 0; i < popupModel.count; i++) {
|
||||||
const existing = activeList.get(i);
|
const existing = popupModel.get(i);
|
||||||
const existingContentId = getContentId(existing.summary, existing.body, existing.appName);
|
const existingContentId = getContentId(existing.summary, existing.body, existing.appName);
|
||||||
if (existingContentId === contentId) {
|
if (existingContentId === contentId) {
|
||||||
return existing.id;
|
return existing.id;
|
||||||
@@ -455,9 +455,9 @@ Singleton {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function findNotificationIndex(internalId) {
|
function findPopupIndex(internalId) {
|
||||||
for (var i = 0; i < activeList.count; i++) {
|
for (var i = 0; i < popupModel.count; i++) {
|
||||||
if (activeList.get(i).id === internalId) {
|
if (popupModel.get(i).id === internalId) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,45 +465,45 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateNotificationFromObject(internalId) {
|
function updateNotificationFromObject(internalId) {
|
||||||
const notifData = activeNotifications[internalId];
|
const notifData = popupState[internalId];
|
||||||
if (!notifData)
|
if (!notifData)
|
||||||
return;
|
return;
|
||||||
const index = findNotificationIndex(internalId);
|
const index = findPopupIndex(internalId);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
return;
|
return;
|
||||||
const data = createData(notifData.notification);
|
const data = createData(notifData.notification);
|
||||||
const existing = activeList.get(index);
|
const existing = popupModel.get(index);
|
||||||
|
|
||||||
// Update properties (keeping timestamp and progress)
|
// Update properties (keeping timestamp and progress)
|
||||||
activeList.setProperty(index, "summary", data.summary);
|
popupModel.setProperty(index, "summary", data.summary);
|
||||||
activeList.setProperty(index, "summaryMarkdown", data.summaryMarkdown);
|
popupModel.setProperty(index, "summaryMarkdown", data.summaryMarkdown);
|
||||||
activeList.setProperty(index, "body", data.body);
|
popupModel.setProperty(index, "body", data.body);
|
||||||
activeList.setProperty(index, "bodyMarkdown", data.bodyMarkdown);
|
popupModel.setProperty(index, "bodyMarkdown", data.bodyMarkdown);
|
||||||
activeList.setProperty(index, "appName", data.appName);
|
popupModel.setProperty(index, "appName", data.appName);
|
||||||
activeList.setProperty(index, "urgency", data.urgency);
|
popupModel.setProperty(index, "urgency", data.urgency);
|
||||||
activeList.setProperty(index, "expireTimeout", data.expireTimeout);
|
popupModel.setProperty(index, "expireTimeout", data.expireTimeout);
|
||||||
activeList.setProperty(index, "originalImage", data.originalImage);
|
popupModel.setProperty(index, "originalImage", data.originalImage);
|
||||||
activeList.setProperty(index, "cachedImage", data.cachedImage);
|
popupModel.setProperty(index, "cachedImage", data.cachedImage);
|
||||||
activeList.setProperty(index, "actionsJson", data.actionsJson);
|
popupModel.setProperty(index, "actionsJson", data.actionsJson);
|
||||||
|
|
||||||
// Update metadata
|
// Update metadata
|
||||||
notifData.metadata.urgency = data.urgency;
|
notifData.metadata.urgency = data.urgency;
|
||||||
notifData.metadata.duration = calculateDuration(data);
|
notifData.metadata.duration = calculateDuration(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeNotification(id) {
|
function removePopup(id) {
|
||||||
const index = findNotificationIndex(id);
|
const index = findPopupIndex(id);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
activeList.remove(index);
|
popupModel.remove(index);
|
||||||
}
|
}
|
||||||
cleanupNotification(id);
|
cleanupNotification(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupNotification(id) {
|
function cleanupNotification(id) {
|
||||||
const notifData = activeNotifications[id];
|
const notifData = popupState[id];
|
||||||
if (notifData) {
|
if (notifData) {
|
||||||
notifData.watcher?.destroy();
|
notifData.watcher?.destroy();
|
||||||
delete activeNotifications[id];
|
delete popupState[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up quickshell ID mapping
|
// Clean up quickshell ID mapping
|
||||||
@@ -519,7 +519,7 @@ Singleton {
|
|||||||
Timer {
|
Timer {
|
||||||
interval: 50
|
interval: 50
|
||||||
repeat: true
|
repeat: true
|
||||||
running: activeList.count > 0
|
running: popupModel.count > 0
|
||||||
onTriggered: updateAllProgress()
|
onTriggered: updateAllProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,9 +527,9 @@ Singleton {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const toRemove = [];
|
const toRemove = [];
|
||||||
|
|
||||||
for (var i = 0; i < activeList.count; i++) {
|
for (var i = 0; i < popupModel.count; i++) {
|
||||||
const notif = activeList.get(i);
|
const notif = popupModel.get(i);
|
||||||
const notifData = activeNotifications[notif.id];
|
const notifData = popupState[notif.id];
|
||||||
if (!notifData)
|
if (!notifData)
|
||||||
continue;
|
continue;
|
||||||
const meta = notifData.metadata;
|
const meta = notifData.metadata;
|
||||||
@@ -541,7 +541,7 @@ Singleton {
|
|||||||
if (progress <= 0) {
|
if (progress <= 0) {
|
||||||
toRemove.push(notif.id);
|
toRemove.push(notif.id);
|
||||||
} else if (Math.abs(notif.progress - progress) > 0.005) {
|
} else if (Math.abs(notif.progress - progress) > 0.005) {
|
||||||
activeList.setProperty(i, "progress", progress);
|
popupModel.setProperty(i, "progress", progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,8 +571,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateImagePath(notificationId, path) {
|
function updateImagePath(notificationId, path) {
|
||||||
updateModel(activeList, notificationId, "cachedImage", path);
|
updateModel(popupModel, notificationId, "cachedImage", path);
|
||||||
updateModel(historyList, notificationId, "cachedImage", path);
|
updateModel(historyModel, notificationId, "cachedImage", path);
|
||||||
saveHistory();
|
saveHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,18 +588,18 @@ Singleton {
|
|||||||
// History management
|
// History management
|
||||||
function addToHistory(data) {
|
function addToHistory(data) {
|
||||||
// Defer list insertion to prevent re-entrant QML incubation crash.
|
// Defer list insertion to prevent re-entrant QML incubation crash.
|
||||||
// See addNewNotification for full explanation.
|
// See addPopup for full explanation.
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
historyList.insert(0, data);
|
historyModel.insert(0, data);
|
||||||
|
|
||||||
while (historyList.count > maxHistory) {
|
while (historyModel.count > maxHistory) {
|
||||||
const old = historyList.get(historyList.count - 1);
|
const old = historyModel.get(historyModel.count - 1);
|
||||||
// Only delete cached images that are in our cache directory
|
// Only delete cached images that are in our cache directory
|
||||||
const cachedPath = old.cachedImage ? old.cachedImage.replace(/^file:\/\//, "") : "";
|
const cachedPath = old.cachedImage ? old.cachedImage.replace(/^file:\/\//, "") : "";
|
||||||
if (cachedPath && cachedPath.startsWith(ImageCacheService.notificationsDir)) {
|
if (cachedPath && cachedPath.startsWith(ImageCacheService.notificationsDir)) {
|
||||||
Quickshell.execDetached(["rm", "-f", cachedPath]);
|
Quickshell.execDetached(["rm", "-f", cachedPath]);
|
||||||
}
|
}
|
||||||
historyList.remove(historyList.count - 1);
|
historyModel.remove(historyModel.count - 1);
|
||||||
}
|
}
|
||||||
saveHistory();
|
saveHistory();
|
||||||
});
|
});
|
||||||
@@ -635,8 +635,8 @@ Singleton {
|
|||||||
function performSaveHistory() {
|
function performSaveHistory() {
|
||||||
try {
|
try {
|
||||||
const items = [];
|
const items = [];
|
||||||
for (var i = 0; i < historyList.count; i++) {
|
for (var i = 0; i < historyModel.count; i++) {
|
||||||
const n = historyList.get(i);
|
const n = historyModel.get(i);
|
||||||
const copy = Object.assign({}, n);
|
const copy = Object.assign({}, n);
|
||||||
copy.timestamp = n.timestamp.getTime();
|
copy.timestamp = n.timestamp.getTime();
|
||||||
items.push(copy);
|
items.push(copy);
|
||||||
@@ -650,7 +650,7 @@ Singleton {
|
|||||||
|
|
||||||
function loadHistory() {
|
function loadHistory() {
|
||||||
try {
|
try {
|
||||||
historyList.clear();
|
historyModel.clear();
|
||||||
for (const item of adapter.notifications || []) {
|
for (const item of adapter.notifications || []) {
|
||||||
const time = new Date(item.timestamp);
|
const time = new Date(item.timestamp);
|
||||||
|
|
||||||
@@ -660,20 +660,20 @@ Singleton {
|
|||||||
cachedImage = item.originalImage || "";
|
cachedImage = item.originalImage || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
historyList.append({
|
historyModel.append({
|
||||||
"id": item.id || "",
|
"id": item.id || "",
|
||||||
"summary": item.summary || "",
|
"summary": item.summary || "",
|
||||||
"summaryMarkdown": processNotificationMarkdown(item.summary || ""),
|
"summaryMarkdown": processNotificationMarkdown(item.summary || ""),
|
||||||
"body": item.body || "",
|
"body": item.body || "",
|
||||||
"bodyMarkdown": processNotificationMarkdown(item.body || ""),
|
"bodyMarkdown": processNotificationMarkdown(item.body || ""),
|
||||||
"appName": item.appName || "",
|
"appName": item.appName || "",
|
||||||
"urgency": item.urgency < 0 || item.urgency > 2 ? 1 : item.urgency,
|
"urgency": item.urgency < 0 || item.urgency > 2 ? 1 : item.urgency,
|
||||||
"timestamp": time,
|
"timestamp": time,
|
||||||
"originalImage": item.originalImage || "",
|
"originalImage": item.originalImage || "",
|
||||||
"cachedImage": cachedImage,
|
"cachedImage": cachedImage,
|
||||||
"actionsJson": item.actionsJson || "[]",
|
"actionsJson": item.actionsJson || "[]",
|
||||||
"originalId": item.originalId || 0
|
"originalId": item.originalId || 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.e("Notifications", "Load failed:", e);
|
Logger.e("Notifications", "Load failed:", e);
|
||||||
@@ -870,7 +870,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pauseTimeout(id) {
|
function pauseTimeout(id) {
|
||||||
const notifData = activeNotifications[id];
|
const notifData = popupState[id];
|
||||||
if (notifData && !notifData.metadata.paused) {
|
if (notifData && !notifData.metadata.paused) {
|
||||||
notifData.metadata.paused = true;
|
notifData.metadata.paused = true;
|
||||||
notifData.metadata.pauseTime = Date.now();
|
notifData.metadata.pauseTime = Date.now();
|
||||||
@@ -878,46 +878,41 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resumeTimeout(id) {
|
function resumeTimeout(id) {
|
||||||
const notifData = activeNotifications[id];
|
const notifData = popupState[id];
|
||||||
if (notifData && notifData.metadata.paused) {
|
if (notifData && notifData.metadata.paused) {
|
||||||
notifData.metadata.timestamp += Date.now() - notifData.metadata.pauseTime;
|
notifData.metadata.timestamp += Date.now() - notifData.metadata.pauseTime;
|
||||||
notifData.metadata.paused = false;
|
notifData.metadata.paused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API
|
// Dismiss a popup notification (e.g. clicked close, swipe, or overflow).
|
||||||
function dismissActiveNotification(id) {
|
// Removes from popup list but KEEPS data for history.
|
||||||
userDismissNotification(id);
|
function dismissPopup(id) {
|
||||||
}
|
const index = findPopupIndex(id);
|
||||||
|
|
||||||
// User dismissed from active view (e.g. clicked close, or swipe)
|
|
||||||
// This behaves like "overflow" - removes from active list but KEEPS data for history
|
|
||||||
function userDismissNotification(id) {
|
|
||||||
const index = findNotificationIndex(id);
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
activeList.remove(index);
|
popupModel.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissOldestActive() {
|
function dismissOldestPopup() {
|
||||||
if (activeList.count > 0) {
|
if (popupModel.count > 0) {
|
||||||
const lastNotif = activeList.get(activeList.count - 1);
|
const lastNotif = popupModel.get(popupModel.count - 1);
|
||||||
dismissActiveNotification(lastNotif.id);
|
dismissPopup(lastNotif.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissAllActive() {
|
function dismissAllPopups() {
|
||||||
for (const id in activeNotifications) {
|
for (const id in popupState) {
|
||||||
activeNotifications[id].notification?.dismiss();
|
popupState[id].notification?.dismiss();
|
||||||
activeNotifications[id].watcher?.destroy();
|
popupState[id].watcher?.destroy();
|
||||||
}
|
}
|
||||||
activeList.clear();
|
popupModel.clear();
|
||||||
activeNotifications = {};
|
popupState = {};
|
||||||
quickshellIdToInternalId = {};
|
quickshellIdToInternalId = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function invokeActionAndSuppressClose(id, actionId) {
|
function invokeActionAndSuppressClose(id, actionId) {
|
||||||
const notifData = activeNotifications[id];
|
const notifData = popupState[id];
|
||||||
if (notifData && notifData.notification && notifData.onClosed) {
|
if (notifData && notifData.notification && notifData.onClosed) {
|
||||||
try {
|
try {
|
||||||
notifData.notification.closed.disconnect(notifData.onClosed);
|
notifData.notification.closed.disconnect(notifData.onClosed);
|
||||||
@@ -929,7 +924,7 @@ Singleton {
|
|||||||
|
|
||||||
function invokeAction(id, actionId) {
|
function invokeAction(id, actionId) {
|
||||||
let invoked = false;
|
let invoked = false;
|
||||||
const notifData = activeNotifications[id];
|
const notifData = popupState[id];
|
||||||
|
|
||||||
if (notifData && notifData.notification) {
|
if (notifData && notifData.notification) {
|
||||||
const actionsToUse = (notifData.notification.actions && notifData.notification.actions.length > 0) ? notifData.notification.actions : (notifData.cachedActions || []);
|
const actionsToUse = (notifData.notification.actions && notifData.notification.actions.length > 0) ? notifData.notification.actions : (notifData.cachedActions || []);
|
||||||
@@ -966,9 +961,9 @@ Singleton {
|
|||||||
}
|
}
|
||||||
} else if (!notifData) {
|
} else if (!notifData) {
|
||||||
Logger.w("NotificationService", "No active notification data for id=" + id + ", searching history for manual invoke");
|
Logger.w("NotificationService", "No active notification data for id=" + id + ", searching history for manual invoke");
|
||||||
for (var i = 0; i < historyList.count; i++) {
|
for (var i = 0; i < historyModel.count; i++) {
|
||||||
if (historyList.get(i).id === id) {
|
if (historyModel.get(i).id === id) {
|
||||||
const histEntry = historyList.get(i);
|
const histEntry = historyModel.get(i);
|
||||||
if (histEntry.originalId) {
|
if (histEntry.originalId) {
|
||||||
invoked = manualInvoke(histEntry.originalId, actionId);
|
invoked = manualInvoke(histEntry.originalId, actionId);
|
||||||
}
|
}
|
||||||
@@ -983,8 +978,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear actions after use
|
// Clear actions after use
|
||||||
updateModel(activeList, id, "actionsJson", "[]");
|
updateModel(popupModel, id, "actionsJson", "[]");
|
||||||
updateModel(historyList, id, "actionsJson", "[]");
|
updateModel(historyModel, id, "actionsJson", "[]");
|
||||||
saveHistory();
|
saveHistory();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1032,15 +1027,15 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function removeFromHistory(notificationId) {
|
function removeFromHistory(notificationId) {
|
||||||
for (var i = 0; i < historyList.count; i++) {
|
for (var i = 0; i < historyModel.count; i++) {
|
||||||
const notif = historyList.get(i);
|
const notif = historyModel.get(i);
|
||||||
if (notif.id === notificationId) {
|
if (notif.id === notificationId) {
|
||||||
// Only delete cached images that are in our cache directory
|
// Only delete cached images that are in our cache directory
|
||||||
const cachedPath = notif.cachedImage ? notif.cachedImage.replace(/^file:\/\//, "") : "";
|
const cachedPath = notif.cachedImage ? notif.cachedImage.replace(/^file:\/\//, "") : "";
|
||||||
if (cachedPath && cachedPath.startsWith(ImageCacheService.notificationsDir)) {
|
if (cachedPath && cachedPath.startsWith(ImageCacheService.notificationsDir)) {
|
||||||
Quickshell.execDetached(["rm", "-f", cachedPath]);
|
Quickshell.execDetached(["rm", "-f", cachedPath]);
|
||||||
}
|
}
|
||||||
historyList.remove(i);
|
historyModel.remove(i);
|
||||||
saveHistory();
|
saveHistory();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1049,14 +1044,14 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function removeOldestHistory() {
|
function removeOldestHistory() {
|
||||||
if (historyList.count > 0) {
|
if (historyModel.count > 0) {
|
||||||
const oldest = historyList.get(historyList.count - 1);
|
const oldest = historyModel.get(historyModel.count - 1);
|
||||||
// Only delete cached images that are in our cache directory
|
// Only delete cached images that are in our cache directory
|
||||||
const cachedPath = oldest.cachedImage ? oldest.cachedImage.replace(/^file:\/\//, "") : "";
|
const cachedPath = oldest.cachedImage ? oldest.cachedImage.replace(/^file:\/\//, "") : "";
|
||||||
if (cachedPath && cachedPath.startsWith(ImageCacheService.notificationsDir)) {
|
if (cachedPath && cachedPath.startsWith(ImageCacheService.notificationsDir)) {
|
||||||
Quickshell.execDetached(["rm", "-f", cachedPath]);
|
Quickshell.execDetached(["rm", "-f", cachedPath]);
|
||||||
}
|
}
|
||||||
historyList.remove(historyList.count - 1);
|
historyModel.remove(historyModel.count - 1);
|
||||||
saveHistory();
|
saveHistory();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1070,14 +1065,14 @@ Singleton {
|
|||||||
Logger.e("Notifications", "Failed to clear cache directory:", e);
|
Logger.e("Notifications", "Failed to clear cache directory:", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
historyList.clear();
|
historyModel.clear();
|
||||||
saveHistory();
|
saveHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHistorySnapshot() {
|
function getHistorySnapshot() {
|
||||||
const items = [];
|
const items = [];
|
||||||
for (var i = 0; i < historyList.count; i++) {
|
for (var i = 0; i < historyModel.count; i++) {
|
||||||
const entry = historyList.get(i);
|
const entry = historyModel.get(i);
|
||||||
items.push({
|
items.push({
|
||||||
"id": entry.id,
|
"id": entry.id,
|
||||||
"summary": entry.summary,
|
"summary": entry.summary,
|
||||||
|
|||||||
Reference in New Issue
Block a user