Customization options for asset grid (#498)

* Add settings options for number of assets per row and storage indicator

* Add attributes to enum to avoid duplicate code

* Also apply customizations to albums

* Minor Refactorings

* Three stage loading i18n fix
This commit is contained in:
Matthias Rupp 2022-08-20 23:19:40 +02:00 committed by GitHub
parent 07b58f46f9
commit 013a0f8324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 209 additions and 64 deletions

View File

@ -122,5 +122,5 @@
"theme_setting_image_viewer_quality_title": "Qualität des Bildbetrachters",
"theme_setting_image_viewer_quality_subtitle": "Einstellen der Qualität des Detailbildbetrachters",
"theme_setting_three_stage_loading_title": "Dreistufiges Laden aktivieren",
"theme_setting_three_stage_loading_subtitle": "Das dreistufige Ladeverfahren liefert die beste Bildqualität, ist dafür aber langsamer beim Laden."
"theme_setting_three_stage_loading_subtitle": "Das dreistufige Ladeverfahren kann die Performance beim Laden verbessern, erhöht allerdings den Datenverbrauch deutlich"
}

View File

@ -135,5 +135,9 @@
"theme_setting_image_viewer_quality_title": "Image viewer quality",
"theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
"theme_setting_three_stage_loading_title": "Enable three-stage loading",
"theme_setting_three_stage_loading_subtitle": "The three-stage loading delivers the best quality image in exchange for a slower loading speed"
"theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load",
"asset_list_settings_title": "Photo Grid",
"asset_list_settings_subtitle": "Photo grid layout settings",
"theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
"theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})"
}

View File

