From c268ab32c50e8f3ee1e5247ba3d37305bac92c06 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 10:04:58 -0500 Subject: [PATCH 01/12] move settings screen --- .../{ => MainScreens}/settingsScreen.dart | 95 ++++++++++--------- 1 file changed, 51 insertions(+), 44 deletions(-) rename lib/screens/{ => MainScreens}/settingsScreen.dart (71%) diff --git a/lib/screens/settingsScreen.dart b/lib/screens/MainScreens/settingsScreen.dart similarity index 71% rename from lib/screens/settingsScreen.dart rename to lib/screens/MainScreens/settingsScreen.dart index 6f5f5398..4a187779 100644 --- a/lib/screens/settingsScreen.dart +++ b/lib/screens/MainScreens/settingsScreen.dart @@ -23,9 +23,10 @@ class _SettingsScreenState extends State { void initState() { super.initState(); Settings.init(); + getPackageInfo(); } - late String version; + String version = ''; Future getPackageInfo() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); @@ -37,12 +38,6 @@ class _SettingsScreenState extends State { return Scaffold( appBar: AppBar( title: const Text('Settings', style: TextStyle(fontSize: 20)), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - Navigator.pop(context); - }, - ), ), body: Container( padding: const EdgeInsets.all(10), @@ -65,8 +60,9 @@ class _SettingsScreenState extends State { height: 20, ), experimentalFeaturesSettings(), - const SizedBox( - height: 300, + SizedBox( + // depends on screen size + height: MediaQuery.of(context).size.height * 0.15, ), aboutSettings(), const SizedBox( @@ -141,42 +137,53 @@ class _SettingsScreenState extends State { // about settings (should contain the version number and the link to the github repo, and author) Widget aboutSettings() => Container( - child: Column( - children: [ - Text( - 'Version: ${Settings.getValue('version')}', - style: TextStyle(fontSize: 20), - ), - SizedBox( - height: 10, - ), - Text( - "Made by Kara Wilson", - style: TextStyle(fontSize: 20), - ), - SizedBox( - height: 10, - ), - // insert https://github.com/Kara-Zor-El/JellyBook - InkWell( - child: Text( - "https://github.com/Kara-Zor-El/JellyBook", - style: TextStyle( - fontSize: 20, - color: Colors.blue, + // future builder to get the version number + child: FutureBuilder( + future: getPackageInfo(), + builder: (context, snapshot) { + return Column( + children: [ + // version number (should be a future builder) + Text( + 'Version: ' + (version.isNotEmpty ? version : 'Unknown'), + style: TextStyle(fontSize: 20), ), - ), - onTap: () async { - if (await canLaunchUrl( - Uri.parse("https://github.com/Kara-Zor-El/JellyBook"), - )) { - await launchUrl( - Uri.parse("https://github.com/Kara-Zor-El/JellyBook"), - ); - } - }, - ), - ], + SizedBox( + height: 10, + ), + Text( + "Made by Kara Wilson", + style: TextStyle(fontSize: 20), + ), + SizedBox( + height: 10, + ), + // insert https://github.com/Kara-Zor-El/JellyBook + InkWell( + child: Text( + "https://github.com/Kara-Zor-El/JellyBook", + style: TextStyle( + fontSize: 20, + color: Colors.blue, + ), + ), + onTap: () async { + try { + if (await canLaunchUrl( + Uri.parse("https://github.com/Kara-Zor-El/JellyBook"), + )) { + await launchUrl( + Uri.parse("https://github.com/Kara-Zor-El/JellyBook"), + ); + } + } catch (e) { + debugPrint(e.toString()); + } + }, + ), + ], + ); + }, ), ); } From e5d9d8fd61f5b1423db003d63f11703866a32597 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 10:05:17 -0500 Subject: [PATCH 02/12] create more dynamic home screen to be used --- lib/screens/MainScreens/mainMenu.dart | 427 +++++++++++++++++++++ lib/screens/homeScreen.dart | 510 +++++++------------------- 2 files changed, 561 insertions(+), 376 deletions(-) create mode 100644 lib/screens/MainScreens/mainMenu.dart diff --git a/lib/screens/MainScreens/mainMenu.dart b/lib/screens/MainScreens/mainMenu.dart new file mode 100644 index 00000000..03950f79 --- /dev/null +++ b/lib/screens/MainScreens/mainMenu.dart @@ -0,0 +1,427 @@ +// The purpose of this file is to create the main menu screen (this is part of the list of screens in the bottom navigation bar) + +import 'package:flutter/material.dart'; +import 'package:jellybook/providers/fetchCategories.dart'; +import 'package:jellybook/screens/collectionScreen.dart'; +import 'package:jellybook/screens/infoScreen.dart'; +import 'package:jellybook/screens/loginScreen.dart'; +import 'package:jellybook/models/login.dart'; +import 'package:isar/isar.dart'; +import 'package:auto_size_text/auto_size_text.dart'; + +class MainMenu extends StatefulWidget { + @override + _MainMenuState createState() => _MainMenuState(); +} + +class _MainMenuState extends State { + /* + Heres what this should look like: + - should be a grid view of cards + - each comic should have its own card + - the cards should have: + - a photo + - the title + - the release data if known + - a more info button + - a progress bar if book has been started + - At the bottom will be a bar witch will contain the following sections: + - a library section + - a search section + - a settings section + */ + + Future logout() async { + final isar = Isar.getInstance(); + var logins = await isar!.logins.where().findAll(); + var loginIds = logins.map((e) => e.isarId).toList(); + await isar.writeTxn(() async { + isar.logins.deleteAll(loginIds); + debugPrint('deleted ${loginIds.length} logins'); + }); + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => LoginScreen()), + ); + } + + int _selectedIndex = 0; + +// should be a futureBuilder + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.home), + onPressed: () {}, + ), + title: const Text('Home'), + actions: [ + IconButton( + icon: const Icon(Icons.logout), + onPressed: () { + logout(); + }, + ), + ], + ), + body: ListView( + children: [ + const SizedBox( + height: 10, + ), + // text align left side of the column + FutureBuilder( + future: getServerCategories(context, returnFolders: true), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData && + snapshot.data != null && + snapshot.data.isNotEmpty) { + debugPrint("snapshot data: ${snapshot.data}"); + return Column( + children: [ + const Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + "Collections", + style: TextStyle( + // size is the size of a title + fontSize: 30, + // decoration: TextDecoration.underline, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox( + height: 10, + ), + SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height / 6 * 1.2, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: snapshot.data?.length, + itemBuilder: (context, index) { + return SizedBox( + width: MediaQuery.of(context).size.width / 3, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: InkWell( + onTap: () { + debugPrint("tapped"); + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation, + secondaryAnimation) => + collectionScreen( + folderId: snapshot.data[index]['id'], + name: snapshot.data[index]['name'], + image: snapshot.data[index]['image'], + bookIds: snapshot.data[index] + ['bookIds'], + ), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + var begin = const Offset(1.0, 0.0); + var end = Offset.zero; + var curve = Curves.ease; + + var tween = Tween( + begin: begin, end: end) + .chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ), + ); + }, + child: Column( + children: [ + SizedBox( + height: 10 * + MediaQuery.of(context).size.height / + 1000, + ), + Padding( + padding: EdgeInsets.only( + left: 10 * + MediaQuery.of(context) + .size + .width / + 1000, + right: 10 * + MediaQuery.of(context) + .size + .width / + 1000), + child: Flexible( + // ensure that it wont overflow + child: AutoSizeText( + snapshot.data[index]['name'], + maxLines: 2, + minFontSize: 5, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + // start all images at the same height rather than same offset + SizedBox( + height: 5 * + MediaQuery.of(context).size.height / + 1000, + ), + // have the image change size based to fit the card + Flexible( + child: SizedBox( + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black + .withOpacity(0.4), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset(0, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: + BorderRadius.circular(8), + // add a shadow to the image + child: Image.network( + // snapshot.data[index]['image'], + snapshot.data?[index] + ['image'] ?? + "https://via.placeholder.com/200x316?text=No+Image", + fit: BoxFit.cover, + ), + ), + ), + ), + ), + const SizedBox( + height: 10, + ), + ], + ), + ), + ), + ); + }, + ), + ), + const SizedBox( + height: 10, + ), + // page break + const Divider( + height: 5, + thickness: 5, + indent: 0, + endIndent: 0, + ), + const SizedBox( + height: 10, + ), + ], + ); + } else { + return Container(); + } + } else { + return SizedBox( + height: 100, + child: const Center( + child: CircularProgressIndicator(), + ), + ); + } + }, + ), + // text align left side of the column + const Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + "Library", + style: TextStyle( + // size is the size of a title + fontSize: 30, + // decoration: TextDecoration.underline, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox( + height: 10, + ), + // vertical list of books + FutureBuilder( + future: getServerCategories(context, returnFolders: false), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + debugPrint("snapshot data: ${snapshot.data}"); + return GridView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + ), + itemBuilder: (context, index) { + return Card( + child: InkWell( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: + (context, animation, secondaryAnimation) => + InfoScreen( + title: snapshot.data![index]['name'] ?? "null", + imageUrl: (snapshot.data![index]['imagePath'] ?? + "null"), + description: snapshot.data![index] + ['description'] ?? + "null", + tags: snapshot.data![index]['tags'] ?? ["null"], + url: snapshot.data![index]['url'] ?? "null", + year: snapshot.data![index]['releaseDate'] ?? + "null", + stars: snapshot.data![index]['rating'] ?? -1, + path: snapshot.data![index]['path'] ?? "null", + comicId: snapshot.data![index]['id'] ?? "null", + ), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + var begin = const Offset(1.0, 0.0); + var end = Offset.zero; + var curve = Curves.ease; + + var tween = Tween(begin: begin, end: end) + .chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ), + ); + }, + child: Column( + children: [ + const SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.4), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset(0, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + // add a shadow to the image + child: Image.network( + snapshot.data![index]['imagePath'], + height: MediaQuery.of(context).size.height / + 6 * + 0.8, + fit: BoxFit.fitWidth, + ), + ), + ), + const SizedBox( + height: 10, + ), + // auto size text to fit the width of the card (max 2 lines) + Flexible( + // give some padding to the text + child: Padding( + padding: + const EdgeInsets.only(left: 5, right: 5), + child: AutoSizeText( + snapshot.data![index]['name'], + maxLines: 3, + minFontSize: 10, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox( + height: 5, + ), + + if (snapshot.data![index]['releaseDate'] != "null") + Flexible( + // give some padding to the text + child: Padding( + padding: + const EdgeInsets.only(left: 5, right: 5), + child: AutoSizeText( + snapshot.data![index]['releaseDate'], + maxLines: 1, + minFontSize: 10, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + ), + ), + ], + ), + ), + ); + }, + itemCount: snapshot.data!.length, + ); + } else { + return SizedBox( + height: 100, + child: const Center( + child: CircularProgressIndicator(), + ), + ); + } + }, + ), + const SizedBox( + height: 10, + ), + ], + ), + ); + } +} diff --git a/lib/screens/homeScreen.dart b/lib/screens/homeScreen.dart index 45985845..e65845b7 100644 --- a/lib/screens/homeScreen.dart +++ b/lib/screens/homeScreen.dart @@ -3,13 +3,18 @@ import 'package:flutter/material.dart'; import 'package:jellybook/providers/fetchCategories.dart'; -import 'package:jellybook/screens/collectionScreen.dart'; -import 'package:jellybook/screens/infoScreen.dart'; -import 'package:jellybook/screens/loginScreen.dart'; import 'package:jellybook/models/login.dart'; import 'package:isar/isar.dart'; import 'package:auto_size_text/auto_size_text.dart'; +// Screens imports +import 'package:jellybook/screens/MainScreens/mainMenu.dart'; +import 'package:jellybook/screens/MainScreens/settingsScreen.dart'; +import 'package:jellybook/screens/MainScreens/downloadsScreen.dart'; +import 'package:jellybook/screens/collectionScreen.dart'; +import 'package:jellybook/screens/infoScreen.dart'; +import 'package:jellybook/screens/loginScreen.dart'; + class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); @@ -46,394 +51,147 @@ class _HomeScreenState extends State { ); } + int _selectedIndex = 0; + final List screens = [ + MainMenu(), + DownloadsScreen(), + // CollectionScreen(), + // InfoScreen(), + SettingsScreen(), + ]; + // should be a futureBuilder @override Widget build(BuildContext context) { + // have the body be the selected screen return Scaffold( - appBar: AppBar( - leading: IconButton( - icon: const Icon(Icons.home), - onPressed: () {}, - ), - title: const Text('Home'), - actions: [ - IconButton( - icon: const Icon(Icons.logout), - onPressed: () { - logout(); - }, + body: screens[_selectedIndex], + bottomNavigationBar: Container( + height: 60, + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), ), - ], - ), - body: ListView( + ), + // child: BottomNavigationBar( + // type: BottomNavigationBarType.fixed, + // showSelectedLabels: false, + // showUnselectedLabels: false, + // selectedItemColor: Colors.pink, + // unselectedItemColor: Colors.grey, + // iconSize: 24, + // // make active one bold + // selectedIconTheme: const IconThemeData( + // size: 30, + // ), + // currentIndex: _selectedIndex, + // onTap: (index) { + // setState(() { + // _selectedIndex = index; + // }); + // }, + // items: const [ + // BottomNavigationBarItem( + // icon: Icon(Icons.home), + // label: 'Home', + // ), + // BottomNavigationBarItem( + // icon: Icon(Icons.search), + // label: 'Search', + // ), + // // Downloaded Screen + // BottomNavigationBarItem( + // icon: Icon(Icons.download), + // label: 'Downloads', + // ), + // // continue reading screen + // BottomNavigationBarItem( + // icon: Icon(Icons.book), + // label: 'Continue Reading', + // ), + // // Settings Screen + // BottomNavigationBarItem( + // icon: Icon(Icons.settings), + // label: 'Settings', + // ), + // ], + // ), + // use a row to make the bottom navigation bar + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - const SizedBox( - height: 10, - ), - // text align left side of the column - FutureBuilder( - future: getServerCategories(context, returnFolders: true), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasData && - snapshot.data != null && - snapshot.data.isNotEmpty) { - debugPrint("snapshot data: ${snapshot.data}"); - return Column( - children: [ - const Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: EdgeInsets.only(left: 10), - child: Text( - "Collections", - style: TextStyle( - // size is the size of a title - fontSize: 30, - // decoration: TextDecoration.underline, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - const SizedBox( - height: 10, - ), - SizedBox( - width: double.infinity, - height: MediaQuery.of(context).size.height / 6 * 1.2, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: snapshot.data?.length, - itemBuilder: (context, index) { - return SizedBox( - width: MediaQuery.of(context).size.width / 3, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - child: InkWell( - onTap: () { - debugPrint("tapped"); - Navigator.push( - context, - PageRouteBuilder( - pageBuilder: (context, animation, - secondaryAnimation) => - collectionScreen( - folderId: snapshot.data[index] - ['id'], - name: snapshot.data[index]['name'], - image: snapshot.data[index] - ['image'], - bookIds: snapshot.data[index] - ['bookIds'], - ), - transitionsBuilder: (context, - animation, - secondaryAnimation, - child) { - var begin = const Offset(1.0, 0.0); - var end = Offset.zero; - var curve = Curves.ease; - - var tween = Tween( - begin: begin, end: end) - .chain( - CurveTween(curve: curve)); - - return SlideTransition( - position: animation.drive(tween), - child: child, - ); - }, - ), - ); - }, - child: Column( - children: [ - SizedBox( - height: 10 * - MediaQuery.of(context) - .size - .height / - 1000, - ), - Padding( - padding: EdgeInsets.only( - left: 10 * - MediaQuery.of(context) - .size - .width / - 1000, - right: 10 * - MediaQuery.of(context) - .size - .width / - 1000), - child: FittedBox( - fit: BoxFit.fitWidth, - child: Text( - // snapshot.data[index]['name'], - snapshot.data?[index]['name'] ?? - "", - textAlign: TextAlign.center, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - ), - // start all images at the same height rather than same offset - SizedBox( - height: 5 * - MediaQuery.of(context) - .size - .height / - 1000, - ), - Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(8), - boxShadow: [ - BoxShadow( - color: Colors.black - .withOpacity(0.4), - spreadRadius: 5, - blurRadius: 7, - offset: const Offset(0, 3), - ), - ], - ), - child: ClipRRect( - borderRadius: - BorderRadius.circular(8), - // add a shadow to the image - child: Image.network( - // snapshot.data[index]['image'], - snapshot.data?[index]['image'] ?? - "https://via.placeholder.com/200x316?text=No+Image", - height: MediaQuery.of(context) - .size - .height / - 6 * - 0.8, - fit: BoxFit.fitWidth, - ), - ), - ), - const SizedBox( - height: 10, - ), - ], - ), - ), - ), - ); - }, - ), - ), - const SizedBox( - height: 10, - ), - // page break - const Divider( - height: 5, - thickness: 5, - indent: 0, - endIndent: 0, - ), - const SizedBox( - height: 10, - ), - ], - ); - } else { - return Container(); - } - } else { - return SizedBox( - height: 100, - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } + // Home Screen + IconButton( + icon: const Icon(Icons.home), + // make the color a gradient between #AA5CC3 and #00A4DC if selected + color: + _selectedIndex == 0 ? const Color(0xFFAA5CC3) : Colors.grey, + iconSize: _selectedIndex == 0 ? 30 : 24, + splashColor: Colors.transparent, + onPressed: () { + setState(() { + _selectedIndex = 0; + }); }, ), - // text align left side of the column - const Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: EdgeInsets.only(left: 10), - child: Text( - "Library", - style: TextStyle( - // size is the size of a title - fontSize: 30, - // decoration: TextDecoration.underline, - fontWeight: FontWeight.bold, - ), - ), - ), + // Search Screen + IconButton( + iconSize: _selectedIndex == 1 ? 30 : 24, + color: + _selectedIndex == 1 ? const Color(0xFFAA5CC3) : Colors.grey, + splashColor: Colors.transparent, + icon: const Icon(Icons.search), + onPressed: () { + setState(() { + _selectedIndex = 1; + }); + }, ), - const SizedBox( - height: 10, + // Downloaded Screen + IconButton( + icon: const Icon(Icons.download), + color: + _selectedIndex == 2 ? const Color(0xFFAA5CC3) : Colors.grey, + iconSize: _selectedIndex == 2 ? 30 : 24, + splashColor: Colors.transparent, + onPressed: () { + setState(() { + _selectedIndex = 2; + }); + }, ), - // vertical list of books - FutureBuilder( - future: getServerCategories(context, returnFolders: false), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - debugPrint("snapshot data: ${snapshot.data}"); - return GridView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - ), - itemBuilder: (context, index) { - return Card( - child: InkWell( - onTap: () { - Navigator.push( - context, - PageRouteBuilder( - pageBuilder: - (context, animation, secondaryAnimation) => - InfoScreen( - title: - snapshot.data![index]['name'] ?? "null", - imageUrl: (snapshot.data![index] - ['imagePath'] ?? - "null"), - description: snapshot.data![index] - ['description'] ?? - "null", - tags: - snapshot.data![index]['tags'] ?? ["null"], - url: snapshot.data![index]['url'] ?? "null", - year: snapshot.data![index]['releaseDate'] ?? - "null", - stars: snapshot.data![index]['rating'] ?? -1, - path: snapshot.data![index]['path'] ?? "null", - comicId: - snapshot.data![index]['id'] ?? "null", - ), - transitionsBuilder: (context, animation, - secondaryAnimation, child) { - var begin = const Offset(1.0, 0.0); - var end = Offset.zero; - var curve = Curves.ease; - - var tween = Tween(begin: begin, end: end) - .chain(CurveTween(curve: curve)); - - return SlideTransition( - position: animation.drive(tween), - child: child, - ); - }, - ), - ); - }, - child: Column( - children: [ - const SizedBox( - height: 10, - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.4), - spreadRadius: 5, - blurRadius: 7, - offset: const Offset(0, 3), - ), - ], - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - // add a shadow to the image - child: Image.network( - snapshot.data![index]['imagePath'], - height: MediaQuery.of(context).size.height / - 6 * - 0.8, - fit: BoxFit.fitWidth, - ), - ), - ), - const SizedBox( - height: 10, - ), - // auto size text to fit the width of the card (max 2 lines) - Flexible( - // give some padding to the text - child: Padding( - padding: - const EdgeInsets.only(left: 5, right: 5), - child: AutoSizeText( - snapshot.data![index]['name'], - maxLines: 3, - minFontSize: 10, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - const SizedBox( - height: 5, - ), - - if (snapshot.data![index]['releaseDate'] != - "null") - Flexible( - // give some padding to the text - child: Padding( - padding: const EdgeInsets.only( - left: 5, right: 5), - child: AutoSizeText( - snapshot.data![index]['releaseDate'], - maxLines: 1, - minFontSize: 10, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 12, - color: Colors.grey, - ), - ), - ), - ), - ], - ), - ), - ); - }, - itemCount: snapshot.data!.length, - ); - } else { - return SizedBox( - height: 100, - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } + // continue reading screen + IconButton( + icon: const Icon(Icons.book), + color: + _selectedIndex == 3 ? const Color(0xFFAA5CC3) : Colors.grey, + iconSize: _selectedIndex == 3 ? 30 : 24, + splashColor: Colors.transparent, + onPressed: () { + setState(() { + _selectedIndex = 3; + }); }, ), - const SizedBox( - height: 10, + // Settings Screen + IconButton( + icon: const Icon(Icons.settings), + color: + _selectedIndex == 4 ? const Color(0xFFAA5CC3) : Colors.grey, + iconSize: _selectedIndex == 4 ? 30 : 24, + splashColor: Colors.transparent, + onPressed: () { + setState(() { + _selectedIndex = 4; + // go to settings screen + }); + }, ), ], ), + ), ); } } From 9ac47fe61f93a733d6f44c29ceec57fafc95c105 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 10:05:31 -0500 Subject: [PATCH 03/12] create a downloaded screen --- lib/screens/MainScreens/downloadsScreen.dart | 334 +++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 lib/screens/MainScreens/downloadsScreen.dart diff --git a/lib/screens/MainScreens/downloadsScreen.dart b/lib/screens/MainScreens/downloadsScreen.dart new file mode 100644 index 00000000..d7ce28b4 --- /dev/null +++ b/lib/screens/MainScreens/downloadsScreen.dart @@ -0,0 +1,334 @@ +// The purpose of this file is to have a screen with all the downloaded books with easy access to delete them + +import 'package:flutter/material.dart'; +import 'package:jellybook/screens/collectionScreen.dart'; +import 'package:jellybook/screens/infoScreen.dart'; +import 'package:isar/isar.dart'; +import 'package:jellybook/models/entry.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:jellybook/providers/fixRichText.dart'; +import 'package:flutter_star/flutter_star.dart'; + +class DownloadsScreen extends StatefulWidget { + @override + _DownloadsScreenState createState() => _DownloadsScreenState(); +} + +class _DownloadsScreenState extends State { + Future>> getEntries() async { + final isar = Isar.getInstance(); + // final isar = Isar.openSync([EntrySchema]); + List> entries = []; + // get the entries from the database + final entryList = + await isar!.entrys.where().filter().downloadedEqualTo(true).findAll(); + // get the length of the list + final length = entryList.length; + // var entryList = box.values.toList(); + debugPrint("bookIds: $length"); + for (int i = 0; i < length; i++) { + // get the first entry that matches the bookId + // convert element.id to int + var entry = entryList.firstWhere((element) => int.parse(element.id) == i); + entries.add({ + 'id': entry.id, + 'title': entry.title, + 'imagePath': entry.imagePath != '' + ? entry.imagePath + : 'https://via.placeholder.com/200x316?text=No+Image', + 'rating': entry.rating, + 'description': entry.description, + 'path': entry.path, + 'year': entry.releaseDate, + 'type': entry.type, + 'tags': entry.tags, + 'url': entry.url, + }); + } + return entries; + } + + Future>> get entries async { + return await getEntries(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Downloads'), + ), + body: Column( + children: [ + const SizedBox(height: 10), + const Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + "Downloads", + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + // add a divider + const SizedBox(height: 5), + Divider(height: 10, color: Colors.grey[400]), + const SizedBox(height: 5), + FutureBuilder( + future: entries, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && + snapshot.data.length > 0 && + snapshot.connectionState == ConnectionState.done) { + return ListView.builder( + itemCount: snapshot.data.length, + itemBuilder: (BuildContext context, int index) { + return ListTile( + onTap: () { + if (snapshot.data[index]['type'] != 'Folder') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: + (context, animation, secondaryAnimation) => + InfoScreen( + comicId: snapshot.data[index]['id'], + title: snapshot.data[index]['title'], + imageUrl: snapshot.data[index]['imagePath'], + stars: snapshot.data[index]['rating'], + description: snapshot.data[index] + ['description'], + path: snapshot.data[index]['type'], + year: snapshot.data[index]['year'], + url: snapshot.data[index]['url'], + tags: snapshot.data[index]['tags'], + ), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + var begin = const Offset(1.0, 0.0); + var end = Offset.zero; + var curve = Curves.ease; + + var tween = Tween(begin: begin, end: end) + .chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ), + ); + } else if (snapshot.data[index]['type'] == 'folder') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: + (context, animation, secondaryAnimation) => + collectionScreen( + folderId: snapshot.data[index]['id'], + name: snapshot.data[index]['title'], + image: snapshot.data[index]['imagePath'], + bookIds: snapshot.data[index]['bookIds'], + ), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + var begin = const Offset(1.0, 0.0); + var end = Offset.zero; + var curve = Curves.ease; + + var tween = Tween(begin: begin, end: end) + .chain(CurveTween(curve: curve)); + + return SlideTransition( + position: animation.drive(tween), + child: child, + ); + }, + ), + ); + } + }, + trailing: IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Delete"), + content: const Text( + "Are you sure you want to delete this book/comic?"), + actions: [ + TextButton( + child: const Text("Cancel"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text("Delete"), + onPressed: () { + final isar = Isar.getInstance(); + var entry = isar!.entrys + .where() + .idEqualTo(snapshot.data[index]['id']) + .findFirstSync(); + isar.entrys.delete(entry!.isarId); + snapshot.data.removeAt(index); + setState(() {}); + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + final isar = Isar.getInstance(); + isar!.entrys.delete(snapshot.data[index]['id']); + // delete the entry from the list + setState(() { + snapshot.data.removeAt(index); + }); + }, + ), + title: Text(snapshot.data[index]['title']), + leading: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(2, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Image.network( + snapshot.data[index]['imagePath'], + width: MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ), + ), + ), + subtitle: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (snapshot.data[index]['rating'] >= 0) + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: + const EdgeInsets.fromLTRB(0, 0, 0, 0), + child: CustomRating( + max: 5, + score: snapshot.data[index]['rating'] / 2, + star: Star( + fillColor: Color.lerp( + Colors.red, + Colors.yellow, + snapshot.data[index]['rating'] / 10)!, + emptyColor: Colors.grey.withOpacity(0.5), + ), + onRating: (double score) {}, + ), + ), + Padding( + padding: + const EdgeInsets.fromLTRB(8, 0, 0, 0), + child: Text( + "${(snapshot.data[index]['rating'] / 2).toStringAsFixed(2)} / 5.00", + style: const TextStyle( + fontStyle: FontStyle.italic, + fontSize: 15, + ), + ), + ), + ], + ), + if (snapshot.data[index]['rating'] < 0 && + snapshot.data[index]['description'] != '') + Flexible( + child: RichText( + text: TextSpan( + text: fixRichText( + snapshot.data[index]['description']), + style: const TextStyle( + fontStyle: FontStyle.italic, + fontSize: 15, + color: Colors.grey, + ), + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ], + ), + ); + }, + ); + } else if (snapshot.hasData && + snapshot.data.length == 0 && + snapshot.connectionState == ConnectionState.done) { + // due to this being inside a Column, just using a Center widget doesn't work + return Expanded( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + "No books were found to be downloaded", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + ), + ), + SizedBox( + height: 10, + ), + Icon(Icons.sentiment_dissatisfied, size: 100), + ], + ), + ), + ); + } else if (snapshot.hasError) { + return Expanded( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + "An error has occured", + style: TextStyle( + fontSize: 25, + ), + ), + SizedBox( + height: 10, + ), + Icon(Icons.sentiment_dissatisfied, size: 100), + ], + ), + ), + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ], + ), + ); + } +} From 5432fe998cafb3c00f0613420dd1c17e7f3bdd4c Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 10:05:43 -0500 Subject: [PATCH 04/12] Code cleanup --- lib/screens/collectionScreen.dart | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/screens/collectionScreen.dart b/lib/screens/collectionScreen.dart index ef6a9948..e858c7be 100644 --- a/lib/screens/collectionScreen.dart +++ b/lib/screens/collectionScreen.dart @@ -154,19 +154,15 @@ class collectionScreen extends StatelessWidget { borderRadius: BorderRadius.circular(5), child: Image.network( snapshot.data[index]['imagePath'], - // set the width to 10% of the screen width width: MediaQuery.of(context).size.width * 0.1, fit: BoxFit.fitWidth, ), ), ), - // have the subitle be the rating subtitle: Row( - // allign the row to the left mainAxisAlignment: MainAxisAlignment.start, children: [ if (snapshot.data[index]['rating'] >= 0) - // allign the stars to the very left of the row Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -197,22 +193,8 @@ class collectionScreen extends StatelessWidget { ), ], ), - // if the rating is less than 0 and the description is not empty - // then display the first 50 characters of the description if (snapshot.data[index]['rating'] < 0 && snapshot.data[index]['description'] != '') - // Text( - // snapshot.data[index]['description'].length > 50 - // ? snapshot.data[index]['description'] - // .substring(0, 42) + - // "..." - // : snapshot.data[index]['description'], - // style: const TextStyle( - // fontStyle: FontStyle.italic, - // fontSize: 15, - // ), - // ), - // use rich text instead Flexible( child: RichText( text: TextSpan( @@ -225,7 +207,6 @@ class collectionScreen extends StatelessWidget { color: Colors.grey, ), ), - // prevent the text from overflowing overflow: TextOverflow.ellipsis, maxLines: 1, ), From 3c81dc84fb50b2b1c59bf0ed75d106bc6e1a5443 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 10:05:55 -0500 Subject: [PATCH 05/12] formatting --- lib/providers/fetchCategories.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/providers/fetchCategories.dart b/lib/providers/fetchCategories.dart index 00341e31..331c3ffb 100644 --- a/lib/providers/fetchCategories.dart +++ b/lib/providers/fetchCategories.dart @@ -173,15 +173,22 @@ Future removeEntriesFromDatabase( for (var i = 0; i < entryIds.length; i++) { if (!serverIds.contains(entryIds[i])) { - int isarId = await isar.entrys.where().idEqualTo(entryIds[i]).findAll().then((value) => value[0].isarId); - entriesToRemove.add(isarId); + if (entries[i].isarId != null) { + int isarId = await isar.entrys + .where() + .idEqualTo(entryIds[i]) + .findAll() + .then((value) => value[0].isarId); + entriesToRemove.add(isarId); + } } } for (var i = 0; i < folderIds.length; i++) { if (!serverIds.contains(folderIds[i])) { - int isarId = await isar.folders.where().findAll().then((value) => value[i].isarId); - foldersToRemove.add(isarId); + int isarId = + await isar.folders.where().findAll().then((value) => value[i].isarId); + foldersToRemove.add(isarId); } } From 3924b3fd68ee2469f5619a27d3fb1702ccaf6451 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 16:15:00 -0500 Subject: [PATCH 06/12] Added "No Cover Art" image --- assets/images/NoCoverArt.png | Bin 0 -> 483 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/images/NoCoverArt.png diff --git a/assets/images/NoCoverArt.png b/assets/images/NoCoverArt.png new file mode 100644 index 0000000000000000000000000000000000000000..5aeed5fd84b070516732d99d1d8b0c6e1160159c GIT binary patch literal 483 zcmeAS@N?(olHy`uVBq!ia0vp^Cm0wQZCIFrta7HuVn9kdz$e7@%$YOOrcGPCc=6Gr zNB8a9H)qbA?c2AnTD5B9#*NE-599%raTa()7BevL9RXp+soH$fK!fr;T^vIy=DfYU z-~W(<#IcXdr^F|}4oy)}aXXeITqvSkEzDff;+V?PvVn>7x+N>`_|NHQL)F%Jo(r&`a$KUtj=xEC-g_W?*4fuMmfdm zWAm?qq|<$0ByMjqx!x?@`NrX<$&&Yu}!D>$Ph8v`@$K zdz%zJFY~+I@~D$b^1eUCcZs~|+uT&6soQn`lpo!#Vj%Tmi^)2ft6?ku7RF!i-7Wln zX@$qr+bpl1{SEbx*}1Z&EpbNqytapTy1Uy$q>t+drun Date: Tue, 13 Dec 2022 16:16:25 -0500 Subject: [PATCH 07/12] addded `libisar.so` to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b3ef4a78..c43ef2ee 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +libisar.so From 350528116d1c226f1fd3ef8a17a6c4761889c1f3 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 16:16:37 -0500 Subject: [PATCH 08/12] added `google_nav_bar` --- pubspec.lock | 7 +++++++ pubspec.yaml | 2 ++ 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index a08528c7..9e98b0f3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -408,6 +408,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + google_nav_bar: + dependency: "direct main" + description: + name: google_nav_bar + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.6" graphs: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6ab2c3c8..d9b2dc2b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: pdfx: ^2.3.0 package_info_plus: ^3.0.2 auto_size_text: ^3.0.0 + google_nav_bar: ^5.0.6 dev_dependencies: flutter_test: @@ -90,6 +91,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/images/Logo.png + - assets/images/NoCoverArt.png # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg From 21f2ea7b8d53492a145a5a3b6c405868c081443e Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 16:17:25 -0500 Subject: [PATCH 09/12] Fix screens for new asset --- lib/providers/fetchBooks.dart | 5 +- lib/screens/MainScreens/downloadsScreen.dart | 44 +++++----- lib/screens/MainScreens/mainMenu.dart | 86 ++++++++++++++------ lib/screens/collectionScreen.dart | 20 +++-- lib/screens/infoScreen.dart | 18 ++-- 5 files changed, 110 insertions(+), 63 deletions(-) diff --git a/lib/providers/fetchBooks.dart b/lib/providers/fetchBooks.dart index eac2a72c..8932afc8 100644 --- a/lib/providers/fetchBooks.dart +++ b/lib/providers/fetchBooks.dart @@ -65,7 +65,7 @@ Future>> getComics( 'imagePath': "$url/Items/${responseData['Items'][i]['Id']}/Images/Primary?&quality=90&Tag=${responseData['Items'][i]['ImageTags']['Primary']}", if (responseData['Items'][i]['ImageTags']['Primary'] == null) - 'imagePath': "https://via.placeholder.com/200x316?text=No+Cover+Art", + 'imagePath': 'Asset', 'releaseDate': responseData['Items'][i]['ProductionYear'].toString(), 'path': responseData['Items'][i]['Path'] ?? '', 'description': responseData['Items'][i]['Overview'] ?? '', @@ -99,7 +99,7 @@ Future>> getComics( String imagePath = "$url/Items/${responseData['Items'][i]['Id']}/Images/Primary?&quality=90&Tag=${responseData['Items'][i]['ImageTags']['Primary']}"; if (responseData['Items'][i]['ImageTags']['Primary'] == null) { - imagePath = "https://via.placeholder.com/200x316?text=No+Cover+Art"; + imagePath = 'Asset'; } String releaseDate = responseData['Items'][i]['ProductionYear'].toString(); @@ -161,6 +161,7 @@ Future>> getComics( entry.folderPath = entryExists.folderPath; entry.filePath = entryExists.filePath; entry.epubCfi = entryExists.epubCfi; + entry.pageNum = entryExists.pageNum; } await isar.writeTxn(() async { diff --git a/lib/screens/MainScreens/downloadsScreen.dart b/lib/screens/MainScreens/downloadsScreen.dart index d7ce28b4..d3f63895 100644 --- a/lib/screens/MainScreens/downloadsScreen.dart +++ b/lib/screens/MainScreens/downloadsScreen.dart @@ -26,16 +26,12 @@ class _DownloadsScreenState extends State { final length = entryList.length; // var entryList = box.values.toList(); debugPrint("bookIds: $length"); - for (int i = 0; i < length; i++) { - // get the first entry that matches the bookId - // convert element.id to int - var entry = entryList.firstWhere((element) => int.parse(element.id) == i); + + for (var entry in entryList) { entries.add({ - 'id': entry.id, 'title': entry.title, - 'imagePath': entry.imagePath != '' - ? entry.imagePath - : 'https://via.placeholder.com/200x316?text=No+Image', + 'imagePath': entry.imagePath, + 'id': entry.id, 'rating': entry.rating, 'description': entry.description, 'path': entry.path, @@ -43,6 +39,7 @@ class _DownloadsScreenState extends State { 'type': entry.type, 'tags': entry.tags, 'url': entry.url, + 'downloaded': entry.downloaded, }); } return entries; @@ -85,6 +82,7 @@ class _DownloadsScreenState extends State { snapshot.data.length > 0 && snapshot.connectionState == ConnectionState.done) { return ListView.builder( + shrinkWrap: true, itemCount: snapshot.data.length, itemBuilder: (BuildContext context, int index) { return ListTile( @@ -178,7 +176,9 @@ class _DownloadsScreenState extends State { .where() .idEqualTo(snapshot.data[index]['id']) .findFirstSync(); - isar.entrys.delete(entry!.isarId); + isar.writeTxn(() async { + await isar.entrys.delete(entry!.isarId); + }); snapshot.data.removeAt(index); setState(() {}); Navigator.of(context).pop(); @@ -188,12 +188,6 @@ class _DownloadsScreenState extends State { ); }, ); - final isar = Isar.getInstance(); - isar!.entrys.delete(snapshot.data[index]['id']); - // delete the entry from the list - setState(() { - snapshot.data.removeAt(index); - }); }, ), title: Text(snapshot.data[index]['title']), @@ -211,11 +205,19 @@ class _DownloadsScreenState extends State { ), child: ClipRRect( borderRadius: BorderRadius.circular(5), - child: Image.network( - snapshot.data[index]['imagePath'], - width: MediaQuery.of(context).size.width * 0.1, - fit: BoxFit.fitWidth, - ), + child: snapshot.data![index]['imagePath'] != 'Asset' + ? Image.network( + snapshot.data![index]['imagePath'], + width: + MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ) + : Image.asset( + 'assets/images/NoCoverArt.png', + width: + MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ), ), ), subtitle: Row( @@ -279,7 +281,6 @@ class _DownloadsScreenState extends State { } else if (snapshot.hasData && snapshot.data.length == 0 && snapshot.connectionState == ConnectionState.done) { - // due to this being inside a Column, just using a Center widget doesn't work return Expanded( child: Center( child: Column( @@ -301,6 +302,7 @@ class _DownloadsScreenState extends State { ), ); } else if (snapshot.hasError) { + debugPrint(snapshot.error.toString()); return Expanded( child: Center( child: Column( diff --git a/lib/screens/MainScreens/mainMenu.dart b/lib/screens/MainScreens/mainMenu.dart index 03950f79..9e98e09a 100644 --- a/lib/screens/MainScreens/mainMenu.dart +++ b/lib/screens/MainScreens/mainMenu.dart @@ -165,18 +165,23 @@ class _MainMenuState extends State { .size .width / 1000), - child: Flexible( - // ensure that it wont overflow - child: AutoSizeText( - snapshot.data[index]['name'], - maxLines: 2, - minFontSize: 5, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, + child: Flex( + direction: Axis.horizontal, + children: [ + Flexible( + // ensure that it wont overflow + child: AutoSizeText( + snapshot.data[index]['name'], + maxLines: 2, + minFontSize: 5, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), ), - ), + ], ), ), // start all images at the same height rather than same offset @@ -185,7 +190,6 @@ class _MainMenuState extends State { MediaQuery.of(context).size.height / 1000, ), - // have the image change size based to fit the card Flexible( child: SizedBox( child: Container( @@ -206,13 +210,21 @@ class _MainMenuState extends State { borderRadius: BorderRadius.circular(8), // add a shadow to the image - child: Image.network( - // snapshot.data[index]['image'], - snapshot.data?[index] - ['image'] ?? - "https://via.placeholder.com/200x316?text=No+Image", - fit: BoxFit.cover, - ), + child: snapshot.data[index] + ['image'] != + null && + snapshot.data[index] + ['image'] != + "Asset" + ? Image.network( + snapshot.data[index] + ['image'], + fit: BoxFit.fitWidth, + ) + : Image.asset( + "assets/images/NoCoverArt.png", + fit: BoxFit.fitWidth, + ), ), ), ), @@ -299,7 +311,7 @@ class _MainMenuState extends State { InfoScreen( title: snapshot.data![index]['name'] ?? "null", imageUrl: (snapshot.data![index]['imagePath'] ?? - "null"), + "Asset"), description: snapshot.data![index] ['description'] ?? "null", @@ -348,13 +360,33 @@ class _MainMenuState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(8), // add a shadow to the image - child: Image.network( - snapshot.data![index]['imagePath'], - height: MediaQuery.of(context).size.height / - 6 * - 0.8, - fit: BoxFit.fitWidth, - ), + // child: Image.network( + // snapshot.data![index]['imagePath'], + // height: MediaQuery.of(context).size.height / + // 6 * + // 0.8, + // fit: BoxFit.fitWidth, + // ), + child: snapshot.data![index]['imagePath'] != + null && + snapshot.data![index]['imagePath'] != + "Asset" + ? Image.network( + snapshot.data![index]['imagePath'], + height: + MediaQuery.of(context).size.height / + 6 * + 0.8, + fit: BoxFit.fitWidth, + ) + : Image.asset( + "assets/images/NoCoverArt.png", + height: + MediaQuery.of(context).size.height / + 6 * + 0.8, + fit: BoxFit.fitWidth, + ), ), ), const SizedBox( diff --git a/lib/screens/collectionScreen.dart b/lib/screens/collectionScreen.dart index e858c7be..5fe5b7e3 100644 --- a/lib/screens/collectionScreen.dart +++ b/lib/screens/collectionScreen.dart @@ -36,9 +36,7 @@ class collectionScreen extends StatelessWidget { entries.add({ 'id': entry.id, 'title': entry.title, - 'imagePath': entry.imagePath != '' - ? entry.imagePath - : 'https://via.placeholder.com/200x316?text=No+Image', + 'imagePath': entry.imagePath != '' ? entry.imagePath : 'Asset', 'rating': entry.rating, 'description': entry.description, 'path': entry.path, @@ -152,11 +150,17 @@ class collectionScreen extends StatelessWidget { ), child: ClipRRect( borderRadius: BorderRadius.circular(5), - child: Image.network( - snapshot.data[index]['imagePath'], - width: MediaQuery.of(context).size.width * 0.1, - fit: BoxFit.fitWidth, - ), + child: snapshot.data[index]['imagePath'] != "Asset" + ? Image.network( + snapshot.data[index]['imagePath'], + width: MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ) + : Image.asset( + 'assets/images/NoCoverArt.png', + width: MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ), ), ), subtitle: Row( diff --git a/lib/screens/infoScreen.dart b/lib/screens/infoScreen.dart index d28d4259..f2b16d43 100644 --- a/lib/screens/infoScreen.dart +++ b/lib/screens/infoScreen.dart @@ -77,11 +77,19 @@ class InfoScreen extends StatelessWidget { ), child: ClipRRect( borderRadius: BorderRadius.circular(20), - child: Image.network( - imageUrl, - width: MediaQuery.of(context).size.width / 4 * 0.8, - fit: BoxFit.fitWidth, - ), + child: imageUrl.isNotEmpty && imageUrl != "Asset" + ? Image.network( + imageUrl, + width: + MediaQuery.of(context).size.width / 4 * 0.8, + fit: BoxFit.fitWidth, + ) + : Image.asset( + 'assets/images/NoCoverArt.png', + width: + MediaQuery.of(context).size.width / 4 * 0.8, + fit: BoxFit.fitWidth, + ), ), ), ), From 4a62e810b8d27d0f2d2d032844922f641e355a8e Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 16:18:12 -0500 Subject: [PATCH 10/12] refactor `fetchCategories.dart` --- lib/providers/fetchCategories.dart | 48 ++++++++++++++---------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/lib/providers/fetchCategories.dart b/lib/providers/fetchCategories.dart index 331c3ffb..f67d42b2 100644 --- a/lib/providers/fetchCategories.dart +++ b/lib/providers/fetchCategories.dart @@ -147,6 +147,8 @@ Future removeEntriesFromDatabase( // now see if the database has any entries that are not in the server // if it does, remove those from the database + // get all the entries from the database + final isar = Isar.getInstance(); final entries = await isar!.entrys.where().findAll(); final folders = await isar.folders.where().findAll(); @@ -156,45 +158,39 @@ Future removeEntriesFromDatabase( List entriesToRemove = []; List foldersToRemove = []; - for (var i = 0; i < entries.length; i++) { - entryIds.add(entries[i].id); + // get all the ids from the server + for (int i = 0; i < comicsArray.length; i++) { + List> comics = await comicsArray[i]; + for (int j = 0; j < comics.length; j++) { + serverIds.add(comics[j]['id']); + } } - for (var i = 0; i < folders.length; i++) { - folderIds.add(folders[i].id); + // get all the ids from the database + for (int i = 0; i < entries.length; i++) { + entryIds.add(entries[i].id); } - for (var i = 0; i < comicsArray.length; i++) { - List> comics = await comicsArray[i]; - for (var j = 0; j < comics.length; j++) { - serverIds.add(comics[j]['id']); - } + // get all the ids from the database + for (int i = 0; i < folders.length; i++) { + folderIds.add(folders[i].id); } - for (var i = 0; i < entryIds.length; i++) { + // find the ids that are in the database but not in the server + for (int i = 0; i < entryIds.length; i++) { if (!serverIds.contains(entryIds[i])) { - if (entries[i].isarId != null) { - int isarId = await isar.entrys - .where() - .idEqualTo(entryIds[i]) - .findAll() - .then((value) => value[0].isarId); - entriesToRemove.add(isarId); - } + entriesToRemove.add(i); } } - for (var i = 0; i < folderIds.length; i++) { + // find the ids that are in the database but not in the server + for (int i = 0; i < folderIds.length; i++) { if (!serverIds.contains(folderIds[i])) { - int isarId = - await isar.folders.where().findAll().then((value) => value[i].isarId); - foldersToRemove.add(isarId); + foldersToRemove.add(i); } } - debugPrint("entriesToRemove: $entriesToRemove"); - debugPrint("foldersToRemove: $foldersToRemove"); - + // remove the entries from the database await isar.writeTxn(() async { await isar.entrys.deleteAll(entriesToRemove); await isar.folders.deleteAll(foldersToRemove); @@ -244,7 +240,7 @@ Future> chooseCategories(List categories, context) async { TextButton( child: const Text('Done'), onPressed: () { - if (selected != null && selected.isNotEmpty) { + if (selected.isNotEmpty) { Navigator.of(context).pop(); } else { debugPrint("No categories selected"); From 859d528b0f488c34f2767a56c9cb26d51951d6f3 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 16:18:20 -0500 Subject: [PATCH 11/12] Create continueReadingScreen.dart --- .../MainScreens/continueReadingScreen.dart | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 lib/screens/MainScreens/continueReadingScreen.dart diff --git a/lib/screens/MainScreens/continueReadingScreen.dart b/lib/screens/MainScreens/continueReadingScreen.dart new file mode 100644 index 00000000..04af05e6 --- /dev/null +++ b/lib/screens/MainScreens/continueReadingScreen.dart @@ -0,0 +1,259 @@ +// the purpose of this file is to show a list of books that the user has started reading and has not finished yet + +import 'package:flutter/material.dart'; +import 'package:jellybook/screens/collectionScreen.dart'; +import 'package:jellybook/screens/infoScreen.dart'; +import 'package:isar/isar.dart'; +import 'package:jellybook/models/entry.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:jellybook/providers/fixRichText.dart'; +import 'package:flutter_star/flutter_star.dart'; + +class ContinueReadingScreen extends StatefulWidget { + @override + _ContinueReadingScreenState createState() => _ContinueReadingScreenState(); +} + +class _ContinueReadingScreenState extends State { + var isar = Isar.getInstance(); + + // get all the entries that have been started but not finished + Future> getEntries() async { + var entries = await isar!.entrys + .where() + .filter() + .downloadedEqualTo(true) + .and() + .pageNumGreaterThan(0) + .findAll(); + // convert the entries to a list + List entriesList = []; + for (int i = 0; i < entries.length; i++) { + entriesList.add(entries[i]); + } + return entriesList; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Continue Reading"), + ), + body: FutureBuilder( + future: getEntries(), + builder: (context, snapshot) { + if (snapshot.hasData && + snapshot.data != null && + snapshot.data!.length > 0) { + debugPrint(snapshot.data.toString()); + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + return Card( + child: ListTile( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InfoScreen( + year: snapshot.data![index].releaseDate, + title: snapshot.data![index].title, + path: snapshot.data![index].filePath, + stars: snapshot.data![index].rating, + comicId: snapshot.data![index].id, + url: snapshot.data![index].url, + tags: snapshot.data![index].tags, + description: snapshot.data![index].description, + imageUrl: snapshot.data![index].imagePath, + ), + ), + ); + }, + title: AutoSizeText( + snapshot.data![index].title, + maxLines: 1, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + leading: Stack( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(2, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: snapshot.data![index].imagePath != "Asset" + ? Image.network( + snapshot.data![index].imagePath, + width: + MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ) + : Image.asset( + 'assets/images/NoCoverArt.png', + width: + MediaQuery.of(context).size.width * 0.1, + fit: BoxFit.fitWidth, + ), + ), + ), + snapshot.data![index].progress != null && + snapshot.data![index].progress != 0 + ? Positioned( + top: 0, + left: 0, + child: Container( + width: + MediaQuery.of(context).size.width * 0.1, + height: + MediaQuery.of(context).size.height * 0.14, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(5), + ), + ), + child: Center( + child: Text( + snapshot.data![index].progress + .toString() + + "%", + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + ), + ), + ) + : Positioned( + top: 0, + left: 0, + child: Container( + width: + MediaQuery.of(context).size.width * 0.1, + height: + MediaQuery.of(context).size.width * 0.14, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(5), + ), + ), + child: Center( + child: Text( + snapshot.data![index].pageNum.toString(), + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + ), + ), + ), + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (snapshot.data![index].description.isNotEmpty) + AutoSizeText( + snapshot.data![index].description, + maxLines: 2, + style: TextStyle( + fontSize: 15, + ), + overflow: TextOverflow.ellipsis, + ), + if (snapshot.data![index].rating > 0) + Row( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), + child: CustomRating( + max: 5, + score: snapshot.data![index].rating / 2, + star: Star( + fillColor: Color.lerp( + Colors.red, + Colors.yellow, + snapshot.data![index].rating / 10)!, + emptyColor: Colors.grey.withOpacity(0.5), + ), + onRating: (double score) {}, + ), + ), + Text( + " " + snapshot.data![index].rating.toString(), + style: TextStyle( + fontSize: 15, + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } else if (snapshot.hasError) { + // return the error in the center of the screen + return Center( + child: Text( + snapshot.error.toString(), + style: TextStyle( + fontSize: 20, + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.waiting) { + return Center( + child: CircularProgressIndicator(), + ); + // if its empty, tell the user that there are no entries + } else if (snapshot.data!.length == 0) { + return Center( + // add text saying that there are no books/comics that have been started and have a frown under it + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "No books/comics have been started", + style: TextStyle( + fontSize: 25, + ), + textAlign: TextAlign.center, + ), + SizedBox( + height: 10, + ), + Icon( + Icons.sentiment_dissatisfied, + size: 100, + ), + ], + ), + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ); + } +} From 417e48589dba4fc7b7a88518037dd7c6334661e0 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 13 Dec 2022 16:18:55 -0500 Subject: [PATCH 12/12] Updated to new nav bar and `ContinueReadingScreen()` --- lib/screens/homeScreen.dart | 226 ++++++++++++++++++------------------ 1 file changed, 112 insertions(+), 114 deletions(-) diff --git a/lib/screens/homeScreen.dart b/lib/screens/homeScreen.dart index e65845b7..7360bc3b 100644 --- a/lib/screens/homeScreen.dart +++ b/lib/screens/homeScreen.dart @@ -2,18 +2,16 @@ // can see all their comics and manage them import 'package:flutter/material.dart'; -import 'package:jellybook/providers/fetchCategories.dart'; import 'package:jellybook/models/login.dart'; import 'package:isar/isar.dart'; -import 'package:auto_size_text/auto_size_text.dart'; // Screens imports import 'package:jellybook/screens/MainScreens/mainMenu.dart'; import 'package:jellybook/screens/MainScreens/settingsScreen.dart'; import 'package:jellybook/screens/MainScreens/downloadsScreen.dart'; -import 'package:jellybook/screens/collectionScreen.dart'; -import 'package:jellybook/screens/infoScreen.dart'; +import 'package:jellybook/screens/MainScreens/continueReadingScreen.dart'; import 'package:jellybook/screens/loginScreen.dart'; +import 'package:google_nav_bar/google_nav_bar.dart'; class HomeScreen extends StatefulWidget { @override @@ -55,8 +53,7 @@ class _HomeScreenState extends State { final List screens = [ MainMenu(), DownloadsScreen(), - // CollectionScreen(), - // InfoScreen(), + ContinueReadingScreen(), SettingsScreen(), ]; @@ -75,122 +72,123 @@ class _HomeScreenState extends State { topRight: Radius.circular(20), ), ), - // child: BottomNavigationBar( - // type: BottomNavigationBarType.fixed, - // showSelectedLabels: false, - // showUnselectedLabels: false, - // selectedItemColor: Colors.pink, - // unselectedItemColor: Colors.grey, - // iconSize: 24, - // // make active one bold - // selectedIconTheme: const IconThemeData( - // size: 30, - // ), - // currentIndex: _selectedIndex, - // onTap: (index) { - // setState(() { - // _selectedIndex = index; - // }); - // }, - // items: const [ - // BottomNavigationBarItem( - // icon: Icon(Icons.home), - // label: 'Home', - // ), - // BottomNavigationBarItem( - // icon: Icon(Icons.search), - // label: 'Search', + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: GNav( + rippleColor: Colors.grey[300]!, + hoverColor: Colors.grey[100]!, + activeColor: Colors.white, + tabBorderRadius: 15, + tabBackgroundGradient: LinearGradient( + colors: [ + const Color(0xFFAA5CC3).withOpacity(0.75), + const Color(0xFF00A4DC).withOpacity(0.75), + ], + ), + haptic: true, + iconSize: 24, + gap: 8, + padding: EdgeInsets.symmetric(horizontal: 25, vertical: 7), + duration: Duration(milliseconds: 600), + tabBackgroundColor: Theme.of(context).primaryColor, + color: Colors.white, + tabs: [ + GButton( + icon: Icons.home, + text: 'Home', + ), + GButton( + icon: Icons.download, + text: 'Downloads', + ), + GButton( + icon: Icons.book, + text: 'Reading', + ), + // GButton( + // icon: Icons.settings, + // text: 'Settings', + // ), + ], + selectedIndex: _selectedIndex, + onTabChange: (index) { + setState(() { + _selectedIndex = index; + }); + }, + ), + ), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // children: [ + // // Home Screen + // IconButton( + // icon: const Icon(Icons.home), + // color: + // _selectedIndex == 0 ? const Color(0xFFAA5CC3) : Colors.grey, + // iconSize: _selectedIndex == 0 ? 30 : 24, + // splashColor: Colors.transparent, + // onPressed: () { + // setState(() { + // _selectedIndex = 0; + // }); + // }, // ), + // // // Search Screen + // // IconButton( + // // iconSize: _selectedIndex == 1 ? 30 : 24, + // // color: + // // _selectedIndex == 1 ? const Color(0xFFAA5CC3) : Colors.grey, + // // splashColor: Colors.transparent, + // // icon: const Icon(Icons.search), + // // onPressed: () { + // // setState(() { + // // _selectedIndex = 1; + // // }); + // // }, + // // ), // // Downloaded Screen - // BottomNavigationBarItem( - // icon: Icon(Icons.download), - // label: 'Downloads', + // IconButton( + // icon: const Icon(Icons.download), + // color: + // _selectedIndex == 1 ? const Color(0xFFAA5CC3) : Colors.grey, + // iconSize: _selectedIndex == 1 ? 30 : 24, + // splashColor: Colors.transparent, + // onPressed: () { + // setState(() { + // _selectedIndex = 1; + // }); + // }, // ), // // continue reading screen - // BottomNavigationBarItem( - // icon: Icon(Icons.book), - // label: 'Continue Reading', + // IconButton( + // icon: const Icon(Icons.book), + // color: + // _selectedIndex == 2 ? const Color(0xFFAA5CC3) : Colors.grey, + // iconSize: _selectedIndex == 2 ? 30 : 24, + // splashColor: Colors.transparent, + // onPressed: () { + // setState(() { + // _selectedIndex = 2; + // }); + // }, // ), // // Settings Screen - // BottomNavigationBarItem( - // icon: Icon(Icons.settings), - // label: 'Settings', - // ), + // // IconButton( + // // icon: const Icon(Icons.settings), + // // color: + // // _selectedIndex == 3 ? const Color(0xFFAA5CC3) : Colors.grey, + // // iconSize: _selectedIndex == 3 ? 30 : 24, + // // splashColor: Colors.transparent, + // // onPressed: () { + // // setState(() { + // // _selectedIndex = 3; + // // // go to settings screen + // // }); + // // }, + // // ), // ], // ), - // use a row to make the bottom navigation bar - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - // Home Screen - IconButton( - icon: const Icon(Icons.home), - // make the color a gradient between #AA5CC3 and #00A4DC if selected - color: - _selectedIndex == 0 ? const Color(0xFFAA5CC3) : Colors.grey, - iconSize: _selectedIndex == 0 ? 30 : 24, - splashColor: Colors.transparent, - onPressed: () { - setState(() { - _selectedIndex = 0; - }); - }, - ), - // Search Screen - IconButton( - iconSize: _selectedIndex == 1 ? 30 : 24, - color: - _selectedIndex == 1 ? const Color(0xFFAA5CC3) : Colors.grey, - splashColor: Colors.transparent, - icon: const Icon(Icons.search), - onPressed: () { - setState(() { - _selectedIndex = 1; - }); - }, - ), - // Downloaded Screen - IconButton( - icon: const Icon(Icons.download), - color: - _selectedIndex == 2 ? const Color(0xFFAA5CC3) : Colors.grey, - iconSize: _selectedIndex == 2 ? 30 : 24, - splashColor: Colors.transparent, - onPressed: () { - setState(() { - _selectedIndex = 2; - }); - }, - ), - // continue reading screen - IconButton( - icon: const Icon(Icons.book), - color: - _selectedIndex == 3 ? const Color(0xFFAA5CC3) : Colors.grey, - iconSize: _selectedIndex == 3 ? 30 : 24, - splashColor: Colors.transparent, - onPressed: () { - setState(() { - _selectedIndex = 3; - }); - }, - ), - // Settings Screen - IconButton( - icon: const Icon(Icons.settings), - color: - _selectedIndex == 4 ? const Color(0xFFAA5CC3) : Colors.grey, - iconSize: _selectedIndex == 4 ? 30 : 24, - splashColor: Colors.transparent, - onPressed: () { - setState(() { - _selectedIndex = 4; - // go to settings screen - }); - }, - ), - ], - ), ), ); }