forked from Cutlery/immich
		
	feat(mobile): Home screen customization options (#1563)
* Try staggered layout for home page * Introduce setting for dynamic layout * Fix some provider related bugs * Make asset grouping configurable * Add translation keys, refactor group title * Rename enum values * Fix enum names * Reformat long if statement * Fix timezone related bug * Minor clean up * Fix unit test * Add second assets check back to home screen
This commit is contained in:
		
							parent
							
								
									911c35a7f1
								
							
						
					
					
						commit
						fd13265131
					
				@ -12,6 +12,10 @@
 | 
				
			|||||||
  "album_viewer_appbar_share_leave": "Leave album",
 | 
					  "album_viewer_appbar_share_leave": "Leave album",
 | 
				
			||||||
  "album_viewer_appbar_share_remove": "Remove from album",
 | 
					  "album_viewer_appbar_share_remove": "Remove from album",
 | 
				
			||||||
  "album_viewer_page_share_add_users": "Add users",
 | 
					  "album_viewer_page_share_add_users": "Add users",
 | 
				
			||||||
 | 
					  "asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
 | 
				
			||||||
 | 
					  "asset_list_layout_settings_group_by": "Group assets by",
 | 
				
			||||||
 | 
					  "asset_list_layout_settings_group_by_month_day": "Month + day",
 | 
				
			||||||
 | 
					  "asset_list_layout_settings_group_by_month": "Month",
 | 
				
			||||||
  "asset_list_settings_subtitle": "Photo grid layout settings",
 | 
					  "asset_list_settings_subtitle": "Photo grid layout settings",
 | 
				
			||||||
  "asset_list_settings_title": "Photo Grid",
 | 
					  "asset_list_settings_title": "Photo Grid",
 | 
				
			||||||
  "backup_album_selection_page_albums_device": "Albums on device ({})",
 | 
					  "backup_album_selection_page_albums_device": "Albums on device ({})",
 | 
				
			||||||
@ -199,4 +203,4 @@
 | 
				
			|||||||
  "version_announcement_overlay_text_2": "please take your time to visit the ",
 | 
					  "version_announcement_overlay_text_2": "please take your time to visit the ",
 | 
				
			||||||
  "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
 | 
					  "version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
 | 
				
			||||||
  "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
 | 
					  "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import 'dart:math';
 | 
					import 'dart:math';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:collection/collection.dart';
 | 
				
			||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
import 'package:flutter/foundation.dart';
 | 
					import 'package:flutter/foundation.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.dart';
 | 
				
			||||||
@ -9,14 +10,15 @@ final log = Logger('AssetGridDataStructure');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
enum RenderAssetGridElementType {
 | 
					enum RenderAssetGridElementType {
 | 
				
			||||||
  assetRow,
 | 
					  assetRow,
 | 
				
			||||||
  dayTitle,
 | 
					  groupDividerTitle,
 | 
				
			||||||
  monthTitle;
 | 
					  monthTitle;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RenderAssetGridRow {
 | 
					class RenderAssetGridRow {
 | 
				
			||||||
  final List<Asset> assets;
 | 
					  final List<Asset> assets;
 | 
				
			||||||
 | 
					  final List<double> widthDistribution;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RenderAssetGridRow(this.assets);
 | 
					  RenderAssetGridRow(this.assets, this.widthDistribution);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RenderAssetGridElement {
 | 
					class RenderAssetGridElement {
 | 
				
			||||||
@ -35,19 +37,36 @@ class RenderAssetGridElement {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum GroupAssetsBy {
 | 
				
			||||||
 | 
					  day,
 | 
				
			||||||
 | 
					  month;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AssetGridLayoutParameters {
 | 
				
			||||||
 | 
					  final int perRow;
 | 
				
			||||||
 | 
					  final bool dynamicLayout;
 | 
				
			||||||
 | 
					  final GroupAssetsBy groupBy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AssetGridLayoutParameters(
 | 
				
			||||||
 | 
					    this.perRow,
 | 
				
			||||||
 | 
					    this.dynamicLayout,
 | 
				
			||||||
 | 
					    this.groupBy,
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _AssetGroupsToRenderListComputeParameters {
 | 
					class _AssetGroupsToRenderListComputeParameters {
 | 
				
			||||||
  final String monthFormat;
 | 
					  final String monthFormat;
 | 
				
			||||||
  final String dayFormat;
 | 
					  final String dayFormat;
 | 
				
			||||||
  final String dayFormatYear;
 | 
					  final String dayFormatYear;
 | 
				
			||||||
  final Map<String, List<Asset>> groups;
 | 
					  final List<Asset> assets;
 | 
				
			||||||
  final int perRow;
 | 
					  final AssetGridLayoutParameters layout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _AssetGroupsToRenderListComputeParameters(
 | 
					  _AssetGroupsToRenderListComputeParameters(
 | 
				
			||||||
    this.monthFormat,
 | 
					    this.monthFormat,
 | 
				
			||||||
    this.dayFormat,
 | 
					    this.dayFormat,
 | 
				
			||||||
    this.dayFormatYear,
 | 
					    this.dayFormatYear,
 | 
				
			||||||
    this.groups,
 | 
					    this.assets,
 | 
				
			||||||
    this.perRow,
 | 
					    this.layout,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,62 +75,75 @@ class RenderList {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  RenderList(this.elements);
 | 
					  RenderList(this.elements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static Map<String, List<Asset>> _groupAssets(
 | 
				
			||||||
 | 
					    List<Asset> assets,
 | 
				
			||||||
 | 
					    GroupAssetsBy groupBy,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    assets.sortByCompare<DateTime>(
 | 
				
			||||||
 | 
					      (e) => e.createdAt,
 | 
				
			||||||
 | 
					      (a, b) => b.compareTo(a),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (groupBy == GroupAssetsBy.day) {
 | 
				
			||||||
 | 
					      return assets.groupListsBy(
 | 
				
			||||||
 | 
					        (element) => DateFormat('y-MM-dd').format(element.createdAt.toLocal()),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else if (groupBy == GroupAssetsBy.month) {
 | 
				
			||||||
 | 
					      return assets.groupListsBy(
 | 
				
			||||||
 | 
					        (element) => DateFormat('y-MM').format(element.createdAt.toLocal()),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static Future<RenderList> _processAssetGroupData(
 | 
					  static Future<RenderList> _processAssetGroupData(
 | 
				
			||||||
    _AssetGroupsToRenderListComputeParameters data,
 | 
					    _AssetGroupsToRenderListComputeParameters data,
 | 
				
			||||||
  ) async {
 | 
					  ) async {
 | 
				
			||||||
    final monthFormat = DateFormat(data.monthFormat);
 | 
					    final monthFormat = DateFormat(data.monthFormat);
 | 
				
			||||||
    final dayFormatSameYear = DateFormat(data.dayFormat);
 | 
					    final dayFormatSameYear = DateFormat(data.dayFormat);
 | 
				
			||||||
    final dayFormatOtherYear = DateFormat(data.dayFormatYear);
 | 
					    final dayFormatOtherYear = DateFormat(data.dayFormatYear);
 | 
				
			||||||
    final groups = data.groups;
 | 
					    final allAssets = data.assets;
 | 
				
			||||||
    final perRow = data.perRow;
 | 
					    final perRow = data.layout.perRow;
 | 
				
			||||||
 | 
					    final dynamicLayout = data.layout.dynamicLayout;
 | 
				
			||||||
 | 
					    final groupBy = data.layout.groupBy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    List<RenderAssetGridElement> elements = [];
 | 
					    List<RenderAssetGridElement> elements = [];
 | 
				
			||||||
    DateTime? lastDate;
 | 
					    DateTime? lastDate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final groups = _groupAssets(allAssets, groupBy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    groups.forEach((groupName, assets) {
 | 
					    groups.forEach((groupName, assets) {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        final date = DateTime.parse(groupName);
 | 
					        final date = assets.first.createdAt.toLocal();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (lastDate == null || lastDate!.month != date.month) {
 | 
					 | 
				
			||||||
          // Month title
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          var monthTitleText = groupName;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          var groupDate = DateTime.tryParse(groupName);
 | 
					 | 
				
			||||||
          if (groupDate != null) {
 | 
					 | 
				
			||||||
            monthTitleText = monthFormat.format(groupDate);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            log.severe("Failed to format date for day title: $groupName");
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Month title
 | 
				
			||||||
 | 
					        if (groupBy == GroupAssetsBy.day &&
 | 
				
			||||||
 | 
					            (lastDate == null || lastDate!.month != date.month)) {
 | 
				
			||||||
          elements.add(
 | 
					          elements.add(
 | 
				
			||||||
            RenderAssetGridElement(
 | 
					            RenderAssetGridElement(
 | 
				
			||||||
              RenderAssetGridElementType.monthTitle,
 | 
					              RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
              title: monthTitleText,
 | 
					              title: monthFormat.format(date),
 | 
				
			||||||
              date: date,
 | 
					              date: date,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add group title
 | 
					        // Group divider title (day or month)
 | 
				
			||||||
        var currentYear = DateTime.now().year;
 | 
					        var formatDate = dayFormatOtherYear;
 | 
				
			||||||
        var groupYear = DateTime.parse(groupName).year;
 | 
					 | 
				
			||||||
        var formatDate =
 | 
					 | 
				
			||||||
            currentYear == groupYear ? dayFormatSameYear : dayFormatOtherYear;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var dateText = groupName;
 | 
					        if (DateTime.now().year == date.year) {
 | 
				
			||||||
 | 
					          formatDate = dayFormatSameYear;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var groupDate = DateTime.tryParse(groupName);
 | 
					        if (groupBy == GroupAssetsBy.month) {
 | 
				
			||||||
        if (groupDate != null) {
 | 
					          formatDate = monthFormat;
 | 
				
			||||||
          dateText = formatDate.format(groupDate);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          log.severe("Failed to format date for day title: $groupName");
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elements.add(
 | 
					        elements.add(
 | 
				
			||||||
          RenderAssetGridElement(
 | 
					          RenderAssetGridElement(
 | 
				
			||||||
            RenderAssetGridElementType.dayTitle,
 | 
					            RenderAssetGridElementType.groupDividerTitle,
 | 
				
			||||||
            title: dateText,
 | 
					            title: formatDate.format(date),
 | 
				
			||||||
            date: date,
 | 
					            date: date,
 | 
				
			||||||
            relatedAssetList: assets,
 | 
					            relatedAssetList: assets,
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
@ -121,12 +153,37 @@ class RenderList {
 | 
				
			|||||||
        int cursor = 0;
 | 
					        int cursor = 0;
 | 
				
			||||||
        while (cursor < assets.length) {
 | 
					        while (cursor < assets.length) {
 | 
				
			||||||
          int rowElements = min(assets.length - cursor, perRow);
 | 
					          int rowElements = min(assets.length - cursor, perRow);
 | 
				
			||||||
 | 
					          final rowAssets = assets.sublist(cursor, cursor + rowElements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Default: All assets have the same width
 | 
				
			||||||
 | 
					          var widthDistribution = List.filled(rowElements, 1.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (dynamicLayout) {
 | 
				
			||||||
 | 
					            final aspectRatios =
 | 
				
			||||||
 | 
					                rowAssets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList();
 | 
				
			||||||
 | 
					            final meanAspectRatio = aspectRatios.sum / rowElements;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 1: mean width
 | 
				
			||||||
 | 
					            // 0.5: width < mean - threshold
 | 
				
			||||||
 | 
					            // 1.5: width > mean + threshold
 | 
				
			||||||
 | 
					            final arConfiguration = aspectRatios.map((e) {
 | 
				
			||||||
 | 
					              if (e - meanAspectRatio > 0.3) return 1.5;
 | 
				
			||||||
 | 
					              if (e - meanAspectRatio < -0.3) return 0.5;
 | 
				
			||||||
 | 
					              return 1.0;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Normalize:
 | 
				
			||||||
 | 
					            final sum = arConfiguration.sum;
 | 
				
			||||||
 | 
					            widthDistribution =
 | 
				
			||||||
 | 
					                arConfiguration.map((e) => (e * rowElements) / sum).toList();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          final rowElement = RenderAssetGridElement(
 | 
					          final rowElement = RenderAssetGridElement(
 | 
				
			||||||
            RenderAssetGridElementType.assetRow,
 | 
					            RenderAssetGridElementType.assetRow,
 | 
				
			||||||
            date: date,
 | 
					            date: date,
 | 
				
			||||||
            assetRow: RenderAssetGridRow(
 | 
					            assetRow: RenderAssetGridRow(
 | 
				
			||||||
              assets.sublist(cursor, cursor + rowElements),
 | 
					              rowAssets,
 | 
				
			||||||
 | 
					              widthDistribution,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -143,9 +200,9 @@ class RenderList {
 | 
				
			|||||||
    return RenderList(elements);
 | 
					    return RenderList(elements);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static Future<RenderList> fromAssetGroups(
 | 
					  static Future<RenderList> fromAssets(
 | 
				
			||||||
    Map<String, List<Asset>> assetGroups,
 | 
					    List<Asset> assets,
 | 
				
			||||||
    int assetsPerRow,
 | 
					    AssetGridLayoutParameters layout,
 | 
				
			||||||
  ) async {
 | 
					  ) async {
 | 
				
			||||||
    // Compute only allows for one parameter. Therefore we pass all parameters in a map
 | 
					    // Compute only allows for one parameter. Therefore we pass all parameters in a map
 | 
				
			||||||
    return compute(
 | 
					    return compute(
 | 
				
			||||||
@ -154,8 +211,8 @@ class RenderList {
 | 
				
			|||||||
        "monthly_title_text_date_format".tr(),
 | 
					        "monthly_title_text_date_format".tr(),
 | 
				
			||||||
        "daily_title_text_date".tr(),
 | 
					        "daily_title_text_date".tr(),
 | 
				
			||||||
        "daily_title_text_date_year".tr(),
 | 
					        "daily_title_text_date_year".tr(),
 | 
				
			||||||
        assetGroups,
 | 
					        assets,
 | 
				
			||||||
        assetsPerRow,
 | 
					        layout,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DailyTitleText extends ConsumerWidget {
 | 
					class GroupDividerTitle extends ConsumerWidget {
 | 
				
			||||||
  const DailyTitleText({
 | 
					  const GroupDividerTitle({
 | 
				
			||||||
    Key? key,
 | 
					    Key? key,
 | 
				
			||||||
    required this.text,
 | 
					    required this.text,
 | 
				
			||||||
    required this.multiselectEnabled,
 | 
					    required this.multiselectEnabled,
 | 
				
			||||||
@ -7,7 +7,7 @@ import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
 | 
				
			|||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.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 'group_divider_title.dart';
 | 
				
			||||||
import 'disable_multi_select_button.dart';
 | 
					import 'disable_multi_select_button.dart';
 | 
				
			||||||
import 'draggable_scrollbar_custom.dart';
 | 
					import 'draggable_scrollbar_custom.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,12 +99,12 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
 | 
				
			|||||||
          widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow;
 | 
					          widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow;
 | 
				
			||||||
        return Row(
 | 
					        return Row(
 | 
				
			||||||
          key: Key("asset-row-${row.assets.first.id}"),
 | 
					          key: Key("asset-row-${row.assets.first.id}"),
 | 
				
			||||||
          children: row.assets.map((Asset asset) {
 | 
					          children: row.assets.mapIndexed((int index, Asset asset) {
 | 
				
			||||||
            bool last = asset.id == row.assets.last.id;
 | 
					            bool last = asset.id == row.assets.last.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Container(
 | 
					            return Container(
 | 
				
			||||||
              key: Key("asset-${asset.id}"),
 | 
					              key: Key("asset-${asset.id}"),
 | 
				
			||||||
              width: size,
 | 
					              width: size * row.widthDistribution[index],
 | 
				
			||||||
              height: size,
 | 
					              height: size,
 | 
				
			||||||
              margin: EdgeInsets.only(
 | 
					              margin: EdgeInsets.only(
 | 
				
			||||||
                top: widget.margin,
 | 
					                top: widget.margin,
 | 
				
			||||||
@ -123,7 +123,7 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
 | 
				
			|||||||
    String title,
 | 
					    String title,
 | 
				
			||||||
    List<Asset> assets,
 | 
					    List<Asset> assets,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    return DailyTitleText(
 | 
					    return GroupDividerTitle(
 | 
				
			||||||
      text: title,
 | 
					      text: title,
 | 
				
			||||||
      multiselectEnabled: widget.selectionActive,
 | 
					      multiselectEnabled: widget.selectionActive,
 | 
				
			||||||
      onSelect: () => _selectAssets(assets),
 | 
					      onSelect: () => _selectAssets(assets),
 | 
				
			||||||
@ -150,7 +150,7 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
 | 
				
			|||||||
  Widget _itemBuilder(BuildContext c, int position) {
 | 
					  Widget _itemBuilder(BuildContext c, int position) {
 | 
				
			||||||
    final item = widget.renderList.elements[position];
 | 
					    final item = widget.renderList.elements[position];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (item.type == RenderAssetGridElementType.dayTitle) {
 | 
					    if (item.type == RenderAssetGridElementType.groupDividerTitle) {
 | 
				
			||||||
      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!);
 | 
				
			||||||
 | 
				
			|||||||
@ -220,8 +220,8 @@ class HomePage extends HookConsumerWidget {
 | 
				
			|||||||
        top: true,
 | 
					        top: true,
 | 
				
			||||||
        child: Stack(
 | 
					        child: Stack(
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            ref.watch(assetProvider).renderList == null ||
 | 
					            ref.watch(assetProvider).renderList == null
 | 
				
			||||||
                    ref.watch(assetProvider).allAssets.isEmpty
 | 
					                || ref.watch(assetProvider).allAssets.isEmpty
 | 
				
			||||||
                ? buildLoadingIndicator()
 | 
					                ? buildLoadingIndicator()
 | 
				
			||||||
                : ImmichAssetGrid(
 | 
					                : ImmichAssetGrid(
 | 
				
			||||||
                    renderList: ref.watch(assetProvider).renderList!,
 | 
					                    renderList: ref.watch(assetProvider).renderList!,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
import 'package:collection/collection.dart';
 | 
					 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
 | 
					import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart';
 | 
					import 'package:immich_mobile/modules/search/models/search_result_page_state.model.dart';
 | 
				
			||||||
@ -7,7 +6,6 @@ import 'package:immich_mobile/modules/search/services/search.service.dart';
 | 
				
			|||||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
					import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
 | 
					import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.dart';
 | 
				
			||||||
import 'package:intl/intl.dart';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> {
 | 
					class SearchResultPageNotifier extends StateNotifier<SearchResultPageState> {
 | 
				
			||||||
  SearchResultPageNotifier(this._searchService)
 | 
					  SearchResultPageNotifier(this._searchService)
 | 
				
			||||||
@ -56,23 +54,16 @@ final searchResultPageProvider =
 | 
				
			|||||||
  return SearchResultPageNotifier(ref.watch(searchServiceProvider));
 | 
					  return SearchResultPageNotifier(ref.watch(searchServiceProvider));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final searchResultGroupByDateTimeProvider = StateProvider((ref) {
 | 
					 | 
				
			||||||
  var assets = ref.watch(searchResultPageProvider).searchResult;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  assets.sortByCompare<DateTime>(
 | 
					 | 
				
			||||||
    (e) => e.createdAt,
 | 
					 | 
				
			||||||
    (a, b) => b.compareTo(a),
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  return assets.groupListsBy(
 | 
					 | 
				
			||||||
    (element) => DateFormat('y-MM-dd').format(element.createdAt.toLocal()),
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
final searchRenderListProvider = FutureProvider((ref) {
 | 
					final searchRenderListProvider = FutureProvider((ref) {
 | 
				
			||||||
  var assetGroups = ref.watch(searchResultGroupByDateTimeProvider);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var settings = ref.watch(appSettingsServiceProvider);
 | 
					  var settings = ref.watch(appSettingsServiceProvider);
 | 
				
			||||||
  final assetsPerRow = settings.getSetting(AppSettingsEnum.tilesPerRow);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return RenderList.fromAssetGroups(assetGroups, assetsPerRow);
 | 
					  final assets = ref.watch(searchResultPageProvider).searchResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final layout = AssetGridLayoutParameters(
 | 
				
			||||||
 | 
					    settings.getSetting(AppSettingsEnum.tilesPerRow),
 | 
				
			||||||
 | 
					    settings.getSetting(AppSettingsEnum.dynamicLayout),
 | 
				
			||||||
 | 
					    GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return RenderList.fromAssets(assets, layout);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,8 @@ enum AppSettingsEnum<T> {
 | 
				
			|||||||
  loadOriginal<bool>("loadOriginal", false),
 | 
					  loadOriginal<bool>("loadOriginal", false),
 | 
				
			||||||
  themeMode<String>("themeMode", "system"), // "light","dark","system"
 | 
					  themeMode<String>("themeMode", "system"), // "light","dark","system"
 | 
				
			||||||
  tilesPerRow<int>("tilesPerRow", 4),
 | 
					  tilesPerRow<int>("tilesPerRow", 4),
 | 
				
			||||||
 | 
					  dynamicLayout<bool>("dynamicLayout", false),
 | 
				
			||||||
 | 
					  groupAssetsBy<int>("groupBy", 0),
 | 
				
			||||||
  uploadErrorNotificationGracePeriod<int>(
 | 
					  uploadErrorNotificationGracePeriod<int>(
 | 
				
			||||||
    "uploadErrorNotificationGracePeriod",
 | 
					    "uploadErrorNotificationGracePeriod",
 | 
				
			||||||
    2,
 | 
					    2,
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_hooks/flutter_hooks.dart';
 | 
				
			||||||
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/shared/providers/asset.provider.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LayoutSettings extends HookConsumerWidget {
 | 
				
			||||||
 | 
					  const LayoutSettings({
 | 
				
			||||||
 | 
					    Key? key,
 | 
				
			||||||
 | 
					  }) : super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
 | 
					    final appSettingService = ref.watch(appSettingsServiceProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final useDynamicLayout = useState(true);
 | 
				
			||||||
 | 
					    final groupBy = useState(GroupAssetsBy.day);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void switchChanged(bool value) {
 | 
				
			||||||
 | 
					      appSettingService.setSetting(AppSettingsEnum.dynamicLayout, value);
 | 
				
			||||||
 | 
					      useDynamicLayout.value = value;
 | 
				
			||||||
 | 
					      ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void changeGroupValue(GroupAssetsBy? value) {
 | 
				
			||||||
 | 
					      if (value != null) {
 | 
				
			||||||
 | 
					        appSettingService.setSetting(AppSettingsEnum.groupAssetsBy, value.index);
 | 
				
			||||||
 | 
					        groupBy.value = value;
 | 
				
			||||||
 | 
					        ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(
 | 
				
			||||||
 | 
					      () {
 | 
				
			||||||
 | 
					        useDynamicLayout.value =
 | 
				
			||||||
 | 
					            appSettingService.getSetting<bool>(AppSettingsEnum.dynamicLayout);
 | 
				
			||||||
 | 
					        groupBy.value =
 | 
				
			||||||
 | 
					            GroupAssetsBy.values[appSettingService.getSetting<int>(AppSettingsEnum.groupAssetsBy)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      [],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Column(
 | 
				
			||||||
 | 
					      children: [
 | 
				
			||||||
 | 
					        SwitchListTile.adaptive(
 | 
				
			||||||
 | 
					          activeColor: Theme.of(context).primaryColor,
 | 
				
			||||||
 | 
					          title: const Text(
 | 
				
			||||||
 | 
					            "asset_list_layout_settings_dynamic_layout_title",
 | 
				
			||||||
 | 
					            style: TextStyle(
 | 
				
			||||||
 | 
					              fontSize: 12,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ).tr(),
 | 
				
			||||||
 | 
					          onChanged: switchChanged,
 | 
				
			||||||
 | 
					          value: useDynamicLayout.value,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        ListTile(
 | 
				
			||||||
 | 
					          title: const Text(
 | 
				
			||||||
 | 
					            "asset_list_layout_settings_group_by",
 | 
				
			||||||
 | 
					            style: TextStyle(
 | 
				
			||||||
 | 
					              fontSize: 12,
 | 
				
			||||||
 | 
					              fontWeight: FontWeight.bold,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ).tr(),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        RadioListTile(
 | 
				
			||||||
 | 
					          activeColor: Theme.of(context).primaryColor,
 | 
				
			||||||
 | 
					          title: const Text(
 | 
				
			||||||
 | 
					            "asset_list_layout_settings_group_by_month_day",
 | 
				
			||||||
 | 
					            style: TextStyle(
 | 
				
			||||||
 | 
					              fontSize: 12,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ).tr(),
 | 
				
			||||||
 | 
					          value: GroupAssetsBy.day,
 | 
				
			||||||
 | 
					          groupValue: groupBy.value,
 | 
				
			||||||
 | 
					          onChanged: changeGroupValue,
 | 
				
			||||||
 | 
					          controlAffinity: ListTileControlAffinity.trailing,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        RadioListTile(
 | 
				
			||||||
 | 
					          activeColor: Theme.of(context).primaryColor,
 | 
				
			||||||
 | 
					          title: const Text(
 | 
				
			||||||
 | 
					            "asset_list_layout_settings_group_by_month",
 | 
				
			||||||
 | 
					            style: TextStyle(
 | 
				
			||||||
 | 
					              fontSize: 12,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ).tr(),
 | 
				
			||||||
 | 
					          value: GroupAssetsBy.month,
 | 
				
			||||||
 | 
					          groupValue: groupBy.value,
 | 
				
			||||||
 | 
					          onChanged: changeGroupValue,
 | 
				
			||||||
 | 
					          controlAffinity: ListTileControlAffinity.trailing,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_layout_settings.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart';
 | 
					import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart';
 | 
				
			||||||
import 'asset_list_tiles_per_row.dart';
 | 
					import 'asset_list_tiles_per_row.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27,6 +28,7 @@ class AssetListSettings extends StatelessWidget {
 | 
				
			|||||||
      children: const [
 | 
					      children: const [
 | 
				
			||||||
        TilesPerRow(),
 | 
					        TilesPerRow(),
 | 
				
			||||||
        StorageIndicator(),
 | 
					        StorageIndicator(),
 | 
				
			||||||
 | 
					        LayoutSettings(),
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -20,8 +20,7 @@ class StorageIndicator extends HookConsumerWidget {
 | 
				
			|||||||
    void switchChanged(bool value) {
 | 
					    void switchChanged(bool value) {
 | 
				
			||||||
      appSettingService.setSetting(AppSettingsEnum.storageIndicator, value);
 | 
					      appSettingService.setSetting(AppSettingsEnum.storageIndicator, value);
 | 
				
			||||||
      showStorageIndicator.value = value;
 | 
					      showStorageIndicator.value = value;
 | 
				
			||||||
 | 
					      ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure();
 | 
				
			||||||
      ref.invalidate(assetProvider);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(
 | 
					    useEffect(
 | 
				
			||||||
 | 
				
			|||||||
@ -23,8 +23,7 @@ class TilesPerRow extends HookConsumerWidget {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void sliderChangedEnd(double _) {
 | 
					    void sliderChangedEnd(double _) {
 | 
				
			||||||
      ref.invalidate(assetProvider);
 | 
					      ref.watch(assetProvider.notifier).rebuildAssetGridDataStructure();
 | 
				
			||||||
      ref.watch(assetProvider.notifier).getAllAsset();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(
 | 
					    useEffect(
 | 
				
			||||||
 | 
				
			|||||||
@ -25,11 +25,15 @@ class AssetsState {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  AssetsState(this.allAssets, {this.renderList});
 | 
					  AssetsState(this.allAssets, {this.renderList});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<AssetsState> withRenderDataStructure(int groupSize) async {
 | 
					  Future<AssetsState> withRenderDataStructure(
 | 
				
			||||||
 | 
					    AssetGridLayoutParameters layout,
 | 
				
			||||||
 | 
					  ) async {
 | 
				
			||||||
    return AssetsState(
 | 
					    return AssetsState(
 | 
				
			||||||
      allAssets,
 | 
					      allAssets,
 | 
				
			||||||
      renderList:
 | 
					      renderList: await RenderList.fromAssets(
 | 
				
			||||||
          await RenderList.fromAssetGroups(await _groupByDate(), groupSize),
 | 
					        allAssets,
 | 
				
			||||||
 | 
					        layout,
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,20 +41,6 @@ class AssetsState {
 | 
				
			|||||||
    return AssetsState([...allAssets, ...toAdd]);
 | 
					    return AssetsState([...allAssets, ...toAdd]);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Map<String, List<Asset>>> _groupByDate() async {
 | 
					 | 
				
			||||||
    sortCompare(List<Asset> assets) {
 | 
					 | 
				
			||||||
      assets.sortByCompare<DateTime>(
 | 
					 | 
				
			||||||
        (e) => e.createdAt,
 | 
					 | 
				
			||||||
        (a, b) => b.compareTo(a),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      return assets.groupListsBy(
 | 
					 | 
				
			||||||
        (element) => DateFormat('y-MM-dd').format(element.createdAt.toLocal()),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return await compute(sortCompare, allAssets.toList());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static AssetsState fromAssetList(List<Asset> assets) {
 | 
					  static AssetsState fromAssetList(List<Asset> assets) {
 | 
				
			||||||
    return AssetsState(assets);
 | 
					    return AssetsState(assets);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -91,10 +81,19 @@ class AssetNotifier extends StateNotifier<AssetsState> {
 | 
				
			|||||||
      _assetCacheService.put(newAssetList);
 | 
					      _assetCacheService.put(newAssetList);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state =
 | 
					    final layout = AssetGridLayoutParameters(
 | 
				
			||||||
        await AssetsState.fromAssetList(newAssetList).withRenderDataStructure(
 | 
					 | 
				
			||||||
      _settingsService.getSetting(AppSettingsEnum.tilesPerRow),
 | 
					      _settingsService.getSetting(AppSettingsEnum.tilesPerRow),
 | 
				
			||||||
 | 
					      _settingsService.getSetting(AppSettingsEnum.dynamicLayout),
 | 
				
			||||||
 | 
					      GroupAssetsBy.values[_settingsService.getSetting(AppSettingsEnum.groupAssetsBy)],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    state = await AssetsState.fromAssetList(newAssetList)
 | 
				
			||||||
 | 
					        .withRenderDataStructure(layout);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Just a little helper to trigger a rebuild of the state object
 | 
				
			||||||
 | 
					  Future<void> rebuildAssetGridDataStructure() async {
 | 
				
			||||||
 | 
					    await _updateAssetsState(state.allAssets, cache: false);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getAllAsset() async {
 | 
					  getAllAsset() async {
 | 
				
			||||||
 | 
				
			|||||||
@ -25,46 +25,61 @@ void main() {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final Map<String, List<Asset>> groups = {
 | 
					  final List<Asset> assets = [];
 | 
				
			||||||
    '2022-01-05': testAssets.sublist(0, 5).map((e) {
 | 
					
 | 
				
			||||||
 | 
					  assets.addAll(
 | 
				
			||||||
 | 
					    testAssets.sublist(0, 5).map((e) {
 | 
				
			||||||
      e.createdAt = DateTime(2022, 1, 5);
 | 
					      e.createdAt = DateTime(2022, 1, 5);
 | 
				
			||||||
      return e;
 | 
					      return e;
 | 
				
			||||||
    }).toList(),
 | 
					    }).toList(),
 | 
				
			||||||
    '2022-01-10': testAssets.sublist(5, 10).map((e) {
 | 
					  );
 | 
				
			||||||
 | 
					  assets.addAll(
 | 
				
			||||||
 | 
					    testAssets.sublist(5, 10).map((e) {
 | 
				
			||||||
      e.createdAt = DateTime(2022, 1, 10);
 | 
					      e.createdAt = DateTime(2022, 1, 10);
 | 
				
			||||||
      return e;
 | 
					      return e;
 | 
				
			||||||
    }).toList(),
 | 
					    }).toList(),
 | 
				
			||||||
    '2022-02-17': testAssets.sublist(10, 15).map((e) {
 | 
					  );
 | 
				
			||||||
 | 
					  assets.addAll(
 | 
				
			||||||
 | 
					    testAssets.sublist(10, 15).map((e) {
 | 
				
			||||||
      e.createdAt = DateTime(2022, 2, 17);
 | 
					      e.createdAt = DateTime(2022, 2, 17);
 | 
				
			||||||
      return e;
 | 
					      return e;
 | 
				
			||||||
    }).toList(),
 | 
					    }).toList(),
 | 
				
			||||||
    '2022-10-15': testAssets.sublist(15, 30).map((e) {
 | 
					  );
 | 
				
			||||||
 | 
					  assets.addAll(
 | 
				
			||||||
 | 
					    testAssets.sublist(15, 30).map((e) {
 | 
				
			||||||
      e.createdAt = DateTime(2022, 10, 15);
 | 
					      e.createdAt = DateTime(2022, 10, 15);
 | 
				
			||||||
      return e;
 | 
					      return e;
 | 
				
			||||||
    }).toList()
 | 
					    }).toList(),
 | 
				
			||||||
  };
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  group('Test grouped', () {
 | 
					  group('Test grouped', () {
 | 
				
			||||||
    test('test grouped check months', () async {
 | 
					    test('test grouped check months', () async {
 | 
				
			||||||
      final renderList = await RenderList.fromAssetGroups(groups, 3);
 | 
					      final renderList = await RenderList.fromAssets(
 | 
				
			||||||
 | 
					        assets,
 | 
				
			||||||
 | 
					        AssetGridLayoutParameters(
 | 
				
			||||||
 | 
					          3,
 | 
				
			||||||
 | 
					          false,
 | 
				
			||||||
 | 
					          GroupAssetsBy.day,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Jan
 | 
					 | 
				
			||||||
      // Day 1
 | 
					 | 
				
			||||||
      // 5 Assets => 2 Rows
 | 
					 | 
				
			||||||
      // Day 2
 | 
					 | 
				
			||||||
      // 5 Assets => 2 Rows
 | 
					 | 
				
			||||||
      // Feb
 | 
					 | 
				
			||||||
      // Day 1
 | 
					 | 
				
			||||||
      // 5 Assets => 2 Rows
 | 
					 | 
				
			||||||
      // Oct
 | 
					      // Oct
 | 
				
			||||||
      // Day 1
 | 
					      // Day 1
 | 
				
			||||||
      // 15 Assets => 5 Rows
 | 
					      // 15 Assets => 5 Rows
 | 
				
			||||||
 | 
					      // Feb
 | 
				
			||||||
 | 
					      // Day 1
 | 
				
			||||||
 | 
					      // 5 Assets => 2 Rows
 | 
				
			||||||
 | 
					      // Jan
 | 
				
			||||||
 | 
					      // Day 2
 | 
				
			||||||
 | 
					      // 5 Assets => 2 Rows
 | 
				
			||||||
 | 
					      // Day 1
 | 
				
			||||||
 | 
					      // 5 Assets => 2 Rows
 | 
				
			||||||
      expect(renderList.elements.length, 18);
 | 
					      expect(renderList.elements.length, 18);
 | 
				
			||||||
      expect(
 | 
					      expect(
 | 
				
			||||||
        renderList.elements[0].type,
 | 
					        renderList.elements[0].type,
 | 
				
			||||||
        RenderAssetGridElementType.monthTitle,
 | 
					        RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      expect(renderList.elements[0].date.month, 1);
 | 
					      expect(renderList.elements[0].date.month, 10);
 | 
				
			||||||
      expect(
 | 
					      expect(
 | 
				
			||||||
        renderList.elements[7].type,
 | 
					        renderList.elements[7].type,
 | 
				
			||||||
        RenderAssetGridElementType.monthTitle,
 | 
					        RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
@ -74,38 +89,44 @@ void main() {
 | 
				
			|||||||
        renderList.elements[11].type,
 | 
					        renderList.elements[11].type,
 | 
				
			||||||
        RenderAssetGridElementType.monthTitle,
 | 
					        RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      expect(renderList.elements[11].date.month, 10);
 | 
					      expect(renderList.elements[11].date.month, 1);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('test grouped check types', () async {
 | 
					    test('test grouped check types', () async {
 | 
				
			||||||
      final renderList = await RenderList.fromAssetGroups(groups, 5);
 | 
					      final renderList = await RenderList.fromAssets(
 | 
				
			||||||
 | 
					        assets,
 | 
				
			||||||
 | 
					        AssetGridLayoutParameters(
 | 
				
			||||||
 | 
					          5,
 | 
				
			||||||
 | 
					          false,
 | 
				
			||||||
 | 
					          GroupAssetsBy.day,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Jan
 | 
					 | 
				
			||||||
      // Day 1
 | 
					 | 
				
			||||||
      // 5 Assets
 | 
					 | 
				
			||||||
      // Day 2
 | 
					 | 
				
			||||||
      // 5 Assets
 | 
					 | 
				
			||||||
      // Feb
 | 
					 | 
				
			||||||
      // Day 1
 | 
					 | 
				
			||||||
      // 5 Assets
 | 
					 | 
				
			||||||
      // Oct
 | 
					      // Oct
 | 
				
			||||||
      // Day 1
 | 
					      // Day 1
 | 
				
			||||||
      // 15 Assets => 3 Rows
 | 
					      // 15 Assets => 3 Rows
 | 
				
			||||||
 | 
					      // Feb
 | 
				
			||||||
 | 
					      // Day 1
 | 
				
			||||||
 | 
					      // 5 Assets => 1 Row
 | 
				
			||||||
 | 
					      // Jan
 | 
				
			||||||
 | 
					      // Day 2
 | 
				
			||||||
 | 
					      // 5 Assets => 1 Row
 | 
				
			||||||
 | 
					      // Day 1
 | 
				
			||||||
 | 
					      // 5 Assets => 1 Row
 | 
				
			||||||
      final types = [
 | 
					      final types = [
 | 
				
			||||||
        RenderAssetGridElementType.monthTitle,
 | 
					        RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
        RenderAssetGridElementType.dayTitle,
 | 
					        RenderAssetGridElementType.groupDividerTitle,
 | 
				
			||||||
 | 
					        RenderAssetGridElementType.assetRow,
 | 
				
			||||||
        RenderAssetGridElementType.assetRow,
 | 
					        RenderAssetGridElementType.assetRow,
 | 
				
			||||||
        RenderAssetGridElementType.dayTitle,
 | 
					 | 
				
			||||||
        RenderAssetGridElementType.assetRow,
 | 
					        RenderAssetGridElementType.assetRow,
 | 
				
			||||||
        RenderAssetGridElementType.monthTitle,
 | 
					        RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
        RenderAssetGridElementType.dayTitle,
 | 
					        RenderAssetGridElementType.groupDividerTitle,
 | 
				
			||||||
        RenderAssetGridElementType.assetRow,
 | 
					        RenderAssetGridElementType.assetRow,
 | 
				
			||||||
        RenderAssetGridElementType.monthTitle,
 | 
					        RenderAssetGridElementType.monthTitle,
 | 
				
			||||||
        RenderAssetGridElementType.dayTitle,
 | 
					        RenderAssetGridElementType.groupDividerTitle,
 | 
				
			||||||
        RenderAssetGridElementType.assetRow,
 | 
					        RenderAssetGridElementType.assetRow,
 | 
				
			||||||
 | 
					        RenderAssetGridElementType.groupDividerTitle,
 | 
				
			||||||
        RenderAssetGridElementType.assetRow,
 | 
					        RenderAssetGridElementType.assetRow,
 | 
				
			||||||
        RenderAssetGridElementType.assetRow
 | 
					 | 
				
			||||||
      ];
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(renderList.elements.length, types.length);
 | 
					      expect(renderList.elements.length, types.length);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user