added album grid page
This commit is contained in:
parent
2bc16bd373
commit
00d3c9a86e
11 changed files with 69 additions and 62 deletions
|
@ -2,6 +2,7 @@ import 'package:aves/model/collection_lens.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/model/image_metadata.dart';
|
import 'package:aves/model/image_metadata.dart';
|
||||||
import 'package:aves/model/metadata_db.dart';
|
import 'package:aves/model/metadata_db.dart';
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:event_bus/event_bus.dart';
|
import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -164,6 +165,28 @@ class CollectionSource {
|
||||||
sortFactor: SortFactor.date,
|
sortFactor: SortFactor.date,
|
||||||
).sortedEntries;
|
).sortedEntries;
|
||||||
|
|
||||||
|
Map<String, ImageEntry> getAlbumEntries() {
|
||||||
|
final entries = _sortedEntriesForFilterList;
|
||||||
|
final regularAlbums = <String>[], appAlbums = <String>[], specialAlbums = <String>[];
|
||||||
|
for (var album in sortedAlbums) {
|
||||||
|
switch (androidFileUtils.getAlbumType(album)) {
|
||||||
|
case AlbumType.regular:
|
||||||
|
regularAlbums.add(album);
|
||||||
|
break;
|
||||||
|
case AlbumType.app:
|
||||||
|
appAlbums.add(album);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
specialAlbums.add(album);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Map.fromEntries([...specialAlbums, ...appAlbums, ...regularAlbums].map((tag) => MapEntry(
|
||||||
|
tag,
|
||||||
|
entries.firstWhere((entry) => entry.directory == tag, orElse: () => null),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, ImageEntry> getCountryEntries() {
|
Map<String, ImageEntry> getCountryEntries() {
|
||||||
final locatedEntries = _sortedEntriesForFilterList.where((entry) => entry.isLocated);
|
final locatedEntries = _sortedEntriesForFilterList.where((entry) => entry.isLocated);
|
||||||
return Map.fromEntries(sortedCountries.map((countryNameAndCode) {
|
return Map.fromEntries(sortedCountries.map((countryNameAndCode) {
|
||||||
|
|
|
@ -14,7 +14,6 @@ class AlbumFilter extends CollectionFilter {
|
||||||
static final Map<String, Color> _appColors = {};
|
static final Map<String, Color> _appColors = {};
|
||||||
|
|
||||||
final String album;
|
final String album;
|
||||||
|
|
||||||
final String uniqueName;
|
final String uniqueName;
|
||||||
|
|
||||||
const AlbumFilter(this.album, this.uniqueName);
|
const AlbumFilter(this.album, this.uniqueName);
|
||||||
|
@ -29,8 +28,8 @@ class AlbumFilter extends CollectionFilter {
|
||||||
String get tooltip => album;
|
String get tooltip => album;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget iconBuilder(context, size) {
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
|
||||||
return IconUtils.getAlbumIcon(context: context, album: album, size: size) ?? Icon(AIcons.album, size: size);
|
return IconUtils.getAlbumIcon(context: context, album: album, size: size) ?? (showGenericIcon ? Icon(AIcons.album, size: size) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -14,7 +14,7 @@ class FavouriteFilter extends CollectionFilter {
|
||||||
String get label => 'Favourite';
|
String get label => 'Favourite';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget iconBuilder(context, size) => Icon(AIcons.favourite, size: size);
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.favourite, size: size);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get typeKey => type;
|
String get typeKey => type;
|
||||||
|
|
|
@ -30,7 +30,7 @@ abstract class CollectionFilter implements Comparable<CollectionFilter> {
|
||||||
|
|
||||||
String get tooltip => label;
|
String get tooltip => label;
|
||||||
|
|
||||||
Widget iconBuilder(BuildContext context, double size);
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true});
|
||||||
|
|
||||||
Future<Color> color(BuildContext context) => SynchronousFuture(stringToColor(label));
|
Future<Color> color(BuildContext context) => SynchronousFuture(stringToColor(label));
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class LocationFilter extends CollectionFilter {
|
||||||
String get label => _location;
|
String get label => _location;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget iconBuilder(context, size) {
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
|
||||||
final flag = countryCodeToFlag(_countryCode);
|
final flag = countryCodeToFlag(_countryCode);
|
||||||
if (flag != null) return Text(flag, style: TextStyle(fontSize: size));
|
if (flag != null) return Text(flag, style: TextStyle(fontSize: size));
|
||||||
return Icon(AIcons.location, size: size);
|
return Icon(AIcons.location, size: size);
|
||||||
|
|
|
@ -49,7 +49,7 @@ class MimeFilter extends CollectionFilter {
|
||||||
String get label => _label;
|
String get label => _label;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget iconBuilder(context, size) => Icon(_icon, size: size);
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get typeKey => type;
|
String get typeKey => type;
|
||||||
|
|
|
@ -42,7 +42,7 @@ class QueryFilter extends CollectionFilter {
|
||||||
String get label => '${query}';
|
String get label => '${query}';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget iconBuilder(context, size) => Icon(AIcons.text, size: size);
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.text, size: size);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Color> color(BuildContext context) => colorful ? super.color(context) : SynchronousFuture(Colors.white);
|
Future<Color> color(BuildContext context) => colorful ? super.color(context) : SynchronousFuture(Colors.white);
|
||||||
|
|
|
@ -20,7 +20,7 @@ class TagFilter extends CollectionFilter {
|
||||||
String get label => tag;
|
String get label => tag;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget iconBuilder(context, size) => Icon(AIcons.tag, size: size);
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => showGenericIcon ? Icon(AIcons.tag, size: size) : null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get typeKey => type;
|
String get typeKey => type;
|
||||||
|
|
|
@ -31,8 +31,6 @@ class AppDrawer extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppDrawerState extends State<AppDrawer> {
|
class _AppDrawerState extends State<AppDrawer> {
|
||||||
bool _albumsExpanded = false;
|
|
||||||
|
|
||||||
CollectionSource get source => widget.source;
|
CollectionSource get source => widget.source;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -184,49 +182,25 @@ class _AppDrawerState extends State<AppDrawer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRegularAlbumSection() {
|
Widget _buildRegularAlbumSection() {
|
||||||
return StreamBuilder(
|
return SafeArea(
|
||||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
top: false,
|
||||||
builder: (context, snapshot) {
|
bottom: false,
|
||||||
final regularAlbums = <String>[], appAlbums = <String>[];
|
child: ListTile(
|
||||||
for (var album in source.sortedAlbums) {
|
leading: const Icon(AIcons.album),
|
||||||
switch (androidFileUtils.getAlbumType(album)) {
|
title: const Text('Albums'),
|
||||||
case AlbumType.regular:
|
trailing: StreamBuilder(
|
||||||
regularAlbums.add(album);
|
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||||
break;
|
builder: (context, snapshot) {
|
||||||
case AlbumType.app:
|
return Text(
|
||||||
appAlbums.add(album);
|
'${source.sortedAlbums.length}',
|
||||||
break;
|
style: TextStyle(
|
||||||
default:
|
color: Colors.white.withOpacity(.6),
|
||||||
break;
|
),
|
||||||
}
|
);
|
||||||
}
|
}),
|
||||||
if (appAlbums.isEmpty && regularAlbums.isEmpty) return const SizedBox.shrink();
|
onTap: () => _goToAlbums(context),
|
||||||
return SafeArea(
|
),
|
||||||
top: false,
|
);
|
||||||
bottom: false,
|
|
||||||
child: ExpansionTile(
|
|
||||||
leading: const Icon(AIcons.album),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
const Text('Albums'),
|
|
||||||
const Spacer(),
|
|
||||||
Text(
|
|
||||||
'${appAlbums.length + regularAlbums.length}',
|
|
||||||
style: TextStyle(
|
|
||||||
color: (_albumsExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onExpansionChanged: (expanded) => setState(() => _albumsExpanded = expanded),
|
|
||||||
children: [
|
|
||||||
...appAlbums.map(_buildAlbumEntry),
|
|
||||||
if (appAlbums.isNotEmpty && regularAlbums.isNotEmpty) const Divider(),
|
|
||||||
...regularAlbums.map(_buildAlbumEntry),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCountrySection() {
|
Widget _buildCountrySection() {
|
||||||
|
@ -273,6 +247,21 @@ class _AppDrawerState extends State<AppDrawer> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _goToAlbums(BuildContext context) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => FilterGridPage(
|
||||||
|
source: source,
|
||||||
|
title: 'Albums',
|
||||||
|
filterEntries: source.getAlbumEntries(),
|
||||||
|
filterBuilder: (s) => AlbumFilter(s, source.getUniqueAlbumName(s)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _goToCountries(BuildContext context) {
|
void _goToCountries(BuildContext context) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
|
@ -283,7 +272,6 @@ class _AppDrawerState extends State<AppDrawer> {
|
||||||
title: 'Countries',
|
title: 'Countries',
|
||||||
filterEntries: source.getCountryEntries(),
|
filterEntries: source.getCountryEntries(),
|
||||||
filterBuilder: (s) => LocationFilter(LocationLevel.country, s),
|
filterBuilder: (s) => LocationFilter(LocationLevel.country, s),
|
||||||
showFilterIcon: true,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -299,7 +287,6 @@ class _AppDrawerState extends State<AppDrawer> {
|
||||||
title: 'Tags',
|
title: 'Tags',
|
||||||
filterEntries: source.getTagEntries(),
|
filterEntries: source.getTagEntries(),
|
||||||
filterBuilder: (s) => TagFilter(s),
|
filterBuilder: (s) => TagFilter(s),
|
||||||
showFilterIcon: false,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ typedef FilterCallback = void Function(CollectionFilter filter);
|
||||||
class AvesFilterChip extends StatefulWidget {
|
class AvesFilterChip extends StatefulWidget {
|
||||||
final CollectionFilter filter;
|
final CollectionFilter filter;
|
||||||
final bool removable;
|
final bool removable;
|
||||||
final bool showLeading;
|
final bool showGenericIcon;
|
||||||
final Decoration decoration;
|
final Decoration decoration;
|
||||||
final FilterCallback onPressed;
|
final FilterCallback onPressed;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class AvesFilterChip extends StatefulWidget {
|
||||||
Key key,
|
Key key,
|
||||||
this.filter,
|
this.filter,
|
||||||
this.removable = false,
|
this.removable = false,
|
||||||
this.showLeading = true,
|
this.showGenericIcon = true,
|
||||||
this.decoration,
|
this.decoration,
|
||||||
@required this.onPressed,
|
@required this.onPressed,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
@ -54,7 +54,7 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final leading = widget.showLeading ? filter.iconBuilder(context, AvesFilterChip.iconSize) : null;
|
final leading = filter.iconBuilder(context, AvesFilterChip.iconSize, showGenericIcon: widget.showGenericIcon);
|
||||||
final trailing = widget.removable ? const Icon(AIcons.clear, size: AvesFilterChip.iconSize) : null;
|
final trailing = widget.removable ? const Icon(AIcons.clear, size: AvesFilterChip.iconSize) : null;
|
||||||
|
|
||||||
Widget content = Padding(
|
Widget content = Padding(
|
||||||
|
|
|
@ -17,14 +17,12 @@ class FilterGridPage extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final Map<String, ImageEntry> filterEntries;
|
final Map<String, ImageEntry> filterEntries;
|
||||||
final CollectionFilter Function(String key) filterBuilder;
|
final CollectionFilter Function(String key) filterBuilder;
|
||||||
final bool showFilterIcon;
|
|
||||||
|
|
||||||
const FilterGridPage({
|
const FilterGridPage({
|
||||||
@required this.source,
|
@required this.source,
|
||||||
@required this.title,
|
@required this.title,
|
||||||
@required this.filterEntries,
|
@required this.filterEntries,
|
||||||
@required this.filterBuilder,
|
@required this.filterBuilder,
|
||||||
@required this.showFilterIcon,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
List<String> get filterKeys => filterEntries.keys.toList();
|
List<String> get filterKeys => filterEntries.keys.toList();
|
||||||
|
@ -62,7 +60,7 @@ class FilterGridPage extends StatelessWidget {
|
||||||
}
|
}
|
||||||
return AvesFilterChip(
|
return AvesFilterChip(
|
||||||
filter: filterBuilder(key),
|
filter: filterBuilder(key),
|
||||||
showLeading: showFilterIcon,
|
showGenericIcon: false,
|
||||||
decoration: decoration,
|
decoration: decoration,
|
||||||
onPressed: (filter) => Navigator.pushAndRemoveUntil(
|
onPressed: (filter) => Navigator.pushAndRemoveUntil(
|
||||||
context,
|
context,
|
||||||
|
|
Loading…
Reference in a new issue