From 10b2e46984fe67335ed2d24df871d596c160c54f Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Sat, 3 May 2025 23:43:30 +0200 Subject: [PATCH] various ui improvements --- .../geckoview/domain/repositories/tab.g.dart | 2 +- .../browser/presentation/screens/browser.dart | 20 +--- .../browser_modules/bottom_app_bar.dart | 22 +++-- .../widgets/sheets/view_tabs.dart | 9 +- .../presentation/widgets/tab_preview.dart | 17 +++- .../presentation/widgets/reader_button.dart | 94 ++++++++++--------- .../search/presentation/screens/search.dart | 45 +++++---- .../widgets/selectable_chips.dart | 2 + 8 files changed, 122 insertions(+), 89 deletions(-) diff --git a/app/lib/features/geckoview/domain/repositories/tab.g.dart b/app/lib/features/geckoview/domain/repositories/tab.g.dart index 87aea6eb..102582d6 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.g.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.g.dart @@ -6,7 +6,7 @@ part of 'tab.dart'; // RiverpodGenerator // ************************************************************************** -String _$tabRepositoryHash() => r'743732f6911372ea901b5a9402791569bfe80137'; +String _$tabRepositoryHash() => r'9b7ef03bec41f149b3cb456b73d7ee0e1b2804df'; /// See also [TabRepository]. @ProviderFor(TabRepository) diff --git a/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart b/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart index f81fbef0..a03c98ee 100644 --- a/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart +++ b/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart @@ -39,10 +39,6 @@ class BrowserScreen extends HookConsumerWidget { final displayedSheet = ref.watch(bottomSheetControllerProvider); final displayedOverlay = ref.watch(overlayControllerProvider); - final selectedTabId = ref.watch( - selectedTabStateProvider.select((value) => value?.id), - ); - final lastBackButtonPress = useRef(null); final overlayController = useOverlayPortalController(); @@ -91,10 +87,7 @@ class BrowserScreen extends HookConsumerWidget { return Visibility( visible: !tabInFullScreen, - child: BrowserBottomAppBar( - selectedTabId: selectedTabId, - displayedSheet: displayedSheet, - ), + child: BrowserBottomAppBar(displayedSheet: displayedSheet), ); }, ), @@ -129,10 +122,7 @@ class BrowserScreen extends HookConsumerWidget { : null, child: BackButtonListener( onBackButtonPressed: () async { - final tabState = - (selectedTabId != null) - ? ref.read(tabStateProvider(selectedTabId)) - : null; + final tabState = ref.read(selectedTabStateProvider); final tabCount = ref.read( tabListProvider.select((tabs) => tabs.value.length), @@ -167,7 +157,7 @@ class BrowserScreen extends HookConsumerWidget { lastBackButtonPress.value = null; final controller = ref.read( - tabSessionProvider(tabId: selectedTabId).notifier, + selectedTabSessionNotifierProvider, ); await controller.stopLoading(); @@ -176,7 +166,7 @@ class BrowserScreen extends HookConsumerWidget { lastBackButtonPress.value = null; final controller = ref.read( - tabSessionProvider(tabId: selectedTabId).notifier, + selectedTabSessionNotifierProvider, ); await controller.goBack(); @@ -190,7 +180,7 @@ class BrowserScreen extends HookConsumerWidget { if (ref .read(tabRepositoryProvider.notifier) - .hasLaunchedFromIntent(selectedTabId)) { + .hasLaunchedFromIntent(tabState?.id)) { //Mark back as unhandled and navigator will pop await SystemNavigator.pop(); return false; diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart index 8d021e72..104e00f4 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart @@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lensai/core/routing/routes.dart'; import 'package:lensai/features/geckoview/domain/controllers/bottom_sheet.dart'; import 'package:lensai/features/geckoview/domain/providers.dart'; +import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart'; import 'package:lensai/features/geckoview/domain/providers/tab_session.dart'; import 'package:lensai/features/geckoview/domain/providers/tab_state.dart'; import 'package:lensai/features/geckoview/domain/providers/web_extensions_state.dart'; @@ -24,12 +25,8 @@ import 'package:lensai/utils/ui_helper.dart' as ui_helper; import 'package:share_plus/share_plus.dart'; class BrowserBottomAppBar extends HookConsumerWidget { - const BrowserBottomAppBar({ - required this.selectedTabId, - required this.displayedSheet, - }); + const BrowserBottomAppBar({required this.displayedSheet}); - final String? selectedTabId; final Sheet? displayedSheet; @override @@ -39,17 +36,26 @@ class BrowserBottomAppBar extends HookConsumerWidget { final tabMenuController = useMenuController(); final trippleDotMenuController = useMenuController(); + final selectedTabId = ref.watch(selectedTabProvider); + final isPrivateTab = ref.watch( + selectedTabStateProvider.select((state) => state?.isPrivate ?? false), + ); + return BottomAppBar( height: AppBar().preferredSize.height, padding: EdgeInsets.zero, child: AppBar( automaticallyImplyLeading: false, titleSpacing: 8.0, + backgroundColor: + (isPrivateTab && displayedSheet is! ViewTabsSheet) + ? const Color(0x648000D7) + : null, title: (selectedTabId != null && displayedSheet is! ViewTabsSheet) ? HookConsumer( builder: (context, ref, child) { - final tabState = ref.watch(tabStateProvider(selectedTabId)); + final tabState = ref.watch(selectedTabStateProvider); final dragStartPosition = useRef(Offset.zero); @@ -111,7 +117,7 @@ class BrowserBottomAppBar extends HookConsumerWidget { onPressed: () async { await ref .read(tabRepositoryProvider.notifier) - .closeTab(selectedTabId!); + .closeTab(selectedTabId); }, leadingIcon: const Icon(Icons.close), child: const Text('Close Tab'), @@ -374,7 +380,7 @@ class BrowserBottomAppBar extends HookConsumerWidget { onPressed: () async { await ref .read(tabRepositoryProvider.notifier) - .closeTab(selectedTabId!); + .closeTab(selectedTabId); trippleDotMenuController.close(); }, icon: const Icon(Icons.close), diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart index 5432aa94..7fc82dfb 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart @@ -11,6 +11,7 @@ import 'package:lensai/core/providers/global_drop.dart'; import 'package:lensai/core/routing/routes.dart'; import 'package:lensai/data/models/drag_data.dart'; import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart'; +import 'package:lensai/features/geckoview/domain/providers/tab_state.dart'; import 'package:lensai/features/geckoview/features/browser/domain/providers.dart'; import 'package:lensai/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart'; import 'package:lensai/features/geckoview/features/browser/presentation/widgets/tab_preview.dart'; @@ -91,7 +92,7 @@ class _TabSheetHeader extends HookConsumerWidget { // enableIMEPersonalizedLearning: !incognitoEnabled, decoration: InputDecoration( // border: InputBorder.none, - prefixIcon: const Icon(Icons.search), + prefixIcon: const Icon(MdiIcons.tabSearch), hintText: 'Search inside tabs...', floatingLabelBehavior: FloatingLabelBehavior.always, suffixIcon: Row( @@ -411,7 +412,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget { ), child: FloatingActionButton.small( onPressed: () async { - await const SearchRoute(tabType: TabType.regular).push(context); + final isCurrentPrivate = + ref.read(selectedTabStateProvider)?.isPrivate ?? false; + await SearchRoute( + tabType: isCurrentPrivate ? TabType.private : TabType.regular, + ).push(context); onClose(); }, diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_preview.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_preview.dart index 23470e59..5feaceda 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_preview.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_preview.dart @@ -87,7 +87,22 @@ class TabPreview extends StatelessWidget { ), if (tab.isPrivate) ...[ const SizedBox(width: 6.0), - const Icon(MdiIcons.dominoMask, color: Color(0xFF8000D7)), + const SizedBox( + height: 16, + width: 24, + child: Stack( + fit: StackFit.expand, + children: [ + Positioned( + top: -4, + child: Icon( + MdiIcons.dominoMask, + color: Color(0xFF8000D7), + ), + ), + ], + ), + ), ], const SizedBox(width: 8.0), ], diff --git a/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart b/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart index 45419cef..2fc5b543 100644 --- a/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart +++ b/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lensai/features/geckoview/domain/entities/states/readerable.dart'; @@ -27,59 +26,66 @@ class ReaderButton extends HookConsumerWidget { ), ); - final icon = useMemoized( - () => - readerabilityState.active - ? Icon( - MdiIcons.bookOpen, - color: Theme.of(context).colorScheme.primary, - ) - : const Icon(MdiIcons.bookOpenOutline, color: Colors.white), - [readerabilityState.active], - ); + final icon = + readerabilityState.active + ? Icon( + MdiIcons.bookOpen, + color: Theme.of(context).colorScheme.primary, + ) + : Icon( + MdiIcons.bookOpenOutline, + color: Theme.of(context).colorScheme.onSurface, + ); return Visibility( visible: readerabilityState.readerable && (enableReadability || readerabilityState.active), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 15.0, horizontal: 8.0), - child: readerChanging.when( - skipLoadingOnReload: true, - data: - (_) => Visibility( - visible: readerabilityState.readerable, - child: InkWell( - onTap: - readerChanging.isLoading - ? null - : () async { - await ref - .read( - readerableScreenControllerProvider.notifier, - ) - .toggleReaderView(!readerabilityState.active); - }, + child: readerChanging.when( + skipLoadingOnReload: true, + data: + (_) => Visibility( + visible: readerabilityState.readerable, + child: InkWell( + onTap: + readerChanging.isLoading + ? null + : () async { + await ref + .read(readerableScreenControllerProvider.notifier) + .toggleReaderView(!readerabilityState.active); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 15.0, + horizontal: 8.0, + ), child: icon, ), ), - error: (error, stackTrace) => SizedBox.shrink(), - loading: - () => AnimateGradientShader( - duration: const Duration(milliseconds: 500), - primaryEnd: Alignment.bottomLeft, - secondaryEnd: Alignment.topRight, - primaryColors: [ - colorScheme.primary, - colorScheme.primaryContainer, - ], - secondaryColors: [ - colorScheme.secondary, - colorScheme.secondaryContainer, - ], + ), + error: (error, stackTrace) => SizedBox.shrink(), + loading: + () => AnimateGradientShader( + duration: const Duration(milliseconds: 500), + primaryEnd: Alignment.bottomLeft, + secondaryEnd: Alignment.topRight, + primaryColors: [ + colorScheme.primary, + colorScheme.primaryContainer, + ], + secondaryColors: [ + colorScheme.secondary, + colorScheme.secondaryContainer, + ], + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 15.0, + horizontal: 8.0, + ), child: icon, ), - ), + ), ), ); } diff --git a/app/lib/features/geckoview/features/search/presentation/screens/search.dart b/app/lib/features/geckoview/features/search/presentation/screens/search.dart index d8adb850..ee574d54 100644 --- a/app/lib/features/geckoview/features/search/presentation/screens/search.dart +++ b/app/lib/features/geckoview/features/search/presentation/screens/search.dart @@ -88,24 +88,33 @@ class SearchScreen extends HookConsumerWidget { pinned: true, automaticallyImplyLeading: false, title: Align( - child: SegmentedButton( - showSelectedIcon: false, - segments: const [ - ButtonSegment( - value: TabType.regular, - label: Text('Regular'), - icon: Icon(MdiIcons.tab), - ), - ButtonSegment( - value: TabType.private, - label: Text('Private'), - icon: Icon(MdiIcons.tabUnselected), - ), - ], - selected: {selectedTabType.value}, - onSelectionChanged: (value) { - selectedTabType.value = value.first; - }, + child: Focus( + canRequestFocus: false, + child: SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment( + value: TabType.regular, + label: Text('Regular'), + icon: Icon(MdiIcons.tab), + ), + ButtonSegment( + value: TabType.private, + label: Text('Private'), + icon: Icon(MdiIcons.tabUnselected), + ), + ], + selected: {selectedTabType.value}, + onSelectionChanged: (value) { + selectedTabType.value = value.first; + }, + style: switch (selectedTabType.value) { + TabType.regular => null, + TabType.private => SegmentedButton.styleFrom( + selectedBackgroundColor: const Color(0x648000D7), + ), + }, + ), ), ), bottom: PreferredSize( diff --git a/app/lib/presentation/widgets/selectable_chips.dart b/app/lib/presentation/widgets/selectable_chips.dart index 9806af2d..5084e5fb 100644 --- a/app/lib/presentation/widgets/selectable_chips.dart +++ b/app/lib/presentation/widgets/selectable_chips.dart @@ -63,6 +63,8 @@ class SelectableChips extends StatelessWidget { builder: (context, controller) { return ListView.builder( controller: controller, + //Improve list performance by not rendering outside screen at all + cacheExtent: 0, scrollDirection: Axis.horizontal, itemCount: items.length, itemBuilder: (context, index) {