fix: never claim 'not in store' while the store state is unknown

A failed/unloaded store snapshot used to be indistinguishable from
a known-empty store, so every missing capability was labelled 'not
in store' the moment the hub or store endpoint was unreachable — a
wrong claim. storeCapabilities is now nullable (null = unknown):
missing caps then get the plain missing chip with an honest
tooltip, no install offer and no not-in-store claim; the analyzer
message says the store cannot be checked right now (EN+DE). Split
and badge covered by new unit + widget tests.

Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
flemming-it 2026-07-22 14:02:28 +02:00
parent c4a39a3779
commit 2535c28fce
6 changed files with 131 additions and 34 deletions

View file

@ -38,13 +38,14 @@ class FlowAnalyzer extends AbstractAnalyzer {
/// rebuild.
final List<String> Function() availableCapabilities;
/// Returns the names of capabilities the public store knows
/// how to install. Used to decide whether an unknown-cap
/// issue should carry an Install button clicking the
/// button on a capability the hub can't actually fetch would
/// just fail. Null = "no store available" install offered
/// for every unknown cap (legacy behaviour).
final List<String> Function()? storeCapabilities;
/// Returns the names of capabilities the store can actually
/// install, or null when the store state is UNKNOWN (snapshot
/// not loaded / store unreachable). Drives whether an
/// unknown-cap issue carries an Install button (in store), the
/// "not in store" recovery message (known, absent) or the
/// neutral store-unknown wording (null) the analyzer never
/// claims "no store provides it" without a loaded snapshot.
final List<String>? Function()? storeCapabilities;
/// Quick fixes attached to the most-recent analyze() pass.
/// Keyed by the same `Issue` instances that landed in
@ -111,9 +112,10 @@ class FlowAnalyzer extends AbstractAnalyzer {
// as Did-you-mean candidates so the suggestion can preserve
// the version constraint when the user already typed one.
final installedFull = caps.toSet();
final storeCaps =
storeCapabilities?.call() ?? const <String>[];
final storeBare = storeCaps.map(_bareCap).toSet();
final storeCaps = storeCapabilities?.call();
final storeKnown = storeCaps != null;
final storeBare =
(storeCaps ?? const <String>[]).map(_bareCap).toSet();
YamlNode? doc;
try {
@ -175,7 +177,9 @@ class FlowAnalyzer extends AbstractAnalyzer {
? strings.unknownCapInStore(useValue)
: didYouMean != null
? strings.unknownCapTypo(useValue, didYouMean)
: strings.unknownCapNotInStore(useValue);
: storeKnown
? strings.unknownCapNotInStore(useValue)
: strings.unknownCapStoreUnknown(useValue);
final issue = Issue(
line: issueLine,
message: message,