feat: better notification storage and image caching + blobs
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 11s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 54s
Python / verify (pull_request) Failing after 1m26s
Rust / fmt (pull_request) Successful in 46s
Rust / clippy (pull_request) Successful in 1m6s
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 11s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 54s
Python / verify (pull_request) Failing after 1m26s
Rust / fmt (pull_request) Successful in 46s
Rust / clippy (pull_request) Successful in 1m6s
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import Quickshell
|
||||
import ZShell
|
||||
import M3Shapes
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Services
|
||||
import Quickshell.Services.Notifications
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var owner
|
||||
required property string image
|
||||
required property string icon
|
||||
required property int urgency
|
||||
required property string summary
|
||||
required property color lowFillColour
|
||||
property real progress: 0
|
||||
property string shownImage
|
||||
property string shownIcon
|
||||
|
||||
readonly property color fillColour: urgency === NotificationUrgency.Critical ? Colors.palette.m3primary : urgency === NotificationUrgency.Low ? lowFillColour : Colors.palette.m3secondaryContainer
|
||||
readonly property color iconColour: urgency === NotificationUrgency.Critical ? Colors.palette.m3onPrimary : urgency === NotificationUrgency.Low ? Colors.palette.m3onSurfaceVariant : Colors.palette.m3onSecondaryContainer
|
||||
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
|
||||
// Keeping the previously shown url avoids replaying the fade when a row
|
||||
// swaps between its raw source and the cached blob.
|
||||
onImageChanged: {
|
||||
if (image && !owner.isSameImage(shownImage))
|
||||
shownImage = image;
|
||||
}
|
||||
onIconChanged: {
|
||||
if (icon && !owner.isSameAppIcon(shownIcon))
|
||||
shownIcon = icon;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
shownImage = image;
|
||||
shownIcon = icon;
|
||||
}
|
||||
|
||||
Component {
|
||||
id: imageComp
|
||||
|
||||
FadeImage {
|
||||
source: Qt.resolvedUrl(root.shownImage)
|
||||
width: Config.notifs.sizes.image
|
||||
height: Config.notifs.sizes.image
|
||||
fadeInLargeAnim: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appIconComp
|
||||
|
||||
FadeIcon {
|
||||
size: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: materialIconComp
|
||||
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
text: Icons.getNotifIcon(root.summary, root.urgency)
|
||||
color: root.iconColour
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
}
|
||||
|
||||
MaterialShape {
|
||||
id: bgShape
|
||||
|
||||
anchors.fill: parent
|
||||
shape: contentLoader.sourceComponent === materialIconComp && root.urgency === NotificationUrgency.Critical ? MaterialShape.SoftBurst : MaterialShape.Circle
|
||||
color: Qt.alpha(root.fillColour, 1)
|
||||
opacity: root.fillColour.a
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.SlowEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Loader {
|
||||
id: contentLoader
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: sourceComponent === materialIconComp ? 1 : 0
|
||||
asynchronous: true
|
||||
sourceComponent: root.image ? imageComp : root.icon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
asynchronous: true
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
active: opacity > 0
|
||||
opacity: root.icon && root.image ? 1 : 0
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
|
||||
color: root.fillColour
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
FadeIcon {
|
||||
anchors.centerIn: parent
|
||||
size: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: root.icon && root.image ? Anim.DefaultEffects : Anim.FastEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
asynchronous: true
|
||||
anchors.fill: parent
|
||||
active: root.progress > 0
|
||||
|
||||
sourceComponent: Shape {
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
id: progressShape
|
||||
|
||||
property real sweep: root.progress * 360
|
||||
property real spacing: root.urgency === NotificationUrgency.Critical ? Tokens.spacing.extraSmall : 0
|
||||
|
||||
capStyle: ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
strokeWidth: 2
|
||||
strokeColor: Colors.palette.m3primary
|
||||
|
||||
PathPolyline {
|
||||
function pointAt(angle: real): point {
|
||||
const dist = bgShape.distanceAtAngle(angle) + progressShape.strokeWidth / 2 + progressShape.spacing;
|
||||
const rad = angle * Math.PI / 180;
|
||||
return Qt.point(root.width / 2 + dist * Math.sin(rad), root.height / 2 - dist * Math.cos(rad));
|
||||
}
|
||||
|
||||
path: {
|
||||
bgShape.morphProgress; // mapToItem-free dependency: re-run when the shape morphs
|
||||
const points = [];
|
||||
for (let angle = 0; angle < progressShape.sweep; angle += 3)
|
||||
points.push(pointAt(angle));
|
||||
points.push(pointAt(progressShape.sweep));
|
||||
return points;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on sweep {
|
||||
Anim {
|
||||
easing: Tokens.anim.emphasizedDecel
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on spacing {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component FadeIcon: FadeImage {
|
||||
id: fadeIcon
|
||||
|
||||
required property int size
|
||||
|
||||
width: size
|
||||
height: size
|
||||
source: Quickshell.iconPath(root.shownIcon)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
fadeInLargeAnim: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user