diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-15 12:21:03 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-15 12:21:03 +0200 |
| commit | 44f23582c0fcee1c50dcd696d320bc7020237132 (patch) | |
| tree | 8d2bd49b423b218388e8d41069bac2fa9c073e8e /desktop/modules/updates/UpdatesPage.qml | |
| parent | 7db7405c80e8f00008bc5196abfda2c500071a98 (diff) | |
| download | quickshell-44f23582c0fcee1c50dcd696d320bc7020237132.tar.gz quickshell-44f23582c0fcee1c50dcd696d320bc7020237132.zip | |
feat(updates): the drawer tile and page
The tile counts what can be installed now (Slackware and the binary
repo) and what only needs a build, and goes accent when there is
something to install. It runs updatesctl while the drawer is open and
not otherwise; a result over 30 minutes old is followed by a refresh
that runs nvchecker, never after a refresh, so a failing nvchecker
cannot loop.
The page has one section per next action: Slackware upgrades, new and
removed first, then upgrade here, awaiting build, upstream newer, app
updates, the order the work is done in. Each row names the package, the
repo it comes from and the one version jump its action covers. A source
that could not be read says so under the section it empties instead of
reading as nothing pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/updates/UpdatesPage.qml')
| -rw-r--r-- | desktop/modules/updates/UpdatesPage.qml | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/desktop/modules/updates/UpdatesPage.qml b/desktop/modules/updates/UpdatesPage.qml new file mode 100644 index 0000000..967b0e0 --- /dev/null +++ b/desktop/modules/updates/UpdatesPage.qml @@ -0,0 +1,131 @@ +// 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 QtQuick +import "../.." + +// One section per action, in the order they are done, each hidden when it +// has nothing and no error. Read-only: installing and building stay in the +// terminal. +Column { + id: page + + required property var up + readonly property var d: up.doc + + spacing: 16 + + function tracked(list) { + return list.map(r => ({ name: r.name, repo: r.repo, ver: r.from + " → " + r.to })); + } + + readonly property var sections: !d ? [] : [ + { title: "Slackware upgrades", err: d.errors.mirror || "", + rows: d.slackware.upgrade.map(r => ({ name: r.name, repo: "", ver: r.from + " → " + r.to })) }, + { title: "New in Slackware", err: "", + rows: d.slackware.new.map(r => ({ name: r.name, repo: "", ver: r.version })) }, + { title: "Removed from Slackware", err: "", + rows: d.slackware.removed.map(r => ({ name: r.name, repo: "", ver: "removed " + r.date })) }, + { title: "Upgrade here", err: d.errors.repo || "", rows: tracked(d.upgrade) }, + { title: "Awaiting build", err: "", rows: tracked(d.build) }, + { title: "Upstream newer", err: d.errors.nvchecker || "", rows: tracked(d.upstream) }, + { title: "App updates", err: "", rows: tracked(d.apps) }, + ] + + function ago(t) { + const m = Math.round((Date.now() / 1000 - t) / 60); + return m < 1 ? "just now" : m < 60 ? m + " min ago" : Math.round(m / 60) + " h ago"; + } + + Text { + visible: page.d !== null && page.up.toInstall === 0 && page.up.toBuild === 0 + && page.d.upstream.length === 0 && page.d.apps.length === 0 + text: "Up to date" + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + } + + Repeater { + model: page.sections + + Column { + required property var modelData + width: page.width + spacing: 4 + visible: modelData.rows.length > 0 || modelData.err !== "" + + Text { + text: modelData.title + (modelData.rows.length ? " " + modelData.rows.length : "") + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true } + } + + Text { + visible: modelData.err !== "" + text: modelData.err + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3; italic: true } + } + + Repeater { + model: modelData.rows + + Item { + required property var modelData + width: page.width + height: 22 + + Text { + anchors { left: parent.left; right: repo.left; rightMargin: 8; verticalCenter: parent.verticalCenter } + elide: Text.ElideRight + textFormat: Text.PlainText + text: modelData.name + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } + Text { + id: repo + anchors { right: ver.left; rightMargin: 12; verticalCenter: parent.verticalCenter } + text: modelData.repo + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + } + Text { + id: ver + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + textFormat: Text.PlainText + text: modelData.ver + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } + } + } + } + } + + Row { + spacing: 10 + + Text { + anchors.verticalCenter: parent.verticalCenter + text: page.up.checking ? "checking…" + : page.d && page.d.checked ? "checked " + page.ago(page.d.checked) : "" + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } + + Button { + text: "Refresh" + enabled: !page.up.checking + onClicked: page.up.run(true) + } + } +} |
