From 642d8feb1d6d65ecc1bc119bc54dc5998f29da62 Mon Sep 17 00:00:00 2001 From: flemming-it Date: Wed, 22 Jul 2026 14:02:54 +0200 Subject: [PATCH] fix(flows): honest unknown-store state + snapshot lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store snapshot is now nullable: until the first successful store search (or after a sealed-area connection switch) the editor gets null and claims neither 'installable' nor 'not in store'. The snapshot reloads after installs and on connection switches — a sealed switch previously kept the other hub's capability offers alive on the mounted Flows page. Editor pin 2535c28. Signed-off-by: flemming-it --- lib/pages/flows.dart | 49 ++++++++++++++++++++++++++++++++++++++------ pubspec.yaml | 2 +- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/pages/flows.dart b/lib/pages/flows.dart index dbd831f..98a6d81 100644 --- a/lib/pages/flows.dart +++ b/lib/pages/flows.dart @@ -10,6 +10,8 @@ // package so a swap is one pubspec change — Studio doesn't // need to know what's inside the editor any more. +import 'dart:async' show unawaited; + import 'package:chain_studio_flow_editor/chain_studio_flow_editor.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -40,10 +42,17 @@ class _FlowsPageState extends State { late final StudioFlowRunDriver _driver; /// Snapshot of bare capability names the public store can - /// install. Refreshed once at open so the editor's analyzer - /// can decide between "Install" (in store) and "Add source" - /// (not in store) without a per-keystroke network call. - List _storeCaps = const []; + /// install; null while the store state is UNKNOWN (not yet + /// loaded / search failed) so the editor claims neither + /// "installable" nor "not in store". Refreshed at open, after + /// an install and on a connection switch. + List? _storeCaps; + + /// Which hub connection the snapshots were loaded against — + /// the sealed-area slug, or empty for the shared hub. A sealed + /// switch keeps this page mounted, so without the guard the + /// editor would keep offering the OTHER hub's capabilities. + String _connKey = Workspace.instance.activeSealed?.slug ?? ''; @override void initState() { @@ -51,6 +60,28 @@ class _FlowsPageState extends State { _driver = StudioFlowRunDriver(); _capabilities = _loadCapabilities(); _loadStoreCapabilities(); + Workspace.instance.addListener(_onWorkspaceChanged); + } + + @override + void dispose() { + Workspace.instance.removeListener(_onWorkspaceChanged); + super.dispose(); + } + + /// Reload both capability snapshots when the CONNECTION target + /// changes (entering/leaving a sealed area) — a plain project + /// filter change stays cheap and reuses the loaded lists. + void _onWorkspaceChanged() { + final key = Workspace.instance.activeSealed?.slug ?? ''; + if (key == _connKey) return; + _connKey = key; + if (!mounted) return; + setState(() { + _storeCaps = null; // other hub — snapshot unknown again + _capabilities = _loadCapabilities(); + }); + _loadStoreCapabilities(); } Future _loadStoreCapabilities() async { @@ -66,8 +97,10 @@ class _FlowsPageState extends State { () => _storeCaps = installableStoreCapabilities(items).toList()..sort(), ); } catch (_) { - // Soft-fail: empty list disables the Install button on - // unknown-cap fixes, but Add-source remains available. + // Soft-fail: keep the last snapshot for THIS connection if + // one exists; otherwise stay in the honest "unknown" state + // (no install offers, no not-in-store claims). Add-source + // remains available either way. } } @@ -134,6 +167,10 @@ class _FlowsPageState extends State { Future?> _runInstall({required String source}) async { try { await HubService.instance.installModule(source: source); + // The store snapshot may have moved too (an install can pull + // in new entries / the operator refreshed a store) — keep the + // badge's truth in step with the capability list. + unawaited(_loadStoreCapabilities()); final caps = await HubService.instance.allCapabilities(); final updated = caps .map((c) => '${c.capability}@${c.version}') diff --git a/pubspec.yaml b/pubspec.yaml index affcf0c..0ba98d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,7 @@ dependencies: # builds against. Bump this in lockstep with the editor tag. # Commit-pinned to editor 0.25.0 until its v0.25.0 tag exists # (equally reproducible); switch back to the tag then. - ref: c4a39a3 + ref: 2535c28 dev_dependencies: flutter_test: