summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Center/HistoryNotificationCard.qml
blob: 5e033e52b2e73321d5c884fb74af0d5760a64c34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
import QtQuick
import QtQuick.Effects
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets

Rectangle {
    id: root

    required property var historyItem
    property bool isSelected: false
    property bool keyboardNavigationActive: false
    property bool descriptionExpanded: NotificationService.expandedMessages[historyItem?.id ? (historyItem.id + "_hist") : ""] || false
    property bool __initialized: false

    Component.onCompleted: {
        Qt.callLater(() => {
            if (root)
                root.__initialized = true;
        });
    }

    readonly property bool compactMode: SettingsData.notificationCompactMode
    readonly property real cardPadding: compactMode ? Theme.notificationCardPaddingCompact : Theme.notificationCardPadding
    readonly property real iconSize: compactMode ? Theme.notificationIconSizeCompact : Theme.notificationIconSizeNormal
    readonly property real contentSpacing: compactMode ? Theme.spacingXS : Theme.spacingS
    readonly property real collapsedContentHeight: iconSize + cardPadding
    readonly property real baseCardHeight: cardPadding * 2 + collapsedContentHeight

    width: parent ? parent.width : 400
    height: baseCardHeight + contentItem.extraHeight
    radius: Theme.cornerRadius
    clip: false
    readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"

    ElevationShadow {
        id: shadowLayer
        anchors.fill: parent
        z: -1
        level: Theme.elevationLevel1
        fallbackOffset: 1
        targetRadius: root.radius
        targetColor: root.color
        borderColor: root.border.color
        borderWidth: root.border.width
        shadowEnabled: root.shadowsAllowed
    }

    color: {
        if (isSelected && keyboardNavigationActive)
            return Theme.primaryPressed;
        return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
    }
    border.color: {
        if (isSelected && keyboardNavigationActive)
            return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5);
        if (historyItem.urgency === 2)
            return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
        return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05);
    }
    border.width: {
        if (isSelected && keyboardNavigationActive)
            return 1.5;
        if (historyItem.urgency === 2)
            return 2;
        return 0;
    }

    Behavior on border.color {
        enabled: root.__initialized
        ColorAnimation {
            duration: root.__initialized ? Theme.shortDuration : 0
            easing.type: Theme.standardEasing
        }
    }

    Rectangle {
        anchors.fill: parent
        radius: parent.radius
        visible: historyItem.urgency === 2
        gradient: Gradient {
            orientation: Gradient.Horizontal
            GradientStop {
                position: 0.0
                color: Theme.primary
            }
            GradientStop {
                position: 0.02
                color: Theme.primary
            }
            GradientStop {
                position: 0.021
                color: "transparent"
            }
        }
    }

    Item {
        id: contentItem

        readonly property real expandedTextHeight: descriptionText.contentHeight
        readonly property real twoLineHeight: descriptionText.font.pixelSize * 1.2 * 2
        readonly property real extraHeight: (descriptionExpanded && expandedTextHeight > twoLineHeight + 2) ? (expandedTextHeight - twoLineHeight) : 0

        anchors.top: parent.top
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.topMargin: cardPadding
        anchors.leftMargin: Theme.spacingL
        anchors.rightMargin: Theme.spacingL + Theme.notificationHoverRevealMargin
        height: collapsedContentHeight + extraHeight

        DankCircularImage {
            id: iconContainer
            readonly property string rawImage: historyItem.image || ""
            readonly property string iconFromImage: {
                if (rawImage.startsWith("image://icon/"))
                    return rawImage.substring(13);
                return "";
            }
            readonly property bool imageHasSpecialPrefix: {
                const icon = iconFromImage;
                return icon.startsWith("material:") || icon.startsWith("svg:") || icon.startsWith("unicode:") || icon.startsWith("image:");
            }
            readonly property bool hasNotificationImage: rawImage !== "" && !rawImage.startsWith("image://icon/")

            width: iconSize
            height: iconSize
            anchors.left: parent.left
            anchors.top: parent.top

            imageSource: {
                if (hasNotificationImage)
                    return historyItem.image;
                if (imageHasSpecialPrefix)
                    return "";
                const appIcon = historyItem.appIcon;
                if (!appIcon)
                    return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
                if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
                    return appIcon;
                if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
                    return "";
                return Paths.resolveIconPath(appIcon);
            }

            hasImage: hasNotificationImage
            fallbackIcon: {
                if (imageHasSpecialPrefix)
                    return iconFromImage;
                return historyItem.appIcon || iconFromImage || "";
            }
            fallbackText: {
                const appName = historyItem.appName || "?";
                return appName.charAt(0).toUpperCase();
            }

            Rectangle {
                anchors.fill: parent
                anchors.margins: -2
                radius: width / 2
                color: "transparent"
                border.color: root.color
                border.width: 5
                visible: parent.hasImage
                antialiasing: true
            }
        }

        Rectangle {
            anchors.left: iconContainer.right
            anchors.leftMargin: Theme.spacingM
            anchors.right: parent.right
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.bottomMargin: contentSpacing
            color: "transparent"

            Column {
                width: parent.width
                anchors.top: parent.top
                spacing: Theme.notificationContentSpacing

                Row {
                    width: parent.width
                    spacing: Theme.spacingXS
                    readonly property real reservedTrailingWidth: historySeparator.implicitWidth + Math.max(historyTimeText.implicitWidth, 72) + spacing

                    StyledText {
                        id: historyTitleText
                        width: Math.min(implicitWidth, Math.max(0, parent.width - parent.reservedTrailingWidth))
                        text: {
                            let title = historyItem.summary || "";
                            const appName = historyItem.appName || "";
                            const prefix = appName + " • ";
                            if (appName && title.toLowerCase().startsWith(prefix.toLowerCase())) {
                                title = title.substring(prefix.length);
                            }
                            return title;
                        }
                        color: Theme.surfaceText
                        font.pixelSize: Theme.fontSizeMedium
                        font.weight: Font.Medium
                        elide: Text.ElideRight
                        maximumLineCount: 1
                        visible: text.length > 0
                    }
                    StyledText {
                        id: historySeparator
                        text: (historyTitleText.text.length > 0 && historyTimeText.text.length > 0) ? " • " : ""
                        color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
                        font.pixelSize: Theme.fontSizeSmall
                        font.weight: Font.Normal
                    }
                    StyledText {
                        id: historyTimeText
                        text: NotificationService.formatHistoryTime(historyItem.timestamp)
                        color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
                        font.pixelSize: Theme.fontSizeSmall
                        font.weight: Font.Normal
                        visible: text.length > 0
                    }
                }

                StyledText {
                    id: descriptionText
                    property bool hasMoreText: truncated

                    text: historyItem.htmlBody || historyItem.body || ""
                    color: Theme.surfaceVariantText
                    font.pixelSize: Theme.fontSizeSmall
                    width: parent.width
                    elide: descriptionExpanded ? Text.ElideNone : Text.ElideRight
                    maximumLineCount: descriptionExpanded ? -1 : (compactMode ? 1 : 2)
                    wrapMode: Text.WordWrap
                    visible: text.length > 0
                    linkColor: Theme.primary
                    onLinkActivated: link => Qt.openUrlExternally(link)

                    MouseArea {
                        anchors.fill: parent
                        cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : (parent.hasMoreText || descriptionExpanded) ? Qt.PointingHandCursor : Qt.ArrowCursor

                        onClicked: mouse => {
                            if (!parent.hoveredLink && (parent.hasMoreText || descriptionExpanded)) {
                                const messageId = historyItem?.id ? (historyItem.id + "_hist") : "";
                                NotificationService.toggleMessageExpansion(messageId);
                            }
                        }

                        propagateComposedEvents: true
                        onPressed: mouse => {
                            if (parent.hoveredLink)
                                mouse.accepted = false;
                        }
                        onReleased: mouse => {
                            if (parent.hoveredLink)
                                mouse.accepted = false;
                        }
                    }
                }
            }
        }
    }

    DankActionButton {
        anchors.top: parent.top
        anchors.right: parent.right
        anchors.topMargin: cardPadding
        anchors.rightMargin: Theme.spacingL
        iconName: "close"
        iconSize: compactMode ? 16 : 18
        buttonSize: compactMode ? 24 : 28
        onClicked: NotificationService.removeFromHistory(historyItem.id)
    }
}