diff --git a/README.md b/README.md index 8a1d44e..ac37d37 100644 --- a/README.md +++ b/README.md @@ -26,24 +26,36 @@ Derpibooru client made with Flutter ## Screenshots -### Main grid view +| Main grid view | Full image view | Image details | App drawer | +| -------------- | --------------- | ------------- | ---------- | +| ![Main grid view](screenshots/gridview.jpg) | ![Full image view](screenshots/fullview.jpg) | ![Image details](screenshots/details.jpg) | ![App drawer](screenshots/drawer.jpg) | -![Main grid view](screenshots/gridview.jpg) + -### Full image view + -![Full image view](screenshots/fullview.jpg) + -### Image details + -![Image details](screenshots/details.jpg) + -### App drawer + -![App drawer](screenshots/drawer.jpg) + + + ## Changelog +* **04.09.2019** + * Added favourites list + * Made history and favourites dismissable + * Added video progress bar + * If a video is playing, all other videos will autostart, if a video + is paused, all other videos will be paused as well + * Rating filter is now saved on change + * **02.06.2019** * Webm files now show their gif thumbnail previews in grid view! * Webm files in the big view *kinda* work now! diff --git a/lib/Elements/FavouritesModal.dart b/lib/Elements/FavouritesModal.dart new file mode 100644 index 0000000..03c17ac --- /dev/null +++ b/lib/Elements/FavouritesModal.dart @@ -0,0 +1,97 @@ +import 'package:chryssibooru/API.dart'; +import 'package:chryssibooru/DerpisRepo.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class FavouritesModal extends StatefulWidget { + FavouritesModal({@required this.repo}); + + final DerpisRepo repo; + + @override + _FavouritesModal createState() => _FavouritesModal(); +} + +class _FavouritesModal extends State { + List _favourites = List(); + DerpisRepo _repo; + Color _dismissColor = Colors.red; + + @override + void initState() { + _repo = widget.repo; + super.initState(); + } + + void _getSearchFavourites() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + _favourites = prefs.getStringList("favourites"); + }); + } + + void _removeSearchFromFavourites(int index) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List favourites = prefs.getStringList("favourites") ?? new List(); + + if (favourites == null) return; + + favourites.removeAt(index); + prefs.setStringList("favourites", favourites); + + _getSearchFavourites(); + } + + @override + Widget build(BuildContext context) { + _getSearchFavourites(); + return new AlertDialog( + contentPadding: EdgeInsets.all(0.0), + title: new Container( + child: Text("Favourites"), + padding: EdgeInsets.only(bottom: 3.0), + decoration: new BoxDecoration( + border: + new Border(bottom: BorderSide(width: 1.0, color: Colors.grey))), + ), + content: new ListView.builder( + padding: EdgeInsets.only(top: 5.0), + itemCount: _favourites.length, + itemBuilder: (BuildContext context, int index) { + return new Dismissible( + key: Key(_favourites[index]), + background: Container( + color: _dismissColor, + ), + onDismissed: (direction) { + _removeSearchFromFavourites(index); + }, + child: InkWell( + child: Padding( + padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), + child: Text(_favourites[index]), + ), + onTap: () { + if (_favourites[index] != _repo.query) { + _repo.derpis = new List(); + _repo.page = 1; + _repo.query = _favourites[index]; + setState(() { + _repo.loadDerpis(); + }); + } + }, + )); + }), + actions: [ + // usually buttons at the bottom of the dialog + new FlatButton( + child: new Text("Close"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + } +} diff --git a/lib/Elements/HistoryModal.dart b/lib/Elements/HistoryModal.dart index 7117328..f1235be 100644 --- a/lib/Elements/HistoryModal.dart +++ b/lib/Elements/HistoryModal.dart @@ -2,6 +2,7 @@ import 'package:chryssibooru/API.dart'; import 'package:chryssibooru/DerpisRepo.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; class HistoryModal extends StatefulWidget { HistoryModal({@required this.repo}); @@ -15,6 +16,7 @@ class HistoryModal extends StatefulWidget { class _HistoryModal extends State { List _history = List(); DerpisRepo _repo; + Color _dismissColor = Colors.red; @override void initState() { @@ -41,33 +43,60 @@ class _HistoryModal extends State { _getSearchHistory(); } + void _addSearchToFavourites(int index) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List history = prefs.getStringList("history") ?? new List(); + List favourites = prefs.getStringList("favourites") ?? new List(); + + if (favourites == null) return; + + favourites.add(history[index]); + prefs.setStringList("favourites", favourites); + } + @override Widget build(BuildContext context) { _getSearchHistory(); return new AlertDialog( - title: new Text("History"), + contentPadding: EdgeInsets.all(0.0), + title: new Container( + child: Text("History"), + padding: EdgeInsets.only(bottom: 3.0), + decoration: new BoxDecoration( + border: + new Border(bottom: BorderSide(width: 1.0, color: Colors.grey))), + ), content: new ListView.builder( + padding: EdgeInsets.only(top: 5.0), itemCount: _history.length, itemBuilder: (BuildContext context, int index) { - return new InkWell( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(_history[index]), - ), - onTap: () { - if (_history[index] != _repo.query) { - _repo.derpis = new List(); - _repo.page = 1; - _repo.query = _history[index]; - setState(() { - _repo.loadDerpis(); - }); - } - }, - onLongPress: () { - _removeSearchFromHistory(index); - }, - ); + return new Dismissible( + key: Key(_history[index]), + background: Container( + color: _dismissColor, + ), + onDismissed: (direction) { + _removeSearchFromHistory(index); + }, + child: InkWell( + child: Padding( + padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), + child: Text(_history[index]), + ), + onTap: () { + if (_history[index] != _repo.query) { + _repo.derpis = new List(); + _repo.page = 1; + _repo.query = _history[index]; + setState(() { + _repo.loadDerpis(); + }); + } + }, + onLongPress: () { + _addSearchToFavourites(index); + }, + )); }), actions: [ // usually buttons at the bottom of the dialog diff --git a/lib/Views/HomePage.dart b/lib/Views/HomePage.dart index 6ff47ea..77bc70d 100644 --- a/lib/Views/HomePage.dart +++ b/lib/Views/HomePage.dart @@ -1,5 +1,6 @@ import 'package:chryssibooru/API.dart'; import 'package:chryssibooru/DerpisRepo.dart'; +import 'package:chryssibooru/Elements/FavouritesModal.dart'; import 'package:chryssibooru/Elements/FilterSheet.dart'; import 'package:chryssibooru/Elements/HistoryModal.dart'; import 'package:chryssibooru/Views/ImageViewer.dart'; @@ -63,6 +64,7 @@ class HomePageState extends State { @override void initState() { getCacheSize(); + _getRatingPrefs(); _scrollController = ScrollController(); _scrollController.addListener(_loadDerpisListener); super.initState(); @@ -79,6 +81,22 @@ class HomePageState extends State { } } + void _getRatingPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + _s = prefs.getBool('safe'); + _q = prefs.getBool('questionable'); + _e = prefs.getBool('explicit'); + }); + } + + void _saveRatingPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setBool('safe', _s); + prefs.setBool('questionable', _q); + prefs.setBool('explicit', _e); + } + void _saveKey(String key) async { setState(() { repo.key = key; @@ -230,14 +248,17 @@ class HomePageState extends State { safe: _s, safeChanged: (value) { _s = value; + _saveRatingPrefs(); }, questionable: _q, questionableChanged: (value) { _q = value; + _saveRatingPrefs(); }, explicit: _e, explicitChanged: (value) { _e = value; + _saveRatingPrefs(); }, ); }, @@ -255,7 +276,7 @@ class HomePageState extends State { DrawerHeader( child: SvgPicture.asset('assets/logo.svg'), decoration: BoxDecoration( - color: Colors.black + color: Color.fromARGB(1, 100, 150, 150) ), ), ListTile( @@ -284,6 +305,19 @@ class HomePageState extends State { } ), ), + ListTile( + title: Text("Favourites"), + subtitle: Text("See your favourite searches", style: TextStyle(fontSize: 12.0)), + leading: Icon(Icons.favorite), + onTap: () => showDialog( + context: context, + builder: (BuildContext context) { + return new FavouritesModal( + repo: repo, + ); + } + ), + ), Divider(), ListTile( title: Text("Buy me a coffe"), diff --git a/lib/Views/ImageViewer.dart b/lib/Views/ImageViewer.dart index 90b156d..78080f1 100644 --- a/lib/Views/ImageViewer.dart +++ b/lib/Views/ImageViewer.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:share/share.dart'; import 'package:video_player/video_player.dart'; +import 'package:percent_indicator/linear_percent_indicator.dart'; import '../Helpers.dart'; @@ -39,6 +40,8 @@ class ImageViewerState extends State { DerpisRepo repo; + double _videoProgressPercent = 0.0; + @override void dispose() { _videoController.dispose(); @@ -51,12 +54,17 @@ class ImageViewerState extends State { _videoController?.dispose(); _videoController = VideoPlayerController.network('https:'+repo.derpis[_id].representations.medium) - ..initialize().then((_) { - _videoController.setVolume(_volume); - _videoController.setLooping(true); - if (_autoplay) _videoController.play(); - setState(() {}); + ..addListener(() { + setState(() { + _videoProgressPercent = (_videoController.value.position.inMilliseconds / _videoController.value.duration.inMilliseconds).clamp(0.0, 1.0); }); + }) + ..initialize().then((_) { + _videoController.setVolume(_volume); + _videoController.setLooping(true); + if (_autoplay) _videoController.play(); + setState(() {}); + }); super.didChangeDependencies(); } @@ -77,14 +85,19 @@ class ImageViewerState extends State { // Swap the video if (_pageController.page.round() != _currentPage) { if (repo.derpis[_pageController.page.round()].mimeType == MimeType.VIDEO_WEBM) { - _videoController.dispose(); + _videoController?.dispose(); _videoController = VideoPlayerController.network('https:'+repo.derpis[_pageController.page.round()].representations.medium) - ..initialize().then((_) { - _videoController.setVolume(_volume); - _videoController.setLooping(true); - if (_autoplay) _videoController.play(); - setState(() {}); + ..addListener(() { + setState(() { + _videoProgressPercent = (_videoController.value.position.inMilliseconds / _videoController.value.duration.inMilliseconds).clamp(0.0, 1.0); }); + }) + ..initialize().then((_) { + _videoController.setVolume(_volume); + _videoController.setLooping(true); + if (_autoplay) _videoController.play(); + setState(() {}); + }); } _currentPage = _pageController.page.round(); } @@ -140,11 +153,7 @@ class ImageViewerState extends State { _videoController.value.isPlaying ? _videoController.pause() : _videoController.play(); - }); - }, - onLongPress: () { - setState(() { - _autoplay = !_autoplay; + _autoplay = _autoplay ? false : true; }); }, ), @@ -157,13 +166,14 @@ class ImageViewerState extends State { _videoController.value.isPlaying ? _videoController.pause() : _videoController.play(); + _autoplay = _autoplay ? false : true; }) ), ), Align( alignment: Alignment.bottomRight, child: IconButton( - icon: Icon(_videoController.value.volume > 0 ? Icons.volume_off : Icons.volume_up), + icon: Icon(_videoController.value.volume > 0 ? Icons.volume_up : Icons.volume_off), onPressed: () => setState(() { _videoController.value.volume > 0 ? _volume = 0.0 @@ -171,6 +181,19 @@ class ImageViewerState extends State { _videoController.setVolume(_volume); }) ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + height: 3, + child: new LinearPercentIndicator( + padding: EdgeInsets.all(0.0), + linearStrokeCap: LinearStrokeCap.butt, + lineHeight: 3.0, + progressColor: Colors.teal, + percent: _videoProgressPercent, + ) + ), ) ], ); @@ -180,6 +203,7 @@ class ImageViewerState extends State { onPageChanged: (pageId) { setState(() { _id = pageId.round(); + _videoProgressPercent = 0.0; }); }, ), diff --git a/pubspec.yaml b/pubspec.yaml index 99d1cc0..895b942 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,10 @@ dependencies: video_player: ^0.10.1+2 + flutter_slidable: ^0.5.3 + + percent_indicator: ^2.1.1 + # webview_flutter: ^0.3.6 diff --git a/screenshots/joined.jpg b/screenshots/joined.jpg new file mode 100644 index 0000000..3852123 Binary files /dev/null and b/screenshots/joined.jpg differ