fixed source update on hidden filter import

This commit is contained in:
Thibault Deckers 2022-02-21 14:22:41 +09:00
parent d110695d53
commit 6b4d9c0bc3
11 changed files with 86 additions and 72 deletions

View file

@ -20,9 +20,9 @@ final Settings settings = Settings._private();
class Settings extends ChangeNotifier { class Settings extends ChangeNotifier {
final EventChannel _platformSettingsChangeChannel = const EventChannel('deckers.thibault/aves/settings_change'); final EventChannel _platformSettingsChangeChannel = const EventChannel('deckers.thibault/aves/settings_change');
final StreamController<String> _updateStreamController = StreamController<String>.broadcast(); final StreamController<SettingsChangedEvent> _updateStreamController = StreamController<SettingsChangedEvent>.broadcast();
Stream<String> get updateStream => _updateStreamController.stream; Stream<SettingsChangedEvent> get updateStream => _updateStreamController.stream;
Settings._private(); Settings._private();
@ -356,6 +356,17 @@ class Settings extends ChangeNotifier {
set hiddenFilters(Set<CollectionFilter> newValue) => setAndNotify(hiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList()); set hiddenFilters(Set<CollectionFilter> newValue) => setAndNotify(hiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList());
void changeFilterVisibility(Set<CollectionFilter> filters, bool visible) {
final _hiddenFilters = hiddenFilters;
if (visible) {
_hiddenFilters.removeAll(filters);
} else {
_hiddenFilters.addAll(filters);
searchHistory = searchHistory..removeWhere(filters.contains);
}
hiddenFilters = _hiddenFilters;
}
// viewer // viewer
List<EntryAction> get viewerQuickActions => getEnumListOrDefault(viewerQuickActionsKey, SettingsDefaults.viewerQuickActions, EntryAction.values); List<EntryAction> get viewerQuickActions => getEnumListOrDefault(viewerQuickActionsKey, SettingsDefaults.viewerQuickActions, EntryAction.values);
@ -540,7 +551,7 @@ class Settings extends ChangeNotifier {
settingsStore.setBool(key, newValue); settingsStore.setBool(key, newValue);
} }
if (oldValue != newValue) { if (oldValue != newValue) {
_updateStreamController.add(key); _updateStreamController.add(SettingsChangedEvent(key, oldValue, newValue));
notifyListeners(); notifyListeners();
} }
} }
@ -583,37 +594,39 @@ class Settings extends ChangeNotifier {
await reset(includeInternalKeys: false); await reset(includeInternalKeys: false);
// apply user modifications // apply user modifications
jsonMap.forEach((key, value) { jsonMap.forEach((key, newValue) {
if (value == null) { final oldValue = settingsStore.get(key);
if (newValue == null) {
settingsStore.remove(key); settingsStore.remove(key);
} else if (key.startsWith(tileExtentPrefixKey)) { } else if (key.startsWith(tileExtentPrefixKey)) {
if (value is double) { if (newValue is double) {
settingsStore.setDouble(key, value); settingsStore.setDouble(key, newValue);
} else { } else {
debugPrint('failed to import key=$key, value=$value is not a double'); debugPrint('failed to import key=$key, value=$newValue is not a double');
} }
} else if (key.startsWith(tileLayoutPrefixKey)) { } else if (key.startsWith(tileLayoutPrefixKey)) {
if (value is String) { if (newValue is String) {
settingsStore.setString(key, value); settingsStore.setString(key, newValue);
} else { } else {
debugPrint('failed to import key=$key, value=$value is not a string'); debugPrint('failed to import key=$key, value=$newValue is not a string');
} }
} else { } else {
switch (key) { switch (key) {
case subtitleTextColorKey: case subtitleTextColorKey:
case subtitleBackgroundColorKey: case subtitleBackgroundColorKey:
if (value is int) { if (newValue is int) {
settingsStore.setInt(key, value); settingsStore.setInt(key, newValue);
} else { } else {
debugPrint('failed to import key=$key, value=$value is not an int'); debugPrint('failed to import key=$key, value=$newValue is not an int');
} }
break; break;
case subtitleFontSizeKey: case subtitleFontSizeKey:
case infoMapZoomKey: case infoMapZoomKey:
if (value is double) { if (newValue is double) {
settingsStore.setDouble(key, value); settingsStore.setDouble(key, newValue);
} else { } else {
debugPrint('failed to import key=$key, value=$value is not a double'); debugPrint('failed to import key=$key, value=$newValue is not a double');
} }
break; break;
case isInstalledAppAccessAllowedKey: case isInstalledAppAccessAllowedKey:
@ -638,10 +651,10 @@ class Settings extends ChangeNotifier {
case subtitleShowOutlineKey: case subtitleShowOutlineKey:
case saveSearchHistoryKey: case saveSearchHistoryKey:
case filePickerShowHiddenFilesKey: case filePickerShowHiddenFilesKey:
if (value is bool) { if (newValue is bool) {
settingsStore.setBool(key, value); settingsStore.setBool(key, newValue);
} else { } else {
debugPrint('failed to import key=$key, value=$value is not a bool'); debugPrint('failed to import key=$key, value=$newValue is not a bool');
} }
break; break;
case localeKey: case localeKey:
@ -661,10 +674,10 @@ class Settings extends ChangeNotifier {
case unitSystemKey: case unitSystemKey:
case accessibilityAnimationsKey: case accessibilityAnimationsKey:
case timeToTakeActionKey: case timeToTakeActionKey:
if (value is String) { if (newValue is String) {
settingsStore.setString(key, value); settingsStore.setString(key, newValue);
} else { } else {
debugPrint('failed to import key=$key, value=$value is not a string'); debugPrint('failed to import key=$key, value=$newValue is not a string');
} }
break; break;
case confirmationDialogsKey: case confirmationDialogsKey:
@ -677,17 +690,29 @@ class Settings extends ChangeNotifier {
case collectionSelectionQuickActionsKey: case collectionSelectionQuickActionsKey:
case viewerQuickActionsKey: case viewerQuickActionsKey:
case videoQuickActionsKey: case videoQuickActionsKey:
if (value is List) { if (newValue is List) {
settingsStore.setStringList(key, value.cast<String>()); settingsStore.setStringList(key, newValue.cast<String>());
} else { } else {
debugPrint('failed to import key=$key, value=$value is not a list'); debugPrint('failed to import key=$key, value=$newValue is not a list');
} }
break; break;
} }
} }
_updateStreamController.add(key); if (oldValue != newValue) {
_updateStreamController.add(SettingsChangedEvent(key, oldValue, newValue));
}
}); });
notifyListeners(); notifyListeners();
} }
} }
} }
@immutable
class SettingsChangedEvent {
final String key;
final dynamic oldValue;
final dynamic newValue;
// old and new values as stored, e.g. `List<String>` for collections
const SettingsChangedEvent(this.key, this.oldValue, this.newValue);
}

