#268 cover/pins/bookmarks sub to dynamics/groups; dynamics sub to groups;

container filter mixin;
debug: cover/dynamics dump;
This commit is contained in:
Thibault Deckers 2025-05-11 21:57:38 +02:00
parent 27879a900d
commit 651b5926dc
57 changed files with 478 additions and 189 deletions

View file

@ -374,6 +374,11 @@ class Dependencies {
license: bsd3,
sourceUrl: 'https://github.com/dart-lang/stack_trace',
),
Dependency(
name: 'Synchronized',
license: mit,
sourceUrl: 'https://github.com/tekartik/synchronized.dart/tree/master/synchronized',
),
Dependency(
name: 'Vector Math',
license: '$zlib, $bsd3',

View file

@ -1,10 +1,13 @@
import 'dart:async';
import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart';
@ -15,12 +18,16 @@ import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:synchronized/synchronized.dart';
final Covers covers = Covers._private();
typedef CoverProps = (int? entryId, String? packageName, Color? color);
class Covers {
final List<StreamSubscription> _subscriptions = [];
final _lock = Lock();
final StreamController<Set<CollectionFilter>?> _entryChangeStreamController = StreamController.broadcast();
final StreamController<Set<CollectionFilter>?> _packageChangeStreamController = StreamController.broadcast();
final StreamController<Set<CollectionFilter>?> _colorChangeStreamController = StreamController.broadcast();
@ -37,6 +44,8 @@ class Covers {
Future<void> init() async {
_rows = await localMediaDb.loadAllCovers();
_subscriptions.add(dynamicAlbums.eventBus.on<DynamicAlbumChangedEvent>().listen((e) => _updateCoveredDynamicAlbums(e.changes)));
_subscriptions.add(albumGrouping.eventBus.on<GroupUriChangedEvent>().listen((e) => _updateCoveredGroup(e.oldGroupUri, e.newGroupUri)));
}
int get count => _rows.length;
@ -172,6 +181,49 @@ class Covers {
return filterPackage ?? appInventory.getAlbumAppPackageName(albumPath);
}
Future<void> _updateCoveredDynamicAlbums(Map<DynamicAlbumFilter, DynamicAlbumFilter?> changes) async {
await _lock.synchronized(() async {
await Future.forEach(changes.entries, (kv) async {
final oldFilter = kv.key;
final newFilter = kv.value;
final cover = await covers.remove(oldFilter, notify: false);
if (cover != null && newFilter != null) {
await covers.set(
filter: newFilter,
entryId: cover.$1,
packageName: cover.$2,
color: cover.$3,
notify: true,
);
}
});
});
}
Future<void> _updateCoveredGroup(Uri oldGroupUri, Uri newGroupUri) async {
await _lock.synchronized(() async {
final grouping = FilterGrouping.forUri(oldGroupUri);
if (grouping != null) {
final oldFilter = grouping.uriToFilter(oldGroupUri);
final newFilter = grouping.uriToFilter(newGroupUri);
if (oldFilter != null) {
final cover = await covers.remove(oldFilter, notify: false);
if (cover != null && newFilter != null) {
await covers.set(
filter: newFilter,
entryId: cover.$1,
packageName: cover.$2,
color: cover.$3,
notify: true,
);
}
}
}
});
}
// import/export
List<Map<String, dynamic>>? export(CollectionSource source) {

View file

@ -1,17 +1,29 @@
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'dart:async';
import 'package:aves/model/filters/container/container.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/container/group_base.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/services/common/services.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/foundation.dart';
import 'package:synchronized/synchronized.dart';
final DynamicAlbums dynamicAlbums = DynamicAlbums._private();
class DynamicAlbums with ChangeNotifier {
final List<StreamSubscription> _subscriptions = [];
final _lock = Lock();
Set<DynamicAlbumFilter> _rows = {};
final EventBus eventBus = EventBus();
DynamicAlbums._private() {
if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this);
_subscriptions.add(albumGrouping.eventBus.on<GroupUriChangedEvent>().listen((e) => _onGroupUriChanged(e.oldGroupUri, e.newGroupUri)));
}
Future<void> init() async {
@ -22,38 +34,86 @@ class DynamicAlbums with ChangeNotifier {
Set<DynamicAlbumFilter> get all => Set.unmodifiable(_rows);
Future<void> add(DynamicAlbumFilter filter) async {
await localMediaDb.addDynamicAlbums({DynamicAlbumRow(name: filter.name, filter: filter.filter)});
_rows.add(filter);
Future<void> _doAdd(Set<DynamicAlbumFilter>? filters) async {
if (filters == null || filters.isEmpty) return;
await localMediaDb.addDynamicAlbums(filters.map((v) => DynamicAlbumRow(name: v.name, filter: v.filter)).toSet());
_rows.addAll(filters);
}
notifyListeners();
Future<void> _doRemove(Set<String>? names) async {
if (names == null || names.isEmpty) return;
await localMediaDb.removeDynamicAlbums(names);
_rows.removeWhere((v) => names.contains(v.name));
}
Future<void> add(DynamicAlbumFilter filter) async {
await _lock.synchronized(() async {
await _doAdd({filter});
notifyListeners();
});
}
Future<void> remove(Set<DynamicAlbumFilter> filters) async {
await localMediaDb.removeDynamicAlbums(filters.map((filter) => filter.name).toSet());
_rows.removeAll(filters);
await _lock.synchronized(() async {
await _doRemove(filters.map((filter) => filter.name).toSet());
notifyListeners();
});
}
notifyListeners();
Future<void> rename(DynamicAlbumFilter oldFilter, String newName) async {
await _lock.synchronized(() async {
final newFilter = DynamicAlbumFilter(newName, oldFilter.filter);
await _doRemove({oldFilter.name});
await _doAdd({newFilter});
notifyListeners();
eventBus.fire(DynamicAlbumChangedEvent({oldFilter: newFilter}));
});
}
Future<void> update(Map<DynamicAlbumFilter, DynamicAlbumFilter?> changes) async {
await _lock.synchronized(() async {
final oldFilterNames = changes.keys.map((v) => v.name).toSet();
final newFilters = changes.values.nonNulls.toSet();
await _doRemove(oldFilterNames);
await _doAdd(newFilters);
notifyListeners();
eventBus.fire(DynamicAlbumChangedEvent(changes));
});
}
Future<void> clear() async {
await localMediaDb.clearDynamicAlbums();
_rows.clear();
notifyListeners();
}
Future<void> rename(DynamicAlbumFilter filter, String newName) async {
await localMediaDb.removeDynamicAlbums({filter.name});
_rows.remove(filter);
await add(DynamicAlbumFilter(newName, filter.filter));
await _lock.synchronized(() async {
await localMediaDb.clearDynamicAlbums();
_rows.clear();
notifyListeners();
});
}
DynamicAlbumFilter? get(String name) => _rows.firstWhereOrNull((row) => row.name == name);
bool contains(String? name) => name != null && get(name) != null;
Future<void> _onGroupUriChanged(Uri oldGroupUri, Uri newGroupUri) async {
bool isOldGroupFilter(CollectionFilter filter) => filter is GroupBaseFilter && filter.uri == oldGroupUri;
final oldDynamicAlbumFilters = _rows.where((v) => v.containsFilter(isOldGroupFilter)).toSet();
if (oldDynamicAlbumFilters.isEmpty) return;
final grouping = FilterGrouping.forUri(oldGroupUri);
final newGroupFilter = grouping?.uriToFilter(newGroupUri);
CollectionFilter? transformer(v) {
if (isOldGroupFilter(v)) return v.reversed ? newGroupFilter?.reverse() : newGroupFilter;
if (v is ContainerFilter) return v.replaceFilters(transformer);
return v;
}
final newFilters = <DynamicAlbumFilter, DynamicAlbumFilter?>{};
oldDynamicAlbumFilters.forEach((oldFilter) {
final newFilter = oldFilter.replaceFilters(transformer);
newFilters[oldFilter] = newFilter;
});
await update(newFilters);
}
// import/export
List<String>? export() {
@ -107,3 +167,10 @@ class DynamicAlbumRow extends Equatable {
'filter': filter.toJson(),
};
}
@immutable
class DynamicAlbumChangedEvent {
final Map<DynamicAlbumFilter, DynamicAlbumFilter?> changes;
const DynamicAlbumChangedEvent(this.changes);
}

View file

@ -9,7 +9,7 @@ class AspectRatioFilter extends CollectionFilter {
final double threshold;
final String op;
late final EntryFilter _test;
late final EntryPredicate _test;
static final landscape = AspectRatioFilter(1, QueryFilter.opGreater);
static final portrait = AspectRatioFilter(1, QueryFilter.opLower);
@ -45,7 +45,7 @@ class AspectRatioFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -1,6 +1,6 @@
import 'package:aves/model/filters/covered/group_base.dart';
import 'package:aves/model/filters/container/group_base.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/filters/container/set_or.dart';
mixin AlbumBaseFilter on CollectionFilter {}
@ -46,4 +46,15 @@ class AlbumGroupFilter extends GroupBaseFilter with AlbumBaseFilter {
@override
String get key => '$type-$reversed-$uri';
// container
@override
AlbumGroupFilter? replaceFilters(CollectionFilter? Function(CollectionFilter oldFilter) toElement) {
return AlbumGroupFilter(
uri,
filter.replaceFilters(toElement),
reversed: reversed,
);
}
}

View file

@ -0,0 +1,11 @@
import 'package:aves/model/filters/filters.dart';
mixin ContainerFilter<T extends CollectionFilter> on CollectionFilter {
Set<CollectionFilter> get innerFilters;
bool containsFilter(CollectionFilterPredicate test) {
return innerFilters.any(test) || innerFilters.whereType<ContainerFilter>().any((v) => v.containsFilter(test));
}
T? replaceFilters(CollectionFilter? Function(CollectionFilter oldFilter) toElement);
}

View file

@ -1,10 +1,14 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/container.dart';
import 'package:aves/model/filters/covered/covered.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart';
import 'package:flutter/widgets.dart';
class DynamicAlbumFilter extends CollectionFilter with CoveredFilter, AlbumBaseFilter {
// a dynamic album can act as:
// - an alias, when inner filter is a simple filter,
// - a combination, when inner filter is a container.
class DynamicAlbumFilter extends CollectionFilter with ContainerFilter, CoveredFilter, AlbumBaseFilter {
static const type = 'dynamic_album';
final String name;
@ -35,7 +39,7 @@ class DynamicAlbumFilter extends CollectionFilter with CoveredFilter, AlbumBaseF
};
@override
EntryFilter get positiveTest => filter.test;
EntryPredicate get positiveTest => filter.test;
@override
bool get exclusiveProp => false;
@ -53,4 +57,21 @@ class DynamicAlbumFilter extends CollectionFilter with CoveredFilter, AlbumBaseF
@override
String get key => '$type-$reversed-$name';
// container
@override
Set<CollectionFilter> get innerFilters => {filter};
@override
DynamicAlbumFilter? replaceFilters(CollectionFilter? Function(CollectionFilter oldFilter) toElement) {
final newFilter = toElement(filter);
return newFilter != null
? DynamicAlbumFilter(
name,
newFilter,
reversed: reversed,
)
: null;
}
}

View file

@ -1,11 +1,12 @@
import 'package:aves/model/filters/container/container.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/filters/covered/covered.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/theme/icons.dart';
import 'package:flutter/widgets.dart';
abstract class GroupBaseFilter extends CollectionFilter with CoveredFilter {
abstract class GroupBaseFilter extends CollectionFilter with ContainerFilter, CoveredFilter {
final Uri uri;
final SetOrFilter filter;
late final String _name;
@ -20,7 +21,7 @@ abstract class GroupBaseFilter extends CollectionFilter with CoveredFilter {
}
@override
EntryFilter get positiveTest => filter.test;
EntryPredicate get positiveTest => filter.test;
@override
bool get exclusiveProp => false;
@ -32,4 +33,9 @@ abstract class GroupBaseFilter extends CollectionFilter with CoveredFilter {
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) {
return allowGenericIcon ? Icon(AIcons.group, size: size) : null;
}
// container
@override
Set<CollectionFilter> get innerFilters => {filter};
}

View file

@ -1,3 +1,4 @@
import 'package:aves/model/filters/container/container.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/covered/location.dart';
@ -5,12 +6,14 @@ import 'package:aves/theme/icons.dart';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
class SetAndFilter extends CollectionFilter {
// `AND` op for multiple filters
// can handle containing no filters
class SetAndFilter extends CollectionFilter with ContainerFilter {
static const type = 'and';
late final List<CollectionFilter> _filters;
late final EntryFilter _test;
late final EntryPredicate _test;
late final IconData? _genericIcon;
@override
@ -18,8 +21,6 @@ class SetAndFilter extends CollectionFilter {
CollectionFilter? get _first => _filters.firstOrNull;
Set<CollectionFilter> get innerFilters => _filters.toSet();
SetAndFilter(Set<CollectionFilter> filters, {super.reversed = false}) {
_filters = filters.toList().sorted();
_test = (entry) => _filters.every((v) => v.test(entry));
@ -53,7 +54,7 @@ class SetAndFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;
@ -74,4 +75,17 @@ class SetAndFilter extends CollectionFilter {
@override
String get key => '$type-$reversed-${_filters.map((v) => v.key)}';
// container
@override
Set<CollectionFilter> get innerFilters => _filters.toSet();
@override
SetAndFilter replaceFilters(CollectionFilter? Function(CollectionFilter oldFilter) toElement) {
return SetAndFilter(
_filters.map((v) => toElement(v)).nonNulls.toSet(),
reversed: reversed,
);
}
}

View file

@ -1,3 +1,4 @@
import 'package:aves/model/filters/container/container.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
@ -5,12 +6,14 @@ import 'package:aves/theme/icons.dart';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
class SetOrFilter extends CollectionFilter {
// `OR` op for multiple filters
// can handle containing no filters
class SetOrFilter extends CollectionFilter with ContainerFilter {
static const type = 'or';
late final List<CollectionFilter> _filters;
late final EntryFilter _test;
late final EntryPredicate _test;
late final IconData? _genericIcon;
@override
@ -18,8 +21,6 @@ class SetOrFilter extends CollectionFilter {
CollectionFilter? get _first => _filters.firstOrNull;
Set<CollectionFilter> get innerFilters => _filters.toSet();
SetOrFilter(Set<CollectionFilter> filters, {super.reversed = false}) {
_filters = filters.toList().sorted();
_test = (entry) => _filters.any((v) => v.test(entry));
@ -53,7 +54,7 @@ class SetOrFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;
@ -74,4 +75,17 @@ class SetOrFilter extends CollectionFilter {
@override
String get key => '$type-$reversed-${_filters.map((v) => v.key)}';
// container
@override
Set<CollectionFilter> get innerFilters => _filters.toSet();
@override
SetOrFilter replaceFilters(CollectionFilter? Function(CollectionFilter oldFilter) toElement) {
return SetOrFilter(
_filters.map((v) => toElement(v)).nonNulls.toSet(),
reversed: reversed,
);
}
}

View file

@ -15,7 +15,7 @@ class CoordinateFilter extends CollectionFilter {
final LatLng sw;
final LatLng ne;
final bool minuteSecondPadding;
late final EntryFilter _test;
late final EntryPredicate _test;
@override
List<Object?> get props => [sw, ne, reversed];
@ -41,7 +41,7 @@ class CoordinateFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
String _formatBounds(String Function(LatLng latLng) s) => '${s(ne)}\n${s(sw)}';

View file

@ -14,7 +14,7 @@ class LocationFilter extends CollectionFilter with CoveredFilter {
final LocationLevel level;
late final String _location;
late final String? _code;
late final EntryFilter _test;
late final EntryPredicate _test;
static final unlocated = LocationFilter(LocationLevel.place, '');
static final located = unlocated.reverse();
@ -78,7 +78,7 @@ class LocationFilter extends CollectionFilter with CoveredFilter {
String get place => _location;
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -1,5 +1,5 @@
import 'package:aves/model/covers.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/covered/covered.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/vaults/vaults.dart';
@ -18,7 +18,7 @@ class StoredAlbumFilter extends CollectionFilter with CoveredFilter, AlbumBaseFi
final String album;
final String? displayName;
late final EntryFilter _test;
late final EntryPredicate _test;
// do not include contextual `displayName` to `props`
@override
@ -45,7 +45,7 @@ class StoredAlbumFilter extends CollectionFilter with CoveredFilter, AlbumBaseFi
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -8,7 +8,7 @@ class TagFilter extends CollectionFilter with CoveredFilter {
static const type = 'tag';
final String tag;
late final EntryFilter _test;
late final EntryPredicate _test;
@override
List<Object?> get props => [tag, reversed];
@ -36,7 +36,7 @@ class TagFilter extends CollectionFilter with CoveredFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;

View file

@ -13,7 +13,7 @@ class DateFilter extends CollectionFilter {
final DateLevel level;
late final DateTime? date;
late final DateTime _effectiveDate;
late final EntryFilter _test;
late final EntryPredicate _test;
static final onThisDay = DateFilter(DateLevel.md, null);
@ -63,7 +63,7 @@ class DateFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -33,7 +33,7 @@ class FavouriteFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;

View file

@ -3,8 +3,8 @@ import 'dart:convert';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/aspect_ratio.dart';
import 'package:aves/model/filters/coordinate.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/covered/tag.dart';
@ -17,8 +17,8 @@ import 'package:aves/model/filters/placeholder.dart';
import 'package:aves/model/filters/query.dart';
import 'package:aves/model/filters/rating.dart';
import 'package:aves/model/filters/recent.dart';
import 'package:aves/model/filters/set_and.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/filters/container/set_and.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/filters/type.dart';
import 'package:aves/model/filters/weekday.dart';
@ -128,9 +128,9 @@ abstract class CollectionFilter extends Equatable implements Comparable<Collecti
String toJson() => jsonEncode(toMap());
EntryFilter get positiveTest;
EntryPredicate get positiveTest;
EntryFilter get test => reversed ? (v) => !positiveTest(v) : positiveTest;
EntryPredicate get test => reversed ? (v) => !positiveTest(v) : positiveTest;
CollectionFilter reverse() => _fromMap(toMap()..['reversed'] = !reversed)!;
@ -150,7 +150,7 @@ abstract class CollectionFilter extends Equatable implements Comparable<Collecti
String getTooltip(BuildContext context) => getLabel(context);
bool match(BuildContext context, String query) => getLabel(context).toUpperCase().contains(query);
bool matchLabel(BuildContext context, String query) => getLabel(context).toUpperCase().contains(query);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => null;
@ -185,7 +185,8 @@ class FilterGridItem<T extends CollectionFilter> with EquatableMixin {
const FilterGridItem(this.filter, this.entry);
}
typedef EntryFilter = bool Function(AvesEntry);
typedef EntryPredicate = bool Function(AvesEntry entry);
typedef CollectionFilterPredicate = bool Function(CollectionFilter filter);
abstract class DummyCollectionFilter extends CollectionFilter {
const DummyCollectionFilter({required super.reversed});
@ -200,7 +201,7 @@ abstract class DummyCollectionFilter extends CollectionFilter {
String get key => throw UnimplementedError();
@override
EntryFilter get positiveTest => throw UnimplementedError();
EntryPredicate get positiveTest => throw UnimplementedError();
@override
List<Object?> get props => throw UnimplementedError();

View file

@ -16,7 +16,7 @@ class MimeFilter extends CollectionFilter {
final String mime;
late final String _label;
late final IconData _icon;
late final EntryFilter _test;
late final EntryPredicate _test;
static final image = MimeFilter(MimeTypes.anyImage);
static final video = MimeFilter(MimeTypes.anyVideo);
@ -58,7 +58,7 @@ class MimeFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -12,7 +12,7 @@ class MissingFilter extends CollectionFilter {
final String metadataType;
late final IconData _icon;
late final EntryFilter _test;
late final EntryPredicate _test;
static final date = MissingFilter._private(_date);
static final fineAddress = MissingFilter._private(_fineAddress);
@ -50,7 +50,7 @@ class MissingFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;

View file

@ -14,7 +14,7 @@ class PathFilter extends CollectionFilter {
// without trailing separator
late final String _rootAlbum;
late final EntryFilter _test;
late final EntryPredicate _test;
@override
List<Object?> get props => [path, reversed];
@ -45,7 +45,7 @@ class PathFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -76,7 +76,7 @@ class PlaceholderFilter extends CollectionFilter {
}
@override
EntryFilter get positiveTest => (entry) => throw Exception('this is not a test');
EntryPredicate get positiveTest => (entry) => throw Exception('this is not a test');
@override
bool get exclusiveProp => false;

View file

@ -14,7 +14,7 @@ class QueryFilter extends CollectionFilter {
final String query;
final bool colorful, live;
late final EntryFilter _test;
late final EntryPredicate _test;
@override
List<Object?> get props => [query, live, reversed];
@ -74,7 +74,7 @@ class QueryFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;
@ -101,7 +101,7 @@ class QueryFilter extends CollectionFilter {
@override
String get key => '$type-$reversed-$query';
EntryFilter? fieldTest(String upQuery) {
EntryPredicate? fieldTest(String upQuery) {
var match = _fieldPattern.firstMatch(upQuery);
if (match == null) return null;

View file

@ -9,7 +9,7 @@ class RatingFilter extends CollectionFilter {
final int rating;
final String op;
late final EntryFilter _test;
late final EntryPredicate _test;
static const opEqual = '=';
static const opOrLower = '<=';
@ -49,7 +49,7 @@ class RatingFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart';
class RecentlyAddedFilter extends CollectionFilter {
static const type = 'recently_added';
static late EntryFilter _test;
static late EntryPredicate _test;
static final instance = RecentlyAddedFilter._private();
static final instanceReversed = RecentlyAddedFilter._private(reversed: true);
@ -39,7 +39,7 @@ class RecentlyAddedFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;

View file

@ -29,7 +29,7 @@ class TrashFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;

View file

@ -21,7 +21,7 @@ class TypeFilter extends CollectionFilter {
final String itemType;
late final IconData _icon;
late final EntryFilter _test;
late final EntryPredicate _test;
static final animated = TypeFilter._private(_animated);
static final geotiff = TypeFilter._private(_geotiff);
@ -75,7 +75,7 @@ class TypeFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => false;

View file

@ -8,7 +8,7 @@ class WeekDayFilter extends CollectionFilter {
static const type = 'weekday';
late final int weekday;
late final EntryFilter _test;
late final EntryPredicate _test;
@override
List<Object?> get props => [weekday, reversed];
@ -32,7 +32,7 @@ class WeekDayFilter extends CollectionFilter {
};
@override
EntryFilter get positiveTest => _test;
EntryPredicate get positiveTest => _test;
@override
bool get exclusiveProp => true;

View file

@ -1,13 +1,14 @@
import 'dart:convert';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/group_base.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/group_base.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/grouping/convert.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/utils/collection_utils.dart';
import 'package:collection/collection.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/foundation.dart';
final FilterGrouping albumGrouping = FilterGrouping._private(FilterGrouping.hostAlbums, AlbumGroupFilter.new);
@ -22,6 +23,8 @@ class FilterGrouping<T extends GroupBaseFilter> with ChangeNotifier {
static const _groupPath = '/group';
static const _groupPathParamKey = 'path';
final EventBus eventBus = EventBus();
final String _host;
final T Function(Uri uri, SetOrFilter filter) _createGroupFilter;
final Map<Uri, Set<Uri>> _groups = {};
@ -30,6 +33,15 @@ class FilterGrouping<T extends GroupBaseFilter> with ChangeNotifier {
if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this);
}
static FilterGrouping? forUri(Uri uri) {
switch (uri.host) {
case hostAlbums:
return albumGrouping;
default:
return null;
}
}
void clear() => _groups.clear();
// TODO TLAD [nested] invalidate summary for derived filters (parent groups, dynamic albums)
@ -52,6 +64,7 @@ class FilterGrouping<T extends GroupBaseFilter> with ChangeNotifier {
// local copy to prevent concurrent modification
addToGroup(Set.of(childrenUris), newUri);
}
eventBus.fire(GroupUriChangedEvent(oldUri, newUri));
}
bool get isNotEmpty => _groups.isNotEmpty;
@ -147,6 +160,7 @@ class FilterGrouping<T extends GroupBaseFilter> with ChangeNotifier {
}
}
eventBus.fire(GroupUriChangedEvent(oldGroupUri, newGroupUri));
_reparentGroupPaths(groupChildrenUris, newGroupUri);
}
}
@ -248,3 +262,11 @@ class FilterGrouping<T extends GroupBaseFilter> with ChangeNotifier {
}).whereNotNullKey();
}
}
@immutable
class GroupUriChangedEvent {
final Uri oldGroupUri;
final Uri newGroupUri;
const GroupUriChangedEvent(this.oldGroupUri, this.newGroupUri);
}

View file

@ -1,6 +1,6 @@
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/covered/group_base.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/container/group_base.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';

View file

@ -1,6 +1,10 @@
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/settings/defaults.dart';
import 'package:aves/utils/collection_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:synchronized/synchronized.dart';
mixin FilterGridsSettings on SettingsAccess {
AlbumChipSectionFactor get albumSectionFactor => getEnumOrDefault(SettingKeys.albumGroupFactorKey, SettingsDefaults.albumGroupFactor, AlbumChipSectionFactor.values);
@ -56,4 +60,41 @@ mixin FilterGridsSettings on SettingsAccess {
void setShowTitleQuery(String routeName, bool newValue) => set(SettingKeys.showTitleQueryPrefixKey + routeName, newValue);
// TODO TLAD [nested] save/load
final _lockForPins = Lock();
Future<void> updatePinnedDynamicAlbums(Map<DynamicAlbumFilter, DynamicAlbumFilter?> changes) async {
await _lockForPins.synchronized(() async {
final _pinnedFilters = pinnedFilters;
bool changed = false;
changes.forEach((oldFilter, newFilter) {
if (newFilter != null) {
changed |= _pinnedFilters.replace(oldFilter, newFilter);
} else {
changed |= _pinnedFilters.remove(oldFilter);
}
});
if (changed) {
pinnedFilters = _pinnedFilters;
}
});
}
Future<void> updatePinnedGroup(Uri oldGroupUri, Uri newGroupUri) async {
await _lockForPins.synchronized(() async {
final _pinnedFilters = pinnedFilters;
bool changed = false;
final grouping = FilterGrouping.forUri(oldGroupUri);
if (grouping != null) {
final oldFilter = grouping.uriToFilter(oldGroupUri);
final newFilter = grouping.uriToFilter(newGroupUri);
if (oldFilter != null && newFilter != null) {
changed |= _pinnedFilters.replace(oldFilter, newFilter);
}
}
if (changed) {
pinnedFilters = _pinnedFilters;
}
});
}
}

View file

@ -1,7 +1,11 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/settings/defaults.dart';
import 'package:aves/utils/collection_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:synchronized/synchronized.dart';
mixin NavigationSettings on SettingsAccess {
bool get mustBackTwiceToExit => getBool(SettingKeys.mustBackTwiceToExitKey) ?? SettingsDefaults.mustBackTwiceToExit;
@ -72,4 +76,45 @@ mixin NavigationSettings on SettingsAccess {
List<String> get drawerPageBookmarks => getStringList(SettingKeys.drawerPageBookmarksKey) ?? SettingsDefaults.drawerPageBookmarks;
set drawerPageBookmarks(List<String> newValue) => set(SettingKeys.drawerPageBookmarksKey, newValue);
final _lockForBookmarks = Lock();
Future<void> updateBookmarkedDynamicAlbums(Map<DynamicAlbumFilter, DynamicAlbumFilter?> changes) async {
await _lockForBookmarks.synchronized(() async {
final _bookmarks = drawerAlbumBookmarks;
bool changed = false;
if (_bookmarks != null) {
changes.forEach((oldFilter, newFilter) {
if (newFilter != null) {
changed |= _bookmarks.replace(oldFilter, newFilter);
} else {
changed |= _bookmarks.remove(oldFilter);
}
});
}
if (changed) {
drawerAlbumBookmarks = _bookmarks;
}
});
}
Future<void> updateBookmarkedGroup(Uri oldGroupUri, Uri newGroupUri) async {
await _lockForBookmarks.synchronized(() async {
final _bookmarks = drawerAlbumBookmarks;
bool changed = false;
if (_bookmarks != null) {
final grouping = FilterGrouping.forUri(oldGroupUri);
if (grouping != null) {
final oldFilter = grouping.uriToFilter(oldGroupUri);
final newFilter = grouping.uriToFilter(newGroupUri);
if (oldFilter is AlbumBaseFilter && newFilter is AlbumBaseFilter) {
changed |= _bookmarks.replace(oldFilter, newFilter);
}
}
}
if (changed) {
drawerAlbumBookmarks = _bookmarks;
}
});
}
}

View file

@ -4,8 +4,10 @@ import 'dart:math';
import 'package:aves/app_flavor.dart';
import 'package:aves/model/device.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/settings/defaults.dart';
import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/enums/map_style.dart';
@ -67,10 +69,21 @@ class Settings with ChangeNotifier, SettingsAccess, SearchSettings, AppSettings,
Future<void> init({required bool monitorPlatformSettings}) async {
await store.init();
resetAppliedLocale();
_subscriptions
..forEach((sub) => sub.cancel())
..clear();
_subscriptions.add(dynamicAlbums.eventBus.on<DynamicAlbumChangedEvent>().listen((e) {
final changes = e.changes;
updateBookmarkedDynamicAlbums(changes);
updatePinnedDynamicAlbums(changes);
}));
_subscriptions.add(albumGrouping.eventBus.on<GroupUriChangedEvent>().listen((e) {
final oldGroupUri = e.oldGroupUri;
final newGroupUri = e.newGroupUri;
updateBookmarkedGroup(oldGroupUri, newGroupUri);
updatePinnedGroup(oldGroupUri, newGroupUri);
}));
if (monitorPlatformSettings) {
_subscriptions
..forEach((sub) => sub.cancel())
..clear();
_subscriptions.add(_platformSettingsChangeChannel.receiveBroadcastStream().listen((event) => _onPlatformSettingsChanged(event as Map?)));
}
initAppSettings();

View file

@ -1,6 +1,6 @@
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';

View file

@ -8,7 +8,7 @@ import 'package:aves/model/entry/extensions/keys.dart';
import 'package:aves/model/entry/extensions/location.dart';
import 'package:aves/model/entry/sort.dart';
import 'package:aves/model/favourites.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/covered/tag.dart';

View file

@ -3,10 +3,10 @@ import 'dart:math';
import 'package:aves/app_mode.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/query.dart';
import 'package:aves/model/filters/set_and.dart';
import 'package:aves/model/filters/container/set_and.dart';
import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/query.dart';
import 'package:aves/model/selection.dart';

View file

@ -9,9 +9,9 @@ import 'package:aves/model/entry/extensions/metadata_edition.dart';
import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/favourites.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/set_and.dart';
import 'package:aves/model/filters/container/set_and.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/highlight.dart';
import 'package:aves/model/metadata/date_modifier.dart';

View file

@ -238,9 +238,17 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
return Row(
children: [
Expanded(
child: Text('cover rows: ${snapshot.data!.length} (${covers.count} in memory)'),
child: Text('covers: ${snapshot.data!.length} rows\n(${covers.count} in memory)'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () => localMediaDb.loadAllCovers().then((list) {
debugPrint('covers dump start');
list.forEach((v) => debugPrint(' $v'));
debugPrint('covers albums dump end');
}),
child: const Text('Dump'),
),
ElevatedButton(
onPressed: () => covers.clear().then((_) => _reload()),
child: const Text('Clear'),
@ -259,9 +267,17 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
return Row(
children: [
Expanded(
child: Text('dynamic album rows: ${snapshot.data!.length} (${dynamicAlbums.count} in memory)'),
child: Text('dynamic albums: ${snapshot.data!.length} rows\n(${dynamicAlbums.count} in memory)'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () => localMediaDb.loadAllDynamicAlbums().then((list) {
debugPrint('dynamic albums dump start');
list.forEach((v) => debugPrint(' $v'));
debugPrint('dynamic albums dump end');
}),
child: const Text('Dump'),
),
ElevatedButton(
onPressed: () => dynamicAlbums.clear().then((_) => _reload()),
child: const Text('Clear'),

View file

@ -1,6 +1,6 @@
import 'package:aves/app_mode.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';

View file

@ -2,8 +2,8 @@ import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/covers.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';

View file

@ -5,8 +5,8 @@ import 'package:aves/app_mode.dart';
import 'package:aves/model/covers.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';
@ -21,7 +21,6 @@ import 'package:aves/services/common/services.dart';
import 'package:aves/services/media/enums.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/collection_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_mixins/entry_storage.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart';
@ -41,7 +40,6 @@ import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
@ -526,68 +524,19 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumBaseFilter>
}
}
Future<void> _doRenameNonStoredAlbum<T extends AlbumBaseFilter>(
BuildContext context,
T oldFilter,
Future<T?> Function() createNewFilter,
bool Function(T filter) isRenamed,
) async {
// save cover and bookmark before renaming
final cover = await covers.remove(oldFilter, notify: false);
final bookmarks = settings.drawerAlbumBookmarks;
final pinnedFilters = settings.pinnedFilters;
// new filter to match new name
final newFilter = await createNewFilter();
Future<void> _doRenameAlbumGroup(BuildContext context, AlbumGroupFilter oldFilter, Uri newUri) async {
albumGrouping.rename(oldFilter.uri, newUri);
final newFilter = albumGrouping.uriToFilter(newUri);
if (newFilter == null) {
showFeedback(context, FeedbackType.warn, context.l10n.genericFailureFeedback);
return;
}
bool _isRenamed(CollectionFilter? v) => v is T && isRenamed(v);
// update cover
if (cover != null) {
await covers.set(
filter: newFilter,
entryId: cover.$1,
packageName: cover.$2,
color: cover.$3,
notify: true,
);
}
// update drawer bookmark
final bookmark = bookmarks?.firstWhereOrNull(_isRenamed);
if (bookmark != null) {
bookmarks?.replace(bookmark, newFilter);
settings.drawerAlbumBookmarks = bookmarks;
}
// update pin
final pin = pinnedFilters.firstWhereOrNull(_isRenamed);
if (pin != null) {
pinnedFilters.replace(pin, newFilter);
settings.pinnedFilters = pinnedFilters;
}
browse(context);
}
Future<void> _doRenameAlbumGroup(BuildContext context, AlbumGroupFilter oldFilter, Uri newUri) async {
final oldUri = oldFilter.uri;
await _doRenameNonStoredAlbum<AlbumGroupFilter>(context, oldFilter, () {
albumGrouping.rename(oldUri, newUri);
final newFilter = albumGrouping.uriToFilter(newUri);
return SynchronousFuture(newFilter is AlbumGroupFilter ? newFilter : null);
}, (filter) => filter.uri == oldUri);
}
Future<void> _doRenameDynamicAlbum(BuildContext context, DynamicAlbumFilter oldFilter, String newName) async {
final oldName = oldFilter.name;
await _doRenameNonStoredAlbum<DynamicAlbumFilter>(context, oldFilter, () async {
await dynamicAlbums.rename(oldFilter, newName);
return DynamicAlbumFilter(newName, oldFilter.filter);
}, (filter) => filter.name == oldName);
await dynamicAlbums.rename(oldFilter, newName);
browse(context);
}
Future<void> _doRenameStoredAlbum(BuildContext context, StoredAlbumFilter albumFilter, String newName) async {

View file

@ -1,5 +1,5 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/covered/tag.dart';

View file

@ -3,7 +3,7 @@ import 'package:aves/model/covers.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/query.dart';
import 'package:aves/model/selection.dart';
import 'package:aves/model/settings/settings.dart';

View file

@ -2,8 +2,8 @@ import 'dart:math';
import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/covers.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/covered/tag.dart';

View file

@ -311,7 +311,7 @@ class _FilterGridContentState<T extends CollectionFilter> extends State<_FilterG
visibleSections = {};
final queryUp = query.toUpperCase();
widget.sections.forEach((sectionKey, sectionFilters) {
final visibleFilters = sectionFilters.where((item) => item.filter.match(context, queryUp)).toList();
final visibleFilters = sectionFilters.where((item) => item.filter.matchLabel(context, queryUp)).toList();
if (visibleFilters.isNotEmpty) {
visibleSections[sectionKey] = visibleFilters;
}

View file

@ -1,4 +1,4 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/selection.dart';

View file

@ -1,6 +1,6 @@
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/collection_source.dart';

View file

@ -1,5 +1,5 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/settings/settings.dart';

View file

@ -1,6 +1,6 @@
import 'dart:async';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/collection_source.dart';

View file

@ -1,6 +1,6 @@
import 'dart:math';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/enums/home_page.dart';

View file

@ -1,7 +1,7 @@
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/filters/aspect_ratio.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/covered/tag.dart';
@ -13,7 +13,7 @@ import 'package:aves/model/filters/missing.dart';
import 'package:aves/model/filters/query.dart';
import 'package:aves/model/filters/rating.dart';
import 'package:aves/model/filters/recent.dart';
import 'package:aves/model/filters/set_and.dart';
import 'package:aves/model/filters/container/set_and.dart';
import 'package:aves/model/filters/type.dart';
import 'package:aves/model/filters/weekday.dart';
import 'package:aves/model/grouping/common.dart';

View file

@ -1,4 +1,4 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/recent.dart';
import 'package:aves/model/settings/settings.dart';

View file

@ -1,4 +1,4 @@
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';

View file

@ -1508,7 +1508,7 @@ packages:
source: hosted
version: "0.3.1"
synchronized:
dependency: transitive
dependency: "direct main"
description:
name: synchronized
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"

View file

@ -124,6 +124,7 @@ dependencies:
streams_channel:
git:
url: https://github.com/deckerst/aves_streams_channel.git
synchronized:
url_launcher:
vector_map_tiles: ^8.0.0 # vector_map_tiles v9.0.0-beta.6 has a buggy cross-platform definition for `cacheFolder`
vector_math:

View file

@ -1,7 +1,7 @@
import 'package:aves/model/filters/aspect_ratio.dart';
import 'package:aves/model/filters/coordinate.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/location.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/covered/tag.dart';
@ -15,8 +15,8 @@ import 'package:aves/model/filters/placeholder.dart';
import 'package:aves/model/filters/query.dart';
import 'package:aves/model/filters/rating.dart';
import 'package:aves/model/filters/recent.dart';
import 'package:aves/model/filters/set_and.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/filters/container/set_and.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/filters/type.dart';
import 'package:aves/model/filters/weekday.dart';
import 'package:aves/model/grouping/common.dart';

View file

@ -1,9 +1,9 @@
import 'package:aves/model/db/db.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/grouping/convert.dart';
import 'package:aves/services/common/services.dart';

View file

@ -1,9 +1,9 @@
import 'package:aves/model/db/db.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/filters/covered/album_group.dart';
import 'package:aves/model/filters/covered/dynamic_album.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/set_or.dart';
import 'package:aves/model/filters/container/set_or.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/grouping/convert.dart';
import 'package:aves/services/common/services.dart';