From e280227ee1c6b29e9330af1e61da2547675ed0e0 Mon Sep 17 00:00:00 2001 From: hihihaha Date: Tue, 29 Aug 2023 14:32:40 +0200 Subject: [PATCH] add notification for messages --- filcnaplo/lib/database/init.dart | 2 +- .../lib/helpers/notification_helper.dart | 80 +++++++++++++++++-- .../lib/helpers/notification_helper.i18n.dart | 4 +- filcnaplo/lib/models/settings.dart | 12 +++ .../lib/ui/widgets/grade/grade_tile.dart | 4 +- filcnaplo_kreta_api/lib/models/absence.dart | 6 +- filcnaplo_kreta_api/lib/models/message.dart | 10 +++ .../settings/notifications_screen.dart | 31 ++++++- .../settings/notifications_screen.i18n.dart | 9 ++- 9 files changed, 141 insertions(+), 17 deletions(-) diff --git a/filcnaplo/lib/database/init.dart b/filcnaplo/lib/database/init.dart index fca6a84..852b1dc 100644 --- a/filcnaplo/lib/database/init.dart +++ b/filcnaplo/lib/database/init.dart @@ -20,7 +20,7 @@ const settingsDB = DatabaseStruct("settings", { "grade_color4": int, "grade_color5": int, // grade colors "vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int, "notifications": int, "notifications_bitfield": int, - "notification_poll_interval": int, "notifications_grades":int, "notifications_absences":int, // notifications + "notification_poll_interval": int, "notifications_grades":int, "notifications_absences":int, "notifications_messages": int, // notifications "x_filc_id": String, "graph_class_avg": int, "presentation_mode": int, "bell_delay": int, "bell_delay_enabled": int, "grade_opening_fun": int, "icon_pack": String, "premium_scopes": String, diff --git a/filcnaplo/lib/helpers/notification_helper.dart b/filcnaplo/lib/helpers/notification_helper.dart index 76b9266..598be68 100644 --- a/filcnaplo/lib/helpers/notification_helper.dart +++ b/filcnaplo/lib/helpers/notification_helper.dart @@ -11,8 +11,10 @@ import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/models/absence.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart' hide Message; import 'package:intl/intl.dart'; +import 'package:filcnaplo_kreta_api/models/message.dart'; class NotificationsHelper { late DatabaseProvider database; @@ -63,6 +65,7 @@ class NotificationsHelper { kretaClient.refreshLogin(); if(settingsProvider.notificationsGradesEnabled) gradeNotification(); if(settingsProvider.notificationsAbsencesEnabled) absenceNotification(); + messageNotification(); } } @@ -133,13 +136,13 @@ class NotificationsHelper { if(absenceJson == null) { return; } - // format api absences to correct format while preserving hasSeen value + // 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.hasSeen = storedAbsence.hasSeen; + apiAbsence.isSeen = storedAbsence.isSeen; return apiAbsence; }).toList(); List modifiedAbsences = []; @@ -147,8 +150,8 @@ class NotificationsHelper { // remove absences that are not new absences.removeWhere((element) => storedAbsences.contains(element)); for(Absence absence in absences) { - if(!absence.hasSeen) { - absence.hasSeen = true; + if(!absence.isSeen) { + absence.isSeen = true; modifiedAbsences.add(absence); const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails('ABSENCES', 'Hiányzások', @@ -195,4 +198,71 @@ class NotificationsHelper { ); await database.userStore.storeAbsences(combinedAbsences, userId: userProvider.id!); } + + 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)); + }(); + })); + + 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); + const AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails('MESSAGES', 'Üzenetek', + channelDescription: 'Értesítés kapott üzenetekkor', + importance: Importance.max, + priority: Priority.max, + color: const Color(0xFF3D7BF4), + ticker: 'Üzenetek'); + const 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!); + } + } diff --git a/filcnaplo/lib/helpers/notification_helper.i18n.dart b/filcnaplo/lib/helpers/notification_helper.i18n.dart index cccd35c..70f54d9 100644 --- a/filcnaplo/lib/helpers/notification_helper.i18n.dart +++ b/filcnaplo/lib/helpers/notification_helper.i18n.dart @@ -17,7 +17,7 @@ extension Localization on String { "body_grade_multiuser": "%s tanuló %s-st kapott %s tantárgyból", "title_absence": "Új hiányzás", "body_absence": "Új hiányzást kaptál %s napon %s tantárgyból", - "body_absence_multiuser": "%s tanuló új hiányzást kapott %s napon %s tantárgyból" + "body_absence_multiuser": "%s tanuló új hiányzást kapott %s napon %s tantárgyból", }, "de_de": { "title_grade": "Neue Note", @@ -25,7 +25,7 @@ extension Localization on String { "body_grade_multiuser": "%s hast eine %s in %s", "title_absence": "Abwesenheit aufgezeichnet", "body_absence": "Auf %s für %s wurde eine Abwesenheit aufgezeichnet", - "body_absence_multiuser": "Für %s wurde am %s für das Thema Mathematik eine Abwesenheit aufgezeichnet" + "body_absence_multiuser": "Für %s wurde am %s für das Thema Mathematik eine Abwesenheit aufgezeichnet", }, }; diff --git a/filcnaplo/lib/models/settings.dart b/filcnaplo/lib/models/settings.dart index 918a990..26969ba 100644 --- a/filcnaplo/lib/models/settings.dart +++ b/filcnaplo/lib/models/settings.dart @@ -33,6 +33,7 @@ class SettingsProvider extends ChangeNotifier { bool _notificationsEnabled; bool _notificationsGradesEnabled; bool _notificationsAbsencesEnabled; + bool _notificationsMessagesEnabled; /* notificationsBitfield values: @@ -88,6 +89,7 @@ class SettingsProvider extends ChangeNotifier { required bool notificationsEnabled, required bool notificationsGradesEnabled, required bool notificationsAbsencesEnabled, + required bool notificationsMessagesEnabled, required int notificationsBitfield, required bool developerMode, required int notificationPollInterval, @@ -128,6 +130,7 @@ class SettingsProvider extends ChangeNotifier { _notificationsEnabled = notificationsEnabled, _notificationsGradesEnabled = notificationsGradesEnabled, _notificationsAbsencesEnabled = notificationsAbsencesEnabled, + _notificationsMessagesEnabled = notificationsMessagesEnabled, _notificationsBitfield = notificationsBitfield, _developerMode = developerMode, _notificationPollInterval = notificationPollInterval, @@ -186,6 +189,7 @@ class SettingsProvider extends ChangeNotifier { notificationsEnabled: map["notifications"] == 1, notificationsGradesEnabled: map["notifications_grades"] == 1, notificationsAbsencesEnabled: map["notifications_absences"] == 1, + notificationsMessagesEnabled: map["notifications_messages"] == 1, notificationsBitfield: map["notifications_bitfield"], notificationPollInterval: map["notification_poll_interval"], developerMode: map["developer_mode"] == 1, @@ -230,6 +234,7 @@ class SettingsProvider extends ChangeNotifier { "notifications": _notificationsEnabled ? 1 : 0, "notifications_grades": _notificationsGradesEnabled ? 1 : 0, "notifications_absences": _notificationsAbsencesEnabled ? 1 : 0, + "notifications_messages": _notificationsMessagesEnabled ? 1 : 0, "notifications_bitfield": _notificationsBitfield, "developer_mode": _developerMode ? 1 : 0, "grade_color1": _gradeColors[0].value, @@ -285,6 +290,7 @@ class SettingsProvider extends ChangeNotifier { notificationsEnabled: true, notificationsGradesEnabled: true, notificationsAbsencesEnabled: true, + notificationsMessagesEnabled: true, notificationsBitfield: 255, developerMode: false, notificationPollInterval: 1, @@ -328,6 +334,7 @@ class SettingsProvider extends ChangeNotifier { bool get notificationsEnabled => _notificationsEnabled; bool get notificationsGradesEnabled => _notificationsGradesEnabled; bool get notificationsAbsencesEnabled => _notificationsAbsencesEnabled; + bool get notificationsMessagesEnabled => _notificationsMessagesEnabled; int get notificationsBitfield => _notificationsBitfield; bool get developerMode => _developerMode; int get notificationPollInterval => _notificationPollInterval; @@ -373,6 +380,7 @@ class SettingsProvider extends ChangeNotifier { bool? notificationsEnabled, bool? notificationsGradesEnabled, bool? notificationsAbsencesEnabled, + bool? notificationsMessagesEnabled, int? notificationsBitfield, bool? developerMode, int? notificationPollInterval, @@ -432,6 +440,10 @@ class SettingsProvider extends ChangeNotifier { notificationsAbsencesEnabled != _notificationsAbsencesEnabled) { _notificationsAbsencesEnabled = notificationsAbsencesEnabled; } + if (notificationsMessagesEnabled != null && + notificationsMessagesEnabled != _notificationsMessagesEnabled) { + _notificationsMessagesEnabled = notificationsMessagesEnabled; + } if (notificationsBitfield != null && notificationsBitfield != _notificationsBitfield) { _notificationsBitfield = notificationsBitfield; diff --git a/filcnaplo/lib/ui/widgets/grade/grade_tile.dart b/filcnaplo/lib/ui/widgets/grade/grade_tile.dart index cfb9365..50e0d3a 100644 --- a/filcnaplo/lib/ui/widgets/grade/grade_tile.dart +++ b/filcnaplo/lib/ui/widgets/grade/grade_tile.dart @@ -192,6 +192,7 @@ class GradeValueWidget extends StatelessWidget { this.outline = false, this.complemented = false, this.nocolor = false, + this.color, }) : super(key: key); final GradeValue value; @@ -202,6 +203,7 @@ class GradeValueWidget extends StatelessWidget { final bool outline; final bool complemented; final bool nocolor; + final Color? color; @override Widget build(BuildContext context) { @@ -209,7 +211,7 @@ class GradeValueWidget extends StatelessWidget { bool isSubjectView = SubjectGradesContainer.of(context) != null; Color color = - gradeColor(context: context, value: value.value, nocolor: nocolor); + this.color ?? gradeColor(context: context, value: value.value, nocolor: nocolor); Widget valueText; final percentage = value.percentage; diff --git a/filcnaplo_kreta_api/lib/models/absence.dart b/filcnaplo_kreta_api/lib/models/absence.dart index d869063..9997351 100644 --- a/filcnaplo_kreta_api/lib/models/absence.dart +++ b/filcnaplo_kreta_api/lib/models/absence.dart @@ -18,7 +18,7 @@ class Absence { DateTime lessonEnd; int? lessonIndex; String group; - bool hasSeen; + bool isSeen; @override bool operator ==(Object other) => identical(this, other) || @@ -43,7 +43,7 @@ class Absence { this.lessonIndex, required this.group, this.json, - this.hasSeen = false, + this.isSeen = false, }); factory Absence.fromJson(Map json) { @@ -89,7 +89,7 @@ class Absence { lessonIndex: lessonIndex, group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "", - hasSeen: false, + isSeen: false, json: json, ); } diff --git a/filcnaplo_kreta_api/lib/models/message.dart b/filcnaplo_kreta_api/lib/models/message.dart index c086830..97cd5f3 100644 --- a/filcnaplo_kreta_api/lib/models/message.dart +++ b/filcnaplo_kreta_api/lib/models/message.dart @@ -16,6 +16,7 @@ class Message { MessageType? type; List recipients; List attachments; + bool isSeen; Message({ required this.id, @@ -32,7 +33,15 @@ class Message { this.replyId, this.conversationId, this.json, + this.isSeen = false, }); + @override + bool operator ==(Object other) => + identical(this, other) || + other is Message && runtimeType == other.runtimeType && id == other.id; + + @override + int get hashCode => id.hashCode; factory Message.fromJson(Map json, {MessageType? forceType}) { Map message = json["uzenet"]; @@ -69,6 +78,7 @@ class Message { replyId: message["elozoUzenetAzonosito"], conversationId: message["beszelgetesAzonosito"], json: json, + isSeen: false, ); } diff --git a/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart index 358316a..b1ea8a1 100644 --- a/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart @@ -74,7 +74,8 @@ class NotificationsScreen extends StatelessWidget { onChanged: (v) => {settings.update(notificationsGradesEnabled: v)}, title: Row( children: [ - GradeValueWidget(GradeValue(5, "", "", 100), fill: true, size: 30, nocolor: !settings.notificationsGradesEnabled,), + GradeValueWidget(GradeValue(5, "", "", 100), fill: true, size: 30, color: settings.gradeColors[4].withOpacity( + settings.notificationsGradesEnabled ? 1.0 : .5)), const SizedBox(width: 14.0), Expanded( child: Text( @@ -101,7 +102,7 @@ class NotificationsScreen extends StatelessWidget { : Icon(FeatherIcons.clock, color: AppColors.of(context).text.withOpacity(.25)), - const SizedBox(width: 14.0), + const SizedBox(width: 23.0), Expanded( child: Text( "absences".i18n, @@ -115,6 +116,32 @@ class NotificationsScreen extends StatelessWidget { ), ], ), + ), + SwitchListTile( + value: settings.notificationsMessagesEnabled, + onChanged: (v) => {settings.update(notificationsMessagesEnabled: v)}, + title: Row( + children: [ + const SizedBox(width: 8), + settings.notificationsMessagesEnabled + ? const Icon(FeatherIcons.messageSquare) + : Icon(FeatherIcons.messageSquare, + color: + AppColors.of(context).text.withOpacity(.25)), + const SizedBox(width: 23.0), + Expanded( + child: Text( + "messages".i18n, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16.0, + color: AppColors.of(context).text.withOpacity( + settings.notificationsMessagesEnabled ? 1.0 : .5), + ), + ), + ), + ], + ), ) ]), )))); diff --git a/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart b/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart index 3848402..6dccf41 100644 --- a/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart @@ -6,18 +6,21 @@ extension SettingsLocalization on String { "en_en": { "notifications_screen": "Notifications", "grades": "Grades", - "absences": "Absences" + "absences": "Absences", + "messages": "Messages", }, "hu_hu": { "notifications_screen": "Értesítések", "grades": "Jegyek", - "absences": "Hiányzások" + "absences": "Hiányzások", + "messages": "Üzenetek", }, "de_de": { "notifications_screen": "Mitteilung", "grades": "Noten", - "absences": "Fehlen" + "absences": "Fehlen", + "messages": "Nachrichten" }, };