From e64ab75753431db900b8d997ad8197819d5138ed Mon Sep 17 00:00:00 2001 From: Kima Date: Sat, 26 Aug 2023 14:56:57 +0200 Subject: [PATCH] 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)), + ), + ); + } +}