From 4802802828c8c6383e13bddb06f589a2df24dc0f Mon Sep 17 00:00:00 2001 From: Levi Date: Sun, 13 Sep 2026 17:20:08 +0800 Subject: [PATCH] fix: limit URL install loading to its submit button --- .../presentation/pages/extensions_page.dart | 20 +------ ui/flutter/test/widget_test.dart | 56 +++++++++++-------- 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart b/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart index 4f7a5ae4..ef3d076d 100644 --- a/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart +++ b/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart @@ -670,7 +670,6 @@ class _Toolbar extends StatelessWidget { @override Widget build(BuildContext context) { final palette = AppPalette.of(context); - final manualInstallBusy = state.busyExtensionIds.contains(ExtensionsController.manualInstallBusyKey); final search = AppTextField( key: const ValueKey('extension-search-input'), controller: searchController, @@ -717,7 +716,6 @@ class _Toolbar extends StatelessWidget { key: const ValueKey('load-local-extension-button'), tooltip: context.l10n.extensionLoadLocal, icon: Icons.folder_open_outlined, - loading: manualInstallBusy, onPressed: onInstallFolder, ), const SizedBox(width: 8), @@ -728,7 +726,6 @@ class _Toolbar extends StatelessWidget { key: const ValueKey('install-extension-button'), tooltip: context.l10n.extensionInstallFromUrl, icon: Icons.add_link, - loading: manualInstallBusy, onPressed: () => onOpenInstall(buttonContext), ), ), @@ -804,18 +801,11 @@ class _FilterBar extends StatelessWidget { } class _OutlineToolbarIconButton extends StatelessWidget { - const _OutlineToolbarIconButton({ - super.key, - required this.tooltip, - required this.icon, - required this.onPressed, - this.loading = false, - }); + const _OutlineToolbarIconButton({super.key, required this.tooltip, required this.icon, required this.onPressed}); final String tooltip; final IconData icon; final VoidCallback? onPressed; - final bool loading; @override Widget build(BuildContext context) { @@ -823,13 +813,7 @@ class _OutlineToolbarIconButton extends StatelessWidget { message: tooltip, child: SizedBox.square( dimension: 32, - child: shad.IconButton.outline( - size: shad.ButtonSize.xSmall, - onPressed: loading ? null : onPressed, - icon: loading - ? const SizedBox.square(dimension: 13, child: shad.CircularProgressIndicator()) - : Icon(icon, size: 17), - ), + child: shad.IconButton.outline(size: shad.ButtonSize.xSmall, onPressed: onPressed, icon: Icon(icon, size: 17)), ), ); } diff --git a/ui/flutter/test/widget_test.dart b/ui/flutter/test/widget_test.dart index 899bc734..80fe6a95 100644 --- a/ui/flutter/test/widget_test.dart +++ b/ui/flutter/test/widget_test.dart @@ -857,30 +857,37 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('extension install actions show progress instead of a disabled icon while busy', ( - WidgetTester tester, - ) async { - await _setTestSize(tester, const Size(1100, 900)); - await tester.pumpWidget( - ProviderScope( - overrides: [extensionsControllerProvider.overrideWith(BusyInstallExtensionsController.new)], - child: shad.ShadcnApp( - theme: AppTheme.light(), - materialTheme: AppTheme.materialLight(), - home: const ExtensionsPage(), + testWidgets( + 'URL installation leaves toolbar icons unchanged while store installation shows progress', + (WidgetTester tester) async { + await _setTestSize(tester, const Size(1100, 900)); + await tester.pumpWidget( + ProviderScope( + overrides: [extensionsControllerProvider.overrideWith(BusyInstallExtensionsController.new)], + child: shad.ShadcnApp( + theme: AppTheme.light(), + materialTheme: AppTheme.materialLight(), + home: const ExtensionsPage(), + ), ), - ), - ); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 100)); + ); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 100)); - final manualInstall = find.byKey(const ValueKey('install-extension-button')); - final storeInstall = find.byKey(const ValueKey('install-store-extension-extension-1')); - expect(find.descendant(of: manualInstall, matching: find.byType(shad.CircularProgressIndicator)), findsOneWidget); - expect(find.descendant(of: storeInstall, matching: find.byType(shad.CircularProgressIndicator)), findsOneWidget); - expect(find.descendant(of: storeInstall, matching: find.byIcon(Icons.download)), findsNothing); - expect(tester.takeException(), isNull); - }); + final manualInstall = find.byKey(const ValueKey('install-extension-button')); + final storeInstall = find.byKey(const ValueKey('install-store-extension-extension-1')); + expect(find.descendant(of: manualInstall, matching: find.byType(shad.CircularProgressIndicator)), findsNothing); + expect(find.descendant(of: manualInstall, matching: find.byIcon(Icons.add_link)), findsOneWidget); + final localInstall = find.byKey(const ValueKey('load-local-extension-button')); + expect(localInstall, findsOneWidget); + expect(find.descendant(of: localInstall, matching: find.byType(shad.CircularProgressIndicator)), findsNothing); + expect(find.descendant(of: localInstall, matching: find.byIcon(Icons.folder_open_outlined)), findsOneWidget); + expect(find.descendant(of: storeInstall, matching: find.byType(shad.CircularProgressIndicator)), findsOneWidget); + expect(find.descendant(of: storeInstall, matching: find.byIcon(Icons.download)), findsNothing); + expect(tester.takeException(), isNull); + }, + variant: TargetPlatformVariant.only(TargetPlatform.macOS), + ); for (final (width, platform) in [ (320.0, TargetPlatform.android), @@ -5301,7 +5308,10 @@ class BusyInstallExtensionsController extends FakeExtensionsController { @override Future build() async { final initial = await super.build(); - return initial.copyWith(busyExtensionIds: {ExtensionsController.manualInstallBusyKey, 'extension-1'}); + return initial.copyWith( + devMode: true, + busyExtensionIds: {ExtensionsController.manualInstallBusyKey, 'extension-1'}, + ); } }