diff options
Diffstat (limited to 'desktop/modules/updates/UpdatesModule.qml')
| -rw-r--r-- | desktop/modules/updates/UpdatesModule.qml | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/desktop/modules/updates/UpdatesModule.qml b/desktop/modules/updates/UpdatesModule.qml new file mode 100644 index 0000000..f7b1f39 --- /dev/null +++ b/desktop/modules/updates/UpdatesModule.qml @@ -0,0 +1,97 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import Quickshell +import Quickshell.Io +import QtQuick +import "../.." + +// Pending updates, sorted by updatesctl into the next action. Nothing runs +// while the drawer is closed: the tile's lifetime is the check's, as with +// KDE Connect. A result older than 30 minutes is followed by a refresh, +// which runs nvchecker. +Module { + id: mod + + name: "updates" + label: "Updates" + + // linux-slackware, present in Inconsolata Nerd Font. + icon: "\uf318" + + property var doc: null + property bool checking: false + + readonly property int toInstall: doc + ? doc.slackware.upgrade.length + doc.slackware.new.length + + doc.slackware.removed.length + doc.upgrade.length + : 0 + readonly property int toBuild: doc ? doc.build.length : 0 + + active: toInstall > 0 + + property bool polling: false + onPollingChanged: if (polling) run(false) + + function run(refresh) { + if (proc.running && mod.checking) return; // never cut nvchecker short + proc.command = ["/usr/bin/python3", + `${Quickshell.shellDir}/modules/updates/updatesctl`].concat(refresh ? ["--refresh"] : []); + proc.running = false; + // After the stop: a killed run can still finish its stream, and + // its handler clears `checking`. + mod.checking = refresh; + proc.running = true; + } + + property Process proc: Process { + stdout: StdioCollector { + onStreamFinished: { + const refreshed = mod.checking; + mod.checking = false; + try { mod.doc = JSON.parse(this.text); } catch (e) { return; } + // Only after a cached read: a refresh whose nvchecker failed + // leaves `checked` old and would otherwise loop. + if (!refreshed && mod.polling && Date.now() / 1000 - mod.doc.checked > 1800) + mod.run(true); + } + } + } + + tileContent: Component { + Text { + Component.onCompleted: mod.polling = true + Component.onDestruction: mod.polling = false + + width: parent ? parent.width : implicitWidth + elide: Text.ElideRight + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: mod.active ? Theme.text : Theme.subtext + text: { + if (!mod.doc) return "checking…"; + const parts = []; + if (mod.toInstall) parts.push(mod.toInstall + " to install"); + if (mod.toBuild) parts.push(mod.toBuild + " to build"); + return parts.join(" · ") || "up to date"; + } + } + } + + page: Component { + Page { + title: "Updates" + UpdatesPage { + width: parent.width + up: mod + } + } + } +} |
