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:
441
quickshell/Modules/DankBar/Popouts/SystemUpdatePopout.qml
Normal file
441
quickshell/Modules/DankBar/Popouts/SystemUpdatePopout.qml
Normal file
@@ -0,0 +1,441 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankPopout {
|
||||
id: systemUpdatePopout
|
||||
|
||||
layerNamespace: "dms:system-update"
|
||||
|
||||
property var parentWidget: null
|
||||
property var triggerScreen: null
|
||||
|
||||
Ref {
|
||||
service: SystemUpdateService
|
||||
}
|
||||
|
||||
property bool _reopenAfterUpgrade: false
|
||||
|
||||
Connections {
|
||||
target: SystemUpdateService
|
||||
function onIsUpgradingChanged() {
|
||||
if (SystemUpdateService.isUpgrading) {
|
||||
return;
|
||||
}
|
||||
if (!systemUpdatePopout._reopenAfterUpgrade) {
|
||||
return;
|
||||
}
|
||||
systemUpdatePopout._reopenAfterUpgrade = false;
|
||||
systemUpdatePopout.open();
|
||||
}
|
||||
}
|
||||
|
||||
popupWidth: 440
|
||||
popupHeight: 560
|
||||
triggerWidth: 55
|
||||
positioning: ""
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: false
|
||||
|
||||
onBackgroundClicked: close()
|
||||
|
||||
onShouldBeVisibleChanged: {
|
||||
if (!shouldBeVisible) {
|
||||
return;
|
||||
}
|
||||
const stale = !SystemUpdateService.lastCheckUnix || (Date.now() / 1000 - SystemUpdateService.lastCheckUnix) > 300;
|
||||
if (stale && !SystemUpdateService.isChecking && !SystemUpdateService.isUpgrading) {
|
||||
SystemUpdateService.checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
content: Component {
|
||||
Rectangle {
|
||||
id: updaterPanel
|
||||
|
||||
color: "transparent"
|
||||
focus: true
|
||||
|
||||
readonly property bool hasTerminalBackend: (SystemUpdateService.backends || []).some(b => b.runsInTerminal === true)
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
systemUpdatePopout.close();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (systemUpdatePopout.shouldBeVisible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: header
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
anchors.topMargin: Theme.spacingL
|
||||
height: 40
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("System Updates")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
switch (true) {
|
||||
case SystemUpdateService.isUpgrading:
|
||||
return I18n.tr("Upgrading...");
|
||||
case SystemUpdateService.isChecking:
|
||||
return I18n.tr("Checking...");
|
||||
case SystemUpdateService.hasError:
|
||||
return I18n.tr("Error");
|
||||
case SystemUpdateService.updateCount === 0:
|
||||
return I18n.tr("Up to date");
|
||||
case SystemUpdateService.updateCount === 1:
|
||||
return I18n.tr("%1 update").arg(SystemUpdateService.updateCount);
|
||||
default:
|
||||
return I18n.tr("%1 updates").arg(SystemUpdateService.updateCount);
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: SystemUpdateService.hasError ? Theme.error : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
id: refreshButton
|
||||
buttonSize: 28
|
||||
iconName: "refresh"
|
||||
iconSize: 18
|
||||
iconColor: Theme.surfaceText
|
||||
enabled: !SystemUpdateService.isChecking && !SystemUpdateService.isUpgrading
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
onClicked: SystemUpdateService.checkForUpdates()
|
||||
|
||||
RotationAnimation {
|
||||
target: refreshButton
|
||||
property: "rotation"
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
running: SystemUpdateService.isChecking
|
||||
loops: Animation.Infinite
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running) {
|
||||
refreshButton.rotation = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: backendsRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: header.bottom
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
anchors.topMargin: Theme.spacingS
|
||||
visible: SystemUpdateService.backends.length > 0 && !SystemUpdateService.isUpgrading
|
||||
text: {
|
||||
const names = (SystemUpdateService.backends || []).map(b => b.displayName).join(", ");
|
||||
return I18n.tr("Backends: %1").arg(names);
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonsRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
anchors.bottomMargin: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
height: 44
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: parent.height
|
||||
radius: Theme.cornerRadius
|
||||
color: primaryMouseArea.containsMouse && primaryMouseArea.enabled ? Theme.primaryHover : Theme.secondaryHover
|
||||
opacity: primaryMouseArea.enabled ? 1.0 : 0.5
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: SystemUpdateService.isUpgrading ? I18n.tr("Cancel") : I18n.tr("Update All")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.primary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: primaryMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: SystemUpdateService.isUpgrading || SystemUpdateService.updateCount > 0
|
||||
onClicked: {
|
||||
if (SystemUpdateService.isUpgrading) {
|
||||
SystemUpdateService.cancelUpdates();
|
||||
return;
|
||||
}
|
||||
const opts = {
|
||||
includeFlatpak: SettingsData.updaterIncludeFlatpak,
|
||||
includeAUR: SettingsData.updaterAllowAUR,
|
||||
terminal: SessionData.terminalOverride
|
||||
};
|
||||
if (updaterPanel.hasTerminalBackend) {
|
||||
systemUpdatePopout._reopenAfterUpgrade = true;
|
||||
SystemUpdateService.runUpdates(opts);
|
||||
systemUpdatePopout.close();
|
||||
return;
|
||||
}
|
||||
SystemUpdateService.runUpdates(opts);
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: parent.height
|
||||
radius: Theme.cornerRadius
|
||||
color: closeMouseArea.containsMouse ? Theme.errorPressed : Theme.secondaryHover
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("Close")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: systemUpdatePopout.close()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bodyArea
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: backendsRow.visible ? backendsRow.bottom : header.bottom
|
||||
anchors.bottom: buttonsRow.top
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.bottomMargin: Theme.spacingM
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
|
||||
|
||||
StyledText {
|
||||
id: statusText
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: !SystemUpdateService.isUpgrading && (SystemUpdateService.updateCount === 0 || SystemUpdateService.hasError || SystemUpdateService.isChecking)
|
||||
text: {
|
||||
switch (true) {
|
||||
case SystemUpdateService.hasError:
|
||||
return I18n.tr("Failed: %1").arg(SystemUpdateService.errorMessage);
|
||||
case !SystemUpdateService.helperAvailable:
|
||||
return I18n.tr("No supported package manager found.");
|
||||
case SystemUpdateService.isChecking:
|
||||
return I18n.tr("Checking for updates...");
|
||||
default:
|
||||
return I18n.tr("Your system is up to date!");
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: SystemUpdateService.hasError ? Theme.errorText : Theme.surfaceText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
DankListView {
|
||||
id: packagesList
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
visible: !SystemUpdateService.isUpgrading && SystemUpdateService.updateCount > 0 && !SystemUpdateService.hasError && !SystemUpdateService.isChecking
|
||||
clip: true
|
||||
spacing: Theme.spacingXS
|
||||
model: SystemUpdateService.availableUpdates
|
||||
|
||||
delegate: Rectangle {
|
||||
width: ListView.view.width
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: packageMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent"
|
||||
|
||||
required property var modelData
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 64
|
||||
height: 18
|
||||
radius: 9
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.18)
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.repo || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - 64 - Theme.spacingS
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: modelData.name || ""
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: {
|
||||
const from = modelData.fromVersion || "";
|
||||
const to = modelData.toVersion || "";
|
||||
if (from && to) {
|
||||
return `${from} → ${to}`;
|
||||
}
|
||||
return to || from || "";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: packageMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: modelData.changelogUrl ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: {
|
||||
if (modelData.changelogUrl) {
|
||||
Qt.openUrlExternally(modelData.changelogUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
visible: SystemUpdateService.isUpgrading && updaterPanel.hasTerminalBackend
|
||||
|
||||
DankIcon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
name: "terminal"
|
||||
size: 32
|
||||
color: Theme.primary
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("Running in terminal")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: I18n.tr("AUR helpers are interactive — see the terminal window for prompts. This popout will return to idle when the upgrade exits.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
visible: SystemUpdateService.isUpgrading && !updaterPanel.hasTerminalBackend
|
||||
contentWidth: width
|
||||
contentHeight: logText.implicitHeight
|
||||
clip: true
|
||||
|
||||
onContentHeightChanged: {
|
||||
if (contentHeight > height) {
|
||||
contentY = contentHeight - height;
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: logText
|
||||
width: parent.width
|
||||
text: (SystemUpdateService.recentLog || []).join("\n")
|
||||
font.family: Theme.monoFontFamily || "monospace"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,41 +7,33 @@ import qs.Widgets
|
||||
BasePill {
|
||||
id: root
|
||||
|
||||
property var widgetData: null
|
||||
property bool isActive: false
|
||||
|
||||
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
|
||||
readonly property bool isChecking: SystemUpdateService.isChecking
|
||||
readonly property bool shouldHide: SettingsData.updaterHideWidget && !hasUpdates && !isChecking && !SystemUpdateService.hasError
|
||||
readonly property bool isClean: SystemUpdateService.sysupdateAvailable && !hasUpdates && !isChecking && !SystemUpdateService.hasError
|
||||
readonly property bool hideWhenIdle: widgetData?.hideWhenIdle === true
|
||||
readonly property bool shouldHide: hideWhenIdle && isClean
|
||||
|
||||
width: shouldHide ? 0 : (isVerticalOrientation ? barThickness : visualWidth)
|
||||
height: shouldHide ? 0 : (isVerticalOrientation ? visualHeight : barThickness)
|
||||
visible: !shouldHide
|
||||
opacity: shouldHide ? 0 : 1
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "hidden_horizontal"
|
||||
when: root.shouldHide && !isVerticalOrientation
|
||||
PropertyChanges {
|
||||
target: root
|
||||
width: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hidden_vertical"
|
||||
when: root.shouldHide && isVerticalOrientation
|
||||
PropertyChanges {
|
||||
target: root
|
||||
height: 0
|
||||
}
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
NumberAnimation {
|
||||
properties: "width,height"
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
|
||||
Reference in New Issue
Block a user