mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	Move selection logic to asset grid class
This commit is contained in:
		
							parent
							
								
									347ac70063
								
							
						
					
					
						commit
						a117e897ca
					
				@ -8,11 +8,17 @@ class DailyTitleText extends ConsumerWidget {
 | 
				
			|||||||
  const DailyTitleText({
 | 
					  const DailyTitleText({
 | 
				
			||||||
    Key? key,
 | 
					    Key? key,
 | 
				
			||||||
    required this.isoDate,
 | 
					    required this.isoDate,
 | 
				
			||||||
    required this.assetGroup,
 | 
					    required this.multiselectEnabled,
 | 
				
			||||||
 | 
					    required this.onSelect,
 | 
				
			||||||
 | 
					    required this.onDeselect,
 | 
				
			||||||
 | 
					    required this.selected,
 | 
				
			||||||
  }) : super(key: key);
 | 
					  }) : super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final String isoDate;
 | 
					  final String isoDate;
 | 
				
			||||||
  final List<AssetResponseDto> assetGroup;
 | 
					  final bool multiselectEnabled;
 | 
				
			||||||
 | 
					  final Function onSelect;
 | 
				
			||||||
 | 
					  final Function onDeselect;
 | 
				
			||||||
 | 
					  final bool selected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
@ -23,51 +29,12 @@ class DailyTitleText extends ConsumerWidget {
 | 
				
			|||||||
        : "daily_title_text_date_year".tr();
 | 
					        : "daily_title_text_date_year".tr();
 | 
				
			||||||
    var dateText =
 | 
					    var dateText =
 | 
				
			||||||
        DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
 | 
					        DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
 | 
				
			||||||
    var isMultiSelectEnable =
 | 
					 | 
				
			||||||
        ref.watch(homePageStateProvider).isMultiSelectEnable;
 | 
					 | 
				
			||||||
    var selectedDateGroup = ref.watch(homePageStateProvider).selectedDateGroup;
 | 
					 | 
				
			||||||
    var selectedItems = ref.watch(homePageStateProvider).selectedItems;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void _handleTitleIconClick() {
 | 
					    void handleTitleIconClick() {
 | 
				
			||||||
      if (isMultiSelectEnable &&
 | 
					      if (selected) {
 | 
				
			||||||
          selectedDateGroup.contains(dateText) &&
 | 
					        onDeselect();
 | 
				
			||||||
          selectedDateGroup.length == 1 &&
 | 
					 | 
				
			||||||
          selectedItems.length <= assetGroup.length) {
 | 
					 | 
				
			||||||
        // Multi select is active - click again on the icon while it is the only active group -> disable multi select
 | 
					 | 
				
			||||||
        ref.watch(homePageStateProvider.notifier).disableMultiSelect();
 | 
					 | 
				
			||||||
      } else if (isMultiSelectEnable &&
 | 
					 | 
				
			||||||
          selectedDateGroup.contains(dateText) &&
 | 
					 | 
				
			||||||
          selectedItems.length != assetGroup.length) {
 | 
					 | 
				
			||||||
        // Multi select is active - click again on the icon while it is not the only active group -> remove that group from selected group/items
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .removeSelectedDateGroup(dateText);
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .removeMultipleSelectedItem(assetGroup);
 | 
					 | 
				
			||||||
      } else if (isMultiSelectEnable &&
 | 
					 | 
				
			||||||
          selectedDateGroup.contains(dateText) &&
 | 
					 | 
				
			||||||
          selectedDateGroup.length > 1) {
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .removeSelectedDateGroup(dateText);
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .removeMultipleSelectedItem(assetGroup);
 | 
					 | 
				
			||||||
      } else if (isMultiSelectEnable && !selectedDateGroup.contains(dateText)) {
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .addSelectedDateGroup(dateText);
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .addMultipleSelectedItems(assetGroup);
 | 
					 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        ref
 | 
					        onSelect();
 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .enableMultiSelect(assetGroup.toSet());
 | 
					 | 
				
			||||||
        ref
 | 
					 | 
				
			||||||
            .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
            .addSelectedDateGroup(dateText);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -89,8 +56,8 @@ class DailyTitleText extends ConsumerWidget {
 | 
				
			|||||||
          ),
 | 
					          ),
 | 
				
			||||||
          const Spacer(),
 | 
					          const Spacer(),
 | 
				
			||||||
          GestureDetector(
 | 
					          GestureDetector(
 | 
				
			||||||
            onTap: _handleTitleIconClick,
 | 
					            onTap: handleTitleIconClick,
 | 
				
			||||||
            child: isMultiSelectEnable && selectedDateGroup.contains(dateText)
 | 
					            child: multiselectEnabled && selected
 | 
				
			||||||
                ? Icon(
 | 
					                ? Icon(
 | 
				
			||||||
                    Icons.check_circle_rounded,
 | 
					                    Icons.check_circle_rounded,
 | 
				
			||||||
                    color: Theme.of(context).primaryColor,
 | 
					                    color: Theme.of(context).primaryColor,
 | 
				
			||||||
 | 
				
			|||||||
@ -13,11 +13,8 @@ class DisableMultiSelectButton extends ConsumerWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
    return Positioned(
 | 
					    return Padding(
 | 
				
			||||||
      top: 10,
 | 
					        padding: const EdgeInsets.only(left: 16.0, top: 15),
 | 
				
			||||||
      left: 0,
 | 
					 | 
				
			||||||
      child: Padding(
 | 
					 | 
				
			||||||
        padding: const EdgeInsets.only(left: 16.0, top: 46),
 | 
					 | 
				
			||||||
        child: Padding(
 | 
					        child: Padding(
 | 
				
			||||||
          padding: const EdgeInsets.symmetric(horizontal: 4.0),
 | 
					          padding: const EdgeInsets.symmetric(horizontal: 4.0),
 | 
				
			||||||
          child: ElevatedButton.icon(
 | 
					          child: ElevatedButton.icon(
 | 
				
			||||||
@ -34,7 +31,6 @@ class DisableMultiSelectButton extends ConsumerWidget {
 | 
				
			|||||||
            ),
 | 
					            ),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import 'dart:collection';
 | 
				
			||||||
import 'dart:math';
 | 
					import 'dart:math';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:collection/collection.dart';
 | 
					import 'package:collection/collection.dart';
 | 
				
			||||||
@ -5,35 +6,27 @@ import 'package:easy_localization/easy_localization.dart';
 | 
				
			|||||||
import 'package:flutter/cupertino.dart';
 | 
					import 'package:flutter/cupertino.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:flutter/src/widgets/framework.dart';
 | 
					import 'package:flutter/src/widgets/framework.dart';
 | 
				
			||||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/thumbnail_image.dart';
 | 
					 | 
				
			||||||
import 'package:openapi/api.dart';
 | 
					import 'package:openapi/api.dart';
 | 
				
			||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
 | 
					import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
 | 
				
			||||||
import 'asset_grid_data_structure.dart';
 | 
					import 'asset_grid_data_structure.dart';
 | 
				
			||||||
import 'daily_title_text.dart';
 | 
					import 'daily_title_text.dart';
 | 
				
			||||||
 | 
					import 'disable_multi_select_button.dart';
 | 
				
			||||||
import 'draggable_scrollbar_custom.dart';
 | 
					import 'draggable_scrollbar_custom.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ImmichAssetGrid extends HookConsumerWidget {
 | 
					typedef ImmichAssetGridSelectionListener = void Function(bool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImmichAssetGridState extends State<ImmichAssetGrid> {
 | 
				
			||||||
  final ItemScrollController _itemScrollController = ItemScrollController();
 | 
					  final ItemScrollController _itemScrollController = ItemScrollController();
 | 
				
			||||||
  final ItemPositionsListener _itemPositionsListener =
 | 
					  final ItemPositionsListener _itemPositionsListener =
 | 
				
			||||||
  ItemPositionsListener.create();
 | 
					  ItemPositionsListener.create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final List<RenderAssetGridElement> renderList;
 | 
					  bool _scrolling = false;
 | 
				
			||||||
  final int assetsPerRow;
 | 
					  bool _multiselect = false;
 | 
				
			||||||
  final double margin;
 | 
					  Set<String> _selectedAssets = HashSet();
 | 
				
			||||||
  final bool showStorageIndicator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ImmichAssetGrid({
 | 
					 | 
				
			||||||
    super.key,
 | 
					 | 
				
			||||||
    required this.renderList,
 | 
					 | 
				
			||||||
    required this.assetsPerRow,
 | 
					 | 
				
			||||||
    required this.showStorageIndicator,
 | 
					 | 
				
			||||||
    this.margin = 5.0,
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  List<AssetResponseDto> get _assets {
 | 
					  List<AssetResponseDto> get _assets {
 | 
				
			||||||
    return renderList
 | 
					    return widget.renderList
 | 
				
			||||||
        .map((e) {
 | 
					        .map((e) {
 | 
				
			||||||
          if (e.type == RenderAssetGridElementType.assetRow) {
 | 
					          if (e.type == RenderAssetGridElementType.assetRow) {
 | 
				
			||||||
            return e.assetRow!.assets;
 | 
					            return e.assetRow!.assets;
 | 
				
			||||||
@ -45,9 +38,48 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
        .toList();
 | 
					        .toList();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					  void _selectAssets(List<AssetResponseDto> assets) {
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!_multiselect) {
 | 
				
			||||||
 | 
					        _multiselect = true;
 | 
				
			||||||
 | 
					        widget.listener?.call(true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (var e in assets) {
 | 
				
			||||||
 | 
					        _selectedAssets.add(e.id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _deselectAssets(List<AssetResponseDto> assets) {
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					      for (var e in assets) {
 | 
				
			||||||
 | 
					        _selectedAssets.remove(e.id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (_selectedAssets.isEmpty) {
 | 
				
			||||||
 | 
					        _multiselect = false;
 | 
				
			||||||
 | 
					        widget.listener?.call(false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _deselectAll() {
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					      _multiselect = false;
 | 
				
			||||||
 | 
					      _selectedAssets.clear();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    widget.listener?.call(false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool _allAssetsSelected(List<AssetResponseDto> assets) {
 | 
				
			||||||
 | 
					    return _multiselect && assets.firstWhereOrNull((e) => !_selectedAssets.contains(e.id)) == null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  double _getItemSize(BuildContext context) {
 | 
					  double _getItemSize(BuildContext context) {
 | 
				
			||||||
    return MediaQuery.of(context).size.width / assetsPerRow -
 | 
					    return MediaQuery.of(context).size.width / widget.assetsPerRow -
 | 
				
			||||||
        margin * (assetsPerRow - 1) / assetsPerRow;
 | 
					        widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _buildThumbnailOrPlaceholder(
 | 
					  Widget _buildThumbnailOrPlaceholder(
 | 
				
			||||||
@ -60,7 +92,10 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
    return ThumbnailImage(
 | 
					    return ThumbnailImage(
 | 
				
			||||||
      asset: asset,
 | 
					      asset: asset,
 | 
				
			||||||
      assetList: _assets,
 | 
					      assetList: _assets,
 | 
				
			||||||
      showStorageIndicator: showStorageIndicator,
 | 
					      multiselectEnabled: _multiselect,
 | 
				
			||||||
 | 
					      isSelected: _selectedAssets.contains(asset.id),
 | 
				
			||||||
 | 
					      onSelect: () => _selectAssets([asset]),
 | 
				
			||||||
 | 
					      onDeselect: () => _deselectAssets([asset]),
 | 
				
			||||||
      useGrayBoxPlaceholder: true,
 | 
					      useGrayBoxPlaceholder: true,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -78,7 +113,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
          key: Key("asset-${asset.id}"),
 | 
					          key: Key("asset-${asset.id}"),
 | 
				
			||||||
          width: size,
 | 
					          width: size,
 | 
				
			||||||
          height: size,
 | 
					          height: size,
 | 
				
			||||||
          margin: EdgeInsets.only(top: margin, right: last ? 0.0 : margin),
 | 
					          margin: EdgeInsets.only(top: widget.margin, right: last ? 0.0 : widget.margin),
 | 
				
			||||||
          child: _buildThumbnailOrPlaceholder(asset, scrolling),
 | 
					          child: _buildThumbnailOrPlaceholder(asset, scrolling),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }).toList(),
 | 
					      }).toList(),
 | 
				
			||||||
@ -89,7 +124,10 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
      BuildContext context, String title, List<AssetResponseDto> assets) {
 | 
					      BuildContext context, String title, List<AssetResponseDto> assets) {
 | 
				
			||||||
    return DailyTitleText(
 | 
					    return DailyTitleText(
 | 
				
			||||||
      isoDate: title,
 | 
					      isoDate: title,
 | 
				
			||||||
      assetGroup: assets,
 | 
					      multiselectEnabled: _multiselect,
 | 
				
			||||||
 | 
					      onSelect: () => _selectAssets(assets),
 | 
				
			||||||
 | 
					      onDeselect: () => _deselectAssets(assets),
 | 
				
			||||||
 | 
					      selected: _allAssetsSelected(assets),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -111,22 +149,22 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _itemBuilder(BuildContext c, int position, bool scrolling) {
 | 
					  Widget _itemBuilder(BuildContext c, int position) {
 | 
				
			||||||
    final item = renderList[position];
 | 
					    final item = widget.renderList[position];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (item.type == RenderAssetGridElementType.dayTitle) {
 | 
					    if (item.type == RenderAssetGridElementType.dayTitle) {
 | 
				
			||||||
      return _buildTitle(c, item.title!, item.relatedAssetList!);
 | 
					      return _buildTitle(c, item.title!, item.relatedAssetList!);
 | 
				
			||||||
    } else if (item.type == RenderAssetGridElementType.monthTitle) {
 | 
					    } else if (item.type == RenderAssetGridElementType.monthTitle) {
 | 
				
			||||||
      return _buildMonthTitle(c, item.title!);
 | 
					      return _buildMonthTitle(c, item.title!);
 | 
				
			||||||
    } else if (item.type == RenderAssetGridElementType.assetRow) {
 | 
					    } else if (item.type == RenderAssetGridElementType.assetRow) {
 | 
				
			||||||
      return _buildAssetRow(c, item.assetRow!, scrolling);
 | 
					      return _buildAssetRow(c, item.assetRow!, _scrolling);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return const Text("Invalid widget type!");
 | 
					    return const Text("Invalid widget type!");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Text _labelBuilder(int pos) {
 | 
					  Text _labelBuilder(int pos) {
 | 
				
			||||||
    final date = renderList[pos].date;
 | 
					    final date = widget.renderList[pos].date;
 | 
				
			||||||
    return Text(DateFormat.yMMMd().format(date),
 | 
					    return Text(DateFormat.yMMMd().format(date),
 | 
				
			||||||
      style: const TextStyle(
 | 
					      style: const TextStyle(
 | 
				
			||||||
        color: Colors.white,
 | 
					        color: Colors.white,
 | 
				
			||||||
@ -135,26 +173,27 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildMultiSelectIndicator() {
 | 
				
			||||||
 | 
					    return DisableMultiSelectButton(
 | 
				
			||||||
 | 
					      onPressed: () => _deselectAll(),
 | 
				
			||||||
 | 
					      selectedItemCount: _selectedAssets.length,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  Widget _buildAssetGrid() {
 | 
				
			||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					 | 
				
			||||||
    final scrolling = useState(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    final useDragScrolling = _assets.length > 100;
 | 
					    final useDragScrolling = _assets.length > 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void dragScrolling(bool active) {
 | 
					    void dragScrolling(bool active) {
 | 
				
			||||||
      scrolling.value = active;
 | 
					      setState(() {
 | 
				
			||||||
    }
 | 
					        _scrolling = active;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    Widget itemBuilder(BuildContext c, int position) {
 | 
					 | 
				
			||||||
      return _itemBuilder(c, position, scrolling.value);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final listWidget = ScrollablePositionedList.builder(
 | 
					    final listWidget = ScrollablePositionedList.builder(
 | 
				
			||||||
      itemBuilder: itemBuilder,
 | 
					      itemBuilder: _itemBuilder,
 | 
				
			||||||
      itemPositionsListener: _itemPositionsListener,
 | 
					      itemPositionsListener: _itemPositionsListener,
 | 
				
			||||||
      itemScrollController: _itemScrollController,
 | 
					      itemScrollController: _itemScrollController,
 | 
				
			||||||
      itemCount: renderList.length,
 | 
					      itemCount: widget.renderList.length,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!useDragScrolling) {
 | 
					    if (!useDragScrolling) {
 | 
				
			||||||
@ -173,4 +212,37 @@ class ImmichAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
      child: listWidget,
 | 
					      child: listWidget,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return Stack(
 | 
				
			||||||
 | 
					      children: [
 | 
				
			||||||
 | 
					        _buildAssetGrid(),
 | 
				
			||||||
 | 
					        if (_multiselect) _buildMultiSelectIndicator(),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImmichAssetGrid extends StatefulWidget {
 | 
				
			||||||
 | 
					  final List<RenderAssetGridElement> renderList;
 | 
				
			||||||
 | 
					  final int assetsPerRow;
 | 
				
			||||||
 | 
					  final double margin;
 | 
				
			||||||
 | 
					  final bool showStorageIndicator;
 | 
				
			||||||
 | 
					  final ImmichAssetGridSelectionListener? listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ImmichAssetGrid({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.renderList,
 | 
				
			||||||
 | 
					    required this.assetsPerRow,
 | 
				
			||||||
 | 
					    required this.showStorageIndicator,
 | 
				
			||||||
 | 
					    this.listener,
 | 
				
			||||||
 | 
					    this.margin = 5.0,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  State<StatefulWidget> createState() {
 | 
				
			||||||
 | 
					    return ImmichAssetGridState();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import 'package:auto_route/auto_route.dart';
 | 
					import 'package:auto_route/auto_route.dart';
 | 
				
			||||||
import 'package:cached_network_image/cached_network_image.dart';
 | 
					import 'package:cached_network_image/cached_network_image.dart';
 | 
				
			||||||
 | 
					import 'package:collection/collection.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:flutter/services.dart';
 | 
					import 'package:flutter/services.dart';
 | 
				
			||||||
import 'package:hive_flutter/hive_flutter.dart';
 | 
					import 'package:hive_flutter/hive_flutter.dart';
 | 
				
			||||||
@ -16,6 +17,10 @@ class ThumbnailImage extends HookConsumerWidget {
 | 
				
			|||||||
  final List<AssetResponseDto> assetList;
 | 
					  final List<AssetResponseDto> assetList;
 | 
				
			||||||
  final bool showStorageIndicator;
 | 
					  final bool showStorageIndicator;
 | 
				
			||||||
  final bool useGrayBoxPlaceholder;
 | 
					  final bool useGrayBoxPlaceholder;
 | 
				
			||||||
 | 
					  final bool isSelected;
 | 
				
			||||||
 | 
					  final bool multiselectEnabled;
 | 
				
			||||||
 | 
					  final Function? onSelect;
 | 
				
			||||||
 | 
					  final Function? onDeselect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const ThumbnailImage({
 | 
					  const ThumbnailImage({
 | 
				
			||||||
    Key? key,
 | 
					    Key? key,
 | 
				
			||||||
@ -23,19 +28,21 @@ class ThumbnailImage extends HookConsumerWidget {
 | 
				
			|||||||
    required this.assetList,
 | 
					    required this.assetList,
 | 
				
			||||||
    this.showStorageIndicator = true,
 | 
					    this.showStorageIndicator = true,
 | 
				
			||||||
    this.useGrayBoxPlaceholder = false,
 | 
					    this.useGrayBoxPlaceholder = false,
 | 
				
			||||||
 | 
					    this.isSelected = false,
 | 
				
			||||||
 | 
					    this.multiselectEnabled = false,
 | 
				
			||||||
 | 
					    this.onDeselect,
 | 
				
			||||||
 | 
					    this.onSelect,
 | 
				
			||||||
  }) : super(key: key);
 | 
					  }) : super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
    var box = Hive.box(userInfoBox);
 | 
					    var box = Hive.box(userInfoBox);
 | 
				
			||||||
    var thumbnailRequestUrl = getThumbnailUrl(asset);
 | 
					    var thumbnailRequestUrl = getThumbnailUrl(asset);
 | 
				
			||||||
    var selectedAsset = ref.watch(homePageStateProvider).selectedItems;
 | 
					 | 
				
			||||||
    var isMultiSelectEnable =
 | 
					 | 
				
			||||||
        ref.watch(homePageStateProvider).isMultiSelectEnable;
 | 
					 | 
				
			||||||
    var deviceId = ref.watch(authenticationProvider).deviceId;
 | 
					    var deviceId = ref.watch(authenticationProvider).deviceId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Widget buildSelectionIcon(AssetResponseDto asset) {
 | 
					    Widget buildSelectionIcon(AssetResponseDto asset) {
 | 
				
			||||||
      if (selectedAsset.contains(asset)) {
 | 
					      if (isSelected) {
 | 
				
			||||||
        return Icon(
 | 
					        return Icon(
 | 
				
			||||||
          Icons.check_circle,
 | 
					          Icons.check_circle,
 | 
				
			||||||
          color: Theme.of(context).primaryColor,
 | 
					          color: Theme.of(context).primaryColor,
 | 
				
			||||||
@ -50,20 +57,12 @@ class ThumbnailImage extends HookConsumerWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return GestureDetector(
 | 
					    return GestureDetector(
 | 
				
			||||||
      onTap: () {
 | 
					      onTap: () {
 | 
				
			||||||
        if (isMultiSelectEnable &&
 | 
					        if (multiselectEnabled) {
 | 
				
			||||||
            selectedAsset.contains(asset) &&
 | 
					          if (isSelected) {
 | 
				
			||||||
            selectedAsset.length == 1) {
 | 
					            onDeselect?.call();
 | 
				
			||||||
          ref.watch(homePageStateProvider.notifier).disableMultiSelect();
 | 
					          } else {
 | 
				
			||||||
        } else if (isMultiSelectEnable &&
 | 
					            onSelect?.call();
 | 
				
			||||||
            selectedAsset.contains(asset) &&
 | 
					          }
 | 
				
			||||||
            selectedAsset.length > 1) {
 | 
					 | 
				
			||||||
          ref
 | 
					 | 
				
			||||||
              .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
              .removeSingleSelectedItem(asset);
 | 
					 | 
				
			||||||
        } else if (isMultiSelectEnable && !selectedAsset.contains(asset)) {
 | 
					 | 
				
			||||||
          ref
 | 
					 | 
				
			||||||
              .watch(homePageStateProvider.notifier)
 | 
					 | 
				
			||||||
              .addSingleSelectedItem(asset);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          AutoRouter.of(context).push(
 | 
					          AutoRouter.of(context).push(
 | 
				
			||||||
            GalleryViewerRoute(
 | 
					            GalleryViewerRoute(
 | 
				
			||||||
@ -74,8 +73,7 @@ class ThumbnailImage extends HookConsumerWidget {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      onLongPress: () {
 | 
					      onLongPress: () {
 | 
				
			||||||
        // Enable multi select function
 | 
					        onSelect?.call();
 | 
				
			||||||
        ref.watch(homePageStateProvider.notifier).enableMultiSelect({asset});
 | 
					 | 
				
			||||||
        HapticFeedback.heavyImpact();
 | 
					        HapticFeedback.heavyImpact();
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      child: Hero(
 | 
					      child: Hero(
 | 
				
			||||||
@ -84,7 +82,7 @@ class ThumbnailImage extends HookConsumerWidget {
 | 
				
			|||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            Container(
 | 
					            Container(
 | 
				
			||||||
              decoration: BoxDecoration(
 | 
					              decoration: BoxDecoration(
 | 
				
			||||||
                border: isMultiSelectEnable && selectedAsset.contains(asset)
 | 
					                border: multiselectEnabled && isSelected
 | 
				
			||||||
                    ? Border.all(
 | 
					                    ? Border.all(
 | 
				
			||||||
                        color: Theme.of(context).primaryColorLight,
 | 
					                        color: Theme.of(context).primaryColorLight,
 | 
				
			||||||
                        width: 10,
 | 
					                        width: 10,
 | 
				
			||||||
@ -128,7 +126,7 @@ class ThumbnailImage extends HookConsumerWidget {
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            if (isMultiSelectEnable)
 | 
					            if (multiselectEnabled)
 | 
				
			||||||
              Padding(
 | 
					              Padding(
 | 
				
			||||||
                padding: const EdgeInsets.all(3.0),
 | 
					                padding: const EdgeInsets.all(3.0),
 | 
				
			||||||
                child: Align(
 | 
					                child: Align(
 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/thumbnail_image.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
 | 
				
			||||||
import 'package:openapi/api.dart';
 | 
					import 'package:openapi/api.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ignore: must_be_immutable
 | 
					// ignore: must_be_immutable
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,6 @@ import 'package:immich_mobile/modules/home/providers/home_page_render_list_provi
 | 
				
			|||||||
import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
 | 
					import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/control_bottom_app_bar.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/control_bottom_app_bar.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/disable_multi_select_button.dart';
 | 
					 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/profile_drawer/profile_drawer.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/profile_drawer/profile_drawer.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
					import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
				
			||||||
@ -20,12 +19,9 @@ class HomePage extends HookConsumerWidget {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
    final appSettingService = ref.watch(appSettingsServiceProvider);
 | 
					    final appSettingService = ref.watch(appSettingsServiceProvider);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    var renderList = ref.watch(renderListProvider);
 | 
					    var renderList = ref.watch(renderListProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var isMultiSelectEnable =
 | 
					    final multiselectEnabled = useState(false);
 | 
				
			||||||
        ref.watch(homePageStateProvider).isMultiSelectEnable;
 | 
					 | 
				
			||||||
    var homePageState = ref.watch(homePageStateProvider);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(
 | 
					    useEffect(
 | 
				
			||||||
      () {
 | 
					      () {
 | 
				
			||||||
@ -41,16 +37,9 @@ class HomePage extends HookConsumerWidget {
 | 
				
			|||||||
      ref.read(assetProvider.notifier).getAllAsset();
 | 
					      ref.read(assetProvider.notifier).getAllAsset();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buildSelectedItemCountIndicator() {
 | 
					 | 
				
			||||||
      return DisableMultiSelectButton(
 | 
					 | 
				
			||||||
        onPressed: ref.watch(homePageStateProvider.notifier).disableMultiSelect,
 | 
					 | 
				
			||||||
        selectedItemCount: homePageState.selectedItems.length,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Widget buildBody() {
 | 
					    Widget buildBody() {
 | 
				
			||||||
      buildSliverAppBar() {
 | 
					      buildSliverAppBar() {
 | 
				
			||||||
        return isMultiSelectEnable
 | 
					        return multiselectEnabled.value
 | 
				
			||||||
            ? const SliverToBoxAdapter(
 | 
					            ? const SliverToBoxAdapter(
 | 
				
			||||||
                child: SizedBox(
 | 
					                child: SizedBox(
 | 
				
			||||||
                  height: 70,
 | 
					                  height: 70,
 | 
				
			||||||
@ -62,9 +51,13 @@ class HomePage extends HookConsumerWidget {
 | 
				
			|||||||
              );
 | 
					              );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      void selectionListener(bool multiselect) {
 | 
				
			||||||
 | 
					        multiselectEnabled.value = multiselect;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return SafeArea(
 | 
					      return SafeArea(
 | 
				
			||||||
        bottom: !isMultiSelectEnable,
 | 
					        bottom: !multiselectEnabled.value,
 | 
				
			||||||
        top: !isMultiSelectEnable,
 | 
					        top: !multiselectEnabled.value,
 | 
				
			||||||
        child: Stack(
 | 
					        child: Stack(
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            CustomScrollView(
 | 
					            CustomScrollView(
 | 
				
			||||||
@ -80,10 +73,10 @@ class HomePage extends HookConsumerWidget {
 | 
				
			|||||||
                    appSettingService.getSetting(AppSettingsEnum.tilesPerRow),
 | 
					                    appSettingService.getSetting(AppSettingsEnum.tilesPerRow),
 | 
				
			||||||
                showStorageIndicator: appSettingService
 | 
					                showStorageIndicator: appSettingService
 | 
				
			||||||
                    .getSetting(AppSettingsEnum.storageIndicator),
 | 
					                    .getSetting(AppSettingsEnum.storageIndicator),
 | 
				
			||||||
 | 
					                listener: selectionListener,
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            if (isMultiSelectEnable) ...[
 | 
					            if (multiselectEnabled.value) ...[
 | 
				
			||||||
              buildSelectedItemCountIndicator(),
 | 
					 | 
				
			||||||
              const ControlBottomAppBar(),
 | 
					              const ControlBottomAppBar(),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user