summaryrefslogtreecommitdiff
path: root/modules/sidebar/NotifGroup.qml
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-18 20:37:00 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-18 20:37:00 +1000
commitb5b536ac8f9c63fec48a6fc252e5080de28f68d2 (patch)
tree2ed7263d8769a816ede39e81cde113881007b8b0 /modules/sidebar/NotifGroup.qml
parentfeat: add sidebar (diff)
downloadcaelestia-shell-b5b536ac8f9c63fec48a6fc252e5080de28f68d2.tar.gz
caelestia-shell-b5b536ac8f9c63fec48a6fc252e5080de28f68d2.tar.bz2
caelestia-shell-b5b536ac8f9c63fec48a6fc252e5080de28f68d2.zip
sidebar: add notifs
Diffstat (limited to 'modules/sidebar/NotifGroup.qml')
-rw-r--r--modules/sidebar/NotifGroup.qml216
1 files changed, 216 insertions, 0 deletions
diff --git a/modules/sidebar/NotifGroup.qml b/modules/sidebar/NotifGroup.qml
new file mode 100644
index 0000000..154b530
--- /dev/null
+++ b/modules/sidebar/NotifGroup.qml
@@ -0,0 +1,216 @@
+pragma ComponentBehavior: Bound
+
+import qs.components
+import qs.components.controls
+import qs.components.effects
+import qs.services
+import qs.config
+import qs.utils
+import Quickshell
+import Quickshell.Widgets
+import Quickshell.Services.Notifications
+import QtQuick
+import QtQuick.Layouts
+
+StyledRect {
+ id: root
+
+ required property string modelData
+ required property Props props
+
+ readonly property list<var> notifs: Notifs.list.filter(notif => notif.appName === modelData).reverse()
+ readonly property string image: notifs.find(n => n.image.length > 0)?.image ?? ""
+ readonly property string appIcon: notifs.find(n => n.appIcon.length > 0)?.appIcon ?? ""
+ readonly property string urgency: notifs.some(n => n.urgency === NotificationUrgency.Critical) ? "critical" : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? "normal" : "low"
+
+ readonly property bool expanded: props.expandedNotifs.includes(modelData)
+
+ anchors.left: parent?.left
+ anchors.right: parent?.right
+ implicitHeight: content.implicitHeight + Appearance.padding.normal * 2
+
+ clip: true
+ radius: Appearance.rounding.normal
+ color: root.urgency === "critical" ? Colours.palette.m3secondaryContainer : Colours.layer(Colours.palette.m3surfaceContainer, 2)
+
+ RowLayout {
+ id: content
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: Appearance.padding.normal
+
+ spacing: Appearance.spacing.normal
+
+ Item {
+ Layout.alignment: Qt.AlignLeft | Qt.AlignTop
+ implicitWidth: Config.notifs.sizes.image
+ implicitHeight: Config.notifs.sizes.image
+
+ Component {
+ id: imageComp
+
+ Image {
+ source: Qt.resolvedUrl(root.image)
+ fillMode: Image.PreserveAspectCrop
+ cache: false
+ asynchronous: true
+ width: Config.notifs.sizes.image
+ height: Config.notifs.sizes.image
+ }
+ }
+
+ Component {
+ id: appIconComp
+
+ ColouredIcon {
+ implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
+ source: Quickshell.iconPath(root.appIcon)
+ colour: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
+ layer.enabled: root.appIcon.endsWith("symbolic")
+ }
+ }
+
+ Component {
+ id: materialIconComp
+
+ MaterialIcon {
+ text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
+ color: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
+ font.pointSize: Appearance.font.size.large
+ }
+ }
+
+ ClippingRectangle {
+ anchors.fill: parent
+ color: root.urgency === "critical" ? Colours.palette.m3error : root.urgency === "low" ? Colours.layer(Colours.palette.m3surfaceContainerHigh, 3) : Colours.palette.m3secondaryContainer
+ radius: Appearance.rounding.full
+
+ Loader {
+ anchors.centerIn: parent
+ asynchronous: true
+ sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
+ }
+ }
+
+ Loader {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ asynchronous: true
+ active: root.appIcon && root.image
+
+ sourceComponent: StyledRect {
+ implicitWidth: Config.notifs.sizes.badge
+ implicitHeight: Config.notifs.sizes.badge
+
+ color: root.urgency === "critical" ? Colours.palette.m3error : root.urgency === "low" ? Colours.palette.m3surfaceContainerHigh : Colours.palette.m3secondaryContainer
+ radius: Appearance.rounding.full
+
+ ColouredIcon {
+ anchors.centerIn: parent
+ implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
+ source: Quickshell.iconPath(root.appIcon)
+ colour: root.urgency === "critical" ? Colours.palette.m3onError : root.urgency === "low" ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
+ layer.enabled: root.appIcon.endsWith("symbolic")
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ Layout.topMargin: -Appearance.padding.small
+ Layout.bottomMargin: -Appearance.padding.small / 2
+ Layout.fillWidth: true
+ spacing: Math.round(Appearance.spacing.small / 2)
+
+ RowLayout {
+ Layout.bottomMargin: -parent.spacing
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.smaller
+
+ StyledText {
+ Layout.fillWidth: true
+ text: root.modelData
+ color: Colours.palette.m3onSurfaceVariant
+ font.pointSize: Appearance.font.size.small
+ elide: Text.ElideRight
+ }
+
+ StyledText {
+ animate: true
+ text: root.notifs[0]?.timeStr ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledRect {
+ implicitWidth: expandBtn.implicitWidth + Appearance.padding.smaller * 2
+ implicitHeight: groupCount.implicitHeight + Appearance.padding.small
+
+ color: root.urgency === "critical" ? Colours.palette.m3error : Colours.layer(Colours.palette.m3surfaceContainerHigh, 3)
+ radius: Appearance.rounding.full
+
+ StateLayer {
+ color: root.urgency === "critical" ? Colours.palette.m3onError : Colours.palette.m3onSurface
+
+ function onClicked(): void {
+ if (root.expanded)
+ root.props.expandedNotifs.splice(root.props.expandedNotifs.indexOf(root.modelData), 1);
+ else
+ root.props.expandedNotifs.push(root.modelData);
+ }
+ }
+
+ RowLayout {
+ id: expandBtn
+
+ anchors.centerIn: parent
+ spacing: Appearance.spacing.small / 2
+
+ StyledText {
+ id: groupCount
+
+ Layout.leftMargin: Appearance.padding.small / 2
+ animate: true
+ text: root.notifs.length
+ color: root.urgency === "critical" ? Colours.palette.m3onError : Colours.palette.m3onSurface
+ font.pointSize: Appearance.font.size.small
+ }
+
+ MaterialIcon {
+ Layout.rightMargin: -Appearance.padding.small / 2
+ animate: true
+ text: root.expanded ? "expand_less" : "expand_more"
+ color: root.urgency === "critical" ? Colours.palette.m3onError : Colours.palette.m3onSurface
+ }
+ }
+ }
+ }
+
+ Repeater {
+ id: notifList
+
+ model: ScriptModel {
+ values: root.expanded ? root.notifs : root.notifs.slice(0, Config.notifs.groupPreviewNum)
+ }
+
+ Layout.fillWidth: true
+
+ Notif {
+ id: notif
+
+ props: root.props
+ expanded: root.expanded
+ }
+ }
+ }
+ }
+
+ // Behavior on implicitHeight {
+ // Anim {
+ // duration: Appearance.anim.durations.expressiveDefaultSpatial
+ // easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ // }
+ // }
+}