Support sorting by filename in Photos tab

This commit is contained in:
Ming Ming 2022-05-26 11:58:05 +08:00
parent 3982f5d5ff
commit 083263c561
5 changed files with 208 additions and 20 deletions

View file

@ -394,6 +394,12 @@
"@settingsUseBlackInDarkThemeFalseDescription": {
"description": "When black in dark theme is set to false"
},
"settingsMiscellaneousTitle": "Miscellaneous",
"settingsMiscellaneousPageTitle": "Miscellaneous settings",
"settingsPhotosTabSortByNameTitle": "Sort by filename in Photos",
"@settingsPhotosTabSortByNameTitle": {
"description": "Sort photos listed in the Photos tab by filename (descending)"
},
"settingsExperimentalTitle": "Experimental",
"settingsExperimentalDescription": "Features that are not ready for everyday use",
"settingsExperimentalPageTitle": "Experimental settings",

View file

@ -20,6 +20,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"settingsExperimentalTitle",
"settingsExperimentalDescription",
"settingsExperimentalPageTitle",
@ -124,6 +127,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"settingsExperimentalTitle",
"settingsExperimentalDescription",
"settingsExperimentalPageTitle",
@ -257,6 +263,9 @@
"settingsUseBlackInDarkThemeTitle",
"settingsUseBlackInDarkThemeTrueDescription",
"settingsUseBlackInDarkThemeFalseDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"settingsExperimentalTitle",
"settingsExperimentalDescription",
"settingsExperimentalPageTitle",
@ -399,6 +408,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"rootPickerSkipConfirmationDialogContent2",
"helpButtonLabel",
"backgroundServiceStopping",
@ -418,6 +430,9 @@
],
"fi": [
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"enhanceSuperResolution4xTitle",
"enhanceStyleTransferTitle"
],
@ -428,6 +443,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"helpTooltip",
"helpButtonLabel",
"removeFromAlbumTooltip",
@ -450,6 +468,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"createCollectionTooltip",
"createCollectionDialogAlbumLabel",
"createCollectionDialogAlbumDescription",
@ -490,6 +511,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"enhanceTooltip",
"enhanceButtonLabel",
"enhanceIntroDialogTitle",
@ -509,6 +533,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"enhanceTooltip",
"enhanceButtonLabel",
"enhanceIntroDialogTitle",
@ -528,6 +555,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"enhanceTooltip",
"enhanceButtonLabel",
"enhanceIntroDialogTitle",
@ -547,6 +577,9 @@
"settingsPhotoEnhancementPageTitle",
"settingsEnhanceMaxResolutionTitle",
"settingsEnhanceMaxResolutionDescription",
"settingsMiscellaneousTitle",
"settingsMiscellaneousPageTitle",
"settingsPhotosTabSortByNameTitle",
"enhanceTooltip",
"enhanceButtonLabel",
"enhanceIntroDialogTitle",

View file

@ -199,6 +199,15 @@ class Pref {
Future<bool> setFirstRunTime(int value) => _set<int>(
PrefKey.firstRunTime, value, (key, value) => provider.setInt(key, value));
bool? isPhotosTabSortByName() =>
provider.getBool(PrefKey.isPhotosTabSortByName);
bool isPhotosTabSortByNameOr([bool def = false]) =>
isPhotosTabSortByName() ?? def;
Future<bool> setPhotosTabSortByName(bool value) => _set<bool>(
PrefKey.isPhotosTabSortByName,
value,
(key, value) => provider.setBool(key, value));
Future<bool> _set<T>(PrefKey key, T value,
Future<bool> Function(PrefKey key, T value) setFn) async {
if (await setFn(key, value)) {
@ -492,6 +501,7 @@ enum PrefKey {
enhanceMaxHeight,
hasShownEnhanceInfo,
firstRunTime,
isPhotosTabSortByName,
// account pref
isEnableFaceRecognitionApp,
@ -554,6 +564,8 @@ extension on PrefKey {
return "hasShownEnhanceInfo";
case PrefKey.firstRunTime:
return "firstRunTime";
case PrefKey.isPhotosTabSortByName:
return "isPhotosTabSortByName";
// account pref
case PrefKey.isEnableFaceRecognitionApp:

View file

@ -1,6 +1,7 @@
import 'dart:math' as math;
import 'dart:ui';
import 'package:collection/collection.dart' show compareNatural;
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -436,6 +437,13 @@ class _HomePhotosState extends State<HomePhotos>
} else {
_stopMetadataTask();
}
} else if (ev.key == PrefKey.isPhotosTabSortByName) {
if (_bloc.state is! ScanAccountDirBlocInit) {
_log.info("[_onPrefUpdated] Update view after changing sort option");
setState(() {
_transformItems(_bloc.state.files);
});
}
}
}
@ -484,6 +492,30 @@ class _HomePhotosState extends State<HomePhotos>
/// Transform a File list to grid items
void _transformItems(List<File> files) {
if (!Pref().isPhotosTabSortByNameOr()) {
_transformItemsByDate(files);
} else {
_transformItemsByName(files);
}
}
void _transformItemsByName(List<File> files) {
_backingFiles = files
.where((f) => f.isArchived != true)
.sorted((a, b) => compareNatural(b.filename, a.filename));
itemStreamListItems = () sync* {
for (int i = 0; i < _backingFiles.length; ++i) {
final item = _transformItemToListItem(i, _backingFiles[i]);
if (item != null) {
yield item;
}
}
}()
.toList();
}
void _transformItemsByDate(List<File> files) {
_backingFiles = files
.where((f) => f.isArchived != true)
.sorted(compareFileDateTimeDescending);
@ -503,25 +535,9 @@ class _HomePhotosState extends State<HomePhotos>
}
memoryAlbumHelper.addFile(f);
final previewUrl = api_util.getFilePreviewUrl(widget.account, f,
width: k.photoThumbSize, height: k.photoThumbSize);
if (file_util.isSupportedImageFormat(f)) {
yield _ImageListItem(
file: f,
account: widget.account,
previewUrl: previewUrl,
onTap: () => _onItemTap(i),
);
} else if (file_util.isSupportedVideoFormat(f)) {
yield _VideoListItem(
file: f,
account: widget.account,
previewUrl: previewUrl,
onTap: () => _onItemTap(i),
);
} else {
_log.shout(
"[_transformItems] Unsupported file format: ${f.contentType}");
final item = _transformItemToListItem(i, f);
if (item != null) {
yield item;
}
}
}()
@ -530,6 +546,30 @@ class _HomePhotosState extends State<HomePhotos>
.build((year) => L10n.global().memoryAlbumName(today.year - year));
}
_ListItem? _transformItemToListItem(int i, File f) {
final previewUrl = api_util.getFilePreviewUrl(widget.account, f,
width: k.photoThumbSize, height: k.photoThumbSize);
if (file_util.isSupportedImageFormat(f)) {
return _ImageListItem(
file: f,
account: widget.account,
previewUrl: previewUrl,
onTap: () => _onItemTap(i),
);
} else if (file_util.isSupportedVideoFormat(f)) {
return _VideoListItem(
file: f,
account: widget.account,
previewUrl: previewUrl,
onTap: () => _onItemTap(i),
);
} else {
_log.shout(
"[_transformItemToListItem] Unsupported file format: ${f.contentType}");
return null;
}
}
void _reqQuery() {
_bloc.add(const ScanAccountDirBlocQuery());
}

