From 7e55e37cbe37f48c17524a5a6d92ae663c9cb460 Mon Sep 17 00:00:00 2001 From: Shubham Sinha Date: Sun, 5 Jun 2022 19:42:02 +0530 Subject: [PATCH] added books --- .env.development | 5 +- lib/app/core/helpers/ad_helper.dart | 12 + lib/app/data/model/book_model.dart | 23 ++ lib/app/data/model/dashboard_model.dart | 7 + lib/app/data/provider/firestore_data.dart | 66 ++--- .../modules/book/bindings/book_binding.dart | 12 + .../book/controllers/book_controller.dart | 229 ++++++++++++++++++ lib/app/modules/book/views/book_view.dart | 105 ++++++++ .../modules/book/views/widgets/btmsheet.dart | 101 ++++++++ .../views/widgets/liquidcustomindicator.dart | 106 ++++++++ lib/app/modules/book/views/widgets/wave.dart | 118 +++++++++ .../content/bindings/content_binding.dart | 12 + .../controllers/content_controller.dart | 52 ++++ .../modules/content/views/content_view.dart | 137 +++++++++++ .../dashboard/bindings/dashboard_binding.dart | 12 + .../controllers/dashboard_controller.dart | 49 ++++ .../dashboard/views/dashboard_view.dart | 114 +++++++++ lib/app/modules/home/views/home_view.dart | 6 + .../subject/bindings/subject_binding.dart | 12 + .../controllers/subject_controller.dart | 50 ++++ .../modules/subject/views/subject_view.dart | 134 ++++++++++ lib/app/routes/app_pages.dart | 26 ++ pubspec.yaml | 2 +- 23 files changed, 1356 insertions(+), 34 deletions(-) create mode 100644 lib/app/data/model/book_model.dart create mode 100644 lib/app/data/model/dashboard_model.dart create mode 100644 lib/app/modules/book/bindings/book_binding.dart create mode 100644 lib/app/modules/book/controllers/book_controller.dart create mode 100644 lib/app/modules/book/views/book_view.dart create mode 100644 lib/app/modules/book/views/widgets/btmsheet.dart create mode 100644 lib/app/modules/book/views/widgets/liquidcustomindicator.dart create mode 100644 lib/app/modules/book/views/widgets/wave.dart create mode 100644 lib/app/modules/content/bindings/content_binding.dart create mode 100644 lib/app/modules/content/controllers/content_controller.dart create mode 100644 lib/app/modules/content/views/content_view.dart create mode 100644 lib/app/modules/dashboard/bindings/dashboard_binding.dart create mode 100644 lib/app/modules/dashboard/controllers/dashboard_controller.dart create mode 100644 lib/app/modules/dashboard/views/dashboard_view.dart create mode 100644 lib/app/modules/subject/bindings/subject_binding.dart create mode 100644 lib/app/modules/subject/controllers/subject_controller.dart create mode 100644 lib/app/modules/subject/views/subject_view.dart diff --git a/.env.development b/.env.development index 91068d2..0f5aa2b 100644 --- a/.env.development +++ b/.env.development @@ -6,4 +6,7 @@ libraryBanner=ca-app-pub-3940256099942544/6300978111 encryptedBanner=ca-app-pub-3940256099942544/6300978111 bottomBanner=ca-app-pub-3940256099942544/6300978111 settingsBanner=ca-app-pub-3940256099942544/6300978111 -viewPdfBanner=ca-app-pub-3940256099942544/2934735716 \ No newline at end of file +viewPdfBanner=ca-app-pub-3940256099942544/2934735716 +bookBanner=ca-app-pub-3940256099942544/6300978111 +bookBottom=ca-app-pub-3940256099942544/6300978111 +viewBookPdf=ca-app-pub-3940256099942544/1033173712 \ No newline at end of file diff --git a/lib/app/core/helpers/ad_helper.dart b/lib/app/core/helpers/ad_helper.dart index a1c272f..464c7ad 100644 --- a/lib/app/core/helpers/ad_helper.dart +++ b/lib/app/core/helpers/ad_helper.dart @@ -40,4 +40,16 @@ class AdHelper { static String get viewPdfBanner { return dotenv.get('viewPdfBanner'); } + + static String get bookBanner { + return dotenv.get('bookBanner'); + } + + static String get viewBookPdf { + return dotenv.get('viewBookPdf'); + } + + static String get bookBottom { + return dotenv.get('bookBottom'); + } } diff --git a/lib/app/data/model/book_model.dart b/lib/app/data/model/book_model.dart new file mode 100644 index 0000000..be3169e --- /dev/null +++ b/lib/app/data/model/book_model.dart @@ -0,0 +1,23 @@ +class Book { + final String bookName; + final int classNumber; + // final String thumbnailLink; + // final List telegramLink; + // final List gdriveLink; + // final int views; + final List chapterNames; + final List ncertDirectLinks; + // final String amazonBuyLink; + + Book({ + required this.bookName, + required this.classNumber, + //required this.thumbnailLink, + // required this.telegramLink, + // required this.gdriveLink, + // required this.views, + required this.chapterNames, + required this.ncertDirectLinks, + // required this.amazonBuyLink, + }); +} diff --git a/lib/app/data/model/dashboard_model.dart b/lib/app/data/model/dashboard_model.dart new file mode 100644 index 0000000..b1011d9 --- /dev/null +++ b/lib/app/data/model/dashboard_model.dart @@ -0,0 +1,7 @@ +class DashboardModel { + final List dashboardList; + + DashboardModel({ + required this.dashboardList, + }); +} diff --git a/lib/app/data/provider/firestore_data.dart b/lib/app/data/provider/firestore_data.dart index b84894e..f8e2467 100644 --- a/lib/app/data/provider/firestore_data.dart +++ b/lib/app/data/provider/firestore_data.dart @@ -2,6 +2,8 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:filegram/app/data/model/views_model.dart'; import '../enums/docpermission.dart'; import 'package:flutter/services.dart'; +import '../model/book_model.dart'; +import '../model/dashboard_model.dart'; import '../model/documents_model.dart'; import '../model/gullak_model.dart'; import '../model/user_model.dart'; @@ -169,24 +171,24 @@ class FirestoreData { } } - // static Future getDashboard({ - // required String collection, - // required String uid, - // required String listName, - // }) async { - // try { - // DocumentSnapshot _doc = - // await _firestore.collection(collection).doc(uid).get(); + static Future getDashboard({ + required String collection, + required String uid, + required String listName, + }) async { + try { + DocumentSnapshot _doc = + await _firestore.collection(collection).doc(uid).get(); - // Map _data = _doc.data() as Map; + Map _data = _doc.data() as Map; - // return DashboardModel( - // dashboardList: List.from(_data[listName]), - // ); - // } catch (e) { - // rethrow; - // } - // } + return DashboardModel( + dashboardList: List.from(_data[listName]), + ); + } catch (e) { + rethrow; + } + } static Future updatePhoneUser( {required String id, required String phoneNumber}) async { @@ -340,24 +342,24 @@ class FirestoreData { } } - // static Future getBook(String uid) async { - // try { - // DocumentSnapshot _doc = - // await _firestore.collection("books").doc(uid).get(); + static Future getBook(String uid) async { + try { + DocumentSnapshot _doc = + await _firestore.collection("books").doc(uid).get(); - // Map _data = _doc.data() as Map; + Map _data = _doc.data() as Map; - // return Book( - // bookName: _data["bookName"] as String, - // chapterNames: List.from(_data["chapterNames"]), - // ncertDirectLinks: List.from(_data["ncertLinks"]), - // classNumber: _data["classNumber"] as int, - // // gdriveLink: - // ); - // } catch (e) { - // rethrow; - // } - // } + return Book( + bookName: _data["bookName"] as String, + chapterNames: List.from(_data["chapterNames"]), + ncertDirectLinks: List.from(_data["ncertLinks"]), + classNumber: _data["classNumber"] as int, + // gdriveLink: + ); + } catch (e) { + rethrow; + } + } // Future getUser(String uid) async { // try { // DocumentSnapshot _doc = await _firestore diff --git a/lib/app/modules/book/bindings/book_binding.dart b/lib/app/modules/book/bindings/book_binding.dart new file mode 100644 index 0000000..7ca08ad --- /dev/null +++ b/lib/app/modules/book/bindings/book_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/book_controller.dart'; + +class BookBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => BookController(), + ); + } +} diff --git a/lib/app/modules/book/controllers/book_controller.dart b/lib/app/modules/book/controllers/book_controller.dart new file mode 100644 index 0000000..647da3f --- /dev/null +++ b/lib/app/modules/book/controllers/book_controller.dart @@ -0,0 +1,229 @@ +import 'dart:io'; + +import 'package:filegram/app/core/extensions.dart'; +import 'package:get/get.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; +import '../../../core/helpers/ad_helper.dart'; +import '../../../data/model/book_model.dart'; +import '../../../data/provider/firestore_data.dart'; +import '../../home/controllers/home_controller.dart'; + +class BookController extends GetxController { + final homeController = Get.find(); + final isLoding = true.obs; + Book book = Book( + bookName: '', classNumber: 0, chapterNames: [], ncertDirectLinks: []); + final total = 1.obs; + final received = 0.obs; + late http.StreamedResponse response; + final List _bytes = []; + InterstitialAd? interstitialAd; + final int maxFailedLoadAttempts = 3; + int interstitialLoadAttempts = 0; + final adDismissed = false.obs; + final inlineAdIndex = 0; + late BannerAd inlineBannerAd; + final isInlineBannerAdLoaded = false.obs; + final isBottomBannerAdLoaded = false.obs; + late BannerAd bottomBannerAd; + String get uid => + ((Get.arguments[0] as String) + (Get.arguments[1] as String)).sort; + + Future getdetails(String url) async { + try { + response = await http.Client().send(http.Request('GET', Uri.parse(url))); + total.value = response.contentLength ?? 0; + received.value = 0; + } catch (e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + } + } + + Future downloadFile(String filePath) async { + final _file = File(filePath); + try { + response.stream.listen((value) { + _bytes.addAll(value); + received.value += value.length; + }).onDone(() async { + await _file.writeAsBytes(_bytes); + }); + } catch (e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + } + } + + void _createBottomBannerAd() { + bottomBannerAd = BannerAd( + adUnitId: AdHelper.bookBottom, + size: AdSize.banner, + request: const AdRequest(), + listener: BannerAdListener( + onAdLoaded: (_) { + isBottomBannerAdLoaded.value = true; + }, + onAdFailedToLoad: (ad, error) { + ad.dispose(); + }, + ), + ); + if (isBottomBannerAdLoaded.isFalse) { + bottomBannerAd.load(); + } + } + + Future createInterstitialAd() async { + try { + await InterstitialAd.load( + adUnitId: AdHelper.viewBookPdf, + request: const AdRequest(), + adLoadCallback: InterstitialAdLoadCallback( + onAdLoaded: (InterstitialAd ad) { + interstitialAd = ad; + interstitialLoadAttempts = 0; + }, + onAdFailedToLoad: (LoadAdError error) { + interstitialLoadAttempts += 1; + interstitialAd = null; + if (interstitialLoadAttempts <= maxFailedLoadAttempts) { + createInterstitialAd(); + } + }, + ), + ); + } on Exception catch (e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + } + } + + AdWidget adWidget({required AdWithView ad}) { + return AdWidget(ad: ad); + } + + Future showInterstitialAd({String? uid}) async { + try { + if (interstitialAd != null) { + interstitialAd!.fullScreenContentCallback = FullScreenContentCallback( + onAdDismissedFullScreenContent: (InterstitialAd ad) { + ad.dispose(); + adDismissed.value = true; + createInterstitialAd(); + }, + onAdFailedToShowFullScreenContent: + (InterstitialAd ad, AdError error) { + ad.dispose(); + createInterstitialAd(); + }, + onAdShowedFullScreenContent: (InterstitialAd ad) {}); + if (interstitialAd != null) { + interstitialAd!.show(); + } + } + } on Exception catch (e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + } + } + + int getListViewItemIndex(int index) { + if (index >= inlineAdIndex && + isInlineBannerAdLoaded.isTrue && + (book.chapterNames.length >= inlineAdIndex)) { + return index - 1; + } + return index; + } + + void _createInlineBannerAd() { + inlineBannerAd = BannerAd( + size: AdSize.mediumRectangle, + adUnitId: AdHelper.bookBanner, + request: const AdRequest(), + listener: BannerAdListener( + onAdLoaded: (_) { + isInlineBannerAdLoaded.value = true; + }, + onAdFailedToLoad: (ad, error) { + ad.dispose(); + }, + ), + ); + + inlineBannerAd.load(); + } + + Future filesDocDir() async { + //Get this App Document Directory + //App Document Directory + folder name + + final Directory? _appDocDir = await getExternalStorageDirectory(); + //App Document Directory + folder name + final Directory _appDocDirFolder = + Directory('${_appDocDir?.path}/Downloads'); + + if (await _appDocDirFolder.exists()) { + //if folder already exists return path + return _appDocDirFolder.path; + } else { + //if folder not exists create folder and then return its path + final Directory _appDocDirNewFolder = + await _appDocDirFolder.create(recursive: true); + return _appDocDirNewFolder.path; + } + } + + @override + void onInit() { + FirestoreData.getBook(uid) + .then((value) { + book = value; + }) + .whenComplete(() => isLoding.value = false) + .catchError((e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + }); + if (isInlineBannerAdLoaded.isFalse) { + _createInlineBannerAd(); + } + createInterstitialAd(); + + _createBottomBannerAd(); + + super.onInit(); + } + + @override + void onClose() { + interstitialAd?.dispose(); + inlineBannerAd.dispose(); + bottomBannerAd.dispose(); + + super.onClose(); + } +} diff --git a/lib/app/modules/book/views/book_view.dart b/lib/app/modules/book/views/book_view.dart new file mode 100644 index 0000000..55d0dcd --- /dev/null +++ b/lib/app/modules/book/views/book_view.dart @@ -0,0 +1,105 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +import 'package:get/get.dart'; + +import '../../../routes/app_pages.dart'; +import '../controllers/book_controller.dart'; +import 'widgets/btmsheet.dart'; + +class BookView extends GetView { + const BookView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Obx( + () => controller.isLoding.isFalse + ? Text( + '${controller.book.bookName} ${controller.book.classNumber}') + : const Text('Loading Book Name ...'), + ), + actions: [ + IconButton( + onPressed: () { + Get.changeThemeMode( + Get.isDarkMode ? ThemeMode.light : ThemeMode.dark); + controller.homeController.changeTheme.toggle(); + }, + icon: Icon( + controller.homeController.changeTheme.isTrue + ? Icons.light_mode + : Icons.dark_mode, + )), + ], + ), + bottomNavigationBar: Obx(() => controller.isBottomBannerAdLoaded.isTrue + ? SizedBox( + height: controller.bottomBannerAd.size.height.toDouble(), + width: controller.bottomBannerAd.size.width.toDouble(), + child: controller.adWidget(ad: controller.bottomBannerAd), + ) + : const SizedBox( + height: 0, + width: 0, + )), + body: Obx(() => controller.isLoding.isFalse + ? ListView.builder( + itemCount: controller.book.chapterNames.length + + (controller.isInlineBannerAdLoaded.isTrue && + (controller.book.chapterNames.length >= + controller.inlineAdIndex) + ? 1 + : 0), + itemBuilder: (context, index) { + if (controller.isInlineBannerAdLoaded.isTrue && + index == controller.inlineAdIndex && + (controller.book.chapterNames.length >= + controller.inlineAdIndex)) { + return Container( + padding: const EdgeInsets.only( + bottom: 10, + ), + width: controller.inlineBannerAd.size.width.toDouble(), + height: controller.inlineBannerAd.size.height.toDouble(), + child: controller.adWidget(ad: controller.inlineBannerAd), + ); + } else { + return ListTile( + title: Text(controller.book + .chapterNames[controller.getListViewItemIndex(index)]), + + // leading: Text('${index + 1}'), + onTap: () async { + String _bookPath = + '${await controller.filesDocDir()}/${controller.book.ncertDirectLinks[controller.getListViewItemIndex(index)]}.pdf'; + if (await File(_bookPath).exists()) { + controller.showInterstitialAd().catchError((e) {}); + Get.toNamed(Routes.viewPdf, + arguments: [_bookPath, false]); + } else { + controller + .getdetails( + 'https://ncert.nic.in/textbook/pdf/${controller.book.ncertDirectLinks[controller.getListViewItemIndex(index)]}.pdf') + .then((value) => Get.bottomSheet( + DownloadBtmSheet( + controller: controller, + filePath: _bookPath, + index: index, + ), + isScrollControlled: true, + )); + } + }, + ); + } + }, + ) + : const Center( + child: CircularProgressIndicator(), + )), + ); + } +} diff --git a/lib/app/modules/book/views/widgets/btmsheet.dart b/lib/app/modules/book/views/widgets/btmsheet.dart new file mode 100644 index 0000000..bd81614 --- /dev/null +++ b/lib/app/modules/book/views/widgets/btmsheet.dart @@ -0,0 +1,101 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../routes/app_pages.dart'; + +import '../../controllers/book_controller.dart'; +import 'liquidcustomindicator.dart'; + +class DownloadBtmSheet extends StatelessWidget { + const DownloadBtmSheet({ + Key? key, + required this.controller, + required this.filePath, + required this.index, + }) : super(key: key); + + final BookController controller; + final String filePath; + final int index; + + @override + Widget build(BuildContext context) { + return Obx(() => AnimatedContainer( + duration: const Duration(milliseconds: 100), + color: Get.isDarkMode ? Colors.black : Colors.white, + padding: const EdgeInsets.all(16), + child: Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + LiquidCustomProgressIndicator( + value: controller.received.value / controller.total.value, + direction: Axis.vertical, + backgroundColor: Colors.amber, + valueColor: const AlwaysStoppedAnimation(Colors.red), + shapePath: _buildHeartPath(), + center: Text( + '${(controller.received.value / controller.total.value * 100).toStringAsFixed(0)} %', + style: const TextStyle( + color: Colors.black, + fontSize: 20.0, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox( + height: 30, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Get.back(), child: const Text('Back')), + TextButton.icon( + onPressed: () async { + await controller.downloadFile(filePath); + }, + icon: const Icon(Icons.file_download), + label: Text((controller.received.value / 1048576) + .toStringAsFixed(1) + + '/' + + (controller.total.value / 1048576).toStringAsFixed(1) + + ' MB'), + ), + File(filePath).existsSync() + ? TextButton.icon( + onPressed: () { + Get.back(); + Get.toNamed( + Routes.viewPdf, + arguments: [filePath, true], + ); + }, + icon: const Icon(Icons.folder_open), + label: const Text('Open File'), + ) + : const SizedBox( + height: 0, + width: 0, + ), + ], + ), + ], + ), + )); + } +} + +Path _buildHeartPath() { + return Path() + ..moveTo(55, 15) + ..cubicTo(55, 12, 50, 0, 30, 0) + ..cubicTo(0, 0, 0, 37.5, 0, 37.5) + ..cubicTo(0, 55, 20, 77, 55, 95) + ..cubicTo(90, 77, 110, 55, 110, 37.5) + ..cubicTo(110, 37.5, 110, 0, 80, 0) + ..cubicTo(65, 0, 55, 12, 55, 15) + ..close(); +} diff --git a/lib/app/modules/book/views/widgets/liquidcustomindicator.dart b/lib/app/modules/book/views/widgets/liquidcustomindicator.dart new file mode 100644 index 0000000..0ee5f4d --- /dev/null +++ b/lib/app/modules/book/views/widgets/liquidcustomindicator.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; + +import 'wave.dart'; + +class LiquidCustomProgressIndicator extends ProgressIndicator { + ///The widget to show in the center of the progress indicator. + final Widget? center; + + ///The direction the liquid travels. + final Axis direction; + + ///The path used to draw the shape of the progress indicator. The size of the progress indicator is controlled by the bounds of this path. + final Path shapePath; + + const LiquidCustomProgressIndicator({ + Key? key, + double value = 0.5, + Color? backgroundColor, + Animation? valueColor, + this.center, + required this.direction, + required this.shapePath, + }) : super( + key: key, + value: value, + backgroundColor: backgroundColor, + valueColor: valueColor, + ); + + Color _getBackgroundColor(BuildContext context) => + backgroundColor ?? Theme.of(context).backgroundColor; + + Color _getValueColor(BuildContext context) => + valueColor?.value ?? Theme.of(context).canvasColor; + + @override + State createState() => _LiquidCustomProgressIndicatorState(); +} + +class _LiquidCustomProgressIndicatorState + extends State { + @override + Widget build(BuildContext context) { + final pathBounds = widget.shapePath.getBounds(); + return SizedBox( + width: pathBounds.width + pathBounds.left, + height: pathBounds.height + pathBounds.top, + child: ClipPath( + clipper: _CustomPathClipper( + path: widget.shapePath, + ), + child: CustomPaint( + painter: _CustomPathPainter( + color: widget._getBackgroundColor(context), + path: widget.shapePath, + ), + child: Stack( + children: [ + Positioned.fill( + left: pathBounds.left, + top: pathBounds.top, + child: Wave( + value: widget.value, + color: widget._getValueColor(context), + direction: widget.direction, + ), + ), + if (widget.center != null) Center(child: widget.center), + ], + ), + ), + ), + ); + } +} + +class _CustomPathPainter extends CustomPainter { + final Color color; + final Path path; + + _CustomPathPainter({required this.color, required this.path}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..color = color; + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(_CustomPathPainter oldDelegate) => + color != oldDelegate.color || path != oldDelegate.path; +} + +class _CustomPathClipper extends CustomClipper { + final Path path; + + _CustomPathClipper({required this.path}); + + @override + Path getClip(Size size) { + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} diff --git a/lib/app/modules/book/views/widgets/wave.dart b/lib/app/modules/book/views/widgets/wave.dart new file mode 100644 index 0000000..a429883 --- /dev/null +++ b/lib/app/modules/book/views/widgets/wave.dart @@ -0,0 +1,118 @@ +import 'dart:math' as math; +import 'package:flutter/material.dart'; + +class Wave extends StatefulWidget { + final double? value; + final Color color; + final Axis direction; + + const Wave({ + Key? key, + required this.value, + required this.color, + required this.direction, + }) : super(key: key); + + @override + _WaveState createState() => _WaveState(); +} + +class _WaveState extends State with SingleTickerProviderStateMixin { + late AnimationController _animationController; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController( + vsync: this, + duration: const Duration(seconds: 2), + ); + _animationController.repeat(); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: CurvedAnimation( + parent: _animationController, + curve: Curves.easeInOut, + ), + builder: (context, child) => ClipPath( + child: Container( + color: widget.color, + ), + clipper: _WaveClipper( + animationValue: _animationController.value, + value: widget.value, + direction: widget.direction, + ), + ), + ); + } +} + +class _WaveClipper extends CustomClipper { + final double animationValue; + final double? value; + final Axis direction; + + _WaveClipper({ + required this.animationValue, + required this.value, + required this.direction, + }); + + @override + Path getClip(Size size) { + if (direction == Axis.horizontal) { + Path path = Path() + ..addPolygon(_generateHorizontalWavePath(size), false) + ..lineTo(0.0, size.height) + ..lineTo(0.0, 0.0) + ..close(); + return path; + } + + Path path = Path() + ..addPolygon(_generateVerticalWavePath(size), false) + ..lineTo(size.width, size.height) + ..lineTo(0.0, size.height) + ..close(); + return path; + } + + List _generateHorizontalWavePath(Size size) { + final waveList = []; + for (int i = -2; i <= size.height.toInt() + 2; i++) { + final waveHeight = (size.width / 20); + final dx = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * + waveHeight + + (size.width * value!); + waveList.add(Offset(dx, i.toDouble())); + } + return waveList; + } + + List _generateVerticalWavePath(Size size) { + final waveList = []; + for (int i = -2; i <= size.width.toInt() + 2; i++) { + final waveHeight = (size.height / 20); + final dy = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * + waveHeight + + (size.height - (size.height * value!)); + waveList.add(Offset(i.toDouble(), dy)); + } + return waveList; + } + + @override + bool shouldReclip(_WaveClipper oldClipper) => + animationValue != oldClipper.animationValue; +} diff --git a/lib/app/modules/content/bindings/content_binding.dart b/lib/app/modules/content/bindings/content_binding.dart new file mode 100644 index 0000000..4f62986 --- /dev/null +++ b/lib/app/modules/content/bindings/content_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/content_controller.dart'; + +class ContentBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => ContentController(), + ); + } +} diff --git a/lib/app/modules/content/controllers/content_controller.dart b/lib/app/modules/content/controllers/content_controller.dart new file mode 100644 index 0000000..0d8ac51 --- /dev/null +++ b/lib/app/modules/content/controllers/content_controller.dart @@ -0,0 +1,52 @@ +import 'package:filegram/app/core/extensions.dart'; +import 'package:get/get.dart'; +import '../../../data/model/dashboard_model.dart'; +import '../../../data/provider/firestore_data.dart'; +import '../../home/controllers/home_controller.dart'; + +class ContentController extends GetxController { + final dashboard = DashboardModel(dashboardList: []).obs; + final dashboardList = [].obs; + final isLoading = true.obs; + final homeController = Get.find(); + String get uid => + ((Get.arguments[0] as String) + (Get.arguments[1] as String)).sort; + + void filterfileList(String fileName) { + if (fileName.isEmpty) { + dashboardList.assignAll(dashboard.value.dashboardList.reversed); + } else { + dashboardList.assignAll(dashboard.value.dashboardList.reversed + .where((String element) => + element.toLowerCase().contains(fileName.toLowerCase())) + .toList()); + } + } + + void onInitialisation() { + // dashboardList.assignAll(ContentData.contentList); + FirestoreData.getDashboard( + collection: 'subjects', + listName: 'subjectList', + uid: uid, + ).then((value) { + dashboard(value); + dashboardList.assignAll(dashboard.value.dashboardList.reversed); + }).whenComplete(() { + isLoading.value = false; + }).catchError((e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + }); + } + + @override + void onInit() { + onInitialisation(); + super.onInit(); + } +} diff --git a/lib/app/modules/content/views/content_view.dart b/lib/app/modules/content/views/content_view.dart new file mode 100644 index 0000000..f8721a4 --- /dev/null +++ b/lib/app/modules/content/views/content_view.dart @@ -0,0 +1,137 @@ +import 'package:filegram/app/routes/app_pages.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../controllers/content_controller.dart'; + +class ContentView extends GetView { + const ContentView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('${Get.arguments[1]} Books'), + actions: [ + IconButton( + onPressed: () { + Get.changeThemeMode( + Get.isDarkMode ? ThemeMode.light : ThemeMode.dark); + controller.homeController.changeTheme.toggle(); + }, + icon: Icon( + controller.homeController.changeTheme.isTrue + ? Icons.light_mode + : Icons.dark_mode, + )), + ], + ), + body: RefreshIndicator( + triggerMode: RefreshIndicatorTriggerMode.onEdge, + backgroundColor: Colors.white, + color: Colors.black87, + strokeWidth: 4, + displacement: 150, + edgeOffset: 0, + onRefresh: () async { + controller.onInitialisation(); + }, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + maxLines: 1, + onChanged: (value) => controller.filterfileList(value), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.search), + hintText: 'Search By Book', + isDense: true, + ), + ), + ), + Expanded( + child: Obx( + () => controller.isLoading.isFalse + ? GridView.builder( + gridDelegate: + const SliverGridDelegateWithMaxCrossAxisExtent( + childAspectRatio: 2, maxCrossAxisExtent: 250), + itemCount: controller.dashboardList.length, + itemBuilder: (context, index) => Obx( + () => GestureDetector( + onTap: () { + Get.toNamed(Routes.book, arguments: [ + controller.uid, + controller.dashboardList[index] + ]); + }, + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + margin: const EdgeInsets.all(10), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + controller + .homeController.changeTheme.isTrue + ? Colors.black54 + : Colors.white70, + controller + .homeController.changeTheme.isTrue + ? Colors.black87 + : Colors.white, + ], + ), + //color: Colors.black87, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: controller + .homeController.changeTheme.isTrue + ? Colors.grey.shade900 + : Colors.grey.shade400, + offset: const Offset(5, 5), + blurRadius: 5, + spreadRadius: 1, + ), + BoxShadow( + color: controller + .homeController.changeTheme.isTrue + ? Colors.grey.shade800 + : Colors.grey.shade300, + offset: const Offset(-4, -4), + blurRadius: 5, + spreadRadius: 1, + ) + ]), + child: Text( + controller.dashboardList[index], + textScaleFactor: 1.3, + textAlign: TextAlign.center, + style: const TextStyle( + fontWeight: FontWeight.w800, + ), + ), + ), + ), + ), + ) + : const Center( + child: CircularProgressIndicator(), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/modules/dashboard/bindings/dashboard_binding.dart b/lib/app/modules/dashboard/bindings/dashboard_binding.dart new file mode 100644 index 0000000..da48f13 --- /dev/null +++ b/lib/app/modules/dashboard/bindings/dashboard_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/dashboard_controller.dart'; + +class DashboardBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => DashboardController(), + ); + } +} diff --git a/lib/app/modules/dashboard/controllers/dashboard_controller.dart b/lib/app/modules/dashboard/controllers/dashboard_controller.dart new file mode 100644 index 0000000..85229f0 --- /dev/null +++ b/lib/app/modules/dashboard/controllers/dashboard_controller.dart @@ -0,0 +1,49 @@ +import 'package:get/get.dart'; + +import '../../../data/model/dashboard_model.dart'; +import '../../../data/provider/firestore_data.dart'; +import '../../home/controllers/home_controller.dart'; + +class DashboardController extends GetxController { + final isLoading = true.obs; + final dashboard = DashboardModel(dashboardList: []).obs; + final dashboardList = [].obs; + final homeController = Get.find(); + void filterfileList(String fileName) { + if (fileName.isEmpty) { + dashboardList.assignAll(dashboard.value.dashboardList.reversed); + } else { + dashboardList.assignAll(dashboard.value.dashboardList.reversed + .where((String element) => + element.toLowerCase().contains(fileName.toLowerCase())) + .toList()); + } + } + + void onInitialisation() { + // dashboardList.assignAll(DashboardData.dashboardList); + FirestoreData.getDashboard( + collection: 'dashboard', + listName: 'dashboardList', + uid: 'dashboard', + ).then((value) { + dashboard(value); + dashboardList.assignAll(dashboard.value.dashboardList.reversed); + }).whenComplete(() { + isLoading.value = false; + }).catchError((e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + }); + } + + @override + void onInit() { + onInitialisation(); + super.onInit(); + } +} diff --git a/lib/app/modules/dashboard/views/dashboard_view.dart b/lib/app/modules/dashboard/views/dashboard_view.dart new file mode 100644 index 0000000..633b1b9 --- /dev/null +++ b/lib/app/modules/dashboard/views/dashboard_view.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; + +import 'package:get/get.dart'; +import '../../../routes/app_pages.dart'; +import '../controllers/dashboard_controller.dart'; + +class DashboardView extends GetView { + const DashboardView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + triggerMode: RefreshIndicatorTriggerMode.onEdge, + backgroundColor: Colors.white, + color: Colors.black87, + strokeWidth: 4, + displacement: 150, + edgeOffset: 0, + onRefresh: () async { + controller.onInitialisation(); + }, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + maxLines: 1, + onChanged: (value) => controller.filterfileList(value), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.search), + hintText: 'Search By Class', + isDense: true, + ), + ), + ), + Expanded( + child: Obx( + () => controller.isLoading.isFalse + ? GridView.builder( + gridDelegate: + const SliverGridDelegateWithMaxCrossAxisExtent( + childAspectRatio: 2, maxCrossAxisExtent: 250), + itemCount: controller.dashboardList.length, + itemBuilder: (context, index) => Obx( + () => GestureDetector( + onTap: () => Get.toNamed(Routes.subject, + arguments: controller.dashboardList[index]), + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + margin: const EdgeInsets.all(10), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + controller + .homeController.changeTheme.isTrue + ? Colors.black54 + : Colors.white70, + controller + .homeController.changeTheme.isTrue + ? Colors.black87 + : Colors.white, + ], + ), + //color: Colors.black87, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: controller + .homeController.changeTheme.isTrue + ? Colors.grey.shade900 + : Colors.grey.shade400, + offset: const Offset(5, 5), + blurRadius: 5, + spreadRadius: 1, + ), + BoxShadow( + color: controller + .homeController.changeTheme.isTrue + ? Colors.grey.shade800 + : Colors.grey.shade300, + offset: const Offset(-4, -4), + blurRadius: 5, + spreadRadius: 1, + ) + ]), + child: Text( + controller.dashboardList[index], + textScaleFactor: 1.5, + textAlign: TextAlign.center, + style: const TextStyle( + fontWeight: FontWeight.w800, + ), + )), + ), + ), + ) + : const Center( + child: CircularProgressIndicator(), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/app/modules/home/views/home_view.dart b/lib/app/modules/home/views/home_view.dart index 0f76b18..af256a0 100644 --- a/lib/app/modules/home/views/home_view.dart +++ b/lib/app/modules/home/views/home_view.dart @@ -1,3 +1,4 @@ +import 'package:filegram/app/modules/dashboard/views/dashboard_view.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -16,6 +17,7 @@ class HomeView extends GetView { @override Widget build(BuildContext context) { List bodyPages = [ + const DashboardView(), const FilesDeviceView(), const EncryptedFileListView(), const SettingsView(), @@ -81,6 +83,10 @@ class HomeView extends GetView { FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: NavigationBar( destinations: const [ + NavigationDestination( + icon: Icon(Icons.dashboard), + label: 'Dashboard', + ), NavigationDestination( icon: Icon(Icons.library_books), label: 'Library', diff --git a/lib/app/modules/subject/bindings/subject_binding.dart b/lib/app/modules/subject/bindings/subject_binding.dart new file mode 100644 index 0000000..c9d7705 --- /dev/null +++ b/lib/app/modules/subject/bindings/subject_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/subject_controller.dart'; + +class SubjectBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => SubjectController(), + ); + } +} diff --git a/lib/app/modules/subject/controllers/subject_controller.dart b/lib/app/modules/subject/controllers/subject_controller.dart new file mode 100644 index 0000000..505354c --- /dev/null +++ b/lib/app/modules/subject/controllers/subject_controller.dart @@ -0,0 +1,50 @@ +import 'package:filegram/app/core/extensions.dart'; +import 'package:get/get.dart'; +import '../../../data/model/dashboard_model.dart'; +import '../../../data/provider/firestore_data.dart'; +import '../../home/controllers/home_controller.dart'; + +class SubjectController extends GetxController { + final dashboard = DashboardModel(dashboardList: []).obs; + final dashboardList = [].obs; + final homeController = Get.find(); + final isLoading = true.obs; + + void filterfileList(String fileName) { + if (fileName.isEmpty) { + dashboardList.assignAll(dashboard.value.dashboardList.reversed); + } else { + dashboardList.assignAll(dashboard.value.dashboardList.reversed + .where((String element) => + element.toLowerCase().contains(fileName.toLowerCase())) + .toList()); + } + } + + void onInitialisation() { + // dashboardList.assignAll(SubjectData.subjectList); + FirestoreData.getDashboard( + collection: 'classes', + listName: 'classList', + uid: (Get.arguments as String).sort) + .then((value) { + dashboard(value); + dashboardList.assignAll(dashboard.value.dashboardList.reversed); + }).whenComplete(() { + isLoading.value = false; + }).catchError((e) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Get.theme.snackBarTheme.backgroundColor!, + title: 'Error', + message: e.toString(), + duration: const Duration(seconds: 5), + )); + }); + } + + @override + void onInit() { + onInitialisation(); + super.onInit(); + } +} diff --git a/lib/app/modules/subject/views/subject_view.dart b/lib/app/modules/subject/views/subject_view.dart new file mode 100644 index 0000000..5faa876 --- /dev/null +++ b/lib/app/modules/subject/views/subject_view.dart @@ -0,0 +1,134 @@ +import 'package:filegram/app/routes/app_pages.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../controllers/subject_controller.dart'; + +class SubjectView extends GetView { + const SubjectView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('${Get.arguments} Subjects'), + actions: [ + IconButton( + onPressed: () { + Get.changeThemeMode( + Get.isDarkMode ? ThemeMode.light : ThemeMode.dark); + controller.homeController.changeTheme.toggle(); + }, + icon: Icon( + controller.homeController.changeTheme.isTrue + ? Icons.light_mode + : Icons.dark_mode, + )), + ], + ), + body: RefreshIndicator( + triggerMode: RefreshIndicatorTriggerMode.onEdge, + backgroundColor: Colors.white, + color: Colors.black87, + strokeWidth: 4, + displacement: 150, + edgeOffset: 0, + onRefresh: () async { + controller.onInitialisation(); + }, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + maxLines: 1, + onChanged: (value) => controller.filterfileList(value), + decoration: const InputDecoration( + prefixIcon: Icon(Icons.search), + hintText: 'Search By Subject', + isDense: true, + ), + ), + ), + Expanded( + child: Obx( + () => controller.isLoading.isFalse + ? GridView.builder( + gridDelegate: + const SliverGridDelegateWithMaxCrossAxisExtent( + childAspectRatio: 2, maxCrossAxisExtent: 250), + itemCount: controller.dashboardList.length, + itemBuilder: (context, index) => Obx( + () => GestureDetector( + onTap: () => Get.toNamed(Routes.content, + arguments: [ + Get.arguments, + controller.dashboardList[index] + ]), + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + margin: const EdgeInsets.all(10), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + controller.homeController.changeTheme + .isTrue + ? Colors.black54 + : Colors.white70, + controller.homeController.changeTheme + .isTrue + ? Colors.black87 + : Colors.white, + ], + ), + //color: Colors.black87, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: controller.homeController + .changeTheme.isTrue + ? Colors.grey.shade900 + : Colors.grey.shade400, + offset: const Offset(5, 5), + blurRadius: 5, + spreadRadius: 1, + ), + BoxShadow( + color: controller.homeController + .changeTheme.isTrue + ? Colors.grey.shade800 + : Colors.grey.shade300, + offset: const Offset(-4, -4), + blurRadius: 5, + spreadRadius: 1, + ) + ]), + child: Text( + controller.dashboardList[index], + textScaleFactor: 1.5, + textAlign: TextAlign.center, + style: const TextStyle( + fontWeight: FontWeight.w800, + ), + )), + ), + ), + ) + : const Center( + child: CircularProgressIndicator(), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index fb666bd..22e33a7 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,5 +1,10 @@ +import 'package:filegram/app/modules/dashboard/bindings/dashboard_binding.dart'; import 'package:get/get.dart'; +import '../modules/book/bindings/book_binding.dart'; +import '../modules/book/views/book_view.dart'; +import '../modules/content/bindings/content_binding.dart'; +import '../modules/content/views/content_view.dart'; import '../modules/encrypt_decrypt/bindings/encrypt_decrypt_binding.dart'; import '../modules/encrypted_file_list/bindings/encrypted_file_list_binding.dart'; import '../modules/files_device/bindings/files_device_binding.dart'; @@ -14,6 +19,8 @@ import '../modules/login/views/login_view.dart'; import '../modules/no_internet/bindings/no_internet_binding.dart'; import '../modules/settings/bindings/settings_binding.dart'; +import '../modules/subject/bindings/subject_binding.dart'; +import '../modules/subject/views/subject_view.dart'; import '../modules/updatePhoneNumber/bindings/update_phone_number_binding.dart'; import '../modules/updatePhoneNumber/views/update_phone_number_view.dart'; import '../modules/view_pdf/bindings/view_pdf_binding.dart'; @@ -37,6 +44,7 @@ class AppPages { HomeBinding(), EncryptDecryptBinding(), SettingsBinding(), + DashboardBinding(), EncryptedFileListBinding(), NoInternetBinding(), FilesDeviceBinding(), @@ -76,5 +84,23 @@ class AppPages { binding: UpdatePhoneNumberBinding(), transition: Transition.rightToLeftWithFade, ), + GetPage( + name: _Paths.content, + page: () => const ContentView(), + binding: ContentBinding(), + transition: Transition.rightToLeftWithFade, + ), + GetPage( + name: _Paths.subject, + page: () => const SubjectView(), + binding: SubjectBinding(), + transition: Transition.rightToLeftWithFade, + ), + GetPage( + name: _Paths.book, + page: () => const BookView(), + binding: BookBinding(), + transition: Transition.rightToLeftWithFade, + ), ]; } diff --git a/pubspec.yaml b/pubspec.yaml index f6b83df..efaacf2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: filegram -version: 2.4.0+75 +version: 2.5.0+76 publish_to: none description: A new Flutter project. environment: