From 51ececc34a468f56cc15607dd2948725de21ec6d Mon Sep 17 00:00:00 2001 From: hihihaha <71922530+Monke14@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:11:22 +0100 Subject: [PATCH] fix notifications --- refilc/lib/database/init.dart | 7 + refilc/lib/database/query.dart | 5 +- refilc/lib/database/store.dart | 7 +- refilc/lib/helpers/notification_helper.dart | 1012 ++++++++--------- .../lib/helpers/notification_helper.i18n.dart | 14 +- refilc_kreta_api/lib/models/absence.dart | 23 +- .../lib/providers/grade_provider.dart | 7 +- 7 files changed, 531 insertions(+), 544 deletions(-) diff --git a/refilc/lib/database/init.dart b/refilc/lib/database/init.dart index ee963a7..fef7fd1 100644 --- a/refilc/lib/database/init.dart +++ b/refilc/lib/database/init.dart @@ -67,7 +67,11 @@ const userDataDB = DatabaseStruct("user_data", { // renamed teachers // non kreta data "renamed_teachers": String, // "subject_lesson_count": String, // non kreta data + // notifications and surprise grades // non kreta data "last_seen_grade": int, + "last_seen_absence": int, + "last_seen_message": int, + "last_seen_lesson": int, // goal planning // non kreta data "goal_plans": String, "goal_averages": String, @@ -131,6 +135,9 @@ Future initDB(DatabaseProvider database) async { "renamed_teachers": "{}", // "subject_lesson_count": "{}", // non kreta data "last_seen_grade": 0, + "last_seen_absence": 0, + "last_seen_message": 0, + "last_seen_lesson": 0, // goal planning // non kreta data "goal_plans": "{}", "goal_averages": "{}", diff --git a/refilc/lib/database/query.dart b/refilc/lib/database/query.dart index 4758ba2..aef1725 100644 --- a/refilc/lib/database/query.dart +++ b/refilc/lib/database/query.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:refilc/api/providers/database_provider.dart'; import 'package:refilc/models/linked_account.dart'; +import 'package:refilc/helpers/notification_helper.dart'; import 'package:refilc/models/self_note.dart'; import 'package:refilc/models/subject_lesson_count.dart'; import 'package:refilc/models/user.dart'; @@ -213,11 +214,11 @@ class UserDatabaseQuery { return lessonCount; } - Future lastSeenGrade({required String userId}) async { + Future lastSeen({required String userId, required LastSeenCategory category}) async { 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?; + int? lastSeenDate = userData.elementAt(0)["last_seen_${category.name}"] as int?; if (lastSeenDate == null) return DateTime(0); DateTime lastSeen = DateTime.fromMillisecondsSinceEpoch(lastSeenDate); return lastSeen; diff --git a/refilc/lib/database/store.dart b/refilc/lib/database/store.dart index 18728b3..2a64bfb 100644 --- a/refilc/lib/database/store.dart +++ b/refilc/lib/database/store.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:refilc/models/linked_account.dart'; +import 'package:refilc/helpers/notification_helper.dart'; import 'package:refilc/models/self_note.dart'; import 'package:refilc/models/subject_lesson_count.dart'; import 'package:refilc_kreta_api/models/week.dart'; @@ -129,10 +130,10 @@ class UserDatabaseStore { where: "id = ?", whereArgs: [userId]); } - Future storeLastSeenGrade(DateTime date, - {required String userId}) async { + Future storeLastSeen(DateTime date, + {required String userId, required LastSeenCategory category}) async { int lastSeenDate = date.millisecondsSinceEpoch; - await db.update("user_data", {"last_seen_grade": lastSeenDate}, + await db.update("user_data", {"last_seen_${category.name}": lastSeenDate}, where: "id = ?", whereArgs: [userId]); } diff --git a/refilc/lib/helpers/notification_helper.dart b/refilc/lib/helpers/notification_helper.dart index e21b4a3..4ae83df 100644 --- a/refilc/lib/helpers/notification_helper.dart +++ b/refilc/lib/helpers/notification_helper.dart @@ -3,6 +3,7 @@ import 'package:refilc/api/providers/status_provider.dart'; import 'package:refilc/api/providers/user_provider.dart'; import 'package:refilc/models/settings.dart'; import 'package:refilc/helpers/notification_helper.i18n.dart'; +import 'package:refilc/models/user.dart'; import 'package:refilc_kreta_api/client/api.dart'; import 'package:refilc_kreta_api/client/client.dart'; import 'package:refilc_kreta_api/models/absence.dart'; @@ -13,10 +14,17 @@ import 'package:refilc_kreta_api/providers/grade_provider.dart'; import 'package:refilc_kreta_api/providers/timetable_provider.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart' hide Message; -import 'package:i18n_extension/i18n_extension.dart'; +import 'package:i18n_extension/i18n_widget.dart'; import 'package:intl/intl.dart'; import 'package:refilc_kreta_api/models/message.dart'; +enum LastSeenCategory { + grade, + absence, + message, + lesson +} // didn't know a better place for this + class NotificationsHelper { late DatabaseProvider database; late SettingsProvider settingsProvider; @@ -25,41 +33,19 @@ class NotificationsHelper { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); - List combineLists( - List list1, - List list2, - K Function(T) keyExtractor, - ) { - Set uniqueKeys = {}; - List combinedList = []; - - for (T item in list1) { - K key = keyExtractor(item); - if (!uniqueKeys.contains(key)) { - uniqueKeys.add(key); - combinedList.add(item); - } - } - - for (T item in list2) { - K key = keyExtractor(item); - if (!uniqueKeys.contains(key)) { - uniqueKeys.add(key); - combinedList.add(item); - } - } - - return combinedList; - } String dayTitle(DateTime date) { try { - return DateFormat("EEEE", I18n.locale.languageCode).format(date); + String dayTitle = + DateFormat("EEEE", I18n.locale.languageCode).format(date); + dayTitle = dayTitle[0].toUpperCase() + + dayTitle.substring(1); // capitalize string + return dayTitle; } catch (e) { return "Unknown"; } } - + @pragma('vm:entry-point') void backgroundJob() async { // initialize providers @@ -69,540 +55,500 @@ class NotificationsHelper { userProvider = await database.query.getUsers(settingsProvider); if (userProvider.id != null && settingsProvider.notificationsEnabled) { - // refresh kreta login - final status = StatusProvider(); - kretaClient = KretaClient( - user: userProvider, settings: settingsProvider, status: status); - kretaClient.refreshLogin(); - if (settingsProvider.notificationsGradesEnabled) gradeNotification(); - if (settingsProvider.notificationsAbsencesEnabled) absenceNotification(); - if (settingsProvider.notificationsMessagesEnabled) messageNotification(); - if (settingsProvider.notificationsLessonsEnabled) lessonNotification(); + List users = userProvider.getUsers(); + + // Process notifications for each user asynchronously + await Future.forEach(users, (User user) async { + // Create a new instance of userProvider for each user + UserProvider userProviderForUser = await database.query.getUsers(settingsProvider); + userProviderForUser.setUser(user.id); + + // Refresh kreta login for current user + final status = StatusProvider(); + KretaClient kretaClientForUser = KretaClient( + user: userProviderForUser, settings: settingsProvider, status: status); + await kretaClientForUser.refreshLogin(); + + // Process notifications for current user + if (settingsProvider.notificationsGradesEnabled) await gradeNotification(userProviderForUser, kretaClientForUser); + if (settingsProvider.notificationsAbsencesEnabled) await absenceNotification(userProviderForUser, kretaClientForUser); + if (settingsProvider.notificationsMessagesEnabled) await messageNotification(userProviderForUser, kretaClientForUser); + if (settingsProvider.notificationsLessonsEnabled) await lessonNotification(userProviderForUser, kretaClientForUser); + }); } } - void gradeNotification() async { + Future gradeNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async { // fetch grades GradeProvider gradeProvider = GradeProvider( settings: settingsProvider, - user: userProvider, + user: currentuserProvider, database: database, - kreta: kretaClient); - gradeProvider.fetch(); - List grades = - await database.userQuery.getGrades(userId: userProvider.id ?? ""); - DateTime lastSeenGrade = - await database.userQuery.lastSeenGrade(userId: userProvider.id ?? ""); + kreta: currentKretaClient); + await gradeProvider.fetch(); + database.userQuery + .getGrades(userId: currentuserProvider.id!) + .then((grades) async { + DateTime lastSeenGrade = await database.userQuery.lastSeen( + userId: currentuserProvider.id!, category: LastSeenCategory.grade); + lastSeenGrade = lastSeenGrade.subtract(const Duration(minutes: 2)); // needed as lastSeenGrade somehow will be a bit in the future - // loop through grades and see which hasn't been seen yet - for (Grade grade in grades) { - // 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 - // it worked in reverse, cuz someone added a * -1 to it, but it has been fixed now :D - // old code below - // if (grade.seenDate.isAfter(lastSeenGrade) && - // grade.date.difference(DateTime.now()).inDays * -1 < 7) { - // new code from here :P - if (grade.seenDate.isAfter(lastSeenGrade) && - grade.date.difference(DateTime.now()).inDays < 7) { - // send notificiation about new grade - AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails( - 'GRADES', - 'Jegyek', - channelDescription: 'Értesítés jegyek beírásakor', - importance: Importance.max, - priority: Priority.max, - color: settingsProvider.customAccentColor, - ticker: 'Jegyek', - ); - NotificationDetails notificationDetails = - NotificationDetails(android: androidNotificationDetails); - if (userProvider.getUsers().length == 1) { - await flutterLocalNotificationsPlugin.show( - grade.id.hashCode, - "title_grade".i18n, - "body_grade".i18n.fill( - [ - grade.value.value.toString(), - grade.subject.isRenamed && - settingsProvider.renamedSubjectsEnabled - ? grade.subject.renamedTo! - : grade.subject.name - ], - ), - notificationDetails, - ); - } else { - // multiple users are added, also display student name - await flutterLocalNotificationsPlugin.show( - grade.id.hashCode, - "title_grade".i18n, - "body_grade_multiuser".i18n.fill( - [ - userProvider.displayName!, - grade.value.value.toString(), - grade.subject.isRenamed && - settingsProvider.renamedSubjectsEnabled - ? grade.subject.renamedTo! - : grade.subject.name - ], - ), - notificationDetails, + // loop through grades and see which hasn't been seen yet + for (Grade grade in grades) { + // 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 + // it worked in reverse, cuz someone added a * -1 to it, but it has been fixed now :D + // old code below + // if (grade.seenDate.isAfter(lastSeenGrade) && + // grade.date.difference(DateTime.now()).inDays * -1 < 7) { + // new code from here :P + if (grade.seenDate.isAfter(lastSeenGrade) && + grade.date.difference(DateTime.now()).inDays < 7) { + // send notificiation about new grade + AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails( + 'GRADES', + 'Jegyek', + channelDescription: 'Értesítés jegyek beírásakor', + importance: Importance.max, + priority: Priority.max, + color: settingsProvider.customAccentColor, + ticker: 'Jegyek', ); + NotificationDetails notificationDetails = + NotificationDetails(android: androidNotificationDetails); + if (currentuserProvider.getUsers().length == 1) { + await flutterLocalNotificationsPlugin.show( + grade.id.hashCode, + "title_grade".i18n, + "body_grade".i18n.fill( + [ + grade.value.value.toString(), + grade.subject.isRenamed && + settingsProvider.renamedSubjectsEnabled + ? grade.subject.renamedTo! + : grade.subject.name + ], + ), + notificationDetails, + ); + } else { + // multiple users are added, also display student name + await flutterLocalNotificationsPlugin.show( + grade.id.hashCode, + "title_grade".i18n, + "body_grade_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + grade.value.value.toString(), + grade.subject.isRenamed && + settingsProvider.renamedSubjectsEnabled + ? grade.subject.renamedTo! + : grade.subject.name + ], + ), + notificationDetails, + ); + } } } } - } - // set grade seen status - gradeProvider.seenAll(); + // set grade seen status + database.userStore.storeLastSeen(DateTime.now(), userId: currentuserProvider.id!, category: LastSeenCategory.grade); + }); } - void absenceNotification() async { + Future absenceNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async { // get absences from api - List? absenceJson = await kretaClient - .getAPI(KretaAPI.absences(userProvider.instituteCode ?? "")); - List storedAbsences = - await database.userQuery.getAbsences(userId: userProvider.id!); + List? absenceJson = await currentKretaClient + .getAPI(KretaAPI.absences(currentuserProvider.instituteCode ?? "")); if (absenceJson == null) { return; } - // format api absences to correct format while preserving isSeen value - List absences = absenceJson.map((e) { - Absence apiAbsence = Absence.fromJson(e); - Absence storedAbsence = storedAbsences.firstWhere( - (stored) => stored.id == apiAbsence.id, - orElse: () => apiAbsence); - apiAbsence.isSeen = storedAbsence.isSeen; - return apiAbsence; - }).toList(); - List modifiedAbsences = []; - if (absences != storedAbsences) { - // remove absences that are not new - absences.removeWhere((element) => storedAbsences.contains(element)); - for (Absence absence in absences) { - if (!absence.isSeen) { - absence.isSeen = true; - modifiedAbsences.add(absence); - AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails( - 'ABSENCES', - 'Hiányzások', - channelDescription: 'Értesítés hiányzások beírásakor', - importance: Importance.max, - priority: Priority.max, - color: settingsProvider.customAccentColor, - ticker: 'Hiányzások', + DateTime lastSeenAbsence = await database.userQuery + .lastSeen(userId: currentuserProvider.id!, category: LastSeenCategory.absence); + // format api absences + List absences = + absenceJson.map((e) => Absence.fromJson(e)).toList(); + for (Absence absence in absences) { + if (absence.date.isAfter(lastSeenAbsence)) { + AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails( + 'ABSENCES', + 'Hiányzások', + channelDescription: 'Értesítés hiányzások beírásakor', + importance: Importance.max, + priority: Priority.max, + color: settingsProvider.customAccentColor, + ticker: 'Hiányzások', + ); + NotificationDetails notificationDetails = + NotificationDetails(android: androidNotificationDetails); + if (currentuserProvider.getUsers().length == 1) { + await flutterLocalNotificationsPlugin.show( + absence.id.hashCode, + "title_absence" + .i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528 + "body_absence".i18n.fill( + [ + DateFormat("yyyy-MM-dd").format(absence.date), + absence.subject.isRenamed && + settingsProvider.renamedSubjectsEnabled + ? absence.subject.renamedTo! + : absence.subject.name + ], + ), + notificationDetails, + ); + } else { + await flutterLocalNotificationsPlugin.show( + absence.id.hashCode, + "title_absence" + .i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528 + "body_absence_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + DateFormat("yyyy-MM-dd").format(absence.date), + absence.subject.isRenamed && + settingsProvider.renamedSubjectsEnabled + ? absence.subject.renamedTo! + : absence.subject.name + ], + ), + notificationDetails, ); - NotificationDetails notificationDetails = - NotificationDetails(android: androidNotificationDetails); - if (userProvider.getUsers().length == 1) { - await flutterLocalNotificationsPlugin.show( - absence.id.hashCode, - "title_absence" - .i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528 - "body_absence".i18n.fill( - [ - DateFormat("yyyy-MM-dd").format(absence.date), - absence.subject.isRenamed && - settingsProvider.renamedSubjectsEnabled - ? absence.subject.renamedTo! - : absence.subject.name - ], - ), - notificationDetails, - ); - } else { - await flutterLocalNotificationsPlugin.show( - absence.id.hashCode, - "title_absence" - .i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528 - "body_absence_multiuser".i18n.fill( - [ - userProvider.displayName!, - DateFormat("yyyy-MM-dd").format(absence.date), - absence.subject.isRenamed && - settingsProvider.renamedSubjectsEnabled - ? absence.subject.renamedTo! - : absence.subject.name - ], - ), - notificationDetails, - ); - } } } } - // combine modified absences and storedabsences list and save them to the database - List combinedAbsences = combineLists( - modifiedAbsences, - storedAbsences, - (Absence absence) => absence.id, - ); - await database.userStore - .storeAbsences(combinedAbsences, userId: userProvider.id!); + await database.userStore.storeLastSeen(DateTime.now(), + userId: currentuserProvider.id!, category: LastSeenCategory.absence); } - void messageNotification() async { - // get messages from api - List? messageJson = - await kretaClient.getAPI(KretaAPI.messages("beerkezett")); - List storedmessages = - await database.userQuery.getMessages(userId: userProvider.id!); - if (messageJson == null) { - return; - } - // format api messages to correct format while preserving isSeen value - // Parse messages - List messages = []; - await Future.wait(List.generate(messageJson.length, (index) { - return () async { - Map message = messageJson.cast()[index]; - Map? innerMessageJson = await kretaClient - .getAPI(KretaAPI.message(message["azonosito"].toString())); - if (innerMessageJson != null) { - messages.add( - Message.fromJson(innerMessageJson, forceType: MessageType.inbox)); + Future messageNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async { + // get messages from api + List? messageJson = + await currentKretaClient.getAPI(KretaAPI.messages("beerkezett")); + if (messageJson == null) { + return; } - }(); - })); - - for (Message message in messages) { - for (Message storedMessage in storedmessages) { - if (message.id == storedMessage.id) { - message.isSeen = storedMessage.isSeen; - } - } - } - List modifiedmessages = []; - if (messages != storedmessages) { - // remove messages that are not new - messages.removeWhere((element) => storedmessages.contains(element)); - for (Message message in messages) { - if (!message.isSeen) { - message.isSeen = true; - modifiedmessages.add(message); - AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails( - 'MESSAGES', - 'Üzenetek', - channelDescription: 'Értesítés kapott üzenetekkor', - importance: Importance.max, - priority: Priority.max, - color: settingsProvider.customAccentColor, - ticker: 'Üzenetek', - ); - NotificationDetails notificationDetails = - NotificationDetails(android: androidNotificationDetails); - if (userProvider.getUsers().length == 1) { - await flutterLocalNotificationsPlugin.show( - message.id.hashCode, - message.author, - message.content.replaceAll(RegExp(r'<[^>]*>'), ''), - notificationDetails, - ); - } else { - await flutterLocalNotificationsPlugin.show( - message.id.hashCode, - "(${userProvider.displayName!}) ${message.author}", - message.content.replaceAll(RegExp(r'<[^>]*>'), ''), - notificationDetails, - ); - } - } - } - } - // combine modified messages and storedmessages list and save them to the database - List combinedmessages = combineLists( - modifiedmessages, - storedmessages, - (Message message) => message.id, - ); - await database.userStore - .storeMessages(combinedmessages, userId: userProvider.id!); - } - - void lessonNotification() async { - // get lesson from api - TimetableProvider timetableProvider = TimetableProvider( - user: userProvider, database: database, kreta: kretaClient); - List storedlessons = - timetableProvider.lessons[Week.current()] ?? []; - List? apilessons = timetableProvider.getWeek(Week.current()) ?? []; - for (Lesson lesson in apilessons) { - for (Lesson storedLesson in storedlessons) { - if (lesson.id == storedLesson.id) { - lesson.isSeen = storedLesson.isSeen; - } - } - } - List modifiedlessons = []; - if (apilessons != storedlessons) { - // remove lessons that are not new - apilessons.removeWhere((element) => storedlessons.contains(element)); - for (Lesson lesson in apilessons) { - if (!lesson.isSeen && lesson.isChanged) { - lesson.isSeen = true; - modifiedlessons.add(lesson); - AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails( - 'LESSONS', - 'Órák', - channelDescription: - 'Értesítés órák elmaradásáról, helyettesítésről', - importance: Importance.max, - priority: Priority.max, - color: settingsProvider.customAccentColor, - ticker: 'Órák', - ); - NotificationDetails notificationDetails = - NotificationDetails(android: androidNotificationDetails); - if (userProvider.getUsers().length == 1) { - if (lesson.status?.name == "Elmaradt") { - switch (I18n.localeStr) { - case "en_en": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_canceled".i18n.fill( - [ - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date) - ], - ), - notificationDetails, - ); - break; - } - case "hu_hu": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_canceled".i18n.fill( - [ - dayTitle(lesson.date), - lesson.lessonIndex, - lesson.name - ], - ), - notificationDetails, - ); - break; - } - default: - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_canceled".i18n.fill( - [ - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date) - ], - ), - notificationDetails, - ); - break; - } - } - } else if (lesson.substituteTeacher?.name != "") { - switch (I18n.localeStr) { - case "en_en": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_substituted".i18n.fill( - [ - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date), - lesson.substituteTeacher!.isRenamed - ? lesson.substituteTeacher!.renamedTo! - : lesson.substituteTeacher!.name - ], - ), - notificationDetails, - ); - break; - } - case "hu_hu": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_substituted".i18n.fill( - [ - dayTitle(lesson.date), - lesson.lessonIndex, - lesson.name, - lesson.substituteTeacher!.isRenamed - ? lesson.substituteTeacher!.renamedTo! - : lesson.substituteTeacher!.name - ], - ), - notificationDetails, - ); - break; - } - default: - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_substituted".i18n.fill( - [ - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date), - lesson.substituteTeacher!.isRenamed - ? lesson.substituteTeacher!.renamedTo! - : lesson.substituteTeacher!.name - ], - ), - notificationDetails, - ); - break; - } - } + // format api messages to correct format + // Parse messages + List messages = []; + await Future.wait(List.generate(messageJson.length, (index) { + return () async { + Map message = messageJson.cast()[index]; + Map? innerMessageJson = await currentKretaClient + .getAPI(KretaAPI.message(message["azonosito"].toString())); + await Future.delayed(const Duration(seconds: 1)); + if (innerMessageJson != null) { + messages.add(Message.fromJson(innerMessageJson, + forceType: MessageType.inbox)); } - } else { - if (lesson.status?.name == "Elmaradt") { - switch (I18n.localeStr) { - case "en_en": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_canceled".i18n.fill( - [ - userProvider.displayName!, - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date) - ], - ), - notificationDetails, - ); - break; - } - case "hu_hu": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_canceled".i18n.fill( - [ - userProvider.displayName!, - dayTitle(lesson.date), - lesson.lessonIndex, - lesson.name - ], - ), - notificationDetails, - ); - break; - } - default: - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_canceled".i18n.fill( - [ - userProvider.displayName!, - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date) - ], - ), - notificationDetails, - ); - break; - } + }(); + })); + + DateTime lastSeenMessage = await database.userQuery.lastSeen( + userId: currentuserProvider.id!, category: LastSeenCategory.message); + + for (Message message in messages) { + if (message.date.isAfter(lastSeenMessage)) { + AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails( + 'MESSAGES', + 'Üzenetek', + channelDescription: 'Értesítés kapott üzenetekkor', + importance: Importance.max, + priority: Priority.max, + color: settingsProvider.customAccentColor, + ticker: 'Üzenetek', + ); + NotificationDetails notificationDetails = + NotificationDetails(android: androidNotificationDetails); + if (currentuserProvider.getUsers().length == 1) { + await flutterLocalNotificationsPlugin.show( + message.id.hashCode, + message.author, + message.content.replaceAll(RegExp(r'<[^>]*>'), ''), + notificationDetails, + ); + } else { + await flutterLocalNotificationsPlugin.show( + message.id.hashCode, + "(${currentuserProvider.displayName!}) ${message.author}", + message.content.replaceAll(RegExp(r'<[^>]*>'), ''), + notificationDetails, + ); + } + } + } + await database.userStore.storeLastSeen(DateTime.now(), + userId: currentuserProvider.id!, category: LastSeenCategory.message); + } + + Future lessonNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async { + // get lessons from api + TimetableProvider timetableProvider = TimetableProvider( + user: currentuserProvider, database: database, kreta: currentKretaClient); + await timetableProvider.restoreUser(); + await timetableProvider.fetch(week: Week.current()); + List apilessons = + timetableProvider.getWeek(Week.current()) ?? []; + + DateTime lastSeenLesson = await database.userQuery.lastSeen( + userId: currentuserProvider.id!, category: LastSeenCategory.lesson); + + for (Lesson lesson in apilessons) { + if (lesson.date.isAfter(lastSeenLesson)) { + AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails( + 'LESSONS', + 'Órák', + channelDescription: + 'Értesítés órák elmaradásáról, helyettesítésről', + importance: Importance.max, + priority: Priority.max, + color: settingsProvider.customAccentColor, + ticker: 'Órák', + ); + NotificationDetails notificationDetails = + NotificationDetails(android: androidNotificationDetails); + if (currentuserProvider.getUsers().length == 1) { + if (lesson.status?.name == "Elmaradt") { + switch (I18n.localeStr) { + case "en_en": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_canceled".i18n.fill( + [ + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date) + ], + ), + notificationDetails, + ); + break; + } + case "hu_hu": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_canceled".i18n.fill( + [ + dayTitle(lesson.date), + lesson.lessonIndex, + lesson.name + ], + ), + notificationDetails, + ); + break; + } + default: + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_canceled".i18n.fill( + [ + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date) + ], + ), + notificationDetails, + ); + break; + } + } + } else if (lesson.substituteTeacher?.name != "") { + switch (I18n.localeStr) { + case "en_en": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_substituted".i18n.fill( + [ + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date), + lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo! + : lesson.substituteTeacher!.name + ], + ), + notificationDetails, + ); + break; + } + case "hu_hu": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_substituted".i18n.fill( + [ + dayTitle(lesson.date), + lesson.lessonIndex, + lesson.name, + lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo! + : lesson.substituteTeacher!.name + ], + ), + notificationDetails, + ); + break; + } + default: + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_substituted".i18n.fill( + [ + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date), + lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo! + : lesson.substituteTeacher!.name + ], + ), + notificationDetails, + ); + break; + } + } } - } else if (lesson.substituteTeacher?.name != "") { - switch (I18n.localeStr) { - case "en_en": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_substituted".i18n.fill( - [ - userProvider.displayName!, - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date), - lesson.substituteTeacher!.isRenamed - ? lesson.substituteTeacher!.renamedTo! - : lesson.substituteTeacher!.name - ], - ), - notificationDetails, - ); - break; - } - case "hu_hu": - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_substituted".i18n.fill( - [ - userProvider.displayName!, - dayTitle(lesson.date), - lesson.lessonIndex, - lesson.name, - lesson.substituteTeacher!.isRenamed - ? lesson.substituteTeacher!.renamedTo! - : lesson.substituteTeacher!.name - ], - ), - notificationDetails, - ); - break; - } - default: - { - await flutterLocalNotificationsPlugin.show( - lesson.id.hashCode, - "title_lesson".i18n, - "body_lesson_substituted".i18n.fill( - [ - userProvider.displayName!, - lesson.lessonIndex, - lesson.name, - dayTitle(lesson.date), - lesson.substituteTeacher!.isRenamed - ? lesson.substituteTeacher!.renamedTo! - : lesson.substituteTeacher!.name - ], - ), - notificationDetails, - ); - break; - } + } else { + if (lesson.status?.name == "Elmaradt") { + switch (I18n.localeStr) { + case "en_en": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_canceled_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date) + ], + ), + notificationDetails, + ); + break; + } + case "hu_hu": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_canceled_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + dayTitle(lesson.date), + lesson.lessonIndex, + lesson.name + ], + ), + notificationDetails, + ); + break; + } + default: + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_canceled_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date) + ], + ), + notificationDetails, + ); + break; + } + } + } else if (lesson.substituteTeacher?.name != "") { + switch (I18n.localeStr) { + case "en_en": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_substituted_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date), + lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo! + : lesson.substituteTeacher!.name + ], + ), + notificationDetails, + ); + break; + } + case "hu_hu": + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_substituted_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + dayTitle(lesson.date), + lesson.lessonIndex, + lesson.name, + lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo! + : lesson.substituteTeacher!.name + ], + ), + notificationDetails, + ); + break; + } + default: + { + await flutterLocalNotificationsPlugin.show( + lesson.id.hashCode, + "title_lesson".i18n, + "body_lesson_substituted_multiuser".i18n.fill( + [ + currentuserProvider.displayName!, + lesson.lessonIndex, + lesson.name, + dayTitle(lesson.date), + lesson.substituteTeacher!.isRenamed + ? lesson.substituteTeacher!.renamedTo! + : lesson.substituteTeacher!.name + ], + ), + notificationDetails, + ); + break; + } + } } } } } + await database.userStore.storeLastSeen(DateTime.now(), + userId: currentuserProvider.id!, category: LastSeenCategory.lesson); } - // combine modified lesson and storedlesson list and save them to the database - List combinedlessons = combineLists( - modifiedlessons, - storedlessons, - (Lesson message) => message.id, - ); - Map> timetableLessons = timetableProvider.lessons; - timetableLessons[Week.current()] = combinedlessons; - await database.userStore - .storeLessons(timetableLessons, userId: userProvider.id!); } - } -} diff --git a/refilc/lib/helpers/notification_helper.i18n.dart b/refilc/lib/helpers/notification_helper.i18n.dart index 37e6ff0..bc4b866 100644 --- a/refilc/lib/helpers/notification_helper.i18n.dart +++ b/refilc/lib/helpers/notification_helper.i18n.dart @@ -1,4 +1,5 @@ import 'package:i18n_extension/i18n_extension.dart'; +import 'package:refilc/api/providers/database_provider.dart'; extension Localization on String { static final _t = Translations.byLocale("hu_hu") + @@ -43,8 +44,19 @@ extension Localization on String { "body_lesson_substituted_multiuser": "(%s) Lektion Nr. %s (%s) wird am %s durch %s ersetzt" }, }; + String get i18n { + // very hacky way to get app language in notifications + // i18n does not like being in background functions (it cannot retrieve locale sometimes) + final DatabaseProvider _databaseProvider = DatabaseProvider(); + _databaseProvider.init().then((value) { + _databaseProvider.query.getSettings(_databaseProvider).then((settings) { + return localize(this, _t, locale: "${settings.language}_${settings.language.toUpperCase()}"); + }); + }); - String get i18n => localize(this, _t); + + return 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/refilc_kreta_api/lib/models/absence.dart b/refilc_kreta_api/lib/models/absence.dart index 232f671..6a08046 100644 --- a/refilc_kreta_api/lib/models/absence.dart +++ b/refilc_kreta_api/lib/models/absence.dart @@ -50,6 +50,7 @@ class Absence { DateTime lessonStart; DateTime lessonEnd; int? lessonIndex; + bool isSeen = json["isSeen"] ?? false; if (json["Ora"] != null) { lessonStart = json["Ora"]["KezdoDatum"] != null ? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal() @@ -62,7 +63,6 @@ class Absence { lessonStart = DateTime(0); lessonEnd = DateTime(0); } - return Absence( id: json["Uid"], date: json["Datum"] != null @@ -89,10 +89,29 @@ class Absence { lessonIndex: lessonIndex, group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "", - isSeen: false, + isSeen: json["isSeen"] ?? false, json: json, ); } + Map toJson() { + return { + "id": id, + "date": date.toIso8601String(), + "delay": delay, + "submitDate": submitDate.toIso8601String(), + "teacher": teacher, + "state": state.toString().split('.').last, + "justification": justification, + "type": type, + "mode": mode, + "subject": subject, + "lessonStart": lessonStart.toIso8601String(), + "lessonEnd": lessonEnd.toIso8601String(), + "lessonIndex": lessonIndex, + "group": group, + "isSeen": isSeen, + }; + } } enum Justification { excused, unexcused, pending } diff --git a/refilc_kreta_api/lib/providers/grade_provider.dart b/refilc_kreta_api/lib/providers/grade_provider.dart index c3918ba..2d61b28 100644 --- a/refilc_kreta_api/lib/providers/grade_provider.dart +++ b/refilc_kreta_api/lib/providers/grade_provider.dart @@ -1,5 +1,6 @@ import 'package:refilc/api/providers/user_provider.dart'; import 'package:refilc/api/providers/database_provider.dart'; +import 'package:refilc/helpers/notification_helper.dart'; import 'package:refilc/models/settings.dart'; import 'package:refilc/models/user.dart'; import 'package:refilc_kreta_api/client/api.dart'; @@ -49,7 +50,7 @@ class GradeProvider with ChangeNotifier { String? userId = _user.id; if (userId != null) { final userStore = _database.userStore; - userStore.storeLastSeenGrade(DateTime.now(), userId: userId); + userStore.storeLastSeen(DateTime.now(), userId: userId, category: LastSeenCategory.grade); _lastSeen = DateTime.now(); } } @@ -58,7 +59,7 @@ class GradeProvider with ChangeNotifier { String? userId = _user.id; if (userId != null) { final userStore = _database.userStore; - userStore.storeLastSeenGrade(DateTime(1969), userId: userId); + userStore.storeLastSeen(DateTime(1969), userId: userId, category: LastSeenCategory.grade); _lastSeen = DateTime(1969); } } @@ -74,7 +75,7 @@ class GradeProvider with ChangeNotifier { await convertBySettings(); _groupAvg = await userQuery.getGroupAverages(userId: userId); notifyListeners(); - DateTime lastSeenDB = await userQuery.lastSeenGrade(userId: userId); + DateTime lastSeenDB = await userQuery.lastSeen(userId: userId, category: LastSeenCategory.grade); if (lastSeenDB.millisecondsSinceEpoch == 0 || lastSeenDB.year == 0 || !_settings.gradeOpeningFun) {