353 lines
11 KiB
Dart
353 lines
11 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:aves/model/collection_lens.dart';
|
|
import 'package:aves/model/collection_source.dart';
|
|
import 'package:aves/model/filters/album.dart';
|
|
import 'package:aves/model/filters/favourite.dart';
|
|
import 'package:aves/model/filters/filters.dart';
|
|
import 'package:aves/model/filters/location.dart';
|
|
import 'package:aves/model/filters/mime.dart';
|
|
import 'package:aves/model/filters/tag.dart';
|
|
import 'package:aves/model/mime_types.dart';
|
|
import 'package:aves/model/settings.dart';
|
|
import 'package:aves/utils/android_file_utils.dart';
|
|
import 'package:aves/utils/color_utils.dart';
|
|
import 'package:aves/widgets/album/collection_page.dart';
|
|
import 'package:aves/widgets/common/aves_logo.dart';
|
|
import 'package:aves/widgets/common/icons.dart';
|
|
import 'package:aves/widgets/debug_page.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:outline_material_icons/outline_material_icons.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
class CollectionDrawer extends StatefulWidget {
|
|
final CollectionSource source;
|
|
|
|
const CollectionDrawer({@required this.source});
|
|
|
|
@override
|
|
_CollectionDrawerState createState() => _CollectionDrawerState();
|
|
}
|
|
|
|
class _CollectionDrawerState extends State<CollectionDrawer> {
|
|
bool _albumsExpanded = false, _placesExpanded = false, _countriesExpanded = false, _tagsExpanded = false;
|
|
|
|
CollectionSource get source => widget.source;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final header = Container(
|
|
decoration: BoxDecoration(
|
|
border: Border(
|
|
bottom: Divider.createBorderSide(context),
|
|
),
|
|
),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(16),
|
|
color: Theme.of(context).accentColor,
|
|
child: SafeArea(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
const AvesLogo(size: 64),
|
|
const SizedBox(width: 16),
|
|
const Text(
|
|
'Aves',
|
|
style: TextStyle(
|
|
fontSize: 44,
|
|
fontFamily: 'Concourse Caps',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final allMediaEntry = _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: const Icon(OMIcons.photo),
|
|
title: 'All media',
|
|
filter: null,
|
|
);
|
|
final videoEntry = _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: const Icon(AIcons.video),
|
|
title: 'Videos',
|
|
filter: MimeFilter(MimeTypes.ANY_VIDEO),
|
|
);
|
|
final animatedEntry = _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: const Icon(AIcons.animated),
|
|
title: 'Animated',
|
|
filter: MimeFilter(MimeFilter.animated),
|
|
);
|
|
final favouriteEntry = _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: const Icon(AIcons.favourite),
|
|
title: 'Favourites',
|
|
filter: FavouriteFilter(),
|
|
);
|
|
final buildAlbumEntry = (String album) => _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: IconUtils.getAlbumIcon(context: context, album: album),
|
|
title: source.getUniqueAlbumName(album),
|
|
dense: true,
|
|
filter: AlbumFilter(album, source.getUniqueAlbumName(album)),
|
|
);
|
|
final buildTagEntry = (String tag) => _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: Icon(
|
|
AIcons.tag,
|
|
color: stringToColor(tag),
|
|
),
|
|
title: tag,
|
|
dense: true,
|
|
filter: TagFilter(tag),
|
|
);
|
|
final buildLocationEntry = (LocationLevel level, String location) {
|
|
String title;
|
|
String flag;
|
|
if (level == LocationLevel.country) {
|
|
final split = location.split(';');
|
|
String countryCode;
|
|
if (split.isNotEmpty) title = split[0];
|
|
if (split.length > 1) countryCode = split[1];
|
|
flag = LocationFilter.countryCodeToFlag(countryCode);
|
|
} else {
|
|
title = location;
|
|
}
|
|
return _FilteredCollectionNavTile(
|
|
source: source,
|
|
leading: flag != null
|
|
? Text(
|
|
flag,
|
|
style: TextStyle(fontSize: IconTheme.of(context).size),
|
|
)
|
|
: Icon(
|
|
AIcons.location,
|
|
color: stringToColor(title),
|
|
),
|
|
title: title,
|
|
dense: true,
|
|
filter: LocationFilter(level, location),
|
|
);
|
|
};
|
|
|
|
final regularAlbums = <String>[], appAlbums = <String>[], specialAlbums = <String>[];
|
|
for (var album in source.sortedAlbums) {
|
|
switch (androidFileUtils.getAlbumType(album)) {
|
|
case AlbumType.Default:
|
|
regularAlbums.add(album);
|
|
break;
|
|
case AlbumType.App:
|
|
appAlbums.add(album);
|
|
break;
|
|
default:
|
|
specialAlbums.add(album);
|
|
break;
|
|
}
|
|
}
|
|
final countries = source.sortedCountries;
|
|
final places = source.sortedPlaces;
|
|
final tags = source.sortedTags;
|
|
|
|
final drawerItems = <Widget>[
|
|
header,
|
|
allMediaEntry,
|
|
videoEntry,
|
|
animatedEntry,
|
|
favouriteEntry,
|
|
if (specialAlbums.isNotEmpty) ...[
|
|
const Divider(),
|
|
...specialAlbums.map(buildAlbumEntry),
|
|
],
|
|
if (appAlbums.isNotEmpty || regularAlbums.isNotEmpty)
|
|
SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: ExpansionTile(
|
|
leading: const Icon(OMIcons.photoAlbum),
|
|
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),
|
|
],
|
|
),
|
|
),
|
|
if (countries.isNotEmpty)
|
|
SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: ExpansionTile(
|
|
leading: const Icon(AIcons.location),
|
|
title: Row(
|
|
children: [
|
|
const Text('Countries'),
|
|
const Spacer(),
|
|
Text(
|
|
'${countries.length}',
|
|
style: TextStyle(
|
|
color: (_countriesExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
onExpansionChanged: (expanded) => setState(() => _countriesExpanded = expanded),
|
|
children: countries.map((s) => buildLocationEntry(LocationLevel.country, s)).toList(),
|
|
),
|
|
),
|
|
if (places.isNotEmpty)
|
|
SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: ExpansionTile(
|
|
leading: const Icon(AIcons.location),
|
|
title: Row(
|
|
children: [
|
|
const Text('Places'),
|
|
const Spacer(),
|
|
Text(
|
|
'${places.length}',
|
|
style: TextStyle(
|
|
color: (_placesExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
onExpansionChanged: (expanded) => setState(() => _placesExpanded = expanded),
|
|
children: places.map((s) => buildLocationEntry(LocationLevel.place, s)).toList(),
|
|
),
|
|
),
|
|
if (tags.isNotEmpty)
|
|
SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: ExpansionTile(
|
|
leading: const Icon(AIcons.tag),
|
|
title: Row(
|
|
children: [
|
|
const Text('Tags'),
|
|
const Spacer(),
|
|
Text(
|
|
'${tags.length}',
|
|
style: TextStyle(
|
|
color: (_tagsExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
onExpansionChanged: (expanded) => setState(() => _tagsExpanded = expanded),
|
|
children: tags.map(buildTagEntry).toList(),
|
|
),
|
|
),
|
|
if (kDebugMode) ...[
|
|
const Divider(),
|
|
SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: ListTile(
|
|
leading: const Icon(OMIcons.whatshot),
|
|
title: const Text('Debug'),
|
|
onTap: () => _goToDebug(context),
|
|
),
|
|
),
|
|
],
|
|
];
|
|
|
|
return Drawer(
|
|
child: Selector<MediaQueryData, double>(
|
|
selector: (c, mq) => mq.viewInsets.bottom,
|
|
builder: (c, mqViewInsetsBottom, child) {
|
|
return SingleChildScrollView(
|
|
padding: EdgeInsets.only(bottom: mqViewInsetsBottom),
|
|
child: Theme(
|
|
data: Theme.of(context).copyWith(
|
|
// color used by `ExpansionTile` for leading icon
|
|
unselectedWidgetColor: Colors.white,
|
|
),
|
|
child: Column(
|
|
children: drawerItems,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
void _goToDebug(BuildContext context) {
|
|
Navigator.pop(context);
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => DebugPage(
|
|
source: source,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _FilteredCollectionNavTile extends StatelessWidget {
|
|
final CollectionSource source;
|
|
final Widget leading;
|
|
final String title;
|
|
final bool dense;
|
|
final CollectionFilter filter;
|
|
|
|
const _FilteredCollectionNavTile({
|
|
@required this.source,
|
|
@required this.leading,
|
|
@required this.title,
|
|
bool dense,
|
|
@required this.filter,
|
|
}) : dense = dense ?? false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: ListTile(
|
|
leading: leading,
|
|
title: Text(title),
|
|
dense: dense,
|
|
onTap: () => _goToCollection(context),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _goToCollection(BuildContext context) {
|
|
Navigator.pushAndRemoveUntil(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => CollectionPage(CollectionLens(
|
|
source: source,
|
|
filters: [filter],
|
|
groupFactor: settings.collectionGroupFactor,
|
|
sortFactor: settings.collectionSortFactor,
|
|
)),
|
|
),
|
|
(route) => false,
|
|
);
|
|
}
|
|
}
|