finished notification fix

This commit is contained in:
hihihaha 2024-03-10 17:38:22 +01:00
parent f6ff98c5ed
commit b8e4c4ea3a
13 changed files with 306 additions and 177 deletions

View File

@ -69,6 +69,7 @@ const userDataDB = DatabaseStruct("user_data", {
// "subject_lesson_count": String, // non kreta data
// notifications and surprise grades // non kreta data
"last_seen_grade": int,
"last_seen_surprisegrade": int,
"last_seen_absence": int,
"last_seen_message": int,
"last_seen_lesson": int,
@ -134,10 +135,11 @@ Future<Database> initDB(DatabaseProvider database) async {
// renamed teachers // non kreta data
"renamed_teachers": "{}",
// "subject_lesson_count": "{}", // non kreta data
"last_seen_grade": 0,
"last_seen_absence": 0,
"last_seen_message": 0,
"last_seen_lesson": 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_plans": "{}",
"goal_averages": "{}",
@ -215,3 +217,4 @@ Future<void> migrateDB(
print("INFO: Database migrated");
}
}

View File

@ -136,6 +136,7 @@ class UserDatabaseStore {
await db.update("user_data", {"last_seen_${category.name}": lastSeenDate},
where: "id = ?", whereArgs: [userId]);
}
// renamed things
Future<void> storeRenamedSubjects(Map<String, String> subjects,

View File

@ -1,9 +1,12 @@
import 'package:flutter/foundation.dart';
import 'package:refilc/api/providers/database_provider.dart';
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/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/client.dart';
import 'package:refilc_kreta_api/models/absence.dart';
@ -18,8 +21,10 @@ import 'package:i18n_extension/i18n_widget.dart';
import 'package:intl/intl.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
@ -91,20 +96,14 @@ class NotificationsHelper {
.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) {
if (grade.date.isAfter(lastSeenGrade) &&
DateTime.now().difference(grade.date).inDays < 7) {
// send notificiation about new grade
AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
@ -132,6 +131,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "grades"
);
} else {
// multiple users are added, also display student name
@ -149,6 +149,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "grades"
);
}
}
@ -200,6 +201,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "absences"
);
} else {
await flutterLocalNotificationsPlugin.show(
@ -217,6 +219,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "absences"
);
}
}
@ -271,6 +274,7 @@ class NotificationsHelper {
message.author,
message.content.replaceAll(RegExp(r'<[^>]*>'), ''),
notificationDetails,
payload: "messages",
);
} else {
await flutterLocalNotificationsPlugin.show(
@ -278,6 +282,7 @@ class NotificationsHelper {
"(${currentuserProvider.displayName!}) ${message.author}",
message.content.replaceAll(RegExp(r'<[^>]*>'), ''),
notificationDetails,
payload: "messages",
);
}
}
@ -297,8 +302,12 @@ class NotificationsHelper {
DateTime lastSeenLesson = await database.userQuery.lastSeen(
userId: currentuserProvider.id!, category: LastSeenCategory.lesson);
Lesson? latestLesson;
for (Lesson lesson in apilessons) {
if((lesson.status?.name != "Elmaradt" || lesson.substituteTeacher?.name != "") && lesson.date.isAfter(latestLesson?.start ?? DateTime(1970))) {
latestLesson = lesson;
}
if (lesson.date.isAfter(lastSeenLesson)) {
AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
@ -329,6 +338,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable"
);
break;
}
@ -345,6 +355,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable"
);
break;
}
@ -361,11 +372,12 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable"
);
break;
}
}
} else if (lesson.substituteTeacher?.name != "") {
} else if (lesson.substituteTeacher?.name != "" && lesson.substituteTeacher != null) {
switch (I18n.localeStr) {
case "en_en":
{
@ -383,6 +395,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -402,6 +415,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -421,6 +435,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -443,6 +458,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -460,6 +476,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -477,6 +494,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -500,6 +518,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -520,6 +539,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -540,6 +560,7 @@ class NotificationsHelper {
],
),
notificationDetails,
payload: "timetable",
);
break;
}
@ -548,7 +569,44 @@ class NotificationsHelper {
}
}
}
await database.userStore.storeLastSeen(DateTime.now(),
// lesson.date does not contain time, only the date
await database.userStore.storeLastSeen(latestLesson?.start ?? DateTime.now(),
userId: currentuserProvider.id!, category: LastSeenCategory.lesson);
}
// Called when the user taps a notification
void onDidReceiveNotificationResponse(NotificationResponse notificationResponse) async {
final String? payload = notificationResponse.payload;
if (notificationResponse.payload != null) {
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);
}
}
}
}

View File

