From 5bb2e914c65efb9e743f7bc8cce31e19b0c0443f Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Tue, 27 Aug 2019 18:56:49 +0900 Subject: [PATCH] album: group by album/date --- lib/model/image_collection.dart | 20 +++++ lib/widgets/album/all_collection_page.dart | 36 ++++++++- lib/widgets/album/sections.dart | 77 ++++++++++++++++++ lib/widgets/album/thumbnail_collection.dart | 88 ++------------------- lib/widgets/common/menu_row.dart | 34 ++++++++ lib/widgets/fullscreen/overlay_top.dart | 23 +----- 6 files changed, 173 insertions(+), 105 deletions(-) create mode 100644 lib/widgets/album/sections.dart create mode 100644 lib/widgets/common/menu_row.dart diff --git a/lib/model/image_collection.dart b/lib/model/image_collection.dart index 89e3d37e0..87faafa33 100644 --- a/lib/model/image_collection.dart +++ b/lib/model/image_collection.dart @@ -2,13 +2,31 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/model/image_file_service.dart'; import 'package:aves/model/image_metadata.dart'; import 'package:aves/model/metadata_db.dart'; +import "package:collection/collection.dart"; import 'package:flutter/material.dart'; class ImageCollection with ChangeNotifier { final List entries; + GroupFactor groupFactor = GroupFactor.date; + ImageCollection(this.entries); + Map> get sections { + switch (groupFactor) { + case GroupFactor.album: + return groupBy(entries, (entry) => entry.bucketDisplayName); + case GroupFactor.date: + return groupBy(entries, (entry) => entry.monthTaken); + } + return Map(); + } + + group(GroupFactor groupFactor) { + this.groupFactor = groupFactor; + notifyListeners(); + } + Future delete(ImageEntry entry) async { final success = await ImageFileService.delete(entry); if (success) { @@ -77,3 +95,5 @@ class ImageCollection with ChangeNotifier { debugPrint('$runtimeType locateEntries complete in ${DateTime.now().difference(start).inSeconds}s'); } } + +enum GroupFactor { album, date } diff --git a/lib/widgets/album/all_collection_page.dart b/lib/widgets/album/all_collection_page.dart index cb1b556e2..820650c37 100644 --- a/lib/widgets/album/all_collection_page.dart +++ b/lib/widgets/album/all_collection_page.dart @@ -1,6 +1,7 @@ import 'package:aves/model/image_collection.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:flutter/material.dart'; @@ -23,13 +24,44 @@ class AllCollectionPage extends StatelessWidget { delegate: ImageSearchDelegate(collection), ), ), - IconButton(icon: Icon(Icons.whatshot), onPressed: () => goToDebug(context)), + PopupMenuButton( + itemBuilder: (context) => [ + PopupMenuItem( + value: AlbumAction.groupByAlbum, + child: MenuRow(text: 'Group by album', checked: collection.groupFactor == GroupFactor.album), + ), + PopupMenuItem( + value: AlbumAction.groupByDate, + child: MenuRow(text: 'Group by date', checked: collection.groupFactor == GroupFactor.date), + ), + PopupMenuDivider(), + PopupMenuItem( + value: AlbumAction.groupByAlbum, + child: MenuRow(text: 'Debug', icon: Icons.whatshot), + ), + ], + onSelected: (action) => onActionSelected(context, action), + ), ], floating: true, ), ); } + onActionSelected(BuildContext context, AlbumAction action) { + switch (action) { + case AlbumAction.groupByAlbum: + collection.group(GroupFactor.album); + break; + case AlbumAction.groupByDate: + collection.group(GroupFactor.date); + break; + case AlbumAction.debug: + goToDebug(context); + break; + } + } + Future goToDebug(BuildContext context) { return Navigator.push( context, @@ -41,3 +73,5 @@ class AllCollectionPage extends StatelessWidget { ); } } + +enum AlbumAction { groupByDate, groupByAlbum, debug } diff --git a/lib/widgets/album/sections.dart b/lib/widgets/album/sections.dart new file mode 100644 index 000000000..27537d8e0 --- /dev/null +++ b/lib/widgets/album/sections.dart @@ -0,0 +1,77 @@ +import 'package:aves/utils/date_utils.dart'; +import 'package:aves/widgets/common/outlined_text.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class DaySectionHeader extends StatelessWidget { + final String text; + + DaySectionHeader({Key key, DateTime date}) + : text = formatDate(date), + super(key: key); + + static DateFormat md = DateFormat.MMMMd(); + static DateFormat ymd = DateFormat.yMMMMd(); + + static formatDate(DateTime date) { + if (isToday(date)) return 'Today'; + if (isThisYear(date)) return md.format(date); + return ymd.format(date); + } + + @override + Widget build(BuildContext context) { + return SectionHeader(text: text); + } +} + +class MonthSectionHeader extends StatelessWidget { + final String text; + + MonthSectionHeader({Key key, DateTime date}) + : text = formatDate(date), + super(key: key); + + static DateFormat m = DateFormat.MMMM(); + static DateFormat ym = DateFormat.yMMMM(); + + static formatDate(DateTime date) { + if (isThisMonth(date)) return 'This month'; + if (isThisYear(date)) return m.format(date); + return ym.format(date); + } + + @override + Widget build(BuildContext context) { + return SectionHeader(text: text); + } +} + +class SectionHeader extends StatelessWidget { + final String text; + + const SectionHeader({Key key, this.text}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(16), + child: OutlinedText( + text, + style: TextStyle( + color: Colors.grey[200], + fontSize: 20, + shadows: [ + Shadow( + offset: Offset(0, 2), + blurRadius: 3, + color: Colors.grey[900], + ), + ], + ), + outlineColor: Colors.black87, + outlineWidth: 2, + ), + ); + } +} diff --git a/lib/widgets/album/thumbnail_collection.dart b/lib/widgets/album/thumbnail_collection.dart index 7f1c2376d..03ed8e20b 100644 --- a/lib/widgets/album/thumbnail_collection.dart +++ b/lib/widgets/album/thumbnail_collection.dart @@ -1,14 +1,11 @@ import 'package:aves/model/image_collection.dart'; import 'package:aves/model/image_entry.dart'; -import 'package:aves/utils/date_utils.dart'; +import 'package:aves/widgets/album/sections.dart'; import 'package:aves/widgets/album/thumbnail.dart'; import 'package:aves/widgets/common/draggable_scrollbar.dart'; -import 'package:aves/widgets/common/outlined_text.dart'; import 'package:aves/widgets/fullscreen/image_page.dart'; -import "package:collection/collection.dart"; import 'package:flutter/material.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; -import 'package:intl/intl.dart'; class ThumbnailCollection extends AnimatedWidget { final ImageCollection collection; @@ -33,14 +30,14 @@ class ThumbnailCollectionContent extends StatelessWidget { final ImageCollection collection; final Widget appBar; - final Map> _sections; + final Map> _sections; final ScrollController _scrollController = ScrollController(); ThumbnailCollectionContent({ Key key, this.collection, this.appBar, - }) : _sections = groupBy(collection.entries, (entry) => entry.monthTaken), + }) : _sections = collection.sections, super(key: key); @override @@ -82,8 +79,8 @@ class ThumbnailCollectionContent extends StatelessWidget { class SectionSliver extends StatelessWidget { final ImageCollection collection; - final Map> sections; - final DateTime sectionKey; + final Map> sections; + final dynamic sectionKey; const SectionSliver({ Key key, @@ -97,7 +94,7 @@ class SectionSliver extends StatelessWidget { // debugPrint('$runtimeType build with sectionKey=$sectionKey'); final columnCount = 4; return SliverStickyHeader( - header: MonthSectionHeader(date: sectionKey), + header: collection.groupFactor == GroupFactor.date ? MonthSectionHeader(date: sectionKey) : SectionHeader(text: sectionKey), sliver: SliverGrid( delegate: SliverChildBuilderDelegate( (sliverContext, index) { @@ -135,76 +132,3 @@ class SectionSliver extends StatelessWidget { ); } } - -class DaySectionHeader extends StatelessWidget { - final String text; - - DaySectionHeader({Key key, DateTime date}) - : text = formatDate(date), - super(key: key); - - static DateFormat md = DateFormat.MMMMd(); - static DateFormat ymd = DateFormat.yMMMMd(); - - static formatDate(DateTime date) { - if (isToday(date)) return 'Today'; - if (isThisYear(date)) return md.format(date); - return ymd.format(date); - } - - @override - Widget build(BuildContext context) { - return SectionHeader(text: text); - } -} - -class MonthSectionHeader extends StatelessWidget { - final String text; - - MonthSectionHeader({Key key, DateTime date}) - : text = formatDate(date), - super(key: key); - - static DateFormat m = DateFormat.MMMM(); - static DateFormat ym = DateFormat.yMMMM(); - - static formatDate(DateTime date) { - if (isThisMonth(date)) return 'This month'; - if (isThisYear(date)) return m.format(date); - return ym.format(date); - } - - @override - Widget build(BuildContext context) { - return SectionHeader(text: text); - } -} - -class SectionHeader extends StatelessWidget { - final String text; - - const SectionHeader({Key key, this.text}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(16), - child: OutlinedText( - text, - style: TextStyle( - color: Colors.grey[200], - fontSize: 20, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 3, - color: Colors.grey[900], - ), - ], - ), - outlineColor: Colors.black87, - outlineWidth: 2, - ), - ); - } -} diff --git a/lib/widgets/common/menu_row.dart b/lib/widgets/common/menu_row.dart new file mode 100644 index 000000000..6c52b900e --- /dev/null +++ b/lib/widgets/common/menu_row.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class MenuRow extends StatelessWidget { + final String text; + final IconData icon; + final bool checked; + + const MenuRow({ + Key key, + this.text, + this.icon, + this.checked, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + if (checked != null) ...[ + Opacity( + opacity: checked ? 1 : 0, + child: Icon(Icons.done), + ), + SizedBox(width: 8), + ], + if (icon != null) ...[ + Icon(icon), + SizedBox(width: 8), + ], + Text(text), + ], + ); + } +} diff --git a/lib/widgets/fullscreen/overlay_top.dart b/lib/widgets/fullscreen/overlay_top.dart index 3c6799772..ee10960df 100644 --- a/lib/widgets/fullscreen/overlay_top.dart +++ b/lib/widgets/fullscreen/overlay_top.dart @@ -1,5 +1,6 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/common/blurred.dart'; +import 'package:aves/widgets/common/menu_row.dart'; import 'package:aves/widgets/fullscreen/image_page.dart'; import 'package:flutter/material.dart'; @@ -99,28 +100,6 @@ class FullscreenTopOverlay extends StatelessWidget { } } -class MenuRow extends StatelessWidget { - final String text; - final IconData icon; - - const MenuRow({ - Key key, - this.text, - this.icon, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Icon(icon), - SizedBox(width: 8), - Text(text), - ], - ); - } -} - class OverlayButton extends StatelessWidget { final Animation scale; final Widget child;