/* * SPDX-FileCopyrightText: 2021 Devin Lin * SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ import QtQuick 2.8 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import Qt5Compat.GraphicalEffects import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasma5support 2.0 as P5Support import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.notificationmanager 1.0 as NotificationManager import org.kde.coreaddons 1.0 as KCoreAddons Item { id: notificationItem required property var notificationsModel required property int notificationsModelType /** * Whether the notification is allowed to invoke any action, or if it should instead * emit the runActionRequested(action) signal, containing the code to run. * * This is useful for cases like the lockscreen, where actions should only be run after * the user logs in. */ property bool requestToInvoke: false property var model property int modelIndex property P5Support.DataSource timeSource readonly property int notificationType: model.type readonly property bool inGroup: model.isInGroup || false readonly property bool inHistory: true readonly property string applicationIconSource: model.applicationIconName readonly property string applicationName: model.applicationName readonly property string originName: model.originName || "" readonly property string summary: model.summary readonly property var time: model.updated || model.created readonly property bool hasReplyAction: model.hasReplyAction || false readonly property string replyActionLabel: model.replyActionLabel || "" readonly property string replyPlaceholderText: model.replyPlaceholderText || "" readonly property string replySubmitButtonText: model.replySubmitButtonText || "" readonly property string replySubmitButtonIconName: model.replySubmitButtonIconName || "" // configure button on every single notifications is bit overwhelming readonly property bool configurable: !inGroup && model.configurable readonly property bool dismissable: model.type === NotificationManager.Notifications.JobType && model.jobState !== NotificationManager.Notifications.JobStateStopped && model.dismissed && notificationSettings.permanentJobPopups readonly property bool dismissed: model.dismissed || false readonly property bool closable: model.closable readonly property string body: model.body || "" readonly property var icon: model.image || model.iconName readonly property var urls: model.urls || [] readonly property int jobState: model.jobState || 0 readonly property int percentage: model.percentage || 0 readonly property int jobError: model.jobError || 0 readonly property bool suspendable: !!model.suspendable readonly property bool killable: !!model.killable readonly property QtObject jobDetails: model.jobDetails || null readonly property string configureActionLabel: model.configureActionLabel || "" readonly property bool hasDefaultAction: model.hasDefaultAction readonly property bool addDefaultAction: (model.hasDefaultAction && model.defaultActionLabel && (model.actionLabels || []).indexOf(model.defaultActionLabel) === -1) ? true : false readonly property var actionNames: { var actions = (model.actionNames || []); if (addDefaultAction) { actions.unshift("default"); // prepend } return actions; } readonly property var actionLabels: { var labels = (model.actionLabels || []); if (addDefaultAction) { labels.unshift(model.defaultActionLabel); } return labels; } /** * This signal is emitted and intended for the parent to make its own decision * on whether to run the requested notification action. */ signal runActionRequested() signal actionInvoked(string actionName) signal replied(string text) signal openUrl(string url) signal fileActionInvoked(QtObject action) signal suspendJobClicked signal resumeJobClicked signal killJobClicked function expire() { if (model.resident) { model.expired = true; } else { if (notificationsModelType === NotificationsModelType.WatchedNotificationsModel) { notificationsModel.expire(model.notificationId); } else if (notificationsModelType === NotificationsModelType.NotificationsModel) { notificationsModel.expire(notificationsModel.index(modelIndex, 0)); } } } function close() { if (notificationsModelType === NotificationsModelType.WatchedNotificationsModel) { notificationsModel.close(model.notificationId); } else if (notificationsModelType === NotificationsModelType.NotificationsModel) { notificationsModel.close(notificationsModel.index(modelIndex, 0)); } } // TODO call function configure() { notificationsModel.configure(notificationsModel.index(modelIndex, 0)) } property var pendingAction: () => {} function runPendingAction() { pendingAction(); } onActionInvoked: { let action = () => { if (notificationsModelType === NotificationsModelType.WatchedNotificationsModel) { if (actionName === "") { notificationsModel.invokeDefaultAction(model.notificationId, NotificationManager.None); } else { notificationsModel.invokeAction(notificationItem.model.notificationId, actionName, NotificationManager.None); } } else if (notificationsModelType === NotificationsModelType.NotificationsModel) { if (actionName === "default") { notificationsModel.invokeDefaultAction(notificationsModel.index(modelIndex, 0), NotificationManager.Close); // notification closes } else { notificationsModel.invokeAction(notificationsModel.index(modelIndex, 0), actionName, NotificationManager.Close); // notification closes } } expire(); } if (notificationItem.requestToInvoke) { pendingAction = action; runActionRequested(); } else { action(); } } onOpenUrl: { let action = () => { Qt.openUrlExternally(url); expire(); } if (notificationItem.requestToInvoke) { pendingAction = action; runActionRequested(); } else { action(); } } onFileActionInvoked: { let action = () => { if (action.objectName === "movetotrash" || action.objectName === "deletefile") { close(); } else { expire(); } } if (notificationItem.requestToInvoke) { pendingAction = action; runActionRequested(); } else { action(); } } onSuspendJobClicked: { let action = () => notificationsModel.suspendJob(notificationsModel.index(modelIndex, 0)); if (notificationItem.requestToInvoke) { pendingAction = action; runActionRequested(); } else { action(); } } onResumeJobClicked: { let action = () => notificationsModel.resumeJob(notificationsModel.index(modelIndex, 0)); if (notificationItem.requestToInvoke) { pendingAction = action; runActionRequested(); } else { action(); } } onKillJobClicked: { let action = () => notificationsModel.killJob(notificationsModel.index(modelIndex, 0)); if (notificationItem.requestToInvoke) { pendingAction = action; runActionRequested(); } else { action(); } } }