@ -14,11 +14,13 @@ import 'package:openapi/api.dart';
class AlbumViewerThumbnail extends HookConsumerWidget {
final AssetResponseDto asset;
final List<AssetResponseDto> assetList;
final bool showStorageIndicator;
const AlbumViewerThumbnail({
Key? key,
required this.asset,
required this.assetList,
this.showStorageIndicator = true,
}) : super(key: key);
@override
@ -166,7 +168,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
child: Stack(
children: [
_buildThumbnailImage(),
_buildAssetStoreLocationIcon(),
if (showStorageIndicator) _buildAssetStoreLocationIcon(),
if (asset.type != AssetTypeEnum.IMAGE) _buildVideoLabel(),
if (isMultiSelectionEnable) _buildAssetSelectionIcon(),
],

View File

@ -13,6 +13,8 @@ import 'package:immich_mobile/modules/album/ui/album_action_outlined_button.dart
import 'package:immich_mobile/modules/album/ui/album_viewer_appbar.dart';
import 'package:immich_mobile/modules/album/ui/album_viewer_editable_title.dart';
import 'package:immich_mobile/modules/album/ui/album_viewer_thumbnail.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/routing/router.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/immich_sliver_persistent_app_bar_delegate.dart';
@ -186,12 +188,17 @@ class AlbumViewerPage extends HookConsumerWidget {
}
Widget _buildImageGrid(AlbumResponseDto albumInfo) {
final appSettingService = ref.watch(appSettingsServiceProvider);
final bool showStorageIndicator =
appSettingService.getSetting(AppSettingsEnum.storageIndicator);
if (albumInfo.assets.isNotEmpty) {
return SliverPadding(
padding: const EdgeInsets.only(top: 10.0),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:
appSettingService.getSetting(AppSettingsEnum.tilesPerRow),
crossAxisSpacing: 5.0,
mainAxisSpacing: 5,
),
@ -200,6 +207,7 @@ class AlbumViewerPage extends HookConsumerWidget {
return AlbumViewerThumbnail(
asset: albumInfo.assets[index],
assetList: albumInfo.assets,
showStorageIndicator: showStorageIndicator,
);
},
childCount: albumInfo.assetCount,

View File

@ -7,11 +7,15 @@ import 'package:openapi/api.dart';
class ImageGrid extends ConsumerWidget {
final List<AssetResponseDto> assetGroup;
final List<AssetResponseDto> sortedAssetGroup;
final int tilesPerRow;
final bool showStorageIndicator;
ImageGrid({
Key? key,
required this.assetGroup,
required this.sortedAssetGroup,
this.tilesPerRow = 4,
this.showStorageIndicator = true,
}) : super(key: key);
List<AssetResponseDto> imageSortedList = [];
@ -19,8 +23,8 @@ class ImageGrid extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: tilesPerRow,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5,
),
@ -34,6 +38,7 @@ class ImageGrid extends ConsumerWidget {
ThumbnailImage(
asset: assetGroup[index],
assetList: sortedAssetGroup,
showStorageIndicator: showStorageIndicator,
),
if (assetType != AssetTypeEnum.IMAGE)
Positioned(

View File

@ -15,8 +15,13 @@ import 'package:openapi/api.dart';
class ThumbnailImage extends HookConsumerWidget {
final AssetResponseDto asset;
final List<AssetResponseDto> assetList;
final bool showStorageIndicator;
const ThumbnailImage({Key? key, required this.asset, required this.assetList})
const ThumbnailImage(
{Key? key,
required this.asset,
required this.assetList,
this.showStorageIndicator = true})
: super(key: key);
@override
@ -123,7 +128,7 @@ class ThumbnailImage extends HookConsumerWidget {
child: _buildSelectionIcon(asset),
),
),
Positioned(
if (showStorageIndicator) Positioned(
right: 10,
bottom: 5,
child: Icon(

View File

@ -10,6 +10,8 @@ import 'package:immich_mobile/modules/home/ui/image_grid.dart';
import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart';
import 'package:immich_mobile/modules/home/ui/monthly_title_text.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/services/app_settings.service.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
@ -21,6 +23,8 @@ class HomePage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final appSettingService = ref.watch(appSettingsServiceProvider);
ScrollController scrollController = useScrollController();
var assetGroupByDateTime = ref.watch(assetGroupByDateTimeProvider);
List<Widget> imageGridGroup = [];
@ -86,6 +90,8 @@ class HomePage extends HookConsumerWidget {
ImageGrid(
assetGroup: immichAssetList,
sortedAssetGroup: sortedAssetList,
tilesPerRow: appSettingService.getSetting(AppSettingsEnum.tilesPerRow),
showStorageIndicator: appSettingService.getSetting(AppSettingsEnum.storageIndicator),
),
);

View File

@ -1,11 +1,16 @@
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
enum AppSettingsEnum {
threeStageLoading, // true, false,
themeMode, // "light","dark","system"
enum AppSettingsEnum<T> {
threeStageLoading<bool>("threeStageLoading", false),
themeMode<String>("themeMode", "system"), // "light","dark","system"
tilesPerRow<int>("tilesPerRow", 4),
storageIndicator<bool>("storageIndicator", true);
const AppSettingsEnum(this.hiveKey, this.defaultValue);
final String hiveKey;
final T defaultValue;
}
class AppSettingsService {
@ -15,63 +20,26 @@ class AppSettingsService {
hiveBox = Hive.box(userSettingInfoBox);
}
T getSetting<T>(AppSettingsEnum settingType) {
var settingKey = _settingHiveBoxKeyLookup(settingType);
if (!hiveBox.containsKey(settingKey)) {
T defaultSetting = _setDefaultSetting(settingType);
return defaultSetting;
T getSetting<T>(AppSettingsEnum<T> settingType) {
if (!hiveBox.containsKey(settingType.hiveKey)) {
return _setDefault(settingType);
}
var result = hiveBox.get(settingKey);
var result = hiveBox.get(settingType.hiveKey);
if (result is T) {
return result;
} else {
debugPrint("Incorrect setting type");
throw TypeError();
if (result is! T) {
return _setDefault(settingType);
}
return result;
}
setSetting<T>(AppSettingsEnum settingType, T value) {
var settingKey = _settingHiveBoxKeyLookup(settingType);
if (hiveBox.containsKey(settingKey)) {
var result = hiveBox.get(settingKey);
if (result is! T) {
debugPrint("Incorrect setting type");
throw TypeError();
}
hiveBox.put(settingKey, value);
} else {
hiveBox.put(settingKey, value);
}
setSetting<T>(AppSettingsEnum<T> settingType, T value) {
hiveBox.put(settingType.hiveKey, value);
}
_setDefaultSetting(AppSettingsEnum settingType) {
var settingKey = _settingHiveBoxKeyLookup(settingType);
// Default value of threeStageLoading is false
if (settingType == AppSettingsEnum.threeStageLoading) {
hiveBox.put(settingKey, false);
return false;
}
// Default value of themeMode is "light"
if (settingType == AppSettingsEnum.themeMode) {
hiveBox.put(settingKey, "system");
return "system";
}
}
String _settingHiveBoxKeyLookup(AppSettingsEnum settingType) {
switch (settingType) {
case AppSettingsEnum.threeStageLoading:
return 'threeStageLoading';
case AppSettingsEnum.themeMode:
return 'themeMode';
}
T _setDefault<T>(AppSettingsEnum<T> settingType) {
hiveBox.put(settingType.hiveKey, settingType.defaultValue);
return settingType.defaultValue;
}
}

View File

@ -0,0 +1,33 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_storage_indicator.dart';
import 'asset_list_tiles_per_row.dart';
class AssetListSettings extends StatelessWidget {
const AssetListSettings({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ExpansionTile(
textColor: Theme.of(context).primaryColor,
title: const Text(
'asset_list_settings_title',
style: TextStyle(
fontWeight: FontWeight.bold,
),
).tr(),
subtitle: const Text(
'asset_list_settings_subtitle',
style: TextStyle(
fontSize: 13,
),
).tr(),
children: const [
TilesPerRow(),
StorageIndicator(),
],
);
}
}

View File

@ -0,0 +1,48 @@
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/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 StorageIndicator extends HookConsumerWidget {
const StorageIndicator({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final appSettingService = ref.watch(appSettingsServiceProvider);
final showStorageIndicator = useState(true);
void switchChanged(bool value) {
appSettingService.setSetting(AppSettingsEnum.storageIndicator, value);
showStorageIndicator.value = value;
ref.invalidate(assetGroupByDateTimeProvider);
}
useEffect(
() {
showStorageIndicator.value = appSettingService.getSetting<bool>(AppSettingsEnum.storageIndicator);
return null;
},
[],
);
return SwitchListTile.adaptive(
activeColor: Theme.of(context).primaryColor,
title: const Text(
"theme_setting_asset_list_storage_indicator_title",
style: TextStyle(
fontSize: 12,
),
).tr(),
onChanged: switchChanged,
value: showStorageIndicator.value,
);
}
}

View File

@ -0,0 +1,63 @@
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/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 TilesPerRow extends HookConsumerWidget {
const TilesPerRow({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final appSettingService = ref.watch(appSettingsServiceProvider);
final itemsValue = useState(4.0);
void sliderChanged(double value) {
appSettingService.setSetting(AppSettingsEnum.tilesPerRow, value.toInt());
itemsValue.value = value;
}
void sliderChangedEnd(double _) {
ref.invalidate(assetGroupByDateTimeProvider);
}
useEffect(
() {
int tilesPerRow =
appSettingService.getSetting(AppSettingsEnum.tilesPerRow);
itemsValue.value = tilesPerRow.toDouble();
return null;
},
[],
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
title: const Text(
"theme_setting_asset_list_tiles_per_row_title",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
).tr(args: ["${itemsValue.value.toInt()}"]),
),
Slider(
onChangeEnd: sliderChangedEnd,
onChanged: sliderChanged,
value: itemsValue.value,
min: 2,
max: 6,
divisions: 4,
label: "${itemsValue.value.toInt()}",
),
],
);
}
}

View File

@ -36,6 +36,7 @@ class ThreeStageLoading extends HookConsumerWidget {
}
return SwitchListTile.adaptive(
activeColor: Theme.of(context).primaryColor,
title: const Text(
"theme_setting_three_stage_loading_title",
style: TextStyle(

View File

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_settings.dart';
import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting/image_viewer_quality_setting.dart';
import 'package:immich_mobile/modules/settings/ui/theme_setting/theme_setting.dart';
@ -36,6 +37,7 @@ class SettingsPage extends HookConsumerWidget {
tiles: [
const ImageViewerQualitySetting(),
const ThemeSetting(),
const AssetListSettings()
],
).toList(),
],