mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-26 13:07:49 -04:00
feat(launcher): add flatpak uninstall option in context menu (#3340)
* feat(launcher): add flatpak uninstall option to context menu * fix(launcher): reuse existing catalog terms for uninstall toasts
This commit is contained in:
1 parent
cf74c10561
commit
3adfa918c1
5 files changed
+168
-6
No files matched your search
@@ -381,6 +381,39 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: uninstallAppConfirmLoader
|
||||
active: false
|
||||
readonly property ConfirmModal loadedModal: item as ConfirmModal
|
||||
|
||||
ConfirmModal {
|
||||
id: uninstallAppConfirm
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AppSearchService
|
||||
function onUninstallAppConfirmRequested(appId, appName, flatpakId) {
|
||||
uninstallAppConfirmLoader.active = true;
|
||||
const showDialog = () => {
|
||||
if (uninstallAppConfirmLoader.loadedModal) {
|
||||
uninstallAppConfirmLoader.loadedModal.showWithOptions({
|
||||
title: I18n.tr("Uninstall %1?", "modal title for app uninstallation").arg(appName),
|
||||
message: I18n.tr("%1 will be removed from your system.", "modal confirmation text for uninstalling an app").arg(appName),
|
||||
confirmText: I18n.tr("Uninstall", "confirm button label to proceed with uninstallation"),
|
||||
cancelText: I18n.tr("Cancel"),
|
||||
confirmColor: Theme.error,
|
||||
onConfirm: () => AppSearchService.uninstallFlatpak(appId, appName, flatpakId)
|
||||
});
|
||||
}
|
||||
};
|
||||
if (uninstallAppConfirmLoader.loadedModal)
|
||||
showDialog();
|
||||
else
|
||||
Qt.callLater(showDialog);
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: notificationCenterLoader
|
||||
|
||||
|
||||
@@ -134,6 +134,37 @@ function classifyAppSource(app) {
|
||||
return "system";
|
||||
}
|
||||
|
||||
function extractFlatpakAppId(app) {
|
||||
if (!app)
|
||||
return "";
|
||||
|
||||
var execRaw = app.execString || app.exec || "";
|
||||
if (app.command && app.command.length > 0) {
|
||||
var isRun = false;
|
||||
for (var i = 0; i < app.command.length; i++) {
|
||||
var arg = String(app.command[i]);
|
||||
if (arg === "run") {
|
||||
isRun = true;
|
||||
continue;
|
||||
}
|
||||
if (isRun && !arg.startsWith("-")) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (execRaw) {
|
||||
var match = execRaw.match(/\bflatpak\s+run\s+(?:--?[^\s]+\s+)*([a-zA-Z0-9_\-\.]+)/);
|
||||
if (match && match[1])
|
||||
return match[1];
|
||||
}
|
||||
|
||||
var id = app.id || "";
|
||||
if (id.endsWith(".desktop"))
|
||||
return id.slice(0, -8);
|
||||
return id;
|
||||
}
|
||||
|
||||
function sortPluginIdsByOrder(pluginIds, order) {
|
||||
if (!order || order.length === 0)
|
||||
return pluginIds;
|
||||
|
||||
@@ -28,6 +28,7 @@ function transformApp(app, override, defaultActions, primaryActionLabel) {
|
||||
keywords: app.keywords || [],
|
||||
actions: actions,
|
||||
source: Utils.classifyAppSource(app),
|
||||
flatpakId: Utils.classifyAppSource(app) === "flatpak" ? Utils.extractFlatpakAppId(app) : "",
|
||||
primaryAction: {
|
||||
name: primaryActionLabel,
|
||||
icon: "open_in_new",
|
||||
|
||||
@@ -6,6 +6,7 @@ import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "ControllerUtils.js" as Utils
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -99,6 +100,8 @@ Item {
|
||||
}
|
||||
readonly property bool isPinned: appId ? SessionData.isPinnedApp(appId) : false
|
||||
readonly property bool isRegularApp: item?.type === "app" && !item.isCore && desktopEntry
|
||||
readonly property bool isFlatpakApp: isRegularApp && (item?.source === "flatpak" || !!item?.flatpakId || Utils.classifyAppSource(desktopEntry) === "flatpak")
|
||||
readonly property string flatpakAppId: item?.flatpakId || (isFlatpakApp ? Utils.extractFlatpakAppId(desktopEntry) : "")
|
||||
readonly property bool isPluginItem: item?.type === "plugin"
|
||||
|
||||
function getPluginContextMenuActions() {
|
||||
@@ -231,6 +234,19 @@ Item {
|
||||
action: launchApp
|
||||
});
|
||||
|
||||
if (isFlatpakApp && flatpakAppId) {
|
||||
items.push({
|
||||
type: "separator"
|
||||
});
|
||||
items.push({
|
||||
type: "item",
|
||||
icon: "delete",
|
||||
text: I18n.tr("Uninstall…", "context menu action to uninstall app"),
|
||||
isDestructive: true,
|
||||
action: uninstallCurrentApp
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -307,6 +323,18 @@ Item {
|
||||
hide();
|
||||
}
|
||||
|
||||
function uninstallCurrentApp() {
|
||||
if (!isFlatpakApp || !flatpakAppId)
|
||||
return;
|
||||
const targetFlatpakId = flatpakAppId;
|
||||
const appName = item?.name || desktopEntry?.name || flatpakAppId;
|
||||
const currentAppId = appId;
|
||||
hide();
|
||||
if (controller)
|
||||
controller.itemExecuted();
|
||||
AppSearchService.requestUninstallFlatpak(currentAppId, appName, targetFlatpakId);
|
||||
}
|
||||
|
||||
function launchApp() {
|
||||
if (isCoreApp) {
|
||||
if (!coreAppData)
|
||||
@@ -619,10 +647,14 @@ Item {
|
||||
height: parent.height
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
const isDestructive = menuItemDelegate.modelData?.isDestructive ?? false;
|
||||
if (root.keyboardNavigation && root.selectedMenuIndex === menuItemDelegate.itemIndex) {
|
||||
return Theme.primaryPressed;
|
||||
return isDestructive ? Theme.withAlpha(Theme.error, 0.25) : Theme.primaryPressed;
|
||||
}
|
||||
return itemMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : Theme.withAlpha(BlurService.hoverColor(Theme.widgetBaseHoverColor), 0);
|
||||
if (itemMouseArea.containsMouse) {
|
||||
return isDestructive ? Theme.withAlpha(Theme.error, 0.15) : BlurService.hoverColor(Theme.widgetBaseHoverColor);
|
||||
}
|
||||
return Theme.withAlpha(BlurService.hoverColor(Theme.widgetBaseHoverColor), 0);
|
||||
}
|
||||
|
||||
Row {
|
||||
@@ -642,8 +674,8 @@ Item {
|
||||
visible: (menuItemDelegate.modelData?.icon ?? "").length > 0
|
||||
name: menuItemDelegate.modelData?.icon ?? ""
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
color: (menuItemDelegate.modelData?.isDestructive ?? false) ? Theme.error : Theme.surfaceText
|
||||
opacity: (menuItemDelegate.modelData?.isDestructive ?? false) ? 1.0 : 0.7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
@@ -651,7 +683,7 @@ Item {
|
||||
StyledText {
|
||||
text: menuItemDelegate.modelData.text || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
color: (menuItemDelegate.modelData?.isDestructive ?? false) ? Theme.error : Theme.surfaceText
|
||||
font.weight: Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
@@ -661,7 +693,7 @@ Item {
|
||||
|
||||
DankRipple {
|
||||
id: menuItemRipple
|
||||
rippleColor: Theme.surfaceText
|
||||
rippleColor: (menuItemDelegate.modelData?.isDestructive ?? false) ? Theme.error : Theme.surfaceText
|
||||
cornerRadius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
@@ -1004,4 +1005,68 @@ Singleton {
|
||||
log.warn("Error setting category on plugin", pluginId, ":", e);
|
||||
}
|
||||
}
|
||||
|
||||
signal uninstallAppConfirmRequested(string appId, string appName, string flatpakId)
|
||||
|
||||
property string _uninstallingAppId: ""
|
||||
property string _uninstallingAppName: ""
|
||||
property string _uninstallingStderr: ""
|
||||
|
||||
Process {
|
||||
id: flatpakUninstallProc
|
||||
running: false
|
||||
command: []
|
||||
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root._uninstallingStderr = (text || "").trim();
|
||||
}
|
||||
}
|
||||
|
||||
onExited: exitCode => {
|
||||
const appName = root._uninstallingAppName;
|
||||
const appId = root._uninstallingAppId;
|
||||
const err = root._uninstallingStderr;
|
||||
root._uninstallingAppName = "";
|
||||
root._uninstallingAppId = "";
|
||||
root._uninstallingStderr = "";
|
||||
|
||||
if (exitCode === 0) {
|
||||
ToastService.showInfo(I18n.tr("Uninstalled: %1", "uninstallation success").arg(appName));
|
||||
if (appId) {
|
||||
SessionData.removePinnedApp(appId);
|
||||
SessionData.removeBarPinnedApp(appId);
|
||||
}
|
||||
root.refreshApplications();
|
||||
} else {
|
||||
ToastService.showError(
|
||||
I18n.tr("Uninstall failed: %1", "uninstallation error").arg(appName),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requestUninstallFlatpak(appId, appName, flatpakId) {
|
||||
if (!flatpakId)
|
||||
return;
|
||||
uninstallAppConfirmRequested(appId, appName, flatpakId);
|
||||
}
|
||||
|
||||
function uninstallFlatpak(appId, appName, flatpakId) {
|
||||
if (!flatpakId)
|
||||
return;
|
||||
if (flatpakUninstallProc.running) {
|
||||
ToastService.showWarning(I18n.tr("An uninstallation is already in progress", "toast warning message"));
|
||||
return;
|
||||
}
|
||||
|
||||
_uninstallingAppId = appId || "";
|
||||
_uninstallingAppName = appName || flatpakId;
|
||||
_uninstallingStderr = "";
|
||||
flatpakUninstallProc.command = ["flatpak", "uninstall", "-y", "--app", flatpakId];
|
||||
flatpakUninstallProc.running = true;
|
||||
|
||||
ToastService.showInfo(I18n.tr("Uninstalling: %1", "uninstallation progress").arg(_uninstallingAppName));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user