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

This commit is contained in:
2026-11-02 19:00:43 +01:00
parent 789305286a
commit 11e7aac195
56 changed files with 5003 additions and 2168 deletions
+136 -86
View File
@@ -3,12 +3,14 @@ pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import QtQuick
import ZShell.Config
import ZShell.Components
import ZShell.Config
import ZShell.Services
import qs.Components
import qs.Components.Notifs
import qs.Daemons
import qs.Helpers
import qs.Services
Item {
id: root
@@ -21,13 +23,10 @@ Item {
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: {
const count = list.count;
if (count === 0)
if (list.count === 0)
return 0;
let height = (count - 1) * Tokens.spacing.small;
for (let i = 0; i < count; i++)
height += (list.itemAtIndex(i) as NotifWrapper)?.nonAnimHeight ?? 0;
let height = list.layoutHeight;
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x)
return 0;
@@ -54,36 +53,55 @@ Item {
anchors.fill: parent
anchors.margins: root.padding
clip: true
// radius: Tokens.rounding.large - root.padding
CustomListView {
id: list
KineticFlickable {
id: view
anchors.fill: parent
cacheBuffer: (QsWindow.window as QsWindow)?.screen.height ?? 0
orientation: Qt.Vertical
spacing: 0
delegate: NotifWrapper {}
displaced: Transition {
Anim {
property: "y"
}
}
model: ScriptModel {
values: {
const popups = NotifServer.popups.filter(n => !n.closed);
contentHeight: list.implicitHeight
contentWidth: width
flickableDirection: Flickable.VerticalFlick
if (!Config.notifs.showInFullscreen) {
if (Hypr.monitorFor(root.panels.screen).activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false)
return [];
LazyListView {
id: list
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: contentHeight
spacing: Tokens.spacing.medium
readyDelay: 1
cacheBuffer: (QsWindow.window as QsWindow)?.screen.height ?? 0
removeDuration: Tokens.anim.durations.normal
useCustomViewport: true
viewport: Qt.rect(0, view.contentY, width, view.height)
model: ScriptModel {
values: {
if (!Config.notifs.showInFullscreen) {
if (Hypr.monitorFor(root.panels.screen).activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false)
return [];
}
return [...NotifServer.popupGroups];
}
return popups;
}
}
move: Transition {
delegate: NotifWrapper {}
onViewportAdjustNeeded: d => {
if (contentYAnim.running)
contentYAnim.complete();
contentYAnim.to = Math.max(0, view.contentY + d);
contentYAnim.start();
}
Anim {
property: "y"
id: contentYAnim
target: view
property: "contentY"
}
}
}
@@ -96,74 +114,106 @@ Item {
component NotifWrapper: Item {
id: wrapper
property int idx
required property int index
required property NotificationRow modelData
readonly property alias nonAnimHeight: notif.nonAnimHeight
required property NotificationGroup modelData
property bool expanded: Config.notifs.openExpanded
readonly property bool holding: area.containsMouse || area.pressed || card.rowPressed
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : Tokens.spacing.small)
implicitWidth: notif.implicitWidth
ListView.onRemove: removeAnim.start()
onIndexChanged: {
if (index !== -1)
idx = index;
function forEachPopup(fn: var): void {
for (const n of [...card.notifs])
fn(n);
}
SequentialAnimation {
id: removeAnim
PropertyAction {
property: "ListView.delayRemove"
target: wrapper
value: true
}
PropertyAction {
property: "enabled"
target: wrapper
value: false
}
PropertyAction {
property: "implicitHeight"
target: wrapper
value: 0
}
PropertyAction {
property: "z"
target: wrapper
value: 1
}
Anim {
property: "x"
target: notif
to: (notif.x >= 0 ? Config.notifs.sizes.width : -Config.notifs.sizes.width) * 2
}
PropertyAction {
property: "ListView.delayRemove"
target: wrapper
value: false
}
function holdPopups(): void {
forEachPopup(n => n.holdPopup());
}
CustomRect {
LazyListView.trackViewport: !expanded && card.nonAnimHeight < card.implicitHeight
LazyListView.preferredHeight: LazyListView.removing ? 0 : card.nonAnimHeight
LazyListView.visibleHeight: LazyListView.removing ? 0 : card.implicitHeight
LazyListView.onRemovingChanged: {
if (!wrapper.LazyListView.removing)
return;
slideOut.to = (area.x >= 0 ? root.implicitWidth : -root.implicitWidth) * 2;
slideOut.start();
}
implicitWidth: area.implicitWidth
implicitHeight: card.implicitHeight
enabled: !LazyListView.removing
z: LazyListView.removing ? 1 : 0
onHoldingChanged: {
if (holding)
holdPopups();
else
forEachPopup(n => n.releasePopup());
}
Connections {
function onNotifsChanged(): void {
if (wrapper.holding)
wrapper.holdPopups();
}
target: wrapper.modelData
}
Anim {
id: slideOut
target: area
property: "x"
duration: Tokens.anim.durations.normal
easing: Tokens.anim.emphasized
}
Item {
anchors.top: parent.top
anchors.topMargin: wrapper.idx === 0 ? 0 : 8
color: "transparent"
implicitHeight: notif.implicitHeight
implicitWidth: notif.implicitWidth
radius: Tokens.rounding.small
Notification {
id: notif
implicitWidth: area.implicitWidth
implicitHeight: card.implicitHeight
clip: true
NotifSwipeArea {
id: area
implicitWidth: root.implicitWidth - root.padding * 2
modelData: wrapper.modelData
implicitHeight: card.implicitHeight
x: wrapper.LazyListView.adding ? implicitWidth : 0
expanded: wrapper.expanded
onRequestExpand: expand => wrapper.expanded = expand
onSwiped: wrapper.forEachPopup(n => n.dismissPopup())
onDismissed: wrapper.forEachPopup(n => n.dismiss())
onClicked: event => {
if (!Config.notifs.actionOnClick || event.button !== Qt.LeftButton || card.notifs.length !== 1)
return;
const actions = card.notifs[0].actions;
if (actions.length === 1)
actions[0].invoke();
}
NotifGroupCard {
id: card
group: wrapper.modelData
container: view
expanded: wrapper.expanded
onRequestExpand: expand => wrapper.expanded = expand
onSwiped: notif => notif.dismissPopup()
onLinkActivated: notif => notif.dismissPopup()
}
}
}
Behavior on y {
enabled: wrapper.LazyListView.ready
Anim {}
}
}
}