diff --git a/mobile/lib/ui/home/home_gallery_widget.dart b/mobile/lib/ui/home/home_gallery_widget.dart index 5d9f9c09dc..366559cb8c 100644 --- a/mobile/lib/ui/home/home_gallery_widget.dart +++ b/mobile/lib/ui/home/home_gallery_widget.dart @@ -13,6 +13,7 @@ import 'package:photos/services/collections_service.dart'; import "package:photos/services/filter/db_filters.dart"; import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart'; import 'package:photos/ui/viewer/gallery/gallery.dart'; +import "package:photos/ui/viewer/gallery/state/selection_state.dart"; class HomeGalleryWidget extends StatelessWidget { final Widget? header; @@ -84,12 +85,16 @@ class HomeGalleryWidget extends StatelessWidget { reloadDebounceTime: const Duration(seconds: 2), reloadDebounceExecutionInterval: const Duration(seconds: 5), ); - return Stack( - alignment: Alignment.bottomCenter, - children: [ - gallery, - FileSelectionOverlayBar(GalleryType.homepage, selectedFiles), - ], + return SelectionState( + selectedFiles: selectedFiles, + // ignore: prefer_const_literals_to_create_immutables + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + gallery, + FileSelectionOverlayBar(GalleryType.homepage, selectedFiles), + ], + ), ); // return gallery; } diff --git a/mobile/lib/ui/viewer/actions/file_selection_overlay_bar.dart b/mobile/lib/ui/viewer/actions/file_selection_overlay_bar.dart index 8e2260c74d..53d835fce4 100644 --- a/mobile/lib/ui/viewer/actions/file_selection_overlay_bar.dart +++ b/mobile/lib/ui/viewer/actions/file_selection_overlay_bar.dart @@ -4,7 +4,9 @@ import 'package:photos/models/collection/collection.dart'; import 'package:photos/models/gallery_type.dart'; import 'package:photos/models/selected_files.dart'; import "package:photos/theme/effects.dart"; +import "package:photos/theme/ente_theme.dart"; import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart'; +import "package:photos/ui/viewer/gallery/state/selection_state.dart"; class FileSelectionOverlayBar extends StatefulWidget { final GalleryType galleryType; @@ -66,18 +68,25 @@ class _FileSelectionOverlayBarState extends State { ? CrossFadeState.showFirst : CrossFadeState.showSecond, duration: const Duration(milliseconds: 400), - firstChild: BottomActionBarWidget( - selectedFiles: widget.selectedFiles, - galleryType: widget.galleryType, - collection: widget.collection, - person: widget.person, - clusterID: widget.clusterID, - onCancel: () { - if (widget.selectedFiles.files.isNotEmpty) { - widget.selectedFiles.clearAll(); - } - }, - backgroundColor: widget.backgroundColor, + firstChild: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + SelectAllButton(backgroundColor: widget.backgroundColor), + BottomActionBarWidget( + selectedFiles: widget.selectedFiles, + galleryType: widget.galleryType, + collection: widget.collection, + person: widget.person, + clusterID: widget.clusterID, + onCancel: () { + if (widget.selectedFiles.files.isNotEmpty) { + widget.selectedFiles.clearAll(); + } + }, + backgroundColor: widget.backgroundColor, + ), + ], ), secondChild: const SizedBox(width: double.infinity), ); @@ -90,3 +99,48 @@ class _FileSelectionOverlayBarState extends State { _hasSelectedFilesNotifier.value = widget.selectedFiles.files.isNotEmpty; } } + +class SelectAllButton extends StatefulWidget { + final Color? backgroundColor; + const SelectAllButton({super.key, required this.backgroundColor}); + + @override + State createState() => _SelectAllButtonState(); +} + +class _SelectAllButtonState extends State { + bool _selectAll = false; + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + setState(() { + final selectionState = SelectionState.of(context); + if (_selectAll) { + selectionState?.selectedFiles.clearAll(); + } else { + selectionState?.selectedFiles + .selectAll(selectionState.allGalleryFiles!.toSet()); + } + _selectAll = !_selectAll; + }); + }, + child: Container( + color: getEnteColorScheme(context).backgroundElevated2, + padding: const EdgeInsets.all(4), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + const Text("All"), + Icon( + _selectAll ? Icons.check_circle : Icons.check_circle_outline, + color: + _selectAll ? getEnteColorScheme(context).strokeMuted : null, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/ui/viewer/gallery/gallery.dart b/mobile/lib/ui/viewer/gallery/gallery.dart index b255c5c375..d895bc80fe 100644 --- a/mobile/lib/ui/viewer/gallery/gallery.dart +++ b/mobile/lib/ui/viewer/gallery/gallery.dart @@ -16,6 +16,7 @@ import "package:photos/ui/viewer/gallery/component/group/type.dart"; import "package:photos/ui/viewer/gallery/component/multiple_groups_gallery_view.dart"; import 'package:photos/ui/viewer/gallery/empty_state.dart'; import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart"; +import "package:photos/ui/viewer/gallery/state/selection_state.dart"; import "package:photos/utils/debouncer.dart"; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; @@ -173,7 +174,7 @@ class GalleryState extends State { if (result.hasMore) { final result = await _loadFiles(); _setFilesAndReload(result.files); - } + } else {} }); } @@ -213,6 +214,8 @@ class GalleryState extends State { // group files into multiple groups and returns `true` if it resulted in a // gallery reload bool _onFilesLoaded(List files) { + SelectionState.of(context)?.allGalleryFiles = files; + final updatedGroupedFiles = widget.enableFileGrouping && widget.groupType.timeGrouping() ? _groupBasedOnTime(files) diff --git a/mobile/lib/ui/viewer/gallery/state/selection_state.dart b/mobile/lib/ui/viewer/gallery/state/selection_state.dart new file mode 100644 index 0000000000..fa2fdfa171 --- /dev/null +++ b/mobile/lib/ui/viewer/gallery/state/selection_state.dart @@ -0,0 +1,29 @@ +import "package:flutter/material.dart"; +import "package:photos/models/file/file.dart"; +import "package:photos/models/selected_files.dart"; + +// ignore: must_be_immutable +class SelectionState extends InheritedWidget { + final SelectedFiles selectedFiles; + + ///Should be assigned later in gallery when files are loaded. + ///Note: EnteFiles in this list should be references of the same EnteFiles + ///that are grouped in gallery, so that when files are added/deleted, + ///both lists are in sync. + List? allGalleryFiles; + + SelectionState({ + Key? key, + required this.selectedFiles, + required Widget child, + }) : super(key: key, child: child); + + static SelectionState? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(covariant InheritedWidget oldWidget) { + return false; + } +}