commit
a199f919b6
@ -66,7 +66,12 @@ const userDataDB = DatabaseStruct("user_data", {
|
|||||||
// renamed teachers // non kreta data
|
// renamed teachers // non kreta data
|
||||||
"renamed_teachers": String,
|
"renamed_teachers": String,
|
||||||
// "subject_lesson_count": String, // non kreta data
|
// "subject_lesson_count": String, // non kreta data
|
||||||
|
// notifications and surprise grades // non kreta data
|
||||||
"last_seen_grade": int,
|
"last_seen_grade": int,
|
||||||
|
"last_seen_surprisegrade": int,
|
||||||
|
"last_seen_absence": int,
|
||||||
|
"last_seen_message": int,
|
||||||
|
"last_seen_lesson": int,
|
||||||
// goal planning // non kreta data
|
// goal planning // non kreta data
|
||||||
"goal_plans": String,
|
"goal_plans": String,
|
||||||
"goal_averages": String,
|
"goal_averages": String,
|
||||||
@ -129,7 +134,11 @@ Future<Database> initDB(DatabaseProvider database) async {
|
|||||||
// renamed teachers // non kreta data
|
// renamed teachers // non kreta data
|
||||||
"renamed_teachers": "{}",
|
"renamed_teachers": "{}",
|
||||||
// "subject_lesson_count": "{}", // non kreta data
|
// "subject_lesson_count": "{}", // non kreta data
|
||||||
"last_seen_grade": 0,
|
"last_seen_grade": DateTime.now().millisecondsSinceEpoch,
|
||||||
|
"last_seen_surprisegrade": 0,
|
||||||
|
"last_seen_absence": DateTime.now().millisecondsSinceEpoch,
|
||||||
|
"last_seen_message": DateTime.now().millisecondsSinceEpoch,
|
||||||
|
"last_seen_lesson": DateTime.now().millisecondsSinceEpoch,
|
||||||
// goal planning // non kreta data
|
// goal planning // non kreta data
|
||||||
"goal_plans": "{}",
|
"goal_plans": "{}",
|
||||||
"goal_averages": "{}",
|
"goal_averages": "{}",
|
||||||
@ -207,3 +216,4 @@ Future<void> migrateDB(
|
|||||||
print("INFO: Database migrated");
|
print("INFO: Database migrated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:refilc/api/providers/database_provider.dart';
|
import 'package:refilc/api/providers/database_provider.dart';
|
||||||
import 'package:refilc/models/linked_account.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/self_note.dart';
|
||||||
import 'package:refilc/models/subject_lesson_count.dart';
|
import 'package:refilc/models/subject_lesson_count.dart';
|
||||||
import 'package:refilc/models/user.dart';
|
import 'package:refilc/models/user.dart';
|
||||||
@ -213,11 +214,11 @@ class UserDatabaseQuery {
|
|||||||
return lessonCount;
|
return lessonCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DateTime> lastSeenGrade({required String userId}) async {
|
Future<DateTime> lastSeen({required String userId, required LastSeenCategory category}) async {
|
||||||
List<Map> userData =
|
List<Map> userData =
|
||||||
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return DateTime(0);
|
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);
|
if (lastSeenDate == null) return DateTime(0);
|
||||||
DateTime lastSeen = DateTime.fromMillisecondsSinceEpoch(lastSeenDate);
|
DateTime lastSeen = DateTime.fromMillisecondsSinceEpoch(lastSeenDate);
|
||||||
return lastSeen;
|
return lastSeen;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:refilc/models/linked_account.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/self_note.dart';
|
||||||
import 'package:refilc/models/subject_lesson_count.dart';
|
import 'package:refilc/models/subject_lesson_count.dart';
|
||||||
import 'package:refilc_kreta_api/models/week.dart';
|
import 'package:refilc_kreta_api/models/week.dart';
|
||||||
@ -129,13 +130,14 @@ class UserDatabaseStore {
|
|||||||
where: "id = ?", whereArgs: [userId]);
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeLastSeenGrade(DateTime date,
|
Future<void> storeLastSeen(DateTime date,
|
||||||
{required String userId}) async {
|
{required String userId, required LastSeenCategory category}) async {
|
||||||
int lastSeenDate = date.millisecondsSinceEpoch;
|
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]);
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// renamed things
|
// renamed things
|
||||||
Future<void> storeRenamedSubjects(Map<String, String> subjects,
|
Future<void> storeRenamedSubjects(Map<String, String> subjects,
|
||||||
{required String userId}) async {
|
{required String userId}) async {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:refilc/api/providers/database_provider.dart';
|
import 'package:refilc/api/providers/database_provider.dart';
|
||||||
import 'package:refilc/api/providers/status_provider.dart';
|
import 'package:refilc/api/providers/status_provider.dart';
|
||||||
import 'package:refilc/api/providers/user_provider.dart';
|
import 'package:refilc/api/providers/user_provider.dart';
|
||||||
import 'package:refilc/models/settings.dart';
|
import 'package:refilc/models/settings.dart';
|
||||||
import 'package:refilc/helpers/notification_helper.i18n.dart';
|
import 'package:refilc/helpers/notification_helper.i18n.dart';
|
||||||
|
import 'package:refilc/models/user.dart';
|
||||||
|
import 'package:refilc/utils/navigation_service.dart';
|
||||||
|
import 'package:refilc/utils/service_locator.dart';
|
||||||
import 'package:refilc_kreta_api/client/api.dart';
|
import 'package:refilc_kreta_api/client/api.dart';
|
||||||
import 'package:refilc_kreta_api/client/client.dart';
|
import 'package:refilc_kreta_api/client/client.dart';
|
||||||
import 'package:refilc_kreta_api/models/absence.dart';
|
import 'package:refilc_kreta_api/models/absence.dart';
|
||||||
@ -13,10 +17,19 @@ import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
|||||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
|
||||||
hide Message;
|
hide Message;
|
||||||
import 'package:i18n_extension/i18n_extension.dart';
|
import 'package:i18n_extension/i18n_widget.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:refilc_kreta_api/models/message.dart';
|
import 'package:refilc_kreta_api/models/message.dart';
|
||||||
|
|
||||||
|
// if you want to add a new category, also add it to the DB or else the app will probably crash
|
||||||
|
enum LastSeenCategory {
|
||||||
|
grade,
|
||||||
|
surprisegrade,
|
||||||
|
absence,
|
||||||
|
message,
|
||||||
|
lesson
|
||||||
|
} // didn't know a better place for this
|
||||||
|
|
||||||
class NotificationsHelper {
|
class NotificationsHelper {
|
||||||
late DatabaseProvider database;
|
late DatabaseProvider database;
|
||||||
late SettingsProvider settingsProvider;
|
late SettingsProvider settingsProvider;
|
||||||
@ -25,36 +38,14 @@ class NotificationsHelper {
|
|||||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||||
FlutterLocalNotificationsPlugin();
|
FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
List<T> combineLists<T, K>(
|
|
||||||
List<T> list1,
|
|
||||||
List<T> list2,
|
|
||||||
K Function(T) keyExtractor,
|
|
||||||
) {
|
|
||||||
Set<K> uniqueKeys = <K>{};
|
|
||||||
List<T> 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) {
|
String dayTitle(DateTime date) {
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
@ -69,43 +60,50 @@ class NotificationsHelper {
|
|||||||
userProvider = await database.query.getUsers(settingsProvider);
|
userProvider = await database.query.getUsers(settingsProvider);
|
||||||
|
|
||||||
if (userProvider.id != null && settingsProvider.notificationsEnabled) {
|
if (userProvider.id != null && settingsProvider.notificationsEnabled) {
|
||||||
// refresh kreta login
|
List<User> 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();
|
final status = StatusProvider();
|
||||||
kretaClient = KretaClient(
|
KretaClient kretaClientForUser = KretaClient(
|
||||||
user: userProvider, settings: settingsProvider, status: status);
|
user: userProviderForUser, settings: settingsProvider, status: status);
|
||||||
kretaClient.refreshLogin();
|
await kretaClientForUser.refreshLogin();
|
||||||
if (settingsProvider.notificationsGradesEnabled) gradeNotification();
|
|
||||||
if (settingsProvider.notificationsAbsencesEnabled) absenceNotification();
|
// Process notifications for current user
|
||||||
if (settingsProvider.notificationsMessagesEnabled) messageNotification();
|
if (settingsProvider.notificationsGradesEnabled) await gradeNotification(userProviderForUser, kretaClientForUser);
|
||||||
if (settingsProvider.notificationsLessonsEnabled) lessonNotification();
|
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<void> gradeNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async {
|
||||||
// fetch grades
|
// fetch grades
|
||||||
GradeProvider gradeProvider = GradeProvider(
|
GradeProvider gradeProvider = GradeProvider(
|
||||||
settings: settingsProvider,
|
settings: settingsProvider,
|
||||||
user: userProvider,
|
user: currentuserProvider,
|
||||||
database: database,
|
database: database,
|
||||||
kreta: kretaClient);
|
kreta: currentKretaClient);
|
||||||
gradeProvider.fetch();
|
await gradeProvider.fetch();
|
||||||
List<Grade> grades =
|
database.userQuery
|
||||||
await database.userQuery.getGrades(userId: userProvider.id ?? "");
|
.getGrades(userId: currentuserProvider.id!)
|
||||||
DateTime lastSeenGrade =
|
.then((grades) async {
|
||||||
await database.userQuery.lastSeenGrade(userId: userProvider.id ?? "");
|
DateTime lastSeenGrade = await database.userQuery.lastSeen(
|
||||||
|
userId: currentuserProvider.id!, category: LastSeenCategory.grade);
|
||||||
|
|
||||||
// loop through grades and see which hasn't been seen yet
|
// loop through grades and see which hasn't been seen yet
|
||||||
for (Grade grade in grades) {
|
for (Grade grade in grades) {
|
||||||
// if grade is not a normal grade (1-5), don't show it
|
// if grade is not a normal grade (1-5), don't show it
|
||||||
if ([1, 2, 3, 4, 5].contains(grade.value.value)) {
|
if ([1, 2, 3, 4, 5].contains(grade.value.value)) {
|
||||||
// if the grade was added over a week ago, don't show it to avoid notification spam
|
// if 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
|
if (grade.date.isAfter(lastSeenGrade) &&
|
||||||
// old code below
|
DateTime.now().difference(grade.date).inDays < 7) {
|
||||||
// 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
|
// send notificiation about new grade
|
||||||
AndroidNotificationDetails androidNotificationDetails =
|
AndroidNotificationDetails androidNotificationDetails =
|
||||||
AndroidNotificationDetails(
|
AndroidNotificationDetails(
|
||||||
@ -119,7 +117,7 @@ class NotificationsHelper {
|
|||||||
);
|
);
|
||||||
NotificationDetails notificationDetails =
|
NotificationDetails notificationDetails =
|
||||||
NotificationDetails(android: androidNotificationDetails);
|
NotificationDetails(android: androidNotificationDetails);
|
||||||
if (userProvider.getUsers().length == 1) {
|
if (currentuserProvider.getUsers().length == 1) {
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
grade.id.hashCode,
|
grade.id.hashCode,
|
||||||
"title_grade".i18n,
|
"title_grade".i18n,
|
||||||
@ -133,6 +131,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "grades"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// multiple users are added, also display student name
|
// multiple users are added, also display student name
|
||||||
@ -141,7 +140,7 @@ class NotificationsHelper {
|
|||||||
"title_grade".i18n,
|
"title_grade".i18n,
|
||||||
"body_grade_multiuser".i18n.fill(
|
"body_grade_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
grade.value.value.toString(),
|
grade.value.value.toString(),
|
||||||
grade.subject.isRenamed &&
|
grade.subject.isRenamed &&
|
||||||
settingsProvider.renamedSubjectsEnabled
|
settingsProvider.renamedSubjectsEnabled
|
||||||
@ -150,41 +149,31 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "grades"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set grade seen status
|
// set grade seen status
|
||||||
gradeProvider.seenAll();
|
database.userStore.storeLastSeen(DateTime.now(), userId: currentuserProvider.id!, category: LastSeenCategory.grade);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void absenceNotification() async {
|
Future<void> absenceNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async {
|
||||||
// get absences from api
|
// get absences from api
|
||||||
List? absenceJson = await kretaClient
|
List? absenceJson = await currentKretaClient
|
||||||
.getAPI(KretaAPI.absences(userProvider.instituteCode ?? ""));
|
.getAPI(KretaAPI.absences(currentuserProvider.instituteCode ?? ""));
|
||||||
List<Absence> storedAbsences =
|
|
||||||
await database.userQuery.getAbsences(userId: userProvider.id!);
|
|
||||||
if (absenceJson == null) {
|
if (absenceJson == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// format api absences to correct format while preserving isSeen value
|
DateTime lastSeenAbsence = await database.userQuery
|
||||||
List<Absence> absences = absenceJson.map((e) {
|
.lastSeen(userId: currentuserProvider.id!, category: LastSeenCategory.absence);
|
||||||
Absence apiAbsence = Absence.fromJson(e);
|
// format api absences
|
||||||
Absence storedAbsence = storedAbsences.firstWhere(
|
List<Absence> absences =
|
||||||
(stored) => stored.id == apiAbsence.id,
|
absenceJson.map((e) => Absence.fromJson(e)).toList();
|
||||||
orElse: () => apiAbsence);
|
|
||||||
apiAbsence.isSeen = storedAbsence.isSeen;
|
|
||||||
return apiAbsence;
|
|
||||||
}).toList();
|
|
||||||
List<Absence> modifiedAbsences = [];
|
|
||||||
if (absences != storedAbsences) {
|
|
||||||
// remove absences that are not new
|
|
||||||
absences.removeWhere((element) => storedAbsences.contains(element));
|
|
||||||
for (Absence absence in absences) {
|
for (Absence absence in absences) {
|
||||||
if (!absence.isSeen) {
|
if (absence.date.isAfter(lastSeenAbsence)) {
|
||||||
absence.isSeen = true;
|
|
||||||
modifiedAbsences.add(absence);
|
|
||||||
AndroidNotificationDetails androidNotificationDetails =
|
AndroidNotificationDetails androidNotificationDetails =
|
||||||
AndroidNotificationDetails(
|
AndroidNotificationDetails(
|
||||||
'ABSENCES',
|
'ABSENCES',
|
||||||
@ -197,7 +186,7 @@ class NotificationsHelper {
|
|||||||
);
|
);
|
||||||
NotificationDetails notificationDetails =
|
NotificationDetails notificationDetails =
|
||||||
NotificationDetails(android: androidNotificationDetails);
|
NotificationDetails(android: androidNotificationDetails);
|
||||||
if (userProvider.getUsers().length == 1) {
|
if (currentuserProvider.getUsers().length == 1) {
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
absence.id.hashCode,
|
absence.id.hashCode,
|
||||||
"title_absence"
|
"title_absence"
|
||||||
@ -212,6 +201,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "absences"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
@ -220,7 +210,7 @@ class NotificationsHelper {
|
|||||||
.i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528
|
.i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528
|
||||||
"body_absence_multiuser".i18n.fill(
|
"body_absence_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
DateFormat("yyyy-MM-dd").format(absence.date),
|
DateFormat("yyyy-MM-dd").format(absence.date),
|
||||||
absence.subject.isRenamed &&
|
absence.subject.isRenamed &&
|
||||||
settingsProvider.renamedSubjectsEnabled
|
settingsProvider.renamedSubjectsEnabled
|
||||||
@ -229,60 +219,43 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "absences"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
await database.userStore.storeLastSeen(DateTime.now(),
|
||||||
// combine modified absences and storedabsences list and save them to the database
|
userId: currentuserProvider.id!, category: LastSeenCategory.absence);
|
||||||
List<Absence> combinedAbsences = combineLists(
|
|
||||||
modifiedAbsences,
|
|
||||||
storedAbsences,
|
|
||||||
(Absence absence) => absence.id,
|
|
||||||
);
|
|
||||||
await database.userStore
|
|
||||||
.storeAbsences(combinedAbsences, userId: userProvider.id!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void messageNotification() async {
|
Future<void> messageNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async {
|
||||||
// get messages from api
|
// get messages from api
|
||||||
List? messageJson =
|
List? messageJson =
|
||||||
await kretaClient.getAPI(KretaAPI.messages("beerkezett"));
|
await currentKretaClient.getAPI(KretaAPI.messages("beerkezett"));
|
||||||
List<Message> storedmessages =
|
|
||||||
await database.userQuery.getMessages(userId: userProvider.id!);
|
|
||||||
if (messageJson == null) {
|
if (messageJson == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// format api messages to correct format while preserving isSeen value
|
// format api messages to correct format
|
||||||
// Parse messages
|
// Parse messages
|
||||||
List<Message> messages = [];
|
List<Message> messages = [];
|
||||||
await Future.wait(List.generate(messageJson.length, (index) {
|
await Future.wait(List.generate(messageJson.length, (index) {
|
||||||
return () async {
|
return () async {
|
||||||
Map message = messageJson.cast<Map>()[index];
|
Map message = messageJson.cast<Map>()[index];
|
||||||
Map? innerMessageJson = await kretaClient
|
Map? innerMessageJson = await currentKretaClient
|
||||||
.getAPI(KretaAPI.message(message["azonosito"].toString()));
|
.getAPI(KretaAPI.message(message["azonosito"].toString()));
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
if (innerMessageJson != null) {
|
if (innerMessageJson != null) {
|
||||||
messages.add(
|
messages.add(Message.fromJson(innerMessageJson,
|
||||||
Message.fromJson(innerMessageJson, forceType: MessageType.inbox));
|
forceType: MessageType.inbox));
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
DateTime lastSeenMessage = await database.userQuery.lastSeen(
|
||||||
|
userId: currentuserProvider.id!, category: LastSeenCategory.message);
|
||||||
|
|
||||||
for (Message message in messages) {
|
for (Message message in messages) {
|
||||||
for (Message storedMessage in storedmessages) {
|
if (message.date.isAfter(lastSeenMessage)) {
|
||||||
if (message.id == storedMessage.id) {
|
|
||||||
message.isSeen = storedMessage.isSeen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<Message> 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 androidNotificationDetails =
|
||||||
AndroidNotificationDetails(
|
AndroidNotificationDetails(
|
||||||
'MESSAGES',
|
'MESSAGES',
|
||||||
@ -295,56 +268,47 @@ class NotificationsHelper {
|
|||||||
);
|
);
|
||||||
NotificationDetails notificationDetails =
|
NotificationDetails notificationDetails =
|
||||||
NotificationDetails(android: androidNotificationDetails);
|
NotificationDetails(android: androidNotificationDetails);
|
||||||
if (userProvider.getUsers().length == 1) {
|
if (currentuserProvider.getUsers().length == 1) {
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
message.id.hashCode,
|
message.id.hashCode,
|
||||||
message.author,
|
message.author,
|
||||||
message.content.replaceAll(RegExp(r'<[^>]*>'), ''),
|
message.content.replaceAll(RegExp(r'<[^>]*>'), ''),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "messages",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
message.id.hashCode,
|
message.id.hashCode,
|
||||||
"(${userProvider.displayName!}) ${message.author}",
|
"(${currentuserProvider.displayName!}) ${message.author}",
|
||||||
message.content.replaceAll(RegExp(r'<[^>]*>'), ''),
|
message.content.replaceAll(RegExp(r'<[^>]*>'), ''),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "messages",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
await database.userStore.storeLastSeen(DateTime.now(),
|
||||||
// combine modified messages and storedmessages list and save them to the database
|
userId: currentuserProvider.id!, category: LastSeenCategory.message);
|
||||||
List<Message> combinedmessages = combineLists(
|
|
||||||
modifiedmessages,
|
|
||||||
storedmessages,
|
|
||||||
(Message message) => message.id,
|
|
||||||
);
|
|
||||||
await database.userStore
|
|
||||||
.storeMessages(combinedmessages, userId: userProvider.id!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lessonNotification() async {
|
Future<void> lessonNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async {
|
||||||
// get lesson from api
|
// get lessons from api
|
||||||
TimetableProvider timetableProvider = TimetableProvider(
|
TimetableProvider timetableProvider = TimetableProvider(
|
||||||
user: userProvider, database: database, kreta: kretaClient);
|
user: currentuserProvider, database: database, kreta: currentKretaClient);
|
||||||
List<Lesson> storedlessons =
|
await timetableProvider.restoreUser();
|
||||||
timetableProvider.lessons[Week.current()] ?? [];
|
await timetableProvider.fetch(week: Week.current());
|
||||||
List? apilessons = timetableProvider.getWeek(Week.current()) ?? [];
|
List<Lesson> apilessons =
|
||||||
|
timetableProvider.getWeek(Week.current()) ?? [];
|
||||||
|
|
||||||
|
DateTime lastSeenLesson = await database.userQuery.lastSeen(
|
||||||
|
userId: currentuserProvider.id!, category: LastSeenCategory.lesson);
|
||||||
|
Lesson? latestLesson;
|
||||||
|
|
||||||
for (Lesson lesson in apilessons) {
|
for (Lesson lesson in apilessons) {
|
||||||
for (Lesson storedLesson in storedlessons) {
|
if((lesson.status?.name != "Elmaradt" || lesson.substituteTeacher?.name != "") && lesson.date.isAfter(latestLesson?.start ?? DateTime(1970))) {
|
||||||
if (lesson.id == storedLesson.id) {
|
latestLesson = lesson;
|
||||||
lesson.isSeen = storedLesson.isSeen;
|
|
||||||
}
|
}
|
||||||
}
|
if (lesson.date.isAfter(lastSeenLesson)) {
|
||||||
}
|
|
||||||
List<Lesson> 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 androidNotificationDetails =
|
||||||
AndroidNotificationDetails(
|
AndroidNotificationDetails(
|
||||||
'LESSONS',
|
'LESSONS',
|
||||||
@ -358,7 +322,7 @@ class NotificationsHelper {
|
|||||||
);
|
);
|
||||||
NotificationDetails notificationDetails =
|
NotificationDetails notificationDetails =
|
||||||
NotificationDetails(android: androidNotificationDetails);
|
NotificationDetails(android: androidNotificationDetails);
|
||||||
if (userProvider.getUsers().length == 1) {
|
if (currentuserProvider.getUsers().length == 1) {
|
||||||
if (lesson.status?.name == "Elmaradt") {
|
if (lesson.status?.name == "Elmaradt") {
|
||||||
switch (I18n.localeStr) {
|
switch (I18n.localeStr) {
|
||||||
case "en_en":
|
case "en_en":
|
||||||
@ -374,6 +338,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -390,6 +355,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -406,11 +372,12 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable"
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (lesson.substituteTeacher?.name != "") {
|
} else if (lesson.substituteTeacher?.name != "" && lesson.substituteTeacher != null) {
|
||||||
switch (I18n.localeStr) {
|
switch (I18n.localeStr) {
|
||||||
case "en_en":
|
case "en_en":
|
||||||
{
|
{
|
||||||
@ -428,6 +395,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -447,6 +415,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -466,6 +435,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -479,15 +449,16 @@ class NotificationsHelper {
|
|||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
lesson.id.hashCode,
|
lesson.id.hashCode,
|
||||||
"title_lesson".i18n,
|
"title_lesson".i18n,
|
||||||
"body_lesson_canceled".i18n.fill(
|
"body_lesson_canceled_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
lesson.lessonIndex,
|
lesson.lessonIndex,
|
||||||
lesson.name,
|
lesson.name,
|
||||||
dayTitle(lesson.date)
|
dayTitle(lesson.date)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -496,15 +467,16 @@ class NotificationsHelper {
|
|||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
lesson.id.hashCode,
|
lesson.id.hashCode,
|
||||||
"title_lesson".i18n,
|
"title_lesson".i18n,
|
||||||
"body_lesson_canceled".i18n.fill(
|
"body_lesson_canceled_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
dayTitle(lesson.date),
|
dayTitle(lesson.date),
|
||||||
lesson.lessonIndex,
|
lesson.lessonIndex,
|
||||||
lesson.name
|
lesson.name
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -513,15 +485,16 @@ class NotificationsHelper {
|
|||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
lesson.id.hashCode,
|
lesson.id.hashCode,
|
||||||
"title_lesson".i18n,
|
"title_lesson".i18n,
|
||||||
"body_lesson_canceled".i18n.fill(
|
"body_lesson_canceled_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
lesson.lessonIndex,
|
lesson.lessonIndex,
|
||||||
lesson.name,
|
lesson.name,
|
||||||
dayTitle(lesson.date)
|
dayTitle(lesson.date)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -533,9 +506,9 @@ class NotificationsHelper {
|
|||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
lesson.id.hashCode,
|
lesson.id.hashCode,
|
||||||
"title_lesson".i18n,
|
"title_lesson".i18n,
|
||||||
"body_lesson_substituted".i18n.fill(
|
"body_lesson_substituted_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
lesson.lessonIndex,
|
lesson.lessonIndex,
|
||||||
lesson.name,
|
lesson.name,
|
||||||
dayTitle(lesson.date),
|
dayTitle(lesson.date),
|
||||||
@ -545,6 +518,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -553,9 +527,9 @@ class NotificationsHelper {
|
|||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
lesson.id.hashCode,
|
lesson.id.hashCode,
|
||||||
"title_lesson".i18n,
|
"title_lesson".i18n,
|
||||||
"body_lesson_substituted".i18n.fill(
|
"body_lesson_substituted_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
dayTitle(lesson.date),
|
dayTitle(lesson.date),
|
||||||
lesson.lessonIndex,
|
lesson.lessonIndex,
|
||||||
lesson.name,
|
lesson.name,
|
||||||
@ -565,6 +539,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -573,9 +548,9 @@ class NotificationsHelper {
|
|||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
lesson.id.hashCode,
|
lesson.id.hashCode,
|
||||||
"title_lesson".i18n,
|
"title_lesson".i18n,
|
||||||
"body_lesson_substituted".i18n.fill(
|
"body_lesson_substituted_multiuser".i18n.fill(
|
||||||
[
|
[
|
||||||
userProvider.displayName!,
|
currentuserProvider.displayName!,
|
||||||
lesson.lessonIndex,
|
lesson.lessonIndex,
|
||||||
lesson.name,
|
lesson.name,
|
||||||
dayTitle(lesson.date),
|
dayTitle(lesson.date),
|
||||||
@ -585,6 +560,7 @@ class NotificationsHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
notificationDetails,
|
notificationDetails,
|
||||||
|
payload: "timetable",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -593,16 +569,44 @@ class NotificationsHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// combine modified lesson and storedlesson list and save them to the database
|
// lesson.date does not contain time, only the date
|
||||||
List<Lesson> combinedlessons = combineLists(
|
await database.userStore.storeLastSeen(latestLesson?.start ?? DateTime.now(),
|
||||||
modifiedlessons,
|
userId: currentuserProvider.id!, category: LastSeenCategory.lesson);
|
||||||
storedlessons,
|
}
|
||||||
(Lesson message) => message.id,
|
|
||||||
);
|
// Called when the user taps a notification
|
||||||
Map<Week, List<Lesson>> timetableLessons = timetableProvider.lessons;
|
void onDidReceiveNotificationResponse(NotificationResponse notificationResponse) async {
|
||||||
timetableLessons[Week.current()] = combinedlessons;
|
final String? payload = notificationResponse.payload;
|
||||||
await database.userStore
|
if (notificationResponse.payload != null) {
|
||||||
.storeLessons(timetableLessons, userId: userProvider.id!);
|
debugPrint('notification payload: $payload');
|
||||||
|
}
|
||||||
|
switch(payload) {
|
||||||
|
case "timetable":
|
||||||
|
locator<NavigationService>().navigateTo("timetable");
|
||||||
|
break;
|
||||||
|
case "grades":
|
||||||
|
locator<NavigationService>().navigateTo("grades");
|
||||||
|
break;
|
||||||
|
case "messages":
|
||||||
|
locator<NavigationService>().navigateTo("messages");
|
||||||
|
break;
|
||||||
|
case "absences":
|
||||||
|
locator<NavigationService>().navigateTo("absences");
|
||||||
|
break;
|
||||||
|
case "settings":
|
||||||
|
locator<NavigationService>().navigateTo("settings");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all notification categories to seen
|
||||||
|
Future<void> setAllCategoriesSeen(UserProvider userProvider) async {
|
||||||
|
if(userProvider.id != null) {
|
||||||
|
for(LastSeenCategory category in LastSeenCategory.values) {
|
||||||
|
await database.userStore.storeLastSeen(DateTime.now(), userId: userProvider.id!, category: category);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:i18n_extension/i18n_extension.dart';
|
import 'package:i18n_extension/i18n_extension.dart';
|
||||||
|
import 'package:refilc/api/providers/database_provider.dart';
|
||||||
|
|
||||||
extension Localization on String {
|
extension Localization on String {
|
||||||
static final _t = Translations.byLocale("hu_hu") +
|
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"
|
"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<Object> params) => localizeFill(this, params);
|
String fill(List<Object> params) => localizeFill(this, params);
|
||||||
String plural(int value) => localizePlural(value, this, _t);
|
String plural(int value) => localizePlural(value, this, _t);
|
||||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
||||||
|
@ -10,6 +10,8 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:refilc/app.dart';
|
import 'package:refilc/app.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:refilc/utils/navigation_service.dart';
|
||||||
|
import 'package:refilc/utils/service_locator.dart';
|
||||||
import 'package:refilc_mobile_ui/screens/error_screen.dart';
|
import 'package:refilc_mobile_ui/screens/error_screen.dart';
|
||||||
import 'package:refilc_mobile_ui/screens/error_report_screen.dart';
|
import 'package:refilc_mobile_ui/screens/error_report_screen.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
@ -23,6 +25,9 @@ void main() async {
|
|||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
binding.renderView.automaticSystemUiAdjustment = false;
|
binding.renderView.automaticSystemUiAdjustment = false;
|
||||||
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
// navigation
|
||||||
|
setupLocator();
|
||||||
|
|
||||||
// Startup
|
// Startup
|
||||||
Startup startup = Startup();
|
Startup startup = Startup();
|
||||||
await startup.start();
|
await startup.start();
|
||||||
@ -30,6 +35,11 @@ void main() async {
|
|||||||
// Custom error page
|
// Custom error page
|
||||||
ErrorWidget.builder = errorBuilder;
|
ErrorWidget.builder = errorBuilder;
|
||||||
|
|
||||||
|
// initialize stripe key
|
||||||
|
stripe.Stripe.publishableKey =
|
||||||
|
'pk_test_51Oo7iUBS0FxsTGxKjGZSQqzDKWHY5ZFYM9XeI0qSdIh2w8jWy6GhHlYpT7GLTzgpl1xhE5YP4BXpA4gMZqPmgMId00cGFYFzbh';
|
||||||
|
|
||||||
|
|
||||||
BackgroundFetch.registerHeadlessTask(backgroundHeadlessTask);
|
BackgroundFetch.registerHeadlessTask(backgroundHeadlessTask);
|
||||||
|
|
||||||
// Run App
|
// Run App
|
||||||
@ -53,6 +63,9 @@ class Startup {
|
|||||||
settings = await database.query.getSettings(database);
|
settings = await database.query.getSettings(database);
|
||||||
user = await database.query.getUsers(settings);
|
user = await database.query.getUsers(settings);
|
||||||
|
|
||||||
|
// Set all notification categories to seen to avoid having notifications that the user has already seen in the app
|
||||||
|
NotificationsHelper().setAllCategoriesSeen(user);
|
||||||
|
|
||||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
||||||
// Notifications setup
|
// Notifications setup
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
@ -113,6 +126,7 @@ class Startup {
|
|||||||
// Initialize notifications
|
// Initialize notifications
|
||||||
await flutterLocalNotificationsPlugin.initialize(
|
await flutterLocalNotificationsPlugin.initialize(
|
||||||
initializationSettings,
|
initializationSettings,
|
||||||
|
onDidReceiveNotificationResponse: NotificationsHelper().onDidReceiveNotificationResponse,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
refilc/lib/utils/navigation_service.dart
Normal file
9
refilc/lib/utils/navigation_service.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NavigationService {
|
||||||
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
Future<dynamic> navigateTo(String routeName) {
|
||||||
|
return navigatorKey.currentState!.pushNamed(routeName);
|
||||||
|
}
|
||||||
|
}
|
8
refilc/lib/utils/service_locator.dart
Normal file
8
refilc/lib/utils/service_locator.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:refilc/utils/navigation_service.dart';
|
||||||
|
|
||||||
|
GetIt locator = GetIt.instance;
|
||||||
|
|
||||||
|
void setupLocator() {
|
||||||
|
locator.registerLazySingleton(() => NavigationService());
|
||||||
|
}
|
@ -77,6 +77,8 @@ dependencies:
|
|||||||
extension_google_sign_in_as_googleapis_auth: ^2.0.12
|
extension_google_sign_in_as_googleapis_auth: ^2.0.12
|
||||||
maps_launcher: ^2.2.0
|
maps_launcher: ^2.2.0
|
||||||
google_fonts: ^6.1.0
|
google_fonts: ^6.1.0
|
||||||
|
flutter_stripe: ^10.0.0
|
||||||
|
get_it: ^7.6.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^3.0.1
|
flutter_lints: ^3.0.1
|
||||||
@ -166,6 +168,7 @@ flutter:
|
|||||||
weight: 500
|
weight: 500
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
image_path: assets/icons/ic_android.png
|
image_path: assets/icons/ic_android.png
|
||||||
android: true
|
android: true
|
||||||
|
@ -50,6 +50,7 @@ class Absence {
|
|||||||
DateTime lessonStart;
|
DateTime lessonStart;
|
||||||
DateTime lessonEnd;
|
DateTime lessonEnd;
|
||||||
int? lessonIndex;
|
int? lessonIndex;
|
||||||
|
bool isSeen = json["isSeen"] ?? false;
|
||||||
if (json["Ora"] != null) {
|
if (json["Ora"] != null) {
|
||||||
lessonStart = json["Ora"]["KezdoDatum"] != null
|
lessonStart = json["Ora"]["KezdoDatum"] != null
|
||||||
? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal()
|
? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal()
|
||||||
@ -62,7 +63,6 @@ class Absence {
|
|||||||
lessonStart = DateTime(0);
|
lessonStart = DateTime(0);
|
||||||
lessonEnd = DateTime(0);
|
lessonEnd = DateTime(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Absence(
|
return Absence(
|
||||||
id: json["Uid"],
|
id: json["Uid"],
|
||||||
date: json["Datum"] != null
|
date: json["Datum"] != null
|
||||||
@ -89,7 +89,7 @@ class Absence {
|
|||||||
lessonIndex: lessonIndex,
|
lessonIndex: lessonIndex,
|
||||||
group:
|
group:
|
||||||
json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "",
|
json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "",
|
||||||
isSeen: false,
|
isSeen: json["isSeen"] ?? false,
|
||||||
json: json,
|
json: json,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:refilc/api/providers/user_provider.dart';
|
import 'package:refilc/api/providers/user_provider.dart';
|
||||||
import 'package:refilc/api/providers/database_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/settings.dart';
|
||||||
import 'package:refilc/models/user.dart';
|
import 'package:refilc/models/user.dart';
|
||||||
import 'package:refilc_kreta_api/client/api.dart';
|
import 'package:refilc_kreta_api/client/api.dart';
|
||||||
@ -49,7 +50,7 @@ class GradeProvider with ChangeNotifier {
|
|||||||
String? userId = _user.id;
|
String? userId = _user.id;
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
final userStore = _database.userStore;
|
final userStore = _database.userStore;
|
||||||
userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
|
userStore.storeLastSeen(DateTime.now(), userId: userId, category: LastSeenCategory.surprisegrade);
|
||||||
_lastSeen = DateTime.now();
|
_lastSeen = DateTime.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +59,7 @@ class GradeProvider with ChangeNotifier {
|
|||||||
String? userId = _user.id;
|
String? userId = _user.id;
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
final userStore = _database.userStore;
|
final userStore = _database.userStore;
|
||||||
userStore.storeLastSeenGrade(DateTime(1969), userId: userId);
|
userStore.storeLastSeen(DateTime(1969), userId: userId, category: LastSeenCategory.surprisegrade);
|
||||||
_lastSeen = DateTime(1969);
|
_lastSeen = DateTime(1969);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ class GradeProvider with ChangeNotifier {
|
|||||||
await convertBySettings();
|
await convertBySettings();
|
||||||
_groupAvg = await userQuery.getGroupAverages(userId: userId);
|
_groupAvg = await userQuery.getGroupAverages(userId: userId);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
DateTime lastSeenDB = await userQuery.lastSeenGrade(userId: userId);
|
DateTime lastSeenDB = await userQuery.lastSeen(userId: userId, category: LastSeenCategory.surprisegrade);
|
||||||
if (lastSeenDB.millisecondsSinceEpoch == 0 ||
|
if (lastSeenDB.millisecondsSinceEpoch == 0 ||
|
||||||
lastSeenDB.year == 0 ||
|
lastSeenDB.year == 0 ||
|
||||||
!_settings.gradeOpeningFun) {
|
!_settings.gradeOpeningFun) {
|
||||||
|
@ -5,6 +5,8 @@ import 'package:refilc/helpers/quick_actions.dart';
|
|||||||
import 'package:refilc/icons/filc_icons.dart';
|
import 'package:refilc/icons/filc_icons.dart';
|
||||||
import 'package:refilc/models/settings.dart';
|
import 'package:refilc/models/settings.dart';
|
||||||
import 'package:refilc/theme/observer.dart';
|
import 'package:refilc/theme/observer.dart';
|
||||||
|
import 'package:refilc/utils/navigation_service.dart';
|
||||||
|
import 'package:refilc/utils/service_locator.dart';
|
||||||
import 'package:refilc_kreta_api/client/client.dart';
|
import 'package:refilc_kreta_api/client/client.dart';
|
||||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
||||||
import 'package:refilc_mobile_ui/common/system_chrome.dart';
|
import 'package:refilc_mobile_ui/common/system_chrome.dart';
|
||||||
@ -45,7 +47,7 @@ class NavigationScreenState extends State<NavigationScreen>
|
|||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
late NavigationRoute selected;
|
late NavigationRoute selected;
|
||||||
List<String> initializers = [];
|
List<String> initializers = [];
|
||||||
final _navigatorState = GlobalKey<NavigatorState>();
|
final _navigatorState = locator<NavigationService>().navigatorKey;
|
||||||
|
|
||||||
late SettingsProvider settings;
|
late SettingsProvider settings;
|
||||||
late NewsProvider newsProvider;
|
late NewsProvider newsProvider;
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:refilc/api/providers/database_provider.dart';
|
||||||
|
import 'package:refilc/api/providers/user_provider.dart';
|
||||||
|
import 'package:refilc/helpers/notification_helper.dart';
|
||||||
import 'package:refilc/models/settings.dart';
|
import 'package:refilc/models/settings.dart';
|
||||||
|
import 'package:refilc/models/user.dart';
|
||||||
import 'package:refilc/theme/colors/colors.dart';
|
import 'package:refilc/theme/colors/colors.dart';
|
||||||
import 'package:refilc_mobile_ui/common/beta_chip.dart';
|
import 'package:refilc_mobile_ui/common/beta_chip.dart';
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
|
||||||
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
|
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
|
||||||
import 'notifications_screen.i18n.dart';
|
import 'notifications_screen.i18n.dart';
|
||||||
|
|
||||||
class MenuNotifications extends StatelessWidget {
|
class MenuNotifications extends StatelessWidget {
|
||||||
@ -53,6 +58,19 @@ class MenuNotifications extends StatelessWidget {
|
|||||||
class NotificationsScreen extends StatelessWidget {
|
class NotificationsScreen extends StatelessWidget {
|
||||||
const NotificationsScreen({super.key});
|
const NotificationsScreen({super.key});
|
||||||
|
|
||||||
|
// Set all notification categories as seen to avoid spamming the user with notifications when they turn on notifications
|
||||||
|
void setAll(BuildContext context, DateTime date) {
|
||||||
|
DatabaseProvider database =
|
||||||
|
Provider.of<DatabaseProvider>(context, listen: false);
|
||||||
|
User? user = Provider.of<UserProvider>(context, listen: false).user;
|
||||||
|
if (user != null) {
|
||||||
|
for (LastSeenCategory category in LastSeenCategory.values) {
|
||||||
|
database.userStore
|
||||||
|
.storeLastSeen(date, userId: user.id, category: category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
SettingsProvider settings = Provider.of<SettingsProvider>(context);
|
SettingsProvider settings = Provider.of<SettingsProvider>(context);
|
||||||
@ -69,151 +87,188 @@ class NotificationsScreen extends StatelessWidget {
|
|||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
|
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
|
||||||
child: Panel(
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Material(
|
SplittedPanel(
|
||||||
type: MaterialType.transparency,
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: SwitchListTile(
|
cardPadding: const EdgeInsets.all(4.0),
|
||||||
shape: RoundedRectangleBorder(
|
isSeparated: true,
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
|
||||||
value: settings.notificationsGradesEnabled,
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(notificationsGradesEnabled: v),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
PanelButton(
|
||||||
FeatherIcons.bookmark,
|
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
|
||||||
color: settings.notificationsGradesEnabled
|
onPressed: () {
|
||||||
? Theme.of(context).colorScheme.secondary
|
settings.update(
|
||||||
: AppColors.of(context).text.withOpacity(.25),
|
notificationsGradesEnabled:
|
||||||
),
|
!settings.notificationsGradesEnabled);
|
||||||
const SizedBox(width: 14.0),
|
setAll(context, DateTime.now());
|
||||||
Expanded(
|
},
|
||||||
child: Text(
|
title: Text(
|
||||||
"grades".i18n,
|
"grades".i18n,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context).text.withOpacity(
|
color: AppColors.of(context).text.withOpacity(
|
||||||
settings.notificationsGradesEnabled
|
settings.notificationsGradesEnabled ? .95 : .25),
|
||||||
? 1.0
|
|
||||||
: .5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
leading: Icon(
|
||||||
|
FeatherIcons.bookmark,
|
||||||
|
size: 22.0,
|
||||||
|
color: AppColors.of(context).text.withOpacity(
|
||||||
|
settings.notificationsGradesEnabled ? .95 : .25),
|
||||||
|
),
|
||||||
|
trailing: Switch(
|
||||||
|
onChanged: (v) =>
|
||||||
|
settings.update(notificationsGradesEnabled: v),
|
||||||
|
value: settings.notificationsGradesEnabled,
|
||||||
|
activeColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12.0),
|
||||||
|
bottom: Radius.circular(12.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
SplittedPanel(
|
||||||
),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
Material(
|
cardPadding: const EdgeInsets.all(4.0),
|
||||||
type: MaterialType.transparency,
|
isSeparated: true,
|
||||||
child: SwitchListTile(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
|
||||||
value: settings.notificationsAbsencesEnabled,
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(notificationsAbsencesEnabled: v),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
PanelButton(
|
||||||
FeatherIcons.clock,
|
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
|
||||||
color: settings.notificationsAbsencesEnabled
|
onPressed: () {
|
||||||
? Theme.of(context).colorScheme.secondary
|
settings.update(
|
||||||
: AppColors.of(context).text.withOpacity(.25),
|
notificationsAbsencesEnabled:
|
||||||
),
|
!settings.notificationsAbsencesEnabled);
|
||||||
const SizedBox(width: 14.0),
|
setAll(context, DateTime.now());
|
||||||
Expanded(
|
},
|
||||||
child: Text(
|
title: Text(
|
||||||
"absences".i18n,
|
"absences".i18n,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context).text.withOpacity(
|
color: AppColors.of(context).text.withOpacity(
|
||||||
settings.notificationsAbsencesEnabled
|
settings.notificationsAbsencesEnabled ? .95 : .25),
|
||||||
? 1.0
|
|
||||||
: .5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
leading: Icon(
|
||||||
|
FeatherIcons.clock,
|
||||||
|
size: 22.0,
|
||||||
|
color: AppColors.of(context).text.withOpacity(
|
||||||
|
settings.notificationsAbsencesEnabled ? .95 : .25),
|
||||||
|
),
|
||||||
|
trailing: Switch(
|
||||||
|
onChanged: (v) =>
|
||||||
|
settings.update(notificationsAbsencesEnabled: v),
|
||||||
|
value: settings.notificationsAbsencesEnabled,
|
||||||
|
activeColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12.0),
|
||||||
|
bottom: Radius.circular(12.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
SplittedPanel(
|
||||||
),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
Material(
|
cardPadding: const EdgeInsets.all(4.0),
|
||||||
type: MaterialType.transparency,
|
isSeparated: true,
|
||||||
child: SwitchListTile(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
|
||||||
value: settings.notificationsMessagesEnabled,
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(notificationsMessagesEnabled: v),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
PanelButton(
|
||||||
FeatherIcons.messageSquare,
|
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
|
||||||
color: settings.notificationsMessagesEnabled
|
onPressed: () {
|
||||||
? Theme.of(context).colorScheme.secondary
|
settings.update(
|
||||||
: AppColors.of(context).text.withOpacity(.25),
|
notificationsMessagesEnabled:
|
||||||
),
|
!settings.notificationsMessagesEnabled);
|
||||||
const SizedBox(width: 14.0),
|
setAll(context, DateTime.now());
|
||||||
Expanded(
|
},
|
||||||
child: Text(
|
title: Text(
|
||||||
"messages".i18n,
|
"messages".i18n,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context).text.withOpacity(
|
color: AppColors.of(context).text.withOpacity(
|
||||||
settings.notificationsMessagesEnabled
|
settings.notificationsMessagesEnabled ? .95 : .25),
|
||||||
? 1.0
|
|
||||||
: .5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
leading: Icon(
|
||||||
|
FeatherIcons.messageSquare,
|
||||||
|
size: 22.0,
|
||||||
|
color: AppColors.of(context).text.withOpacity(
|
||||||
|
settings.notificationsMessagesEnabled ? .95 : .25),
|
||||||
|
),
|
||||||
|
trailing: Switch(
|
||||||
|
onChanged: (v) =>
|
||||||
|
settings.update(notificationsMessagesEnabled: v),
|
||||||
|
value: settings.notificationsMessagesEnabled,
|
||||||
|
activeColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12.0),
|
||||||
|
bottom: Radius.circular(12.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
SplittedPanel(
|
||||||
),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
Material(
|
cardPadding: const EdgeInsets.all(4.0),
|
||||||
type: MaterialType.transparency,
|
isSeparated: true,
|
||||||
child: SwitchListTile(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
|
||||||
value: settings.notificationsLessonsEnabled,
|
|
||||||
onChanged: (v) =>
|
|
||||||
settings.update(notificationsLessonsEnabled: v),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
PanelButton(
|
||||||
FeatherIcons.calendar,
|
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
|
||||||
color: settings.notificationsLessonsEnabled
|
onPressed: () {
|
||||||
? Theme.of(context).colorScheme.secondary
|
settings.update(
|
||||||
: AppColors.of(context).text.withOpacity(.25),
|
notificationsLessonsEnabled:
|
||||||
),
|
!settings.notificationsLessonsEnabled);
|
||||||
const SizedBox(width: 14.0),
|
setAll(context, DateTime.now());
|
||||||
Expanded(
|
},
|
||||||
child: Text(
|
title: Text(
|
||||||
"lessons".i18n,
|
"lessons".i18n,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16.0,
|
|
||||||
color: AppColors.of(context).text.withOpacity(
|
color: AppColors.of(context).text.withOpacity(
|
||||||
settings.notificationsLessonsEnabled
|
settings.notificationsLessonsEnabled ? .95 : .25),
|
||||||
? 1.0
|
|
||||||
: .5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
leading: Icon(
|
||||||
|
FeatherIcons.bookmark,
|
||||||
|
size: 22.0,
|
||||||
|
color: AppColors.of(context).text.withOpacity(
|
||||||
|
settings.notificationsLessonsEnabled ? .95 : .25),
|
||||||
|
),
|
||||||
|
trailing: Switch(
|
||||||
|
onChanged: (v) =>
|
||||||
|
settings.update(notificationsLessonsEnabled: v),
|
||||||
|
value: settings.notificationsLessonsEnabled,
|
||||||
|
activeColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12.0),
|
||||||
|
bottom: Radius.circular(12.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
// only used for debugging, pressing **will** cause notification spam
|
||||||
|
kDebugMode
|
||||||
|
? SplittedPanel(
|
||||||
|
padding: const EdgeInsets.only(top: 9.0),
|
||||||
|
cardPadding: const EdgeInsets.all(4.0),
|
||||||
|
isSeparated: true,
|
||||||
|
children: [
|
||||||
|
PanelButton(
|
||||||
|
onPressed: () => setAll(
|
||||||
|
context, DateTime(1970, 1, 1, 0, 0, 0, 0, 0)),
|
||||||
|
title: Text("set_all_as_unseen".i18n),
|
||||||
|
leading: Icon(
|
||||||
|
FeatherIcons.mail,
|
||||||
|
size: 22.0,
|
||||||
|
color: AppColors.of(context).text.withOpacity(0.95),
|
||||||
),
|
),
|
||||||
),
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12.0),
|
||||||
|
bottom: Radius.circular(4.0)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -8,7 +8,8 @@ extension SettingsLocalization on String {
|
|||||||
"grades": "Grades",
|
"grades": "Grades",
|
||||||
"absences": "Absences",
|
"absences": "Absences",
|
||||||
"messages": "Messages",
|
"messages": "Messages",
|
||||||
"lessons": "Lessons"
|
"lessons": "Lessons",
|
||||||
|
"set_all_as_unseen": "Set all as unseen",
|
||||||
|
|
||||||
},
|
},
|
||||||
"hu_hu": {
|
"hu_hu": {
|
||||||
@ -16,14 +17,16 @@ extension SettingsLocalization on String {
|
|||||||
"grades": "Jegyek",
|
"grades": "Jegyek",
|
||||||
"absences": "Hiányzások",
|
"absences": "Hiányzások",
|
||||||
"messages": "Üzenetek",
|
"messages": "Üzenetek",
|
||||||
"lessons": "Órák"
|
"lessons": "Órák",
|
||||||
|
"set_all_as_unseen": "Összes kategória beállítása olvasatlannak",
|
||||||
},
|
},
|
||||||
"de_de": {
|
"de_de": {
|
||||||
"notifications_screen": "Mitteilung",
|
"notifications_screen": "Mitteilung",
|
||||||
"grades": "Noten",
|
"grades": "Noten",
|
||||||
"absences": "Fehlen",
|
"absences": "Fehlen",
|
||||||
"messages": "Nachrichten",
|
"messages": "Nachrichten",
|
||||||
"lessons": "Unterricht"
|
"lessons": "Unterricht",
|
||||||
|
"set_all_as_unseen": "Alle als ungelesen einstellen",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ dependencies:
|
|||||||
google_fonts: ^6.1.0
|
google_fonts: ^6.1.0
|
||||||
flutter_any_logo: ^1.1.1
|
flutter_any_logo: ^1.1.1
|
||||||
custom_sliding_segmented_control: ^1.8.1
|
custom_sliding_segmented_control: ^1.8.1
|
||||||
|
get_it: ^7.6.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^3.0.1
|
flutter_lints: ^3.0.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user