@ -47,9 +47,9 @@ extension Localization on String {
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) {
final DatabaseProvider databaseProvider = DatabaseProvider();
databaseProvider.init().then((value) {
databaseProvider.query.getSettings(databaseProvider).then((settings) {
return localize(this, _t, locale: "${settings.language}_${settings.language.toUpperCase()}");
});
});

View File

@ -10,6 +10,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:refilc/app.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_report_screen.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
@ -24,19 +26,22 @@ void main() async {
// ignore: deprecated_member_use
binding.renderView.automaticSystemUiAdjustment = false;
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// navigation
setupLocator();
// Startup
Startup startup = Startup();
await startup.start();
// Custom error page
ErrorWidget.builder = errorBuilder;
BackgroundFetch.registerHeadlessTask(backgroundHeadlessTask);
// initialize stripe key
stripe.Stripe.publishableKey =
'pk_test_51Oo7iUBS0FxsTGxKjGZSQqzDKWHY5ZFYM9XeI0qSdIh2w8jWy6GhHlYpT7GLTzgpl1xhE5YP4BXpA4gMZqPmgMId00cGFYFzbh';
BackgroundFetch.registerHeadlessTask(backgroundHeadlessTask);
// Run App
runApp(App(
database: startup.database,
@ -58,6 +63,9 @@ class Startup {
settings = await database.query.getSettings(database);
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;
// Notifications setup
if (!kIsWeb) {
@ -118,6 +126,7 @@ class Startup {
// Initialize notifications
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: NotificationsHelper().onDidReceiveNotificationResponse,
);
}

View 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);
}
}

View 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());
}

View File

@ -78,6 +78,7 @@ dependencies:
maps_launcher: ^2.2.0
google_fonts: ^6.1.0
flutter_stripe: ^10.0.0
get_it: ^7.6.7
dev_dependencies:
flutter_lints: ^3.0.1
@ -97,6 +98,7 @@ flutter:
- assets/images/subject_covers/
- assets/launch_icons/
- assets/images/ext_logo/
- assets/svg/menu_icons/
fonts:
- family: FilcIcons
@ -161,6 +163,7 @@ flutter:
weight: 700
style: italic
flutter_launcher_icons:
image_path: assets/icons/ic_android.png
android: true

View File

