From 2d11c45972c5a96e9d2d41d89796453caf3d78c4 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 13:50:03 +0200 Subject: [PATCH 01/14] added rounded expansion tile (fixed absence bug) --- filcnaplo/lib/ui/filter/sort.dart | 57 ++++++++--- .../lib/pages/absences/absences_page.dart | 99 +++++++++++++------ .../common/widgets/absence/absence_tile.dart | 54 +++++++--- .../absence_group/absence_group_tile.dart | 14 ++- .../pages/absences/absence_subject_view.dart | 18 +++- filcnaplo_mobile_ui/pubspec.yaml | 3 + 6 files changed, 180 insertions(+), 65 deletions(-) diff --git a/filcnaplo/lib/ui/filter/sort.dart b/filcnaplo/lib/ui/filter/sort.dart index 0d9591c..dee8ba3 100644 --- a/filcnaplo/lib/ui/filter/sort.dart +++ b/filcnaplo/lib/ui/filter/sort.dart @@ -12,7 +12,8 @@ import 'package:filcnaplo_mobile_ui/common/widgets/lesson/changed_lesson_tile.da import 'package:filcnaplo/utils/format.dart'; // difference.inDays is not reliable -bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day); +bool _sameDate(DateTime a, DateTime b) => + (a.year == b.year && a.month == b.month && a.day == b.day); List sortDateWidgets( BuildContext context, { @@ -35,13 +36,16 @@ List sortDateWidgets( if (message.conversationId != null) { convMessages.add(w); - Conversation conv = conversations.firstWhere((e) => e.id == message.conversationId, orElse: () => Conversation(id: message.conversationId!)); + Conversation conv = conversations.firstWhere( + (e) => e.id == message.conversationId, + orElse: () => Conversation(id: message.conversationId!)); conv.add(message); if (conv.messages.length == 1) conversations.add(conv); } if (conversations.any((c) => c.id == message.messageId)) { - Conversation conv = conversations.firstWhere((e) => e.id == message.messageId); + Conversation conv = + conversations.firstWhere((e) => e.id == message.messageId); convMessages.add(w); conv.add(message); } @@ -87,26 +91,41 @@ List sortDateWidgets( // Group Absence Tiles List absenceTileWidgets = elements.where((element) { - return element.widget is AbsenceViewable && (element.widget as AbsenceViewable).absence.delay == 0; + return element.widget is AbsenceViewable && + (element.widget as AbsenceViewable).absence.delay == 0; }).toList(); - List absenceTiles = absenceTileWidgets.map((e) => e.widget as AbsenceViewable).toList(); + List absenceTiles = + absenceTileWidgets.map((e) => e.widget as AbsenceViewable).toList(); if (absenceTiles.length > 1) { - elements.removeWhere((element) => element.widget.runtimeType == AbsenceViewable && (element.widget as AbsenceViewable).absence.delay == 0); + elements.removeWhere((element) => + element.widget.runtimeType == AbsenceViewable && + (element.widget as AbsenceViewable).absence.delay == 0); if (elements.isEmpty) { cst = false; } - elements.add(DateWidget( - widget: AbsenceGroupTile(absenceTiles, showDate: !cst), - date: absenceTileWidgets.first.date, - key: "${absenceTileWidgets.first.date.millisecondsSinceEpoch}-absence-group")); + elements.add( + DateWidget( + widget: AbsenceGroupTile( + absenceTiles, + showDate: !cst, + padding: const EdgeInsets.symmetric(horizontal: 6.0), + ), + date: absenceTileWidgets.first.date, + key: + "${absenceTileWidgets.first.date.millisecondsSinceEpoch}-absence-group"), + ); } // Bring Lesson Tiles to front & sort by index asc List lessonTiles = elements.where((element) { return element.widget.runtimeType == ChangedLessonTile; }).toList(); - lessonTiles.sort((a, b) => (a.widget as ChangedLessonTile).lesson.lessonIndex.compareTo((b.widget as ChangedLessonTile).lesson.lessonIndex)); - elements.removeWhere((element) => element.widget.runtimeType == ChangedLessonTile); + lessonTiles.sort((a, b) => (a.widget as ChangedLessonTile) + .lesson + .lessonIndex + .compareTo((b.widget as ChangedLessonTile).lesson.lessonIndex)); + elements.removeWhere( + (element) => element.widget.runtimeType == ChangedLessonTile); elements.insertAll(0, lessonTiles); final date = (elements + absenceTileWidgets).first.date; @@ -122,7 +141,8 @@ List sortDateWidgets( spawnIsolate: false, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, animation, item, index) => filterItemBuilder(context, animation, item.widget, index), + itemBuilder: (context, animation, item, index) => + filterItemBuilder(context, animation, item.widget, index), items: elements, ), ), @@ -131,9 +151,12 @@ List sortDateWidgets( } final nh = DateTime.now(); - final now = DateTime(nh.year, nh.month, nh.day).subtract(const Duration(seconds: 1)); + final now = + DateTime(nh.year, nh.month, nh.day).subtract(const Duration(seconds: 1)); - if (showDivider && items.any((i) => i.date.isBefore(now)) && items.any((i) => i.date.isAfter(now))) { + if (showDivider && + items.any((i) => i.date.isBefore(now)) && + items.any((i) => i.date.isAfter(now))) { items.add( DateWidget( date: now, @@ -153,7 +176,9 @@ List sortDateWidgets( } // Sort future dates asc, past dates desc - items.sort((a, b) => (a.date.isAfter(now) && b.date.isAfter(now) ? 1 : -1) * a.date.compareTo(b.date)); + items.sort((a, b) => + (a.date.isAfter(now) && b.date.isAfter(now) ? 1 : -1) * + a.date.compareTo(b.date)); return items.map((e) => e.widget).toList(); } diff --git a/filcnaplo_desktop_ui/lib/pages/absences/absences_page.dart b/filcnaplo_desktop_ui/lib/pages/absences/absences_page.dart index 26ac74a..f9270b5 100644 --- a/filcnaplo_desktop_ui/lib/pages/absences/absences_page.dart +++ b/filcnaplo_desktop_ui/lib/pages/absences/absences_page.dart @@ -35,7 +35,8 @@ class SubjectAbsence { List absences; double percentage; - SubjectAbsence({required this.subject, this.absences = const [], this.percentage = 0.0}); + SubjectAbsence( + {required this.subject, this.absences = const [], this.percentage = 0.0}); } class AbsencesPage extends StatefulWidget { @@ -45,7 +46,8 @@ class AbsencesPage extends StatefulWidget { _AbsencesPageState createState() => _AbsencesPageState(); } -class _AbsencesPageState extends State with TickerProviderStateMixin { +class _AbsencesPageState extends State + with TickerProviderStateMixin { late UserProvider user; late AbsenceProvider absenceProvider; late TimetableProvider timetableProvider; @@ -65,7 +67,9 @@ class _AbsencesPageState extends State with TickerProviderStateMix WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) { - if (!lesson.isEmpty && lesson.subject.id != '' && lesson.lessonYearIndex != null) { + if (!lesson.isEmpty && + lesson.subject.id != '' && + lesson.lessonYearIndex != null) { _lessonCount.update( lesson.subject, (value) { @@ -89,25 +93,30 @@ class _AbsencesPageState extends State with TickerProviderStateMix if (absence.delay != 0) continue; if (!_absences.containsKey(absence.subject)) { - _absences[absence.subject] = SubjectAbsence(subject: absence.subject, absences: [absence]); + _absences[absence.subject] = + SubjectAbsence(subject: absence.subject, absences: [absence]); } else { _absences[absence.subject]?.absences.add(absence); } } _absences.forEach((subject, absence) { - final absentLessonsOfSubject = absenceProvider.absences.where((e) => e.subject == subject && e.delay == 0).length; + final absentLessonsOfSubject = absenceProvider.absences + .where((e) => e.subject == subject && e.delay == 0) + .length; final totalLessonsOfSubject = _lessonCount[subject]?.lessonYearIndex ?? 0; double absentLessonsOfSubjectPercentage; if (absentLessonsOfSubject <= totalLessonsOfSubject) { - absentLessonsOfSubjectPercentage = absentLessonsOfSubject / totalLessonsOfSubject * 100; + absentLessonsOfSubjectPercentage = + absentLessonsOfSubject / totalLessonsOfSubject * 100; } else { absentLessonsOfSubjectPercentage = -1; } - _absences[subject]?.percentage = absentLessonsOfSubjectPercentage.clamp(-1, 100.0); + _absences[subject]?.percentage = + absentLessonsOfSubjectPercentage.clamp(-1, 100.0); }); absences = _absences.values.toList(); @@ -131,7 +140,8 @@ class _AbsencesPageState extends State with TickerProviderStateMix body: Padding( padding: const EdgeInsets.only(top: 12.0), child: NestedScrollView( - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), + physics: const BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics()), headerSliverBuilder: (context, _) => [ SliverAppBar( pinned: true, @@ -145,7 +155,10 @@ class _AbsencesPageState extends State with TickerProviderStateMix padding: const EdgeInsets.only(left: 8.0), child: Text( "Absences".i18n, - style: TextStyle(color: AppColors.of(context).text, fontSize: 32.0, fontWeight: FontWeight.bold), + style: TextStyle( + color: AppColors.of(context).text, + fontSize: 32.0, + fontWeight: FontWeight.bold), ), ), bottom: FilterBar(items: [ @@ -158,7 +171,8 @@ class _AbsencesPageState extends State with TickerProviderStateMix body: TabBarView( physics: const BouncingScrollPhysics(), controller: _tabController, - children: List.generate(3, (index) => filterViewBuilder(context, index))), + children: List.generate( + 3, (index) => filterViewBuilder(context, index))), ), ), ); @@ -174,10 +188,17 @@ class _AbsencesPageState extends State with TickerProviderStateMix widget: AbsenceSubjectTile( a.subject, percentage: a.percentage, - excused: a.absences.where((a) => a.state == Justification.excused).length, - unexcused: a.absences.where((a) => a.state == Justification.unexcused).length, - pending: a.absences.where((a) => a.state == Justification.pending).length, - onTap: () => AbsenceSubjectView.show(a.subject, a.absences, context: context), + excused: a.absences + .where((a) => a.state == Justification.excused) + .length, + unexcused: a.absences + .where((a) => a.state == Justification.unexcused) + .length, + pending: a.absences + .where((a) => a.state == Justification.pending) + .length, + onTap: () => AbsenceSubjectView.show(a.subject, a.absences, + context: context), ), )); } @@ -186,15 +207,18 @@ class _AbsencesPageState extends State with TickerProviderStateMix for (var absence in absenceProvider.absences) { if (absence.delay != 0) { items.add(DateWidget( - date: absence.date, - widget: AbsenceViewable(absence, padding: EdgeInsets.zero), - )); + date: absence.date, + widget: AbsenceViewable( + absence, + padding: EdgeInsets.zero, + ))); } } break; case AbsenceFilter.misses: for (var note in noteProvider.notes) { - if (note.type?.name == "HaziFeladatHiany" || note.type?.name == "Felszereleshiany") { + if (note.type?.name == "HaziFeladatHiany" || + note.type?.name == "Felszereleshiany") { items.add(DateWidget( date: note.date, widget: MissTile(note), @@ -232,10 +256,15 @@ class _AbsencesPageState extends State with TickerProviderStateMix showDialog( context: context, builder: (context) => AlertDialog( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0)), title: Text("attention".i18n), content: Text("attention_body".i18n), - actions: [ActionButton(label: "Ok", onTap: () => Navigator.of(context).pop())], + actions: [ + ActionButton( + label: "Ok", + onTap: () => Navigator.of(context).pop()) + ], ), ); }, @@ -262,7 +291,10 @@ class _AbsencesPageState extends State with TickerProviderStateMix ); }, child: Column( - children: getFilterWidgets(AbsenceFilter.values[activeData]).map((e) => e.widget).cast().toList(), + children: getFilterWidgets(AbsenceFilter.values[activeData]) + .map((e) => e.widget) + .cast() + .toList(), ), ), ), @@ -284,7 +316,8 @@ class _AbsencesPageState extends State with TickerProviderStateMix itemCount: max(filterWidgets.length + (activeData <= 1 ? 1 : 0), 1), itemBuilder: (context, index) { if (filterWidgets.isNotEmpty) { - if ((index == 0 && activeData == 1) || (index == 0 && activeData == 0)) { + if ((index == 0 && activeData == 1) || + (index == 0 && activeData == 0)) { int value1 = 0; int value2 = 0; String title1 = ""; @@ -292,18 +325,26 @@ class _AbsencesPageState extends State with TickerProviderStateMix String suffix = ""; if (activeData == AbsenceFilter.absences.index) { - value1 = absenceProvider.absences.where((e) => e.delay == 0 && e.state == Justification.excused).length; - value2 = absenceProvider.absences.where((e) => e.delay == 0 && e.state == Justification.unexcused).length; + value1 = absenceProvider.absences + .where((e) => + e.delay == 0 && e.state == Justification.excused) + .length; + value2 = absenceProvider.absences + .where((e) => + e.delay == 0 && e.state == Justification.unexcused) + .length; title1 = "stat_1".i18n; title2 = "stat_2".i18n; suffix = " " + "hr".i18n; } else if (activeData == AbsenceFilter.delays.index) { value1 = absenceProvider.absences - .where((e) => e.delay != 0 && e.state == Justification.excused) + .where((e) => + e.delay != 0 && e.state == Justification.excused) .map((e) => e.delay) .fold(0, (a, b) => a + b); value2 = absenceProvider.absences - .where((e) => e.delay != 0 && e.state == Justification.unexcused) + .where((e) => + e.delay != 0 && e.state == Justification.unexcused) .map((e) => e.delay) .fold(0, (a, b) => a + b); title1 = "stat_3".i18n; @@ -312,7 +353,8 @@ class _AbsencesPageState extends State with TickerProviderStateMix } return Padding( - padding: const EdgeInsets.only(bottom: 24.0, left: 24.0, right: 24.0), + padding: const EdgeInsets.only( + bottom: 24.0, left: 24.0, right: 24.0), child: Row(children: [ Expanded( child: StatisticsTile( @@ -348,7 +390,8 @@ class _AbsencesPageState extends State with TickerProviderStateMix } return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 6.0), + padding: + const EdgeInsets.symmetric(horizontal: 24.0, vertical: 6.0), child: filterWidgets[index - (activeData <= 1 ? 1 : 0)], ); } else { diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart index 3186b21..b01e8e7 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart @@ -9,7 +9,9 @@ import 'package:provider/provider.dart'; import 'absence_tile.i18n.dart'; class AbsenceTile extends StatelessWidget { - const AbsenceTile(this.absence, {Key? key, this.onTap, this.elevation = 0.0, this.padding}) : super(key: key); + const AbsenceTile(this.absence, + {Key? key, this.onTap, this.elevation = 0.0, this.padding}) + : super(key: key); final Absence absence; final void Function()? onTap; @@ -37,39 +39,61 @@ class AbsenceTile extends StatelessWidget { child: Material( type: MaterialType.transparency, child: Padding( - padding: padding ?? (group ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 8.0)), + padding: padding ?? + (group + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 8.0, + )), child: ListTile( onTap: onTap, visualDensity: VisualDensity.compact, dense: group, - contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(!group ? 14.0 : 12.0)), + contentPadding: const EdgeInsets.only( + left: 14.0, right: 12.0, top: 2.0, bottom: 2.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(!group ? 14.0 : 12.0)), leading: Container( width: 44.0, decoration: BoxDecoration( shape: BoxShape.circle, color: !group ? color.withOpacity(.25) : null, ), - child: Center(child: Icon(justificationIcon(absence.state), color: color)), + child: Center( + child: Icon(justificationIcon(absence.state), color: color)), ), title: !group ? Text.rich(TextSpan( text: "${absence.delay == 0 ? "" : absence.delay}", - style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 15.5), + style: const TextStyle( + fontWeight: FontWeight.w700, fontSize: 15.5), children: [ TextSpan( text: absence.delay == 0 - ? justificationName(absence.state).fill(["absence".i18n]).capital() - : 'minute'.plural(absence.delay) + justificationName(absence.state).fill(["delay".i18n]), + ? justificationName(absence.state) + .fill(["absence".i18n]).capital() + : 'minute'.plural(absence.delay) + + justificationName(absence.state) + .fill(["delay".i18n]), style: const TextStyle(fontWeight: FontWeight.w600), ), ], )) : Text( - (absence.lessonIndex != null ? "${absence.lessonIndex}. " : "") + (absence.subject.renamedTo ?? absence.subject.name.capital()), + (absence.lessonIndex != null + ? "${absence.lessonIndex}. " + : "") + + (absence.subject.renamedTo ?? + absence.subject.name.capital()), maxLines: 2, overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14.0, + fontStyle: absence.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ), subtitle: !group ? Text( @@ -77,7 +101,12 @@ class AbsenceTile extends StatelessWidget { maxLines: 2, overflow: TextOverflow.ellipsis, // DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date), - style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), + style: TextStyle( + fontWeight: FontWeight.w500, + fontStyle: absence.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ) : null, ), @@ -97,7 +126,8 @@ class AbsenceTile extends StatelessWidget { } } - static Color justificationColor(Justification state, {required BuildContext context}) { + static Color justificationColor(Justification state, + {required BuildContext context}) { switch (state) { case Justification.excused: return AppColors.of(context).green; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart index 8ba3974..62e8b45 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart @@ -5,6 +5,8 @@ import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_c import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart'; import 'package:filcnaplo/utils/format.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; +import 'package:rounded_expansion_tile/rounded_expansion_tile.dart'; import 'absence_group_tile.i18n.dart'; class AbsenceGroupTile extends StatelessWidget { @@ -30,13 +32,17 @@ class AbsenceGroupTile extends StatelessWidget { child: Material( type: MaterialType.transparency, child: Padding( - padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0), + padding: padding ?? + const EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.0), child: AbsenceGroupContainer( - child: ExpansionTile( + child: RoundedExpansionTile( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), - tilePadding: const EdgeInsets.symmetric(horizontal: 8.0), - backgroundColor: Colors.transparent, + childrenPadding: const EdgeInsets.symmetric(horizontal: 8.0), + tileColor: Colors.transparent, + duration: const Duration(milliseconds: 250), + trailingDuration: 0.5, + trailing: const Icon(FeatherIcons.chevronDown), leading: Container( width: 44.0, height: 44.0, diff --git a/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart b/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart index 6c8060f..dcf162f 100755 --- a/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart +++ b/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart @@ -19,14 +19,18 @@ import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.i18n.dar import 'package:provider/provider.dart'; class AbsenceSubjectView extends StatelessWidget { - const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) : super(key: key); + const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) + : super(key: key); final Subject subject; final List absences; - static void show(Subject subject, List absences, {required BuildContext context}) { + static void show(Subject subject, List absences, + {required BuildContext context}) { Navigator.of(context, rootNavigator: true) - .push(CupertinoPageRoute(builder: (context) => AbsenceSubjectView(subject, absences: absences))) + .push(CupertinoPageRoute( + builder: (context) => + AbsenceSubjectView(subject, absences: absences))) .then((value) { if (value == null) return; @@ -36,7 +40,8 @@ class AbsenceSubjectView extends StatelessWidget { TimetablePage.jump(context, lesson: lesson); } else { ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( - content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)), + content: Text("Cannot find lesson".i18n, + style: const TextStyle(color: Colors.white)), backgroundColor: AppColors.of(context).red, context: context, )); @@ -54,7 +59,10 @@ class AbsenceSubjectView extends StatelessWidget { date: a.date, )) .toList(); - List absenceTiles = sortDateWidgets(context, dateWidgets: dateWidgets, padding: EdgeInsets.zero, hasShadow: true); + List absenceTiles = sortDateWidgets(context, + dateWidgets: dateWidgets, + padding: const EdgeInsets.symmetric(vertical: 6.0), + hasShadow: true); SettingsProvider settingsProvider = Provider.of(context); diff --git a/filcnaplo_mobile_ui/pubspec.yaml b/filcnaplo_mobile_ui/pubspec.yaml index ced8ee0..adcf153 100755 --- a/filcnaplo_mobile_ui/pubspec.yaml +++ b/filcnaplo_mobile_ui/pubspec.yaml @@ -43,6 +43,9 @@ dependencies: dotted_border: ^2.0.0+3 screenshot: ^2.1.0 image_gallery_saver: ^2.0.2 + rounded_expansion_tile: + git: + url: https://github.com/kimaah/rounded_expansion_tile.git dev_dependencies: flutter_lints: ^1.0.0 From e64ab75753431db900b8d997ad8197819d5138ed Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 14:56:57 +0200 Subject: [PATCH 02/14] teacher rename base and settings done :orbnsmirk: --- filcnaplo/lib/database/init.dart | 5 +- filcnaplo/lib/database/query.dart | 111 +++-- filcnaplo/lib/database/store.dart | 79 +++- filcnaplo/lib/models/settings.dart | 28 +- filcnaplo_kreta_api/lib/models/teacher.dart | 34 ++ .../lib/screens/settings/settings_screen.dart | 4 + .../lib/models/premium_scopes.dart | 1 + .../lib/ui/mobile/premium/upsell.dart | 1 + ...names.i18n.dart => modify_names.i18n.dart} | 120 +++-- .../mobile/settings/modify_subject_names.dart | 2 +- .../mobile/settings/modify_teacher_names.dart | 439 ++++++++++++++++++ 11 files changed, 718 insertions(+), 106 deletions(-) create mode 100644 filcnaplo_kreta_api/lib/models/teacher.dart rename filcnaplo_premium/lib/ui/mobile/settings/{modify_subject_names.i18n.dart => modify_names.i18n.dart} (58%) create mode 100644 filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart diff --git a/filcnaplo/lib/database/init.dart b/filcnaplo/lib/database/init.dart index bce6d24..410ad4f 100644 --- a/filcnaplo/lib/database/init.dart +++ b/filcnaplo/lib/database/init.dart @@ -25,7 +25,8 @@ const settingsDB = DatabaseStruct("settings", { "grade_opening_fun": int, "icon_pack": String, "premium_scopes": String, "premium_token": String, "premium_login": String, "last_account_id": String, "renamed_subjects_enabled": int, - "renamed_subjects_italics": int, + "renamed_subjects_italics": int, "renamed_teachers_enabled": int, + "renamed_teachers_italics": int, }); // DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING // YOU'VE BEEN WARNED!!! @@ -40,6 +41,8 @@ const userDataDB = DatabaseStruct("user_data", { "events": String, "absences": String, "group_averages": String, // renamed subjects // non kreta data "renamed_subjects": String, + // renamed teachers // non kreta data + "renamed_teachers": String, // "subject_lesson_count": String, // non kreta data "last_seen_grade": int, }); diff --git a/filcnaplo/lib/database/query.dart b/filcnaplo/lib/database/query.dart index ab37bc1..fb892f6 100644 --- a/filcnaplo/lib/database/query.dart +++ b/filcnaplo/lib/database/query.dart @@ -26,7 +26,8 @@ class DatabaseQuery { Future getSettings(DatabaseProvider database) async { Map settingsMap = (await db.query("settings")).elementAt(0); - SettingsProvider settings = SettingsProvider.fromMap(settingsMap, database: database); + SettingsProvider settings = + SettingsProvider.fromMap(settingsMap, database: database); return settings; } @@ -36,7 +37,10 @@ class DatabaseQuery { for (var user in usersMap) { userProvider.addUser(User.fromMap(user)); } - if (userProvider.getUsers().map((e) => e.id).contains(settings.lastAccountId)) { + if (userProvider + .getUsers() + .map((e) => e.id) + .contains(settings.lastAccountId)) { userProvider.setUser(settings.lastAccountId); } else { if (usersMap.isNotEmpty) { @@ -54,100 +58,133 @@ class UserDatabaseQuery { final Database db; Future> getGrades({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? gradesJson = userData.elementAt(0)["grades"] as String?; if (gradesJson == null) return []; - List grades = (jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList(); + List grades = + (jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList(); return grades; } Future>> getLessons({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return {}; String? lessonsJson = userData.elementAt(0)["timetable"] as String?; if (lessonsJson == null) return {}; if (jsonDecode(lessonsJson) is List) return {}; - Map> lessons = (jsonDecode(lessonsJson) as Map).cast().map((key, value) { - return MapEntry(Week.fromId(int.parse(key)), value.cast>().map((e) => Lesson.fromJson(e)).toList()); + Map> lessons = + (jsonDecode(lessonsJson) as Map).cast().map((key, value) { + return MapEntry( + Week.fromId(int.parse(key)), + value + .cast>() + .map((e) => Lesson.fromJson(e)) + .toList()); }).cast(); return lessons; } Future> getExams({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? examsJson = userData.elementAt(0)["exams"] as String?; if (examsJson == null) return []; - List exams = (jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList(); + List exams = + (jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList(); return exams; } Future> getHomework({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? homeworkJson = userData.elementAt(0)["homework"] as String?; if (homeworkJson == null) return []; - List homework = (jsonDecode(homeworkJson) as List).map((e) => Homework.fromJson(e)).toList(); + List homework = (jsonDecode(homeworkJson) as List) + .map((e) => Homework.fromJson(e)) + .toList(); return homework; } Future> getMessages({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? messagesJson = userData.elementAt(0)["messages"] as String?; if (messagesJson == null) return []; - List messages = (jsonDecode(messagesJson) as List).map((e) => Message.fromJson(e)).toList(); + List messages = (jsonDecode(messagesJson) as List) + .map((e) => Message.fromJson(e)) + .toList(); return messages; } Future> getNotes({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? notesJson = userData.elementAt(0)["notes"] as String?; if (notesJson == null) return []; - List notes = (jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList(); + List notes = + (jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList(); return notes; } Future> getEvents({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? eventsJson = userData.elementAt(0)["events"] as String?; if (eventsJson == null) return []; - List events = (jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList(); + List events = + (jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList(); return events; } Future> getAbsences({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; String? absencesJson = userData.elementAt(0)["absences"] as String?; if (absencesJson == null) return []; - List absences = (jsonDecode(absencesJson) as List).map((e) => Absence.fromJson(e)).toList(); + List absences = (jsonDecode(absencesJson) as List) + .map((e) => Absence.fromJson(e)) + .toList(); return absences; } Future> getGroupAverages({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return []; - String? groupAveragesJson = userData.elementAt(0)["group_averages"] as String?; + String? groupAveragesJson = + userData.elementAt(0)["group_averages"] as String?; if (groupAveragesJson == null) return []; - List groupAverages = (jsonDecode(groupAveragesJson) as List).map((e) => GroupAverage.fromJson(e)).toList(); + List groupAverages = (jsonDecode(groupAveragesJson) as List) + .map((e) => GroupAverage.fromJson(e)) + .toList(); return groupAverages; } - Future getSubjectLessonCount({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + Future getSubjectLessonCount( + {required String userId}) async { + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return SubjectLessonCount.fromMap({}); - String? lessonCountJson = userData.elementAt(0)["subject_lesson_count"] as String?; + String? lessonCountJson = + userData.elementAt(0)["subject_lesson_count"] as String?; if (lessonCountJson == null) return SubjectLessonCount.fromMap({}); - SubjectLessonCount lessonCount = SubjectLessonCount.fromMap(jsonDecode(lessonCountJson) as Map); + SubjectLessonCount lessonCount = + SubjectLessonCount.fromMap(jsonDecode(lessonCountJson) as Map); return lessonCount; } Future lastSeenGrade({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return DateTime(0); int? lastSeenDate = userData.elementAt(0)["last_seen_grade"] as int?; if (lastSeenDate == null) return DateTime(0); @@ -156,10 +193,24 @@ class UserDatabaseQuery { } Future> renamedSubjects({required String userId}) async { - List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); if (userData.isEmpty) return {}; - String? renamedSubjectsJson = userData.elementAt(0)["renamed_subjects"] as String?; + String? renamedSubjectsJson = + userData.elementAt(0)["renamed_subjects"] as String?; if (renamedSubjectsJson == null) return {}; - return (jsonDecode(renamedSubjectsJson) as Map).map((key, value) => MapEntry(key.toString(), value.toString())); + return (jsonDecode(renamedSubjectsJson) as Map) + .map((key, value) => MapEntry(key.toString(), value.toString())); + } + + Future> renamedTeachers({required String userId}) async { + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); + if (userData.isEmpty) return {}; + String? renamedTeachersJson = + userData.elementAt(0)["renamed_teachers"] as String?; + if (renamedTeachersJson == null) return {}; + return (jsonDecode(renamedTeachersJson) as Map) + .map((key, value) => MapEntry(key.toString(), value.toString())); } } diff --git a/filcnaplo/lib/database/store.dart b/filcnaplo/lib/database/store.dart index 53ae1e6..86330b4 100644 --- a/filcnaplo/lib/database/store.dart +++ b/filcnaplo/lib/database/store.dart @@ -27,9 +27,11 @@ class DatabaseStore { } Future storeUser(User user) async { - List userRes = await db.query("users", where: "id = ?", whereArgs: [user.id]); + List userRes = + await db.query("users", where: "id = ?", whereArgs: [user.id]); if (userRes.isNotEmpty) { - await db.update("users", user.toMap(), where: "id = ?", whereArgs: [user.id]); + await db + .update("users", user.toMap(), where: "id = ?", whereArgs: [user.id]); } else { await db.insert("users", user.toMap()); await db.insert("user_data", {"id": user.id}); @@ -49,64 +51,93 @@ class UserDatabaseStore { Future storeGrades(List grades, {required String userId}) async { String gradesJson = jsonEncode(grades.map((e) => e.json).toList()); - await db.update("user_data", {"grades": gradesJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"grades": gradesJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeLessons(Map?> lessons, {required String userId}) async { + Future storeLessons(Map?> lessons, + {required String userId}) async { final map = lessons.map>>( - (k, v) => MapEntry(k.id.toString(), v!.where((e) => e.json != null).map((e) => e.json!).toList().cast()), + (k, v) => MapEntry(k.id.toString(), + v!.where((e) => e.json != null).map((e) => e.json!).toList().cast()), ); String lessonsJson = jsonEncode(map); - await db.update("user_data", {"timetable": lessonsJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"timetable": lessonsJson}, + where: "id = ?", whereArgs: [userId]); } Future storeExams(List exams, {required String userId}) async { String examsJson = jsonEncode(exams.map((e) => e.json).toList()); - await db.update("user_data", {"exams": examsJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"exams": examsJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeHomework(List homework, {required String userId}) async { + Future storeHomework(List homework, + {required String userId}) async { String homeworkJson = jsonEncode(homework.map((e) => e.json).toList()); - await db.update("user_data", {"homework": homeworkJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"homework": homeworkJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeMessages(List messages, {required String userId}) async { + Future storeMessages(List messages, + {required String userId}) async { String messagesJson = jsonEncode(messages.map((e) => e.json).toList()); - await db.update("user_data", {"messages": messagesJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"messages": messagesJson}, + where: "id = ?", whereArgs: [userId]); } Future storeNotes(List notes, {required String userId}) async { String notesJson = jsonEncode(notes.map((e) => e.json).toList()); - await db.update("user_data", {"notes": notesJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"notes": notesJson}, + where: "id = ?", whereArgs: [userId]); } Future storeEvents(List events, {required String userId}) async { String eventsJson = jsonEncode(events.map((e) => e.json).toList()); - await db.update("user_data", {"events": eventsJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"events": eventsJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeAbsences(List absences, {required String userId}) async { + Future storeAbsences(List absences, + {required String userId}) async { String absencesJson = jsonEncode(absences.map((e) => e.json).toList()); - await db.update("user_data", {"absences": absencesJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"absences": absencesJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeGroupAverages(List groupAverages, {required String userId}) async { - String groupAveragesJson = jsonEncode(groupAverages.map((e) => e.json).toList()); - await db.update("user_data", {"group_averages": groupAveragesJson}, where: "id = ?", whereArgs: [userId]); + Future storeGroupAverages(List groupAverages, + {required String userId}) async { + String groupAveragesJson = + jsonEncode(groupAverages.map((e) => e.json).toList()); + await db.update("user_data", {"group_averages": groupAveragesJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeSubjectLessonCount(SubjectLessonCount lessonCount, {required String userId}) async { + Future storeSubjectLessonCount(SubjectLessonCount lessonCount, + {required String userId}) async { String lessonCountJson = jsonEncode(lessonCount.toMap()); - await db.update("user_data", {"subject_lesson_count": lessonCountJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"subject_lesson_count": lessonCountJson}, + where: "id = ?", whereArgs: [userId]); } - Future storeLastSeenGrade(DateTime date, {required String userId}) async { + Future storeLastSeenGrade(DateTime date, + {required String userId}) async { int lastSeenDate = date.millisecondsSinceEpoch; - await db.update("user_data", {"last_seen_grade": lastSeenDate}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"last_seen_grade": lastSeenDate}, + where: "id = ?", whereArgs: [userId]); } - Future storeRenamedSubjects(Map subjects, {required String userId}) async { + Future storeRenamedSubjects(Map subjects, + {required String userId}) async { String renamedSubjectsJson = jsonEncode(subjects); - await db.update("user_data", {"renamed_subjects": renamedSubjectsJson}, where: "id = ?", whereArgs: [userId]); + await db.update("user_data", {"renamed_subjects": renamedSubjectsJson}, + where: "id = ?", whereArgs: [userId]); + } + + Future storeRenamedTeachers(Map teachers, + {required String userId}) async { + String renamedTeachersJson = jsonEncode(teachers); + await db.update("user_data", {"renamed_teachers": renamedTeachersJson}, + where: "id = ?", whereArgs: [userId]); } } diff --git a/filcnaplo/lib/models/settings.dart b/filcnaplo/lib/models/settings.dart index c0589d7..79ef40f 100644 --- a/filcnaplo/lib/models/settings.dart +++ b/filcnaplo/lib/models/settings.dart @@ -69,6 +69,8 @@ class SettingsProvider extends ChangeNotifier { String _lastAccountId; bool _renamedSubjectsEnabled; bool _renamedSubjectsItalics; + bool _renamedTeachersEnabled; + bool _renamedTeachersItalics; SettingsProvider({ DatabaseProvider? database, @@ -106,6 +108,8 @@ class SettingsProvider extends ChangeNotifier { required String lastAccountId, required bool renameSubjectsEnabled, required bool renameSubjectsItalics, + required bool renameTeachersEnabled, + required bool renameTeachersItalics, }) : _database = database, _language = language, _startPage = startPage, @@ -140,7 +144,9 @@ class SettingsProvider extends ChangeNotifier { _premiumLogin = premiumLogin, _lastAccountId = lastAccountId, _renamedSubjectsEnabled = renameSubjectsEnabled, - _renamedSubjectsItalics = renameSubjectsItalics; + _renamedSubjectsItalics = renameSubjectsItalics, + _renamedTeachersEnabled = renameTeachersEnabled, + _renamedTeachersItalics = renameTeachersItalics; factory SettingsProvider.fromMap(Map map, {required DatabaseProvider database}) { @@ -195,6 +201,8 @@ class SettingsProvider extends ChangeNotifier { lastAccountId: map["last_account_id"], renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1, renameSubjectsItalics: map["renamed_subjects_italics"] == 1, + renameTeachersEnabled: map["renamed_teachers_enabled"] == 1, + renameTeachersItalics: map["renamed_teachers_italics"] == 1, ); } @@ -236,7 +244,9 @@ class SettingsProvider extends ChangeNotifier { "premium_login": _premiumLogin, "last_account_id": _lastAccountId, "renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0, - "renamed_subjects_italics": _renamedSubjectsItalics ? 1 : 0 + "renamed_subjects_italics": _renamedSubjectsItalics ? 1 : 0, + "renamed_teachers_enabled": _renamedTeachersEnabled ? 1 : 0, + "renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0, }; } @@ -283,6 +293,8 @@ class SettingsProvider extends ChangeNotifier { lastAccountId: "", renameSubjectsEnabled: false, renameSubjectsItalics: false, + renameTeachersEnabled: false, + renameTeachersItalics: false, ); } @@ -324,6 +336,8 @@ class SettingsProvider extends ChangeNotifier { String get lastAccountId => _lastAccountId; bool get renamedSubjectsEnabled => _renamedSubjectsEnabled; bool get renamedSubjectsItalics => _renamedSubjectsItalics; + bool get renamedTeachersEnabled => _renamedTeachersEnabled; + bool get renamedTeachersItalics => _renamedTeachersItalics; Future update({ bool store = true, @@ -361,6 +375,8 @@ class SettingsProvider extends ChangeNotifier { String? lastAccountId, bool? renamedSubjectsEnabled, bool? renamedSubjectsItalics, + bool? renamedTeachersEnabled, + bool? renamedTeachersItalics, }) async { if (language != null && language != _language) _language = language; if (startPage != null && startPage != _startPage) _startPage = startPage; @@ -448,6 +464,14 @@ class SettingsProvider extends ChangeNotifier { renamedSubjectsItalics != _renamedSubjectsItalics) { _renamedSubjectsItalics = renamedSubjectsItalics; } + if (renamedTeachersEnabled != null && + renamedTeachersEnabled != _renamedTeachersEnabled) { + _renamedTeachersEnabled = renamedTeachersEnabled; + } + if (renamedTeachersItalics != null && + renamedTeachersItalics != _renamedTeachersItalics) { + _renamedTeachersItalics = renamedTeachersItalics; + } if (store) await _database?.store.storeSettings(this); notifyListeners(); } diff --git a/filcnaplo_kreta_api/lib/models/teacher.dart b/filcnaplo_kreta_api/lib/models/teacher.dart new file mode 100644 index 0000000..2ad20c0 --- /dev/null +++ b/filcnaplo_kreta_api/lib/models/teacher.dart @@ -0,0 +1,34 @@ +import 'package:filcnaplo/utils/format.dart'; + +class Teacher { + String id; + String name; + String? renamedTo; + + bool get isRenamed => renamedTo != null; + + Teacher({required this.id, required this.name, this.renamedTo}); + + factory Teacher.fromJson(Map json) { + return Teacher( + id: json["Uid"] ?? "", + name: (json["Nev"] ?? "").trim(), + ); + } + + factory Teacher.fromString(String string) { + return Teacher( + id: string.trim().replaceAll(' ', '').toLowerCase().specialChars(), + name: string.trim(), + ); + } + + @override + bool operator ==(other) { + if (other is! Teacher) return false; + return id == other.id; + } + + @override + int get hashCode => id.hashCode; +} diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart index 80bf7c4..119819b 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart @@ -41,6 +41,7 @@ import 'package:filcnaplo_premium/ui/mobile/settings/nickname.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart'; +import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({Key? key}) : super(key: key); @@ -805,6 +806,9 @@ class _SettingsScreenState extends State MenuRenamedSubjects( settings: settings, ), + MenuRenamedTeachers( + settings: settings, + ), ], ), ), diff --git a/filcnaplo_premium/lib/models/premium_scopes.dart b/filcnaplo_premium/lib/models/premium_scopes.dart index 0d8b92f..f793723 100644 --- a/filcnaplo_premium/lib/models/premium_scopes.dart +++ b/filcnaplo_premium/lib/models/premium_scopes.dart @@ -18,6 +18,7 @@ class PremiumScopes { /// Modify subject names static const renameSubjects = "filc.premium.RENAME_SUBJECTS"; + static const renameTeachers = "filc.premium.RENAME_TEACHERS"; /// Tinta diff --git a/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart b/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart index 1576dff..701791d 100644 --- a/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart +++ b/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart @@ -8,6 +8,7 @@ enum PremiumFeature { profile, iconpack, subjectrename, + teacherrename, weeklytimetable, goalplanner, widget, diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.i18n.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_names.i18n.dart similarity index 58% rename from filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.i18n.dart rename to filcnaplo_premium/lib/ui/mobile/settings/modify_names.i18n.dart index 8d668a0..8c10d88 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.i18n.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/modify_names.i18n.dart @@ -1,48 +1,72 @@ -import 'package:i18n_extension/i18n_extension.dart'; - -extension SettingsLocalization on String { - static final _t = Translations.byLocale("hu_hu") + - { - "en_en": { - "renamed_subjects": "Renamed Subjects", - "rename_subjects": "Rename Subjects", - "rename_subject": "Rename Subject", - "select_subject": "Select Subject", - "modified_name": "Modified Name", - "modify_subjects": "Modify Subjects", - "cancel": "Cancel", - "done": "Done", - "rename_new_subject": "Rename New Subject", - "italics_toggle": "Toggle Italics", - }, - "hu_hu": { - "renamed_subjects": "Átnevezett Tantárgyaid", - "rename_subjects": "Tantárgyak átnevezése", - "rename_subject": "Tantárgy átnevezése", - "select_subject": "Válassz tantárgyat", - "modified_name": "Módosított név", - "modify_subjects": "Tantárgyak átnevezése", - "cancel": "Mégse", - "done": "Kész", - "rename_new_subject": "Új Tantárgy átnevezése", - "italics_toggle": "Dőlt betűs megjelenítés", - }, - "de_de": { - "renamed_subjects": "Umbenannte Fächer", - "rename_subjects": "Fächer umbenennen", - "rename_subject": "Fach umbenennen", - "select_subject": "Fach auswählen", - "modified_name": "Geänderter Name", - "modify_subjects": "Fächer ändern", - "cancel": "Abbrechen", - "done": "Erledigt", - "rename_new_subject": "Neues Fach umbenennen", - "italics_toggle": "Kursivschrift umschalten", - }, - }; - - String get i18n => localize(this, _t); - String fill(List params) => localizeFill(this, params); - String plural(int value) => localizePlural(value, this, _t); - String version(Object modifier) => localizeVersion(modifier, this, _t); -} +import 'package:i18n_extension/i18n_extension.dart'; + +extension SettingsLocalization on String { + static final _t = Translations.byLocale("hu_hu") + + { + "en_en": { + // subject rename + "renamed_subjects": "Renamed Subjects", + "rename_subjects": "Rename Subjects", + "rename_subject": "Rename Subject", + "select_subject": "Select Subject", + "modified_name": "Modified Name", + "modify_subjects": "Modify Subjects", + "cancel": "Cancel", + "done": "Done", + "rename_new_subject": "Rename New Subject", + "italics_toggle": "Italic Font", + // teacher rename + "renamed_teachers": "Renamed Teachers", + "rename_teachers": "Rename Teachers", + "rename_teacher": "Rename Teacher", + "select_teacher": "Select Teacher", + "modify_teachers": "Modify Teachers", + "rename_new_teacher": "Rename New Teacher", + }, + "hu_hu": { + // subject rename + "renamed_subjects": "Átnevezett Tantárgyaid", + "rename_subjects": "Tantárgyak átnevezése", + "rename_subject": "Tantárgy átnevezése", + "select_subject": "Válassz tantárgyat", + "modified_name": "Módosított név", + "modify_subjects": "Tantárgyak módosítása", + "cancel": "Mégse", + "done": "Kész", + "rename_new_subject": "Új tantárgy átnevezése", + "italics_toggle": "Dőlt betűs megjelenítés", + // teacher rename + "renamed_teachers": "Átnevezett tanáraid", + "rename_teachers": "Tanárok átnevezése", + "rename_teacher": "Tanár átnevezése", + "select_teacher": "Válassz tanárt", + "modify_teachers": "Tanárok módosítása", + "rename_new_teacher": "Új tanár átnevezése", + }, + "de_de": { + // subject rename + "renamed_subjects": "Umbenannte Fächer", + "rename_subjects": "Fächer umbenennen", + "rename_subject": "Fach umbenennen", + "select_subject": "Fach auswählen", + "modified_name": "Geänderter Name", + "modify_subjects": "Fächer ändern", + "cancel": "Abbrechen", + "done": "Erledigt", + "rename_new_subject": "Neues Fach umbenennen", + "italics_toggle": "Kursivschrift umschalten", + // teacher rename + "renamed_teachers": "Renamed Teachers", + "rename_teachers": "Rename Teachers", + "rename_teacher": "Rename Teacher", + "select_teacher": "Select Teacher", + "modify_teachers": "Modify Teachers", + "rename_new_teacher": "Rename New Teacher", + }, + }; + + String get i18n => localize(this, _t); + String fill(List params) => localizeFill(this, params); + String plural(int value) => localizePlural(value, this, _t); + String version(Object modifier) => localizeVersion(modifier, this, _t); +} diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart index 1bad1b4..37daf97 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart @@ -19,7 +19,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; -import 'modify_subject_names.i18n.dart'; +import 'modify_names.i18n.dart'; class MenuRenamedSubjects extends StatelessWidget { const MenuRenamedSubjects({Key? key, required this.settings}) diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart new file mode 100644 index 0000000..e03a35e --- /dev/null +++ b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart @@ -0,0 +1,439 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:filcnaplo/api/providers/database_provider.dart'; +import 'package:filcnaplo/api/providers/user_provider.dart'; +import 'package:filcnaplo/models/settings.dart'; +import 'package:filcnaplo/theme/colors/colors.dart'; +import 'package:filcnaplo/utils/format.dart'; +import 'package:filcnaplo_kreta_api/models/teacher.dart'; +import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; +import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; +import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart'; +import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; +import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; +import 'package:filcnaplo_premium/models/premium_scopes.dart'; +import 'package:filcnaplo_premium/providers/premium_provider.dart'; +import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; +import 'package:provider/provider.dart'; + +import 'modify_names.i18n.dart'; + +class MenuRenamedTeachers extends StatelessWidget { + const MenuRenamedTeachers({Key? key, required this.settings}) + : super(key: key); + + final SettingsProvider settings; + + @override + Widget build(BuildContext context) { + return PanelButton( + padding: const EdgeInsets.only(left: 14.0), + onPressed: () { + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.renameTeachers)) { + PremiumLockedFeatureUpsell.show( + context: context, feature: PremiumFeature.teacherrename); + return; + } + + Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute(builder: (context) => const ModifyTeacherNames()), + ); + }, + title: Text( + "rename_teachers".i18n, + style: TextStyle( + color: AppColors.of(context) + .text + .withOpacity(settings.renamedTeachersEnabled ? 1.0 : .5)), + ), + leading: settings.renamedTeachersEnabled + ? const Icon(FeatherIcons.penTool) + : Icon(FeatherIcons.penTool, + color: AppColors.of(context).text.withOpacity(.25)), + trailingDivider: true, + trailing: Switch( + onChanged: (v) async { + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.renameTeachers)) { + PremiumLockedFeatureUpsell.show( + context: context, feature: PremiumFeature.teacherrename); + return; + } + + settings.update(renamedTeachersEnabled: v); + await Provider.of(context, listen: false) + .convertBySettings(); + await Provider.of(context, listen: false) + .convertBySettings(); + await Provider.of(context, listen: false) + .convertBySettings(); + }, + value: settings.renamedTeachersEnabled, + activeColor: Theme.of(context).colorScheme.secondary, + ), + ); + } +} + +class ModifyTeacherNames extends StatefulWidget { + const ModifyTeacherNames({Key? key}) : super(key: key); + + @override + State createState() => _ModifyTeacherNamesState(); +} + +class _ModifyTeacherNamesState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + final _teacherName = TextEditingController(); + String? selectedTeacherId; + + late List teachers; + late UserProvider user; + late DatabaseProvider dbProvider; + late SettingsProvider settings; + + @override + void initState() { + super.initState(); + teachers = (Provider.of(context, listen: false) + .grades + .map((e) => e.teacher) + .toSet() + .toList() + ..sort((a, b) => a.compareTo(b))) + .map((e) => Teacher.fromString(e)) + .toList(); + print(teachers.map((e) => e.name)); + user = Provider.of(context, listen: false); + dbProvider = Provider.of(context, listen: false); + } + + Future> fetchRenamedTeachers() async { + return await dbProvider.userQuery.renamedTeachers(userId: user.id!); + } + + void showRenameDialog() { + showDialog( + context: context, + builder: (context) => StatefulBuilder(builder: (context, setS) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(14.0))), + title: Text("rename_teacher".i18n), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DropdownButton2( + items: teachers + .map((item) => DropdownMenuItem( + value: item.id, + child: Text( + item.name, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: AppColors.of(context).text, + ), + overflow: TextOverflow.ellipsis, + ), + )) + .toList(), + onChanged: (String? v) async { + final renamedSubs = await fetchRenamedTeachers(); + + setS(() { + selectedTeacherId = v; + + if (renamedSubs.containsKey(selectedTeacherId)) { + _teacherName.text = renamedSubs[selectedTeacherId]!; + } else { + _teacherName.text = ""; + } + }); + }, + iconSize: 14, + iconEnabledColor: AppColors.of(context).text, + iconDisabledColor: AppColors.of(context).text, + underline: const SizedBox(), + itemHeight: 40, + itemPadding: const EdgeInsets.only(left: 14, right: 14), + buttonWidth: 50, + dropdownWidth: 300, + dropdownPadding: null, + buttonDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + ), + dropdownDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + ), + dropdownElevation: 8, + scrollbarRadius: const Radius.circular(40), + scrollbarThickness: 6, + scrollbarAlwaysShow: true, + offset: const Offset(-10, -10), + buttonSplashColor: Colors.transparent, + customButton: Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey, width: 2), + borderRadius: BorderRadius.circular(12.0), + ), + padding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 8.0), + child: Text( + selectedTeacherId == null + ? "select_teacher".i18n + : teachers + .firstWhere( + (element) => element.id == selectedTeacherId) + .name, + style: Theme.of(context).textTheme.titleSmall!.copyWith( + fontWeight: FontWeight.w700, + color: AppColors.of(context).text.withOpacity(0.75)), + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.center, + ), + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Icon(FeatherIcons.arrowDown, size: 32), + ), + TextField( + controller: _teacherName, + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: + const BorderSide(color: Colors.grey, width: 1.5), + borderRadius: BorderRadius.circular(12.0), + ), + focusedBorder: OutlineInputBorder( + borderSide: + const BorderSide(color: Colors.grey, width: 1.5), + borderRadius: BorderRadius.circular(12.0), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 12.0), + hintText: "modified_name".i18n, + suffixIcon: IconButton( + icon: const Icon( + FeatherIcons.x, + color: Colors.grey, + ), + onPressed: () { + setState(() { + _teacherName.text = ""; + }); + }, + ), + ), + ), + ], + ), + actions: [ + TextButton( + child: Text( + "cancel".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () { + Navigator.of(context).maybePop(); + }, + ), + TextButton( + child: Text( + "done".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () async { + if (selectedTeacherId != null) { + final renamedSubs = await fetchRenamedTeachers(); + + renamedSubs[selectedTeacherId!] = _teacherName.text; + await dbProvider.userStore + .storeRenamedTeachers(renamedSubs, userId: user.id!); + await Provider.of(context, listen: false) + .convertBySettings(); + await Provider.of(context, listen: false) + .convertBySettings(); + await Provider.of(context, listen: false) + .convertBySettings(); + } + Navigator.of(context).pop(true); + setState(() {}); + }, + ), + ], + ); + }), + ).then((val) { + _teacherName.text = ""; + selectedTeacherId = null; + }); + } + + @override + Widget build(BuildContext context) { + settings = Provider.of(context); + return Scaffold( + key: _scaffoldKey, + appBar: AppBar( + surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, + leading: BackButton(color: AppColors.of(context).text), + title: Text( + "modify_teachers".i18n, + style: TextStyle(color: AppColors.of(context).text), + ), + ), + body: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Panel( + child: SwitchListTile( + title: Text("italics_toggle".i18n), + onChanged: (value) => + settings.update(renamedTeachersItalics: value), + value: settings.renamedTeachersItalics, + ), + ), + const SizedBox( + height: 20, + ), + InkWell( + onTap: showRenameDialog, + borderRadius: BorderRadius.circular(12.0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey, width: 2), + borderRadius: BorderRadius.circular(12.0), + ), + padding: const EdgeInsets.symmetric( + vertical: 18.0, horizontal: 12.0), + child: Center( + child: Text( + "rename_new_teacher".i18n, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 18, + color: AppColors.of(context).text.withOpacity(.85), + ), + ), + ), + ), + ), + const SizedBox( + height: 30, + ), + FutureBuilder>( + future: fetchRenamedTeachers(), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.data!.isEmpty) { + return Container(); + } + + return Panel( + title: Text("renamed_teachers".i18n), + child: Column( + children: snapshot.data!.keys.map( + (key) { + Teacher? teacher = teachers + .firstWhere((element) => key == element.id); + String renameTo = snapshot.data![key]!; + return RenamedTeacherItem( + teacher: teacher, + renamedTo: renameTo, + modifyCallback: () { + setState(() { + selectedTeacherId = teacher.id; + _teacherName.text = renameTo; + }); + showRenameDialog(); + }, + removeCallback: () { + setState(() { + Map subs = + Map.from(snapshot.data!); + subs.remove(key); + dbProvider.userStore.storeRenamedTeachers( + subs, + userId: user.id!); + }); + }, + ); + }, + ).toList(), + ), + ); + }, + ), + ], + ), + ), + )); + } +} + +class RenamedTeacherItem extends StatelessWidget { + const RenamedTeacherItem({ + Key? key, + required this.teacher, + required this.renamedTo, + required this.modifyCallback, + required this.removeCallback, + }) : super(key: key); + + final Teacher teacher; + final String renamedTo; + final void Function() modifyCallback; + final void Function() removeCallback; + + @override + Widget build(BuildContext context) { + return ListTile( + minLeadingWidth: 32.0, + dense: true, + contentPadding: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), + visualDensity: VisualDensity.compact, + onTap: () {}, + leading: Icon(FeatherIcons.user, + color: AppColors.of(context).text.withOpacity(.75)), + title: InkWell( + onTap: modifyCallback, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + teacher.name.capital(), + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: AppColors.of(context).text.withOpacity(.75)), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Text( + renamedTo, + style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + trailing: InkWell( + onTap: removeCallback, + child: Icon(FeatherIcons.trash, + color: AppColors.of(context).red.withOpacity(.75)), + ), + ); + } +} From ded029e4cbf705d8253d6fc0db8437be7f6eda32 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 15:15:56 +0200 Subject: [PATCH 03/14] maybe finished teacher rename --- .../lib/screens/settings/settings_screen.dart | 2 - filcnaplo_kreta_api/lib/models/exam.dart | 17 +++++-- filcnaplo_kreta_api/lib/models/grade.dart | 51 ++++++++++++++----- filcnaplo_kreta_api/lib/models/homework.dart | 5 +- filcnaplo_kreta_api/lib/models/lesson.dart | 36 ++++++++----- filcnaplo_kreta_api/lib/models/note.dart | 21 +++++--- .../lib/common/widgets/exam/exam_view.dart | 22 +++++--- .../lib/common/widgets/grade/grade_view.dart | 33 +++++++++--- .../widgets/homework/homework_view.dart | 12 ++++- .../common/widgets/lesson/lesson_view.dart | 33 +++++++++--- .../lib/common/widgets/note/note_tile.dart | 18 +++++-- .../lib/common/widgets/note/note_view.dart | 20 ++++++-- .../grades/calculator/grade_calculator.dart | 25 ++++++--- .../ui/mobile/goal_planner/goal_planner.dart | 31 +++++++---- .../mobile/settings/modify_teacher_names.dart | 12 ++--- 15 files changed, 246 insertions(+), 92 deletions(-) diff --git a/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart index aef41ff..90a3213 100644 --- a/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart @@ -1,6 +1,4 @@ import 'package:filcnaplo/api/providers/update_provider.dart'; -import 'package:filcnaplo/theme/colors/accent.dart'; -import 'package:filcnaplo/theme/observer.dart'; import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; import 'package:filcnaplo_kreta_api/providers/event_provider.dart'; import 'package:filcnaplo_kreta_api/providers/exam_provider.dart'; diff --git a/filcnaplo_kreta_api/lib/models/exam.dart b/filcnaplo_kreta_api/lib/models/exam.dart index ac25bc9..5f7689b 100644 --- a/filcnaplo_kreta_api/lib/models/exam.dart +++ b/filcnaplo_kreta_api/lib/models/exam.dart @@ -1,4 +1,5 @@ import 'category.dart'; +import 'teacher.dart'; class Exam { Map? json; @@ -7,7 +8,7 @@ class Exam { Category? mode; int? subjectIndex; String subjectName; - String teacher; + Teacher teacher; String description; String group; String id; @@ -28,14 +29,20 @@ class Exam { factory Exam.fromJson(Map json) { return Exam( id: json["Uid"] ?? "", - date: json["BejelentesDatuma"] != null ? DateTime.parse(json["BejelentesDatuma"]).toLocal() : DateTime(0), - writeDate: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0), + date: json["BejelentesDatuma"] != null + ? DateTime.parse(json["BejelentesDatuma"]).toLocal() + : DateTime(0), + writeDate: json["Datum"] != null + ? DateTime.parse(json["Datum"]).toLocal() + : DateTime(0), mode: json["Modja"] != null ? Category.fromJson(json["Modja"]) : null, subjectIndex: json["OrarendiOraOraszama"], subjectName: json["TantargyNeve"] ?? "", - teacher: (json["RogzitoTanarNeve"] ?? "").trim(), + teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()), description: (json["Temaja"] ?? "").trim(), - group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] ?? "" : "", + group: json["OsztalyCsoport"] != null + ? json["OsztalyCsoport"]["Uid"] ?? "" + : "", json: json, ); } diff --git a/filcnaplo_kreta_api/lib/models/grade.dart b/filcnaplo_kreta_api/lib/models/grade.dart index de82478..249f303 100644 --- a/filcnaplo_kreta_api/lib/models/grade.dart +++ b/filcnaplo_kreta_api/lib/models/grade.dart @@ -1,13 +1,14 @@ import 'package:filcnaplo/utils/format.dart'; import 'category.dart'; import 'subject.dart'; +import 'teacher.dart'; class Grade { Map? json; String id; DateTime date; GradeValue value; - String teacher; + Teacher teacher; String description; GradeType type; String groupId; @@ -38,23 +39,35 @@ class Grade { factory Grade.fromJson(Map json) { return Grade( id: json["Uid"] ?? "", - date: json["KeszitesDatuma"] != null ? DateTime.parse(json["KeszitesDatuma"]).toLocal() : DateTime(0), + date: json["KeszitesDatuma"] != null + ? DateTime.parse(json["KeszitesDatuma"]).toLocal() + : DateTime(0), value: GradeValue( json["SzamErtek"] ?? 0, json["SzovegesErtek"] ?? "", json["SzovegesErtekelesRovidNev"] ?? "", json["SulySzazalekErteke"] ?? 0, - percentage: json["ErtekFajta"] != null ? json["ErtekFajta"]["Uid"] == "3,Szazalekos" : false, + percentage: json["ErtekFajta"] != null + ? json["ErtekFajta"]["Uid"] == "3,Szazalekos" + : false, ), - teacher: (json["ErtekeloTanarNeve"] ?? "").trim(), + teacher: Teacher.fromString((json["ErtekeloTanarNeve"] ?? "").trim()), description: json["Tema"] ?? "", - type: json["Tipus"] != null ? Category.getGradeType(json["Tipus"]["Nev"]) : GradeType.unknown, + type: json["Tipus"] != null + ? Category.getGradeType(json["Tipus"]["Nev"]) + : GradeType.unknown, groupId: (json["OsztalyCsoport"] ?? {})["Uid"] ?? "", subject: Subject.fromJson(json["Tantargy"] ?? {}), - gradeType: json["ErtekFajta"] != null ? Category.fromJson(json["ErtekFajta"]) : null, + gradeType: json["ErtekFajta"] != null + ? Category.fromJson(json["ErtekFajta"]) + : null, mode: Category.fromJson(json["Mod"] ?? {}), - writeDate: json["RogzitesDatuma"] != null ? DateTime.parse(json["RogzitesDatuma"]).toLocal() : DateTime(0), - seenDate: json["LattamozasDatuma"] != null ? DateTime.parse(json["LattamozasDatuma"]).toLocal() : DateTime(0), + writeDate: json["RogzitesDatuma"] != null + ? DateTime.parse(json["RogzitesDatuma"]).toLocal() + : DateTime(0), + seenDate: json["LattamozasDatuma"] != null + ? DateTime.parse(json["LattamozasDatuma"]).toLocal() + : DateTime(0), form: (json["Jelleg"] ?? "Na") != "Na" ? json["Jelleg"] : "", json: json, ); @@ -76,7 +89,8 @@ class GradeValue { set value(int v) => _value = v; int get value { String _valueName = valueName.toLowerCase().specialChars(); - if (_value == 0 && ["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) { + if (_value == 0 && + ["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) { switch (_valueName) { case "peldas": return 5; @@ -101,7 +115,8 @@ class GradeValue { set weight(int v) => _weight = v; int get weight { String _valueName = valueName.toLowerCase().specialChars(); - if (_value == 0 && ["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) { + if (_value == 0 && + ["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) { return 0; } return _weight; @@ -110,11 +125,23 @@ class GradeValue { final bool _percentage; bool get percentage => _percentage; - GradeValue(int value, String valueName, this.shortName, int weight, {bool percentage = false}) + GradeValue(int value, String valueName, this.shortName, int weight, + {bool percentage = false}) : _value = value, _valueName = valueName, _weight = weight, _percentage = percentage; } -enum GradeType { midYear, firstQ, secondQ, halfYear, thirdQ, fourthQ, endYear, levelExam, ghost, unknown } +enum GradeType { + midYear, + firstQ, + secondQ, + halfYear, + thirdQ, + fourthQ, + endYear, + levelExam, + ghost, + unknown +} diff --git a/filcnaplo_kreta_api/lib/models/homework.dart b/filcnaplo_kreta_api/lib/models/homework.dart index b233a3c..bd4ab39 100644 --- a/filcnaplo_kreta_api/lib/models/homework.dart +++ b/filcnaplo_kreta_api/lib/models/homework.dart @@ -1,6 +1,7 @@ import 'package:filcnaplo_kreta_api/client/api.dart'; import 'subject.dart'; +import 'teacher.dart'; class Homework { Map? json; @@ -9,7 +10,7 @@ class Homework { DateTime deadline; bool byTeacher; bool homeworkEnabled; - String teacher; + Teacher teacher; String content; Subject subject; String group; @@ -45,7 +46,7 @@ class Homework { : DateTime(0), byTeacher: json["IsTanarRogzitette"] ?? true, homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false, - teacher: (json["RogzitoTanarNeve"] ?? "").trim(), + teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()), content: (json["Szoveg"] ?? "").trim(), subject: Subject.fromJson(json["Tantargy"] ?? {}), group: json["OsztalyCsoport"] != null diff --git a/filcnaplo_kreta_api/lib/models/lesson.dart b/filcnaplo_kreta_api/lib/models/lesson.dart index b724734..0ac9ab2 100644 --- a/filcnaplo_kreta_api/lib/models/lesson.dart +++ b/filcnaplo_kreta_api/lib/models/lesson.dart @@ -1,5 +1,6 @@ import 'subject.dart'; import 'category.dart'; +import 'teacher.dart'; class Lesson { Map? json; @@ -8,8 +9,8 @@ class Lesson { Subject subject; String lessonIndex; int? lessonYearIndex; - String substituteTeacher; - String teacher; + Teacher? substituteTeacher; + Teacher teacher; bool homeworkEnabled; DateTime start; DateTime end; @@ -31,7 +32,7 @@ class Lesson { required this.subject, required this.lessonIndex, this.lessonYearIndex, - this.substituteTeacher = "", + this.substituteTeacher, required this.teacher, this.homeworkEnabled = false, required this.start, @@ -53,27 +54,38 @@ class Lesson { factory Lesson.fromJson(Map json) { return Lesson( id: json["Uid"] ?? "", - status: json["Allapot"] != null ? Category.fromJson(json["Allapot"]) : null, - date: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0), + status: + json["Allapot"] != null ? Category.fromJson(json["Allapot"]) : null, + date: json["Datum"] != null + ? DateTime.parse(json["Datum"]).toLocal() + : DateTime(0), subject: Subject.fromJson(json["Tantargy"] ?? {}), lessonIndex: json["Oraszam"] != null ? json["Oraszam"].toString() : "+", lessonYearIndex: json["OraEvesSorszama"], - substituteTeacher: (json["HelyettesTanarNeve"] ?? "").trim(), - teacher: (json["TanarNeve"] ?? "").trim(), + substituteTeacher: + Teacher.fromString((json["HelyettesTanarNeve"] ?? "").trim()), + teacher: Teacher.fromString((json["TanarNeve"] ?? "").trim()), homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false, - start: json["KezdetIdopont"] != null ? DateTime.parse(json["KezdetIdopont"]).toLocal() : DateTime(0), + start: json["KezdetIdopont"] != null + ? DateTime.parse(json["KezdetIdopont"]).toLocal() + : DateTime(0), studentPresence: json["TanuloJelenlet"] != null ? (json["TanuloJelenlet"]["Nev"] ?? "") == "Hianyzas" ? false : true : true, - end: json["VegIdopont"] != null ? DateTime.parse(json["VegIdopont"]).toLocal() : DateTime(0), + end: json["VegIdopont"] != null + ? DateTime.parse(json["VegIdopont"]).toLocal() + : DateTime(0), homeworkId: json["HaziFeladatUid"] ?? "", exam: json["BejelentettSzamonkeresUid"] ?? "", type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null, description: json["Tema"] ?? "", - room: ((json["TeremNeve"] ?? "").split("_").join(" ") as String).replaceAll(RegExp(r" ?terem ?", caseSensitive: false), ""), - groupName: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Nev"] ?? "" : "", + room: ((json["TeremNeve"] ?? "").split("_").join(" ") as String) + .replaceAll(RegExp(r" ?terem ?", caseSensitive: false), ""), + groupName: json["OsztalyCsoport"] != null + ? json["OsztalyCsoport"]["Nev"] ?? "" + : "", name: json["Nev"] ?? "", online: json["IsDigitalisOra"] ?? false, isEmpty: json['isEmpty'] ?? false, @@ -92,6 +104,6 @@ class Lesson { return null; } - bool get isChanged => status?.name == "Elmaradt" || substituteTeacher != ""; + bool get isChanged => status?.name == "Elmaradt" || substituteTeacher != null; bool get swapDesc => room.length > 8; } diff --git a/filcnaplo_kreta_api/lib/models/note.dart b/filcnaplo_kreta_api/lib/models/note.dart index 1b15b43..ba94fa5 100644 --- a/filcnaplo_kreta_api/lib/models/note.dart +++ b/filcnaplo_kreta_api/lib/models/note.dart @@ -1,4 +1,5 @@ import 'category.dart'; +import 'teacher.dart'; class Note { Map? json; @@ -6,7 +7,7 @@ class Note { String title; DateTime date; DateTime submitDate; - String teacher; + Teacher teacher; DateTime seenDate; String groupId; String content; @@ -29,11 +30,19 @@ class Note { return Note( id: json["Uid"] ?? "", title: json["Cim"] ?? "", - date: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0), - submitDate: json["KeszitesDatuma"] != null ? DateTime.parse(json["KeszitesDatuma"]).toLocal() : DateTime(0), - teacher: (json["KeszitoTanarNeve"] ?? "").trim(), - seenDate: json["LattamozasDatuma"] != null ? DateTime.parse(json["LattamozasDatuma"]).toLocal() : DateTime(0), - groupId: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] ?? "" : "", + date: json["Datum"] != null + ? DateTime.parse(json["Datum"]).toLocal() + : DateTime(0), + submitDate: json["KeszitesDatuma"] != null + ? DateTime.parse(json["KeszitesDatuma"]).toLocal() + : DateTime(0), + teacher: Teacher.fromString((json["KeszitoTanarNeve"] ?? "").trim()), + seenDate: json["LattamozasDatuma"] != null + ? DateTime.parse(json["LattamozasDatuma"]).toLocal() + : DateTime(0), + groupId: json["OsztalyCsoport"] != null + ? json["OsztalyCsoport"]["Uid"] ?? "" + : "", content: json["Tartalom"].replaceAll("\r", "") ?? "", type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null, json: json, diff --git a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart index 188787c..4061baa 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart @@ -12,7 +12,8 @@ class ExamView extends StatelessWidget { final Exam exam; - static show(Exam exam, {required BuildContext context}) => showBottomCard(context: context, child: ExamView(exam)); + static show(Exam exam, {required BuildContext context}) => + showBottomCard(context: context, child: ExamView(exam)); @override Widget build(BuildContext context) { @@ -27,7 +28,8 @@ class ExamView extends StatelessWidget { leading: Padding( padding: const EdgeInsets.only(left: 6.0), child: Icon( - SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context), + SubjectIcon.resolveVariant( + subjectName: exam.subjectName, context: context), size: 36.0, color: AppColors.of(context).text.withOpacity(.75), ), @@ -39,7 +41,10 @@ class ExamView extends StatelessWidget { style: const TextStyle(fontWeight: FontWeight.w600), ), subtitle: Text( - exam.teacher, + (exam.teacher.isRenamed + ? exam.teacher.renamedTo + : exam.teacher.name) ?? + '', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500), @@ -51,9 +56,14 @@ class ExamView extends StatelessWidget { ), // Details - if (exam.writeDate.year != 0) Detail(title: "date".i18n, description: exam.writeDate.format(context)), - if (exam.description != "") Detail(title: "description".i18n, description: exam.description), - if (exam.mode != null) Detail(title: "mode".i18n, description: exam.mode!.description), + if (exam.writeDate.year != 0) + Detail( + title: "date".i18n, + description: exam.writeDate.format(context)), + if (exam.description != "") + Detail(title: "description".i18n, description: exam.description), + if (exam.mode != null) + Detail(title: "mode".i18n, description: exam.mode!.description), ], ), ); diff --git a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart index c510961..4673b3d 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart @@ -11,7 +11,8 @@ import 'grade_view.i18n.dart'; class GradeView extends StatelessWidget { const GradeView(this.grade, {Key? key}) : super(key: key); - static show(Grade grade, {required BuildContext context}) => showBottomCard(context: context, child: GradeView(grade)); + static show(Grade grade, {required BuildContext context}) => + showBottomCard(context: context, child: GradeView(grade)); final Grade grade; @@ -30,10 +31,21 @@ class GradeView extends StatelessWidget { grade.subject.renamedTo ?? grade.subject.name.capital(), maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), + style: TextStyle( + fontWeight: FontWeight.w600, + fontStyle: grade.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ), subtitle: Text( - !Provider.of(context, listen: false).presentationMode ? grade.teacher : "Tanár", + !Provider.of(context, listen: false) + .presentationMode + ? (grade.teacher.isRenamed + ? grade.teacher.renamedTo + : grade.teacher.name) ?? + '' + : "Tanár", maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500), @@ -49,13 +61,20 @@ class GradeView extends StatelessWidget { title: "value".i18n, description: "${grade.value.valueName} " + percentText(), ), - if (grade.description != "") Detail(title: "description".i18n, description: grade.description), - if (grade.mode.description != "") Detail(title: "mode".i18n, description: grade.mode.description), - if (grade.writeDate.year != 0) Detail(title: "date".i18n, description: grade.writeDate.format(context)), + if (grade.description != "") + Detail(title: "description".i18n, description: grade.description), + if (grade.mode.description != "") + Detail(title: "mode".i18n, description: grade.mode.description), + if (grade.writeDate.year != 0) + Detail( + title: "date".i18n, + description: grade.writeDate.format(context)), ], ), ); } - String percentText() => grade.value.weight != 100 && grade.value.weight > 0 ? "${grade.value.weight}%" : ""; + String percentText() => grade.value.weight != 100 && grade.value.weight > 0 + ? "${grade.value.weight}%" + : ""; } diff --git a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart index fef95b5..8e26d92 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart @@ -51,10 +51,18 @@ class HomeworkView extends StatelessWidget { homework.subject.renamedTo ?? homework.subject.name.capital(), maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), + style: TextStyle( + fontWeight: FontWeight.w600, + fontStyle: homework.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ), subtitle: Text( - homework.teacher, + (homework.teacher.isRenamed + ? homework.teacher.renamedTo + : homework.teacher.name) ?? + '', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500), diff --git a/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart index 45dd6c0..222e05f 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart @@ -54,10 +54,23 @@ class LessonView extends StatelessWidget { lesson.subject.renamedTo ?? lesson.subject.name.capital(), maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), + style: TextStyle( + fontWeight: FontWeight.w600, + fontStyle: lesson.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ), subtitle: Text( - lesson.substituteTeacher == "" ? lesson.teacher : lesson.substituteTeacher, + ((lesson.substituteTeacher == null || + lesson.substituteTeacher!.name == "") + ? (lesson.teacher.isRenamed + ? lesson.teacher.renamedTo + : lesson.teacher.name) + : (lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo + : lesson.substituteTeacher!.name)) ?? + '', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500), @@ -69,10 +82,18 @@ class LessonView extends StatelessWidget { ), // Details - if (lesson.room != "") Detail(title: "Room".i18n, description: lesson.room.replaceAll("_", " ")), - if (lesson.description != "") Detail(title: "Description".i18n, description: lesson.description), - if (lesson.lessonYearIndex != null) Detail(title: "Lesson Number".i18n, description: "${lesson.lessonYearIndex}."), - if (lesson.groupName != "") Detail(title: "Group".i18n, description: lesson.groupName), + if (lesson.room != "") + Detail( + title: "Room".i18n, + description: lesson.room.replaceAll("_", " ")), + if (lesson.description != "") + Detail(title: "Description".i18n, description: lesson.description), + if (lesson.lessonYearIndex != null) + Detail( + title: "Lesson Number".i18n, + description: "${lesson.lessonYearIndex}."), + if (lesson.groupName != "") + Detail(title: "Group".i18n, description: lesson.groupName), ], ), ); diff --git a/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart index 89ba313..c3761c9 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart @@ -4,7 +4,8 @@ import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; import 'package:flutter/material.dart'; class NoteTile extends StatelessWidget { - const NoteTile(this.note, {Key? key, this.onTap, this.padding}) : super(key: key); + const NoteTile(this.note, {Key? key, this.onTap, this.padding}) + : super(key: key); final Note note; final void Function()? onTap; @@ -20,11 +21,20 @@ class NoteTile extends StatelessWidget { visualDensity: VisualDensity.compact, contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0), onTap: onTap, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)), + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)), leading: ProfileImage( - name: note.teacher, + name: (note.teacher.isRenamed + ? note.teacher.renamedTo + : note.teacher.name) ?? + '', radius: 22.0, - backgroundColor: ColorUtils.stringToColor(note.teacher), + backgroundColor: ColorUtils.stringToColor( + (note.teacher.isRenamed + ? note.teacher.renamedTo + : note.teacher.name) ?? + '', + ), ), title: Text( note.title, diff --git a/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart index 98aecea..64680af 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart @@ -12,7 +12,8 @@ class NoteView extends StatelessWidget { final Note note; - static void show(Note note, {required BuildContext context}) => showSlidingBottomSheet(context: context, child: NoteView(note)); + static void show(Note note, {required BuildContext context}) => + showSlidingBottomSheet(context: context, child: NoteView(note)); @override Widget build(BuildContext context) { @@ -25,9 +26,17 @@ class NoteView extends StatelessWidget { // Header ListTile( leading: ProfileImage( - name: note.teacher, + name: (note.teacher.isRenamed + ? note.teacher.renamedTo + : note.teacher.name) ?? + '', radius: 22.0, - backgroundColor: ColorUtils.stringToColor(note.teacher), + backgroundColor: ColorUtils.stringToColor( + (note.teacher.isRenamed + ? note.teacher.renamedTo + : note.teacher.name) ?? + '', + ), ), title: Text( note.title, @@ -36,7 +45,10 @@ class NoteView extends StatelessWidget { style: const TextStyle(fontWeight: FontWeight.w600), ), subtitle: Text( - note.teacher, + (note.teacher.isRenamed + ? note.teacher.renamedTo + : note.teacher.name) ?? + '', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500), diff --git a/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart b/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart index 85c00c5..384afa5 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:filcnaplo_kreta_api/models/category.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/models/subject.dart'; +import 'package:filcnaplo_kreta_api/models/teacher.dart'; import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart'; import 'package:filcnaplo_mobile_ui/common/material_action_button.dart'; import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart'; @@ -42,7 +43,8 @@ class _GradeCalculatorState extends State { padding: const EdgeInsets.only(bottom: 8.0), child: Text( "Grade Calculator".i18n, - style: const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600), + style: + const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600), ), ), @@ -63,7 +65,9 @@ class _GradeCalculatorState extends State { Container( width: 80.0, padding: const EdgeInsets.only(right: 12.0), - child: Center(child: GradeValueWidget(GradeValue(newValue.toInt(), "", "", 0))), + child: Center( + child: GradeValueWidget( + GradeValue(newValue.toInt(), "", "", 0))), ), ]), @@ -90,7 +94,8 @@ class _GradeCalculatorState extends State { child: Center( child: TextField( controller: _weightController, - style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 22.0), + style: const TextStyle( + fontWeight: FontWeight.w600, fontSize: 22.0), autocorrect: false, textAlign: TextAlign.right, keyboardType: TextInputType.number, @@ -122,7 +127,8 @@ class _GradeCalculatorState extends State { child: Text("Add Grade".i18n), onPressed: () { if (calculatorProvider.ghosts.length >= 50) { - ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(content: Text("limit_reached".i18n), context: context)); + ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( + content: Text("limit_reached".i18n), context: context)); return; } @@ -133,7 +139,11 @@ class _GradeCalculatorState extends State { grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate)); date = grades.first.date.add(const Duration(days: 7)); } else { - List grades = calculatorProvider.grades.where((e) => e.type == GradeType.midYear && e.subject == widget.subject).toList(); + List grades = calculatorProvider.grades + .where((e) => + e.type == GradeType.midYear && + e.subject == widget.subject) + .toList(); grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate)); date = grades.first.date; } @@ -143,8 +153,9 @@ class _GradeCalculatorState extends State { date: date, writeDate: date, description: "Ghost Grade".i18n, - value: GradeValue(newValue.toInt(), "", "", newWeight.toInt()), - teacher: "Ghost", + value: + GradeValue(newValue.toInt(), "", "", newWeight.toInt()), + teacher: Teacher.fromString("Ghost"), type: GradeType.ghost, form: "", subject: widget.subject, diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart index fd4d508..52b4a93 100644 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart @@ -13,6 +13,7 @@ import 'dart:math'; import 'package:filcnaplo_kreta_api/models/category.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/models/subject.dart'; +import 'package:filcnaplo_kreta_api/models/teacher.dart'; import 'package:flutter/foundation.dart' show listEquals; /// Generate list of grades that achieve the wanted goal. @@ -46,7 +47,8 @@ class GoalPlanner { for (int i = g.max; i >= 0; i--) { int newGradeToAdd = g.gradeToAdd - 1; - List newPlan = GoalPlannerHelper._addToList(g.plan, g.gradeToAdd, i); + List newPlan = + GoalPlannerHelper._addToList(g.plan, g.gradeToAdd, i); Avg newAvg = GoalPlannerHelper._addToAvg(g.currentAvg, g.gradeToAdd, i); int newN = GoalPlannerHelper.howManyNeeded( @@ -57,7 +59,7 @@ class GoalPlanner { id: '', date: DateTime(0), value: GradeValue(e, '', '', 100), - teacher: '', + teacher: Teacher.fromString(''), description: '', form: '', groupId: '', @@ -83,7 +85,8 @@ class GoalPlanner { grades, goal, ), - Avg(GoalPlannerHelper.averageEvals(grades), GoalPlannerHelper.weightSum(grades)), + Avg(GoalPlannerHelper.averageEvals(grades), + GoalPlannerHelper.weightSum(grades)), [], ), ); @@ -92,7 +95,9 @@ class GoalPlanner { for (var e in plans) { e.sum = e.plan.fold(0, (int a, b) => a + b); e.avg = e.sum / e.plan.length; - e.sigma = sqrt(e.plan.map((i) => pow(i - e.avg, 2)).fold(0, (num a, b) => a + b) / e.plan.length); + e.sigma = sqrt( + e.plan.map((i) => pow(i - e.avg, 2)).fold(0, (num a, b) => a + b) / + e.plan.length); } // filter without aggression @@ -141,7 +146,8 @@ class Plan { } class GoalPlannerHelper { - static Avg _addToAvg(Avg base, int grade, int n) => Avg((base.avg * base.n + grade * n) / (base.n + n), base.n + n); + static Avg _addToAvg(Avg base, int grade, int n) => + Avg((base.avg * base.n + grade * n) / (base.n + n), base.n + n); static List _addToList(List l, T e, int n) { if (n == 0) return l; @@ -158,15 +164,20 @@ class GoalPlannerHelper { if (avg >= goal) return 0; if (grade * 1.0 == goal) return -1; int candidate = (wsum * (avg - goal) / (goal - grade)).floor(); - return (candidate * grade + avg * wsum) / (candidate + wsum) < goal ? candidate + 1 : candidate; + return (candidate * grade + avg * wsum) / (candidate + wsum) < goal + ? candidate + 1 + : candidate; } static double averageEvals(List grades, {bool finalAvg = false}) { - double average = - grades.map((e) => e.value.value * e.value.weight / 100.0).fold(0.0, (double a, double b) => a + b) / weightSum(grades, finalAvg: finalAvg); + double average = grades + .map((e) => e.value.value * e.value.weight / 100.0) + .fold(0.0, (double a, double b) => a + b) / + weightSum(grades, finalAvg: finalAvg); return average.isNaN ? 0.0 : average; } - static double weightSum(List grades, {bool finalAvg = false}) => - grades.map((e) => finalAvg ? 1 : e.value.weight / 100).fold(0, (a, b) => a + b); + static double weightSum(List grades, {bool finalAvg = false}) => grades + .map((e) => finalAvg ? 1 : e.value.weight / 100) + .fold(0, (a, b) => a + b); } diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart index e03a35e..a14015b 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart @@ -99,13 +99,11 @@ class _ModifyTeacherNamesState extends State { void initState() { super.initState(); teachers = (Provider.of(context, listen: false) - .grades - .map((e) => e.teacher) - .toSet() - .toList() - ..sort((a, b) => a.compareTo(b))) - .map((e) => Teacher.fromString(e)) - .toList(); + .grades + .map((e) => e.teacher) + .toSet() + .toList() + ..sort((a, b) => a.name.compareTo(b.name))); print(teachers.map((e) => e.name)); user = Provider.of(context, listen: false); dbProvider = Provider.of(context, listen: false); From 6003f6fd2a9c24b610e797672c6b2b303f3e6bb3 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 15:32:23 +0200 Subject: [PATCH 04/14] actually finished teacher rename --- filcnaplo_kreta_api/lib/models/absence.dart | 30 +++++++---- .../lib/providers/absence_provider.dart | 37 ++++++++++--- .../lib/providers/grade_provider.dart | 5 ++ .../lib/providers/homework_provider.dart | 7 +++ .../lib/providers/timetable_provider.dart | 7 +++ .../common/widgets/absence/absence_view.dart | 53 ++++++++++++++----- 6 files changed, 108 insertions(+), 31 deletions(-) diff --git a/filcnaplo_kreta_api/lib/models/absence.dart b/filcnaplo_kreta_api/lib/models/absence.dart index c0000e0..d694c86 100644 --- a/filcnaplo_kreta_api/lib/models/absence.dart +++ b/filcnaplo_kreta_api/lib/models/absence.dart @@ -1,5 +1,6 @@ import "category.dart"; import "subject.dart"; +import "teacher.dart"; class Absence { Map? json; @@ -7,7 +8,7 @@ class Absence { DateTime date; int delay; DateTime submitDate; - String teacher; + Teacher teacher; Justification state; Category? justification; Category? type; @@ -41,8 +42,12 @@ class Absence { DateTime lessonEnd; int? lessonIndex; if (json["Ora"] != null) { - lessonStart = json["Ora"]["KezdoDatum"] != null ? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal() : DateTime(0); - lessonEnd = json["Ora"]["VegDatum"] != null ? DateTime.parse(json["Ora"]["VegDatum"]).toLocal() : DateTime(0); + lessonStart = json["Ora"]["KezdoDatum"] != null + ? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal() + : DateTime(0); + lessonEnd = json["Ora"]["VegDatum"] != null + ? DateTime.parse(json["Ora"]["VegDatum"]).toLocal() + : DateTime(0); lessonIndex = json["Ora"]["Oraszam"]; } else { lessonStart = DateTime(0); @@ -51,26 +56,33 @@ class Absence { return Absence( id: json["Uid"], - date: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0), + date: json["Datum"] != null + ? DateTime.parse(json["Datum"]).toLocal() + : DateTime(0), delay: json["KesesPercben"] ?? 0, - submitDate: json["KeszitesDatuma"] != null ? DateTime.parse(json["KeszitesDatuma"]).toLocal() : DateTime(0), - teacher: (json["RogzitoTanarNeve"] ?? "").trim(), + submitDate: json["KeszitesDatuma"] != null + ? DateTime.parse(json["KeszitesDatuma"]).toLocal() + : DateTime(0), + teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()), state: json["IgazolasAllapota"] == "Igazolt" ? Justification.excused : json["IgazolasAllapota"] == "Igazolando" ? Justification.pending : Justification.unexcused, - justification: json["IgazolasTipusa"] != null ? Category.fromJson(json["IgazolasTipusa"]) : null, + justification: json["IgazolasTipusa"] != null + ? Category.fromJson(json["IgazolasTipusa"]) + : null, type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null, mode: json["Mod"] != null ? Category.fromJson(json["Mod"]) : null, subject: Subject.fromJson(json["Tantargy"] ?? {}), lessonStart: lessonStart, lessonEnd: lessonEnd, lessonIndex: lessonIndex, - group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "", + group: + json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "", json: json, ); } } -enum Justification { excused, unexcused, pending } \ No newline at end of file +enum Justification { excused, unexcused, pending } diff --git a/filcnaplo_kreta_api/lib/providers/absence_provider.dart b/filcnaplo_kreta_api/lib/providers/absence_provider.dart index c683447..6450953 100644 --- a/filcnaplo_kreta_api/lib/providers/absence_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/absence_provider.dart @@ -27,7 +27,10 @@ class AbsenceProvider with ChangeNotifier { // Load absences from the database if (userId != null) { - var dbAbsences = await Provider.of(_context, listen: false).userQuery.getAbsences(userId: userId); + var dbAbsences = + await Provider.of(_context, listen: false) + .userQuery + .getAbsences(userId: userId); _absences = dbAbsences; await convertBySettings(); } @@ -36,12 +39,26 @@ class AbsenceProvider with ChangeNotifier { // for renamed subjects Future convertBySettings() async { final _database = Provider.of(_context, listen: false); - Map renamedSubjects = (await _database.query.getSettings(_database)).renamedSubjectsEnabled - ? await _database.userQuery.renamedSubjects(userId: Provider.of(_context, listen: false).user!.id) - : {}; + Map renamedSubjects = + (await _database.query.getSettings(_database)).renamedSubjectsEnabled + ? await _database.userQuery.renamedSubjects( + userId: + Provider.of(_context, listen: false).user!.id) + : {}; + Map renamedTeachers = + (await _database.query.getSettings(_database)).renamedTeachersEnabled + ? await _database.userQuery.renamedTeachers( + userId: + Provider.of(_context, listen: false).user!.id) + : {}; for (Absence absence in _absences) { - absence.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[absence.subject.id] : null; + absence.subject.renamedTo = renamedSubjects.isNotEmpty + ? renamedSubjects[absence.subject.id] + : null; + absence.teacher.renamedTo = renamedTeachers.isNotEmpty + ? renamedTeachers[absence.teacher.id] + : null; } notifyListeners(); @@ -53,9 +70,11 @@ class AbsenceProvider with ChangeNotifier { if (user == null) throw "Cannot fetch Absences for User null"; String iss = user.instituteCode; - List? absencesJson = await Provider.of(_context, listen: false).getAPI(KretaAPI.absences(iss)); + List? absencesJson = await Provider.of(_context, listen: false) + .getAPI(KretaAPI.absences(iss)); if (absencesJson == null) throw "Cannot fetch Absences for User ${user.id}"; - List absences = absencesJson.map((e) => Absence.fromJson(e)).toList(); + List absences = + absencesJson.map((e) => Absence.fromJson(e)).toList(); if (absences.isNotEmpty || _absences.isNotEmpty) await store(absences); } @@ -66,7 +85,9 @@ class AbsenceProvider with ChangeNotifier { if (user == null) throw "Cannot store Absences for User null"; String userId = user.id; - await Provider.of(_context, listen: false).userStore.storeAbsences(absences, userId: userId); + await Provider.of(_context, listen: false) + .userStore + .storeAbsences(absences, userId: userId); _absences = absences; await convertBySettings(); } diff --git a/filcnaplo_kreta_api/lib/providers/grade_provider.dart b/filcnaplo_kreta_api/lib/providers/grade_provider.dart index b86c440..de33a64 100644 --- a/filcnaplo_kreta_api/lib/providers/grade_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/grade_provider.dart @@ -83,10 +83,15 @@ class GradeProvider with ChangeNotifier { Map renamedSubjects = _settings.renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects(userId: _user.user!.id) : {}; + Map renamedTeachers = _settings.renamedTeachersEnabled + ? await _database.userQuery.renamedTeachers(userId: _user.user!.id) + : {}; for (Grade grade in _grades) { grade.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null; + grade.teacher.renamedTo = + renamedTeachers.isNotEmpty ? renamedTeachers[grade.teacher.id] : null; grade.value.value = _settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0; diff --git a/filcnaplo_kreta_api/lib/providers/homework_provider.dart b/filcnaplo_kreta_api/lib/providers/homework_provider.dart index 34cde5a..8f5fa4d 100644 --- a/filcnaplo_kreta_api/lib/providers/homework_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/homework_provider.dart @@ -48,11 +48,18 @@ class HomeworkProvider with ChangeNotifier { (await _database.query.getSettings(_database)).renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects(userId: _user.id!) : {}; + Map renamedTeachers = + (await _database.query.getSettings(_database)).renamedTeachersEnabled + ? await _database.userQuery.renamedTeachers(userId: _user.id!) + : {}; for (Homework homework in _homework) { homework.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[homework.subject.id] : null; + homework.teacher.renamedTo = renamedTeachers.isNotEmpty + ? renamedTeachers[homework.teacher.id] + : null; } notifyListeners(); diff --git a/filcnaplo_kreta_api/lib/providers/timetable_provider.dart b/filcnaplo_kreta_api/lib/providers/timetable_provider.dart index fdd35d1..841a35f 100644 --- a/filcnaplo_kreta_api/lib/providers/timetable_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/timetable_provider.dart @@ -40,11 +40,18 @@ class TimetableProvider with ChangeNotifier { (await _database.query.getSettings(_database)).renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects(userId: _user.id!) : {}; + Map renamedTeachers = + (await _database.query.getSettings(_database)).renamedTeachersEnabled + ? await _database.userQuery.renamedTeachers(userId: _user.id!) + : {}; for (Lesson lesson in _lessons.values.expand((e) => e)) { lesson.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[lesson.subject.id] : null; + lesson.teacher.renamedTo = renamedTeachers.isNotEmpty + ? renamedTeachers[lesson.teacher.id] + : null; } notifyListeners(); diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart index ef9c006..23ec169 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart @@ -17,19 +17,23 @@ import 'package:provider/provider.dart'; import 'absence_view.i18n.dart'; class AbsenceView extends StatelessWidget { - const AbsenceView(this.absence, {Key? key, this.outsideContext, this.viewable = false}) : super(key: key); + const AbsenceView(this.absence, + {Key? key, this.outsideContext, this.viewable = false}) + : super(key: key); final Absence absence; final BuildContext? outsideContext; final bool viewable; static show(Absence absence, {required BuildContext context}) { - showBottomCard(context: context, child: AbsenceView(absence, outsideContext: context)); + showBottomCard( + context: context, child: AbsenceView(absence, outsideContext: context)); } @override Widget build(BuildContext context) { - Color color = AbsenceTile.justificationColor(absence.state, context: context); + Color color = + AbsenceTile.justificationColor(absence.state, context: context); SettingsProvider settingsProvider = Provider.of(context); return Padding( @@ -41,7 +45,8 @@ class AbsenceView extends StatelessWidget { ListTile( visualDensity: VisualDensity.compact, contentPadding: const EdgeInsets.only(left: 16.0, right: 12.0), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0)), leading: Container( width: 44.0, height: 44.0, @@ -60,10 +65,18 @@ class AbsenceView extends StatelessWidget { absence.subject.renamedTo ?? absence.subject.name.capital(), maxLines: 2, overflow: TextOverflow.ellipsis, - style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), + style: TextStyle( + fontWeight: FontWeight.w700, + fontStyle: absence.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ), subtitle: Text( - absence.teacher, + (absence.teacher.isRenamed + ? absence.teacher.renamedTo + : absence.teacher.name) ?? + '', // DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date), style: const TextStyle(fontWeight: FontWeight.w500), ), @@ -77,12 +90,15 @@ class AbsenceView extends StatelessWidget { if (absence.delay > 0) Detail( title: "delay".i18n, - description: absence.delay.toString() + " " + "minutes".i18n.plural(absence.delay), + description: absence.delay.toString() + + " " + + "minutes".i18n.plural(absence.delay), ), if (absence.lessonIndex != null) Detail( title: "Lesson".i18n, - description: "${absence.lessonIndex}. (${absence.lessonStart.format(context, timeOnly: true)}" + description: + "${absence.lessonIndex}. (${absence.lessonStart.format(context, timeOnly: true)}" " - " "${absence.lessonEnd.format(context, timeOnly: true)})", ), @@ -91,13 +107,19 @@ class AbsenceView extends StatelessWidget { title: "Excuse".i18n, description: absence.justification?.description ?? "", ), - if (absence.mode != null) Detail(title: "Mode".i18n, description: absence.mode?.description ?? ""), - Detail(title: "Submit date".i18n, description: absence.submitDate.format(context)), + if (absence.mode != null) + Detail( + title: "Mode".i18n, + description: absence.mode?.description ?? ""), + Detail( + title: "Submit date".i18n, + description: absence.submitDate.format(context)), // Show in timetable if (!viewable) Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 6.0, top: 12.0), + padding: const EdgeInsets.only( + left: 16.0, right: 16.0, bottom: 6.0, top: 12.0), child: PanelActionButton( leading: const Icon(FeatherIcons.calendar), title: Text( @@ -109,12 +131,15 @@ class AbsenceView extends StatelessWidget { Navigator.of(context).pop(); if (outsideContext != null) { - ReverseSearch.getLessonByAbsence(absence, context).then((lesson) { + ReverseSearch.getLessonByAbsence(absence, context) + .then((lesson) { if (lesson != null) { TimetablePage.jump(outsideContext!, lesson: lesson); } else { - ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( - content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)), + ScaffoldMessenger.of(context) + .showSnackBar(CustomSnackBar( + content: Text("Cannot find lesson".i18n, + style: const TextStyle(color: Colors.white)), backgroundColor: AppColors.of(context).red, context: context, )); From 1ea0f958639344ffcbf0dcf1e021f17f30b11969 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 17:05:16 +0200 Subject: [PATCH 05/14] finished goal planner first half --- .../lib/pages/grades/grade_subject_view.dart | 41 +++-- .../ui/mobile/goal_planner/goal_input.dart | 66 +++++--- .../goal_planner_screen.i18n.dart | 39 +++++ .../ui/mobile/goal_planner/grade_display.dart | 8 +- .../ui/mobile/goal_planner/route_option.dart | 45 +++-- .../lib/ui/mobile/goal_planner/test.dart | 156 ++++++++++++++---- 6 files changed, 267 insertions(+), 88 deletions(-) create mode 100644 filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart diff --git a/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart b/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart index a638637..5b87c8e 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart @@ -21,6 +21,10 @@ import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_pro import 'package:filcnaplo_mobile_ui/pages/grades/grades_count.dart'; import 'package:filcnaplo_mobile_ui/pages/grades/graph.dart'; import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart'; +import 'package:filcnaplo_premium/ui/mobile/goal_planner/test.dart'; +import 'package:filcnaplo_premium/models/premium_scopes.dart'; +import 'package:filcnaplo_premium/providers/premium_provider.dart'; +import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_expandable_fab/flutter_expandable_fab.dart'; @@ -215,25 +219,25 @@ class _GradeSubjectViewState extends State { gradeCalc(context); }, ), - // FloatingActionButton.small( - // child: const Icon(FeatherIcons.flag, size: 20.0), - // backgroundColor: Theme.of(context).colorScheme.secondary, - // onPressed: () { - // if (!Provider.of(context, listen: false) - // .hasScope(PremiumScopes.goalPlanner)) { - // PremiumLockedFeatureUpsell.show( - // context: context, feature: PremiumFeature.goalplanner); - // return; - // } + FloatingActionButton.small( + child: const Icon(FeatherIcons.flag, size: 20.0), + backgroundColor: Theme.of(context).colorScheme.secondary, + onPressed: () { + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.goalPlanner)) { + PremiumLockedFeatureUpsell.show( + context: context, feature: PremiumFeature.goalplanner); + return; + } - // ScaffoldMessenger.of(context).showSnackBar( - // const SnackBar(content: Text("Hamarosan..."))); + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar(content: Text("Hamarosan..."))); - // Navigator.of(context).push(CupertinoPageRoute( - // builder: (context) => PremiumGoalplannerNewGoalScreen( - // subject: widget.subject))); - // }, - // ), + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => + GoalPlannerTest(subject: widget.subject))); + }, + ), ], ), ), @@ -263,7 +267,8 @@ class _GradeSubjectViewState extends State { subject: widget.subject, context: context), scrollController: _scrollController, title: widget.subject.renamedTo ?? widget.subject.name.capital(), - italic: settingsProvider.renamedSubjectsItalics && widget.subject.isRenamed, + italic: settingsProvider.renamedSubjectsItalics && + widget.subject.isRenamed, child: SubjectGradesContainer( child: CupertinoScrollbar( child: ListView.builder( diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart index 678d270..6fdfe54 100644 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart @@ -1,8 +1,15 @@ +import 'package:filcnaplo/models/settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; class GoalInput extends StatelessWidget { - const GoalInput({Key? key, required this.currentAverage, required this.value, required this.onChanged}) : super(key: key); + const GoalInput( + {Key? key, + required this.currentAverage, + required this.value, + required this.onChanged}) + : super(key: key); final double currentAverage; final double value; @@ -24,6 +31,8 @@ class GoalInput extends StatelessWidget { @override Widget build(BuildContext context) { + SettingsProvider settings = Provider.of(context); + List presets = [2, 3, 4, 5]; presets = presets.where((e) => gradeToAvg(e) > currentAverage).toList(); @@ -44,7 +53,8 @@ class GoalInput extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(right: 20.0), child: CustomPaint( - painter: GoalSliderPainter(value: (value - 1) / 4), + painter: GoalSliderPainter( + value: (value - 1) / 4, settings: settings), ), ), ), @@ -61,8 +71,9 @@ class GoalInput extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(99.0), - color: gradeColor(e).withOpacity(selected ? 1.0 : 0.2), - border: Border.all(color: gradeColor(e), width: 4), + color: + gradeColor(e, settings).withOpacity(selected ? 1.0 : 0.2), + border: Border.all(color: gradeColor(e, settings), width: 4), ), child: Material( type: MaterialType.transparency, @@ -70,11 +81,13 @@ class GoalInput extends StatelessWidget { borderRadius: BorderRadius.circular(99.0), onTap: () => setValue(gradeToAvg(e)), child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 24.0), + padding: const EdgeInsets.symmetric( + vertical: 2.0, horizontal: 24.0), child: Text( e.toString(), style: TextStyle( - color: selected ? Colors.white : gradeColor(e), + color: + selected ? Colors.white : gradeColor(e, settings), fontWeight: FontWeight.bold, fontSize: 24.0, ), @@ -93,8 +106,9 @@ class GoalInput extends StatelessWidget { class GoalSliderPainter extends CustomPainter { final double value; + final SettingsProvider settings; - GoalSliderPainter({required this.value}); + GoalSliderPainter({required this.value, required this.settings}); @override void paint(Canvas canvas, Size size) { @@ -115,21 +129,24 @@ class GoalSliderPainter extends CustomPainter { const Radius.circular(99.0), ), Paint() - ..shader = const LinearGradient(colors: [ - Color(0xffFF3B30), - Color(0xffFF9F0A), - Color(0xffFFD60A), - Color(0xff34C759), - Color(0xff247665), + ..shader = LinearGradient(colors: [ + settings.gradeColors[0], + settings.gradeColors[1], + settings.gradeColors[2], + settings.gradeColors[3], + settings.gradeColors[4], ]).createShader(rect), ); canvas.drawOval( - Rect.fromCircle(center: Offset(size.width * value, size.height / 2), radius: radius - cpadding), + Rect.fromCircle( + center: Offset(size.width * value, size.height / 2), + radius: radius - cpadding), Paint()..color = Colors.white, ); for (int i = 1; i < 4; i++) { canvas.drawOval( - Rect.fromCircle(center: Offset(size.width / 4 * i, size.height / 2), radius: 4), + Rect.fromCircle( + center: Offset(size.width / 4 * i, size.height / 2), radius: 4), Paint()..color = Colors.white.withOpacity(.5), ); } @@ -145,12 +162,19 @@ double gradeToAvg(int grade) { return grade - 0.5; } -Color gradeColor(int grade) { +Color gradeColor(int grade, SettingsProvider settings) { + // return [ + // const Color(0xffFF3B30), + // const Color(0xffFF9F0A), + // const Color(0xffFFD60A), + // const Color(0xff34C759), + // const Color(0xff247665), + // ].elementAt(grade.clamp(1, 5) - 1); return [ - const Color(0xffFF3B30), - const Color(0xffFF9F0A), - const Color(0xffFFD60A), - const Color(0xff34C759), - const Color(0xff247665), + settings.gradeColors[0], + settings.gradeColors[1], + settings.gradeColors[2], + settings.gradeColors[3], + settings.gradeColors[4], ].elementAt(grade.clamp(1, 5) - 1); } diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart new file mode 100644 index 0000000..6855167 --- /dev/null +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart @@ -0,0 +1,39 @@ +import 'package:i18n_extension/i18n_extension.dart'; + +extension Localization on String { + static final _t = Translations.byLocale("hu_hu") + + { + "en_en": { + "goal_planner_title": "Goal Planning", + "set_a_goal": "Your Goal", + "select_subject": "Subject", + "pick_route": "Pick a Route", + "track_it": "Track it!", + "recommended": "Recommended", + "fastest": "Fastest", + }, + "hu_hu": { + "goal_planner_title": "Cél követés", + "set_a_goal": "Kitűzött cél", + "select_subject": "Tantárgy", + "pick_route": "Válassz egy utat", + "track_it": "Követés!", + "recommended": "Ajánlott", + "fastest": "Leggyorsabb", + }, + "de_de": { + "goal_planner_title": "Zielplanung", + "set_a_goal": "Dein Ziel", + "select_subject": "Thema", + "pick_route": "Wähle einen Weg", + "track_it": "Verfolge es!", + "recommended": "Empfohlen", + "fastest": "Am schnellsten", + }, + }; + + String get i18n => localize(this, _t); + String fill(List params) => localizeFill(this, params); + String plural(int value) => localizePlural(value, this, _t); + String version(Object modifier) => localizeVersion(modifier, this, _t); +} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart index 8ac288d..1a1142a 100644 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart @@ -1,5 +1,7 @@ +import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class GradeDisplay extends StatelessWidget { const GradeDisplay({Key? key, required this.grade}) : super(key: key); @@ -8,12 +10,14 @@ class GradeDisplay extends StatelessWidget { @override Widget build(BuildContext context) { + SettingsProvider settings = Provider.of(context); + return Container( width: 36, height: 36, decoration: BoxDecoration( shape: BoxShape.circle, - color: gradeColor(grade).withOpacity(.3), + color: gradeColor(grade, settings).withOpacity(.3), ), child: Center( child: Text( @@ -21,7 +25,7 @@ class GradeDisplay extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.bold, fontSize: 22.0, - color: gradeColor(grade), + color: gradeColor(grade, settings), ), ), ), diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart index bc19b8c..8c7f9ca 100644 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart @@ -1,11 +1,18 @@ import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart'; +import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner_screen.i18n.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/grade_display.dart'; import 'package:flutter/material.dart'; enum RouteMark { recommended, fastest } class RouteOption extends StatelessWidget { - const RouteOption({Key? key, required this.plan, this.mark, this.selected = false, required this.onSelected}) : super(key: key); + const RouteOption( + {Key? key, + required this.plan, + this.mark, + this.selected = false, + required this.onSelected}) + : super(key: key); final Plan plan; final RouteMark? mark; @@ -17,20 +24,20 @@ class RouteOption extends StatelessWidget { switch (mark!) { case RouteMark.recommended: - return const Text("Recommended", style: style); + return Text("recommended".i18n, style: style); case RouteMark.fastest: - return const Text("Fastest", style: style); + return Text("fastest".i18n, style: style); } } - Color markColor() { + Color markColor(BuildContext context) { switch (mark) { case RouteMark.recommended: - return const Color(0xff6a63d4); + return const Color.fromARGB(255, 104, 93, 255); case RouteMark.fastest: - return const Color(0xffe9d524); + return const Color.fromARGB(255, 255, 91, 146); default: - return Colors.teal; + return Theme.of(context).colorScheme.primary; } } @@ -58,14 +65,16 @@ class RouteOption extends StatelessWidget { ], )); } else { - gradeWidgets.addAll(List.generate(count, (_) => GradeDisplay(grade: i))); + gradeWidgets + .addAll(List.generate(count, (_) => GradeDisplay(grade: i))); } if (count > 0) { gradeWidgets.add(SizedBox( height: 36.0, width: 32.0, - child: Center(child: Icon(Icons.add, color: Colors.black.withOpacity(.5))), + child: Center( + child: Icon(Icons.add, color: Colors.black.withOpacity(.5))), )); } } @@ -77,19 +86,23 @@ class RouteOption extends StatelessWidget { child: SizedBox( width: double.infinity, child: Card( - surfaceTintColor: selected ? markColor().withOpacity(.2) : Colors.white, + surfaceTintColor: + selected ? markColor(context).withOpacity(.2) : Colors.white, margin: EdgeInsets.zero, elevation: 5, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.0), - side: selected ? BorderSide(color: markColor(), width: 4.0) : BorderSide.none, + side: selected + ? BorderSide(color: markColor(context), width: 4.0) + : BorderSide.none, ), child: InkWell( borderRadius: BorderRadius.circular(16.0), onTap: onSelected, child: Padding( - padding: const EdgeInsets.only(top: 16.0, bottom: 16.0, left: 20.0, right: 12.0), + padding: const EdgeInsets.only( + top: 16.0, bottom: 16.0, left: 20.0, right: 12.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -98,12 +111,14 @@ class RouteOption extends StatelessWidget { Chip( label: markLabel(), visualDensity: VisualDensity.compact, - backgroundColor: selected ? markColor() : Colors.transparent, + backgroundColor: + selected ? markColor(context) : Colors.transparent, labelPadding: const EdgeInsets.symmetric(horizontal: 8.0), - labelStyle: TextStyle(color: selected ? Colors.white : null), + labelStyle: + TextStyle(color: selected ? Colors.white : null), shape: StadiumBorder( side: BorderSide( - color: markColor(), + color: markColor(context), width: 3.0, ), ), diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart index 3c2056f..626cdcb 100644 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart @@ -1,8 +1,15 @@ +import 'package:filcnaplo/helpers/subject.dart'; +import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; +import 'package:filcnaplo_kreta_api/models/subject.dart'; +import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; +import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart'; +import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner_screen.i18n.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/route_option.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; enum PlanResult { available, // There are possible solutions @@ -12,13 +19,25 @@ enum PlanResult { } class GoalPlannerTest extends StatefulWidget { - const GoalPlannerTest({Key? key}) : super(key: key); + final Subject subject; + + const GoalPlannerTest({Key? key, required this.subject}) : super(key: key); @override State createState() => _GoalPlannerTestState(); } class _GoalPlannerTestState extends State { + late GradeProvider gradeProvider; + late GradeCalculatorProvider calculatorProvider; + late SettingsProvider settingsProvider; + + bool gradeCalcMode = false; + + List getSubjectGrades(Subject subject) => !gradeCalcMode + ? gradeProvider.grades.where((e) => e.subject == subject).toList() + : calculatorProvider.grades.where((e) => e.subject == subject).toList(); + double goalValue = 4.0; List grades = []; @@ -39,11 +58,14 @@ class _GoalPlannerTestState extends State { final planner = GoalPlanner(goalValue, grades); final plans = planner.solve(); - plans.sort((a, b) => (a.avg - (2 * goalValue + 5) / 3).abs().compareTo(b.avg - (2 * goalValue + 5) / 3)); + plans.sort((a, b) => (a.avg - (2 * goalValue + 5) / 3) + .abs() + .compareTo(b.avg - (2 * goalValue + 5) / 3)); try { final singleSolution = plans.every((e) => e.sigma == 0); - recommended = plans.where((e) => singleSolution ? true : e.sigma > 0).first; + recommended = + plans.where((e) => singleSolution ? true : e.sigma > 0).first; plans.removeWhere((e) => e == recommended); } catch (_) {} @@ -78,8 +100,18 @@ class _GoalPlannerTestState extends State { return PlanResult.available; } + void getGrades() { + grades = getSubjectGrades(widget.subject).toList(); + } + @override Widget build(BuildContext context) { + gradeProvider = Provider.of(context); + calculatorProvider = Provider.of(context); + settingsProvider = Provider.of(context); + + getGrades(); + final currentAvg = GoalPlannerHelper.averageEvals(grades); final result = getResult(); @@ -87,33 +119,89 @@ class _GoalPlannerTestState extends State { return Scaffold( body: SafeArea( child: ListView( - padding: const EdgeInsets.all(20.0), + padding: const EdgeInsets.only( + left: 22.0, right: 22.0, top: 5.0, bottom: 220.0), children: [ - const Align( - alignment: Alignment.topLeft, - child: BackButton(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BackButton(), + Padding( + padding: const EdgeInsets.only(right: 15.0), + child: Text( + 'goal_planner_title'.i18n, + style: const TextStyle( + fontWeight: FontWeight.w500, fontSize: 18.0), + ), + ), + ], ), const SizedBox(height: 12.0), - const Text( - "Set a goal", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), - ), - const SizedBox(height: 4.0), - Text( - goalValue.toString(), - style: TextStyle( - fontWeight: FontWeight.w900, - fontSize: 42.0, - color: gradeColor(goalValue.round()), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "set_a_goal".i18n, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, + ), + ), + const SizedBox(height: 4.0), + Text( + goalValue.toString(), + style: TextStyle( + fontWeight: FontWeight.w900, + fontSize: 42.0, + color: gradeColor(goalValue.round(), settingsProvider), + ), + ), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "select_subject".i18n, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, + ), + ), + const SizedBox(height: 4.0), + Column( + children: [ + Icon( + SubjectIcon.resolveVariant( + context: context, + subject: widget.subject, + ), + size: 48.0, + ), + Text( + (widget.subject.isRenamed + ? widget.subject.renamedTo + : widget.subject.name) ?? + '', + style: const TextStyle( + fontSize: 17.0, + fontWeight: FontWeight.w500, + ), + ) + ], + ) + ], + ) + ], ), const SizedBox(height: 24.0), - const Text( - "Pick a route", - style: TextStyle( + Text( + "pick_route".i18n, + style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20.0, ), @@ -157,8 +245,9 @@ class _GoalPlannerTestState extends State { child: Container( padding: const EdgeInsets.only(top: 24.0), decoration: BoxDecoration( - color: const Color.fromARGB(255, 215, 255, 242), - borderRadius: const BorderRadius.vertical(top: Radius.circular(24.0)), + color: Colors.white, + borderRadius: + const BorderRadius.vertical(top: Radius.circular(24.0)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(.1), @@ -183,13 +272,16 @@ class _GoalPlannerTestState extends State { SizedBox( width: double.infinity, child: RawMaterialButton( - onPressed: () {}, - fillColor: const Color(0xff01342D), + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Hamarosan..."))); + }, + fillColor: Theme.of(context).colorScheme.primary, shape: const StadiumBorder(), padding: const EdgeInsets.symmetric(vertical: 8.0), - child: const Text( - "Track it!", - style: TextStyle( + child: Text( + "track_it".i18n, + style: const TextStyle( color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.w600, From f7d263570de8a5d971a9ef48e7e2d4d0bd346d4e Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 17:21:34 +0200 Subject: [PATCH 06/14] added beta chip, changed things --- filcnaplo_mobile_ui/lib/common/beta_chip.dart | 41 ++++++++++ .../lib/screens/settings/settings_screen.dart | 77 ++++++++----------- .../settings/settings_screen.i18n.dart | 2 +- .../lib/ui/mobile/goal_planner/test.dart | 17 +++- 4 files changed, 86 insertions(+), 51 deletions(-) create mode 100644 filcnaplo_mobile_ui/lib/common/beta_chip.dart diff --git a/filcnaplo_mobile_ui/lib/common/beta_chip.dart b/filcnaplo_mobile_ui/lib/common/beta_chip.dart new file mode 100644 index 0000000..9b4c0e8 --- /dev/null +++ b/filcnaplo_mobile_ui/lib/common/beta_chip.dart @@ -0,0 +1,41 @@ +import 'package:filcnaplo/theme/colors/colors.dart'; +import 'package:flutter/material.dart'; + +class BetaChip extends StatelessWidget { + const BetaChip({Key? key, this.disabled = false}) : super(key: key); + + final bool disabled; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 25, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + child: Padding( + padding: const EdgeInsets.only(left: 8, right: 8), + child: Center( + child: Text( + "BETA", + softWrap: true, + style: TextStyle( + fontSize: 10, + color: disabled + ? AppColors.of(context).text.withOpacity(.5) + : Colors.white, + fontWeight: FontWeight.w600, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ), + decoration: BoxDecoration( + color: !disabled + ? Theme.of(context).colorScheme.secondary + : AppColors.of(context).text.withOpacity(.25), + borderRadius: BorderRadius.circular(40), + ), + ), + ); + } +} diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart index 119819b..2eca057 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart @@ -15,6 +15,7 @@ import 'package:filcnaplo/models/user.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_mobile_ui/common/action_button.dart'; +import 'package:filcnaplo_mobile_ui/common/beta_chip.dart'; import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart'; import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; @@ -157,7 +158,8 @@ class _SettingsScreenState extends State void initState() { super.initState(); Future.delayed(Duration.zero, () { - futureRelease = Provider.of(context, listen: false).installedVersion(); + futureRelease = Provider.of(context, listen: false) + .installedVersion(); }); _hideContainersController = AnimationController( vsync: this, duration: const Duration(milliseconds: 200)); @@ -446,50 +448,32 @@ class _SettingsScreenState extends State contentPadding: const EdgeInsets.only(left: 12.0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), - title: Row(children: [ - Icon(FeatherIcons.messageSquare, - color: settings.notificationsEnabled ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25)), - const SizedBox(width: 14.0), - Text( - "notifications".i18n, - style: TextStyle( - color: AppColors.of(context).text.withOpacity( - settings.notificationsEnabled ? 1.0 : .5), - fontWeight: FontWeight.w600, - fontSize: 16.0, - ), - ), - const SizedBox( - width: 5, - ), - SizedBox( - height: 30, - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - child: Padding( - padding: - const EdgeInsets.only(left: 10, right: 10), - child: Center( - child: Text("BETA", - style: TextStyle( - fontSize: 9.1, - color: AppColors.of(context) - .text - .withOpacity( - settings.notificationsEnabled - ? 1.0 - : .5), - fontWeight: FontWeight.w600, - overflow: TextOverflow.ellipsis))), - ), - decoration: BoxDecoration( - color: settings.notificationsEnabled + title: Row( + children: [ + Icon(FeatherIcons.messageSquare, + color: settings.notificationsEnabled ? Theme.of(context).colorScheme.secondary - : AppColors.of(context).text.withOpacity(.25), - borderRadius: BorderRadius.circular(40)), + : AppColors.of(context) + .text + .withOpacity(.25)), + const SizedBox(width: 14.0), + Text( + "notifications".i18n, + style: TextStyle( + color: AppColors.of(context).text.withOpacity( + settings.notificationsEnabled ? 1.0 : .5), + fontWeight: FontWeight.w600, + fontSize: 16.0, + ), ), - ) - ]), + const SizedBox( + width: 5, + ), + BetaChip( + disabled: !settings.notificationsEnabled, + ), + ], + ), onChanged: (value) => settings.update(notificationsEnabled: value), ), @@ -927,7 +911,8 @@ class _SettingsScreenState extends State shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), title: Text("devmode".i18n, - style: const TextStyle(fontWeight: FontWeight.w500)), + style: + const TextStyle(fontWeight: FontWeight.w500)), onChanged: (v) => settings.update(developerMode: false), value: settings.developerMode, @@ -1002,8 +987,8 @@ class _SettingsScreenState extends State if (devmodeCountdown > 0) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( duration: const Duration(milliseconds: 200), - content: Text( - "devmoretaps".i18n.fill([devmodeCountdown])), + content: + Text("devmoretaps".i18n.fill([devmodeCountdown])), )); setState(() => devmodeCountdown--); diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart index ae7f40d..8c6336b 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart @@ -163,7 +163,7 @@ extension SettingsLocalization on String { "theme": "Thema", "color": "Farbe", "grade_colors": "Grad Farben", - "notifications": "Benachrichtigungen", + "notifications": "Mitteilung", "news": "Nachrichten", "extras": "Extras", "about": "Informationen", diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart index 626cdcb..7327b9e 100644 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart +++ b/filcnaplo_premium/lib/ui/mobile/goal_planner/test.dart @@ -10,6 +10,7 @@ import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner_screen.i18 import 'package:filcnaplo_premium/ui/mobile/goal_planner/route_option.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:filcnaplo_mobile_ui/common/beta_chip.dart'; enum PlanResult { available, // There are possible solutions @@ -128,10 +129,18 @@ class _GoalPlannerTestState extends State { const BackButton(), Padding( padding: const EdgeInsets.only(right: 15.0), - child: Text( - 'goal_planner_title'.i18n, - style: const TextStyle( - fontWeight: FontWeight.w500, fontSize: 18.0), + child: Row( + children: [ + Text( + 'goal_planner_title'.i18n, + style: const TextStyle( + fontWeight: FontWeight.w500, fontSize: 18.0), + ), + const SizedBox( + width: 5, + ), + const BetaChip(), + ], ), ), ], From 6d4939d88a74975310087184970c98e56e58a4be Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 17:24:47 +0200 Subject: [PATCH 07/14] changed teacher rename ico --- .../lib/ui/mobile/settings/modify_teacher_names.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart index a14015b..3e0b549 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart @@ -50,7 +50,7 @@ class MenuRenamedTeachers extends StatelessWidget { .withOpacity(settings.renamedTeachersEnabled ? 1.0 : .5)), ), leading: settings.renamedTeachersEnabled - ? const Icon(FeatherIcons.penTool) + ? const Icon(FeatherIcons.users) : Icon(FeatherIcons.penTool, color: AppColors.of(context).text.withOpacity(.25)), trailingDivider: true, From bc1ec4585528aa66072a9bcdca6c13d91f3690a8 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 19:12:03 +0200 Subject: [PATCH 08/14] simplified news provider --- .../lib/api/providers/news_provider.dart | 80 ++++++++++++------- filcnaplo/lib/database/init.dart | 5 +- filcnaplo/lib/models/news.dart | 6 ++ filcnaplo/lib/models/settings.dart | 24 +++--- .../screens/navigation/navigation_screen.dart | 5 +- .../lib/screens/news/news_view.dart | 43 +++++++--- 6 files changed, 111 insertions(+), 52 deletions(-) diff --git a/filcnaplo/lib/api/providers/news_provider.dart b/filcnaplo/lib/api/providers/news_provider.dart index 6f0f05d..45acb1e 100644 --- a/filcnaplo/lib/api/providers/news_provider.dart +++ b/filcnaplo/lib/api/providers/news_provider.dart @@ -1,7 +1,4 @@ // ignore_for_file: use_build_context_synchronously - -import 'dart:math'; - import 'package:filcnaplo/api/client.dart'; import 'package:filcnaplo/models/news.dart'; import 'package:filcnaplo/models/settings.dart'; @@ -11,7 +8,7 @@ import 'package:provider/provider.dart'; class NewsProvider extends ChangeNotifier { // Private late List _news; - late int _state; + //late int _state; late int _fresh; bool show = false; late BuildContext _context; @@ -30,56 +27,83 @@ class NewsProvider extends ChangeNotifier { Future restore() async { // Load news state from the database - var state_ = Provider.of(_context, listen: false).newsState; + var seen_ = Provider.of(_context, listen: false).seenNews; - if (state_ == -1) { + if (seen_.isEmpty) { var news_ = await FilcAPI.getNews(); if (news_ != null) { - state_ = news_.length; _news = news_; + show = true; } } - _state = state_; - Provider.of(_context, listen: false).update(newsState: _state); + //_state = seen_; + // Provider.of(_context, listen: false) + // .update(seenNewsId: news_.id); } Future fetch() async { var news_ = await FilcAPI.getNews(); if (news_ == null) return; + show = false; + _news = news_; - _fresh = news_.length - _state; - if (_fresh < 0) { - _state = news_.length; - Provider.of(_context, listen: false).update(newsState: _state); + for (var news in news_) { + if (news.expireDate.isAfter(DateTime.now()) && + Provider.of(_context, listen: false) + .seenNews + .contains(news.id) == + false) { + show = true; + Provider.of(_context, listen: false) + .update(seenNewsId: news.id); + + notifyListeners(); + } } + // print(news_.length); + // print(_state); - _fresh = max(_fresh, 0); + // _news = news_; + // _fresh = news_.length - _state; - if (_fresh > 0) { - show = true; - notifyListeners(); - } + // if (_fresh < 0) { + // _state = news_.length; + // Provider.of(_context, listen: false) + // .update(newsState: _state); + // } + + // _fresh = max(_fresh, 0); + + // if (_fresh > 0) { + // show = true; + // notifyListeners(); + // } + + // print(_fresh); + // print(_state); + // print(show); } void lock() => show = false; void release() { - if (_fresh == 0) return; + // if (_fresh == 0) return; - _fresh--; - _state++; + // _fresh--; + // //_state++; - Provider.of(_context, listen: false).update(newsState: _state); + // // Provider.of(_context, listen: false) + // // .update(seenNewsId: _state); - if (_fresh > 0) { - show = true; - } else { - show = false; - } + // if (_fresh > 0) { + // show = true; + // } else { + // show = false; + // } - notifyListeners(); + // notifyListeners(); } } diff --git a/filcnaplo/lib/database/init.dart b/filcnaplo/lib/database/init.dart index 410ad4f..2cede66 100644 --- a/filcnaplo/lib/database/init.dart +++ b/filcnaplo/lib/database/init.dart @@ -12,7 +12,8 @@ import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart'; const settingsDB = DatabaseStruct("settings", { "language": String, "start_page": int, "rounding": int, "theme": int, - "accent_color": int, "news": int, "news_state": int, "developer_mode": int, + "accent_color": int, "news": int, "seen_news": String, + "developer_mode": int, "update_channel": int, "config": String, "custom_accent_color": int, "custom_background_color": int, "custom_highlight_color": int, // general "grade_color1": int, "grade_color2": int, "grade_color3": int, @@ -92,6 +93,8 @@ Future initDB(DatabaseProvider database) async { "group_averages": "[]", // renamed subjects // non kreta data "renamed_subjects": "{}", + // renamed teachers // non kreta data + "renamed_teachers": "{}", // "subject_lesson_count": "{}", // non kreta data "last_seen_grade": 0, }); diff --git a/filcnaplo/lib/models/news.dart b/filcnaplo/lib/models/news.dart index c6f5bc0..e4e4ba9 100644 --- a/filcnaplo/lib/models/news.dart +++ b/filcnaplo/lib/models/news.dart @@ -1,30 +1,36 @@ class News { + String id; String title; String content; String link; String openLabel; String platform; bool emergency; + DateTime expireDate; Map? json; News({ + required this.id, required this.title, required this.content, required this.link, required this.openLabel, required this.platform, required this.emergency, + required this.expireDate, this.json, }); factory News.fromJson(Map json) { return News( + id: json["id"] ?? "", title: json["title"] ?? "", content: json["content"] ?? "", link: json["link"] ?? "", openLabel: json["open_label"] ?? "", platform: json["platform"] ?? "", emergency: json["emergency"] ?? false, + expireDate: DateTime.parse(json["expire_date"] ?? ''), json: json, ); } diff --git a/filcnaplo/lib/models/settings.dart b/filcnaplo/lib/models/settings.dart index 79ef40f..e95d7fc 100644 --- a/filcnaplo/lib/models/settings.dart +++ b/filcnaplo/lib/models/settings.dart @@ -29,7 +29,7 @@ class SettingsProvider extends ChangeNotifier { // zero is one, ... List _gradeColors; bool _newsEnabled; - int _newsState; + String _seenNews; bool _notificationsEnabled; /* notificationsBitfield values: @@ -81,7 +81,7 @@ class SettingsProvider extends ChangeNotifier { required AccentColor accentColor, required List gradeColors, required bool newsEnabled, - required int newsState, + required String seenNews, required bool notificationsEnabled, required int notificationsBitfield, required bool developerMode, @@ -118,7 +118,7 @@ class SettingsProvider extends ChangeNotifier { _accentColor = accentColor, _gradeColors = gradeColors, _newsEnabled = newsEnabled, - _newsState = newsState, + _seenNews = seenNews, _notificationsEnabled = notificationsEnabled, _notificationsBitfield = notificationsBitfield, _developerMode = developerMode, @@ -158,6 +158,8 @@ class SettingsProvider extends ChangeNotifier { log("[ERROR] SettingsProvider.fromMap: $e"); } + print(map['seen_news']); + return SettingsProvider( database: database, language: map["language"], @@ -173,7 +175,7 @@ class SettingsProvider extends ChangeNotifier { Color(map["grade_color5"]), ], newsEnabled: map["news"] == 1, - newsState: map["news_state"], + seenNews: map["seen_news"], notificationsEnabled: map["notifications"] == 1, notificationsBitfield: map["notifications_bitfield"], notificationPollInterval: map["notification_poll_interval"], @@ -214,7 +216,7 @@ class SettingsProvider extends ChangeNotifier { "theme": _theme.index, "accent_color": _accentColor.index, "news": _newsEnabled ? 1 : 0, - "news_state": _newsState, + "seen_news": _seenNews, "notifications": _notificationsEnabled ? 1 : 0, "notifications_bitfield": _notificationsBitfield, "developer_mode": _developerMode ? 1 : 0, @@ -266,7 +268,7 @@ class SettingsProvider extends ChangeNotifier { DarkMobileAppColors().gradeFive, ], newsEnabled: true, - newsState: -1, + seenNews: '', notificationsEnabled: true, notificationsBitfield: 255, developerMode: false, @@ -306,7 +308,7 @@ class SettingsProvider extends ChangeNotifier { AccentColor get accentColor => _accentColor; List get gradeColors => _gradeColors; bool get newsEnabled => _newsEnabled; - int get newsState => _newsState; + List get seenNews => _seenNews.split(','); bool get notificationsEnabled => _notificationsEnabled; int get notificationsBitfield => _notificationsBitfield; bool get developerMode => _developerMode; @@ -348,7 +350,7 @@ class SettingsProvider extends ChangeNotifier { AccentColor? accentColor, List? gradeColors, bool? newsEnabled, - int? newsState, + String? seenNewsId, bool? notificationsEnabled, int? notificationsBitfield, bool? developerMode, @@ -391,7 +393,11 @@ class SettingsProvider extends ChangeNotifier { if (newsEnabled != null && newsEnabled != _newsEnabled) { _newsEnabled = newsEnabled; } - if (newsState != null && newsState != _newsState) _newsState = newsState; + if (seenNewsId != null && !_seenNews.split(',').contains(seenNewsId)) { + var tempList = _seenNews.split(','); + tempList.add(seenNewsId); + _seenNews = tempList.join(','); + } if (notificationsEnabled != null && notificationsEnabled != _notificationsEnabled) { _notificationsEnabled = notificationsEnabled; diff --git a/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart b/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart index caec818..82635a8 100755 --- a/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart @@ -139,7 +139,6 @@ class NavigationScreenState extends State void initState() { super.initState(); - HomeWidget.setAppGroupId('hu.refilc.naplo.group'); _checkForWidgetLaunch(); @@ -199,9 +198,9 @@ class NavigationScreenState extends State // Show news WidgetsBinding.instance.addPostFrameCallback((_) { if (newsProvider.show) { - newsProvider.lock(); - NewsView.show(newsProvider.news[newsProvider.state], context: context) + NewsView.show(newsProvider.news[0], context: context) .then((value) => newsProvider.release()); + newsProvider.lock(); } }); diff --git a/filcnaplo_mobile_ui/lib/screens/news/news_view.dart b/filcnaplo_mobile_ui/lib/screens/news/news_view.dart index 8c37e51..ff12f7f 100755 --- a/filcnaplo_mobile_ui/lib/screens/news/news_view.dart +++ b/filcnaplo_mobile_ui/lib/screens/news/news_view.dart @@ -31,23 +31,30 @@ class NewsView extends StatelessWidget { physics: const BouncingScrollPhysics(), children: [ Padding( - padding: const EdgeInsets.only(left: 8.0, right: 6.0, top: 14.0, bottom: 8.0), + padding: const EdgeInsets.only( + left: 8.0, right: 6.0, top: 14.0, bottom: 8.0), child: Text( news.title, maxLines: 3, - style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 18.0), + style: const TextStyle( + fontWeight: FontWeight.w600, fontSize: 18.0), ), ), SelectableLinkify( text: news.content.escapeHtml(), - options: const LinkifyOptions(looseUrl: true, removeWww: true), + options: + const LinkifyOptions(looseUrl: true, removeWww: true), onOpen: (link) { launch( link.url, - customTabsOption: CustomTabsOption(showPageTitle: true, toolbarColor: Theme.of(context).scaffoldBackgroundColor), + customTabsOption: CustomTabsOption( + showPageTitle: true, + toolbarColor: + Theme.of(context).scaffoldBackgroundColor), ); }, - style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0), + style: const TextStyle( + fontWeight: FontWeight.w500, fontSize: 14.0), ), ], ), @@ -59,10 +66,15 @@ class NewsView extends StatelessWidget { children: [ if (news.link != "") DialogButton( - label: news.openLabel != "" ? news.openLabel : "open".i18n.toUpperCase(), + label: news.openLabel != "" + ? news.openLabel + : "open".i18n.toUpperCase(), onTap: () => launch( news.link, - customTabsOption: CustomTabsOption(showPageTitle: true, toolbarColor: Theme.of(context).scaffoldBackgroundColor), + customTabsOption: CustomTabsOption( + showPageTitle: true, + toolbarColor: + Theme.of(context).scaffoldBackgroundColor), ), ), DialogButton( @@ -78,12 +90,15 @@ class NewsView extends StatelessWidget { ); } - static Future show(News news, {required BuildContext context, bool force = false}) { + static Future show(News news, + {required BuildContext context, bool force = false}) { if (news.title == "") return Future.value(null); - bool popup = news.platform == '' || force; + bool popup = news.platform == 'all' || force; - if (Provider.of(context, listen: false).newsEnabled || news.emergency || force) { + if (Provider.of(context, listen: false).newsEnabled || + news.emergency || + force) { switch (news.platform.trim().toLowerCase()) { case "android": if (Platform.isAndroid) popup = true; @@ -100,6 +115,9 @@ class NewsView extends StatelessWidget { case "macos": if (Platform.isMacOS) popup = true; break; + case "all": + popup = true; + break; default: popup = true; } @@ -108,7 +126,10 @@ class NewsView extends StatelessWidget { } if (popup) { - return showDialog(context: context, builder: (context) => NewsView(news), barrierDismissible: true); + return showDialog( + context: context, + builder: (context) => NewsView(news), + barrierDismissible: true); } else { return Future.value(null); } From ee2a42bbe2007391b1c7297e3b6e503532ad0b8f Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 20:08:32 +0200 Subject: [PATCH 09/14] modified confetti --- filcnaplo_mobile_ui/lib/pages/home/home_page.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/filcnaplo_mobile_ui/lib/pages/home/home_page.dart b/filcnaplo_mobile_ui/lib/pages/home/home_page.dart index 449eb5d..02298d7 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/home_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/home_page.dart @@ -356,14 +356,14 @@ class _HomePageState extends State with TickerProviderStateMixin { // confetti 🎊 if (_confettiController != null) Align( - alignment: Alignment.bottomCenter, + alignment: Alignment.topCenter, child: ConfettiWidget( confettiController: _confettiController!, - blastDirection: -pi / 2, - emissionFrequency: 0.01, - numberOfParticles: 80, - maxBlastForce: 100, - minBlastForce: 90, + blastDirectionality: BlastDirectionality.explosive, + emissionFrequency: 0.02, + numberOfParticles: 120, + maxBlastForce: 20, + minBlastForce: 10, gravity: 0.3, minimumSize: const Size(5, 5), maximumSize: const Size(20, 20), From 8ff4f0b88f10cab23219599379b6adc66f3dafef Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 20:19:05 +0200 Subject: [PATCH 10/14] only show normal grade notifications --- .../lib/helpers/notification_helper.dart | 53 ++++++++++--------- filcnaplo_kreta_api/lib/models/grade.dart | 9 ++++ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/filcnaplo/lib/helpers/notification_helper.dart b/filcnaplo/lib/helpers/notification_helper.dart index ff0eb05..45684f3 100644 --- a/filcnaplo/lib/helpers/notification_helper.dart +++ b/filcnaplo/lib/helpers/notification_helper.dart @@ -42,31 +42,34 @@ class NotificationsHelper { // loop through grades and see which hasn't been seen yet for (Grade grade in grades) { - // if the grade was added over a week ago, don't show it to avoid notification spam - if (grade.seenDate.isAfter(lastSeenGrade) && - grade.date.difference(DateTime.now()).inDays * -1 < 7) { - // send notificiation about new grade - const AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails('GRADES', 'Jegyek', - channelDescription: 'Értesítés jegyek beírásakor', - importance: Importance.max, - priority: Priority.max, - color: Color(0xFF3D7BF4), - ticker: 'Jegyek'); - const NotificationDetails notificationDetails = - NotificationDetails(android: androidNotificationDetails); - await flutterLocalNotificationsPlugin.show( - // probably shouldn't use a random int - Random().nextInt(432234 * 2), - "title".i18n, - "body".i18n.fill([ - grade.value.value.toString(), - grade.subject.isRenamed && - settingsProvider.renamedSubjectsEnabled - ? grade.subject.renamedTo! - : grade.subject.name - ]), - notificationDetails); + // if grade is not a normal grade (1-5), don't show it + if ([1, 2, 3, 4, 5].contains(grade.value.value)) { + // if the grade was added over a week ago, don't show it to avoid notification spam + if (grade.seenDate.isAfter(lastSeenGrade) && + grade.date.difference(DateTime.now()).inDays * -1 < 7) { + // send notificiation about new grade + const AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails('GRADES', 'Jegyek', + channelDescription: 'Értesítés jegyek beírásakor', + importance: Importance.max, + priority: Priority.max, + color: Color(0xFF3D7BF4), + ticker: 'Jegyek'); + const NotificationDetails notificationDetails = + NotificationDetails(android: androidNotificationDetails); + await flutterLocalNotificationsPlugin.show( + // probably shouldn't use a random int + Random().nextInt(432234 * 2), + "title".i18n, + "body".i18n.fill([ + grade.value.value.toString(), + grade.subject.isRenamed && + settingsProvider.renamedSubjectsEnabled + ? grade.subject.renamedTo! + : grade.subject.name + ]), + notificationDetails); + } } } // set grade seen status diff --git a/filcnaplo_kreta_api/lib/models/grade.dart b/filcnaplo_kreta_api/lib/models/grade.dart index 249f303..80c7db7 100644 --- a/filcnaplo_kreta_api/lib/models/grade.dart +++ b/filcnaplo_kreta_api/lib/models/grade.dart @@ -101,7 +101,16 @@ class GradeValue { case "rossz": return 2; case "hanyag": + return 1; + // other + case "jeles": + return 5; + case "kozepes": + return 3; + case "elegseges": return 2; + case "elegtelen": + return 1; } } return _value; From f4bf509fa75c84b0190067d941a76f0ad1aea2a5 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 20:22:02 +0200 Subject: [PATCH 11/14] fixed icon --- .../lib/ui/mobile/settings/modify_teacher_names.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart index 3e0b549..ea89227 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart @@ -51,7 +51,7 @@ class MenuRenamedTeachers extends StatelessWidget { ), leading: settings.renamedTeachersEnabled ? const Icon(FeatherIcons.users) - : Icon(FeatherIcons.penTool, + : Icon(FeatherIcons.users, color: AppColors.of(context).text.withOpacity(.25)), trailingDivider: true, trailing: Switch( From d1f79a63a16b6bf421fb82aa5892ea4dc07cf565 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 20:25:01 +0200 Subject: [PATCH 12/14] added notifications to group --- filcnaplo/lib/helpers/notification_helper.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/filcnaplo/lib/helpers/notification_helper.dart b/filcnaplo/lib/helpers/notification_helper.dart index 45684f3..568bb62 100644 --- a/filcnaplo/lib/helpers/notification_helper.dart +++ b/filcnaplo/lib/helpers/notification_helper.dart @@ -49,12 +49,16 @@ class NotificationsHelper { grade.date.difference(DateTime.now()).inDays * -1 < 7) { // send notificiation about new grade const AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails('GRADES', 'Jegyek', - channelDescription: 'Értesítés jegyek beírásakor', - importance: Importance.max, - priority: Priority.max, - color: Color(0xFF3D7BF4), - ticker: 'Jegyek'); + AndroidNotificationDetails( + 'GRADES', + 'Jegyek', + channelDescription: 'Értesítés jegyek beírásakor', + importance: Importance.max, + priority: Priority.max, + color: Color(0xFF3D7BF4), + ticker: 'Jegyek', + groupKey: 'refilc.notifications.GRADES_GROUP', + ); const NotificationDetails notificationDetails = NotificationDetails(android: androidNotificationDetails); await flutterLocalNotificationsPlugin.show( From 625c43d786039cd373022e1f005ccfb8cb6ccf79 Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 21:12:17 +0200 Subject: [PATCH 13/14] updated privacy policy --- .../lib/screens/settings/privacy_view.dart | 25 ++++------ .../lib/screens/settings/settings_screen.dart | 49 +++++++++++++------ .../lib/ui/mobile/settings/icon_pack.dart | 11 +++-- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart b/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart index f6e34d8..89a9c30 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart @@ -28,27 +28,22 @@ class PrivacyView extends StatelessWidget { ), SelectableLinkify( text: """ -A reFilc egy kliensalkalmazás, segítségével az e-Kréta rendszeréből letöltheted és felhasználóbarát módon megjelenítheted az adataidat. -Tanulmányi adataid csak közvetlenül az alkalmazás és a Kréta-szerverek között közlekednek, titkosított kapcsolaton keresztül. + • A reFilc (továbbiakban alkalmazás) egy mobilos, asztali és webes kliensalkalmazás, segítségével az e-Kréta rendszeréből letöltheted és felhasználóbarát módon megjelenítheted az adataidat. Tanulmányi adataid csak közvetlenül az alkalmazás és a Kréta-szerverek között közlekednek, titkosított kapcsolaton keresztül. -A reFilc fejlesztői és üzemeltetői a tanulmányi adataidat semmilyen célból nem másolják, nem tárolják és harmadik félnek nem továbbítják. Ezeket így az e-Kréta Informatikai Zrt. kezeli, az ő tájékoztatójukat itt találod: https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4065038. -Azok törlésével vagy módosítával kapcsolatban keresd az osztályfőnöködet vagy az iskolád rendszergazdáját. + • A reFilc fejlesztői és/vagy üzemeltetői, valamint az alkalmazás a tanulmányi és személyes adataidat semmilyen célból és semmilyen körülmények között nem másolják, nem tárolják és harmadik félnek nem továbbítják. Ezeket így az Educational Development Informatikai Zrt. kezeli, az Ő adatkezeléssel kapcsolatos tájékoztatójukat itt találod: https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4065038 -Az alkalmazás névtelen használati statisztikákat gyűjt, ezek alapján tudjuk meghatározni a felhasználók és a telepítések számát. Ezt a beállításokban kikapcsolhatod. -Kérünk, hogy ha csak teheted, hagyd ezt a funkciót bekapcsolva. + • Azok törlésével vagy módosítával kapcsolatban keresd az osztályfőnöködet vagy az iskolád rendszergazdáját. -Amikor az alkalmazás hibába ütközik, lehetőség van hibajelentés küldésére. -Ez személyes- vagy tanulmányi adatokat nem tartalmaz, viszont részletes információval szolgál a hibáról és eszközödről. -A küldés előtt megjelenő képernyőn a te felelősséged átnézni a továbbításra kerülő adatsort. -A hibajelentéseket a reFilc fejlesztői felületén és egy privát Discord szobában tároljuk, ezekhez csak az app fejlesztői férnek hozzá. -Az alkalmazás belépéskor a GitHub API segítségével ellenőrzi, hogy elérhető-e új verzió, és kérésre innen is tölti le a telepítőt. + • Az alkalmazás névtelen használati statisztikákat gyűjt, ezek alapján tudjuk meghatározni a felhasználók és a telepítések számát, valamint az eszközük platformját. Ezt a beállításokban kikapcsolhatod. Kérünk, hogy ha csak teheted, hagyd ezt a funkciót bekapcsolva, hogy pontosabb információnk legyen a felhasználóink platform-megoszlásáról. -Ha az adataiddal kapcsolatban bármilyen kérdésed van (törlés, módosítás, adathordozás), keress minket a filcnaplo@filcnaplo.hu címen. + • Amikor az alkalmazás hibába ütközik, lehetőség van hibajelentés küldésére. Ez személyes- és/vagy tanulmányi adatokat nem tartalmaz, viszont részletes információval szolgál a hibáról, annak okáról és eszközödről. A küldés előtt megjelenő képernyőn a te felelősséged átnézni a továbbításra kerülő adatsort. A hibajelentéseket a reFilc fejlesztői felületén és egy privát Discord szobában tároljuk, ezekhez csak az app fejlesztői férnek hozzá. -Az alkalmazás használatával jelzed, hogy ezt a tájékoztatót tudomásul vetted. + • Az alkalmazás (az alábbi platformokon: Android, Linux, Windows) minden egyes indításakor a reFilc API, valamint a Github API segítségével ellenőrzi, hogy elérhető-e új verzió, és kérésre innen letölti és telepíti a frissítést. -Utolsó módosítás: 2023. 05. 28. - """, + • Amennyiben az adataiddal kapcsolatban bármilyen kérdésed van (megtekintés, törlés, módosítás, adathordozás), keress minket a social@refilc.hu e-mail címen, vagy Discord szerverünkön! + + • A kliensalkalmazás bármely eszközön és platformon történő használatával tudomásul vetted és elfogadod a jelen adatkezelési tájékoztatót. A reFilc csapata fenntartja a jogot a tájékoztató módosítására és a módosításokról nem köteles értesíteni a felhasználóit! +""", onOpen: (link) => launch(link.url, customTabsOption: CustomTabsOption( toolbarColor: Theme.of(context).scaffoldBackgroundColor, diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart index 2eca057..64d7c1f 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart @@ -386,7 +386,10 @@ class _SettingsScreenState extends State }, title: Text("language".i18n), leading: const Icon(FeatherIcons.globe), - trailing: Text(languageText), + trailing: Text( + languageText, + style: const TextStyle(fontSize: 14.0), + ), ), PanelButton( onPressed: () { @@ -395,7 +398,10 @@ class _SettingsScreenState extends State }, title: Text("startpage".i18n), leading: const Icon(FeatherIcons.play), - trailing: Text(startPageTitle.capital()), + trailing: Text( + startPageTitle.capital(), + style: const TextStyle(fontSize: 14.0), + ), ), PanelButton( onPressed: () { @@ -404,8 +410,10 @@ class _SettingsScreenState extends State }, title: Text("rounding".i18n), leading: const Icon(FeatherIcons.gitCommit), - trailing: - Text((settings.rounding / 10).toStringAsFixed(1)), + trailing: Text( + (settings.rounding / 10).toStringAsFixed(1), + style: const TextStyle(fontSize: 14.0), + ), ), PanelButton( onPressed: () { @@ -414,7 +422,10 @@ class _SettingsScreenState extends State }, title: Text("vibrate".i18n), leading: const Icon(FeatherIcons.radio), - trailing: Text(vibrateTitle), + trailing: Text( + vibrateTitle, + style: const TextStyle(fontSize: 14.0), + ), ), PanelButton( padding: const EdgeInsets.only(left: 14.0), @@ -621,7 +632,10 @@ class _SettingsScreenState extends State }, title: Text("theme".i18n), leading: const Icon(FeatherIcons.sun), - trailing: Text(themeModeText), + trailing: Text( + themeModeText, + style: const TextStyle(fontSize: 14.0), + ), ), PanelButton( onPressed: () async { @@ -805,6 +819,19 @@ class _SettingsScreenState extends State child: Panel( title: Text("about".i18n), child: Column(children: [ + PanelButton( + leading: const Icon(FeatherIcons.mail), + title: Text("news".i18n), + onPressed: () => _openNews(context), + ), + PanelButton( + leading: const Icon(FeatherIcons.lock), + title: Text("privacy".i18n), + // onPressed: () => launchUrl( + // Uri.parse("https://refilc.hu/privacy-policy"), + // mode: LaunchMode.inAppWebView), + onPressed: () => _openPrivacy(context), + ), PanelButton( leading: const Icon(FeatherIcons.atSign), title: const Text("Discord"), @@ -826,16 +853,6 @@ class _SettingsScreenState extends State Uri.parse("https://github.com/refilc"), mode: LaunchMode.externalApplication), ), - PanelButton( - leading: const Icon(FeatherIcons.mail), - title: Text("news".i18n), - onPressed: () => _openNews(context), - ), - PanelButton( - leading: const Icon(FeatherIcons.lock), - title: Text("privacy".i18n), - onPressed: () => _openPrivacy(context), - ), PanelButton( leading: const Icon(FeatherIcons.award), title: Text("licenses".i18n), diff --git a/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart b/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart index 438676e..2857946 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart @@ -19,8 +19,10 @@ class PremiumIconPackSelector extends StatelessWidget { return PanelButton( onPressed: () { - if (!Provider.of(context, listen: false).hasScope(PremiumScopes.customIcons)) { - PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.iconpack); + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.customIcons)) { + PremiumLockedFeatureUpsell.show( + context: context, feature: PremiumFeature.iconpack); return; } @@ -28,7 +30,10 @@ class PremiumIconPackSelector extends StatelessWidget { }, title: Text("icon_pack".i18n), leading: const Icon(FeatherIcons.grid), - trailing: Text(settings.iconPack.name.capital()), + trailing: Text( + settings.iconPack.name.capital(), + style: const TextStyle(fontSize: 14.0), + ), ); } } From 534a223cbf9145d0646ffd50363eaedbbfbf95dc Mon Sep 17 00:00:00 2001 From: ReinerRego <59338514+ReinerRego@users.noreply.github.com> Date: Sun, 27 Aug 2023 21:10:25 +0200 Subject: [PATCH 14/14] Update README.md --recursive kiszedve mert nem kell --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 117db86..c7e5ae9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ### Clone the project ```sh -git clone --recursive https://github.com/refilc/naplo +git clone https://github.com/refilc/naplo cd naplo ```