diff --git a/lib/local_storage/sqflite_localpodcast.dart b/lib/local_storage/sqflite_localpodcast.dart index 2dc24e4..4e0642b 100644 --- a/lib/local_storage/sqflite_localpodcast.dart +++ b/lib/local_storage/sqflite_localpodcast.dart @@ -671,40 +671,78 @@ class DBHelper { var list = []; if (hideListened) { if (count == -1) { - switch (filter) { - case Filter.all: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + if (reverse) { + switch (filter) { + case Filter.all: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id LEFT JOIN PlayHistory H ON E.enclosure_url = H.enclosure_url WHERE P.id = ? GROUP BY E.enclosure_url HAVING SUM(H.listen_time) is null OR SUM(H.listen_time) = 0 ORDER BY E.milliseconds ASC""", [id]); - break; - case Filter.liked: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + break; + case Filter.liked: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds ASC""", [id]); - break; - case Filter.downloaded: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + break; + case Filter.downloaded: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? AND E.media_id != E.enclosure_url ORDER BY E.milliseconds ASC""", - [id]); - break; - case Filter.search: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + [id]); + break; + case Filter.search: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds ASC""", - [id, '%$query%']); - break; - default: + [id, '%$query%']); + break; + default: + } + } else { + switch (filter) { + case Filter.all: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + LEFT JOIN PlayHistory H ON E.enclosure_url = H.enclosure_url + WHERE P.id = ? GROUP BY E.enclosure_url HAVING SUM(H.listen_time) is null + OR SUM(H.listen_time) = 0 ORDER BY E.milliseconds DESC""", [id]); + break; + case Filter.liked: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds DESC""", [id]); + break; + case Filter.downloaded: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? AND E.media_id != E.enclosure_url ORDER BY E.milliseconds DESC""", + [id]); + break; + case Filter.search: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds DESC""", + [id, '%$query%']); + break; + default: + } } } else if (reverse) { switch (filter) { @@ -798,38 +836,74 @@ class DBHelper { } } else { if (count == -1) { - switch (filter) { - case Filter.all: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + if (reverse) { + switch (filter) { + case Filter.all: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? ORDER BY E.milliseconds ASC""", [id]); - break; - case Filter.liked: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + break; + case Filter.liked: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds ASC""", [id]); - break; - case Filter.downloaded: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + break; + case Filter.downloaded: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? AND E.media_id != E.enclosure_url ORDER BY E.milliseconds ASC""", - [id]); - break; - case Filter.search: - list = await dbClient.rawQuery( - """SELECT E.title, E.enclosure_url, E.enclosure_length, + [id]); + break; + case Filter.search: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds ASC""", - [id, '%$query%']); - break; - default: + [id, '%$query%']); + break; + default: + } + } else { + switch (filter) { + case Filter.all: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? ORDER BY E.milliseconds DESC""", [id]); + break; + case Filter.liked: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? AND E.liked = 1 ORDER BY E.milliseconds DESC""", [id]); + break; + case Filter.downloaded: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? AND E.media_id != E.enclosure_url ORDER BY E.milliseconds DESC""", + [id]); + break; + case Filter.search: + list = await dbClient.rawQuery( + """SELECT E.title, E.enclosure_url, E.enclosure_length, + E.milliseconds, P.imagePath, P.title as feed_title, E.duration, E.explicit, + P.primaryColor FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id + WHERE P.id = ? AND E.title LIKE ? ORDER BY E.milliseconds DESC""", + [id, '%$query%']); + break; + default: + } } } else if (reverse) { switch (filter) { diff --git a/lib/podcasts/podcast_detail.dart b/lib/podcasts/podcast_detail.dart index 213e28d..b5d729a 100644 --- a/lib/podcasts/podcast_detail.dart +++ b/lib/podcasts/podcast_detail.dart @@ -86,6 +86,10 @@ class _PodcastDetailState extends State { bool _selectAll; + bool _selectBefore; + + bool _selectAfter; + @override void initState() { super.initState(); @@ -95,6 +99,8 @@ class _PodcastDetailState extends State { _scroll = false; _multiSelect = false; _selectAll = false; + _selectAfter = false; + _selectBefore = false; } @override @@ -696,6 +702,7 @@ class _PodcastDetailState extends State { ), onPressed: () { setState(() { + _top = -1; _selectedEpisodes = []; _multiSelect = true; }); @@ -862,32 +869,48 @@ class _PodcastDetailState extends State { filter: _filter, query: _query), builder: (context, snapshot) { - if (_selectAll) { - _selectedEpisodes = snapshot.data; + if (snapshot.hasData) { + if (_selectAll) { + _selectedEpisodes = snapshot.data; + } + if (_selectBefore) { + final index = snapshot.data + .indexOf(_selectedEpisodes.first); + if (index != 0) { + _selectedEpisodes = snapshot.data + .sublist(0, index + 1); + } + } + if (_selectAfter) { + final index = snapshot.data + .indexOf(_selectedEpisodes.first); + _selectedEpisodes = + snapshot.data.sublist(index); + } + return EpisodeGrid( + episodes: snapshot.data, + showFavorite: true, + showNumber: _filter == Filter.all && + !_hideListened + ? true + : false, + layout: _layout, + reverse: _reverse, + episodeCount: _episodeCount, + initNum: _scroll ? 0 : 12, + multiSelect: _multiSelect, + selectedList: _selectedEpisodes ?? [], + onSelect: (value) => setState(() { + _selectAll = false; + _selectBefore = false; + _selectAfter = false; + _selectedEpisodes = value; + }), + ); } - return (snapshot.hasData) - ? EpisodeGrid( - episodes: snapshot.data, - showFavorite: true, - showNumber: _filter == Filter.all && - !_hideListened - ? true - : false, - layout: _layout, - reverse: _reverse, - episodeCount: _episodeCount, - initNum: _scroll ? 0 : 12, - multiSelect: _multiSelect, - selectedList: - _selectedEpisodes ?? [], - onSelect: (value) => setState(() { - _selectAll = false; - _selectedEpisodes = value; - }), - ) - : SliverToBoxAdapter( - child: Center(), - ); + return SliverToBoxAdapter( + child: Center(), + ); }), SliverList( delegate: SliverChildBuilderDelegate( @@ -919,7 +942,23 @@ class _PodcastDetailState extends State { onSelectAll: (value) { setState(() { _selectAll = value; - _selectedEpisodes = []; + _selectAfter = false; + _selectBefore = false; + if (!value) { + _selectedEpisodes = []; + } + }); + }, + onSelectAfter: (value) { + setState(() { + _selectBefore = false; + _selectAfter = true; + }); + }, + onSelectBefore: (value) { + setState(() { + _selectAfter = false; + _selectBefore = true; }); }, onClose: (value) { @@ -927,6 +966,8 @@ class _PodcastDetailState extends State { if (value) { _multiSelect = false; _selectAll = false; + _selectAfter = false; + _selectBefore = false; } }); }, @@ -959,12 +1000,16 @@ class MultiSelectMenuBar extends StatefulWidget { this.selectAll, this.onSelectAll, this.onClose, + this.onSelectAfter, + this.onSelectBefore, Key key}) : super(key: key); final List selectedList; final bool selectAll; final ValueChanged onSelectAll; final ValueChanged onClose; + final ValueChanged onSelectBefore; + final ValueChanged onSelectAfter; @override _MultiSelectMenuBarState createState() => _MultiSelectMenuBarState(); @@ -1146,22 +1191,84 @@ class _MultiSelectMenuBarState extends State { tween: Tween(begin: 0, end: 1), duration: Duration(milliseconds: 500), builder: (context, value, child) => Container( - height: 80.0 * value, + height: 90.0 * value, decoration: BoxDecoration(color: context.primaryColor), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ - SizedBox( - height: 30, - child: Align( - alignment: Alignment.centerRight, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 20.0), - child: Text('${widget.selectedList.length} selected', - style: context.textTheme.headline6 - .copyWith(color: context.accentColor))), - ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 40, + child: Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Text('${widget.selectedList.length} selected', + style: context.textTheme.headline6 + .copyWith(color: context.accentColor))), + ), + ), + Spacer(), + if (widget.selectedList.length == 1) + SizedBox( + height: 25, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + side: BorderSide(color: context.accentColor), + primary: context.textColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(100)))), + onPressed: () { + widget.onSelectBefore(true); + }, + child: Text('Before')), + ), + ), + if (widget.selectedList.length == 1) + SizedBox( + height: 25, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + side: BorderSide(color: context.accentColor), + primary: context.textColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(100)))), + onPressed: () { + widget.onSelectAfter(true); + }, + child: Text('After')), + ), + ), + SizedBox( + height: 25, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 5), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + side: BorderSide(color: context.accentColor), + backgroundColor: + widget.selectAll ? context.accentColor : null, + primary: widget.selectAll + ? Colors.white + : context.textColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(100)))), + onPressed: () { + widget.onSelectAll(!widget.selectAll); + }, + child: Text('All')), + ), + ) + ], ), Row( children: [ @@ -1287,12 +1394,6 @@ class _MultiSelectMenuBarState extends State { } }), Spacer(), - _buttonOnMenu( - child: Icon(Icons.select_all_rounded, - color: widget.selectAll ? context.accentColor : null), - onTap: () { - widget.onSelectAll(!widget.selectAll); - }), _buttonOnMenu( child: Icon(Icons.close), onTap: () => widget.onClose(true))