1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-30 17:42:06 -04:00

system updater: complete overhaul

Move system update flow to GO, with a CLI (convenient AIO tool) and
server integration. All lifecycle, scheduling, execution occurs on
backend side.

Run some backends via pkexec, some via terminal like paru/yay.

Incorporate flatpak as an option to update.

Add terminal override setting in GUI, in addition to $TERMINAL env
variable.

fixes #2307
fixes #822
fixes #1102
fixes #1812
fixes #1087
fixes #1743
This commit is contained in:
bbedward
2026-04-29 12:33:57 -04:00
parent a4cfdf4a59
commit 7bd9574868
43 changed files with 3556 additions and 710 deletions

View File

@@ -189,10 +189,10 @@ Item {
StyledText {
text: {
if (!SystemUpdateService.shellVersion && !DMSService.cliVersion)
if (!ShellVersionService.shellVersion && !DMSService.cliVersion)
return "dms";
let version = SystemUpdateService.shellVersion || "";
let version = ShellVersionService.shellVersion || "";
let cliVersion = DMSService.cliVersion || "";
// Debian/Ubuntu/OpenSUSE git format: 1.0.3+git2264.c5c5ce84
@@ -218,7 +218,7 @@ Item {
let baseVersion = extractBaseVersion(cliVersion);
if (!baseVersion)
baseVersion = extractBaseVersion(SystemUpdateService.semverVersion);
baseVersion = extractBaseVersion(ShellVersionService.semverVersion);
if (baseVersion) {
return `dms (git) v${baseVersion}-${match[1]}`;
}
@@ -253,8 +253,8 @@ Item {
}
StyledText {
visible: SystemUpdateService.shellCodename.length > 0
text: `"${SystemUpdateService.shellCodename}"`
visible: ShellVersionService.shellCodename.length > 0
text: `"${ShellVersionService.shellCodename}"`
font.pixelSize: Theme.fontSizeMedium
font.italic: true
color: Theme.surfaceVariantText

View File

@@ -325,6 +325,8 @@ Item {
placeholderText: I18n.tr("Enter launch prefix (e.g., 'uwsm-app')")
onTextEdited: SettingsData.set("launchPrefix", text)
}
TerminalPickerRow {}
}
SettingsCard {

View File

@@ -1,11 +1,53 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
Item {
id: root
readonly property var intervalOptions: [
{
label: I18n.tr("Every 15 minutes"),
seconds: 900
},
{
label: I18n.tr("Every 30 minutes"),
seconds: 1800
},
{
label: I18n.tr("Every hour"),
seconds: 3600
},
{
label: I18n.tr("Every 4 hours"),
seconds: 14400
},
{
label: I18n.tr("Once a day"),
seconds: 86400
}
]
function intervalLabelFor(seconds) {
for (const opt of intervalOptions) {
if (opt.seconds === seconds) {
return opt.label;
}
}
return intervalOptions[1].label;
}
function intervalSecondsFor(label) {
for (const opt of intervalOptions) {
if (opt.label === label) {
return opt.seconds;
}
}
return 1800;
}
DankFlickable {
anchors.fill: parent
clip: true
@@ -25,18 +67,60 @@ Item {
title: I18n.tr("System Updater")
settingKey: "systemUpdater"
SettingsToggleRow {
text: I18n.tr("Hide Updater Widget", "When updater widget is used, then hide it if no update found")
description: I18n.tr("When updater widget is used, then hide it if no update found")
checked: SettingsData.updaterHideWidget
onToggled: checked => {
SettingsData.set("updaterHideWidget", checked);
StyledText {
width: parent.width - Theme.spacingM * 2
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
visible: SystemUpdateService.backends.length > 0
text: {
const names = (SystemUpdateService.backends || []).map(b => b.displayName).join(", ");
return I18n.tr("Detected backends: %1").arg(names);
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
}
SettingsDropdownRow {
text: I18n.tr("Check interval")
description: I18n.tr("How often the server polls for new updates.")
options: root.intervalOptions.map(o => o.label)
currentValue: root.intervalLabelFor(SettingsData.updaterIntervalSeconds)
onValueChanged: label => {
const secs = root.intervalSecondsFor(label);
SettingsData.set("updaterIntervalSeconds", secs);
SystemUpdateService.setInterval(secs);
}
}
SettingsToggleRow {
text: I18n.tr("Use Custom Command")
description: I18n.tr("Use custom command for update your system")
text: I18n.tr("Include Flatpak updates")
description: I18n.tr("Apply Flatpak updates alongside system updates when running 'Update All'.")
visible: (SystemUpdateService.backends || []).some(b => b.repo === "flatpak")
checked: SettingsData.updaterIncludeFlatpak
onToggled: checked => SettingsData.set("updaterIncludeFlatpak", checked)
}
SettingsToggleRow {
text: I18n.tr("Include AUR updates")
description: I18n.tr("Run paru/yay with AUR enabled when 'Update All' is clicked.")
visible: (SystemUpdateService.backends || []).some(b => b.id === "paru" || b.id === "yay")
checked: SettingsData.updaterAllowAUR
onToggled: checked => SettingsData.set("updaterAllowAUR", checked)
}
TerminalPickerRow {}
}
SettingsCard {
width: parent.width
iconName: "tune"
title: I18n.tr("Advanced")
settingKey: "systemUpdaterAdvanced"
SettingsToggleRow {
text: I18n.tr("Use custom command")
description: I18n.tr("Open a terminal and run a custom command instead of the in-shell upgrade flow.")
checked: SettingsData.updaterUseCustomCommand
onToggled: checked => {
if (!checked) {
@@ -49,11 +133,32 @@ Item {
}
}
Rectangle {
width: parent.width - Theme.spacingM * 2
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
visible: SettingsData.updaterUseCustomCommand
height: warnText.implicitHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
StyledText {
id: warnText
anchors.fill: parent
anchors.margins: Theme.spacingS
text: I18n.tr("Custom command and terminal params are split on whitespace; paths with spaces will break.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.warning
wrapMode: Text.WordWrap
}
}
FocusScope {
width: parent.width - Theme.spacingM * 2
height: customCommandColumn.implicitHeight
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
visible: SettingsData.updaterUseCustomCommand
Column {
id: customCommandColumn
@@ -61,7 +166,7 @@ Item {
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("System update custom command")
text: I18n.tr("Custom update command")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
@@ -69,7 +174,7 @@ Item {
DankTextField {
id: updaterCustomCommand
width: parent.width
placeholderText: "myPkgMngr --sysupdate"
placeholderText: "topgrade --no-retry"
backgroundColor: Theme.surfaceContainerHighest
normalBorderColor: Theme.outlineMedium
focusedBorderColor: Theme.primary
@@ -98,6 +203,7 @@ Item {
height: terminalParamsColumn.implicitHeight
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
visible: SettingsData.updaterUseCustomCommand
Column {
id: terminalParamsColumn
@@ -105,7 +211,7 @@ Item {
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Terminal custom additional parameters")
text: I18n.tr("Terminal additional parameters")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
@@ -113,7 +219,7 @@ Item {
DankTextField {
id: updaterTerminalCustomClass
width: parent.width
placeholderText: "-T udpClass"
placeholderText: "-T updater"
backgroundColor: Theme.surfaceContainerHighest
normalBorderColor: Theme.outlineMedium
focusedBorderColor: Theme.primary

View File

@@ -0,0 +1,31 @@
import QtQuick
import qs.Common
SettingsDropdownRow {
id: root
readonly property string autoLabel: I18n.tr("Auto")
text: I18n.tr("Terminal")
settingKey: "terminalOverride"
options: {
const opts = [autoLabel];
const installed = SessionData.installedTerminals || [];
const list = installed.length > 0 ? installed : SessionData.terminalOptions;
for (const t of list) {
opts.push(t);
}
if (SessionData.terminalOverride && !opts.includes(SessionData.terminalOverride)) {
opts.push(SessionData.terminalOverride);
}
return opts;
}
currentValue: SessionData.terminalOverride.length > 0 ? SessionData.terminalOverride : autoLabel
onValueChanged: label => {
const next = label === autoLabel ? "" : label;
SessionData.set("terminalOverride", next);
}
}

View File

@@ -246,7 +246,8 @@ Item {
"text": I18n.tr("System Update"),
"description": I18n.tr("Check for system updates"),
"icon": "update",
"enabled": SystemUpdateService.distributionSupported
"enabled": SystemUpdateService.sysupdateAvailable,
"warning": SystemUpdateService.sysupdateAvailable ? undefined : I18n.tr("Requires DMS server with sysupdate capability")
},
{
"id": "powerMenuButton",
@@ -430,7 +431,7 @@ Item {
"id": widget.id,
"enabled": widget.enabled
};
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion"];
var keys = ["size", "selectedGpuIndex", "pciId", "mountPath", "diskUsageMode", "minimumWidth", "showSwap", "showInGb", "mediaSize", "clockCompactMode", "focusedWindowCompactMode", "runningAppsCompactMode", "keyboardLayoutNameCompactMode", "runningAppsGroupByApp", "runningAppsCurrentWorkspace", "runningAppsCurrentMonitor", "showNetworkIcon", "showBluetoothIcon", "showAudioIcon", "showAudioPercent", "showVpnIcon", "showBrightnessIcon", "showBrightnessPercent", "showMicIcon", "showMicPercent", "showBatteryIcon", "showPrinterIcon", "showScreenSharingIcon", "controlCenterGroupOrder", "barMaxVisibleApps", "barMaxVisibleRunningApps", "barShowOverflowBadge", "trayUseInlineExpansion", "hideWhenIdle"];
for (var i = 0; i < keys.length; i++) {
if (widget[keys[i]] !== undefined)
result[keys[i]] = widget[keys[i]];
@@ -579,6 +580,17 @@ Item {
setWidgetsForSection(sectionId, widgets);
}
function handleHideWhenIdleChanged(sectionId, widgetIndex, enabled) {
var widgets = getWidgetsForSection(sectionId).slice();
if (widgetIndex < 0 || widgetIndex >= widgets.length) {
return;
}
var newWidget = cloneWidgetData(widgets[widgetIndex]);
newWidget.hideWhenIdle = enabled;
widgets[widgetIndex] = newWidget;
setWidgetsForSection(sectionId, widgets);
}
function handleDiskUsageModeChanged(sectionId, widgetIndex, mode) {
var widgets = getWidgetsForSection(sectionId).slice();
if (widgetIndex < 0 || widgetIndex >= widgets.length) {
@@ -714,6 +726,8 @@ Item {
item.barShowOverflowBadge = widget.barShowOverflowBadge;
if (widget.trayUseInlineExpansion !== undefined)
item.trayUseInlineExpansion = widget.trayUseInlineExpansion;
if (widget.hideWhenIdle !== undefined)
item.hideWhenIdle = widget.hideWhenIdle;
}
widgets.push(item);
});
@@ -1003,6 +1017,9 @@ Item {
onOverflowSettingChanged: (sectionId, widgetIndex, settingName, value) => {
widgetsTab.handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value);
}
onHideWhenIdleChanged: (sectionId, widgetIndex, enabled) => {
widgetsTab.handleHideWhenIdleChanged(sectionId, widgetIndex, enabled);
}
}
}
@@ -1070,6 +1087,9 @@ Item {
onOverflowSettingChanged: (sectionId, widgetIndex, settingName, value) => {
widgetsTab.handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value);
}
onHideWhenIdleChanged: (sectionId, widgetIndex, enabled) => {
widgetsTab.handleHideWhenIdleChanged(sectionId, widgetIndex, enabled);
}
}
}
@@ -1137,6 +1157,9 @@ Item {
onOverflowSettingChanged: (sectionId, widgetIndex, settingName, value) => {
widgetsTab.handleOverflowSettingChanged(sectionId, widgetIndex, settingName, value);
}
onHideWhenIdleChanged: (sectionId, widgetIndex, enabled) => {
widgetsTab.handleHideWhenIdleChanged(sectionId, widgetIndex, enabled);
}
}
}
}

View File

@@ -33,6 +33,7 @@ Column {
signal showInGbChanged(string sectionId, int widgetIndex, bool enabled)
signal diskUsageModeChanged(string sectionId, int widgetIndex, int mode)
signal overflowSettingChanged(string sectionId, int widgetIndex, string settingName, var value)
signal hideWhenIdleChanged(string sectionId, int widgetIndex, bool enabled)
function cloneWidgetData(widget) {
var result = {
@@ -335,6 +336,25 @@ Column {
}
}
DankActionButton {
id: hideWhenIdleButton
buttonSize: 28
visible: modelData.id === "systemUpdate"
iconName: "visibility_off"
iconSize: 16
iconColor: (modelData.hideWhenIdle === true) ? Theme.primary : Theme.outline
onClicked: {
root.hideWhenIdleChanged(root.sectionId, index, modelData.hideWhenIdle !== true);
}
onEntered: {
const tooltipText = modelData.hideWhenIdle === true ? "Hide when no updates: ON" : "Hide when no updates: OFF";
sharedTooltip.show(tooltipText, hideWhenIdleButton, 0, 0, "bottom");
}
onExited: {
sharedTooltip.hide();
}
}
DankActionButton {
id: memMenuButton
visible: modelData.id === "memUsage"