View file

@ -66,6 +66,14 @@ class _SettingsState extends State<Settings> {
final settings = AccountPref.of(widget.account);
_isEnableMemoryAlbum = settings.isEnableMemoryAlbumOr(true);
_prefUpdatedListener.begin();
}
@override
dispose() {
_prefUpdatedListener.end();
super.dispose();
}
@override
@ -107,7 +115,9 @@ class _SettingsState extends State<Settings> {
title: Text(L10n.global().settingsMemoriesTitle),
subtitle: Text(L10n.global().settingsMemoriesSubtitle),
value: _isEnableMemoryAlbum,
onChanged: _onEnableMemoryAlbumChanged,
onChanged: Pref().isPhotosTabSortByNameOr()
? null
: _onEnableMemoryAlbumChanged,
),
_buildSubSettings(
context,
@ -157,6 +167,15 @@ class _SettingsState extends State<Settings> {
description: L10n.global().settingsThemeDescription,
builder: () => _ThemeSettings(),
),
_buildSubSettings(
context,
leading: Icon(
Icons.emoji_symbols_outlined,
color: AppTheme.getUnfocusedIconColor(context),
),
label: L10n.global().settingsMiscellaneousTitle,
builder: () => const _MiscSettings(),
),
if (_enabledExperiments.isNotEmpty)
_buildSubSettings(
context,
@ -365,6 +384,12 @@ class _SettingsState extends State<Settings> {
}
}
void _onPrefUpdated(PrefUpdatedEvent ev) {
if (ev.key == PrefKey.isPhotosTabSortByName) {
setState(() {});
}
}
Future<void> _setExifSupport(bool value) async {
final oldValue = _isEnableExif;
setState(() {
@ -385,6 +410,9 @@ class _SettingsState extends State<Settings> {
late bool _isEnableExif;
late bool _isEnableMemoryAlbum;
late final _prefUpdatedListener =
AppEventListener<PrefUpdatedEvent>(_onPrefUpdated);
static final _log = Logger("widget.settings._SettingsState");
static const String _sourceRepo = "https://bit.ly/3LQerBv";
@ -1287,6 +1315,75 @@ class _ThemeSettingsState extends State<_ThemeSettings> {
static final _log = Logger("widget.settings._ThemeSettingsState");
}
class _MiscSettings extends StatefulWidget {
const _MiscSettings({Key? key}) : super(key: key);
@override
createState() => _MiscSettingsState();
}
class _MiscSettingsState extends State<_MiscSettings> {
@override
initState() {
super.initState();
_isPhotosTabSortByName = Pref().isPhotosTabSortByNameOr();
}
@override
build(BuildContext context) {
return AppTheme(
child: Scaffold(
body: Builder(
builder: (context) => _buildContent(context),
),
),
);
}
Widget _buildContent(BuildContext context) {
return CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
title: Text(L10n.global().settingsMiscellaneousPageTitle),
),
SliverList(
delegate: SliverChildListDelegate(
[
SwitchListTile(
title: Text(L10n.global().settingsPhotosTabSortByNameTitle),
value: _isPhotosTabSortByName,
onChanged: (value) => _onPhotosTabSortByNameChanged(value),
),
],
),
),
],
);
}
void _onPhotosTabSortByNameChanged(bool value) async {
final oldValue = _isPhotosTabSortByName;
setState(() {
_isPhotosTabSortByName = value;
});
if (!await Pref().setPhotosTabSortByName(value)) {
_log.severe("[_onPhotosTabSortByNameChanged] Failed writing pref");
SnackBarManager().showSnackBar(SnackBar(
content: Text(L10n.global().writePreferenceFailureNotification),
duration: k.snackBarDurationNormal,
));
setState(() {
_isPhotosTabSortByName = oldValue;
});
}
}
late bool _isPhotosTabSortByName;
static final _log = Logger("widget.settings._MiscSettingsState");
}
class _ExperimentalSettings extends StatefulWidget {
@override
createState() => _ExperimentalSettingsState();