import 'dart:math'; import 'package:animations/animations.dart'; import 'package:refilc/models/settings.dart'; import 'package:refilc/utils/format.dart'; import 'package:refilc_kreta_api/providers/grade_provider.dart'; import 'package:refilc/helpers/average_helper.dart'; import 'package:refilc/helpers/subject.dart'; import 'package:refilc_kreta_api/models/grade.dart'; import 'package:refilc_kreta_api/models/subject.dart'; import 'package:refilc_mobile_ui/common/average_display.dart'; import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:refilc_mobile_ui/common/panel/panel.dart'; import 'package:refilc_mobile_ui/common/trend_display.dart'; import 'package:refilc_mobile_ui/common/widgets/cretification/certification_tile.dart'; import 'package:refilc/ui/widgets/grade/grade_tile.dart'; import 'package:refilc_mobile_ui/common/widgets/grade/grade_viewable.dart'; import 'package:refilc_mobile_ui/common/hero_scrollview.dart'; import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator.dart'; import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart'; import 'package:refilc_desktop_ui/pages/grades/grades_count.dart'; import 'package:refilc_mobile_ui/pages/grades/graph.dart'; import 'package:refilc_mobile_ui/pages/grades/subject_grades_container.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_expandable_fab/flutter_expandable_fab.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; import 'grades_page.i18n.dart'; //import 'package:refilc_plus/ui/mobile/goal_planner/new_goal.dart'; class GradeSubjectView extends StatefulWidget { const GradeSubjectView(this.subject, {super.key, this.groupAverage = 0.0}); final GradeSubject subject; final double groupAverage; void push(BuildContext context, {bool root = false}) { Navigator.of(context, rootNavigator: root) .push(CupertinoPageRoute(builder: (context) => this)); } @override State createState() => _GradeSubjectViewState(); } class _GradeSubjectViewState extends State { final GlobalKey _scaffoldKey = GlobalKey(); // Controllers PersistentBottomSheetController? _sheetController; final ScrollController _scrollController = ScrollController(); List gradeTiles = []; // Providers late GradeProvider gradeProvider; late GradeCalculatorProvider calculatorProvider; late SettingsProvider settingsProvider; late double average; late Widget gradeGraph; bool gradeCalcMode = false; List getSubjectGrades(GradeSubject subject) => !gradeCalcMode ? gradeProvider.grades.where((e) => e.subject == subject).toList() : calculatorProvider.grades.where((e) => e.subject == subject).toList(); bool showGraph(List subjectGrades) { if (gradeCalcMode) return true; final gradeDates = subjectGrades.map((e) => e.date.millisecondsSinceEpoch); final maxGradeDate = gradeDates.fold(0, max); final minGradeDate = gradeDates.fold(0, min); if (maxGradeDate - minGradeDate < const Duration(days: 5).inMilliseconds) { return false; // naplo/#78 } return subjectGrades.where((e) => e.type == GradeType.midYear).length > 1; } void buildTiles(List subjectGrades) { List tiles = []; if (showGraph(subjectGrades)) { tiles.add(gradeGraph); } else { tiles.add(Container(height: 24.0)); } // ignore: no_leading_underscores_for_local_identifiers List _gradeTiles = []; if (!gradeCalcMode) { subjectGrades.sort((a, b) => -a.date.compareTo(b.date)); for (var grade in subjectGrades) { if (grade.type == GradeType.midYear) { _gradeTiles.add(GradeViewable(grade)); } else { _gradeTiles.add(CertificationTile(grade, padding: EdgeInsets.zero)); } } } else if (subjectGrades.isNotEmpty) { subjectGrades.sort((a, b) => -a.date.compareTo(b.date)); for (var grade in subjectGrades) { _gradeTiles.add(GradeTile(grade)); } } tiles.add( PageTransitionSwitcher( transitionBuilder: ( Widget child, Animation primaryAnimation, Animation secondaryAnimation, ) { return SharedAxisTransition( animation: primaryAnimation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.vertical, fillColor: Colors.transparent, child: child, ); }, child: _gradeTiles.isNotEmpty ? Panel( key: ValueKey(gradeCalcMode), title: Text( gradeCalcMode ? "Ghost Grades".i18n : "Grades".i18n, ), child: Column( children: _gradeTiles, )) : const SizedBox(), ), ); tiles.add(Padding( padding: EdgeInsets.only(bottom: !gradeCalcMode ? 24.0 : 250.0))); gradeTiles = List.castFrom(tiles); } @override Widget build(BuildContext context) { gradeProvider = Provider.of(context); calculatorProvider = Provider.of(context); settingsProvider = Provider.of(context); List subjectGrades = getSubjectGrades(widget.subject).toList(); average = AverageHelper.averageEvals(subjectGrades); final prevAvg = subjectGrades.isNotEmpty ? AverageHelper.averageEvals(subjectGrades .where((e) => e.date.isBefore(subjectGrades .reduce((v, e) => e.date.isAfter(v.date) ? e : v) .date .subtract(const Duration(days: 30)))) .toList()) : 0.0; gradeGraph = Padding( padding: const EdgeInsets.only(top: 16.0, bottom: 8.0), child: Panel( title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("annual_average".i18n), if (average != prevAvg) TrendDisplay(current: average, previous: prevAvg), ], ), child: Container( padding: const EdgeInsets.only(top: 12.0, right: 12.0), child: Row( children: [ Expanded( child: GradeGraph(subjectGrades, dayThreshold: 5, classAvg: widget.groupAverage)), Padding( padding: const EdgeInsets.only(bottom: 24.0), child: GradesCount(grades: subjectGrades), ), ], ), ), ), ); if (!gradeCalcMode) { buildTiles(subjectGrades); } else { List ghostGrades = calculatorProvider.ghosts .where((e) => e.subject == widget.subject) .toList(); buildTiles(ghostGrades); } return Scaffold( key: _scaffoldKey, floatingActionButtonLocation: ExpandableFab.location, floatingActionButton: Visibility( visible: !gradeCalcMode && subjectGrades .where((e) => e.type == GradeType.midYear) .isNotEmpty, child: ExpandableFab( type: ExpandableFabType.up, distance: 50, children: [ FloatingActionButton.small( child: const Icon(FeatherIcons.plus), onPressed: () { gradeCalc(context); }, ), // FloatingActionButton.small( // child: const Icon(FeatherIcons.flag, size: 20.0), // onPressed: () { // Navigator.of(context).push(CupertinoPageRoute(builder: (context) => PremiumGoalplannerNewGoalScreen(subject: widget.subject))); // }, // ), ], ), ), body: RefreshIndicator( onRefresh: () async {}, color: Theme.of(context).colorScheme.secondary, child: HeroScrollView( onClose: () { if (_sheetController != null && gradeCalcMode) { _sheetController!.close(); } else { Navigator.of(context).pop(); } }, navBarItems: [ const SizedBox(width: 6.0), if (widget.groupAverage != 0) Center( child: AverageDisplay( average: widget.groupAverage, border: true)), const SizedBox(width: 6.0), if (average != 0) Center(child: AverageDisplay(average: average)), const SizedBox(width: 12.0), ], icon: SubjectIcon.resolveVariant( subject: widget.subject, context: context), scrollController: _scrollController, title: widget.subject.renamedTo ?? widget.subject.name.capital(), italic: widget.subject.isRenamed && settingsProvider.renamedSubjectsItalics, child: SubjectGradesContainer( child: CupertinoScrollbar( child: ListView.builder( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 24.0), shrinkWrap: true, itemBuilder: (context, index) => gradeTiles[index], itemCount: gradeTiles.length, ), ), )), )); } void gradeCalc(BuildContext context) { // Scroll to the top of the page _scrollController.animateTo(75, duration: const Duration(milliseconds: 500), curve: Curves.ease); calculatorProvider.clear(); calculatorProvider.addAllGrades(gradeProvider.grades); _sheetController = _scaffoldKey.currentState?.showBottomSheet( (context) => RoundedBottomSheet( borderRadius: 14.0, child: GradeCalculator(widget.subject)), backgroundColor: const Color(0x00000000), elevation: 12.0, ); // Hide the fab and grades setState(() { gradeCalcMode = true; }); _sheetController!.closed.then((value) { // Show fab and grades if (mounted) { setState(() { gradeCalcMode = false; }); } }); } }