diff --git a/refilc/lib/database/init.dart b/refilc/lib/database/init.dart index fef7fd1..ff5c601 100644 --- a/refilc/lib/database/init.dart +++ b/refilc/lib/database/init.dart @@ -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 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 migrateDB( print("INFO: Database migrated"); } } + diff --git a/refilc/lib/database/store.dart b/refilc/lib/database/store.dart index 2a64bfb..4b0f23f 100644 --- a/refilc/lib/database/store.dart +++ b/refilc/lib/database/store.dart @@ -136,6 +136,7 @@ class UserDatabaseStore { await db.update("user_data", {"last_seen_${category.name}": lastSeenDate}, where: "id = ?", whereArgs: [userId]); } + // renamed things Future storeRenamedSubjects(Map subjects, diff --git a/refilc/lib/helpers/notification_helper.dart b/refilc/lib/helpers/notification_helper.dart index 4ae83df..fa1ed8a 100644 --- a/refilc/lib/helpers/notification_helper.dart +++ b/refilc/lib/helpers/notification_helper.dart @@ -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().navigateTo("timetable"); + break; + case "grades": + locator().navigateTo("grades"); + break; + case "messages": + locator().navigateTo("messages"); + break; + case "absences": + locator().navigateTo("absences"); + break; + case "settings": + locator().navigateTo("settings"); + break; + default: + break; + } + } + + // Set all notification categories to seen + Future 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); + } + } + } } diff --git a/refilc/lib/helpers/notification_helper.i18n.dart b/refilc/lib/helpers/notification_helper.i18n.dart index bc4b866..49db9f7 100644 --- a/refilc/lib/helpers/notification_helper.i18n.dart +++ b/refilc/lib/helpers/notification_helper.i18n.dart @@ -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()}"); }); }); diff --git a/refilc/lib/main.dart b/refilc/lib/main.dart index f33a476..327f25e 100644 --- a/refilc/lib/main.dart +++ b/refilc/lib/main.dart @@ -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, ); } diff --git a/refilc/lib/utils/navigation_service.dart b/refilc/lib/utils/navigation_service.dart new file mode 100644 index 0000000..ee8a479 --- /dev/null +++ b/refilc/lib/utils/navigation_service.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +class NavigationService { + final GlobalKey navigatorKey = GlobalKey(); + + Future navigateTo(String routeName) { + return navigatorKey.currentState!.pushNamed(routeName); + } +} \ No newline at end of file diff --git a/refilc/lib/utils/service_locator.dart b/refilc/lib/utils/service_locator.dart new file mode 100644 index 0000000..0ca94b7 --- /dev/null +++ b/refilc/lib/utils/service_locator.dart @@ -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()); +} \ No newline at end of file diff --git a/refilc/pubspec.yaml b/refilc/pubspec.yaml index 918146e..6d05e57 100644 --- a/refilc/pubspec.yaml +++ b/refilc/pubspec.yaml @@ -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 diff --git a/refilc_kreta_api/lib/providers/grade_provider.dart b/refilc_kreta_api/lib/providers/grade_provider.dart index 2d61b28..3ab9701 100644 --- a/refilc_kreta_api/lib/providers/grade_provider.dart +++ b/refilc_kreta_api/lib/providers/grade_provider.dart @@ -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) { diff --git a/refilc_mobile_ui/lib/screens/navigation/navigation_screen.dart b/refilc_mobile_ui/lib/screens/navigation/navigation_screen.dart index 4659feb..22ca045 100644 --- a/refilc_mobile_ui/lib/screens/navigation/navigation_screen.dart +++ b/refilc_mobile_ui/lib/screens/navigation/navigation_screen.dart @@ -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 with WidgetsBindingObserver { late NavigationRoute selected; List initializers = []; - final _navigatorState = GlobalKey(); + final _navigatorState = locator().navigatorKey; late SettingsProvider settings; late NewsProvider newsProvider; diff --git a/refilc_mobile_ui/lib/screens/settings/notifications_screen.dart b/refilc_mobile_ui/lib/screens/settings/notifications_screen.dart index ba4b618..b3cdbe2 100644 --- a/refilc_mobile_ui/lib/screens/settings/notifications_screen.dart +++ b/refilc_mobile_ui/lib/screens/settings/notifications_screen.dart @@ -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(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(context, listen: false); User? user = Provider.of(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(), + ], ), ), ), diff --git a/refilc_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart b/refilc_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart index c9d0087..310779f 100644 --- a/refilc_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart +++ b/refilc_mobile_ui/lib/screens/settings/notifications_screen.i18n.dart @@ -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", }, }; diff --git a/refilc_mobile_ui/pubspec.yaml b/refilc_mobile_ui/pubspec.yaml index b630c65..36cbc33 100644 --- a/refilc_mobile_ui/pubspec.yaml +++ b/refilc_mobile_ui/pubspec.yaml @@ -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 \ No newline at end of file + uses-material-design: true