Merge pull request #1927 from AlexNabokikh/1926

fix(1926): cache notification icons
This commit is contained in:
Lysec
2026-02-23 07:58:49 +01:00
committed by GitHub
3 changed files with 62 additions and 4 deletions
+9 -1
View File
@@ -547,7 +547,15 @@ Singleton {
// Image handling
function queueImage(path, appName, summary, notificationId) {
if (!path || !path.startsWith("image://") || !notificationId)
if (!path || !notificationId)
return;
// Cache image:// URIs and temporary file paths (e.g. /tmp/ from Chromium)
const filePath = path.startsWith("file://") ? path.substring(7) : path;
const isImageUri = path.startsWith("image://");
const isTempFile = (path.startsWith("/") || path.startsWith("file://")) && filePath.startsWith("/tmp/");
if (!isImageUri && !isTempFile)
return;
ImageCacheService.getNotificationIcon(path, appName, summary, function (cachedPath, success) {
+52 -2
View File
@@ -162,8 +162,11 @@ Singleton {
return;
}
// File paths are used directly, not cached
if (imageUri.startsWith("/") || imageUri.startsWith("file://")) {
// Resolve bare file path for temp check
const filePath = imageUri.startsWith("file://") ? imageUri.substring(7) : imageUri;
// File paths in persistent locations are used directly, not cached
if ((imageUri.startsWith("/") || imageUri.startsWith("file://")) && !isTemporaryPath(filePath)) {
callback(imageUri, false);
return;
}
@@ -171,12 +174,59 @@ Singleton {
const cacheKey = generateNotificationKey(imageUri, appName, summary);
const cachedPath = notificationsDir + cacheKey + ".png";
// Temporary file paths are copied to cache before the source is cleaned up
if (imageUri.startsWith("/") || imageUri.startsWith("file://")) {
processRequest(cacheKey, cachedPath, imageUri, callback, function () {
copyTempFileToCache(filePath, cachedPath, cacheKey);
});
return;
}
processRequest(cacheKey, cachedPath, imageUri, callback, function () {
// Notifications always use Qt fallback (image:// URIs can't be read by ImageMagick)
queueFallbackProcessing(imageUri, cachedPath, cacheKey, 64);
});
}
// Check if a path is in a temporary directory that may be cleaned up
function isTemporaryPath(path) {
return path.startsWith("/tmp/");
}
// Copy a temporary file to the cache directory
function copyTempFileToCache(sourcePath, destPath, cacheKey) {
const srcEsc = sourcePath.replace(/'/g, "'\\''");
const dstEsc = destPath.replace(/'/g, "'\\''");
const processString = `
import QtQuick
import Quickshell.Io
Process {
command: ["cp", "--", "${srcEsc}", "${dstEsc}"]
stdout: StdioCollector {}
stderr: StdioCollector {}
}
`;
queueUtilityProcess({
name: "CopyTempFile_" + cacheKey,
processString: processString,
onComplete: function (exitCode) {
if (exitCode === 0) {
Logger.d("ImageCache", "Temp file cached:", destPath);
notifyCallbacks(cacheKey, destPath, true);
} else {
Logger.w("ImageCache", "Failed to cache temp file:", sourcePath);
notifyCallbacks(cacheKey, "", false);
}
},
onError: function () {
Logger.e("ImageCache", "Error caching temp file:", sourcePath);
notifyCallbacks(cacheKey, "", false);
}
});
}
// -------------------------------------------------
// Public API: Get Circular Avatar (256x256)
// -------------------------------------------------
+1 -1
View File
@@ -15,7 +15,7 @@ Item {
property color borderColor: "transparent"
property int imageFillMode: Image.PreserveAspectCrop
readonly property bool showFallback: (fallbackIcon !== undefined && fallbackIcon !== "") && (imagePath === undefined || imagePath === "")
readonly property bool showFallback: (fallbackIcon !== undefined && fallbackIcon !== "") && (imagePath === undefined || imagePath === "" || imageSource.status === Image.Error)
readonly property int status: imageSource.status
Rectangle {