drawer on all pages

This commit is contained in:
Thibault Deckers 2020-03-26 10:56:02 +09:00
parent 578835d3d2
commit 4ea985b8f8
9 changed files with 179 additions and 183 deletions

View file

@ -1,10 +1,10 @@
import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/model/image_file_service.dart';
import 'package:aves/model/settings.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/viewer_service.dart';
import 'package:aves/widgets/album/all_collection_drawer.dart';
import 'package:aves/widgets/album/all_collection_page.dart';
import 'package:aves/widgets/album/collection_page.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/common/providers/media_store_collection_provider.dart';
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
@ -13,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
import 'package:pedantic/pedantic.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:screen/screen.dart';
void main() {
@ -104,24 +105,14 @@ class _HomePageState extends State<HomePage> {
? SingleFullscreenPage(
entry: _sharedEntry,
)
: const MediaStoreCollectionPage();
: MediaStoreCollectionProvider(
child: Consumer<CollectionLens>(
builder: (context, collection, child) => CollectionPage(
collection: collection,
),
),
);
}),
);
}
}
class MediaStoreCollectionPage extends StatelessWidget {
const MediaStoreCollectionPage();
@override
Widget build(BuildContext context) {
debugPrint('$runtimeType build');
return const MediaStoreCollectionProvider(
child: Scaffold(
body: AllCollectionPage(),
drawer: AllCollectionDrawer(),
resizeToAvoidBottomInset: false,
),
);
}
}

View file

@ -23,7 +23,7 @@ class CollectionLens with ChangeNotifier {
List<CollectionFilter> filters,
GroupFactor groupFactor,
SortFactor sortFactor,
}) : this.filters = filters ?? [],
}) : this.filters = [if (filters != null) ...filters.where((f) => f != null)],
this.groupFactor = groupFactor ?? GroupFactor.month,
this.sortFactor = sortFactor ?? SortFactor.date {
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => onEntryAdded()));

View file

