1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-25 10:43:13 +02:00

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:
Matthias Rupp 2023-02-09 18:35:44 +01:00 committed by GitHub
parent 911c35a7f1
commit fd13265131
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 298 additions and 125 deletions

View File

@ -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"
} }

View File

@ -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,
), ),
); );
} }

View File

@ -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,

View File

@ -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!);

View File

@ -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!,

View File

@ -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);
}); });

View File

@ -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,

View File

@ -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,
),
],
);
}
}

View File

@ -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(),
], ],
); );
} }

View File

@ -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(

View File

@ -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(

View File

@ -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 {

View File

@ -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);