Tier 0: render what the services already decided, honestly
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -45,9 +45,57 @@ Singleton {
|
||||
+ Number(root.firmware?.count ?? 0)
|
||||
|
||||
readonly property int securityCount: Number(root.dnf?.securityCount ?? 0)
|
||||
|
||||
// Whether the advisory query answered at all. It only ever shrinks the
|
||||
// alarm -- the page says "nothing security-critical" when the count is
|
||||
// zero -- so a failed query returning zero would be reassurance nobody
|
||||
// measured.
|
||||
readonly property bool securityKnown: root.dnf?.securityKnown !== false
|
||||
|
||||
readonly property bool rebootNeeded: root.kernel?.rebootNeeded === true
|
||||
|
||||
// The last time EVERY source answered. The helper carries this forward
|
||||
// rather than restamping it on a partial failure, so it never describes a
|
||||
// check that did not complete.
|
||||
readonly property bool everChecked: root.checkedAt > 0
|
||||
|
||||
// ── What is not known, and why ──────────────────────────────────────────
|
||||
//
|
||||
// The three sources fail independently, so they carry their own errors.
|
||||
// A source that could not answer has an UNKNOWN count, which is not a count
|
||||
// of zero -- and the whole distance between those two is the distance
|
||||
// between "Up to date" and a lie. `total` sums what was actually counted,
|
||||
// so it is only the whole truth when nothing failed.
|
||||
readonly property var failedSources: {
|
||||
const failed = [];
|
||||
for (const source of ["dnf", "flatpak", "firmware"]) {
|
||||
const record = source === "dnf" ? root.dnf
|
||||
: source === "flatpak" ? root.flatpak : root.firmware;
|
||||
if (String(record?.error ?? "") !== "")
|
||||
failed.push(source);
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
|
||||
readonly property bool anySourceFailed: root.failedSources.length > 0
|
||||
|
||||
function sourceError(source: string): string {
|
||||
const record = source === "dnf" ? root.dnf
|
||||
: source === "flatpak" ? root.flatpak
|
||||
: source === "firmware" ? root.firmware : null;
|
||||
return String(record?.error ?? "");
|
||||
}
|
||||
|
||||
// "System packages", or "System packages and Firmware" -- named so the
|
||||
// headline can say which source it could not reach rather than leaving
|
||||
// someone to work it out from three cards.
|
||||
function describeFailedSources(): string {
|
||||
const names = root.failedSources.map(source => root.sourceLabel(source));
|
||||
if (names.length <= 1)
|
||||
return names.join("");
|
||||
return names.slice(0, -1).join(", ") + " and " + names[names.length - 1];
|
||||
}
|
||||
|
||||
// How much there is to fetch, when every pending item was priced. The
|
||||
// helper omits the figure for a source it could only partly price, and a
|
||||
// partial total presented as the whole download understates it -- which is
|
||||
@@ -117,9 +165,20 @@ Singleton {
|
||||
}
|
||||
|
||||
// A count nobody has verified is not a count. Saying "up to date" on the
|
||||
// strength of a check that never ran is the one wrong answer that looks
|
||||
// reassuring.
|
||||
// strength of a check that never ran -- or one that failed -- is the one
|
||||
// wrong answer that looks reassuring.
|
||||
//
|
||||
// A source that could not answer is checked FIRST and by name. "Up to date"
|
||||
// is a claim about all three, and it cannot be made while one of them is
|
||||
// unknown, however many the other two counted.
|
||||
function summary(): string {
|
||||
if (root.anySourceFailed) {
|
||||
const which = root.describeFailedSources();
|
||||
return root.total > 0
|
||||
? root.total + " update" + (root.total === 1 ? "" : "s")
|
||||
+ " found, but " + which + " could not be checked"
|
||||
: which + " could not be checked";
|
||||
}
|
||||
if (!root.everChecked)
|
||||
return "Not checked yet";
|
||||
if (root.total === 0)
|
||||
@@ -225,22 +284,28 @@ Singleton {
|
||||
readonly property bool loadingChangelog: changelogProcess.running
|
||||
|
||||
// Returns the record for an item, or null while one is being fetched.
|
||||
// Starting the fetch is a side effect on purpose: the page asks for a
|
||||
// changelog by rendering one, and there is nothing else to ask.
|
||||
// Reading and requesting are two functions on purpose. The first version
|
||||
// started the fetch as a side effect of being rendered, and mutating the
|
||||
// queue from inside a binding evaluation is a binding loop: the queue
|
||||
// write bumps a property the binding reads, which re-evaluates the
|
||||
// binding, forty-odd times per page open. Bindings call changelogFor
|
||||
// (pure); the expand action calls requestChangelog (imperative).
|
||||
function changelogFor(source: string, name: string): var {
|
||||
root.changelogRevision;
|
||||
const key = source + "/" + name;
|
||||
const known = root.changelogs[key];
|
||||
if (known !== undefined)
|
||||
return known;
|
||||
const known = root.changelogs[source + "/" + name];
|
||||
return known !== undefined ? known : null;
|
||||
}
|
||||
|
||||
function requestChangelog(source: string, name: string): void {
|
||||
if (!source || !name)
|
||||
return null;
|
||||
if (changelogProcess.key === key
|
||||
return;
|
||||
const key = source + "/" + name;
|
||||
if (root.changelogs[key] !== undefined
|
||||
|| changelogProcess.key === key
|
||||
|| root.changelogQueue.some(entry => entry.source + "/" + entry.name === key))
|
||||
return null;
|
||||
return;
|
||||
root.changelogQueue = root.changelogQueue.concat([{ source: source, name: name }]);
|
||||
root.pumpChangelogs();
|
||||
return null;
|
||||
}
|
||||
|
||||
function pumpChangelogs(): void {
|
||||
|
||||
Reference in New Issue
Block a user