forked from firka/student-legacy
297 lines
10 KiB
Dart
297 lines
10 KiB
Dart
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<GradeSubjectView> createState() => _GradeSubjectViewState();
|
|
}
|
|
|
|
class _GradeSubjectViewState extends State<GradeSubjectView> {
|
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
// Controllers
|
|
PersistentBottomSheetController? _sheetController;
|
|
final ScrollController _scrollController = ScrollController();
|
|
|
|
List<Widget> gradeTiles = [];
|
|
|
|
// Providers
|
|
late GradeProvider gradeProvider;
|
|
late GradeCalculatorProvider calculatorProvider;
|
|
late SettingsProvider settingsProvider;
|
|
|
|
late double average;
|
|
late Widget gradeGraph;
|
|
|
|
bool gradeCalcMode = false;
|
|
|
|
List<Grade> getSubjectGrades(GradeSubject subject) => !gradeCalcMode
|
|
? gradeProvider.grades.where((e) => e.subject == subject).toList()
|
|
: calculatorProvider.grades.where((e) => e.subject == subject).toList();
|
|
|
|
bool showGraph(List<Grade> 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<Grade> subjectGrades) {
|
|
List<Widget> tiles = [];
|
|
|
|
if (showGraph(subjectGrades)) {
|
|
tiles.add(gradeGraph);
|
|
} else {
|
|
tiles.add(Container(height: 24.0));
|
|
}
|
|
|
|
// ignore: no_leading_underscores_for_local_identifiers
|
|
List<Widget> _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<double> primaryAnimation,
|
|
Animation<double> 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<GradeProvider>(context);
|
|
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
|
|
settingsProvider = Provider.of<SettingsProvider>(context);
|
|
|
|
List<Grade> 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<Grade> 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;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|