1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-13 00:42:49 -05:00

fix notification dismiss performance

This commit is contained in:
bbedward
2025-08-21 17:47:14 -04:00
parent 41a0ae7de5
commit 098a19acd1
4 changed files with 301 additions and 140 deletions

View File

@@ -95,6 +95,8 @@ Singleton {
property color error: currentThemeData.error || "#F2B8B5"
property color warning: currentThemeData.warning || "#FF9800"
property color info: currentThemeData.info || "#2196F3"
property color tempWarning: "#ff9933"
property color tempDanger: "#ff5555"
property color primaryHover: Qt.rgba(primary.r, primary.g, primary.b, 0.12)
property color primaryHoverLight: Qt.rgba(primary.r, primary.g, primary.b, 0.08)

View File

@@ -308,110 +308,26 @@ QtObject {
if (!group)
return
// Save current state for smart navigation
const currentGroupKey = group.key
const isNotification = currentItem.type === "notification"
const notificationIndex = currentItem.notificationIndex
const totalNotificationsInGroup = group.notifications ? group.notifications.length : 0
const isLastNotificationInGroup = isNotification
&& totalNotificationsInGroup === 1
const isLastNotificationInList = isNotification
&& notificationIndex === totalNotificationsInGroup - 1
// Store what to select next BEFORE clearing
let nextTargetType = ""
let nextTargetGroupKey = ""
let nextTargetNotificationIndex = -1
if (currentItem.type === "group") {
NotificationService.dismissGroup(group.key)
// Look for next group
for (var i = currentItem.groupIndex + 1; i < groups.length; i++) {
nextTargetType = "group"
nextTargetGroupKey = groups[i].key
break
}
if (!nextTargetGroupKey && currentItem.groupIndex > 0) {
nextTargetType = "group"
nextTargetGroupKey = groups[currentItem.groupIndex - 1].key
}
} else if (isNotification) {
const notification = group.notifications[notificationIndex]
} else if (currentItem.type === "notification") {
const notification = group.notifications[currentItem.notificationIndex]
NotificationService.dismissNotification(notification)
if (isLastNotificationInGroup) {
for (var i = currentItem.groupIndex + 1; i < groups.length; i++) {
nextTargetType = "group"
nextTargetGroupKey = groups[i].key
break
}
if (!nextTargetGroupKey && currentItem.groupIndex > 0) {
nextTargetType = "group"
nextTargetGroupKey = groups[currentItem.groupIndex - 1].key
}
} else if (isLastNotificationInList) {
// If group still has notifications after this one is removed, select the new last one
if (group.count > 1) {
nextTargetType = "notification"
nextTargetGroupKey = currentGroupKey
// After removing current notification, the new last index will be count-2
nextTargetNotificationIndex = group.count - 2
} else {
// Group will be empty or collapsed, select the group header
nextTargetType = "group"
nextTargetGroupKey = currentGroupKey
nextTargetNotificationIndex = -1
}
} else {
nextTargetType = "notification"
nextTargetGroupKey = currentGroupKey
nextTargetNotificationIndex = notificationIndex
}
}
rebuildFlatNavigation()
// Find and select the target we identified
if (flatNavigation.length === 0) {
selectedFlatIndex = 0
updateSelectedIdFromIndex()
} else if (nextTargetGroupKey) {
let found = false
for (var i = 0; i < flatNavigation.length; i++) {
const item = flatNavigation[i]
if (nextTargetType === "group" && item.type === "group"
&& item.groupKey === nextTargetGroupKey) {
selectedFlatIndex = i
found = true
break
} else if (nextTargetType === "notification"
&& item.type === "notification"
&& item.groupKey === nextTargetGroupKey
&& item.notificationIndex === nextTargetNotificationIndex) {
selectedFlatIndex = i
found = true
break
keyboardNavigationActive = false
if (listView) {
listView.keyboardActive = false
}
}
if (!found) {
selectedFlatIndex = Math.min(selectedFlatIndex,
flatNavigation.length - 1)
}
updateSelectedIdFromIndex()
} else {
selectedFlatIndex = Math.min(selectedFlatIndex,
flatNavigation.length - 1)
selectedFlatIndex = Math.min(selectedFlatIndex, flatNavigation.length - 1)
updateSelectedIdFromIndex()
}
ensureVisible()
}
}
function ensureVisible() {
if (flatNavigation.length === 0

View File

@@ -25,6 +25,17 @@ Singleton {
property int seqCounter: 0
property bool bulkDismissing: false
property var _dismissQueue: []
property int _dismissBatchSize: 8
property int _dismissTickMs: 8
property bool _suspendGrouping: false
property var _groupCache: ({"notifications": [], "popups": []})
property bool _groupsDirty: false
Component.onCompleted: {
_recomputeGroups()
}
Timer {
id: addGate
interval: enterAnimMs + 50
@@ -47,11 +58,41 @@ Singleton {
}
}
Timer {
id: dismissPump
interval: _dismissTickMs
repeat: true
running: false
onTriggered: {
let n = Math.min(_dismissBatchSize, _dismissQueue.length)
for (let i = 0; i < n; ++i) {
const w = _dismissQueue.pop()
try {
if (w && w.notification) w.notification.dismiss()
} catch (e) {}
}
if (_dismissQueue.length === 0) {
dismissPump.stop()
_suspendGrouping = false
bulkDismissing = false
popupsDisabled = false
_recomputeGroupsLater()
}
}
}
Timer {
id: groupsDebounce
interval: 16
repeat: false
onTriggered: _recomputeGroups()
}
property bool timeUpdateTick: false
property bool clockFormatChanged: false
readonly property var groupedNotifications: getGroupedNotifications()
readonly property var groupedPopups: getGroupedPopups()
readonly property var groupedNotifications: _groupCache.notifications
readonly property var groupedPopups: _groupCache.popups
property var expandedGroups: ({})
property var expandedMessages: ({})
@@ -89,6 +130,8 @@ Singleton {
processQueue()
}
}
_recomputeGroupsLater()
}
}
@@ -217,12 +260,8 @@ Singleton {
target: wrapper.notification.Retainable
function onDropped(): void {
const notifIndex = root.notifications.indexOf(wrapper)
const allIndex = root.allWrappers.indexOf(wrapper)
if (allIndex !== -1)
root.allWrappers.splice(allIndex, 1)
if (notifIndex !== -1)
root.notifications.splice(notifIndex, 1)
root.allWrappers = root.allWrappers.filter(w => w !== wrapper)
root.notifications = root.notifications.filter(w => w !== wrapper)
if (root.bulkDismissing)
return
@@ -236,6 +275,7 @@ Singleton {
}
cleanupExpansionStates()
root._recomputeGroupsLater()
}
function onAboutToDestroy(): void {
@@ -256,30 +296,20 @@ Singleton {
addGateBusy = false
notificationQueue = []
for (const w of visibleNotifications)
for (const w of allWrappers)
w.popup = false
visibleNotifications = []
const toDismiss = notifications.slice()
_dismissQueue = notifications.slice()
if (notifications.length)
notifications.splice(0, notifications.length)
notifications = []
expandedGroups = {}
expandedMessages = {}
for (var i = 0; i < toDismiss.length; ++i) {
const w = toDismiss[i]
if (w && w.notification) {
try {
w.notification.dismiss()
} catch (e) {
_suspendGrouping = true
}
}
}
bulkDismissing = false
popupsDisabled = false
if (!dismissPump.running && _dismissQueue.length)
dismissPump.start()
}
function dismissNotification(wrapper) {
@@ -293,10 +323,10 @@ Singleton {
popupsDisabled = disable
if (disable) {
notificationQueue = []
visibleNotifications = []
for (const notif of root.allWrappers) {
for (const notif of visibleNotifications) {
notif.popup = false
}
visibleNotifications = []
}
}
@@ -325,32 +355,20 @@ Singleton {
}
function removeFromVisibleNotifications(wrapper) {
const i = visibleNotifications.findIndex(n => n === wrapper)
if (i !== -1) {
const v = [...visibleNotifications]
v.splice(i, 1)
visibleNotifications = v
visibleNotifications = visibleNotifications.filter(n => n !== wrapper)
processQueue()
}
}
function releaseWrapper(w) {
let v = visibleNotifications.slice()
const vi = v.indexOf(w)
if (vi !== -1) {
v.splice(vi, 1)
visibleNotifications = v
}
let q = notificationQueue.slice()
const qi = q.indexOf(w)
if (qi !== -1) {
q.splice(qi, 1)
notificationQueue = q
}
visibleNotifications = visibleNotifications.filter(n => n !== w)
notificationQueue = notificationQueue.filter(n => n !== w)
if (w && w.destroy && !w.isPersistent) {
Qt.callLater(() => {
try {
w.destroy()
} catch (e) {}
})
}
}
@@ -362,7 +380,25 @@ Singleton {
return wrapper.appName.toLowerCase()
}
function getGroupedNotifications() {
function _recomputeGroups() {
if (_suspendGrouping) {
_groupsDirty = true
return
}
_groupCache = {
"notifications": _calcGroupedNotifications(),
"popups": _calcGroupedPopups()
}
_groupsDirty = false
}
function _recomputeGroupsLater() {
_groupsDirty = true
if (!groupsDebounce.running)
groupsDebounce.start()
}
function _calcGroupedNotifications() {
const groups = {}
for (const notif of notifications) {
@@ -400,7 +436,7 @@ Singleton {
})
}
function getGroupedPopups() {
function _calcGroupedPopups() {
const groups = {}
for (const notif of popups) {

207
spam-notifications.sh Executable file
View File

@@ -0,0 +1,207 @@
#!/bin/bash
# Notification Spam Test Script - Sends 100 rapid notifications from fake apps
echo "NOTIFICATION SPAM TEST - 100 RAPID NOTIFICATIONS"
echo "============================================================="
echo "WARNING: This will send 100 notifications very quickly!"
echo "Press Ctrl+C to cancel, or wait 3 seconds to continue..."
sleep 3
# Arrays of fake app names and icons
APPS=(
"slack:mail-message-new"
"discord:internet-chat"
"teams:call-start"
"zoom:camera-video"
"spotify:audio-x-generic"
"chrome:web-browser"
"firefox:web-browser"
"vscode:text-editor"
"terminal:utilities-terminal"
"steam:applications-games"
"telegram:internet-chat"
"whatsapp:phone"
"signal:security-high"
"thunderbird:mail-client"
"calendar:office-calendar"
"notes:text-editor"
"todo:emblem-default"
"weather:weather-few-clouds"
"news:rss"
"reddit:web-browser"
"twitter:internet-web-browser"
"instagram:camera-photo"
"youtube:video-x-generic"
"netflix:media-playback-start"
"github:folder-development"
"gitlab:folder-development"
"jira:applications-office"
"notion:text-editor"
"obsidian:accessories-text-editor"
"dropbox:folder-remote"
"gdrive:folder-google-drive"
"onedrive:folder-cloud"
"backup:drive-harddisk"
"antivirus:security-high"
"vpn:network-vpn"
"torrent:network-server"
"docker:application-x-executable"
"kubernetes:applications-system"
"postgres:database"
"mongodb:database"
"redis:database"
"nginx:network-server"
"apache:network-server"
"jenkins:applications-development"
"gradle:applications-development"
"maven:applications-development"
"npm:package-x-generic"
"yarn:package-x-generic"
"pip:package-x-generic"
"apt:system-software-install"
)
# Arrays of message types
TITLES=(
"New message"
"Update available"
"Download complete"
"Task finished"
"Build successful"
"Deployment complete"
"Sync complete"
"Backup finished"
"Security alert"
"New notification"
"Process complete"
"Upload finished"
"Connection established"
"Meeting starting"
"Reminder"
"Warning"
"Error occurred"
"Success"
"Failed"
"Pending"
"In progress"
"Scheduled"
"New activity"
"Status update"
"Alert"
"Information"
"Breaking news"
"Hot update"
"Trending"
"New release"
)
MESSAGES=(
"Your request has been processed successfully"
"New content is available for download"
"Operation completed without errors"
"Check your inbox for updates"
"3 new items require your attention"
"Background task finished executing"
"All systems operational"
"Performance metrics updated"
"Configuration saved successfully"
"Database connection established"
"Cache cleared and rebuilt"
"Service restarted automatically"
"Logs have been rotated"
"Memory usage optimized"
"Network latency improved"
"Security scan completed - no threats"
"Automatic backup created"
"Files synchronized across devices"
"Updates installed successfully"
"New features are now available"
"Your subscription has been renewed"
"Report generated and ready"
"Analysis complete - view results"
"Queue processed: 42 items"
"Rate limit will reset in 5 minutes"
"API call successful (200 OK)"
"Webhook delivered successfully"
"Container started on port 8080"
"Build artifact uploaded"
"Test suite passed: 100/100"
"Coverage report: 95%"
"Dependencies updated to latest"
"Migration completed successfully"
"Index rebuilt for faster queries"
"SSL certificate renewed"
"Firewall rules updated"
"DNS propagation complete"
"CDN cache purged globally"
"Load balancer health check: OK"
"Cluster scaled to 5 nodes"
)
# Urgency levels
URGENCY=("low" "normal")
# Counter
COUNT=0
TOTAL=100
echo ""
echo "Starting notification spam..."
echo "------------------------------"
# Send notifications rapidly
for i in $(seq 1 $TOTAL); do
# Pick random app, title, message, and urgency
APP=${APPS[$RANDOM % ${#APPS[@]}]}
APP_NAME=${APP%%:*}
APP_ICON=${APP#*:}
TITLE=${TITLES[$RANDOM % ${#TITLES[@]}]}
MESSAGE=${MESSAGES[$RANDOM % ${#MESSAGES[@]}]}
URG=${URGENCY[$RANDOM % ${#URGENCY[@]}]}
# Add some variety with random numbers and timestamps
RAND_NUM=$((RANDOM % 1000))
TIMESTAMP=$(date +"%H:%M:%S")
# Randomly add extra details to some messages
if [ $((RANDOM % 3)) -eq 0 ]; then
MESSAGE="[$TIMESTAMP] $MESSAGE (#$RAND_NUM)"
fi
# Send notification with very short delay
notify-send \
-h string:desktop-entry:$APP_NAME \
-i $APP_ICON \
-u $URG \
"$APP_NAME: $TITLE" \
"$MESSAGE" &
# Increment counter
COUNT=$((COUNT + 1))
# Show progress every 10 notifications
if [ $((COUNT % 10)) -eq 0 ]; then
echo " Sent $COUNT/$TOTAL notifications..."
fi
# Tiny delay to prevent complete system freeze
# Adjust this value: smaller = faster spam, larger = slower spam
sleep 0.01
done
# Wait for all background notifications to complete
wait
echo ""
echo "Spam test complete!"
echo "============================================================="
echo "Statistics:"
echo " Total notifications sent: $TOTAL"
echo " Apps simulated: ${#APPS[@]}"
echo " Message variations: ${#MESSAGES[@]}"
echo " Time taken: ~$(($TOTAL / 100)) seconds"
echo ""
echo "Check your notification center - it should be FULL!"
echo "Tip: You may want to clear all notifications after this test"
echo ""