133 lines
4.4 KiB
Diff
133 lines
4.4 KiB
Diff
|
|
diff --git a/kickoff/CMakeLists.txt b/kickoff/CMakeLists.txt
|
||
|
|
index 70e361c..d860cb4 100644
|
||
|
|
--- a/kickoff/CMakeLists.txt
|
||
|
|
+++ b/kickoff/CMakeLists.txt
|
||
|
|
@@ -26,6 +26,7 @@ QML_SOURCES
|
||
|
|
ListOfGridsView.qml
|
||
|
|
ListOfGridsViewDelegate.qml
|
||
|
|
NormalPage.qml
|
||
|
|
+ UpdateBar.qml
|
||
|
|
PlacesPage.qml
|
||
|
|
SectionView.qml
|
||
|
|
VerticalStackView.qml
|
||
|
|
diff --git a/kickoff/FullRepresentation.qml b/kickoff/FullRepresentation.qml
|
||
|
|
index aa25d3c..7d9a6bb 100644
|
||
|
|
--- a/kickoff/FullRepresentation.qml
|
||
|
|
+++ b/kickoff/FullRepresentation.qml
|
||
|
|
@@ -77,6 +77,9 @@ EmptyPage {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+ // Quine Linux: system update status (UpdateBar.qml).
|
||
|
|
+ footer: UpdateBar {}
|
||
|
|
+
|
||
|
|
contentItem: VerticalStackView {
|
||
|
|
id: contentItemStackView
|
||
|
|
focus: true
|
||
|
|
diff --git a/kickoff/UpdateBar.qml b/kickoff/UpdateBar.qml
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000..3ebe376
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/kickoff/UpdateBar.qml
|
||
|
|
@@ -0,0 +1,100 @@
|
||
|
|
+/*
|
||
|
|
+ SPDX-FileCopyrightText: 2026 Quine Foundation
|
||
|
|
+ SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
+*/
|
||
|
|
+
|
||
|
|
+// Quine Linux: a bar at the bottom of Kickoff when a newer system image is
|
||
|
|
+// available, or downloaded and waiting for a restart. It reads the status
|
||
|
|
+// that quine-update-check.service writes as root (quine-linux's overlay,
|
||
|
|
+// /usr/libexec/quine-linux/update-check), and is hidden otherwise.
|
||
|
|
+
|
||
|
|
+pragma ComponentBehavior: Bound
|
||
|
|
+
|
||
|
|
+import QtQuick
|
||
|
|
+import org.kde.kirigami as Kirigami
|
||
|
|
+import org.kde.plasma.plasma5support as P5Support
|
||
|
|
+import org.kde.plasma.private.sessions as Sessions
|
||
|
|
+
|
||
|
|
+Kirigami.InlineMessage {
|
||
|
|
+ id: root
|
||
|
|
+
|
||
|
|
+ readonly property string statusFile: "/run/quine-linux/update-status.json"
|
||
|
|
+ readonly property string readCommand: "cat " + statusFile
|
||
|
|
+ // "current", "available" or "staged"; anything else hides the bar.
|
||
|
|
+ property string updateState: ""
|
||
|
|
+ property string newBuild: ""
|
||
|
|
+
|
||
|
|
+ position: Kirigami.InlineMessage.Position.Footer
|
||
|
|
+ // Hidden, InlineMessage's implicit height is 0, so the menu doesn't keep
|
||
|
|
+ // room for it (EmptyPage sizes by the footer's implicit height).
|
||
|
|
+ visible: updateState === "available" || updateState === "staged"
|
||
|
|
+ showCloseButton: false
|
||
|
|
+ type: updateState === "staged" ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information
|
||
|
|
+ icon.name: updateState === "staged" ? "system-reboot-update" : "system-software-update"
|
||
|
|
+ text: updateState === "staged"
|
||
|
|
+ ? i18nc("@info", "An update is ready. Restart to finish updating.")
|
||
|
|
+ : i18nc("@info", "A system update is available%1.",
|
||
|
|
+ newBuild ? i18nc("@info build date", " (built %1)", newBuild) : "")
|
||
|
|
+
|
||
|
|
+ actions: [
|
||
|
|
+ Kirigami.Action {
|
||
|
|
+ visible: root.updateState === "available"
|
||
|
|
+ text: i18nc("@action:button", "Update Now")
|
||
|
|
+ icon.name: "system-software-update"
|
||
|
|
+ // Aurora's System Update: `ujust update` in a terminal.
|
||
|
|
+ onTriggered: {
|
||
|
|
+ executable.connectSource("kioclient exec /usr/share/applications/dev.getaurora.system-update.desktop")
|
||
|
|
+ kickoff.expanded = false
|
||
|
|
+ }
|
||
|
|
+ },
|
||
|
|
+ Kirigami.Action {
|
||
|
|
+ visible: root.updateState === "staged" && sessions.canReboot
|
||
|
|
+ text: i18nc("@action:button", "Restart")
|
||
|
|
+ icon.name: "system-reboot"
|
||
|
|
+ onTriggered: {
|
||
|
|
+ kickoff.expanded = false
|
||
|
|
+ sessions.requestReboot()
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ ]
|
||
|
|
+
|
||
|
|
+ Sessions.SessionManagement {
|
||
|
|
+ id: sessions
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ P5Support.DataSource {
|
||
|
|
+ id: executable
|
||
|
|
+ engine: "executable"
|
||
|
|
+ connectedSources: []
|
||
|
|
+ onNewData: (source, data) => {
|
||
|
|
+ disconnectSource(source)
|
||
|
|
+ if (source !== root.readCommand) {
|
||
|
|
+ return
|
||
|
|
+ }
|
||
|
|
+ let status = null
|
||
|
|
+ try {
|
||
|
|
+ status = JSON.parse(data["stdout"])
|
||
|
|
+ } catch (e) {
|
||
|
|
+ // No file yet (the first check hasn't run, or a live session).
|
||
|
|
+ }
|
||
|
|
+ root.updateState = status?.state ?? ""
|
||
|
|
+ const built = status?.new?.timestamp
|
||
|
|
+ root.newBuild = built ? Qt.formatDate(new Date(built), Qt.locale(), Locale.ShortFormat) : ""
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ function refresh() {
|
||
|
|
+ executable.connectSource(readCommand)
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ Component.onCompleted: refresh()
|
||
|
|
+
|
||
|
|
+ Connections {
|
||
|
|
+ target: kickoff
|
||
|
|
+ function onExpandedChanged() {
|
||
|
|
+ if (kickoff.expanded) {
|
||
|
|
+ root.refresh()
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|