@ -50,7 +50,7 @@ class GradeProvider with ChangeNotifier {
String? userId = _user.id;
if (userId != null) {
final userStore = _database.userStore;
userStore.storeLastSeen(DateTime.now(), userId: userId, category: LastSeenCategory.grade);
userStore.storeLastSeen(DateTime.now(), userId: userId, category: LastSeenCategory.surprisegrade);
_lastSeen = DateTime.now();
}
}
@ -59,7 +59,7 @@ class GradeProvider with ChangeNotifier {
String? userId = _user.id;
if (userId != null) {
final userStore = _database.userStore;
userStore.storeLastSeen(DateTime(1969), userId: userId, category: LastSeenCategory.grade);
userStore.storeLastSeen(DateTime(1969), userId: userId, category: LastSeenCategory.surprisegrade);
_lastSeen = DateTime(1969);
}
}
@ -75,7 +75,7 @@ class GradeProvider with ChangeNotifier {
await convertBySettings();
_groupAvg = await userQuery.getGroupAverages(userId: userId);
notifyListeners();
DateTime lastSeenDB = await userQuery.lastSeen(userId: userId, category: LastSeenCategory.grade);
DateTime lastSeenDB = await userQuery.lastSeen(userId: userId, category: LastSeenCategory.surprisegrade);
if (lastSeenDB.millisecondsSinceEpoch == 0 ||
lastSeenDB.year == 0 ||
!_settings.gradeOpeningFun) {

View File

@ -2,6 +2,8 @@ import 'package:refilc/api/providers/update_provider.dart';
import 'package:refilc/helpers/quick_actions.dart';
import 'package:refilc/models/settings.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/providers/grade_provider.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart';
@ -42,7 +44,7 @@ class NavigationScreenState extends State<NavigationScreen>
with WidgetsBindingObserver {
late NavigationRoute selected;
List<String> initializers = [];
final _navigatorState = GlobalKey<NavigatorState>();
final _navigatorState = locator<NavigationService>().navigatorKey;
late SettingsProvider settings;
late NewsProvider newsProvider;

View File

@ -1,3 +1,4 @@
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';
@ -5,12 +6,12 @@ import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc/theme/colors/colors.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:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
import 'notifications_screen.i18n.dart';
class MenuNotifications extends StatelessWidget {
@ -57,13 +58,15 @@ class MenuNotifications extends StatelessWidget {
class NotificationsScreen extends StatelessWidget {
const NotificationsScreen({super.key});
void setAllAsSeen(BuildContext context) {
// Set all notification categories as seen to avoid spamming the user with notifications when they turn on notifications
DatabaseProvider database = Provider.of<DatabaseProvider>(context, listen: false);
// 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(DateTime.now(), userId: user.id, category: category);
if (user != null) {
for (LastSeenCategory category in LastSeenCategory.values) {
database.userStore
.storeLastSeen(date, userId: user.id, category: category);
}
}
}
@ -84,159 +87,188 @@ class NotificationsScreen extends StatelessWidget {
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
child: Panel(
child: Column(
children: [
Material(
type: MaterialType.transparency,
child: SwitchListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
value: settings.notificationsGradesEnabled,
onChanged: (v) {
settings.update(notificationsGradesEnabled: v);
setAllAsSeen(context);
child: Column(
children: [
SplittedPanel(
padding: const EdgeInsets.only(top: 8.0),
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
children: [
PanelButton(
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
onPressed: () {
settings.update(
notificationsEnabled:
!settings.notificationsGradesEnabled);
setAll(context, DateTime.now());
},
title: Row(
children: [
Icon(
FeatherIcons.bookmark,
color: settings.notificationsGradesEnabled
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"grades".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(
settings.notificationsGradesEnabled
? 1.0
: .5,
),
),
),
),
],
title: Text(
"grades".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.notificationsGradesEnabled ? .95 : .25),
),
),
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),
),
),
),
Material(
type: MaterialType.transparency,
child: SwitchListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
value: settings.notificationsAbsencesEnabled,
onChanged: (v) {
settings.update(notificationsAbsencesEnabled: v);
setAllAsSeen(context);
],
),
SplittedPanel(
padding: const EdgeInsets.only(top: 8.0),
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
children: [
PanelButton(
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
onPressed: () {
settings.update(
notificationsEnabled:
!settings.notificationsAbsencesEnabled);
setAll(context, DateTime.now());
},
title: Row(
children: [
Icon(
FeatherIcons.clock,
color: settings.notificationsAbsencesEnabled
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"absences".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(
settings.notificationsAbsencesEnabled
? 1.0
: .5,
),
),
),
),
],
title: Text(
"absences".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.notificationsAbsencesEnabled ? .95 : .25),
),
),
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),
),
),
),
Material(
type: MaterialType.transparency,
child: SwitchListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
value: settings.notificationsMessagesEnabled,
onChanged: (v) {
settings.update(notificationsMessagesEnabled: v);
setAllAsSeen(context);
],
),
SplittedPanel(
padding: const EdgeInsets.only(top: 8.0),
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
children: [
PanelButton(
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
onPressed: () {
settings.update(
notificationsEnabled:
!settings.notificationsMessagesEnabled);
setAll(context, DateTime.now());
},
title: Row(
children: [
Icon(
FeatherIcons.messageSquare,
color: settings.notificationsMessagesEnabled
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 14.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,
),
),
),
),
],
title: Text(
"messages".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.notificationsMessagesEnabled ? .95 : .25),
),
),
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),
),
),
),
Material(
type: MaterialType.transparency,
child: SwitchListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
value: settings.notificationsLessonsEnabled,
onChanged: (v) {
settings.update(notificationsLessonsEnabled: v);
setAllAsSeen(context);
],
),
SplittedPanel(
padding: const EdgeInsets.only(top: 8.0),
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
children: [
PanelButton(
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
onPressed: () {
settings.update(
notificationsEnabled:
!settings.notificationsLessonsEnabled);
setAll(context, DateTime.now());
},
title: Row(
children: [
Icon(
FeatherIcons.calendar,
color: settings.notificationsLessonsEnabled
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"lessons".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(
settings.notificationsLessonsEnabled
? 1.0
: .5,
),
),
),
),
],
title: Text(
"lessons".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.notificationsLessonsEnabled ? .95 : .25),
),
),
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(),
],
),
),
),

View File

@ -8,7 +8,8 @@ extension SettingsLocalization on String {
"grades": "Grades",
"absences": "Absences",
"messages": "Messages",
"lessons": "Lessons"
"lessons": "Lessons",
"set_all_as_unseen": "Set all as unseen",
},
"hu_hu": {
@ -16,14 +17,16 @@ extension SettingsLocalization on String {
"grades": "Jegyek",
"absences": "Hiányzások",
"messages": "Üzenetek",
"lessons": "Órák"
"lessons": "Órák",
"set_all_as_unseen": "Összes kategória beállítása olvasatlannak",
},
"de_de": {
"notifications_screen": "Mitteilung",
"grades": "Noten",
"absences": "Fehlen",
"messages": "Nachrichten",
"lessons": "Unterricht"
"lessons": "Unterricht",
"set_all_as_unseen": "Alle als ungelesen einstellen",
},
};

View File

@ -67,9 +67,10 @@ dependencies:
flutter_stripe: ^10.0.0
flutter_any_logo: ^1.1.1
custom_sliding_segmented_control: ^1.8.1
get_it: ^7.6.7
dev_dependencies:
flutter_lints: ^3.0.1
flutter:
uses-material-design: true
uses-material-design: true