View file

@ -79,10 +79,10 @@ class CollectionLens with ChangeNotifier {
favourites.addListener(_onFavouritesChanged); favourites.addListener(_onFavouritesChanged);
} }
_subscriptions.add(settings.updateStream _subscriptions.add(settings.updateStream
.where([ .where((event) => [
Settings.collectionSortFactorKey, Settings.collectionSortFactorKey,
Settings.collectionGroupFactorKey, Settings.collectionGroupFactorKey,
].contains) ].contains(event.key))
.listen((_) => _onSettingsChanged())); .listen((_) => _onSettingsChanged()));
_refresh(); _refresh();
} }

View file

@ -45,7 +45,14 @@ mixin SourceBase {
abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin, TrashMixin { abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin, TrashMixin {
CollectionSource() { CollectionSource() {
settings.updateStream.where((key) => key == Settings.localeKey).listen((_) => invalidateAlbumDisplayNames()); settings.updateStream.where((event) => event.key == Settings.localeKey).listen((_) => invalidateAlbumDisplayNames());
settings.updateStream.where((event) => event.key == Settings.hiddenFiltersKey).listen((event) {
final oldValue = event.oldValue;
if (oldValue is List<String>?) {
final oldHiddenFilters = (oldValue ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet();
_onFilterVisibilityChanged(oldHiddenFilters, settings.hiddenFilters);
}
});
} }
final EventBus _eventBus = EventBus(); final EventBus _eventBus = EventBus();
@ -441,20 +448,13 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
return recentEntry(filter); return recentEntry(filter);
} }
void changeFilterVisibility(Set<CollectionFilter> filters, bool visible) { void _onFilterVisibilityChanged(Set<CollectionFilter> oldHiddenFilters, Set<CollectionFilter> currentHiddenFilters) {
final hiddenFilters = settings.hiddenFilters;
if (visible) {
hiddenFilters.removeAll(filters);
} else {
hiddenFilters.addAll(filters);
settings.searchHistory = settings.searchHistory..removeWhere(filters.contains);
}
settings.hiddenFilters = hiddenFilters;
updateDerivedFilters(); updateDerivedFilters();
eventBus.fire(FilterVisibilityChangedEvent(filters, visible)); eventBus.fire(const FilterVisibilityChangedEvent());
if (visible) { final newlyVisibleFilters = oldHiddenFilters.whereNot(currentHiddenFilters.contains).toSet();
final candidateEntries = visibleEntries.where((entry) => filters.any((f) => f.test(entry))).toSet(); if (newlyVisibleFilters.isNotEmpty) {
final candidateEntries = visibleEntries.where((entry) => newlyVisibleFilters.any((f) => f.test(entry))).toSet();
analyze(null, entries: candidateEntries); analyze(null, entries: candidateEntries);
} }
} }

View file

@ -1,6 +1,5 @@
import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/actions/move_type.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@immutable @immutable
@ -34,10 +33,7 @@ class EntryRefreshedEvent {
@immutable @immutable
class FilterVisibilityChangedEvent { class FilterVisibilityChangedEvent {
final Set<CollectionFilter> filters; const FilterVisibilityChangedEvent();
final bool visible;
const FilterVisibilityChangedEvent(this.filters, this.visible);
} }
@immutable @immutable

View file

@ -229,9 +229,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
} }
} }
settings.updateStream.where((key) => key == Settings.isInstalledAppAccessAllowedKey).listen((_) => applyIsInstalledAppAccessAllowed()); settings.updateStream.where((event) => event.key == Settings.isInstalledAppAccessAllowedKey).listen((_) => applyIsInstalledAppAccessAllowed());
settings.updateStream.where((key) => key == Settings.keepScreenOnKey).listen((_) => applyKeepScreenOn()); settings.updateStream.where((event) => event.key == Settings.keepScreenOnKey).listen((_) => applyKeepScreenOn());
settings.updateStream.where((key) => key == Settings.platformAccelerometerRotationKey).listen((_) => applyIsRotationLocked()); settings.updateStream.where((event) => event.key == Settings.platformAccelerometerRotationKey).listen((_) => applyIsRotationLocked());
applyKeepScreenOn(); applyKeepScreenOn();
applyIsRotationLocked(); applyIsRotationLocked();
@ -239,7 +239,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
Future<void> _setupErrorReporting() async { Future<void> _setupErrorReporting() async {
await reportService.init(); await reportService.init();
settings.updateStream.where((key) => key == Settings.isErrorReportingAllowedKey).listen( settings.updateStream.where((event) => event.key == Settings.isErrorReportingAllowedKey).listen(
(_) => reportService.setCollectionEnabled(settings.isErrorReportingAllowed), (_) => reportService.setCollectionEnabled(settings.isErrorReportingAllowed),
); );
await reportService.setCollectionEnabled(settings.isErrorReportingAllowed); await reportService.setCollectionEnabled(settings.isErrorReportingAllowed);

View file

@ -40,7 +40,7 @@ class _CollectionPageState extends State<CollectionPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_subscriptions.add(settings.updateStream.where((key) => key == Settings.enableBinKey).listen((_) { _subscriptions.add(settings.updateStream.where((event) => event.key == Settings.enableBinKey).listen((_) {
if (!settings.enableBin) { if (!settings.enableBin) {
collection.removeFilter(TrashFilter.instance); collection.removeFilter(TrashFilter.instance);
} }

View file

@ -132,7 +132,6 @@ class _AppDebugPageState extends State<AppDebugPage> {
), ),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
final source = context.read<CollectionSource>();
await source.init(); await source.init();
await source.refresh(); await source.refresh();
}, },
@ -163,18 +162,16 @@ class _AppDebugPageState extends State<AppDebugPage> {
Future<void> _onActionSelected(AppDebugAction action) async { Future<void> _onActionSelected(AppDebugAction action) async {
switch (action) { switch (action) {
case AppDebugAction.prepScreenshotThumbnails: case AppDebugAction.prepScreenshotThumbnails:
final source = context.read<CollectionSource>(); settings.changeFilterVisibility(settings.hiddenFilters, true);
source.changeFilterVisibility(settings.hiddenFilters, true); settings.changeFilterVisibility({
source.changeFilterVisibility({
TagFilter('aves-thumbnail', not: true), TagFilter('aves-thumbnail', not: true),
}, false); }, false);
await favourites.clear(); await favourites.clear();
await favourites.add(source.visibleEntries); await favourites.add(source.visibleEntries);
break; break;
case AppDebugAction.prepScreenshotStats: case AppDebugAction.prepScreenshotStats:
final source = context.read<CollectionSource>(); settings.changeFilterVisibility(settings.hiddenFilters, true);
source.changeFilterVisibility(settings.hiddenFilters, true); settings.changeFilterVisibility({
source.changeFilterVisibility({
PathFilter('/storage/emulated/0/Pictures/Dev'), PathFilter('/storage/emulated/0/Pictures/Dev'),
}, false); }, false);
break; break;

View file

@ -1,7 +1,7 @@
import 'package:aves/model/actions/chip_actions.dart'; import 'package:aves/model/actions/chip_actions.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/highlight.dart'; import 'package:aves/model/highlight.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart';
@ -51,8 +51,7 @@ class ChipActionDelegate {
); );
if (confirmed == null || !confirmed) return; if (confirmed == null || !confirmed) return;
final source = context.read<CollectionSource>(); settings.changeFilterVisibility({filter}, false);
source.changeFilterVisibility({filter}, false);
} }
void _goTo( void _goTo(

View file

@ -271,8 +271,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
); );
if (confirmed == null || !confirmed) return; if (confirmed == null || !confirmed) return;
final source = context.read<CollectionSource>(); settings.changeFilterVisibility(filters, false);
source.changeFilterVisibility(filters, false);
_browse(context); _browse(context);
} }

View file

@ -1,7 +1,6 @@
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/path.dart'; import 'package:aves/model/filters/path.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
@ -116,7 +115,7 @@ class _HiddenFilters extends StatelessWidget {
.map((filter) => AvesFilterChip( .map((filter) => AvesFilterChip(
filter: filter, filter: filter,
removable: true, removable: true,
onTap: (filter) => context.read<CollectionSource>().changeFilterVisibility({filter}, true), onTap: (filter) => settings.changeFilterVisibility({filter}, true),
onLongPress: null, onLongPress: null,
)) ))
.toList(), .toList(),
@ -152,7 +151,7 @@ class _HiddenPaths extends StatelessWidget {
trailing: IconButton( trailing: IconButton(
icon: const Icon(AIcons.clear), icon: const Icon(AIcons.clear),
onPressed: () { onPressed: () {
context.read<CollectionSource>().changeFilterVisibility({pathFilter}, true); settings.changeFilterVisibility({pathFilter}, true);
}, },
tooltip: context.l10n.actionRemove, tooltip: context.l10n.actionRemove,
), ),
@ -176,7 +175,7 @@ class _HiddenPaths extends StatelessWidget {
// wait for the dialog to hide as applying the change may block the UI // wait for the dialog to hide as applying the change may block the UI
await Future.delayed(Durations.pageTransitionAnimation * timeDilation); await Future.delayed(Durations.pageTransitionAnimation * timeDilation);
if (path != null && path.isNotEmpty) { if (path != null && path.isNotEmpty) {
context.read<CollectionSource>().changeFilterVisibility({PathFilter(path)}, false); settings.changeFilterVisibility({PathFilter(path)}, false);
} }
}, },
), ),

View file

@ -2,7 +2,6 @@ import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/enums/video_loop_mode.dart'; import 'package:aves/model/settings/enums/video_loop_mode.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
@ -36,7 +35,7 @@ class VideoSection extends StatelessWidget {
if (!standalonePage) if (!standalonePage)
SwitchListTile( SwitchListTile(
value: currentShowVideos, value: currentShowVideos,
onChanged: (v) => context.read<CollectionSource>().changeFilterVisibility({MimeFilter.video}, v), onChanged: (v) => settings.changeFilterVisibility({MimeFilter.video}, v),
title: Text(context.l10n.settingsVideoShowVideos), title: Text(context.l10n.settingsVideoShowVideos),
), ),
const VideoActionsTile(), const VideoActionsTile(),