@ -1,29 +1,15 @@
import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/settings.dart';
import 'package:aves/widgets/album/search_delegate.dart';
import 'package:aves/widgets/album/thumbnail_collection.dart';
import 'package:aves/widgets/common/menu_row.dart';
import 'package:aves/widgets/debug_page.dart';
import 'package:aves/widgets/stats.dart';
import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
import 'package:pedantic/pedantic.dart';
import 'package:provider/provider.dart';
class AllCollectionPage extends StatelessWidget {
const AllCollectionPage();
@override
Widget build(BuildContext context) {
debugPrint('$runtimeType build');
return ThumbnailCollection(
appBar: _AllCollectionAppBar(),
);
}
}
class _AllCollectionAppBar extends SliverAppBar {
_AllCollectionAppBar()
class AllCollectionAppBar extends SliverAppBar {
AllCollectionAppBar()
: super(
title: const Text('All'),
actions: _buildActions(),
@ -45,44 +31,40 @@ class _AllCollectionAppBar extends SliverAppBar {
),
Builder(
builder: (context) => Consumer<CollectionLens>(
builder: (context, collection, child) => PopupMenuButton<AlbumAction>(
builder: (context, collection, child) => PopupMenuButton<CollectionAction>(
itemBuilder: (context) => [
PopupMenuItem(
value: AlbumAction.sortByDate,
value: CollectionAction.sortByDate,
child: MenuRow(text: 'Sort by date', checked: collection.sortFactor == SortFactor.date),
),
PopupMenuItem(
value: AlbumAction.sortBySize,
value: CollectionAction.sortBySize,
child: MenuRow(text: 'Sort by size', checked: collection.sortFactor == SortFactor.size),
),
PopupMenuItem(
value: AlbumAction.sortByName,
value: CollectionAction.sortByName,
child: MenuRow(text: 'Sort by name', checked: collection.sortFactor == SortFactor.name),
),
const PopupMenuDivider(),
if (collection.sortFactor == SortFactor.date) ...[
PopupMenuItem(
value: AlbumAction.groupByAlbum,
value: CollectionAction.groupByAlbum,
child: MenuRow(text: 'Group by album', checked: collection.groupFactor == GroupFactor.album),
),
PopupMenuItem(
value: AlbumAction.groupByMonth,
value: CollectionAction.groupByMonth,
child: MenuRow(text: 'Group by month', checked: collection.groupFactor == GroupFactor.month),
),
PopupMenuItem(
value: AlbumAction.groupByDay,
value: CollectionAction.groupByDay,
child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day),
),
const PopupMenuDivider(),
],
PopupMenuItem(
value: AlbumAction.stats,
value: CollectionAction.stats,
child: MenuRow(text: 'Stats', icon: OMIcons.pieChart),
),
PopupMenuItem(
value: AlbumAction.debug,
child: MenuRow(text: 'Debug', icon: OMIcons.whatshot),
),
],
onSelected: (action) => _onActionSelected(context, collection, action),
),
@ -91,54 +73,40 @@ class _AllCollectionAppBar extends SliverAppBar {
];
}
static void _onActionSelected(BuildContext context, CollectionLens collection, AlbumAction action) async {
static void _onActionSelected(BuildContext context, CollectionLens collection, CollectionAction action) async {
// wait for the popup menu to hide before proceeding with the action
await Future.delayed(const Duration(milliseconds: 300));
switch (action) {
case AlbumAction.debug:
unawaited(_goToDebug(context, collection));
break;
case AlbumAction.stats:
case CollectionAction.stats:
unawaited(_goToStats(context, collection));
break;
case AlbumAction.groupByAlbum:
case CollectionAction.groupByAlbum:
settings.collectionGroupFactor = GroupFactor.album;
collection.group(GroupFactor.album);
break;
case AlbumAction.groupByMonth:
case CollectionAction.groupByMonth:
settings.collectionGroupFactor = GroupFactor.month;
collection.group(GroupFactor.month);
break;
case AlbumAction.groupByDay:
case CollectionAction.groupByDay:
settings.collectionGroupFactor = GroupFactor.day;
collection.group(GroupFactor.day);
break;
case AlbumAction.sortByDate:
case CollectionAction.sortByDate:
settings.collectionSortFactor = SortFactor.date;
collection.sort(SortFactor.date);
break;
case AlbumAction.sortBySize:
case CollectionAction.sortBySize:
settings.collectionSortFactor = SortFactor.size;
collection.sort(SortFactor.size);
break;
case AlbumAction.sortByName:
case CollectionAction.sortByName:
settings.collectionSortFactor = SortFactor.name;
collection.sort(SortFactor.name);
break;
}
}
static Future _goToDebug(BuildContext context, CollectionLens collection) {
return Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DebugPage(
entries: collection.sortedEntries,
),
),
);
}
static Future _goToStats(BuildContext context, CollectionLens collection) {
return Navigator.push(
context,
@ -151,4 +119,4 @@ class _AllCollectionAppBar extends SliverAppBar {
}
}
enum AlbumAction { debug, stats, groupByAlbum, groupByMonth, groupByDay, sortByDate, sortBySize, sortByName }
enum CollectionAction { stats, groupByAlbum, groupByMonth, groupByDay, sortByDate, sortBySize, sortByName }

View file

@ -5,108 +5,92 @@ import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/collection_source.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/color_utils.dart';
import 'package:aves/widgets/album/filtered_collection_page.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:flutter_svg/flutter_svg.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
import 'package:provider/provider.dart';
class AllCollectionDrawer extends StatefulWidget {
const AllCollectionDrawer();
class CollectionDrawer extends StatefulWidget {
final CollectionSource source;
const CollectionDrawer({@required this.source});
@override
_AllCollectionDrawerState createState() => _AllCollectionDrawerState();
_CollectionDrawerState createState() => _CollectionDrawerState();
}
class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
class _CollectionDrawerState extends State<CollectionDrawer> {
bool _albumsExpanded = false, _tagsExpanded = false, _countriesExpanded = false;
CollectionSource get source => widget.source;
@override
Widget build(BuildContext context) {
final collection = Provider.of<CollectionLens>(context);
final source = collection.source;
final header = DrawerHeader(
final header = Container(
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
backgroundColor: Colors.white,
radius: 44,
child: Padding(
padding: const EdgeInsets.only(top: 6.0),
child: SvgPicture.asset(
'assets/aves_logo.svg',
width: 64,
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',
),
),
),
const SizedBox(width: 16),
const Text(
'Aves',
style: TextStyle(
fontSize: 44,
fontFamily: 'Concourse Caps',
),
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
const Icon(OMIcons.photoLibrary),
const SizedBox(width: 4),
Text('${collection.imageCount}'),
]),
Row(children: [
const Icon(OMIcons.videoLibrary),
const SizedBox(width: 4),
Text('${collection.videoCount}'),
]),
Row(children: [
const Icon(OMIcons.photoAlbum),
const SizedBox(width: 4),
Text('${source.albumCount}'),
]),
],
),
],
],
),
],
),
),
),
);
final allMediaEntry = _FilteredCollectionNavTile(
source: source,
leading: const Icon(OMIcons.photo),
title: 'All media',
filter: null,
);
final videoEntry = _FilteredCollectionNavTile(
source: source,
leading: const Icon(OMIcons.movie),
title: 'Videos',
filter: VideoFilter(),
);
final gifEntry = _FilteredCollectionNavTile(
collection: collection,
source: source,
leading: const Icon(OMIcons.gif),
title: 'GIFs',
filter: GifFilter(),
);
final videoEntry = _FilteredCollectionNavTile(
collection: collection,
leading: const Icon(OMIcons.videoLibrary),
title: 'Videos',
filter: VideoFilter(),
);
final buildAlbumEntry = (album) => _FilteredCollectionNavTile(
collection: collection,
source: source,
leading: IconUtils.getAlbumIcon(context, album),
title: CollectionSource.getUniqueAlbumName(album, source.sortedAlbums),
dense: true,
filter: AlbumFilter(album),
);
final buildTagEntry = (tag) => _FilteredCollectionNavTile(
collection: collection,
source: source,
leading: Icon(
OMIcons.label,
OMIcons.localOffer,
color: stringToColor(tag),
),
title: tag,
@ -114,7 +98,7 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
filter: TagFilter(tag),
);
final buildCountryEntry = (country) => _FilteredCollectionNavTile(
collection: collection,
source: source,
leading: Icon(
OMIcons.place,
color: stringToColor(country),
@ -141,10 +125,11 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
final countries = source.sortedCountries;
final tags = source.sortedTags;
final drawerItems = [
final drawerItems = <Widget>[
header,
gifEntry,
allMediaEntry,
videoEntry,
gifEntry,
if (specialAlbums.isNotEmpty) ...[
const Divider(),
...specialAlbums.map(buildAlbumEntry),
@ -202,7 +187,7 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
top: false,
bottom: false,
child: ExpansionTile(
leading: const Icon(OMIcons.label),
leading: const Icon(OMIcons.localOffer),
title: Row(
children: [
const Text('Tags'),
@ -219,6 +204,28 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
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: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DebugPage(
source: source,
),
),
);
},
),
),
],
];
return Drawer(
@ -244,14 +251,14 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
}
class _FilteredCollectionNavTile extends StatelessWidget {
final CollectionLens collection;
final CollectionSource source;
final Widget leading;
final String title;
final bool dense;
final CollectionFilter filter;
const _FilteredCollectionNavTile({
@required this.collection,
@required this.source,
@required this.leading,
@required this.title,
bool dense,
@ -268,16 +275,18 @@ class _FilteredCollectionNavTile extends StatelessWidget {
title: Text(title),
dense: dense,
onTap: () {
Navigator.pop(context);
Navigator.push(
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => FilteredCollectionPage(
collection: collection,
filter: filter,
builder: (context) => CollectionPage(
collection: CollectionLens(
source: source,
filters: [filter],
),
title: title,
),
),
(route) => false,
);
},
),

View file

@ -1,6 +1,6 @@
import 'package:aves/model/collection_filters.dart';
import 'package:aves/model/collection_lens.dart';
import 'package:aves/widgets/album/all_collection_page.dart';
import 'package:aves/widgets/album/all_collection_app_bar.dart';
import 'package:aves/widgets/album/collection_drawer.dart';
import 'package:aves/widgets/album/thumbnail_collection.dart';
import 'package:aves/widgets/common/menu_row.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
@ -9,30 +9,36 @@ import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
import 'package:provider/provider.dart';
class FilteredCollectionPage extends StatelessWidget {
class CollectionPage extends StatelessWidget {
final CollectionLens collection;
final CollectionFilter filter;
final String title;
FilteredCollectionPage({Key key, CollectionLens collection, this.filter, this.title})
: this.collection = CollectionLens.from(collection, filter),
super(key: key);
const CollectionPage({
Key key,
@required this.collection,
this.title,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MediaQueryDataProvider(
child: Scaffold(
body: ChangeNotifierProvider<CollectionLens>.value(
value: collection,
child: ThumbnailCollection(
appBar: SliverAppBar(
title: Text(title),
actions: _buildActions(),
floating: true,
),
child: ChangeNotifierProvider<CollectionLens>.value(
value: collection,
child: Scaffold(
body: ThumbnailCollection(
appBar: collection.filters.isEmpty
? AllCollectionAppBar()
: SliverAppBar(
title: Text(title),
actions: _buildActions(),
floating: true,
),
),
drawer: CollectionDrawer(
source: collection.source,
),
resizeToAvoidBottomInset: false,
),
resizeToAvoidBottomInset: false,
),
);
}
@ -41,10 +47,10 @@ class FilteredCollectionPage extends StatelessWidget {
return [
Builder(
builder: (context) => Consumer<CollectionLens>(
builder: (context, collection, child) => PopupMenuButton<AlbumAction>(
builder: (context, collection, child) => PopupMenuButton<CollectionAction>(
itemBuilder: (context) => [
PopupMenuItem(
value: AlbumAction.stats,
value: CollectionAction.stats,
child: MenuRow(text: 'Stats', icon: OMIcons.pieChart),
),
],
@ -55,9 +61,9 @@ class FilteredCollectionPage extends StatelessWidget {
];
}
static void _onActionSelected(BuildContext context, CollectionLens collection, AlbumAction action) {
static void _onActionSelected(BuildContext context, CollectionLens collection, CollectionAction action) {
switch (action) {
case AlbumAction.stats:
case CollectionAction.stats:
_goToStats(context, collection);
break;
default:

View file

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class AvesLogo extends StatelessWidget {
final double size;
const AvesLogo({@required this.size});
@override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundColor: Colors.white,
radius: size / 2,
child: Padding(
padding: EdgeInsets.only(top: size / 15),
child: SvgPicture.asset(
'assets/aves_logo.svg',
width: size / 1.4,
),
),
);
}
}

View file

@ -1,3 +1,4 @@
import 'package:aves/model/collection_source.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/model/image_metadata.dart';
import 'package:aves/model/metadata_db.dart';
@ -8,9 +9,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class DebugPage extends StatefulWidget {
final List<ImageEntry> entries;
final CollectionSource source;
const DebugPage({this.entries});
const DebugPage({this.source});
@override
State<StatefulWidget> createState() => DebugPageState();
@ -21,7 +22,7 @@ class DebugPageState extends State<DebugPage> {
Future<List<CatalogMetadata>> _dbMetadataLoader;
Future<List<AddressDetails>> _dbAddressLoader;
List<ImageEntry> get entries => widget.entries;
List<ImageEntry> get entries => widget.source.entries;
@override
void initState() {

View file

@ -4,7 +4,7 @@ import 'package:aves/model/image_entry.dart';
import 'package:aves/model/settings.dart';
import 'package:aves/utils/android_app_service.dart';
import 'package:aves/utils/geo_utils.dart';
import 'package:aves/widgets/album/filtered_collection_page.dart';
import 'package:aves/widgets/album/collection_page.dart';
import 'package:aves/widgets/fullscreen/info/info_page.dart';
import 'package:aves/widgets/fullscreen/info/navigation_button.dart';
import 'package:flutter/material.dart';
@ -130,9 +130,8 @@ class _LocationSectionState extends State<LocationSection> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilteredCollectionPage(
collection: collection,
filter: CountryFilter(country),
builder: (context) => CollectionPage(
collection: CollectionLens.from(collection, CountryFilter(country)),
title: country,
),
),

View file

@ -1,7 +1,7 @@
import 'package:aves/model/collection_filters.dart';
import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/album/filtered_collection_page.dart';
import 'package:aves/widgets/album/collection_page.dart';
import 'package:aves/widgets/fullscreen/info/info_page.dart';
import 'package:aves/widgets/fullscreen/info/navigation_button.dart';
import 'package:collection/collection.dart';
@ -26,7 +26,7 @@ class XmpTagSectionSliver extends AnimatedWidget {
tags.isEmpty
? []
: [
const SectionRow(OMIcons.label),
const SectionRow(OMIcons.localOffer),
Padding(
padding: const EdgeInsets.symmetric(horizontal: NavigationButton.buttonBorderWidth / 2),
child: Wrap(
@ -49,9 +49,8 @@ class XmpTagSectionSliver extends AnimatedWidget {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FilteredCollectionPage(
collection: collection,
filter: TagFilter(tag),
builder: (context) => CollectionPage(
collection: CollectionLens.from(collection, TagFilter(tag)),
title: tag,
),
),