diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index e06f7ca441..2c2c64fb25 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -137,25 +139,50 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { ref.read(tabProvider.notifier).state = TabEnum.values[index]; } -class _BottomNavigationBar extends ConsumerWidget { +class _BottomNavigationBar extends ConsumerStatefulWidget { const _BottomNavigationBar({required this.tabsRouter, required this.destinations}); final List destinations; final TabsRouter tabsRouter; @override - Widget build(BuildContext context, WidgetRef ref) { - final isScreenLandscape = context.orientation == Orientation.landscape; - final isMultiselectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + ConsumerState createState() => _BottomNavigationBarState(); +} - if (isScreenLandscape || isMultiselectEnabled) { +class _BottomNavigationBarState extends ConsumerState<_BottomNavigationBar> { + bool hideNavigationBar = false; + StreamSubscription? _eventSubscription; + + @override + void initState() { + super.initState(); + _eventSubscription = EventStream.shared.listen(_onEvent); + } + + void _onEvent(MultiSelectToggleEvent event) { + setState(() { + hideNavigationBar = event.isEnabled; + }); + } + + @override + void dispose() { + _eventSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isScreenLandscape = context.orientation == Orientation.landscape; + + if (isScreenLandscape || hideNavigationBar) { return const SizedBox.shrink(); } return NavigationBar( - selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), - destinations: destinations, + selectedIndex: widget.tabsRouter.activeIndex, + onDestinationSelected: (index) => _onNavigationSelected(widget.tabsRouter, index, ref), + destinations: widget.destinations, ); } } diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index f61fad5484..d44b215b03 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/presentation/pages/search/paginated_search.provider.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; @@ -627,7 +628,12 @@ class _SearchResultGrid extends ConsumerWidget { return timelineService; }), ], - child: Timeline(key: ValueKey(searchResult.totalAssets), appBar: null, groupBy: GroupAssetsBy.none), + child: Timeline( + key: ValueKey(searchResult.totalAssets), + groupBy: GroupAssetsBy.none, + appBar: null, + bottomSheet: const GeneralBottomSheet(minChildSize: 0.20), + ), ), ), ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index a2c88d9fd7..82f2e1c3b2 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -22,13 +22,13 @@ class BaseBottomSheet extends ConsumerStatefulWidget { this.slivers, this.controller, this.initialChildSize = 0.35, - this.minChildSize = 0.15, + double? minChildSize, this.maxChildSize = 0.65, this.expand = true, this.shouldCloseOnMinExtent = true, this.resizeOnScroll = true, this.backgroundColor, - }); + }) : minChildSize = minChildSize ?? 0.15; @override ConsumerState createState() => _BaseDraggableScrollableSheetState(); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 70b2fb00b0..8edfcb749e 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -6,8 +6,8 @@ import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; @@ -26,7 +26,8 @@ import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class GeneralBottomSheet extends ConsumerWidget { - const GeneralBottomSheet({super.key}); + final double? minChildSize; + const GeneralBottomSheet({super.key, this.minChildSize}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -60,6 +61,7 @@ class GeneralBottomSheet extends ConsumerWidget { return BaseBottomSheet( initialChildSize: 0.45, + minChildSize: minChildSize, maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 838edd8a47..b0886f020e 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -115,6 +115,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { _baseScaleFactor = _scaleFactor; }); }); + + ref.listenManual(multiSelectProvider.select((s) => s.isEnabled), _onMultiSelectionToggled); } void _onEvent(Event event) { @@ -130,6 +132,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { } } + void _onMultiSelectionToggled(_, bool isEnabled) { + EventStream.shared.emit(MultiSelectToggleEvent(isEnabled)); + } + @override void dispose() { _scrollController.dispose(); diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index 742cbd7dea..e225e0c98d 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -1,8 +1,8 @@ import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; final multiSelectProvider = NotifierProvider( @@ -10,6 +10,11 @@ final multiSelectProvider = NotifierProvider selectedAssets; final Set lockedSelectionAssets;