diff --git a/.gitignore b/.gitignore index 241895d..eb2131f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,9 +29,11 @@ filcnaplo_premium/.flutter-plugins filcnaplo_premium/.flutter-plugins-dependencies filcnaplo_premium/pubspec.lock filcnaplo_premium/.dart_tool/ -filcnaplo_premium/android/ .vscode .github .idea .gitmodules + +filcnaplo/.DS_Store +.DS_Store diff --git a/.gitmodules b/.gitmodules index e69de29..edc2455 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "filcnaplo_premium"] + path = filcnaplo_premium + url = git@github.com:refilc/naplo-plus.git diff --git a/filcnaplo/.metadata b/filcnaplo/.metadata index 17008cb..40e4102 100644 --- a/filcnaplo/.metadata +++ b/filcnaplo/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "b0366e0a3f089e15fd89c97604ab402fe26b724c" channel: "stable" project_type: app @@ -13,17 +13,17 @@ project_type: app migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c + base_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c - platform: linux - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c + base_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c - platform: macos - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c + base_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c - platform: windows - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c + base_revision: b0366e0a3f089e15fd89c97604ab402fe26b724c # User provided section diff --git a/filcnaplo/android/app/src/main/AndroidManifest.xml b/filcnaplo/android/app/src/main/AndroidManifest.xml index 14d49f3..ef5b9d0 100644 --- a/filcnaplo/android/app/src/main/AndroidManifest.xml +++ b/filcnaplo/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:requestLegacyExternalStorage="true" android:enableOnBackInvokedCallback="false"> - + + @@ -39,6 +40,69 @@ + + + + diff --git a/filcnaplo/android/app/src/main/res/ic_launcher-web.png b/filcnaplo/android/app/src/main/res/ic_launcher-web.png index 90d1850..25ed396 100644 Binary files a/filcnaplo/android/app/src/main/res/ic_launcher-web.png and b/filcnaplo/android/app/src/main/res/ic_launcher-web.png differ diff --git a/filcnaplo/android/app/src/main/res/playstore-icon.png b/filcnaplo/android/app/src/main/res/playstore-icon.png index 90d1850..25ed396 100644 Binary files a/filcnaplo/android/app/src/main/res/playstore-icon.png and b/filcnaplo/android/app/src/main/res/playstore-icon.png differ diff --git a/filcnaplo/assets/launch_icons/refilc_concept.png b/filcnaplo/assets/launch_icons/refilc_concept.png new file mode 100644 index 0000000..bc031ab Binary files /dev/null and b/filcnaplo/assets/launch_icons/refilc_concept.png differ diff --git a/filcnaplo/assets/launch_icons/refilc_default.png b/filcnaplo/assets/launch_icons/refilc_default.png new file mode 100644 index 0000000..ab54f58 Binary files /dev/null and b/filcnaplo/assets/launch_icons/refilc_default.png differ diff --git a/filcnaplo/assets/launch_icons/refilc_overcomplicated.png b/filcnaplo/assets/launch_icons/refilc_overcomplicated.png new file mode 100644 index 0000000..de1f125 Binary files /dev/null and b/filcnaplo/assets/launch_icons/refilc_overcomplicated.png differ diff --git a/filcnaplo/assets/launch_icons/refilc_pride.png b/filcnaplo/assets/launch_icons/refilc_pride.png new file mode 100644 index 0000000..2b1a305 Binary files /dev/null and b/filcnaplo/assets/launch_icons/refilc_pride.png differ diff --git a/filcnaplo/ios/Runner.xcodeproj/project.pbxproj b/filcnaplo/ios/Runner.xcodeproj/project.pbxproj index d93b9f7..df89d5d 100644 --- a/filcnaplo/ios/Runner.xcodeproj/project.pbxproj +++ b/filcnaplo/ios/Runner.xcodeproj/project.pbxproj @@ -17,6 +17,14 @@ 3127F7A828EAEE8500C2EFB3 /* lesson_model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3127F7A728EAEE8500C2EFB3 /* lesson_model.swift */; }; 373A6ECB5FC71FE9D8AF2EDB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F0ADD56276103500A3016C8 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B4461282AE6E0FF00AAE6FD /* refilc_default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461202AE6E0FF00AAE6FD /* refilc_default@2x.png */; }; + 3B4461292AE6E0FF00AAE6FD /* refilc_concept@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461212AE6E0FF00AAE6FD /* refilc_concept@2x.png */; }; + 3B44612A2AE6E0FF00AAE6FD /* refilc_concept@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461222AE6E0FF00AAE6FD /* refilc_concept@3x.png */; }; + 3B44612B2AE6E0FF00AAE6FD /* refilc_overcomplicated@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461232AE6E0FF00AAE6FD /* refilc_overcomplicated@2x.png */; }; + 3B44612C2AE6E0FF00AAE6FD /* refilc_default@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461242AE6E0FF00AAE6FD /* refilc_default@3x.png */; }; + 3B44612D2AE6E0FF00AAE6FD /* refilc_overcomplicated@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461252AE6E0FF00AAE6FD /* refilc_overcomplicated@3x.png */; }; + 3B44612E2AE6E0FF00AAE6FD /* refilc_pride@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461262AE6E0FF00AAE6FD /* refilc_pride@2x.png */; }; + 3B44612F2AE6E0FF00AAE6FD /* refilc_pride@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B4461272AE6E0FF00AAE6FD /* refilc_pride@3x.png */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -73,6 +81,14 @@ 3127F7A728EAEE8500C2EFB3 /* lesson_model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = lesson_model.swift; sourceTree = ""; }; 317DE77A294F6FFB002E323E /* livecard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = livecard.entitlements; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B4461202AE6E0FF00AAE6FD /* refilc_default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_default@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_default@2x.png"; sourceTree = ""; }; + 3B4461212AE6E0FF00AAE6FD /* refilc_concept@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_concept@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_concept@2x.png"; sourceTree = ""; }; + 3B4461222AE6E0FF00AAE6FD /* refilc_concept@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_concept@3x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized (1)/refilc_concept@3x.png"; sourceTree = ""; }; + 3B4461232AE6E0FF00AAE6FD /* refilc_overcomplicated@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_overcomplicated@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_overcomplicated@2x.png"; sourceTree = ""; }; + 3B4461242AE6E0FF00AAE6FD /* refilc_default@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_default@3x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized (1)/refilc_default@3x.png"; sourceTree = ""; }; + 3B4461252AE6E0FF00AAE6FD /* refilc_overcomplicated@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_overcomplicated@3x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized (1)/refilc_overcomplicated@3x.png"; sourceTree = ""; }; + 3B4461262AE6E0FF00AAE6FD /* refilc_pride@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_pride@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_pride@2x.png"; sourceTree = ""; }; + 3B4461272AE6E0FF00AAE6FD /* refilc_pride@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_pride@3x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized (1)/refilc_pride@3x.png"; sourceTree = ""; }; 707F8089D970F81C480F73C4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -122,6 +138,21 @@ path = livecard; sourceTree = ""; }; + 3B44611F2AE6DF7200AAE6FD /* App Icons */ = { + isa = PBXGroup; + children = ( + 3B4461212AE6E0FF00AAE6FD /* refilc_concept@2x.png */, + 3B4461222AE6E0FF00AAE6FD /* refilc_concept@3x.png */, + 3B4461202AE6E0FF00AAE6FD /* refilc_default@2x.png */, + 3B4461242AE6E0FF00AAE6FD /* refilc_default@3x.png */, + 3B4461232AE6E0FF00AAE6FD /* refilc_overcomplicated@2x.png */, + 3B4461252AE6E0FF00AAE6FD /* refilc_overcomplicated@3x.png */, + 3B4461262AE6E0FF00AAE6FD /* refilc_pride@2x.png */, + 3B4461272AE6E0FF00AAE6FD /* refilc_pride@3x.png */, + ); + path = "App Icons"; + sourceTree = ""; + }; 6640A963014A9D4F31026053 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -178,6 +209,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 3B44611F2AE6DF7200AAE6FD /* App Icons */, 3127F73928EAEC3200C2EFB3 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, @@ -286,10 +318,18 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3B44612A2AE6E0FF00AAE6FD /* refilc_concept@3x.png in Resources */, + 3B44612F2AE6E0FF00AAE6FD /* refilc_pride@3x.png in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B44612E2AE6E0FF00AAE6FD /* refilc_pride@2x.png in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 3B44612C2AE6E0FF00AAE6FD /* refilc_default@3x.png in Resources */, + 3B4461292AE6E0FF00AAE6FD /* refilc_concept@2x.png in Resources */, + 3B4461282AE6E0FF00AAE6FD /* refilc_default@2x.png in Resources */, + 3B44612D2AE6E0FF00AAE6FD /* refilc_overcomplicated@3x.png in Resources */, + 3B44612B2AE6E0FF00AAE6FD /* refilc_overcomplicated@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/filcnaplo/ios/Runner/Info.plist b/filcnaplo/ios/Runner/Info.plist index f569c39..aa695d8 100644 --- a/filcnaplo/ios/Runner/Info.plist +++ b/filcnaplo/ios/Runner/Info.plist @@ -2,6 +2,59 @@ + CFBundleAlternateIcons + + refilc_pride + + CFBundleIconFiles + + refilc_pride + + UIPrerenderedIcon + + + refilc_overcomplicated + + CFBundleIconFiles + + refilc_overcomplicated + + UIPrerenderedIcon + + + refilc_concept + + CFBundleIconFiles + + refilc_concept + + UIPrerenderedIcon + + + refilc_default + + CFBundleIconFiles + + refilc_default + + UIPrerenderedIcon + + + + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconName + + CFBundleIconFiles + + + + UIPrerenderedIcon + + + BGTaskSchedulerPermittedIdentifiers com.transistorsoft.fetch diff --git a/filcnaplo/lib/api/login.dart b/filcnaplo/lib/api/login.dart index f0834c8..0d2f1cc 100644 --- a/filcnaplo/lib/api/login.dart +++ b/filcnaplo/lib/api/login.dart @@ -163,6 +163,8 @@ Future loginAPI({ Provider.of(context, listen: false).fetch(), Provider.of(context, listen: false).fetch(), Provider.of(context, listen: false).fetchAll(), + Provider.of(context, listen: false) + .fetchAllRecipients(), Provider.of(context, listen: false).fetch(), Provider.of(context, listen: false).fetch(), Provider.of(context, listen: false).fetch(), diff --git a/filcnaplo/lib/api/providers/sync.dart b/filcnaplo/lib/api/providers/sync.dart index 42f6277..56723f9 100644 --- a/filcnaplo/lib/api/providers/sync.dart +++ b/filcnaplo/lib/api/providers/sync.dart @@ -34,7 +34,8 @@ Future syncAll(BuildContext context) { print("INFO Syncing all"); UserProvider user = Provider.of(context, listen: false); - StatusProvider statusProvider = Provider.of(context, listen: false); + StatusProvider statusProvider = + Provider.of(context, listen: false); List> tasks = []; int taski = 0; @@ -47,10 +48,14 @@ Future syncAll(BuildContext context) { tasks = [ syncStatus(Provider.of(context, listen: false).fetch()), - syncStatus(Provider.of(context, listen: false).fetch(week: Week.current())), + syncStatus(Provider.of(context, listen: false) + .fetch(week: Week.current())), syncStatus(Provider.of(context, listen: false).fetch()), - syncStatus(Provider.of(context, listen: false).fetch(from: DateTime.now().subtract(const Duration(days: 30)))), + syncStatus(Provider.of(context, listen: false) + .fetch(from: DateTime.now().subtract(const Duration(days: 30)))), syncStatus(Provider.of(context, listen: false).fetchAll()), + syncStatus( + Provider.of(context, listen: false).fetchAllRecipients()), syncStatus(Provider.of(context, listen: false).fetch()), syncStatus(Provider.of(context, listen: false).fetch()), syncStatus(Provider.of(context, listen: false).fetch()), @@ -58,14 +63,17 @@ Future syncAll(BuildContext context) { // Sync student syncStatus(() async { if (user.user == null) return; - Map? studentJson = await Provider.of(context, listen: false).getAPI(KretaAPI.student(user.instituteCode!)); + Map? studentJson = await Provider.of(context, listen: false) + .getAPI(KretaAPI.student(user.instituteCode!)); if (studentJson == null) return; Student student = Student.fromJson(studentJson); user.user?.name = student.name; // Store user - await Provider.of(context, listen: false).store.storeUser(user.user!); + await Provider.of(context, listen: false) + .store + .storeUser(user.user!); }()), ]; diff --git a/filcnaplo/lib/app.dart b/filcnaplo/lib/app.dart index d12e5cc..313f523 100644 --- a/filcnaplo/lib/app.dart +++ b/filcnaplo/lib/app.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use + import 'dart:io'; import 'dart:math'; @@ -14,7 +16,7 @@ import 'package:filcnaplo/theme/theme.dart'; import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; import 'package:filcnaplo_premium/providers/goal_provider.dart'; -import 'package:filcnaplo_premium/providers/share_provider.dart'; +import 'package:filcnaplo_kreta_api/providers/share_provider.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -62,11 +64,10 @@ class App extends StatelessWidget { final DatabaseProvider database; const App( - {Key? key, + {super.key, required this.database, required this.settings, - required this.user}) - : super(key: key); + required this.user}); @override Widget build(BuildContext context) { diff --git a/filcnaplo/lib/database/init.dart b/filcnaplo/lib/database/init.dart index 47ce105..d00885f 100644 --- a/filcnaplo/lib/database/init.dart +++ b/filcnaplo/lib/database/init.dart @@ -34,7 +34,7 @@ const settingsDB = DatabaseStruct("settings", { "renamed_subjects_italics": int, "renamed_teachers_enabled": int, "renamed_teachers_italics": int, "live_activity_color": String, - "welcome_message": String, + "welcome_message": String, "app_icon": String, }); // DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING // YOU'VE BEEN WARNED!!! @@ -45,7 +45,7 @@ const usersDB = DatabaseStruct("users", { }); const userDataDB = DatabaseStruct("user_data", { "id": String, "grades": String, "timetable": String, "exams": String, - "homework": String, "messages": String, "notes": String, + "homework": String, "messages": String, "recipients": String, "notes": String, "events": String, "absences": String, "group_averages": String, // renamed subjects // non kreta data "renamed_subjects": String, @@ -101,7 +101,8 @@ Future initDB(DatabaseProvider database) async { ); await migrateDB(db, struct: userDataDB, defaultValues: { "grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]", - "messages": "[]", "notes": "[]", "events": "[]", "absences": "[]", + "messages": "[]", "recipients": "[]", "notes": "[]", "events": "[]", + "absences": "[]", "group_averages": "[]", // renamed subjects // non kreta data "renamed_subjects": "{}", diff --git a/filcnaplo/lib/database/query.dart b/filcnaplo/lib/database/query.dart index fb3094c..9115e41 100644 --- a/filcnaplo/lib/database/query.dart +++ b/filcnaplo/lib/database/query.dart @@ -122,6 +122,19 @@ class UserDatabaseQuery { return messages; } + Future> getRecipients({required String userId}) async { + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); + if (userData.isEmpty) return []; + String? recipientsJson = userData.elementAt(0)["recipients"] as String?; + if (recipientsJson == null) return []; + List recipients = (jsonDecode(recipientsJson) as List) + .map((e) => + SendRecipient.fromJson(e, SendRecipientType.fromJson(e['tipus']))) + .toList(); + return recipients; + } + Future> getNotes({required String userId}) async { List userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); diff --git a/filcnaplo/lib/database/store.dart b/filcnaplo/lib/database/store.dart index 185462d..e31adf9 100644 --- a/filcnaplo/lib/database/store.dart +++ b/filcnaplo/lib/database/store.dart @@ -86,6 +86,13 @@ class UserDatabaseStore { where: "id = ?", whereArgs: [userId]); } + Future storeRecipients(List recipients, + {required String userId}) async { + String recipientsJson = jsonEncode(recipients.map((e) => e.json).toList()); + await db.update("user_data", {"recipients": recipientsJson}, + where: "id = ?", whereArgs: [userId]); + } + Future storeNotes(List notes, {required String userId}) async { String notesJson = jsonEncode(notes.map((e) => e.json).toList()); await db.update("user_data", {"notes": notesJson}, diff --git a/filcnaplo/lib/helpers/attachment_helper.dart b/filcnaplo/lib/helpers/attachment_helper.dart index 187bef7..24b88b1 100644 --- a/filcnaplo/lib/helpers/attachment_helper.dart +++ b/filcnaplo/lib/helpers/attachment_helper.dart @@ -9,16 +9,20 @@ import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/models/attachment.dart'; import 'package:filcnaplo_kreta_api/models/homework.dart'; import 'package:flutter/widgets.dart'; -import 'package:open_file/open_file.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; extension AttachmentHelper on Attachment { - Future download(BuildContext context, {bool overwrite = false}) async { + Future download(BuildContext context, + {bool overwrite = false}) async { String downloads = await StorageHelper.downloadsPath(); - if (!overwrite && await File("$downloads/$name").exists()) return "$downloads/$name"; + if (!overwrite && await File("$downloads/$name").exists()) { + return "$downloads/$name"; + } - Uint8List data = await Provider.of(context, listen: false).getAPI(downloadUrl, rawResponse: true); + Uint8List data = await Provider.of(context, listen: false) + .getAPI(downloadUrl, rawResponse: true); if (!await StorageHelper.write("$downloads/$name", data)) return ""; return "$downloads/$name"; @@ -28,19 +32,24 @@ extension AttachmentHelper on Attachment { String downloads = await StorageHelper.downloadsPath(); if (!await File("$downloads/$name").exists()) await download(context); - var result = await OpenFile.open("$downloads/$name"); + var result = await OpenFilex.open("$downloads/$name"); return result.type == ResultType.done; } } extension HomeworkAttachmentHelper on HomeworkAttachment { - Future download(BuildContext context, {bool overwrite = false}) async { + Future download(BuildContext context, + {bool overwrite = false}) async { String downloads = await StorageHelper.downloadsPath(); - if (!overwrite && await File("$downloads/$name").exists()) return "$downloads/$name"; + if (!overwrite && await File("$downloads/$name").exists()) { + return "$downloads/$name"; + } - String url = downloadUrl(Provider.of(context, listen: false).instituteCode ?? ""); - Uint8List data = await Provider.of(context, listen: false).getAPI(url, rawResponse: true); + String url = downloadUrl( + Provider.of(context, listen: false).instituteCode ?? ""); + Uint8List data = await Provider.of(context, listen: false) + .getAPI(url, rawResponse: true); if (!await StorageHelper.write("$downloads/$name", data)) return ""; return "$downloads/$name"; @@ -50,7 +59,7 @@ extension HomeworkAttachmentHelper on HomeworkAttachment { String downloads = await StorageHelper.downloadsPath(); if (!await File("$downloads/$name").exists()) await download(context); - var result = await OpenFile.open("$downloads/$name"); + var result = await OpenFilex.open("$downloads/$name"); return result.type == ResultType.done; } } diff --git a/filcnaplo/lib/helpers/update_helper.dart b/filcnaplo/lib/helpers/update_helper.dart index 0c33860..2fd604e 100644 --- a/filcnaplo/lib/helpers/update_helper.dart +++ b/filcnaplo/lib/helpers/update_helper.dart @@ -5,7 +5,7 @@ import 'dart:typed_data'; import 'package:filcnaplo/api/client.dart'; import 'package:filcnaplo/helpers/storage_helper.dart'; import 'package:filcnaplo/models/release.dart'; -import 'package:open_file/open_file.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:permission_handler/permission_handler.dart'; enum UpdateState { none, preparing, downloading, installing } @@ -37,7 +37,7 @@ extension UpdateHelper on Release { (await Permission.manageExternalStorage.request().isGranted && await Permission.requestInstallPackages.request().isGranted); if (installPerms) { - var result = await OpenFile.open(apk.path); + var result = await OpenFilex.open(apk.path); if (result.type != ResultType.done) { // ignore: avoid_print diff --git a/filcnaplo/lib/main.dart b/filcnaplo/lib/main.dart index 9ceab63..c3ae0e0 100644 --- a/filcnaplo/lib/main.dart +++ b/filcnaplo/lib/main.dart @@ -17,6 +17,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; void main() async { // Initalize WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized(); + // ignore: deprecated_member_use binding.renderView.automaticSystemUiAdjustment = false; SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); // Startup @@ -63,7 +64,7 @@ class Startup { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>()! - .requestPermission(); + .requestNotificationsPermission(); } else if (Platform.isIOS) { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< diff --git a/filcnaplo/lib/models/settings.dart b/filcnaplo/lib/models/settings.dart index 9bbb2df..0499e65 100644 --- a/filcnaplo/lib/models/settings.dart +++ b/filcnaplo/lib/models/settings.dart @@ -79,6 +79,7 @@ class SettingsProvider extends ChangeNotifier { bool _renamedTeachersItalics; Color _liveActivityColor; String _welcomeMessage; + String _appIcon; SettingsProvider({ DatabaseProvider? database, @@ -126,6 +127,7 @@ class SettingsProvider extends ChangeNotifier { required bool renameTeachersItalics, required Color liveActivityColor, required String welcomeMessage, + required String appIcon, }) : _database = database, _language = language, _startPage = startPage, @@ -170,7 +172,8 @@ class SettingsProvider extends ChangeNotifier { _renamedTeachersEnabled = renameTeachersEnabled, _renamedTeachersItalics = renameTeachersItalics, _liveActivityColor = liveActivityColor, - _welcomeMessage = welcomeMessage; + _welcomeMessage = welcomeMessage, + _appIcon = appIcon; factory SettingsProvider.fromMap(Map map, {required DatabaseProvider database}) { @@ -235,6 +238,7 @@ class SettingsProvider extends ChangeNotifier { renameTeachersItalics: map["renamed_teachers_italics"] == 1, liveActivityColor: Color(map["live_activity_color"]), welcomeMessage: map["welcome_message"], + appIcon: map["app_icon"], ); } @@ -287,6 +291,7 @@ class SettingsProvider extends ChangeNotifier { "renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0, "live_activity_color": _liveActivityColor.value, "welcome_message": _welcomeMessage, + "app_icon": _appIcon, }; } @@ -343,6 +348,7 @@ class SettingsProvider extends ChangeNotifier { renameTeachersItalics: false, liveActivityColor: const Color(0xFF676767), welcomeMessage: '', + appIcon: 'refilc_default', ); } @@ -394,6 +400,7 @@ class SettingsProvider extends ChangeNotifier { bool get renamedTeachersItalics => _renamedTeachersItalics; Color get liveActivityColor => _liveActivityColor; String get welcomeMessage => _welcomeMessage; + String get appIcon => _appIcon; Future update({ bool store = true, @@ -441,6 +448,7 @@ class SettingsProvider extends ChangeNotifier { bool? renamedTeachersItalics, Color? liveActivityColor, String? welcomeMessage, + String? appIcon, }) async { if (language != null && language != _language) _language = language; if (startPage != null && startPage != _startPage) _startPage = startPage; @@ -568,6 +576,10 @@ class SettingsProvider extends ChangeNotifier { if (welcomeMessage != null && welcomeMessage != _welcomeMessage) { _welcomeMessage = welcomeMessage; } + if (appIcon != null && appIcon != _appIcon) { + _appIcon = appIcon; + } + // store or not if (store) await _database?.store.storeSettings(this); notifyListeners(); } diff --git a/filcnaplo/lib/ui/filter/widgets.dart b/filcnaplo/lib/ui/filter/widgets.dart index 79bf730..a23cfc2 100644 --- a/filcnaplo/lib/ui/filter/widgets.dart +++ b/filcnaplo/lib/ui/filter/widgets.dart @@ -35,6 +35,7 @@ import 'package:provider/provider.dart'; const List homeFilters = [ FilterType.all, FilterType.grades, + FilterType.exams, FilterType.messages, FilterType.absences ]; diff --git a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/block_picker.dart b/filcnaplo/lib/ui/flutter_colorpicker/block_picker.dart similarity index 95% rename from filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/block_picker.dart rename to filcnaplo/lib/ui/flutter_colorpicker/block_picker.dart index dbf27f8..77b7ce3 100644 --- a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/block_picker.dart +++ b/filcnaplo/lib/ui/flutter_colorpicker/block_picker.dart @@ -87,13 +87,13 @@ Widget _defaultItemBuilder(Color color, bool isCurrentColor, void Function() cha // The blocky color picker you can alter the layout and shape. class BlockPicker extends StatefulWidget { BlockPicker({ - Key? key, + super.key, required this.pickerColor, required this.onColorChanged, this.useInShowDialog = true, this.layoutBuilder = _defaultLayoutBuilder, this.itemBuilder = _defaultItemBuilder, - }) : super(key: key); + }); final Color? pickerColor; final ValueChanged onColorChanged; diff --git a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart b/filcnaplo/lib/ui/flutter_colorpicker/colorpicker.dart similarity index 94% rename from filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart rename to filcnaplo/lib/ui/flutter_colorpicker/colorpicker.dart index 9653e32..b727996 100644 --- a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart +++ b/filcnaplo/lib/ui/flutter_colorpicker/colorpicker.dart @@ -7,16 +7,18 @@ /// /// You can create your own layout by importing `picker.dart`. +// ignore_for_file: use_build_context_synchronously + library hsv_picker; import 'package:filcnaplo/models/shared_theme.dart'; import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart'; -import 'package:filcnaplo_premium/providers/share_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/block_picker.dart'; -import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/palette.dart'; -import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/utils.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/theme.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/theme.i18n.dart'; +import 'package:filcnaplo_kreta_api/providers/share_provider.dart'; +import 'package:filcnaplo/ui/flutter_colorpicker/block_picker.dart'; +import 'package:filcnaplo/ui/flutter_colorpicker/palette.dart'; +import 'package:filcnaplo/ui/flutter_colorpicker/utils.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/theme_screen.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/theme_screen.i18n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; @@ -24,7 +26,7 @@ import 'package:provider/provider.dart'; class FilcColorPicker extends StatefulWidget { const FilcColorPicker({ - Key? key, + super.key, required this.colorMode, required this.pickerColor, required this.onColorChanged, @@ -53,7 +55,7 @@ class FilcColorPicker extends StatefulWidget { this.colorHistory, this.onHistoryChanged, required this.onThemeIdProvided, - }) : super(key: key); + }); final CustomColorMode colorMode; final Color pickerColor; @@ -78,10 +80,10 @@ class FilcColorPicker extends StatefulWidget { final void Function(SharedTheme theme) onThemeIdProvided; @override - _FilcColorPickerState createState() => _FilcColorPickerState(); + FilcColorPickerState createState() => FilcColorPickerState(); } -class _FilcColorPickerState extends State { +class FilcColorPickerState extends State { final idController = TextEditingController(); late final ShareProvider shareProvider; diff --git a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colors.dart b/filcnaplo/lib/ui/flutter_colorpicker/colors.dart similarity index 100% rename from filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colors.dart rename to filcnaplo/lib/ui/flutter_colorpicker/colors.dart diff --git a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/palette.dart b/filcnaplo/lib/ui/flutter_colorpicker/palette.dart similarity index 94% rename from filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/palette.dart rename to filcnaplo/lib/ui/flutter_colorpicker/palette.dart index fb33f69..ffbf1b5 100644 --- a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/palette.dart +++ b/filcnaplo/lib/ui/flutter_colorpicker/palette.dart @@ -3,6 +3,7 @@ // FROM: https://pub.dev/packages/flutter_colorpicker // FROM: https://pub.dev/packages/flutter_colorpicker +// ignore: dangling_library_doc_comments /// The components of HSV Color Picker /// /// Try to create a Color Picker with other layout on your own :) @@ -317,11 +318,11 @@ class ColorPickerInput extends StatefulWidget { const ColorPickerInput( this.color, this.onColorChanged, { - Key? key, + super.key, this.enableAlpha = true, this.embeddedText = false, this.disable = false, - }) : super(key: key); + }); final Color color; final ValueChanged onColorChanged; @@ -330,10 +331,10 @@ class ColorPickerInput extends StatefulWidget { final bool disable; @override - _ColorPickerInputState createState() => _ColorPickerInputState(); + ColorPickerInputState createState() => ColorPickerInputState(); } -class _ColorPickerInputState extends State { +class ColorPickerInputState extends State { TextEditingController textEditingController = TextEditingController(); int inputColor = 0; @@ -346,11 +347,7 @@ class _ColorPickerInputState extends State { @override Widget build(BuildContext context) { if (inputColor != widget.color.value) { - textEditingController.text = '#' + - widget.color.red.toRadixString(16).toUpperCase().padLeft(2, '0') + - widget.color.green.toRadixString(16).toUpperCase().padLeft(2, '0') + - widget.color.blue.toRadixString(16).toUpperCase().padLeft(2, '0') + - (widget.enableAlpha ? widget.color.alpha.toRadixString(16).toUpperCase().padLeft(2, '0') : ''); + textEditingController.text = '#${widget.color.red.toRadixString(16).toUpperCase().padLeft(2, '0')}${widget.color.green.toRadixString(16).toUpperCase().padLeft(2, '0')}${widget.color.blue.toRadixString(16).toUpperCase().padLeft(2, '0')}${widget.enableAlpha ? widget.color.alpha.toRadixString(16).toUpperCase().padLeft(2, '0') : ''}'; } return Padding( padding: const EdgeInsets.only(top: 6.0, left: 12.0, right: 12.0), @@ -516,10 +513,10 @@ class ColorPickerSlider extends StatelessWidget { this.onColorChanged, this.onColorChangeEnd, this.onProblem, { - Key? key, + super.key, this.displayThumbColor = false, this.fullThumbColor = false, - }) : super(key: key); + }); final TrackType trackType; final HSVColor hsvColor; @@ -657,13 +654,13 @@ class ColorPickerSlider extends StatelessWidget { class ColorIndicator extends StatelessWidget { const ColorIndicator( this.hsvColor, { - Key? key, + super.key, this.currentHsvColor, this.icon, this.width = 50.0, this.height = 50.0, this.adaptive = false, - }) : super(key: key); + }); final HSVColor hsvColor; final HSVColor? currentHsvColor; @@ -711,8 +708,8 @@ class ColorPickerArea extends StatelessWidget { this.onColorChanged, this.onChangeEnd, this.paletteType, { - Key? key, - }) : super(key: key); + super.key, + }); final HSVColor hsvColor; final ValueChanged onColorChanged; diff --git a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/utils.dart b/filcnaplo/lib/ui/flutter_colorpicker/utils.dart similarity index 97% rename from filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/utils.dart rename to filcnaplo/lib/ui/flutter_colorpicker/utils.dart index 5e413a7..92e422e 100644 --- a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/utils.dart +++ b/filcnaplo/lib/ui/flutter_colorpicker/utils.dart @@ -3,6 +3,7 @@ // FROM: https://pub.dev/packages/flutter_colorpicker // FROM: https://pub.dev/packages/flutter_colorpicker +// ignore: dangling_library_doc_comments /// Common function lib import 'dart:math'; diff --git a/filcnaplo/lib/ui/widgets/grade/grade_tile.dart b/filcnaplo/lib/ui/widgets/grade/grade_tile.dart index a2778e5..d31a2d4 100644 --- a/filcnaplo/lib/ui/widgets/grade/grade_tile.dart +++ b/filcnaplo/lib/ui/widgets/grade/grade_tile.dart @@ -10,14 +10,20 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; class GradeTile extends StatelessWidget { - const GradeTile(this.grade, - {Key? key, this.onTap, this.padding, this.censored = false}) - : super(key: key); + const GradeTile( + this.grade, { + super.key, + this.onTap, + this.padding, + this.censored = false, + this.viewOverride = false, + }); final Grade grade; final void Function()? onTap; final EdgeInsetsGeometry? padding; final bool censored; + final bool viewOverride; @override Widget build(BuildContext context) { @@ -26,7 +32,8 @@ class GradeTile extends StatelessWidget { bool isTitleItalic = false; bool isSubtitleItalic = false; EdgeInsets leadingPadding = EdgeInsets.zero; - bool isSubjectView = SubjectGradesContainer.of(context) != null; + bool isSubjectView = + SubjectGradesContainer.of(context) != null || viewOverride; String subjectName = grade.subject.renamedTo ?? grade.subject.name.capital(); String modeDescription = grade.mode.description.capital(); @@ -187,7 +194,7 @@ class GradeTile extends StatelessWidget { class GradeValueWidget extends StatelessWidget { const GradeValueWidget( this.value, { - Key? key, + super.key, this.size = 38.0, this.fill = false, this.contrast = false, @@ -196,7 +203,7 @@ class GradeValueWidget extends StatelessWidget { this.complemented = false, this.nocolor = false, this.color, - }) : super(key: key); + }); final GradeValue value; final double size; diff --git a/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart b/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart index 1aeb3ef..59e7547 100644 --- a/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart +++ b/filcnaplo/lib/ui/widgets/lesson/lesson_tile.dart @@ -16,8 +16,7 @@ import 'package:provider/provider.dart'; import 'lesson_tile.i18n.dart'; class LessonTile extends StatelessWidget { - const LessonTile(this.lesson, {Key? key, this.onTap, this.swapDesc = false}) - : super(key: key); + const LessonTile(this.lesson, {super.key, this.onTap, this.swapDesc = false}); final Lesson lesson; final bool swapDesc; @@ -287,8 +286,7 @@ enum LessonSubtileType { homework, exam, absence } class LessonSubtile extends StatelessWidget { const LessonSubtile( - {Key? key, this.onPressed, required this.title, required this.type}) - : super(key: key); + {super.key, this.onPressed, required this.title, required this.type}); final Function()? onPressed; final String title; diff --git a/filcnaplo/lib/ui/widgets/message/message_tile.dart b/filcnaplo/lib/ui/widgets/message/message_tile.dart index f866a34..4247fc9 100644 --- a/filcnaplo/lib/ui/widgets/message/message_tile.dart +++ b/filcnaplo/lib/ui/widgets/message/message_tile.dart @@ -11,12 +11,12 @@ import 'package:provider/provider.dart'; class MessageTile extends StatelessWidget { const MessageTile( this.message, { - Key? key, + super.key, this.messages, this.padding, this.onTap, this.censored = false, - }) : super(key: key); + }); final Message message; final List? messages; diff --git a/filcnaplo/pubspec.yaml b/filcnaplo/pubspec.yaml index 2ccd85e..68451e6 100644 --- a/filcnaplo/pubspec.yaml +++ b/filcnaplo/pubspec.yaml @@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez" homepage: https://refilc.hu publish_to: "none" -version: 4.4.0+233 +version: 4.5.0+236 environment: sdk: ">=2.17.0 <3.0.0" @@ -25,21 +25,22 @@ dependencies: flutter_localizations: sdk: flutter - i18n_extension: ^9.0.0 + i18n_extension: ^10.0.1 sqflite: ^2.2.0+2 intl: ^0.18.0 provider: ^5.0.0 http: ^0.13.3 - uuid: ^3.0.4 + uuid: ^4.2.1 html: ^0.15.0 - open_file: - git: - url: https://github.com/crazecoder/open_file - ref: master + open_filex: ^4.3.4 + # open_file: + # git: + # url: https://github.com/crazecoder/open_file + # ref: master path_provider: ^2.0.2 - permission_handler: ^10.2.0 + permission_handler: ^11.0.1 share_plus: ^7.0.2 - connectivity_plus: ^4.0.1 + connectivity_plus: ^5.0.2 flutter_displaymode: ^0.6.0 quick_actions: ^1.0.1 animated_list_plus: ^0.5.0 @@ -53,9 +54,9 @@ dependencies: lottie: ^1.4.3 rive: ^0.9.1 animated_background: ^2.0.0 - dropdown_button2: ^1.8.9 + dropdown_button2: ^1.9.4 home_widget: ^0.1.6 - flutter_expandable_fab: ^1.8.1 + flutter_expandable_fab: ^2.0.0 uni_links: ^0.5.1 url_launcher: ^6.1.6 workmanager: ^0.5.1 @@ -63,7 +64,7 @@ dependencies: image_picker: ^0.8.6 animations: ^2.0.1 background_fetch: ^1.1.5 - flutter_local_notifications: ^14.1.0 + flutter_local_notifications: ^16.2.0 package_info_plus: ^4.0.2 screenshot: ^2.1.0 flutter_staggered_grid_view: ^0.7.0 @@ -73,7 +74,7 @@ dependencies: url: https://github.com/kimaah/image_crop.git dev_dependencies: - flutter_lints: ^2.0.1 + flutter_lints: ^3.0.1 flutter_launcher_icons: "^0.13.1" flutter_native_splash: "^2.3.0" sqflite_common_ffi: ^2.0.0+3 @@ -87,6 +88,7 @@ flutter: - assets/animations/ - assets/images/ - assets/images/subject_covers/ + - assets/launch_icons/ fonts: - family: FilcIcons diff --git a/filcnaplo/windows/runner/flutter_window.cpp b/filcnaplo/windows/runner/flutter_window.cpp index b25e363..955ee30 100644 --- a/filcnaplo/windows/runner/flutter_window.cpp +++ b/filcnaplo/windows/runner/flutter_window.cpp @@ -31,6 +31,11 @@ bool FlutterWindow::OnCreate() { this->Show(); }); + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + return true; } diff --git a/filcnaplo_desktop_ui/lib/common/filter_bar.dart b/filcnaplo_desktop_ui/lib/common/filter_bar.dart index 12dbf84..ef52e1e 100644 --- a/filcnaplo_desktop_ui/lib/common/filter_bar.dart +++ b/filcnaplo_desktop_ui/lib/common/filter_bar.dart @@ -61,19 +61,26 @@ class FilterBar extends StatelessWidget implements PreferredSizeWidget { // avoid fading over selected tab return ShaderMask( shaderCallback: (Rect bounds) { - final Color bg = Theme.of(context).scaffoldBackgroundColor; + final Color bg = + Theme.of(context).scaffoldBackgroundColor; final double index = controller.animation!.value; - return LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ - index < 0.2 ? Colors.transparent : bg, - Colors.transparent, - Colors.transparent, - index > controller.length - 1.2 ? Colors.transparent : bg - ], stops: const [ - 0, - 0.1, - 0.9, - 1 - ]).createShader(bounds); + return LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + index < 0.2 ? Colors.transparent : bg, + Colors.transparent, + Colors.transparent, + index > controller.length - 1.2 + ? Colors.transparent + : bg + ], + stops: const [ + 0, + 0.1, + 0.9, + 1 + ]).createShader(bounds); }, blendMode: BlendMode.dstOut, child: child); diff --git a/filcnaplo_desktop_ui/lib/pages/grades/grade_subject_view.dart b/filcnaplo_desktop_ui/lib/pages/grades/grade_subject_view.dart index 1b30577..a08e173 100644 --- a/filcnaplo_desktop_ui/lib/pages/grades/grade_subject_view.dart +++ b/filcnaplo_desktop_ui/lib/pages/grades/grade_subject_view.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:animations/animations.dart'; +import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/utils/format.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; import 'package:filcnaplo/helpers/average_helper.dart'; @@ -56,6 +57,7 @@ class _GradeSubjectViewState extends State { // Providers late GradeProvider gradeProvider; late GradeCalculatorProvider calculatorProvider; + late SettingsProvider settingsProvider; late double average; late Widget gradeGraph; @@ -142,6 +144,7 @@ class _GradeSubjectViewState extends State { Widget build(BuildContext context) { gradeProvider = Provider.of(context); calculatorProvider = Provider.of(context); + settingsProvider = Provider.of(context); List subjectGrades = getSubjectGrades(widget.subject).toList(); average = AverageHelper.averageEvals(subjectGrades); @@ -244,7 +247,8 @@ class _GradeSubjectViewState extends State { subject: widget.subject, context: context), scrollController: _scrollController, title: widget.subject.renamedTo ?? widget.subject.name.capital(), - italic: widget.subject.isRenamed, + italic: widget.subject.isRenamed && + settingsProvider.renamedSubjectsItalics, child: SubjectGradesContainer( child: CupertinoScrollbar( child: ListView.builder( diff --git a/filcnaplo_desktop_ui/lib/pages/grades/grades_page.dart b/filcnaplo_desktop_ui/lib/pages/grades/grades_page.dart index 06bbcd5..e64f14e 100644 --- a/filcnaplo_desktop_ui/lib/pages/grades/grades_page.dart +++ b/filcnaplo_desktop_ui/lib/pages/grades/grades_page.dart @@ -21,7 +21,7 @@ import 'package:filcnaplo_desktop_ui/pages/grades/grade_subject_view.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:filcnaplo/helpers/average_helper.dart'; -import 'package:filcnaplo_premium/ui/mobile/grades/average_selector.dart'; +import 'package:filcnaplo_mobile_ui/pages/grades/average_selector.dart'; import 'grades_page.i18n.dart'; class GradesPage extends StatefulWidget { @@ -41,14 +41,15 @@ class _GradesPageState extends State { int avgDropValue = 0; - List getSubjectGrades(GradeSubject subject, {int days = 0}) => gradeProvider - .grades - .where((e) => - e.subject == subject && - e.type == GradeType.midYear && - (days == 0 || - e.date.isBefore(DateTime.now().subtract(Duration(days: days))))) - .toList(); + List getSubjectGrades(GradeSubject subject, {int days = 0}) => + gradeProvider.grades + .where((e) => + e.subject == subject && + e.type == GradeType.midYear && + (days == 0 || + e.date + .isBefore(DateTime.now().subtract(Duration(days: days))))) + .toList(); void generateTiles() { List subjects = gradeProvider.grades @@ -144,8 +145,9 @@ class _GradesPageState extends State { Expanded( child: StatisticsTile( outline: true, - title: AutoSizeText( // https://discord.com/channels/1111649116020285532/1153397476578050130 - "classavg".i18n, + title: AutoSizeText( + // https://discord.com/channels/1111649116020285532/1153397476578050130 + "classavg".i18n, textAlign: TextAlign.center, maxLines: 2, wrapWords: false, @@ -208,7 +210,7 @@ class _GradesPageState extends State { title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - PremiumAverageSelector( + AverageSelector( value: avgDropValue, onChanged: (value) { setState(() { diff --git a/filcnaplo_desktop_ui/lib/screens/login/login_screen.dart b/filcnaplo_desktop_ui/lib/screens/login/login_screen.dart index e01a626..6d59ee0 100644 --- a/filcnaplo_desktop_ui/lib/screens/login/login_screen.dart +++ b/filcnaplo_desktop_ui/lib/screens/login/login_screen.dart @@ -57,9 +57,9 @@ class _LoginScreenState extends State { FilcAPI.getSchools().then((schools) { if (schools != null) { - schoolController.update(() { - schoolController.schools = schools; - }); + schoolController.update(() { + schoolController.schools = schools; + }); } else { ElegantNotification.error( background: Colors.white, @@ -239,9 +239,9 @@ class _LoginScreenState extends State { ), ), SchoolInput( - scroll: _scrollController, - controller: schoolController, - ), + scroll: _scrollController, + controller: schoolController, + ), ], ), ), @@ -321,8 +321,8 @@ class _LoginScreenState extends State { if (username == "" || password == - "" /*|| - schoolController.selectedSchool == null*/ + "" || + schoolController.selectedSchool == null ) { return setState(() => _loginState = LoginState.missingFields); } @@ -332,8 +332,7 @@ class _LoginScreenState extends State { loginAPI( username: username, password: password, - instituteCode: 'shit', - // instituteCode: schoolController.selectedSchool!.instituteCode, + instituteCode: schoolController.selectedSchool!.instituteCode, context: context, onLogin: (user) { ElegantNotification.success( diff --git a/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart b/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart index 5421f06..6a98479 100644 --- a/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart +++ b/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart @@ -62,6 +62,8 @@ class _SidebarState extends State { Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), + Provider.of(context, listen: false) + .restoreRecipients(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), diff --git a/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart index 90a3213..137a69d 100644 --- a/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.dart @@ -38,8 +38,7 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'settings_screen.i18n.dart'; import 'package:flutter/services.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/nickname.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/user/nickname.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({Key? key}) : super(key: key); @@ -68,6 +67,8 @@ class _SettingsScreenState extends State Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), + Provider.of(context, listen: false) + .restoreRecipients(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), @@ -400,9 +401,9 @@ class _SettingsScreenState extends State if (v) { showDialog( context: context, - builder: (context) => - WillPopScope( - onWillPop: () async => false, + builder: (context) => PopScope( + onPopInvoked: (didPop) => + false, child: AlertDialog( shape: RoundedRectangleBorder( @@ -586,7 +587,8 @@ class _SettingsScreenState extends State .secondary, ), ), - const PremiumIconPackSelector(), + // we need icon pack selector here + // const PremiumIconPackSelector(), ], ), ), diff --git a/filcnaplo_kreta_api/lib/client/api.dart b/filcnaplo_kreta_api/lib/client/api.dart index 708da1d..fb1e624 100644 --- a/filcnaplo_kreta_api/lib/client/api.dart +++ b/filcnaplo_kreta_api/lib/client/api.dart @@ -21,23 +21,14 @@ class KretaAPI { static String groups(String iss) => BaseKreta.kreta(iss) + KretaApiEndpoints.groups; static String groupAverages(String iss, String uid) => - BaseKreta.kreta(iss) + - KretaApiEndpoints.groupAverages + - "?oktatasiNevelesiFeladatUid=" + - uid; + "${BaseKreta.kreta(iss)}${KretaApiEndpoints.groupAverages}?oktatasiNevelesiFeladatUid=$uid"; static String averages(String iss, String uid) => - BaseKreta.kreta(iss) + - KretaApiEndpoints.averages + - "?oktatasiNevelesiFeladatUid=" + - uid; + "${BaseKreta.kreta(iss)}${KretaApiEndpoints.averages}?oktatasiNevelesiFeladatUid=$uid"; static String timetable(String iss, {DateTime? start, DateTime? end}) => BaseKreta.kreta(iss) + KretaApiEndpoints.timetable + (start != null && end != null - ? "?datumTol=" + - start.toUtc().toIso8601String() + - "&datumIg=" + - end.toUtc().toIso8601String() + ? "?datumTol=${start.toUtc().toIso8601String()}&datumIg=${end.toUtc().toIso8601String()}" : ""); static String exams(String iss) => BaseKreta.kreta(iss) + KretaApiEndpoints.exams; @@ -46,7 +37,7 @@ class KretaAPI { KretaApiEndpoints.homework + (id != null ? "/$id" : "") + (id == null && start != null - ? "?datumTol=" + DateFormat('yyyy-MM-dd').format(start) + ? "?datumTol=${DateFormat('yyyy-MM-dd').format(start)}" : ""); static String capabilities(String iss) => BaseKreta.kreta(iss) + KretaApiEndpoints.capabilities; @@ -55,29 +46,27 @@ class KretaAPI { BaseKreta.kreta(iss) + KretaApiEndpoints.downloadHomeworkAttachments(uid, type); static String subjects(String iss, String uid) => - BaseKreta.kreta(iss) + - KretaApiEndpoints.subjects + - "?oktatasiNevelesiFeladatUid=" + uid; - // Structure: - // { - // "Uid": 000, - // "Tantargy": { - // "Uid": 000, - // "Nev": "Irodalom", - // "Kategoria": { - // "Uid": "000,magyar_nyelv_es_irodalom", - // "Nev": "magyar_nyelv_es_irodalom", - // "Leiras": "Magyar nyelv és irodalom" - // }, - // "SortIndex": 0, - // }, - // "Atlag": null, // float - // "AtlagAlakulasaIdoFuggvenyeben": Array[], // no idea what this is - // "SulyozottOsztalyzatOsszege": null, // int | float - // "SulyozottOsztalyzatSzama": null, // int | float - // "SortIndex": 0 - // } - // refer to https://discord.com/channels/1111649116020285532/1111798771513303102/1148368925969612920 + "${BaseKreta.kreta(iss)}${KretaApiEndpoints.subjects}?oktatasiNevelesiFeladatUid=$uid"; + // Structure: + // { + // "Uid": 000, + // "Tantargy": { + // "Uid": 000, + // "Nev": "Irodalom", + // "Kategoria": { + // "Uid": "000,magyar_nyelv_es_irodalom", + // "Nev": "magyar_nyelv_es_irodalom", + // "Leiras": "Magyar nyelv és irodalom" + // }, + // "SortIndex": 0, + // }, + // "Atlag": null, // float + // "AtlagAlakulasaIdoFuggvenyeben": Array[], // no idea what this is + // "SulyozottOsztalyzatOsszege": null, // int | float + // "SulyozottOsztalyzatSzama": null, // int | float + // "SortIndex": 0 + // } + // refer to https://discord.com/channels/1111649116020285532/1111798771513303102/1148368925969612920 // ADMIN API static const sendMessage = @@ -90,8 +79,10 @@ class KretaAPI { BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientCategories; static const availableCategories = BaseKreta.kretaAdmin + KretaAdminEndpoints.availableCategories; - static const recipientsTeacher = - BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientsTeacher; + static const recipientTeachers = + BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientTeachers; + static const recipientDirectorate = + BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientDirectorate; static const uploadAttachment = BaseKreta.kretaAdmin + KretaAdminEndpoints.uploadAttachment; static String downloadAttachment(String id) => @@ -129,7 +120,8 @@ class KretaApiEndpoints { static const capabilities = "/ellenorzo/V3/Sajat/Intezmenyek"; static String downloadHomeworkAttachments(String uid, String type) => "/ellenorzo/V3/Sajat/Csatolmany/$uid"; - static const subjects = "/ellenorzo/V3/Sajat/Ertekelesek/Atlagok/TantargyiAtlagok"; + static const subjects = + "/ellenorzo/V3/Sajat/Ertekelesek/Atlagok/TantargyiAtlagok"; } class KretaAdminEndpoints { @@ -141,7 +133,8 @@ class KretaAdminEndpoints { "/api/v1/kommunikacio/postaladaelemek/$id"; static const recipientCategories = "/api/v1/adatszotarak/cimzetttipusok"; static const availableCategories = "/api/v1/kommunikacio/cimezhetotipusok"; - static const recipientsTeacher = "/api/v1/kreta/alkalmazottak/tanar"; + static const recipientTeachers = "/api/v1/kreta/alkalmazottak/tanar"; + static const recipientDirectorate = "/api/v1/kreta/alkalmazottak/igazgatosag"; static const uploadAttachment = "/ideiglenesfajlok"; static String downloadAttachment(String id) => "/api/v1/dokumentumok/uzenetek/$id"; diff --git a/filcnaplo_kreta_api/lib/client/client.dart b/filcnaplo_kreta_api/lib/client/client.dart index d4ed845..f9a4589 100644 --- a/filcnaplo_kreta_api/lib/client/client.dart +++ b/filcnaplo_kreta_api/lib/client/client.dart @@ -139,6 +139,9 @@ class KretaClient { if (!headerMap.containsKey("content-type")) { headerMap["content-type"] = "application/json"; } + if (url.contains('kommunikacio/uzenetek')) { + headerMap["X-Uzenet-Lokalizacio"] = "hu-HU"; + } } res = await client.post(Uri.parse(url), headers: headerMap, body: body); @@ -153,6 +156,7 @@ class KretaClient { if (res == null) throw "Login error"; if (json) { + print(jsonDecode(res.body)); return jsonDecode(res.body); } else { return res.body; @@ -165,6 +169,69 @@ class KretaClient { } } + Future sendFilesAPI( + String url, { + Map? headers, + bool autoHeader = true, + Map? body, + }) async { + Map headerMap; + + if (headers != null) { + headerMap = headers; + } else { + headerMap = {}; + } + + try { + http.StreamedResponse? res; + + for (int i = 0; i < 3; i++) { + if (autoHeader) { + if (!headerMap.containsKey("authorization") && accessToken != null) { + headerMap["authorization"] = "Bearer $accessToken"; + } + if (!headerMap.containsKey("user-agent") && userAgent != null) { + headerMap["user-agent"] = "$userAgent"; + } + if (!headerMap.containsKey("content-type")) { + headerMap["content-type"] = "multipart/form-data"; + } + if (url.contains('kommunikacio/uzenetek')) { + headerMap["X-Uzenet-Lokalizacio"] = "hu-HU"; + } + } + + var request = http.MultipartRequest("POST", Uri.parse(url)); + + // request.files.add(value) + + request.fields.addAll(body ?? {}); + request.headers.addAll(headers ?? {}); + + res = await request.send(); + + if (res.statusCode == 401) { + await refreshLogin(); + headerMap.remove("authorization"); + } else { + break; + } + } + + if (res == null) throw "Login error"; + + print(res.statusCode); + + return res.statusCode; + } on http.ClientException catch (error) { + print( + "ERROR: KretaClient.postAPI ($url) ClientException: ${error.message}"); + } catch (error) { + print("ERROR: KretaClient.postAPI ($url) ${error.runtimeType}: $error"); + } + } + Future refreshLogin() async { if (_loginRefreshing) return; _loginRefreshing = true; diff --git a/filcnaplo_kreta_api/lib/controllers/timetable_controller.dart b/filcnaplo_kreta_api/lib/controllers/timetable_controller.dart index f552062..acd8da4 100644 --- a/filcnaplo_kreta_api/lib/controllers/timetable_controller.dart +++ b/filcnaplo_kreta_api/lib/controllers/timetable_controller.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_print +// ignore_for_file: avoid_print, use_build_context_synchronously import 'dart:async'; import 'dart:developer'; @@ -154,9 +154,9 @@ class TimetableController extends ChangeNotifier { } for (int i = 0; i < days.length; i++) { - List _day = List.castFrom(days[i]); + List day0 = List.castFrom(days[i]); - List lessonIndexes = _getIndexes(_day); + List lessonIndexes = _getIndexes(day0); int minIndex = 0, maxIndex = 0; if (lessonIndexes.isNotEmpty) { @@ -170,7 +170,7 @@ class TimetableController extends ChangeNotifier { // Fill missing indexes with empty spaces for (var i in List.generate( maxIndex - minIndex + 1, (int i) => minIndex + i)) { - List indexLessons = _getLessonsByIndex(_day, i); + List indexLessons = _getLessonsByIndex(day0, i); // Empty lesson if (indexLessons.isEmpty) { @@ -193,13 +193,13 @@ class TimetableController extends ChangeNotifier { } // Additional lessons - day.addAll(_day.where((l) => + day.addAll(day0.where((l) => int.tryParse(l.lessonIndex) == null && l.subject.id != '')); day.sort((a, b) => a.start.compareTo(b.start)); // Special Dates - for (var l in _day) { + for (var l in day0) { l.subject.id == '' ? day.insert(0, l) : null; } diff --git a/filcnaplo_kreta_api/lib/models/grade.dart b/filcnaplo_kreta_api/lib/models/grade.dart index 35d6709..16877b8 100644 --- a/filcnaplo_kreta_api/lib/models/grade.dart +++ b/filcnaplo_kreta_api/lib/models/grade.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'package:filcnaplo/utils/format.dart'; import 'category.dart'; import 'subject.dart'; diff --git a/filcnaplo_kreta_api/lib/models/message.dart b/filcnaplo_kreta_api/lib/models/message.dart index 97cd5f3..e938ff5 100644 --- a/filcnaplo_kreta_api/lib/models/message.dart +++ b/filcnaplo_kreta_api/lib/models/message.dart @@ -68,13 +68,21 @@ class Message { messageId: message["azonosito"], seen: json["isElolvasva"] ?? false, deleted: json["isToroltElem"] ?? false, - date: message["kuldesDatum"] != null ? DateTime.parse(message["kuldesDatum"]).toLocal() : DateTime(0), + date: message["kuldesDatum"] != null + ? DateTime.parse(message["kuldesDatum"]).toLocal() + : DateTime(0), author: (message["feladoNev"] ?? "").trim(), content: message["szoveg"].replaceAll("\r", "") ?? "", subject: message["targy"] ?? "", type: type, - recipients: (message["cimzettLista"] as List).cast().map((Map recipient) => Recipient.fromJson(recipient)).toList(), - attachments: (message["csatolmanyok"] as List).cast().map((Map attachment) => Attachment.fromJson(attachment)).toList(), + recipients: (message["cimzettLista"] as List) + .cast() + .map((Map recipient) => Recipient.fromJson(recipient)) + .toList(), + attachments: (message["csatolmanyok"] as List) + .cast() + .map((Map attachment) => Attachment.fromJson(attachment)) + .toList(), replyId: message["elozoUzenetAzonosito"], conversationId: message["beszelgetesAzonosito"], json: json, @@ -105,3 +113,100 @@ class Conversation { Message get newest => _messages.first; } + +// sendable message object and it's things +class SendMessage { + Map? json; + int id; + int lastMessageId; + String? subject; + String text; + DateTime sentDate; + String senderRank; + String senderName; + List? attachments; + List recipients; + + SendMessage({ + required this.id, + required this.lastMessageId, + this.subject, + required this.text, + required this.sentDate, + required this.senderRank, + required this.senderName, + this.attachments, + required this.recipients, + }); +} + +class SendRecipient { + Map? json; + int? id; + int? kretaId; + String? name; + SendRecipientType type; + + SendRecipient({ + required this.id, + required this.kretaId, + required this.name, + required this.type, + this.json, + }); + + factory SendRecipient.fromJson(Map json, SendRecipientType type) { + print(json); + return SendRecipient( + id: int.parse(json['kretaAzonosito'] ?? '0'), + kretaId: json['kretaAzonosito'], + name: json['nev'], + type: type, + json: json, + ); + } + + Object get kretaJson => { + 'azonosito': kretaId ?? 0, + 'kretaAzonosito': kretaId ?? 0, + 'nev': name ?? 'Teszt Lajos', + 'tipus': { + 'azonosito': type.id, + 'kod': type.code, + 'leiras': type.description, + 'nev': type.name, + 'rovidNev': type.shortName, + } + }; +} + +class SendRecipientType { + Map? json; + int id; + String code; + String description; + String name; + String shortName; + + SendRecipientType({ + required this.id, + required this.code, + required this.description, + required this.name, + required this.shortName, + this.json, + }); + + factory SendRecipientType.fromJson(Map json) { + return SendRecipientType( + id: json['azonosito'], + code: json['kod'], + description: json['leiras'], + name: json['nev'], + shortName: json['rovidNev'], + json: json, + ); + } +} + +enum AddresseeType { teachers, directorate } diff --git a/filcnaplo_kreta_api/lib/models/week.dart b/filcnaplo_kreta_api/lib/models/week.dart index 03cc048..2a162bd 100644 --- a/filcnaplo_kreta_api/lib/models/week.dart +++ b/filcnaplo_kreta_api/lib/models/week.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart'; class Week { diff --git a/filcnaplo_kreta_api/lib/providers/absence_provider.dart b/filcnaplo_kreta_api/lib/providers/absence_provider.dart index 6450953..f4b0acf 100644 --- a/filcnaplo_kreta_api/lib/providers/absence_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/absence_provider.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'package:filcnaplo/api/providers/user_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/models/user.dart'; @@ -43,12 +45,14 @@ class AbsenceProvider with ChangeNotifier { (await _database.query.getSettings(_database)).renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects( userId: + // ignore: use_build_context_synchronously Provider.of(_context, listen: false).user!.id) : {}; Map renamedTeachers = (await _database.query.getSettings(_database)).renamedTeachersEnabled ? await _database.userQuery.renamedTeachers( userId: + // ignore: use_build_context_synchronously Provider.of(_context, listen: false).user!.id) : {}; diff --git a/filcnaplo_kreta_api/lib/providers/exam_provider.dart b/filcnaplo_kreta_api/lib/providers/exam_provider.dart index 8f0c1e5..1b6e245 100644 --- a/filcnaplo_kreta_api/lib/providers/exam_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/exam_provider.dart @@ -38,17 +38,19 @@ class ExamProvider with ChangeNotifier { // for renamed subjects Future convertBySettings() async { - final _database = Provider.of(_context, listen: false); + final database = Provider.of(_context, listen: false); Map renamedSubjects = - (await _database.query.getSettings(_database)).renamedSubjectsEnabled - ? await _database.userQuery.renamedSubjects( + (await database.query.getSettings(database)).renamedSubjectsEnabled + ? await database.userQuery.renamedSubjects( userId: + // ignore: use_build_context_synchronously Provider.of(_context, listen: false).user!.id) : {}; Map renamedTeachers = - (await _database.query.getSettings(_database)).renamedTeachersEnabled - ? await _database.userQuery.renamedTeachers( + (await database.query.getSettings(database)).renamedTeachersEnabled + ? await database.userQuery.renamedTeachers( userId: + // ignore: use_build_context_synchronously Provider.of(_context, listen: false).user!.id) : {}; diff --git a/filcnaplo_kreta_api/lib/providers/message_provider.dart b/filcnaplo_kreta_api/lib/providers/message_provider.dart index 8499b85..a04e1a1 100644 --- a/filcnaplo_kreta_api/lib/providers/message_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/message_provider.dart @@ -1,3 +1,7 @@ +// ignore_for_file: use_build_context_synchronously +import 'dart:convert'; +import 'dart:math'; + import 'package:filcnaplo/api/providers/user_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/models/user.dart'; @@ -9,17 +13,22 @@ import 'package:provider/provider.dart'; class MessageProvider with ChangeNotifier { late List _messages; + late List _recipients; late BuildContext _context; + List get messages => _messages; + List get recipients => _recipients; MessageProvider({ List initialMessages = const [], required BuildContext context, }) { _messages = List.castFrom(initialMessages); + _recipients = []; _context = context; if (_messages.isEmpty) restore(); + if (_recipients.isEmpty) restoreRecipients(); } Future restore() async { @@ -27,27 +36,33 @@ class MessageProvider with ChangeNotifier { // Load messages from the database if (userId != null) { - var dbMessages = await Provider.of(_context, listen: false).userQuery.getMessages(userId: userId); + var dbMessages = + await Provider.of(_context, listen: false) + .userQuery + .getMessages(userId: userId); _messages = dbMessages; notifyListeners(); } } // Fetches all types of Messages - Future fetchAll() => Future.forEach(MessageType.values, (MessageType v) => fetch(type: v)); + Future fetchAll() => + Future.forEach(MessageType.values, (MessageType v) => fetch(type: v)); // Fetches Messages from the Kreta API then stores them in the database Future fetch({MessageType type = MessageType.inbox}) async { // Check Message Type if (type == MessageType.draft) return; - String messageType = ["beerkezett", "elkuldott", "torolt"].elementAt(type.index); + String messageType = + ["beerkezett", "elkuldott", "torolt"].elementAt(type.index); // Check User User? user = Provider.of(_context, listen: false).user; if (user == null) throw "Cannot fetch Messages for User null"; // Get messages - List? messagesJson = await Provider.of(_context, listen: false).getAPI(KretaAPI.messages(messageType)); + List? messagesJson = await Provider.of(_context, listen: false) + .getAPI(KretaAPI.messages(messageType)); if (messagesJson == null) throw "Cannot fetch Messages for User ${user.id}"; // Parse messages @@ -55,8 +70,12 @@ class MessageProvider with ChangeNotifier { await Future.wait(List.generate(messagesJson.length, (index) { return () async { Map message = messagesJson.cast()[index]; - Map? messageJson = await Provider.of(_context, listen: false).getAPI(KretaAPI.message(message["azonosito"].toString())); - if (messageJson != null) messages.add(Message.fromJson(messageJson, forceType: type)); + Map? messageJson = + await Provider.of(_context, listen: false) + .getAPI(KretaAPI.message(message["azonosito"].toString())); + if (messageJson != null) { + messages.add(Message.fromJson(messageJson, forceType: type)); + } }(); })); @@ -73,8 +92,177 @@ class MessageProvider with ChangeNotifier { if (user == null) throw "Cannot store Messages for User null"; String userId = user.id; - await Provider.of(_context, listen: false).userStore.storeMessages(_messages, userId: userId); + await Provider.of(_context, listen: false) + .userStore + .storeMessages(_messages, userId: userId); notifyListeners(); } + + // restore recipients + Future restoreRecipients() async { + String? userId = Provider.of(_context, listen: false).id; + + // Load messages from the database + if (userId != null) { + var dbRecipients = + await Provider.of(_context, listen: false) + .userQuery + .getRecipients(userId: userId); + _recipients = dbRecipients; + notifyListeners(); + } + } + + // fetch all recipients + Future fetchAllRecipients() => Future.forEach( + AddresseeType.values, (AddresseeType v) => fetchRecipients(type: v)); + + // fetch recipients + Future fetchRecipients( + {AddresseeType type = AddresseeType.teachers}) async { + Map addressable = {}; + + // check user + User? user = Provider.of(_context, listen: false).user; + if (user == null) throw "Cannot fetch Messages for User null"; + + // get categories + List? availableCategoriesJson = + await Provider.of(_context, listen: false) + .getAPI(KretaAPI.recipientCategories); + + // print(availableCategoriesJson); + + // get recipients + List? recipientTeachersJson = + await Provider.of(_context, listen: false) + .getAPI(KretaAPI.recipientTeachers); + List? recipientDirectorateJson = + await Provider.of(_context, listen: false) + .getAPI(KretaAPI.recipientDirectorate); + + if (availableCategoriesJson == null || + recipientTeachersJson == null || + recipientDirectorateJson == null) { + throw "Cannot fetch Recipients for User ${user.id}"; + } + + for (var e in availableCategoriesJson) { + // print(e); + switch (e['kod']) { + case 'TANAR': + addressable + .addAll({AddresseeType.teachers: SendRecipientType.fromJson(e)}); + break; + case 'IGAZGATOSAG': + addressable.addAll( + {AddresseeType.directorate: SendRecipientType.fromJson(e)}); + break; + default: + break; + } + } + + // parse recipients + List recipients = []; + + if (addressable.containsKey(AddresseeType.teachers) && + type == AddresseeType.teachers) { + recipients.addAll(recipientTeachersJson.map((e) => + SendRecipient.fromJson(e, addressable[AddresseeType.teachers]!))); + } + if (addressable.containsKey(AddresseeType.directorate) && + type == AddresseeType.directorate) { + recipients.addAll(recipientDirectorateJson.map((e) => + SendRecipient.fromJson(e, addressable[AddresseeType.directorate]!))); + } + + // if (kDebugMode) { + // print(addressable); + // print(recipients); + // print(recipients.first.json); + // } + + await storeRecipients(recipients, type); + } + + // store recipients + Future storeRecipients( + List recipients, AddresseeType type) async { + _recipients.removeWhere((r) => (type == AddresseeType.teachers + ? (r.type.code == 'TANAR') + : (type == AddresseeType.directorate + ? (r.type.code == 'IGAZGATOSAG') + : r.type.code != ''))); + _recipients.addAll(recipients); + + User? user = Provider.of(_context, listen: false).user; + if (user == null) throw "Cannot store Recipients for User null"; + + String userId = user.id; + await Provider.of(_context, listen: false) + .userStore + .storeRecipients(_recipients, userId: userId); + + notifyListeners(); + } + + // send message + Future sendMessage({ + required List recipients, + String subject = "Nincs tárgy", + required String messageText, + }) async { + List recipientList = []; + + User? user = Provider.of(_context, listen: false).user; + if (user == null) throw "Cannot send Message as User null"; + + // for (var r in recipients) { + // recipientList.add({ + // "azonosito": r.id ?? "", + // "kretaAzonosito": r.kretaId ?? "", + // "nev": r.name ?? "Teszt Lajos", + // "tipus": { + // "kod": r.type.code, + // "leiras": r.type.description, + // "azonosito": r.type.id, + // "nev": r.type.name, + // "rovidNev": r.type.shortName, + // } + // }); + // } + recipientList.addAll(recipients.map((e) => e.kretaJson)); + + Map body = { + "cimzettLista": recipientList, + "csatolmanyok": [], + "azonosito": (Random().nextInt(10000) + 10000), + "feladoNev": user.name, + "feladoTitulus": user.role == Role.parent ? "Szülő" : "Diák", + "kuldesDatum": DateTime.now().toIso8601String(), + "targy": subject, + "szoveg": messageText, + // "elozoUzenetAzonosito": 0, + }; + + Map headers = { + "content-type": "application/json", + }; + + var res = await Provider.of(_context, listen: false).postAPI( + KretaAPI.sendMessage, + autoHeader: true, + json: true, + body: json.encode(body), + headers: headers, + ); + + if (res!['hibakod'] == 'UzenetKuldesEngedelyRule') { + return 'send_permission_error'; + } + + return 'successfully_sent'; + } } diff --git a/filcnaplo_premium/lib/providers/share_provider.dart b/filcnaplo_kreta_api/lib/providers/share_provider.dart similarity index 100% rename from filcnaplo_premium/lib/providers/share_provider.dart rename to filcnaplo_kreta_api/lib/providers/share_provider.dart diff --git a/filcnaplo_kreta_api/lib/providers/timetable_provider.dart b/filcnaplo_kreta_api/lib/providers/timetable_provider.dart index b7d235f..9e59826 100644 --- a/filcnaplo_kreta_api/lib/providers/timetable_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/timetable_provider.dart @@ -65,8 +65,14 @@ class TimetableProvider with ChangeNotifier { User? user = _user.user; if (user == null) throw "Cannot fetch Lessons for User null"; String iss = user.instituteCode; - List? lessonsJson = await _kreta - .getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end)); + + List? lessonsJson; + try { + lessonsJson = await _kreta + .getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end)); + } catch (e) { + lessonsJson = null; + } if (lessonsJson == null) { if (kDebugMode) print('Cannot fetch Lessons for User ${user.id}'); diff --git a/filcnaplo_kreta_api/pubspec.yaml b/filcnaplo_kreta_api/pubspec.yaml index e9375a9..6b3d5e4 100644 --- a/filcnaplo_kreta_api/pubspec.yaml +++ b/filcnaplo_kreta_api/pubspec.yaml @@ -11,7 +11,10 @@ dependencies: path: ../filcnaplo/ http: ^0.13.3 provider: ^5.0.0 - file_picker: ^5.3.2 + file_picker: ^6.1.1 + intl: ^0.18.1 + i18n_extension: ^10.0.1 + uuid: ^4.2.1 dev_dependencies: - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 diff --git a/filcnaplo_mobile_ui/lib/common/action_button.dart b/filcnaplo_mobile_ui/lib/common/action_button.dart index fc316a1..6515941 100755 --- a/filcnaplo_mobile_ui/lib/common/action_button.dart +++ b/filcnaplo_mobile_ui/lib/common/action_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class ActionButton extends StatelessWidget { - const ActionButton({Key? key, required this.label, this.activeColor, this.onTap}) : super(key: key); + const ActionButton({super.key, required this.label, this.activeColor, this.onTap}); final Color? activeColor; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/average_display.dart b/filcnaplo_mobile_ui/lib/common/average_display.dart index 735497e..15d60c7 100755 --- a/filcnaplo_mobile_ui/lib/common/average_display.dart +++ b/filcnaplo_mobile_ui/lib/common/average_display.dart @@ -4,8 +4,7 @@ import 'package:flutter/material.dart'; import 'package:i18n_extension/i18n_widget.dart'; class AverageDisplay extends StatelessWidget { - const AverageDisplay({Key? key, this.average = 0.0, this.border = false}) - : super(key: key); + const AverageDisplay({super.key, this.average = 0.0, this.border = false}); final double average; final bool border; diff --git a/filcnaplo_mobile_ui/lib/common/beta_chip.dart b/filcnaplo_mobile_ui/lib/common/beta_chip.dart index 9b4c0e8..1d35da6 100644 --- a/filcnaplo_mobile_ui/lib/common/beta_chip.dart +++ b/filcnaplo_mobile_ui/lib/common/beta_chip.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class BetaChip extends StatelessWidget { - const BetaChip({Key? key, this.disabled = false}) : super(key: key); + const BetaChip({super.key, this.disabled = false}); final bool disabled; @@ -12,6 +12,12 @@ class BetaChip extends StatelessWidget { height: 25, child: AnimatedContainer( duration: const Duration(milliseconds: 200), + decoration: BoxDecoration( + color: !disabled + ? Theme.of(context).colorScheme.secondary + : AppColors.of(context).text.withOpacity(.25), + borderRadius: BorderRadius.circular(40), + ), child: Padding( padding: const EdgeInsets.only(left: 8, right: 8), child: Center( @@ -29,12 +35,6 @@ class BetaChip extends StatelessWidget { ), ), ), - decoration: BoxDecoration( - color: !disabled - ? Theme.of(context).colorScheme.secondary - : AppColors.of(context).text.withOpacity(.25), - borderRadius: BorderRadius.circular(40), - ), ), ); } diff --git a/filcnaplo_mobile_ui/lib/common/bottom_card.dart b/filcnaplo_mobile_ui/lib/common/bottom_card.dart index 82c4c86..9c01341 100755 --- a/filcnaplo_mobile_ui/lib/common/bottom_card.dart +++ b/filcnaplo_mobile_ui/lib/common/bottom_card.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class BottomCard extends StatelessWidget { - const BottomCard({Key? key, this.child}) : super(key: key); + const BottomCard({super.key, this.child}); final Widget? child; diff --git a/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu.dart b/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu.dart index c447ee7..8b4ca2e 100755 --- a/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu.dart +++ b/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_shee import 'package:flutter/material.dart'; class BottomSheetMenu extends StatelessWidget { - const BottomSheetMenu({Key? key, this.items = const []}) : super(key: key); + const BottomSheetMenu({super.key, this.items = const []}); final List items; diff --git a/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu_item.dart b/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu_item.dart index 525feea..2f8f21c 100755 --- a/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu_item.dart +++ b/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/bottom_sheet_menu_item.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; import 'package:flutter/material.dart'; class BottomSheetMenuItem extends StatelessWidget { - const BottomSheetMenuItem({Key? key, required this.onPressed, required this.title, this.icon}) : super(key: key); + const BottomSheetMenuItem({super.key, required this.onPressed, required this.title, this.icon}); final void Function()? onPressed; final Widget? title; diff --git a/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart b/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart index e0d5689..a22bbe5 100755 --- a/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart +++ b/filcnaplo_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class RoundedBottomSheet extends StatelessWidget { - const RoundedBottomSheet({Key? key, this.child, this.borderRadius = 12.0, this.shrink = true, this.showHandle = true}) : super(key: key); + const RoundedBottomSheet({super.key, this.child, this.borderRadius = 12.0, this.shrink = true, this.showHandle = true}); final Widget? child; final double borderRadius; diff --git a/filcnaplo_mobile_ui/lib/common/custom_snack_bar.dart b/filcnaplo_mobile_ui/lib/common/custom_snack_bar.dart index 2f56809..b7c4349 100755 --- a/filcnaplo_mobile_ui/lib/common/custom_snack_bar.dart +++ b/filcnaplo_mobile_ui/lib/common/custom_snack_bar.dart @@ -10,6 +10,7 @@ SnackBar CustomSnackBar({ Duration? duration, }) { // backgroundColor > Brightness > Theme Background + // ignore: no_leading_underscores_for_local_identifiers Color _backgroundColor = backgroundColor ?? (AppColors.fromBrightness(brightness ?? Theme.of(context).brightness).highlight); Color textColor = AppColors.fromBrightness(brightness ?? Theme.of(context).brightness).text; diff --git a/filcnaplo_mobile_ui/lib/common/detail.dart b/filcnaplo_mobile_ui/lib/common/detail.dart index cb70b7e..1ba73ad 100755 --- a/filcnaplo_mobile_ui/lib/common/detail.dart +++ b/filcnaplo_mobile_ui/lib/common/detail.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class Detail extends StatelessWidget { - const Detail({Key? key, required this.title, required this.description, this.maxLines = 3}) : super(key: key); + const Detail({super.key, required this.title, required this.description, this.maxLines = 3}); final String title; final String description; diff --git a/filcnaplo_mobile_ui/lib/common/dialog_button.dart b/filcnaplo_mobile_ui/lib/common/dialog_button.dart index 17bf422..a16962b 100755 --- a/filcnaplo_mobile_ui/lib/common/dialog_button.dart +++ b/filcnaplo_mobile_ui/lib/common/dialog_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class DialogButton extends StatelessWidget { - const DialogButton({Key? key, required this.label, this.onTap}) : super(key: key); + const DialogButton({super.key, required this.label, this.onTap}); final String label; final Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/dot.dart b/filcnaplo_mobile_ui/lib/common/dot.dart index 2d827a2..05f722f 100755 --- a/filcnaplo_mobile_ui/lib/common/dot.dart +++ b/filcnaplo_mobile_ui/lib/common/dot.dart @@ -4,7 +4,7 @@ class Dot extends StatelessWidget { final Color color; final double size; - const Dot({Key? key, this.color = Colors.grey, this.size = 16.0}) : super(key: key); + const Dot({super.key, this.color = Colors.grey, this.size = 16.0}); @override Widget build(BuildContext context) { diff --git a/filcnaplo_mobile_ui/lib/common/empty.dart b/filcnaplo_mobile_ui/lib/common/empty.dart index ea0a06c..48c4c1f 100755 --- a/filcnaplo_mobile_ui/lib/common/empty.dart +++ b/filcnaplo_mobile_ui/lib/common/empty.dart @@ -17,7 +17,7 @@ List faces = [ ]; class Empty extends StatelessWidget { - const Empty({Key? key, this.subtitle}) : super(key: key); + const Empty({super.key, this.subtitle}); final String? subtitle; @@ -38,7 +38,7 @@ class Empty extends StatelessWidget { children: subtitle != null ? [ TextSpan( - text: "\n" + subtitle!, + text: "\n${subtitle!}", style: TextStyle( fontSize: 18.0, height: 2.0, diff --git a/filcnaplo_mobile_ui/lib/common/filter_bar.dart b/filcnaplo_mobile_ui/lib/common/filter_bar.dart index c95e1a7..8191e2f 100755 --- a/filcnaplo_mobile_ui/lib/common/filter_bar.dart +++ b/filcnaplo_mobile_ui/lib/common/filter_bar.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; class FilterBar extends StatefulWidget implements PreferredSizeWidget { const FilterBar({ - Key? key, + super.key, required this.items, required this.controller, this.onTap, @@ -13,8 +13,8 @@ class FilterBar extends StatefulWidget implements PreferredSizeWidget { this.disableFading = false, this.scrollable = true, this.censored = false, - }) : assert(items.length == controller.length), - super(key: key); + this.tabAlignment = TabAlignment.start, + }) : assert(items.length == controller.length); final List items; final TabController controller; @@ -23,6 +23,7 @@ class FilterBar extends StatefulWidget implements PreferredSizeWidget { final bool disableFading; final bool scrollable; final bool censored; + final TabAlignment tabAlignment; @override final Size preferredSize = const Size.fromHeight(42.0); @@ -37,7 +38,9 @@ class _FilterBarState extends State { void initState() { super.initState(); - censoredItemsWidth = List.generate(widget.items.length, (index) => 25 + Random().nextDouble() * 50).toList(); + censoredItemsWidth = List.generate( + widget.items.length, (index) => 25 + Random().nextDouble() * 50) + .toList(); } @override @@ -67,18 +70,21 @@ class _FilterBarState extends State { tabs: widget.censored ? censoredItemsWidth .map( - (e) => Container( - width: e, - height: 15, - decoration: BoxDecoration( - color: AppColors.of(context).text.withOpacity(.45), - borderRadius: BorderRadius.circular(8.0), + (e) => Tab( + child: Container( + width: e, + height: 15, + decoration: BoxDecoration( + color: AppColors.of(context).text.withOpacity(.45), + borderRadius: BorderRadius.circular(8.0), + ), ), ), ) .toList() : widget.items, onTap: widget.onTap, + tabAlignment: widget.tabAlignment, ); return Container( @@ -93,19 +99,26 @@ class _FilterBarState extends State { // avoid fading over selected tab return ShaderMask( shaderCallback: (Rect bounds) { - final Color bg = Theme.of(context).scaffoldBackgroundColor; + final Color bg = + Theme.of(context).scaffoldBackgroundColor; final double index = widget.controller.animation!.value; - return LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ - index < 0.2 ? Colors.transparent : bg, - Colors.transparent, - Colors.transparent, - index > widget.controller.length - 1.2 ? Colors.transparent : bg - ], stops: const [ - 0, - 0.1, - 0.9, - 1 - ]).createShader(bounds); + return LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + index < 0.2 ? Colors.transparent : bg, + Colors.transparent, + Colors.transparent, + index > widget.controller.length - 1.2 + ? Colors.transparent + : bg + ], + stops: const [ + 0, + 0.1, + 0.9, + 1 + ]).createShader(bounds); }, blendMode: BlendMode.dstOut, child: child); diff --git a/filcnaplo_mobile_ui/lib/common/hero_scrollview.dart b/filcnaplo_mobile_ui/lib/common/hero_scrollview.dart index 91872a2..af3174d 100755 --- a/filcnaplo_mobile_ui/lib/common/hero_scrollview.dart +++ b/filcnaplo_mobile_ui/lib/common/hero_scrollview.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo/utils/format.dart'; class HeroScrollView extends StatefulWidget { const HeroScrollView( - {Key? key, + {super.key, required this.child, required this.title, required this.icon, @@ -12,8 +12,7 @@ class HeroScrollView extends StatefulWidget { this.navBarItems = const [], this.onClose, this.iconSize = 64.0, - this.scrollController}) - : super(key: key); + this.scrollController}); final Widget child; final String title; @@ -25,10 +24,10 @@ class HeroScrollView extends StatefulWidget { final bool italic; @override - _HeroScrollViewState createState() => _HeroScrollViewState(); + HeroScrollViewState createState() => HeroScrollViewState(); } -class _HeroScrollViewState extends State { +class HeroScrollViewState extends State { late ScrollController _scrollController; bool showBarTitle = false; @@ -69,6 +68,7 @@ class _HeroScrollViewState extends State { surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, title: AnimatedOpacity( opacity: showBarTitle ? 1.0 : 0.0, + duration: const Duration(milliseconds: 200), child: Row( children: [ Icon(widget.icon, @@ -86,8 +86,7 @@ class _HeroScrollViewState extends State { ), ), ], - ), - duration: const Duration(milliseconds: 200)), + )), leading: BackButton( color: AppColors.of(context).text, onPressed: () { diff --git a/filcnaplo_mobile_ui/lib/common/material_action_button.dart b/filcnaplo_mobile_ui/lib/common/material_action_button.dart index eaf1f96..df8ba68 100755 --- a/filcnaplo_mobile_ui/lib/common/material_action_button.dart +++ b/filcnaplo_mobile_ui/lib/common/material_action_button.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; class MaterialActionButton extends StatelessWidget { const MaterialActionButton({ - Key? key, + super.key, required this.child, this.onPressed, this.backgroundColor, - }) : super(key: key); + }); final Widget child; final Function()? onPressed; @@ -19,17 +19,17 @@ class MaterialActionButton extends StatelessWidget { return RawMaterialButton( padding: const EdgeInsets.symmetric(horizontal: 16.0), shape: const StadiumBorder(), - child: DefaultTextStyle( - child: child, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - fontWeight: FontWeight.w600, - color: backgroundColor != null ? ColorUtils.foregroundColor(backgroundColor!) : null, - ), - ), fillColor: backgroundColor ?? AppColors.of(context).text.withOpacity(.15), elevation: 0, highlightElevation: 0, onPressed: onPressed, + child: DefaultTextStyle( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontWeight: FontWeight.w600, + color: backgroundColor != null ? ColorUtils.foregroundColor(backgroundColor!) : null, + ), + child: child, + ), ); } } diff --git a/filcnaplo_mobile_ui/lib/common/new_content_indicator.dart b/filcnaplo_mobile_ui/lib/common/new_content_indicator.dart index 3287848..2a01007 100755 --- a/filcnaplo_mobile_ui/lib/common/new_content_indicator.dart +++ b/filcnaplo_mobile_ui/lib/common/new_content_indicator.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class NewContentIndicator extends StatelessWidget { - const NewContentIndicator({Key? key, this.size = 64.0}) : super(key: key); + const NewContentIndicator({super.key, this.size = 64.0}); final double size; diff --git a/filcnaplo_mobile_ui/lib/common/panel/panel.dart b/filcnaplo_mobile_ui/lib/common/panel/panel.dart index 0e2f351..8f612d7 100755 --- a/filcnaplo_mobile_ui/lib/common/panel/panel.dart +++ b/filcnaplo_mobile_ui/lib/common/panel/panel.dart @@ -5,8 +5,7 @@ import 'package:provider/provider.dart'; class Panel extends StatelessWidget { const Panel( - {Key? key, this.child, this.title, this.padding, this.hasShadow = true}) - : super(key: key); + {super.key, this.child, this.title, this.padding, this.hasShadow = true}); final Widget? child; final Widget? title; @@ -48,7 +47,7 @@ class Panel extends StatelessWidget { } class PanelTitle extends StatelessWidget { - const PanelTitle({Key? key, required this.title}) : super(key: key); + const PanelTitle({super.key, required this.title}); final Widget title; @@ -67,7 +66,7 @@ class PanelTitle extends StatelessWidget { } class PanelHeader extends StatelessWidget { - const PanelHeader({Key? key, required this.padding}) : super(key: key); + const PanelHeader({super.key, required this.padding}); final EdgeInsetsGeometry padding; @@ -95,7 +94,7 @@ class PanelHeader extends StatelessWidget { } class PanelBody extends StatelessWidget { - const PanelBody({Key? key, this.child, this.padding}) : super(key: key); + const PanelBody({super.key, this.child, this.padding}); final Widget? child; final EdgeInsetsGeometry? padding; @@ -123,7 +122,7 @@ class PanelBody extends StatelessWidget { } class PanelFooter extends StatelessWidget { - const PanelFooter({Key? key, required this.padding}) : super(key: key); + const PanelFooter({super.key, required this.padding}); final EdgeInsetsGeometry padding; diff --git a/filcnaplo_mobile_ui/lib/common/panel/panel_action_button.dart b/filcnaplo_mobile_ui/lib/common/panel/panel_action_button.dart index 3bf62cf..4fbac67 100755 --- a/filcnaplo_mobile_ui/lib/common/panel/panel_action_button.dart +++ b/filcnaplo_mobile_ui/lib/common/panel/panel_action_button.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; class PanelActionButton extends StatelessWidget { const PanelActionButton({ - Key? key, + super.key, this.onPressed, this.padding = const EdgeInsets.symmetric(horizontal: 14.0), this.leading, this.title, this.trailing, - }) : super(key: key); + }); final void Function()? onPressed; final EdgeInsetsGeometry padding; diff --git a/filcnaplo_mobile_ui/lib/common/panel/panel_button.dart b/filcnaplo_mobile_ui/lib/common/panel/panel_button.dart index d1f3b64..c94d6fa 100755 --- a/filcnaplo_mobile_ui/lib/common/panel/panel_button.dart +++ b/filcnaplo_mobile_ui/lib/common/panel/panel_button.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; class PanelButton extends StatelessWidget { const PanelButton({ - Key? key, + super.key, this.onPressed, this.padding = const EdgeInsets.symmetric(horizontal: 14.0), this.leading, @@ -13,7 +13,7 @@ class PanelButton extends StatelessWidget { this.trailing, this.background = false, this.trailingDivider = false, - }) : super(key: key); + }); final void Function()? onPressed; final EdgeInsetsGeometry padding; diff --git a/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart b/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart index fec8b1a..c6128d7 100644 --- a/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart +++ b/filcnaplo_mobile_ui/lib/common/personality_card/empty_card.dart @@ -5,9 +5,9 @@ import 'package:provider/provider.dart'; class EmptyCard extends StatefulWidget { const EmptyCard({ - Key? key, + super.key, required this.text, - }) : super(key: key); + }); final String text; diff --git a/filcnaplo_mobile_ui/lib/common/personality_card/personality_card.dart b/filcnaplo_mobile_ui/lib/common/personality_card/personality_card.dart index b442c38..343ad01 100644 --- a/filcnaplo_mobile_ui/lib/common/personality_card/personality_card.dart +++ b/filcnaplo_mobile_ui/lib/common/personality_card/personality_card.dart @@ -18,9 +18,9 @@ import 'personality_card.i18n.dart'; class PersonalityCard extends StatefulWidget { const PersonalityCard({ - Key? key, + super.key, required this.user, - }) : super(key: key); + }); final UserProvider user; diff --git a/filcnaplo_mobile_ui/lib/common/profile_image/profile_button.dart b/filcnaplo_mobile_ui/lib/common/profile_image/profile_button.dart index 03714ad..976c7bd 100755 --- a/filcnaplo_mobile_ui/lib/common/profile_image/profile_button.dart +++ b/filcnaplo_mobile_ui/lib/common/profile_image/profile_button.dart @@ -17,7 +17,7 @@ import 'package:provider/provider.dart'; import 'package:wtf_sliding_sheet/wtf_sliding_sheet.dart'; class ProfileButton extends StatelessWidget { - const ProfileButton({Key? key, required this.child}) : super(key: key); + const ProfileButton({super.key, required this.child}); final ProfileImage child; @@ -35,6 +35,8 @@ class ProfileButton extends StatelessWidget { Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), + Provider.of(context, listen: false) + .restoreRecipients(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), diff --git a/filcnaplo_mobile_ui/lib/common/profile_image/profile_image.dart b/filcnaplo_mobile_ui/lib/common/profile_image/profile_image.dart index 9c75d95..8be0567 100755 --- a/filcnaplo_mobile_ui/lib/common/profile_image/profile_image.dart +++ b/filcnaplo_mobile_ui/lib/common/profile_image/profile_image.dart @@ -8,7 +8,7 @@ import 'package:filcnaplo/utils/color.dart'; class ProfileImage extends StatefulWidget { const ProfileImage({ - Key? key, + super.key, this.onTap, this.onDoubleTap, this.onLongPress, @@ -20,7 +20,7 @@ class ProfileImage extends StatefulWidget { this.role = Role.student, this.censored = false, this.profilePictureString = "", - }) : super(key: key); + }); final void Function()? onTap; final void Function()? onDoubleTap; @@ -176,7 +176,7 @@ class _ProfileImageState extends State { children: [ if (widget.name != null && (widget.name?.trim().length ?? 0) > 0) Hero( - tag: widget.heroTag! + "background", + tag: "${widget.heroTag!}background", transitionOnUserGestures: true, child: Material( clipBehavior: Clip.hardEdge, @@ -197,27 +197,27 @@ class _ProfileImageState extends State { ), ), Hero( - tag: widget.heroTag! + "child", + tag: "${widget.heroTag!}child", transitionOnUserGestures: true, child: Material( clipBehavior: Clip.hardEdge, shape: profilePicture != null ? const CircleBorder() : null, - child: profilePicture ?? child, type: MaterialType.transparency, + child: profilePicture ?? child, ), ), // Badge if (widget.badge) Hero( - tag: widget.heroTag! + "new_content_indicator", + tag: "${widget.heroTag!}new_content_indicator", child: NewContentIndicator(size: widget.radius * 2), ), // Role indicator if (widget.role == Role.parent) Hero( - tag: widget.heroTag! + "role_indicator", + tag: "${widget.heroTag!}role_indicator", child: FittedBox( fit: BoxFit.fitHeight, child: SizedBox( diff --git a/filcnaplo_mobile_ui/lib/common/progress_bar.dart b/filcnaplo_mobile_ui/lib/common/progress_bar.dart index ffffa00..e4eb936 100755 --- a/filcnaplo_mobile_ui/lib/common/progress_bar.dart +++ b/filcnaplo_mobile_ui/lib/common/progress_bar.dart @@ -2,8 +2,7 @@ import 'package:flutter/material.dart'; class ProgressBar extends StatelessWidget { const ProgressBar( - {Key? key, required this.value, this.backgroundColor, this.height = 8.0}) - : super(key: key); + {super.key, required this.value, this.backgroundColor, this.height = 8.0}); final double value; final Color? backgroundColor; diff --git a/filcnaplo_mobile_ui/lib/common/round_border_icon.dart b/filcnaplo_mobile_ui/lib/common/round_border_icon.dart index ac1fdf4..a5bfef3 100644 --- a/filcnaplo_mobile_ui/lib/common/round_border_icon.dart +++ b/filcnaplo_mobile_ui/lib/common/round_border_icon.dart @@ -7,12 +7,11 @@ class RoundBorderIcon extends StatelessWidget { final Widget icon; const RoundBorderIcon( - {Key? key, + {super.key, this.color = Colors.black, this.width = 1.5, this.padding = 5.0, - required this.icon}) - : super(key: key); + required this.icon}); @override Widget build(BuildContext context) { diff --git a/filcnaplo_mobile_ui/lib/common/soon_alert/soon_alert.dart b/filcnaplo_mobile_ui/lib/common/soon_alert/soon_alert.dart index 4fdba24..213127a 100644 --- a/filcnaplo_mobile_ui/lib/common/soon_alert/soon_alert.dart +++ b/filcnaplo_mobile_ui/lib/common/soon_alert/soon_alert.dart @@ -3,7 +3,7 @@ import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.i18n.dart'; import 'package:flutter/material.dart'; class SoonAlert extends StatelessWidget { - const SoonAlert({Key? key}) : super(key: key); + const SoonAlert({super.key}); static show({required BuildContext context}) => showDialog(context: context, builder: (context) => const SoonAlert()); diff --git a/filcnaplo_mobile_ui/lib/common/trend_display.dart b/filcnaplo_mobile_ui/lib/common/trend_display.dart index 0631056..f5e1cb9 100755 --- a/filcnaplo_mobile_ui/lib/common/trend_display.dart +++ b/filcnaplo_mobile_ui/lib/common/trend_display.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:i18n_extension/i18n_widget.dart'; class TrendDisplay extends StatelessWidget { - const TrendDisplay({Key? key, required this.current, required this.previous, this.padding}) : super(key: key); + const TrendDisplay({super.key, required this.current, required this.previous, this.padding}); final T current; final T previous; diff --git a/filcnaplo_mobile_ui/lib/common/viewable.dart b/filcnaplo_mobile_ui/lib/common/viewable.dart index eb73a47..7190248 100755 --- a/filcnaplo_mobile_ui/lib/common/viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/viewable.dart @@ -59,12 +59,12 @@ enum _ViewableLocation { class Viewable extends StatefulWidget { const Viewable({ - Key? key, + super.key, required this.view, required this.tile, this.actions = const [], this.previewBuilder, - }) : super(key: key); + }); final Widget tile; final Widget view; @@ -255,12 +255,11 @@ class _ViewableState extends State with TickerProviderStateMixin { class _DecoyChild extends StatefulWidget { const _DecoyChild({ - Key? key, this.beginRect, required this.controller, this.endRect, this.child, - }) : super(key: key); + }); final Rect? beginRect; final AnimationController controller; @@ -364,17 +363,13 @@ class _ViewableRoute extends PopupRoute { required _ViewableLocation contextMenuLocation, this.barrierLabel, _ViewablePreviewBuilderChildless? builder, - ui.ImageFilter? filter, + super.filter, required Rect previousChildRect, - RouteSettings? settings, + super.settings, }) : _actions = actions, _builder = builder, _contextMenuLocation = contextMenuLocation, - _previousChildRect = previousChildRect, - super( - filter: filter, - settings: settings, - ); + _previousChildRect = previousChildRect; static const Color _kModalBarrierColor = Color(0x6604040F); @@ -607,7 +602,6 @@ class _ViewableRoute extends PopupRoute { class _ContextMenuRouteStatic extends StatefulWidget { const _ContextMenuRouteStatic({ - Key? key, this.actions, required this.child, this.childGlobalKey, @@ -615,7 +609,7 @@ class _ContextMenuRouteStatic extends StatefulWidget { this.onDismiss, required this.orientation, this.sheetGlobalKey, - }) : super(key: key); + }); final List? actions; final Widget child; @@ -899,9 +893,9 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T class _ViewableSheet extends StatelessWidget { const _ViewableSheet({ - Key? key, + super.key, required this.actions, - }) : super(key: key); + }); final List actions; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_display.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_display.dart index b0fd86d..86ab0fa 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_display.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_display.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class AbsenceDisplay extends StatelessWidget { - const AbsenceDisplay(this.excused, this.unexcused, this.pending, {Key? key}) : super(key: key); + const AbsenceDisplay(this.excused, this.unexcused, this.pending, {super.key}); final int excused; final int unexcused; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_subject_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_subject_tile.dart index adf5e5f..9a3e0c3 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_subject_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_subject_tile.dart @@ -9,13 +9,12 @@ import 'package:provider/provider.dart'; class AbsenceSubjectTile extends StatelessWidget { const AbsenceSubjectTile(this.subject, - {Key? key, + {super.key, this.percentage = 0.0, this.excused = 0, this.unexcused = 0, this.pending = 0, - this.onTap}) - : super(key: key); + this.onTap}); final GradeSubject subject; final void Function()? onTap; @@ -62,11 +61,11 @@ class AbsenceSubjectTile extends StatelessWidget { alignment: Alignment.centerRight, children: [ const Opacity( + opacity: 0, child: Text("100%", - style: TextStyle(fontFamily: "monospace")), - opacity: 0), + style: TextStyle(fontFamily: "monospace"))), Text( - percentage.round().toString() + "%", + "${percentage.round()}%", style: TextStyle( // fontFamily: "monospace", color: getColorByPercentage(percentage, context: context), diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart index b01e8e7..83dabf1 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_tile.dart @@ -10,8 +10,7 @@ import 'absence_tile.i18n.dart'; class AbsenceTile extends StatelessWidget { const AbsenceTile(this.absence, - {Key? key, this.onTap, this.elevation = 0.0, this.padding}) - : super(key: key); + {super.key, this.onTap, this.elevation = 0.0, this.padding}); final Absence absence; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart index 1ffac65..72945f2 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_view.dart @@ -18,8 +18,7 @@ import 'absence_view.i18n.dart'; class AbsenceView extends StatelessWidget { const AbsenceView(this.absence, - {Key? key, this.outsideContext, this.viewable = false}) - : super(key: key); + {super.key, this.outsideContext, this.viewable = false}); final Absence absence; final BuildContext? outsideContext; @@ -90,9 +89,7 @@ class AbsenceView extends StatelessWidget { if (absence.delay > 0) Detail( title: "delay".i18n, - description: absence.delay.toString() + - " " + - "minutes".i18n.plural(absence.delay), + description: "${absence.delay} ${"minutes".i18n.plural(absence.delay)}", ), if (absence.lessonIndex != null) Detail( diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_viewable.dart index 4eda2b4..a8ec1a0 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence/absence_viewable.dart @@ -15,7 +15,7 @@ import 'package:filcnaplo/utils/reverse_search.dart'; import 'absence_view.i18n.dart'; class AbsenceViewable extends StatelessWidget { - const AbsenceViewable(this.absence, {Key? key, this.padding}) : super(key: key); + const AbsenceViewable(this.absence, {super.key, this.padding}); final Absence absence; final EdgeInsetsGeometry? padding; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_container.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_container.dart index 1b225e1..4f91e7b 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_container.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_container.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class AbsenceGroupContainer extends InheritedWidget { - const AbsenceGroupContainer({Key? key, required Widget child}) : super(key: key, child: child); + const AbsenceGroupContainer({super.key, required super.child}); static AbsenceGroupContainer? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType(); diff --git a/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart index 62e8b45..faec32b 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/absence_group/absence_group_tile.dart @@ -11,8 +11,7 @@ import 'absence_group_tile.i18n.dart'; class AbsenceGroupTile extends StatelessWidget { const AbsenceGroupTile(this.absences, - {Key? key, this.showDate = false, this.padding}) - : super(key: key); + {super.key, this.showDate = false, this.padding}); final List absences; final bool showDate; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart index fb9c943..78fa38e 100644 --- a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart @@ -5,7 +5,7 @@ import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class AdTile extends StatelessWidget { - const AdTile(this.ad, {Key? key, this.onTap, this.padding}) : super(key: key); + const AdTile(this.ad, {super.key, this.onTap, this.padding}); final Ad ad; final Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_viewable.dart index fa7ed4a..bbf0fcc 100644 --- a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_viewable.dart @@ -5,7 +5,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'ad_tile.dart'; class AdViewable extends StatelessWidget { - const AdViewable(this.ad, {Key? key}) : super(key: key); + const AdViewable(this.ad, {super.key}); final Ad ad; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/card_handle.dart b/filcnaplo_mobile_ui/lib/common/widgets/card_handle.dart index fb4fb5f..592200c 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/card_handle.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/card_handle.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class CardHandle extends StatelessWidget { - const CardHandle({Key? key, this.child}) : super(key: key); + const CardHandle({super.key, this.child}); final Widget? child; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_card.dart b/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_card.dart index c9bedd8..016dbfb 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_card.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_card.dart @@ -8,7 +8,7 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'certification_card.i18n.dart'; class CertificationCard extends StatelessWidget { - const CertificationCard(this.grades, {Key? key, required this.gradeType, this.padding}) : super(key: key); + const CertificationCard(this.grades, {super.key, required this.gradeType, this.padding}); final List grades; final GradeType gradeType; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_tile.dart index 83421f5..484d21c 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_tile.dart @@ -11,7 +11,7 @@ import 'package:provider/provider.dart'; import 'certification_tile.i18n.dart'; class CertificationTile extends StatelessWidget { - const CertificationTile(this.grade, {Key? key, this.onTap, this.padding}) : super(key: key); + const CertificationTile(this.grade, {super.key, this.onTap, this.padding}); final Function()? onTap; final Grade grade; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_view.dart index 15cb282..09ef8ff 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/cretification/certification_view.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class CertificationView extends StatelessWidget { - const CertificationView(this.grades, {Key? key, required this.gradeType}) : super(key: key); + const CertificationView(this.grades, {super.key, required this.gradeType}); final List grades; final GradeType gradeType; @@ -26,6 +26,9 @@ class CertificationView extends StatelessWidget { icon: FeatherIcons.award, iconSize: 50, child: ListView( + shrinkWrap: true, + padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), + physics: const BouncingScrollPhysics(), children: [ SafeArea( child: Panel( @@ -35,9 +38,6 @@ class CertificationView extends StatelessWidget { ), ) ], - shrinkWrap: true, - padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), - physics: const BouncingScrollPhysics(), ))); } } diff --git a/filcnaplo_mobile_ui/lib/common/widgets/custom_switch.dart b/filcnaplo_mobile_ui/lib/common/widgets/custom_switch.dart index a6640cc..06a8124 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/custom_switch.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/custom_switch.dart @@ -5,10 +5,10 @@ class CustomSwitch extends StatelessWidget { final bool value; const CustomSwitch({ - Key? key, + super.key, required this.onChanged, required this.value, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/filcnaplo_mobile_ui/lib/common/widgets/event/event_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/event/event_tile.dart index 7076f8c..807c17e 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/event/event_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/event/event_tile.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; import 'package:flutter/material.dart'; class EventTile extends StatelessWidget { - const EventTile(this.event, {Key? key, this.onTap, this.padding}) : super(key: key); + const EventTile(this.event, {super.key, this.onTap, this.padding}); final Event event; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/event/event_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/event/event_view.dart index 0f75abe..015747b 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/event/event_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/event/event_view.dart @@ -6,7 +6,7 @@ import 'package:flutter_custom_tabs/flutter_custom_tabs.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; class EventView extends StatelessWidget { - const EventView(this.event, {Key? key}) : super(key: key); + const EventView(this.event, {super.key}); final Event event; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/event/event_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/event/event_viewable.dart index f58b192..2b3655e 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/event/event_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/event/event_viewable.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/event/event_view.dart'; import 'package:flutter/material.dart'; class EventViewable extends StatelessWidget { - const EventViewable(this.event, {Key? key}) : super(key: key); + const EventViewable(this.event, {super.key}); final Event event; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_tile.dart index 151ad39..3f9076e 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_tile.dart @@ -6,8 +6,7 @@ import 'package:filcnaplo/utils/format.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class ExamTile extends StatelessWidget { - const ExamTile(this.exam, {Key? key, this.onTap, this.padding}) - : super(key: key); + const ExamTile(this.exam, {super.key, this.onTap, this.padding}); final Exam exam; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart index 632fb2f..703b67d 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_view.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'exam_view.i18n.dart'; class ExamView extends StatelessWidget { - const ExamView(this.exam, {Key? key}) : super(key: key); + const ExamView(this.exam, {super.key}); final Exam exam; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_viewable.dart index c931bae..7eab3b6 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/exam/exam_viewable.dart @@ -6,7 +6,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_view.dart'; import 'package:flutter/material.dart'; class ExamViewable extends StatelessWidget { - const ExamViewable(this.exam, {Key? key}) : super(key: key); + const ExamViewable(this.exam, {super.key}); final Exam exam; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart index 00e87d0..0b9dda4 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart @@ -9,12 +9,11 @@ import 'package:provider/provider.dart'; class GradeSubjectTile extends StatelessWidget { const GradeSubjectTile(this.subject, - {Key? key, + {super.key, this.average = 0.0, this.groupAverage = 0.0, this.onTap, - this.averageBefore = 0.0}) - : super(key: key); + this.averageBefore = 0.0}); final GradeSubject subject; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart index 4673b3d..1267ef6 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_view.dart @@ -9,7 +9,7 @@ import 'package:provider/provider.dart'; import 'grade_view.i18n.dart'; class GradeView extends StatelessWidget { - const GradeView(this.grade, {Key? key}) : super(key: key); + const GradeView(this.grade, {super.key}); static show(Grade grade, {required BuildContext context}) => showBottomCard(context: context, child: GradeView(grade)); @@ -59,7 +59,7 @@ class GradeView extends StatelessWidget { // Grade Details Detail( title: "value".i18n, - description: "${grade.value.valueName} " + percentText(), + description: "${grade.value.valueName} ${percentText()}", ), if (grade.description != "") Detail(title: "description".i18n, description: grade.description), diff --git a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_viewable.dart index 69547ed..0ce16e5 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/grade/grade_viewable.dart @@ -7,7 +7,7 @@ import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart'; import 'package:flutter/material.dart'; class GradeViewable extends StatelessWidget { - const GradeViewable(this.grade, {Key? key, this.padding}) : super(key: key); + const GradeViewable(this.grade, {super.key, this.padding}); final Grade grade; final EdgeInsetsGeometry? padding; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/grade/new_grades.dart b/filcnaplo_mobile_ui/lib/common/widgets/grade/new_grades.dart index d666ddd..07f7fd7 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/grade/new_grades.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/grade/new_grades.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; @@ -10,7 +12,7 @@ import 'package:rive/rive.dart'; import 'new_grades.i18n.dart'; class NewGradesSurprise extends StatelessWidget { - const NewGradesSurprise(this.grades, {Key? key, this.censored = false}) : super(key: key); + const NewGradesSurprise(this.grades, {super.key, this.censored = false}); final List grades; final bool censored; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/grade/surprise_grade.dart b/filcnaplo_mobile_ui/lib/common/widgets/grade/surprise_grade.dart index ba787d3..2ec8873 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/grade/surprise_grade.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/grade/surprise_grade.dart @@ -3,17 +3,19 @@ import 'dart:ui'; import 'package:animated_background/animated_background.dart' as bg; import 'package:filcnaplo/helpers/subject.dart'; +import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_mobile_ui/pages/home/particle.dart'; import 'package:flutter/material.dart'; import 'package:filcnaplo/utils/format.dart'; +import 'package:provider/provider.dart'; import 'package:rive/rive.dart' as rive; import 'new_grades.i18n.dart'; class SurpriseGrade extends StatefulWidget { - const SurpriseGrade(this.grade, {Key? key}) : super(key: key); + const SurpriseGrade(this.grade, {super.key}); final Grade grade; @@ -21,20 +23,27 @@ class SurpriseGrade extends StatefulWidget { State createState() => _SurpriseGradeState(); } -class _SurpriseGradeState extends State with TickerProviderStateMixin { +class _SurpriseGradeState extends State + with TickerProviderStateMixin { late AnimationController _revealAnimFade; late AnimationController _revealAnimScale; late AnimationController _revealAnimGrade; late AnimationController _revealAnimParticle; late rive.RiveAnimationController _controller; + late SettingsProvider settingsProvider; + @override void initState() { super.initState(); - _revealAnimFade = AnimationController(vsync: this, duration: const Duration(milliseconds: 500)); - _revealAnimScale = AnimationController(vsync: this, duration: const Duration(milliseconds: 300)); - _revealAnimGrade = AnimationController(vsync: this, duration: const Duration(seconds: 1)); - _revealAnimParticle = AnimationController(vsync: this, duration: const Duration(seconds: 2)); + _revealAnimFade = AnimationController( + vsync: this, duration: const Duration(milliseconds: 500)); + _revealAnimScale = AnimationController( + vsync: this, duration: const Duration(milliseconds: 300)); + _revealAnimGrade = + AnimationController(vsync: this, duration: const Duration(seconds: 1)); + _revealAnimParticle = + AnimationController(vsync: this, duration: const Duration(seconds: 2)); _revealAnimScale.animateTo(0.7, duration: Duration.zero); _controller = rive.SimpleAnimation('Timeline 1', autoplay: false); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -65,7 +74,9 @@ class _SurpriseGradeState extends State with TickerProviderStateM void reveal() async { if (!subtitle) { - _revealAnimParticle.animateBack(0.0, curve: Curves.fastLinearToSlowEaseIn, duration: const Duration(milliseconds: 300)); + _revealAnimParticle.animateBack(0.0, + curve: Curves.fastLinearToSlowEaseIn, + duration: const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 50)); _revealAnimGrade.animateBack(0.0, curve: Curves.fastLinearToSlowEaseIn); await Future.delayed(const Duration(milliseconds: 50)); @@ -78,13 +89,20 @@ class _SurpriseGradeState extends State with TickerProviderStateM setState(() => hold = false); _controller.isActive = true; await Future.delayed(const Duration(seconds: 2)); - if (mounted) _revealAnimGrade.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn); + if (mounted) { + _revealAnimGrade.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn); + } await Future.delayed(const Duration(milliseconds: 700)); - if (mounted) await _revealAnimParticle.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn); + if (mounted) { + await _revealAnimParticle.animateTo(1.0, + curve: Curves.fastLinearToSlowEaseIn); + } } @override Widget build(BuildContext context) { + settingsProvider = Provider.of(context); + return AnimatedBuilder( animation: _revealAnimFade, builder: (context, child) { @@ -136,7 +154,8 @@ class _SurpriseGradeState extends State with TickerProviderStateM mainAxisAlignment: MainAxisAlignment.center, children: [ SlideTransition( - position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, 0.7))), + position: _revealAnimGrade.drive( + Tween(begin: Offset.zero, end: const Offset(0, 0.7))), child: AnimatedScale( scale: hold ? 1.1 : 1.0, curve: Curves.easeOutBack, @@ -146,7 +165,10 @@ class _SurpriseGradeState extends State with TickerProviderStateM onLongPressEnd: (_) => reveal(), onLongPressCancel: reveal, child: ScaleTransition( - scale: CurvedAnimation(curve: Curves.easeInOut, parent: _revealAnimGrade.drive(Tween(begin: 1.0, end: 0.8))), + scale: CurvedAnimation( + curve: Curves.easeInOut, + parent: _revealAnimGrade + .drive(Tween(begin: 1.0, end: 0.8))), child: Stack( alignment: Alignment.center, children: [ @@ -161,66 +183,101 @@ class _SurpriseGradeState extends State with TickerProviderStateM ), ), SlideTransition( - position: _revealAnimParticle.drive(Tween(begin: const Offset(0, 0.3), end: const Offset(0, 0.8))), + position: _revealAnimParticle.drive(Tween( + begin: const Offset(0, 0.3), + end: const Offset(0, 0.8))), child: FadeTransition( opacity: _revealAnimParticle, child: ClipRRect( borderRadius: BorderRadius.circular(24.0), child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 32.0, sigmaY: 32.0), + filter: ImageFilter.blur( + sigmaX: 32.0, sigmaY: 32.0), child: Container( width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 20.0), + padding: const EdgeInsets.symmetric( + horizontal: 32.0, vertical: 20.0), decoration: BoxDecoration( color: Colors.white.withOpacity(.3), - borderRadius: BorderRadius.circular(24.0), - border: Border.all(color: Colors.black.withOpacity(.3), width: 1.0), + borderRadius: + BorderRadius.circular(24.0), + border: Border.all( + color: Colors.black + .withOpacity(.3), + width: 1.0), ), child: Row( children: [ Expanded( child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisSize: + MainAxisSize.min, children: [ - if (widget.grade.description != "") + if (widget.grade + .description != + "") Text( - widget.grade.description, + widget + .grade.description, style: const TextStyle( color: Colors.white, - fontWeight: FontWeight.bold, + fontWeight: + FontWeight.bold, fontSize: 26.0, ), maxLines: 2, - overflow: TextOverflow.ellipsis, + overflow: TextOverflow + .ellipsis, ), Text( - widget.grade.subject.renamedTo ?? widget.grade.subject.name.capital(), + widget.grade.subject + .renamedTo ?? + widget.grade.subject + .name + .capital(), style: TextStyle( - color: Colors.white.withOpacity(.8), - fontWeight: FontWeight.bold, + color: Colors.white + .withOpacity(.8), + fontWeight: + FontWeight.bold, fontSize: 24.0, - fontStyle: widget.grade.subject.isRenamed ? FontStyle.italic : null), + fontStyle: widget + .grade + .subject + .isRenamed && + settingsProvider + .renamedSubjectsItalics + ? FontStyle.italic + : null), maxLines: 2, - overflow: TextOverflow.ellipsis, + overflow: + TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( "${widget.grade.value.weight}%", style: TextStyle( - color: Colors.white.withOpacity(.7), - fontWeight: FontWeight.w600, + color: Colors.white + .withOpacity(.7), + fontWeight: + FontWeight.w600, fontSize: 20.0, ), maxLines: 2, - overflow: TextOverflow.ellipsis, + overflow: + TextOverflow.ellipsis, ), ], ), ), const SizedBox(width: 20.0), Icon( - SubjectIcon.resolveVariant(subject: widget.grade.subject, context: context), + SubjectIcon.resolveVariant( + subject: + widget.grade.subject, + context: context), color: Colors.white, size: 82.0, ), @@ -257,7 +314,10 @@ class _SurpriseGradeState extends State with TickerProviderStateM animation: _revealAnimParticle, builder: (context, child) { bool shouldPaint = false; - if (_revealAnimParticle.status == AnimationStatus.forward || _revealAnimParticle.status == AnimationStatus.reverse) { + if (_revealAnimParticle.status == + AnimationStatus.forward || + _revealAnimParticle.status == + AnimationStatus.reverse) { shouldPaint = true; } return ScaleTransition( @@ -265,25 +325,46 @@ class _SurpriseGradeState extends State with TickerProviderStateM child: FadeTransition( opacity: _revealAnimGrade, child: SlideTransition( - position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, -0.6))), + position: _revealAnimGrade.drive(Tween( + begin: Offset.zero, + end: const Offset(0, -0.6))), child: Column( mainAxisSize: MainAxisSize.min, children: [ SlideTransition( - position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, -0.9))), + position: _revealAnimGrade.drive(Tween( + begin: Offset.zero, + end: const Offset(0, -0.9))), child: Text( - ["legendary", "epic", "rare", "uncommon", "common"][5 - widget.grade.value.value].i18n, + [ + "legendary", + "epic", + "rare", + "uncommon", + "common" + ][5 - widget.grade.value.value] + .i18n, style: TextStyle( fontSize: 46.0, fontWeight: FontWeight.bold, - color: gradeColor(context: context, value: widget.grade.value.value), + color: gradeColor( + context: context, + value: widget.grade.value.value), shadows: [ Shadow( - color: gradeColor(context: context, value: widget.grade.value.value).withOpacity(.5), + color: gradeColor( + context: context, + value: + widget.grade.value.value) + .withOpacity(.5), blurRadius: 24.0, ), Shadow( - color: gradeColor(context: context, value: widget.grade.value.value).withOpacity(.3), + color: gradeColor( + context: context, + value: + widget.grade.value.value) + .withOpacity(.3), offset: const Offset(-3, -3), ), ], @@ -292,7 +373,10 @@ class _SurpriseGradeState extends State with TickerProviderStateM ), const SizedBox(height: 32.0), ScaleTransition( - scale: CurvedAnimation(curve: Curves.easeInOutBack, parent: _revealAnimParticle.drive(Tween(begin: 0.6, end: 1.0))), + scale: CurvedAnimation( + curve: Curves.easeInOutBack, + parent: _revealAnimParticle + .drive(Tween(begin: 0.6, end: 1.0))), child: CustomPaint( painter: PimpPainter( particle: Sprinkles(), @@ -308,8 +392,11 @@ class _SurpriseGradeState extends State with TickerProviderStateM shouldPaint: shouldPaint, ), child: RotationTransition( - turns: - CurvedAnimation(curve: Curves.easeInBack, parent: _revealAnimGrade).drive(Tween(begin: 0.95, end: 1.0)), + turns: CurvedAnimation( + curve: Curves.easeInBack, + parent: _revealAnimGrade) + .drive( + Tween(begin: 0.95, end: 1.0)), child: GradeValueWidget( widget.grade.value, fill: true, @@ -337,7 +424,12 @@ class _SurpriseGradeState extends State with TickerProviderStateM } class PimpPainter extends CustomPainter { - PimpPainter({required this.particle, required this.seed, required this.controller, required this.shouldPaint}) : super(repaint: controller); + PimpPainter( + {required this.particle, + required this.seed, + required this.controller, + required this.shouldPaint}) + : super(repaint: controller); final Particle particle; final int seed; @@ -380,7 +472,8 @@ class Sprinkles extends Particle { return AnimatedPositionedParticle( begin: const Offset(0.0, -10.0), end: const Offset(0.0, -60.0), - child: FadingRect(width: 5.0, height: 15.0, color: randomColor(n)), + child: + FadingRect(width: 5.0, height: 15.0, color: randomColor(n)), ); }, initialDistance: -pi / randomMirrorOffset), diff --git a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_attachment_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_attachment_tile.dart index c62cc45..98dfd4d 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_attachment_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_attachment_tile.dart @@ -11,7 +11,7 @@ import 'package:flutter/material.dart'; import 'homework_attachment_tile.i18n.dart'; class HomeworkAttachmentTile extends StatelessWidget { - const HomeworkAttachmentTile(this.attachment, {Key? key}) : super(key: key); + const HomeworkAttachmentTile(this.attachment, {super.key}); final HomeworkAttachment attachment; @@ -31,13 +31,13 @@ class HomeworkAttachmentTile extends StatelessWidget { builder: (context) => ImageView(snapshot.data!), )); }, + borderRadius: BorderRadius.circular(12.0), child: Ink.image( image: FileImage(File(snapshot.data ?? "")), height: 200.0, width: double.infinity, fit: BoxFit.cover, ), - borderRadius: BorderRadius.circular(12.0), ), ), ), diff --git a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_tile.dart index 6b1ad2e..dd8050a 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_tile.dart @@ -9,8 +9,7 @@ import 'package:provider/provider.dart'; class HomeworkTile extends StatelessWidget { const HomeworkTile(this.homework, - {Key? key, this.onTap, this.padding, this.censored = false}) - : super(key: key); + {super.key, this.onTap, this.padding, this.censored = false}); final Homework homework; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart index a473cff..20cc9df 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_view.dart @@ -12,7 +12,7 @@ import 'package:provider/provider.dart'; import 'homework_view.i18n.dart'; class HomeworkView extends StatelessWidget { - const HomeworkView(this.homework, {Key? key}) : super(key: key); + const HomeworkView(this.homework, {super.key}); final Homework homework; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_viewable.dart index 29c69fa..b00223b 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/homework/homework_viewable.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_view.dart'; import 'package:flutter/material.dart'; class HomeworkViewable extends StatelessWidget { - const HomeworkViewable(this.homework, {Key? key}) : super(key: key); + const HomeworkViewable(this.homework, {super.key}); final Homework homework; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_tile.dart index 4b2b485..de5fffb 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_tile.dart @@ -1,13 +1,14 @@ +import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo_kreta_api/models/lesson.dart'; import 'package:flutter/material.dart'; import 'package:filcnaplo/utils/format.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; +import 'package:provider/provider.dart'; import 'changed_lesson_tile.i18n.dart'; class ChangedLessonTile extends StatelessWidget { - const ChangedLessonTile(this.lesson, {Key? key, this.onTap, this.padding}) - : super(key: key); + const ChangedLessonTile(this.lesson, {super.key, this.onTap, this.padding}); final Lesson lesson; final void Function()? onTap; @@ -15,6 +16,8 @@ class ChangedLessonTile extends StatelessWidget { @override Widget build(BuildContext context) { + SettingsProvider settingsProvider = Provider.of(context); + String lessonIndexTrailing = ""; // Only put a trailing . if its a digit @@ -57,7 +60,8 @@ class ChangedLessonTile extends StatelessWidget { ), ), title: Text( - lesson.status?.name == "Elmaradt" && lesson.substituteTeacher?.name != "" + lesson.status?.name == "Elmaradt" && + lesson.substituteTeacher?.name != "" ? "cancelled".i18n : "substituted".i18n, maxLines: 2, @@ -70,7 +74,10 @@ class ChangedLessonTile extends StatelessWidget { overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: FontWeight.w500, - fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null), + fontStyle: lesson.subject.isRenamed && + settingsProvider.renamedSubjectsItalics + ? FontStyle.italic + : null), ), trailing: const Icon(FeatherIcons.arrowRight), minLeadingWidth: 0, diff --git a/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_viewable.dart index 65e7457..2b697c1 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/lesson/changed_lesson_viewable.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart'; import 'package:flutter/material.dart'; class ChangedLessonViewable extends StatelessWidget { - const ChangedLessonViewable(this.lesson, {Key? key}) : super(key: key); + const ChangedLessonViewable(this.lesson, {super.key}); final Lesson lesson; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart index fe5bf88..32f8bec 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_view.dart @@ -9,7 +9,7 @@ import 'package:provider/provider.dart'; import 'lesson_view.i18n.dart'; class LessonView extends StatelessWidget { - const LessonView(this.lesson, {Key? key}) : super(key: key); + const LessonView(this.lesson, {super.key}); final Lesson lesson; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart index d721d70..2a26ded 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart @@ -6,7 +6,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/lesson/lesson_view.dart'; import 'package:flutter/material.dart'; class LessonViewable extends StatelessWidget { - const LessonViewable(this.lesson, {Key? key, this.swapDesc = false}) : super(key: key); + const LessonViewable(this.lesson, {super.key, this.swapDesc = false}); final Lesson lesson; final bool swapDesc; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/message/attachment_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/message/attachment_tile.dart index 8ef597d..b8fb9e3 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/message/attachment_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/message/attachment_tile.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class AttachmentTile extends StatelessWidget { - const AttachmentTile(this.attachment, {Key? key}) : super(key: key); + const AttachmentTile(this.attachment, {super.key}); final Attachment attachment; @@ -32,13 +32,13 @@ class AttachmentTile extends StatelessWidget { }, ); }, + borderRadius: BorderRadius.circular(12.0), child: Ink.image( image: FileImage(File(snapshot.data ?? "")), height: 200.0, width: double.infinity, fit: BoxFit.cover, ), - borderRadius: BorderRadius.circular(12.0), ), ), ), diff --git a/filcnaplo_mobile_ui/lib/common/widgets/message/image_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/message/image_view.dart index 50f34db..2a40ceb 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/message/image_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/message/image_view.dart @@ -7,7 +7,7 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:photo_view/photo_view.dart'; class ImageView extends StatelessWidget { - const ImageView(this.path, {Key? key}) : super(key: key); + const ImageView(this.path, {super.key}); final String path; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/message/message_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/message/message_view.dart index bd192ff..af1fd5f 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/message/message_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/message/message_view.dart @@ -5,7 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class MessageView extends StatefulWidget { - const MessageView(this.messages, {Key? key}) : super(key: key); + const MessageView(this.messages, {super.key}); final List messages; @@ -13,10 +13,10 @@ class MessageView extends StatefulWidget { Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => MessageView(messages))); @override - _MessageViewState createState() => _MessageViewState(); + MessageViewState createState() => MessageViewState(); } -class _MessageViewState extends State { +class MessageViewState extends State { @override Widget build(BuildContext context) { return Scaffold( diff --git a/filcnaplo_mobile_ui/lib/common/widgets/message/message_view_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/message/message_view_tile.dart index 42cfd29..535f772 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/message/message_view_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/message/message_view_tile.dart @@ -13,7 +13,7 @@ import 'package:provider/provider.dart'; import 'message_view_tile.i18n.dart'; class MessageViewTile extends StatelessWidget { - const MessageViewTile(this.message, {Key? key}) : super(key: key); + const MessageViewTile(this.message, {super.key}); final Message message; @@ -76,7 +76,7 @@ class MessageViewTile extends StatelessWidget { maxLines: 2, ), subtitle: Text( - "to".i18n + " " + recipientLabel, + "${"to".i18n} $recipientLabel", style: const TextStyle(fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis, maxLines: 1, diff --git a/filcnaplo_mobile_ui/lib/common/widgets/message/message_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/message/message_viewable.dart index 410288d..b40f806 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/message/message_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/message/message_viewable.dart @@ -5,7 +5,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/message/message_view.dart'; import 'package:flutter/material.dart'; class MessageViewable extends StatelessWidget { - const MessageViewable(this.message, {Key? key}) : super(key: key); + const MessageViewable(this.message, {super.key}); final Message message; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/miss_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/miss_tile.dart index 6c453de..6114004 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/miss_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/miss_tile.dart @@ -5,7 +5,7 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'miss_tile.i18n.dart'; class MissTile extends StatelessWidget { - const MissTile(this.note, {Key? key}) : super(key: key); + const MissTile(this.note, {super.key}); final Note note; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_tile.dart index a6e149f..91ab61c 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_tile.dart @@ -6,7 +6,7 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'missed_exam_tile.i18n.dart'; class MissedExamTile extends StatelessWidget { - const MissedExamTile(this.missedExams, {Key? key, this.onTap, this.padding}) : super(key: key); + const MissedExamTile(this.missedExams, {super.key, this.onTap, this.padding}); final List missedExams; final Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_view.dart index 6178cc7..da64303 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_view.dart @@ -11,7 +11,7 @@ import 'package:provider/provider.dart'; import 'missed_exam_tile.i18n.dart'; class MissedExamView extends StatelessWidget { - const MissedExamView(this.missedExams, {Key? key}) : super(key: key); + const MissedExamView(this.missedExams, {super.key}); final List missedExams; @@ -26,8 +26,7 @@ class MissedExamView extends StatelessWidget { } class MissedExamViewTile extends StatelessWidget { - const MissedExamViewTile(this.lesson, {Key? key, this.padding}) - : super(key: key); + const MissedExamViewTile(this.lesson, {super.key, this.padding}); final EdgeInsetsGeometry? padding; final Lesson lesson; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_viewable.dart index d060621..a60bb3a 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/missed_exam/missed_exam_viewable.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/missed_exam/missed_exam_view. import 'package:flutter/material.dart'; class MissedExamViewable extends StatelessWidget { - const MissedExamViewable(this.missedExams, {Key? key}) : super(key: key); + const MissedExamViewable(this.missedExams, {super.key}); final List missedExams; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart index c3761c9..276d7c8 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/note/note_tile.dart @@ -4,8 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; import 'package:flutter/material.dart'; class NoteTile extends StatelessWidget { - const NoteTile(this.note, {Key? key, this.onTap, this.padding}) - : super(key: key); + const NoteTile(this.note, {super.key, this.onTap, this.padding}); final Note note; final void Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart index 64680af..2f02f6c 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/note/note_view.dart @@ -8,7 +8,7 @@ import 'package:flutter_custom_tabs/flutter_custom_tabs.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; class NoteView extends StatelessWidget { - const NoteView(this.note, {Key? key}) : super(key: key); + const NoteView(this.note, {super.key}); final Note note; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/note/note_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/note/note_viewable.dart index 8208845..9d2e886 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/note/note_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/note/note_viewable.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/note/note_view.dart'; import 'package:flutter/material.dart'; class NoteViewable extends StatelessWidget { - const NoteViewable(this.note, {Key? key}) : super(key: key); + const NoteViewable(this.note, {super.key}); final Note note; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/statistics_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/statistics_tile.dart index 9d01db3..f128168 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/statistics_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/statistics_tile.dart @@ -7,7 +7,7 @@ import 'package:provider/provider.dart'; class StatisticsTile extends StatelessWidget { const StatisticsTile({ - Key? key, + super.key, required this.value, this.title, this.decimal = true, @@ -15,7 +15,7 @@ class StatisticsTile extends StatelessWidget { this.valueSuffix = '', this.fill = false, this.outline = false, - }) : super(key: key); + }); final double value; final Widget? title; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/update/update_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/update/update_tile.dart index 41c66e4..9482ef0 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/update/update_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/update/update_tile.dart @@ -5,7 +5,7 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'update_tile.i18n.dart'; class UpdateTile extends StatelessWidget { - const UpdateTile(this.release, {Key? key, this.onTap, this.padding}) : super(key: key); + const UpdateTile(this.release, {super.key, this.onTap, this.padding}); final Release release; final Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/update/update_viewable.dart b/filcnaplo_mobile_ui/lib/common/widgets/update/update_viewable.dart index 0eb6a1c..119d031 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/update/update_viewable.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/update/update_viewable.dart @@ -4,7 +4,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart'; import 'package:flutter/material.dart'; class UpdateViewable extends StatelessWidget { - const UpdateViewable(this.release, {Key? key}) : super(key: key); + const UpdateViewable(this.release, {super.key}); final Release release; diff --git a/filcnaplo_mobile_ui/lib/common/widgets/update/updates_view.dart b/filcnaplo_mobile_ui/lib/common/widgets/update/updates_view.dart index 7463ce5..ff94a78 100755 --- a/filcnaplo_mobile_ui/lib/common/widgets/update/updates_view.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/update/updates_view.dart @@ -14,7 +14,7 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'updates_view.i18n.dart'; class UpdateView extends StatefulWidget { - const UpdateView(this.release, {Key? key}) : super(key: key); + const UpdateView(this.release, {super.key}); final Release release; @@ -22,10 +22,10 @@ class UpdateView extends StatefulWidget { showBottomCard(context: context, child: UpdateView(release)); @override - _UpdateViewState createState() => _UpdateViewState(); + UpdateViewState createState() => UpdateViewState(); } -class _UpdateViewState extends State { +class UpdateViewState extends State { double progress = 0.0; UpdateState state = UpdateState.none; @@ -90,6 +90,9 @@ class _UpdateViewState extends State { // Download button Center( child: MaterialActionButton( + backgroundColor: AppColors.of(context).filc, + onPressed: + state == UpdateState.none ? () => downloadPrecheck() : null, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -114,9 +117,6 @@ class _UpdateViewState extends State { .toUpperCase()), ], ), - backgroundColor: AppColors.of(context).filc, - onPressed: - state == UpdateState.none ? () => downloadPrecheck() : null, ), ), ], diff --git a/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart b/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart index c0a0a95..6e3addf 100755 --- a/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart +++ b/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view.dart @@ -19,8 +19,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.i18n.dar import 'package:provider/provider.dart'; class AbsenceSubjectView extends StatelessWidget { - const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) - : super(key: key); + const AbsenceSubjectView(this.subject, {super.key, this.absences = const []}); final GradeSubject subject; final List absences; diff --git a/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view_container.dart b/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view_container.dart index a0f1b87..b738ae1 100755 --- a/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view_container.dart +++ b/filcnaplo_mobile_ui/lib/pages/absences/absence_subject_view_container.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; class AbsenceSubjectViewContainer extends InheritedWidget { - const AbsenceSubjectViewContainer({Key? key, required Widget child}) : super(key: key, child: child); + const AbsenceSubjectViewContainer({super.key, required super.child}); - static AbsenceSubjectViewContainer? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType(); + static AbsenceSubjectViewContainer? of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType(); @override bool updateShouldNotify(AbsenceSubjectViewContainer oldWidget) => false; diff --git a/filcnaplo_mobile_ui/lib/pages/absences/absences_page.dart b/filcnaplo_mobile_ui/lib/pages/absences/absences_page.dart index 43e6518..7d3120a 100755 --- a/filcnaplo_mobile_ui/lib/pages/absences/absences_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/absences/absences_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'dart:math'; import 'package:animations/animations.dart'; @@ -42,13 +44,13 @@ class SubjectAbsence { } class AbsencesPage extends StatefulWidget { - const AbsencesPage({Key? key}) : super(key: key); + const AbsencesPage({super.key}); @override - _AbsencesPageState createState() => _AbsencesPageState(); + AbsencesPageState createState() => AbsencesPageState(); } -class _AbsencesPageState extends State +class AbsencesPageState extends State with TickerProviderStateMixin { late UserProvider user; late AbsenceProvider absenceProvider; @@ -308,10 +310,10 @@ class _AbsencesPageState extends State Animation secondaryAnimation, ) { return FadeThroughTransition( - child: child, animation: primaryAnimation, secondaryAnimation: secondaryAnimation, fillColor: Theme.of(context).colorScheme.background, + child: child, ); }, child: Column( @@ -359,7 +361,7 @@ class _AbsencesPageState extends State .length; title1 = "stat_1".i18n; title2 = "stat_2".i18n; - suffix = " " + "hr".i18n; + suffix = " ${"hr".i18n}"; } else if (activeData == AbsenceFilter.delays.index) { value1 = absenceProvider.absences .where((e) => @@ -373,7 +375,7 @@ class _AbsencesPageState extends State .fold(0, (a, b) => a + b); title1 = "stat_3".i18n; title2 = "stat_4".i18n; - suffix = " " + "min".i18n; + suffix = " ${"min".i18n}"; } return Padding( diff --git a/filcnaplo_premium/lib/ui/mobile/grades/average_selector.dart b/filcnaplo_mobile_ui/lib/pages/grades/average_selector.dart similarity index 78% rename from filcnaplo_premium/lib/ui/mobile/grades/average_selector.dart rename to filcnaplo_mobile_ui/lib/pages/grades/average_selector.dart index 134e70b..95d850f 100644 --- a/filcnaplo_premium/lib/ui/mobile/grades/average_selector.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/average_selector.dart @@ -16,17 +16,17 @@ final Map avgDropItems = { 7: "7_days_average", }; -class PremiumAverageSelector extends StatefulWidget { - const PremiumAverageSelector({Key? key, this.onChanged, required this.value}) : super(key: key); +class AverageSelector extends StatefulWidget { + const AverageSelector({super.key, this.onChanged, required this.value}); final Function(int?)? onChanged; final int value; @override - _PremiumAverageSelectorState createState() => _PremiumAverageSelectorState(); + AverageSelectorState createState() => AverageSelectorState(); } -class _PremiumAverageSelectorState extends State { +class AverageSelectorState extends State { @override Widget build(BuildContext context) { List> dropdownItems = avgDropItems.keys.map((item) { @@ -47,14 +47,16 @@ class _PremiumAverageSelectorState extends State { return DropdownButton2( items: dropdownItems, onChanged: (int? value) { - if (Provider.of(context, listen: false).hasScope(PremiumScopes.gradeStats)) { + if (Provider.of(context, listen: false) + .hasScope(PremiumScopes.gradeStats)) { if (widget.onChanged != null) { setState(() { widget.onChanged!(value); }); } } else { - PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.gradestats); + PremiumLockedFeatureUpsell.show( + context: context, feature: PremiumFeature.gradestats); } }, value: widget.value, @@ -84,10 +86,9 @@ class _PremiumAverageSelectorState extends State { children: [ Text( avgDropItems[widget.value]!.i18n, - style: Theme.of(context) - .textTheme - .titleSmall! - .copyWith(fontWeight: FontWeight.w600, color: AppColors.of(context).text.withOpacity(0.65)), + style: Theme.of(context).textTheme.titleSmall!.copyWith( + fontWeight: FontWeight.w600, + color: AppColors.of(context).text.withOpacity(0.65)), ), const SizedBox( width: 4, diff --git a/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart b/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart index 5e531b5..0271db3 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator.dart @@ -14,15 +14,15 @@ import 'package:provider/provider.dart'; import 'grade_calculator.i18n.dart'; class GradeCalculator extends StatefulWidget { - const GradeCalculator(this.subject, {Key? key}) : super(key: key); + const GradeCalculator(this.subject, {super.key}); - final GradeSubject subject; + final GradeSubject? subject; @override - _GradeCalculatorState createState() => _GradeCalculatorState(); + GradeCalculatorState createState() => GradeCalculatorState(); } -class _GradeCalculatorState extends State { +class GradeCalculatorState extends State { late GradeCalculatorProvider calculatorProvider; final _weightController = TextEditingController(text: "100"); @@ -142,7 +142,8 @@ class _GradeCalculatorState extends State { List grades = calculatorProvider.grades .where((e) => e.type == GradeType.midYear && - e.subject == widget.subject) + (e.subject == widget.subject || + widget.subject == null)) .toList(); grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate)); date = grades.first.date; @@ -158,7 +159,12 @@ class _GradeCalculatorState extends State { teacher: Teacher.fromString("Ghost"), type: GradeType.ghost, form: "", - subject: widget.subject, + subject: widget.subject ?? + GradeSubject( + id: randomId(), + category: Category(id: randomId()), + name: 'All', + ), mode: Category.fromJson({}), seenDate: DateTime(0), groupId: "", diff --git a/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator_provider.dart b/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator_provider.dart index 00383e8..eb5a0fd 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator_provider.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/calculator/grade_calculator_provider.dart @@ -1,24 +1,18 @@ -import 'package:filcnaplo/api/providers/database_provider.dart'; -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_kreta_api/client/client.dart'; +// import 'package:filcnaplo/api/providers/database_provider.dart'; +// import 'package:filcnaplo/api/providers/user_provider.dart'; +// import 'package:filcnaplo/models/settings.dart'; +// import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; class GradeCalculatorProvider extends GradeProvider { GradeCalculatorProvider({ - List initialGrades = const [], - required SettingsProvider settings, - required UserProvider user, - required DatabaseProvider database, - required KretaClient kreta, - }) : super( - initialGrades: initialGrades, - settings: settings, - database: database, - kreta: kreta, - user: user, - ); + super.initialGrades, + required super.settings, + required super.user, + required super.database, + required super.kreta, + }); List _grades = []; List _ghosts = []; diff --git a/filcnaplo_mobile_ui/lib/pages/grades/fail_warning.dart b/filcnaplo_mobile_ui/lib/pages/grades/fail_warning.dart index 14b55cf..abed478 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/fail_warning.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/fail_warning.dart @@ -5,13 +5,14 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'grades_page.i18n.dart'; class FailWarning extends StatelessWidget { - const FailWarning({Key? key, required this.subjectAvgs}) : super(key: key); + const FailWarning({super.key, required this.subjectAvgs}); final Map subjectAvgs; @override Widget build(BuildContext context) { - final failingSubjectCount = subjectAvgs.values.where((avg) => avg < 2.0).length; + final failingSubjectCount = + subjectAvgs.values.where((avg) => avg < 2.0).length; if (failingSubjectCount == 0) { return const SizedBox(); diff --git a/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart b/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart index efc4cf4..b3e93f5 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/grade_subject_view.dart @@ -39,8 +39,7 @@ import 'grades_page.i18n.dart'; // import 'package:filcnaplo_premium/ui/mobile/goal_planner/new_goal.dart'; class GradeSubjectView extends StatefulWidget { - const GradeSubjectView(this.subject, {Key? key, this.groupAverage = 0.0}) - : super(key: key); + const GradeSubjectView(this.subject, {super.key, this.groupAverage = 0.0}); final GradeSubject subject; final double groupAverage; @@ -110,6 +109,7 @@ class _GradeSubjectViewState extends State { ), )); + // ignore: no_leading_underscores_for_local_identifiers List _gradeTiles = []; if (!gradeCalcMode) { @@ -138,8 +138,8 @@ class _GradeSubjectViewState extends State { animation: primaryAnimation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.vertical, - child: child, fillColor: Colors.transparent, + child: child, ); }, child: _gradeTiles.isNotEmpty @@ -230,24 +230,20 @@ class _GradeSubjectViewState extends State { .where((e) => e.type == GradeType.midYear) .isNotEmpty, child: ExpandableFab( - backgroundColor: Theme.of(context).colorScheme.secondary, type: ExpandableFabType.up, distance: 50, - closeButtonStyle: ExpandableFabCloseButtonStyle( - backgroundColor: Theme.of(context).colorScheme.secondary, - ), + childrenOffset: const Offset(-3.8, 0.0), children: [ FloatingActionButton.small( heroTag: "btn_ghost_grades", - child: const Icon(FeatherIcons.plus), backgroundColor: Theme.of(context).colorScheme.secondary, onPressed: () { gradeCalc(context); }, + child: const Icon(FeatherIcons.plus), ), FloatingActionButton.small( heroTag: "btn_goal_planner", - child: const Icon(FeatherIcons.flag, size: 20.0), backgroundColor: Theme.of(context).colorScheme.secondary, onPressed: () { if (!Provider.of(context, listen: false) @@ -264,6 +260,7 @@ class _GradeSubjectViewState extends State { builder: (context) => GoalPlannerScreen(subject: widget.subject))); }, + child: const Icon(FeatherIcons.flag, size: 20.0), ), ], ), @@ -348,7 +345,7 @@ class _GradeSubjectViewState extends State { _sheetController = _scaffoldKey.currentState?.showBottomSheet( (context) => RoundedBottomSheet( - child: GradeCalculator(widget.subject), borderRadius: 14.0), + borderRadius: 14.0, child: GradeCalculator(widget.subject)), backgroundColor: const Color(0x00000000), elevation: 12.0, ); diff --git a/filcnaplo_mobile_ui/lib/pages/grades/grades_count.dart b/filcnaplo_mobile_ui/lib/pages/grades/grades_count.dart index f2a2e70..835119b 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/grades_count.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/grades_count.dart @@ -4,19 +4,24 @@ import 'package:filcnaplo_mobile_ui/pages/grades/grades_count_item.dart'; import 'package:collection/collection.dart'; class GradesCount extends StatelessWidget { - const GradesCount({Key? key, required this.grades}) : super(key: key); + const GradesCount({super.key, required this.grades}); final List grades; @override Widget build(BuildContext context) { - List gradesCount = List.generate(5, (int index) => grades.where((e) => e.value.value == index + 1).length); + List gradesCount = List.generate(5, + (int index) => grades.where((e) => e.value.value == index + 1).length); return Padding( - padding: const EdgeInsets.only(bottom: 6.0, top: 6.0, left: 12.0, right: 6.0), + padding: + const EdgeInsets.only(bottom: 6.0, top: 6.0, left: 12.0, right: 6.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - children: gradesCount.mapIndexed((index, e) => GradesCountItem(count: e, value: index + 1)).toList(), + children: gradesCount + .mapIndexed( + (index, e) => GradesCountItem(count: e, value: index + 1)) + .toList(), ), ); } diff --git a/filcnaplo_mobile_ui/lib/pages/grades/grades_count_item.dart b/filcnaplo_mobile_ui/lib/pages/grades/grades_count_item.dart index 9713e95..c5b1e3e 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/grades_count_item.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/grades_count_item.dart @@ -3,7 +3,7 @@ import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:flutter/material.dart'; class GradesCountItem extends StatelessWidget { - const GradesCountItem({Key? key, required this.count, required this.value}) : super(key: key); + const GradesCountItem({super.key, required this.count, required this.value}); final int count; final int value; @@ -26,7 +26,8 @@ class GradesCountItem extends StatelessWidget { style: const TextStyle(fontSize: 15.0), ), const SizedBox(width: 5.0), - GradeValueWidget(GradeValue(value, "Value", "Value", 100), size: 19.0, fill: true, shadow: false), + GradeValueWidget(GradeValue(value, "Value", "Value", 100), + size: 19.0, fill: true, shadow: false), ], ); } diff --git a/filcnaplo_mobile_ui/lib/pages/grades/grades_page.dart b/filcnaplo_mobile_ui/lib/pages/grades/grades_page.dart index 48bd153..b75f383 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/grades_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/grades_page.dart @@ -1,7 +1,10 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:filcnaplo/api/providers/update_provider.dart'; +import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart'; // import 'package:filcnaplo_kreta_api/client/api.dart'; // import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; @@ -11,6 +14,7 @@ import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/models/subject.dart'; import 'package:filcnaplo_kreta_api/models/group_average.dart'; import 'package:filcnaplo_mobile_ui/common/average_display.dart'; +import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:filcnaplo_mobile_ui/common/empty.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_button.dart'; @@ -24,23 +28,31 @@ import 'package:filcnaplo_mobile_ui/pages/grades/graph.dart'; import 'package:filcnaplo_mobile_ui/pages/grades/grade_subject_view.dart'; import 'package:filcnaplo_premium/providers/premium_provider.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; import 'package:filcnaplo/helpers/average_helper.dart'; -import 'package:filcnaplo_premium/ui/mobile/grades/average_selector.dart'; +import 'average_selector.dart'; import 'package:filcnaplo_premium/ui/mobile/premium/premium_inline.dart'; +import 'calculator/grade_calculator.dart'; +import 'calculator/grade_calculator_provider.dart'; import 'grades_page.i18n.dart'; class GradesPage extends StatefulWidget { - const GradesPage({Key? key}) : super(key: key); + const GradesPage({super.key}); @override - _GradesPageState createState() => _GradesPageState(); + GradesPageState createState() => GradesPageState(); } -class _GradesPageState extends State { +class GradesPageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + + PersistentBottomSheetController? _sheetController; + late UserProvider user; late GradeProvider gradeProvider; late UpdateProvider updateProvider; + late GradeCalculatorProvider calculatorProvider; late String firstName; late Widget yearlyGraph; late Widget gradesCount; @@ -48,15 +60,26 @@ class _GradesPageState extends State { int avgDropValue = 0; - List getSubjectGrades(GradeSubject subject, {int days = 0}) => - gradeProvider.grades - .where((e) => - e.subject == subject && - e.type == GradeType.midYear && - (days == 0 || - e.date - .isBefore(DateTime.now().subtract(Duration(days: days))))) - .toList(); + bool gradeCalcMode = false; + + List getSubjectGrades(GradeSubject subject, + {int days = 0}) => + !gradeCalcMode + ? gradeProvider + .grades + .where((e) => + e + .subject == + subject && + e.type == GradeType.midYear && + (days == + 0 || + e.date.isBefore( + DateTime.now().subtract(Duration(days: days))))) + .toList() + : calculatorProvider.grades + .where((e) => e.subject == subject) + .toList(); void generateTiles() { List subjects = gradeProvider.grades @@ -68,50 +91,86 @@ class _GradesPageState extends State { Map subjectAvgs = {}; - tiles.addAll(subjects.map((subject) { - List subjectGrades = getSubjectGrades(subject); + if (!gradeCalcMode) { + tiles.addAll(subjects.map((subject) { + List subjectGrades = getSubjectGrades(subject); - double avg = AverageHelper.averageEvals(subjectGrades); - double averageBefore = 0.0; + double avg = AverageHelper.averageEvals(subjectGrades); + double averageBefore = 0.0; - if (avgDropValue != 0) { - List gradesBefore = - getSubjectGrades(subject, days: avgDropValue); - averageBefore = - avgDropValue == 0 ? 0.0 : AverageHelper.averageEvals(gradesBefore); + if (avgDropValue != 0) { + List gradesBefore = + getSubjectGrades(subject, days: avgDropValue); + averageBefore = avgDropValue == 0 + ? 0.0 + : AverageHelper.averageEvals(gradesBefore); + } + var nullavg = GroupAverage(average: 0.0, subject: subject, uid: "0"); + double groupAverage = gradeProvider.groupAverages + .firstWhere((e) => e.subject == subject, orElse: () => nullavg) + .average; + + if (avg != 0) subjectAvgs[subject] = avg; + + return GradeSubjectTile( + subject, + averageBefore: averageBefore, + average: avg, + groupAverage: avgDropValue == 0 ? groupAverage : 0.0, + onTap: () { + GradeSubjectView(subject, groupAverage: groupAverage) + .push(context, root: true); + }, + ); + })); + } else { + tiles.clear(); + + List ghostGrades = calculatorProvider.ghosts; + ghostGrades.sort((a, b) => -a.date.compareTo(b.date)); + + List _gradeTiles = []; + for (Grade grade in ghostGrades) { + _gradeTiles.add(GradeTile( + grade, + viewOverride: true, + )); } - var nullavg = GroupAverage(average: 0.0, subject: subject, uid: "0"); - double groupAverage = gradeProvider.groupAverages - .firstWhere((e) => e.subject == subject, orElse: () => nullavg) - .average; - if (avg != 0) subjectAvgs[subject] = avg; - - return GradeSubjectTile( - subject, - averageBefore: averageBefore, - average: avg, - groupAverage: avgDropValue == 0 ? groupAverage : 0.0, - onTap: () { - GradeSubjectView(subject, groupAverage: groupAverage) - .push(context, root: true); - }, + tiles.add( + _gradeTiles.isNotEmpty + ? Panel( + key: ValueKey(gradeCalcMode), + title: Text( + "Ghost Grades".i18n, + ), + child: Column( + children: _gradeTiles, + ), + ) + : const SizedBox(), ); - })); + } - if (tiles.isNotEmpty) { + if (tiles.isNotEmpty || gradeCalcMode) { tiles.insert(0, yearlyGraph); tiles.insert(1, gradesCount); - tiles.insert(2, FailWarning(subjectAvgs: subjectAvgs)); - tiles.insert( + if (!gradeCalcMode) { + tiles.insert(2, FailWarning(subjectAvgs: subjectAvgs)); + tiles.insert( 3, PanelTitle( - title: Text(avgDropValue == 0 - ? "Subjects".i18n - : "Subjects_changes".i18n))); - tiles.insert(4, const PanelHeader(padding: EdgeInsets.only(top: 12.0))); - tiles.add(const PanelFooter(padding: EdgeInsets.only(bottom: 12.0))); - tiles.add(const Padding(padding: EdgeInsets.only(bottom: 24.0))); + title: Text( + avgDropValue == 0 ? "Subjects".i18n : "Subjects_changes".i18n, + )), + ); + + tiles.insert(4, const PanelHeader(padding: EdgeInsets.only(top: 12.0))); + tiles.add(const PanelFooter(padding: EdgeInsets.only(bottom: 12.0))); + } + tiles.add(Padding( + padding: EdgeInsets.only(bottom: !gradeCalcMode ? 24.0 : 250.0), + )); } else { tiles.insert( 0, @@ -145,7 +204,7 @@ class _GradesPageState extends State { gradeProvider.groupAverages.length : 0.0; - if (subjectAvg > 0) { + if (subjectAvg > 0 && !gradeCalcMode) { tiles.add(Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -200,6 +259,7 @@ class _GradesPageState extends State { user = Provider.of(context); gradeProvider = Provider.of(context); updateProvider = Provider.of(context); + calculatorProvider = Provider.of(context); context.watch(); List nameParts = user.displayName?.split(" ") ?? ["?"]; @@ -218,21 +278,31 @@ class _GradesPageState extends State { .date : DateTime.now(); - final currentStudentAvg = AverageHelper.averageEvals(gradeProvider.grades - .where((e) => e.type == GradeType.midYear) - .toList()); + final currentStudentAvg = AverageHelper.averageEvals(!gradeCalcMode + ? gradeProvider.grades + .where((e) => e.type == GradeType.midYear) + .toList() + : calculatorProvider.grades); + final prevStudentAvg = AverageHelper.averageEvals(gradeProvider.grades .where((e) => e.type == GradeType.midYear) .where((e) => e.date.isBefore(now.subtract(const Duration(days: 30)))) .toList()); - List graphGrades = gradeProvider.grades - .where((e) => - e.type == GradeType.midYear && - (avgDropValue == 0 || + List graphGrades = !gradeCalcMode + ? gradeProvider.grades + .where((e) => + e.type == GradeType.midYear && + (avgDropValue == 0 || + e.date.isAfter( + DateTime.now().subtract(Duration(days: avgDropValue))))) + .toList() + : calculatorProvider.grades + .where(((e) => + avgDropValue == 0 || e.date.isAfter( DateTime.now().subtract(Duration(days: avgDropValue))))) - .toList(); + .toList(); yearlyGraph = Padding( padding: const EdgeInsets.only(top: 12.0, bottom: 8.0), @@ -240,7 +310,7 @@ class _GradesPageState extends State { title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - PremiumAverageSelector( + AverageSelector( value: avgDropValue, onChanged: (value) { setState(() { @@ -278,6 +348,7 @@ class _GradesPageState extends State { generateTiles(); return Scaffold( + key: _scaffoldKey, body: Padding( padding: const EdgeInsets.only(top: 9.0), child: NestedScrollView( @@ -291,7 +362,24 @@ class _GradesPageState extends State { snap: false, surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, actions: [ - // Profile Icon + if (!gradeCalcMode) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 5.0, vertical: 0.0), + child: IconButton( + splashRadius: 24.0, + onPressed: () { + // SoonAlert.show(context: context); + gradeCalcTotal(context); + }, + icon: Icon( + FeatherIcons.plus, + color: AppColors.of(context).text, + ), + ), + ), + + // profile Icon Padding( padding: const EdgeInsets.only(right: 24.0), child: ProfileButton( @@ -338,8 +426,8 @@ class _GradesPageState extends State { return Padding( padding: panelPadding, child: PanelBody( - child: subjectTiles[index], padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: subjectTiles[index], )); } else { return Padding( @@ -355,4 +443,30 @@ class _GradesPageState extends State { ), ); } + + void gradeCalcTotal(BuildContext context) { + calculatorProvider.clear(); + calculatorProvider.addAllGrades(gradeProvider.grades); + + _sheetController = _scaffoldKey.currentState?.showBottomSheet( + (context) => const RoundedBottomSheet( + borderRadius: 14.0, child: GradeCalculator(null)), + backgroundColor: const Color(0x00000000), + elevation: 12.0, + ); + + // Hide the fab and grades + setState(() { + gradeCalcMode = true; + }); + + _sheetController!.closed.then((value) { + // Show fab and grades + if (mounted) { + setState(() { + gradeCalcMode = false; + }); + } + }); + } } diff --git a/filcnaplo_mobile_ui/lib/pages/grades/graph.dart b/filcnaplo_mobile_ui/lib/pages/grades/graph.dart index 5e0baa9..f594693 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/graph.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/graph.dart @@ -12,17 +12,18 @@ import 'package:provider/provider.dart'; import 'graph.i18n.dart'; class GradeGraph extends StatefulWidget { - const GradeGraph(this.data, {Key? key, this.dayThreshold = 7, this.classAvg}) : super(key: key); + const GradeGraph(this.data, + {super.key, this.dayThreshold = 7, this.classAvg}); final List data; final int dayThreshold; final double? classAvg; @override - _GradeGraphState createState() => _GradeGraphState(); + GradeGraphState createState() => GradeGraphState(); } -class _GradeGraphState extends State { +class GradeGraphState extends State { late SettingsProvider settings; List getSpots(List data) { @@ -34,7 +35,9 @@ class _GradeGraphState extends State { // Sort data to points by treshold for (var element in data) { - if (sortedData.last.isNotEmpty && sortedData.last.last.writeDate.difference(element.writeDate).inDays > widget.dayThreshold) { + if (sortedData.last.isNotEmpty && + sortedData.last.last.writeDate.difference(element.writeDate).inDays > + widget.dayThreshold) { sortedData.add([]); } for (var dataList in sortedData) { @@ -48,7 +51,9 @@ class _GradeGraphState extends State { if (dataList.isNotEmpty) { subjectData.add(FlSpot( - dataList[0].writeDate.month + (dataList[0].writeDate.day / 31) + ((dataList[0].writeDate.year - data.last.writeDate.year) * 12), + dataList[0].writeDate.month + + (dataList[0].writeDate.day / 31) + + ((dataList[0].writeDate.year - data.last.writeDate.year) * 12), double.parse(average.toStringAsFixed(2)), )); } @@ -74,14 +79,19 @@ class _GradeGraphState extends State { .toList(); // Filter ghost data - List ghostData = widget.data.where((e) => e.value.weight != 0).where((e) => e.type == GradeType.ghost).toList(); + List ghostData = widget.data + .where((e) => e.value.weight != 0) + .where((e) => e.type == GradeType.ghost) + .toList(); // Calculate average double average = AverageHelper.averageEvals(data); // Calculate graph color Color averageColor = average >= 1 && average <= 5 - ? ColorTween(begin: settings.gradeColors[average.floor() - 1], end: settings.gradeColors[average.ceil() - 1]) + ? ColorTween( + begin: settings.gradeColors[average.floor() - 1], + end: settings.gradeColors[average.ceil() - 1]) .transform(average - average.floor())! : Theme.of(context).colorScheme.secondary; @@ -92,17 +102,26 @@ class _GradeGraphState extends State { ghostSpots = getSpots(data + ghostData); // hax - ghostSpots = ghostSpots.where((e) => e.x >= subjectSpots.map((f) => f.x).reduce(max)).toList(); + ghostSpots = ghostSpots + .where((e) => e.x >= subjectSpots.map((f) => f.x).reduce(max)) + .toList(); ghostSpots = ghostSpots.map((e) => FlSpot(e.x + 0.1, e.y)).toList(); - ghostSpots.add(subjectSpots.firstWhere((e) => e.x >= subjectSpots.map((f) => f.x).reduce(max), orElse: () => const FlSpot(-1, -1))); - ghostSpots.removeWhere((element) => element.x == -1 && element.y == -1); // naplo/#74 + ghostSpots.add(subjectSpots.firstWhere( + (e) => e.x >= subjectSpots.map((f) => f.x).reduce(max), + orElse: () => const FlSpot(-1, -1))); + ghostSpots.removeWhere( + (element) => element.x == -1 && element.y == -1); // naplo/#74 } - Grade halfYearGrade = widget.data.lastWhere((e) => e.type == GradeType.halfYear, orElse: () => Grade.fromJson({})); + Grade halfYearGrade = widget.data.lastWhere( + (e) => e.type == GradeType.halfYear, + orElse: () => Grade.fromJson({})); if (halfYearGrade.date.year != 0 && data.isNotEmpty) { final maxX = ghostSpots.isNotEmpty ? ghostSpots.first.x : 0; - final x = halfYearGrade.writeDate.month + (halfYearGrade.writeDate.day / 31) + ((halfYearGrade.writeDate.year - data.last.writeDate.year) * 12); + final x = halfYearGrade.writeDate.month + + (halfYearGrade.writeDate.day / 31) + + ((halfYearGrade.writeDate.year - data.last.writeDate.year) * 12); if (x <= maxX) { extraLinesV.add( VerticalLine( @@ -110,7 +129,7 @@ class _GradeGraphState extends State { strokeWidth: 3.0, color: AppColors.of(context).red.withOpacity(.75), label: VerticalLineLabel( - labelResolver: (_) => " " + "mid".i18n + " ​", // <- zwsp for padding + labelResolver: (_) => " ${"mid".i18n} ​", // <- zwsp for padding show: true, alignment: Alignment.topLeft, style: TextStyle( @@ -126,7 +145,9 @@ class _GradeGraphState extends State { } // Horizontal line displaying the class average - if (widget.classAvg != null && widget.classAvg! > 0.0 && settings.graphClassAvg) { + if (widget.classAvg != null && + widget.classAvg! > 0.0 && + settings.graphClassAvg) { extraLinesH.add(HorizontalLine( y: widget.classAvg!, color: AppColors.of(context).text.withOpacity(.75), @@ -147,12 +168,15 @@ class _GradeGraphState extends State { ) : ClipRect( child: SizedBox( + height: 158, child: subjectSpots.length > 1 ? Padding( padding: const EdgeInsets.only(top: 8.0, right: 8.0), child: LineChart( LineChartData( - extraLinesData: ExtraLinesData(verticalLines: extraLinesV, horizontalLines: extraLinesH), + extraLinesData: ExtraLinesData( + verticalLines: extraLinesV, + horizontalLines: extraLinesH), lineBarsData: [ LineChartBarData( preventCurveOverShooting: true, @@ -230,7 +254,8 @@ class _GradeGraphState extends State { strokeWidth: 3.5, ), FlDotData( - getDotPainter: (a, b, c, d) => FlDotCirclePainter( + getDotPainter: (a, b, c, d) => + FlDotCirclePainter( strokeWidth: 0, color: Colors.grey.shade900, radius: 10.0, @@ -252,25 +277,45 @@ class _GradeGraphState extends State { showTitles: true, reservedSize: 24, getTextStyles: (context, value) => TextStyle( - color: AppColors.of(context).text.withOpacity(.75), + color: + AppColors.of(context).text.withOpacity(.75), fontWeight: FontWeight.bold, fontSize: 14.0, ), margin: 12.0, getTitles: (value) { - var format = DateFormat("MMM", I18n.of(context).locale.toString()); + var format = DateFormat( + "MMM", I18n.of(context).locale.toString()); - String title = format.format(DateTime(0, value.floor() % 12)).replaceAll(".", ""); - title = title.substring(0, min(title.length, 4)); + String title = format + .format(DateTime(0, value.floor() % 12)) + .replaceAll(".", ""); + title = + title.substring(0, min(title.length, 4)); return title.toUpperCase(); }, interval: () { - List tData = ghostData.isNotEmpty ? ghostData : data; - tData.sort((a, b) => a.writeDate.compareTo(b.writeDate)); - return tData.first.writeDate.add(const Duration(days: 120)).isBefore(tData.last.writeDate) ? 2.0 : 1.0; + List tData = + ghostData.isNotEmpty ? ghostData : data; + tData.sort((a, b) => + a.writeDate.compareTo(b.writeDate)); + return tData.first.writeDate + .add(const Duration(days: 120)) + .isBefore(tData.last.writeDate) + ? 2.0 + : 1.0; }(), - checkToShowTitle: (double minValue, double maxValue, SideTitles sideTitles, double appliedInterval, double value) { if (value == maxValue || value == minValue) return false; return true; }, + checkToShowTitle: (double minValue, + double maxValue, + SideTitles sideTitles, + double appliedInterval, + double value) { + if (value == maxValue || value == minValue) { + return false; + } + return true; + }, ), leftTitles: SideTitles( showTitles: true, @@ -289,7 +334,6 @@ class _GradeGraphState extends State { ), ) : null, - height: 158, ), ); } diff --git a/filcnaplo_mobile_ui/lib/pages/grades/subject_grades_container.dart b/filcnaplo_mobile_ui/lib/pages/grades/subject_grades_container.dart index 58b2bc0..11e8e8f 100755 --- a/filcnaplo_mobile_ui/lib/pages/grades/subject_grades_container.dart +++ b/filcnaplo_mobile_ui/lib/pages/grades/subject_grades_container.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; class SubjectGradesContainer extends InheritedWidget { - const SubjectGradesContainer({Key? key, required Widget child}) : super(key: key, child: child); + const SubjectGradesContainer({super.key, required super.child}); - static SubjectGradesContainer? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType(); + static SubjectGradesContainer? of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType(); @override bool updateShouldNotify(SubjectGradesContainer oldWidget) => false; diff --git a/filcnaplo_mobile_ui/lib/pages/home/home_page.dart b/filcnaplo_mobile_ui/lib/pages/home/home_page.dart index 00c9e9f..fb0a37d 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/home_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/home_page.dart @@ -31,15 +31,16 @@ import 'home_page.i18n.dart'; import 'package:filcnaplo/ui/filter/widgets.dart'; import 'package:filcnaplo/ui/filter/sort.dart'; import 'package:i18n_extension/i18n_extension.dart'; +// import 'package:dropdown_button2/dropdown_button2.dart'; class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); + const HomePage({super.key}); @override - _HomePageState createState() => _HomePageState(); + HomePageState createState() => HomePageState(); } -class _HomePageState extends State with TickerProviderStateMixin { +class HomePageState extends State with TickerProviderStateMixin { late TabController _tabController; late UserProvider user; late SettingsProvider settings; @@ -63,7 +64,7 @@ class _HomePageState extends State with TickerProviderStateMixin { late String firstName; late List listOrder; - static const pageCount = 4; + static const pageCount = 5; @override void initState() { @@ -174,6 +175,19 @@ class _HomePageState extends State with TickerProviderStateMixin { _liveCardAnimation.animateTo(_liveCard.show ? 1.0 : 0.0); setGreeting(); + //for extra filters + + // final List items = [ + // 'Item1', + // 'Item2', + // 'Item3', + // 'Item4', + // 'Item5', + // 'Item6', + // 'Item7', + // 'Item8', + // ]; + // String? selectedValue; return Scaffold( body: Stack( @@ -252,12 +266,12 @@ class _HomePageState extends State with TickerProviderStateMixin { ), ), shadowColor: Colors.black, - // Filter Bar bottom: FilterBar( items: [ Tab(text: "All".i18n), Tab(text: "Grades".i18n), + Tab(text: "Exams".i18n), Tab(text: "Messages".i18n), Tab(text: "Absences".i18n), ], @@ -351,7 +365,7 @@ class _HomePageState extends State with TickerProviderStateMixin { : Container(), ); }, - childCount: 4, + childCount: 5, findChildIndexCallback: (Key key) { final ValueKey valueKey = key as ValueKey; diff --git a/filcnaplo_mobile_ui/lib/pages/home/home_page.i18n.dart b/filcnaplo_mobile_ui/lib/pages/home/home_page.i18n.dart index 9cd9ea6..a5e09b4 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/home_page.i18n.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/home_page.i18n.dart @@ -15,6 +15,7 @@ extension Localization on String { "empty": "Nothing to see here.", "All": "All", "Grades": "Grades", + "Exams": "Exams", "Messages": "Messages", "Absences": "Absences", "update_available": "Update Available", @@ -34,6 +35,7 @@ extension Localization on String { "empty": "Nincs itt semmi látnivaló.", "All": "Összes", "Grades": "Jegyek", + "Exams": "Számonkérések", "Messages": "Üzenetek", "Absences": "Hiányok", "update_available": "Frissítés elérhető", @@ -53,6 +55,7 @@ extension Localization on String { "empty": "Hier gibt es nichts zu sehen.", "All": "Alles", "Grades": "Noten", + "Exams": "Aufsätze", "Messages": "Nachrichten", "Absences": "Fehlen", "update_available": "Update verfügbar", diff --git a/filcnaplo_mobile_ui/lib/pages/home/live_card/heads_up_countdown.dart b/filcnaplo_mobile_ui/lib/pages/home/live_card/heads_up_countdown.dart index 03e5381..fd69ccf 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/live_card/heads_up_countdown.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/live_card/heads_up_countdown.dart @@ -5,7 +5,8 @@ import 'package:flutter/material.dart'; import 'package:lottie/lottie.dart'; class HeadsUpCountdown extends StatefulWidget { - const HeadsUpCountdown({Key? key, required this.maxTime, required this.elapsedTime}) : super(key: key); + const HeadsUpCountdown( + {super.key, required this.maxTime, required this.elapsedTime}); final double maxTime; final double elapsedTime; @@ -92,7 +93,8 @@ class _HeadsUpCountdownState extends State { AnimatedOpacity( opacity: dur.inSeconds > 0 ? 0.0 : 1.0, duration: const Duration(milliseconds: 500), - child: Lottie.asset("assets/animations/bell-alert.json", width: 400), + child: Lottie.asset("assets/animations/bell-alert.json", + width: 400), ), ], ), diff --git a/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card.dart b/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card.dart index 2973c4c..078b583 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card.dart @@ -16,13 +16,13 @@ import 'package:provider/provider.dart'; import 'live_card.i18n.dart'; class LiveCard extends StatefulWidget { - const LiveCard({Key? key}) : super(key: key); + const LiveCard({super.key}); @override - _LiveCardState createState() => _LiveCardState(); + LiveCardStateA createState() => LiveCardStateA(); } -class _LiveCardState extends State { +class LiveCardStateA extends State { late void Function() listener; late UserProvider _userProvider; late LiveCardProvider liveCard; @@ -285,8 +285,8 @@ class _LiveCardState extends State { animation: primaryAnimation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.horizontal, - child: child, fillColor: Theme.of(context).scaffoldBackgroundColor, + child: child, ); }, child: child, diff --git a/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card_widget.dart b/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card_widget.dart index 2498a22..6b439b1 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card_widget.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/live_card/live_card_widget.dart @@ -10,7 +10,7 @@ enum ProgressAccuracy { minutes, seconds } class LiveCardWidget extends StatefulWidget { const LiveCardWidget({ - Key? key, + super.key, this.isEvent = false, this.leading, this.title, @@ -26,7 +26,7 @@ class LiveCardWidget extends StatefulWidget { this.progressAccuracy = ProgressAccuracy.minutes, this.onProgressTap, this.onTap, - }) : super(key: key); + }); final bool isEvent; final String? leading; @@ -321,7 +321,7 @@ class _LiveCardWidgetState extends State { widget.nextRoom!, style: TextStyle( height: 1.1, - fontSize: 11.0, + fontSize: 12.0, fontWeight: FontWeight.w600, color: Theme.of(context) .colorScheme diff --git a/filcnaplo_mobile_ui/lib/pages/messages/messages_page.dart b/filcnaplo_mobile_ui/lib/pages/messages/messages_page.dart index 1a8334b..62a5777 100755 --- a/filcnaplo_mobile_ui/lib/pages/messages/messages_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/messages/messages_page.dart @@ -6,27 +6,31 @@ import 'package:filcnaplo_kreta_api/providers/message_provider.dart'; import 'package:filcnaplo/api/providers/user_provider.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo_kreta_api/models/message.dart'; +import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:filcnaplo_mobile_ui/common/empty.dart'; import 'package:filcnaplo_mobile_ui/common/filter_bar.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_button.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; import 'package:filcnaplo/ui/filter/sort.dart'; -import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.dart'; +// import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/message/message_viewable.dart'; import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; import 'messages_page.i18n.dart'; +import 'send_message/send_message.dart'; class MessagesPage extends StatefulWidget { - const MessagesPage({Key? key}) : super(key: key); + const MessagesPage({super.key}); @override - _MessagesPageState createState() => _MessagesPageState(); + MessagesPageState createState() => MessagesPageState(); } -class _MessagesPageState extends State +class MessagesPageState extends State with TickerProviderStateMixin { + final GlobalKey _scaffoldKey = GlobalKey(); + late UserProvider user; late MessageProvider messageProvider; late UpdateProvider updateProvider; @@ -50,6 +54,7 @@ class _MessagesPageState extends State firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0]; return Scaffold( + key: _scaffoldKey, body: Padding( padding: const EdgeInsets.only(top: 12.0), child: NestedScrollView( @@ -68,7 +73,7 @@ class _MessagesPageState extends State horizontal: 8.0, vertical: 5.0), child: IconButton( splashRadius: 24.0, - onPressed: () { + onPressed: () async { // Navigator.of(context, rootNavigator: true) // .push(PageRouteBuilder( // pageBuilder: (context, animation, secondaryAnimation) => @@ -81,7 +86,8 @@ class _MessagesPageState extends State // [DeviceOrientation.portraitUp]); // setSystemChrome(context); // }); - SoonAlert.show(context: context); + // SoonAlert.show(context: context); + await showSendMessageSheet(context); }, icon: Icon( FeatherIcons.send, @@ -219,4 +225,16 @@ class _MessagesPageState extends State ), ); } + + Future showSendMessageSheet(BuildContext context) async { + await messageProvider.fetchAllRecipients(); + + _scaffoldKey.currentState?.showBottomSheet( + (context) => RoundedBottomSheet( + borderRadius: 14.0, + child: SendMessageSheet(messageProvider.recipients)), + backgroundColor: const Color(0x00000000), + elevation: 12.0, + ); + } } diff --git a/filcnaplo_mobile_ui/lib/pages/messages/send_message/send_message.dart b/filcnaplo_mobile_ui/lib/pages/messages/send_message/send_message.dart new file mode 100644 index 0000000..6e28737 --- /dev/null +++ b/filcnaplo_mobile_ui/lib/pages/messages/send_message/send_message.dart @@ -0,0 +1,221 @@ +// ignore_for_file: use_build_context_synchronously + +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:filcnaplo/theme/colors/colors.dart'; +import 'package:filcnaplo_kreta_api/models/message.dart'; +import 'package:filcnaplo_kreta_api/providers/message_provider.dart'; +import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart'; +// import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart'; +import 'package:filcnaplo_mobile_ui/common/material_action_button.dart'; +import 'package:filcnaplo_mobile_ui/pages/messages/send_message/send_message.i18n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; + +class SendMessageSheet extends StatefulWidget { + const SendMessageSheet(this.availableRecipients, {super.key}); + + final List availableRecipients; + + @override + SendMessageSheetState createState() => SendMessageSheetState(); +} + +class SendMessageSheetState extends State { + late MessageProvider messageProvider; + + final _subjectController = TextEditingController(); + final _messageController = TextEditingController(); + + double newValue = 5.0; + double newWeight = 100.0; + + List selectedRecipients = []; + + @override + Widget build(BuildContext context) { + messageProvider = Provider.of(context); + + return Container( + width: double.infinity, + padding: const EdgeInsets.all(6.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + "send_message".i18n, + style: + const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600), + ), + ), + + // message recipients + Padding( + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + child: DropdownButton2( + items: widget.availableRecipients + .map((item) => DropdownMenuItem( + value: item.kretaId.toString(), + child: Text( + "${item.name ?? (item.id ?? 'Nincs név').toString()}${item.type.code != 'TANAR' ? " (${item.type.shortName})" : ''}", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: AppColors.of(context).text, + ), + overflow: TextOverflow.ellipsis, + ), + )) + .toList(), + onChanged: (String? v) async { + int kretaId = int.parse(v ?? '0'); + + setState(() { + selectedRecipients.add(widget.availableRecipients + .firstWhere((e) => e.kretaId == kretaId)); + + widget.availableRecipients + .removeWhere((e) => e.kretaId == kretaId); + }); + }, + iconSize: 14, + iconEnabledColor: AppColors.of(context).text, + iconDisabledColor: AppColors.of(context).text, + underline: const SizedBox(), + itemHeight: 40, + itemPadding: const EdgeInsets.only(left: 14, right: 14), + buttonWidth: 50, + dropdownWidth: 300, + dropdownPadding: null, + buttonDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + ), + dropdownDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + ), + dropdownElevation: 8, + scrollbarRadius: const Radius.circular(40), + scrollbarThickness: 6, + scrollbarAlwaysShow: true, + offset: const Offset(-10, -10), + buttonSplashColor: Colors.transparent, + customButton: Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey, width: 2), + borderRadius: BorderRadius.circular(12.0), + ), + padding: + const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0), + child: Text( + selectedRecipients.isEmpty + ? "select_recipient".i18n + : selectedRecipients + .map((e) => + '${e.name ?? (e.id ?? 'Nincs név').toString()}, ') + .join(), + style: Theme.of(context).textTheme.titleSmall!.copyWith( + fontWeight: FontWeight.w700, + color: AppColors.of(context).text.withOpacity(0.75)), + overflow: TextOverflow.ellipsis, + maxLines: 2, + textAlign: TextAlign.center, + ), + ), + ), + ), + // Row(children: buildRecipientTiles()), + + // message content + Column(children: [ + Container( + // width: 180.0, + padding: const EdgeInsets.only(right: 12.0, left: 12.0), + child: Center( + child: TextField( + controller: _subjectController, + style: const TextStyle( + fontWeight: FontWeight.w600, fontSize: 18.0), + autocorrect: true, + textAlign: TextAlign.left, + keyboardType: TextInputType.text, + inputFormatters: [ + LengthLimitingTextInputFormatter(100), + ], + decoration: InputDecoration( + // border: InputBorder.none, + // enabledBorder: InputBorder.none, + // focusedBorder: InputBorder.none, + hintText: "message_subject".i18n, + suffixStyle: const TextStyle(fontSize: 16.0), + ), + ), + ), + ), + Container( + height: 200, + padding: const EdgeInsets.only(right: 12.0, left: 12.0), + child: TextField( + controller: _messageController, + style: const TextStyle( + fontWeight: FontWeight.w500, fontSize: 16.0), + autocorrect: true, + textAlign: TextAlign.left, + keyboardType: TextInputType.multiline, + maxLines: 10, + minLines: 1, + inputFormatters: [ + LengthLimitingTextInputFormatter(500), + ], + decoration: InputDecoration( + // border: InputBorder.none, + // enabledBorder: InputBorder.none, + // focusedBorder: InputBorder.none, + hintText: "message_text".i18n, + suffixStyle: const TextStyle(fontSize: 14.0), + ), + ), + ), + ]), + Container( + width: 120.0, + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: MaterialActionButton( + child: Text("send".i18n), + onPressed: () async { + if (_messageController.text.replaceAll(' ', '') == '') { + return; + } + + String subjectText = + _subjectController.text.replaceAll(' ', '') != '' + ? _subjectController.text + : 'Nincs tárgy'; + + var res = await messageProvider.sendMessage( + recipients: selectedRecipients, + subject: subjectText, + messageText: _messageController.text, + ); + + // do after send + if (res == 'send_permission_error') { + ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( + content: Text('cant_send'.i18n), context: context)); + } + + if (res == 'successfully_sent') { + ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( + content: Text('sent'.i18n), context: context)); + } + + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + ); + } +} diff --git a/filcnaplo_mobile_ui/lib/pages/messages/send_message/send_message.i18n.dart b/filcnaplo_mobile_ui/lib/pages/messages/send_message/send_message.i18n.dart new file mode 100644 index 0000000..c78c64b --- /dev/null +++ b/filcnaplo_mobile_ui/lib/pages/messages/send_message/send_message.i18n.dart @@ -0,0 +1,42 @@ +import 'package:i18n_extension/i18n_extension.dart'; + +extension Localization on String { + static final _t = Translations.byLocale("hu_hu") + + { + "en_en": { + "recipients": "Recipients", + "send_message": "Send Message", + "send": "Send", + "sent": "Message sent successfully.", + "message_subject": "Subject...", + "message_text": "Message text...", + "select_recipient": "Add Recipient", + "cant_send": "You can't send a message to one of the recipients!", + }, + "hu_hu": { + "recipients": "Címzettek", + "send_message": "Üzenetküldés", + "send": "Küldés", + "sent": "Sikeres üzenetküldés.", + "message_subject": "Tárgy...", + "message_text": "Üzenet szövege...", + "select_recipient": "Címzett hozzáadása", + "cant_send": "Az egyik címzettnek nem küldhetsz üzenetet!", + }, + "de_de": { + "recipients": "Empfänger", + "send_message": "Nachricht senden", + "send": "Versenden", + "sent": "Nachricht erfolgreich gesendet.", + "message_subject": "Betreff...", + "message_text": "Nachrichtentext...", + "select_recipient": "Empfänger hinzufügen", + "cant_send": "Neki nem küldhetsz üzenetet!", + }, + }; + + String get i18n => localize(this, _t); + String fill(List params) => localizeFill(this, params); + String plural(int value) => localizePlural(value, this, _t); + String version(Object modifier) => localizeVersion(modifier, this, _t); +} diff --git a/filcnaplo_mobile_ui/lib/pages/timetable/day_title.dart b/filcnaplo_mobile_ui/lib/pages/timetable/day_title.dart index e3d4cd6..ce62972 100755 --- a/filcnaplo_mobile_ui/lib/pages/timetable/day_title.dart +++ b/filcnaplo_mobile_ui/lib/pages/timetable/day_title.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:filcnaplo/utils/format.dart'; class DayTitle extends StatefulWidget { - const DayTitle({Key? key, required this.dayTitle, required this.controller}) : super(key: key); + const DayTitle({super.key, required this.dayTitle, required this.controller}); final String Function(int) dayTitle; final TabController controller; @@ -50,7 +50,11 @@ class _DayTitleState extends State { width: MediaQuery.of(context).size.width / 1.5, child: Text( widget.dayTitle(index).capital(), - style: TextStyle(color: AppColors.of(context).text.withOpacity(opacity), fontSize: 32.0, fontWeight: FontWeight.bold), + style: TextStyle( + color: + AppColors.of(context).text.withOpacity(opacity), + fontSize: 32.0, + fontWeight: FontWeight.bold), ), ); }, diff --git a/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart b/filcnaplo_mobile_ui/lib/pages/timetable/fs_timetable.dart similarity index 89% rename from filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart rename to filcnaplo_mobile_ui/lib/pages/timetable/fs_timetable.dart index 9d8aa6b..466f35c 100644 --- a/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart +++ b/filcnaplo_mobile_ui/lib/pages/timetable/fs_timetable.dart @@ -12,17 +12,16 @@ import 'package:intl/intl.dart'; import 'package:i18n_extension/i18n_widget.dart'; import 'package:provider/provider.dart'; -class PremiumFSTimetable extends StatefulWidget { - const PremiumFSTimetable({Key? key, required this.controller}) - : super(key: key); +class FSTimetable extends StatefulWidget { + const FSTimetable({super.key, required this.controller}); final TimetableController controller; @override - State createState() => _PremiumFSTimetableState(); + State createState() => _FSTimetableState(); } -class _PremiumFSTimetableState extends State { +class _FSTimetableState extends State { late SettingsProvider settings; @override @@ -81,7 +80,7 @@ class _PremiumFSTimetableState extends State { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text( - (index).toString()+".", + "$index.", style: TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.secondary), @@ -178,11 +177,11 @@ class _PremiumFSTimetableState extends State { lessons[lessonIndex].subject.name.capital(), maxLines: 1, style: TextStyle( - fontStyle: lessons[lessonIndex] - .subject - .isRenamed && settings.renamedSubjectsItalics - ? FontStyle.italic - : null, + fontStyle: + lessons[lessonIndex].subject.isRenamed && + settings.renamedSubjectsItalics + ? FontStyle.italic + : null, ), overflow: TextOverflow.clip, softWrap: false, diff --git a/filcnaplo_mobile_ui/lib/pages/timetable/timetable_page.dart b/filcnaplo_mobile_ui/lib/pages/timetable/timetable_page.dart index f447a73..a556498 100755 --- a/filcnaplo_mobile_ui/lib/pages/timetable/timetable_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/timetable/timetable_page.dart @@ -12,26 +12,27 @@ import 'package:filcnaplo_mobile_ui/common/empty.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_button.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; +import 'package:filcnaplo_mobile_ui/common/system_chrome.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/lesson/lesson_view.dart'; import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/lesson/lesson_viewable.dart'; import 'package:filcnaplo_mobile_ui/pages/timetable/day_title.dart'; +import 'package:filcnaplo_mobile_ui/pages/timetable/fs_timetable.dart'; import 'package:filcnaplo_mobile_ui/screens/navigation/navigation_route_handler.dart'; import 'package:filcnaplo_mobile_ui/screens/navigation/navigation_screen.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import 'package:i18n_extension/i18n_widget.dart'; -import 'package:filcnaplo_premium/ui/mobile/timetable/fs_timetable_button.dart'; import 'timetable_page.i18n.dart'; // todo: "fix" overflow (priority: -1) class TimetablePage extends StatefulWidget { - const TimetablePage({Key? key, this.initialDay, this.initialWeek}) - : super(key: key); + const TimetablePage({super.key, this.initialDay, this.initialWeek}); final DateTime? initialDay; final Week? initialWeek; @@ -56,10 +57,10 @@ class TimetablePage extends StatefulWidget { } @override - _TimetablePageState createState() => _TimetablePageState(); + TimetablePageState createState() => TimetablePageState(); } -class _TimetablePageState extends State +class TimetablePageState extends State with TickerProviderStateMixin, WidgetsBindingObserver { late UserProvider user; late TimetableProvider timetableProvider; @@ -199,7 +200,38 @@ class _TimetablePageState extends State snap: false, surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, actions: [ - PremiumFSTimetableButton(controller: _controller, tabcontroller: _tabController), + Padding( + padding: const EdgeInsets.all(8.0), + child: IconButton( + splashRadius: 24.0, + onPressed: () { + // If timetable empty, show empty + if (_tabController.length == 0) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("empty_timetable".i18n), + duration: const Duration(seconds: 2), + )); + return; + } + + Navigator.of(context, rootNavigator: true) + .push(PageRouteBuilder( + pageBuilder: + (context, animation, secondaryAnimation) => + FSTimetable( + controller: _controller, + ), + )) + .then((_) { + SystemChrome.setPreferredOrientations( + [DeviceOrientation.portraitUp]); + setSystemChrome(context); + }); + }, + icon: Icon(FeatherIcons.trello, + color: AppColors.of(context).text), + ), + ), // Profile Icon Padding( @@ -232,8 +264,8 @@ class _TimetablePageState extends State animation: primaryAnimation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.horizontal, - child: child, fillColor: Theme.of(context).scaffoldBackgroundColor, + child: child, ); }, layoutBuilder: (List entries) { @@ -286,6 +318,7 @@ class _TimetablePageState extends State ), shadowColor: Theme.of(context).shadowColor, bottom: PreferredSize( + preferredSize: const Size.fromHeight(50.0), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( @@ -321,29 +354,7 @@ class _TimetablePageState extends State child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - "${_controller.currentWeekId + 1}. " + - "week".i18n + - " (" + - // Week start - DateFormat( - (_controller.currentWeek.start.year != - DateTime.now().year - ? "yy. " - : "") + - "MMM d.", - I18n.of(context).locale.languageCode) - .format(_controller.currentWeek.start) + - " - " + - // Week end - DateFormat( - (_controller.currentWeek.start.year != - DateTime.now().year - ? "yy. " - : "") + - "MMM d.", - I18n.of(context).locale.languageCode) - .format(_controller.currentWeek.end) + - ")", + "${_controller.currentWeekId + 1}. ${"week".i18n} (${DateFormat("${_controller.currentWeek.start.year != DateTime.now().year ? "yy. " : ""}MMM d.", I18n.of(context).locale.languageCode).format(_controller.currentWeek.start)} - ${DateFormat("${_controller.currentWeek.start.year != DateTime.now().year ? "yy. " : ""}MMM d.", I18n.of(context).locale.languageCode).format(_controller.currentWeek.end)})", style: const TextStyle( fontWeight: FontWeight.w500, fontSize: 14.0, @@ -365,7 +376,6 @@ class _TimetablePageState extends State ], ), ), - preferredSize: const Size.fromHeight(50.0), ), ), ], @@ -376,10 +386,10 @@ class _TimetablePageState extends State Animation secondaryAnimation, ) { return FadeThroughTransition( - child: child, animation: primaryAnimation, secondaryAnimation: secondaryAnimation, fillColor: Theme.of(context).scaffoldBackgroundColor, + child: child, ); }, child: _controller.days != null diff --git a/filcnaplo_mobile_ui/lib/premium/components/active_sponsor_card.dart b/filcnaplo_mobile_ui/lib/premium/components/active_sponsor_card.dart index abbdee1..c047b08 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/active_sponsor_card.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/active_sponsor_card.dart @@ -12,23 +12,29 @@ class ActiveSponsorCard extends StatelessWidget { static PremiumFeatureLevel? estimateLevel(List scopes) { if (scopes.contains(PremiumScopes.all)) { - return PremiumFeatureLevel.tinta; + return PremiumFeatureLevel.ink; } - if (scopes.contains(PremiumScopes.timetableWidget) || scopes.contains(PremiumScopes.goalPlanner)) { - return PremiumFeatureLevel.tinta; + if (scopes.contains(PremiumScopes.timetableWidget) || + scopes.contains(PremiumScopes.goalPlanner)) { + return PremiumFeatureLevel.ink; } - if (scopes.contains(PremiumScopes.customColors) || scopes.contains(PremiumScopes.nickname)) { - return PremiumFeatureLevel.kupak; + if (scopes.contains(PremiumScopes.customColors) || + scopes.contains(PremiumScopes.nickname)) { + return PremiumFeatureLevel.cap; } return null; } IconData _levelIcon(PremiumFeatureLevel level) { switch (level) { - case PremiumFeatureLevel.kupak: + case PremiumFeatureLevel.cap: return FilcIcons.kupak; - case PremiumFeatureLevel.tinta: + case PremiumFeatureLevel.ink: return FilcIcons.tinta; + case PremiumFeatureLevel.old: + return FilcIcons.kupak; + case PremiumFeatureLevel.sponge: + return FilcIcons.kupak; } } @@ -44,12 +50,18 @@ class ActiveSponsorCard extends StatelessWidget { Color glow; switch (level) { - case PremiumFeatureLevel.kupak: + case PremiumFeatureLevel.cap: glow = Colors.lightGreen; break; - case PremiumFeatureLevel.tinta: + case PremiumFeatureLevel.ink: glow = Colors.purple; break; + case PremiumFeatureLevel.old: + glow = Colors.red; + break; + case PremiumFeatureLevel.sponge: + glow = Colors.red; + break; } return Container( @@ -66,12 +78,14 @@ class ActiveSponsorCard extends StatelessWidget { margin: EdgeInsets.zero, elevation: 0, color: const Color(0xff2B2B2B), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)), + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)), child: InkWell( borderRadius: BorderRadius.circular(14.0), splashColor: glow.withOpacity(.2), onTap: () { - Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) { + Navigator.of(context, rootNavigator: true) + .push(MaterialPageRoute(builder: (context) { return const PremiumScreen(); })); }, @@ -84,8 +98,10 @@ class ActiveSponsorCard extends StatelessWidget { child: Stack( children: [ CircleAvatar( - backgroundColor: Theme.of(context).colorScheme.secondary, - backgroundImage: NetworkImage("https://github.com/${premium.login}.png?size=128"), + backgroundColor: + Theme.of(context).colorScheme.secondary, + backgroundImage: NetworkImage( + "https://github.com/${premium.login}.png?size=128"), ), Positioned.fill( child: Align( @@ -122,7 +138,10 @@ class ActiveSponsorCard extends StatelessWidget { child: Text( premium.login, overflow: TextOverflow.ellipsis, - style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 20, color: Colors.white), + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 20, + color: Colors.white), ), ), Padding( diff --git a/filcnaplo_mobile_ui/lib/premium/components/avatar_stack.dart b/filcnaplo_mobile_ui/lib/premium/components/avatar_stack.dart index 53f4701..c6b8acf 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/avatar_stack.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/avatar_stack.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class AvatarStack extends StatelessWidget { - const AvatarStack({Key? key, required this.children}) : super(key: key); + const AvatarStack({super.key, required this.children}); final List children; diff --git a/filcnaplo_mobile_ui/lib/premium/components/github_connect_button.dart b/filcnaplo_mobile_ui/lib/premium/components/github_connect_button.dart index 23a467e..d01c271 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/github_connect_button.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/github_connect_button.dart @@ -7,7 +7,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; class GithubConnectButton extends StatelessWidget { - const GithubConnectButton({Key? key}) : super(key: key); + const GithubConnectButton({super.key}); @override Widget build(BuildContext context) { @@ -26,7 +26,10 @@ class GithubConnectButton extends StatelessWidget { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( "Prémium deaktiválva.", - style: TextStyle(color: AppColors.of(context).text, fontWeight: FontWeight.bold, fontSize: 18.0), + style: TextStyle( + color: AppColors.of(context).text, + fontWeight: FontWeight.bold, + fontSize: 18.0), ), backgroundColor: Theme.of(context).scaffoldBackgroundColor, )); @@ -74,7 +77,9 @@ class GithubConnectButton extends StatelessWidget { child: Transform.translate( offset: const Offset(2.0, 2.0), child: Icon( - premium.hasPremium ? FeatherIcons.minusCircle : FeatherIcons.plusCircle, + premium.hasPremium + ? FeatherIcons.minusCircle + : FeatherIcons.plusCircle, color: Colors.white, size: 16.0, ), @@ -85,8 +90,13 @@ class GithubConnectButton extends StatelessWidget { ), ), Text( - premium.hasPremium ? "GitHub szétkapcsolása" : "GitHub csatlakoztatása", - style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 20, color: Colors.white), + premium.hasPremium + ? "GitHub szétkapcsolása" + : "GitHub csatlakoztatása", + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 20, + color: Colors.white), ), ], ), diff --git a/filcnaplo_mobile_ui/lib/premium/components/goal_card.dart b/filcnaplo_mobile_ui/lib/premium/components/goal_card.dart index 3cac000..7eb8eb7 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/goal_card.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/goal_card.dart @@ -3,7 +3,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; class PremiumGoalCard extends StatelessWidget { - const PremiumGoalCard({Key? key, this.progress = 100, this.target = 1}) : super(key: key); + const PremiumGoalCard({super.key, this.progress = 100, this.target = 1}); final double progress; final double target; @@ -20,7 +20,8 @@ class PremiumGoalCard extends StatelessWidget { children: [ Text( "Cél: ${target.round()} támogató", - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18.0), + style: + const TextStyle(fontWeight: FontWeight.bold, fontSize: 18.0), ), const SizedBox(height: 8.0), Stack( @@ -43,7 +44,10 @@ class PremiumGoalCard extends StatelessWidget { height: 12, width: size.maxWidth * (progress / 100), decoration: BoxDecoration( - gradient: const LinearGradient(colors: [Color(0xFFFF2A9D), Color(0xFFFF37F7)]), + gradient: const LinearGradient(colors: [ + Color(0xFFFF2A9D), + Color(0xFFFF37F7) + ]), borderRadius: BorderRadius.circular(45.0), ), ), @@ -52,8 +56,10 @@ class PremiumGoalCard extends StatelessWidget { child: Stack( children: [ ImageFiltered( - imageFilter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), - child: Image.asset("assets/images/heart.png", color: Colors.black.withOpacity(.3)), + imageFilter: + ImageFilter.blur(sigmaX: 5, sigmaY: 5), + child: Image.asset("assets/images/heart.png", + color: Colors.black.withOpacity(.3)), ), Image.asset("assets/images/heart.png"), ], diff --git a/filcnaplo_mobile_ui/lib/premium/components/plan_card.dart b/filcnaplo_mobile_ui/lib/premium/components/plan_card.dart index d5ccf55..e2ef406 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/plan_card.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/plan_card.dart @@ -4,7 +4,7 @@ import 'package:url_launcher/url_launcher.dart'; class PremiumPlanCard extends StatelessWidget { const PremiumPlanCard({ - Key? key, + super.key, this.icon, this.title, this.description, @@ -12,7 +12,7 @@ class PremiumPlanCard extends StatelessWidget { this.url, this.gradient, this.active = false, - }) : super(key: key); + }); final Widget? icon; final Widget? title; @@ -28,7 +28,8 @@ class PremiumPlanCard extends StatelessWidget { margin: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), child: InkWell( - customBorder: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), + customBorder: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), onTap: () { if (url != null) { launchUrl( @@ -51,13 +52,20 @@ class PremiumPlanCard extends StatelessWidget { children: [ if (icon != null) ...[ IconTheme( - data: Theme.of(context).iconTheme.copyWith(size: 42.0), + data: Theme.of(context) + .iconTheme + .copyWith(size: 42.0), child: icon!, ), const SizedBox(height: 12.0), ], DefaultTextStyle( - style: Theme.of(context).textTheme.displaySmall!.copyWith(fontWeight: FontWeight.bold, fontSize: 25.0), + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontWeight: FontWeight.bold, + fontSize: 25.0), child: title!, ), ], @@ -78,7 +86,8 @@ class PremiumPlanCard extends StatelessWidget { borderRadius: BorderRadius.circular(99.0), ), margin: const EdgeInsets.all(4.0), - padding: const EdgeInsets.symmetric(horizontal: 12.0), + padding: + const EdgeInsets.symmetric(horizontal: 12.0), child: const Text( "Aktív", style: TextStyle( @@ -95,10 +104,16 @@ class PremiumPlanCard extends StatelessWidget { TextSpan(text: "\$$price"), TextSpan( text: " / hó", - style: TextStyle(color: Theme.of(context).textTheme.bodyMedium!.color!.withOpacity(.7)), + style: TextStyle( + color: Theme.of(context) + .textTheme + .bodyMedium! + .color! + .withOpacity(.7)), ), ]), - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24.0), + style: const TextStyle( + fontWeight: FontWeight.bold, fontSize: 24.0), ), ], ), @@ -108,13 +123,15 @@ class PremiumPlanCard extends StatelessWidget { children: [ if (icon != null) ...[ IconTheme( - data: Theme.of(context).iconTheme.copyWith(size: 24.0, color: AppColors.of(context).text), + data: Theme.of(context).iconTheme.copyWith( + size: 24.0, color: AppColors.of(context).text), child: icon!, ), ], const SizedBox(width: 12.0), DefaultTextStyle( - style: Theme.of(context).textTheme.displaySmall!.copyWith(fontWeight: FontWeight.bold, fontSize: 25.0), + style: Theme.of(context).textTheme.displaySmall!.copyWith( + fontWeight: FontWeight.bold, fontSize: 25.0), child: title!, ), ], @@ -123,10 +140,13 @@ class PremiumPlanCard extends StatelessWidget { const SizedBox(height: 6.0), if (description != null) DefaultTextStyle( - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Theme.of(context).textTheme.bodyMedium!.color!.withOpacity(.8), fontSize: 18), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context) + .textTheme + .bodyMedium! + .color! + .withOpacity(.8), + fontSize: 18), child: description!, ), ], diff --git a/filcnaplo_mobile_ui/lib/premium/components/reward_card.dart b/filcnaplo_mobile_ui/lib/premium/components/reward_card.dart index eeabb68..dc6eba7 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/reward_card.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/reward_card.dart @@ -1,7 +1,13 @@ import 'package:flutter/material.dart'; class PremiumRewardCard extends StatelessWidget { - const PremiumRewardCard({Key? key, this.imageKey, this.icon, this.title, this.description, this.soon = false}) : super(key: key); + const PremiumRewardCard( + {super.key, + this.imageKey, + this.icon, + this.title, + this.description, + this.soon = false}); final String? imageKey; final Widget? icon; @@ -24,12 +30,14 @@ class PremiumRewardCard extends StatelessWidget { labelPadding: EdgeInsets.zero, padding: EdgeInsets.symmetric(horizontal: 12.0), backgroundColor: Color(0x777645D3), - label: Text("Hamarosan", style: TextStyle(fontWeight: FontWeight.w500)), + label: Text("Hamarosan", + style: TextStyle(fontWeight: FontWeight.w500)), ), ), if (imageKey != null) Padding( - padding: const EdgeInsets.symmetric(horizontal: 14.0).add(EdgeInsets.only(bottom: 12.0, top: soon ? 0 : 14.0)), + padding: const EdgeInsets.symmetric(horizontal: 14.0) + .add(EdgeInsets.only(bottom: 12.0, top: soon ? 0 : 14.0)), child: Image.asset("assets/images/${imageKey!}.png"), ) else @@ -42,7 +50,10 @@ class PremiumRewardCard extends StatelessWidget { if (title != null) Expanded( child: DefaultTextStyle( - style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.w700, fontSize: 20), + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(fontWeight: FontWeight.w700, fontSize: 20), child: title!, ), ), @@ -51,9 +62,13 @@ class PremiumRewardCard extends StatelessWidget { ), if (description != null) Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0).add(const EdgeInsets.only(top: 4.0, bottom: 12.0)), + padding: const EdgeInsets.symmetric(horizontal: 12.0) + .add(const EdgeInsets.only(top: 4.0, bottom: 12.0)), child: DefaultTextStyle( - style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 16), + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(fontSize: 16), child: description!, ), ), diff --git a/filcnaplo_mobile_ui/lib/premium/components/supporter_chip.dart b/filcnaplo_mobile_ui/lib/premium/components/supporter_chip.dart index 188bdf0..37174d7 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/supporter_chip.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/supporter_chip.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/models/supporter.dart'; import 'package:flutter/material.dart'; class SupporterChip extends StatelessWidget { - const SupporterChip({Key? key, required this.supporter}) : super(key: key); + const SupporterChip({super.key, required this.supporter}); final Supporter supporter; diff --git a/filcnaplo_mobile_ui/lib/premium/components/supporter_group_card.dart b/filcnaplo_mobile_ui/lib/premium/components/supporter_group_card.dart index cdc8f67..831853a 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/supporter_group_card.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/supporter_group_card.dart @@ -5,13 +5,13 @@ import 'package:flutter/material.dart'; class SupporterGroupCard extends StatelessWidget { const SupporterGroupCard({ - Key? key, + super.key, this.title, this.icon, this.expanded = false, this.supporters = const [], this.glow, - }) : super(key: key); + }); final Widget? icon; final Widget? title; @@ -34,7 +34,8 @@ class SupporterGroupCard extends StatelessWidget { ), child: Card( margin: EdgeInsets.zero, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)), child: Padding( padding: const EdgeInsets.all(24.0), child: Column( @@ -46,7 +47,10 @@ class SupporterGroupCard extends StatelessWidget { if (title != null) Expanded( child: DefaultTextStyle( - style: Theme.of(context).textTheme.titleLarge!.copyWith(fontWeight: FontWeight.w700), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(fontWeight: FontWeight.w700), child: title!, ), ), @@ -55,12 +59,16 @@ class SupporterGroupCard extends StatelessWidget { const SizedBox(height: 12.0), if (expanded) Column( - children: supporters.map((e) => SupporterTile(supporter: e)).toList(), + children: supporters + .map((e) => SupporterTile(supporter: e)) + .toList(), ) else Wrap( spacing: 8.0, - children: supporters.map((e) => SupporterChip(supporter: e)).toList(), + children: supporters + .map((e) => SupporterChip(supporter: e)) + .toList(), ), ], ), diff --git a/filcnaplo_mobile_ui/lib/premium/components/supporter_tile.dart b/filcnaplo_mobile_ui/lib/premium/components/supporter_tile.dart index a783a4d..4e25450 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/supporter_tile.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/supporter_tile.dart @@ -2,7 +2,7 @@ import 'package:filcnaplo/models/supporter.dart'; import 'package:flutter/material.dart'; class SupporterTile extends StatelessWidget { - const SupporterTile({Key? key, required this.supporter}) : super(key: key); + const SupporterTile({super.key, required this.supporter}); final Supporter supporter; diff --git a/filcnaplo_mobile_ui/lib/premium/components/supporters_button.dart b/filcnaplo_mobile_ui/lib/premium/components/supporters_button.dart index 7d9fb6d..33848ea 100755 --- a/filcnaplo_mobile_ui/lib/premium/components/supporters_button.dart +++ b/filcnaplo_mobile_ui/lib/premium/components/supporters_button.dart @@ -6,7 +6,7 @@ import 'package:filcnaplo_mobile_ui/premium/supporters_screen.dart'; import 'package:flutter/material.dart'; class SupportersButton extends StatelessWidget { - const SupportersButton({Key? key, required this.supporters}) : super(key: key); + const SupportersButton({super.key, required this.supporters}); final Future supporters; @@ -19,7 +19,8 @@ class SupportersButton extends StatelessWidget { customBorder: const StadiumBorder(), onTap: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => SupportersScreen(supporters: supporters)), + MaterialPageRoute( + builder: (context) => SupportersScreen(supporters: supporters)), ); }, child: Container( @@ -38,24 +39,35 @@ class SupportersButton extends StatelessWidget { if (!snapshot.hasData) { return const SizedBox(); } - final sponsors = snapshot.data!.github.where((e) => e.type == DonationType.monthly).toList(); - sponsors.shuffle(Random((DateTime.now().millisecondsSinceEpoch / 1000 / 60 / 60 / 24).floor())); + final sponsors = snapshot.data!.github + .where((e) => e.type == DonationType.monthly) + .toList(); + sponsors.shuffle(Random( + (DateTime.now().millisecondsSinceEpoch / + 1000 / + 60 / + 60 / + 24) + .floor())); return AvatarStack( children: [ // ignore: prefer_is_empty if (sponsors.length > 0 && sponsors[0].avatar != "") CircleAvatar( - backgroundColor: Theme.of(context).colorScheme.secondary, + backgroundColor: + Theme.of(context).colorScheme.secondary, backgroundImage: NetworkImage(sponsors[0].avatar), ), if (sponsors.length > 1 && sponsors[1].avatar != "") CircleAvatar( - backgroundColor: Theme.of(context).colorScheme.secondary, + backgroundColor: + Theme.of(context).colorScheme.secondary, backgroundImage: NetworkImage(sponsors[1].avatar), ), if (sponsors.length > 2 && sponsors[2].avatar != "") CircleAvatar( - backgroundColor: Theme.of(context).colorScheme.secondary, + backgroundColor: + Theme.of(context).colorScheme.secondary, backgroundImage: NetworkImage(sponsors[2].avatar), ), ], diff --git a/filcnaplo_mobile_ui/lib/premium/premium_button.dart b/filcnaplo_mobile_ui/lib/premium/premium_button.dart index 0b328cb..88a0e61 100755 --- a/filcnaplo_mobile_ui/lib/premium/premium_button.dart +++ b/filcnaplo_mobile_ui/lib/premium/premium_button.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:animations/animations.dart'; class PremiumButton extends StatefulWidget { - const PremiumButton({Key? key}) : super(key: key); + const PremiumButton({super.key}); @override State createState() => _PremiumButtonState(); diff --git a/filcnaplo_mobile_ui/lib/premium/premium_screen.dart b/filcnaplo_mobile_ui/lib/premium/premium_screen.dart index c710a23..0e7d6ab 100755 --- a/filcnaplo_mobile_ui/lib/premium/premium_screen.dart +++ b/filcnaplo_mobile_ui/lib/premium/premium_screen.dart @@ -17,7 +17,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; class PremiumScreen extends StatelessWidget { - const PremiumScreen({Key? key}) : super(key: key); + const PremiumScreen({super.key}); @override Widget build(BuildContext context) { @@ -139,7 +139,7 @@ class PremiumScreen extends StatelessWidget { "https://github.com/sponsors/filc/sponsorships?tier_id=238453&preview=true", active: ActiveSponsorCard.estimateLevel( context.watch().scopes) == - PremiumFeatureLevel.kupak, + PremiumFeatureLevel.cap, ), const SizedBox(height: 8.0), PremiumPlanCard( @@ -155,7 +155,7 @@ class PremiumScreen extends StatelessWidget { "https://github.com/sponsors/filc/sponsorships?tier_id=238454&preview=true", active: ActiveSponsorCard.estimateLevel( context.watch().scopes) == - PremiumFeatureLevel.tinta, + PremiumFeatureLevel.ink, ), const SizedBox(height: 12.0), PremiumGoalCard( diff --git a/filcnaplo_mobile_ui/lib/premium/supporters_screen.dart b/filcnaplo_mobile_ui/lib/premium/supporters_screen.dart index 1f250ca..d7e7e36 100755 --- a/filcnaplo_mobile_ui/lib/premium/supporters_screen.dart +++ b/filcnaplo_mobile_ui/lib/premium/supporters_screen.dart @@ -6,7 +6,7 @@ import 'package:filcnaplo_mobile_ui/premium/styles/gradients.dart'; import 'package:flutter/material.dart'; class SupportersScreen extends StatelessWidget { - const SupportersScreen({Key? key, required this.supporters}) : super(key: key); + const SupportersScreen({super.key, required this.supporters}); final Future supporters; @@ -15,12 +15,28 @@ class SupportersScreen extends StatelessWidget { return FutureBuilder( future: supporters, builder: (context, snapshot) { - final highlightedSupporters = - snapshot.data?.github.where((e) => e.type == DonationType.monthly && e.price >= 5 && e.comment != "").toList() ?? []; - final tintaSupporters = - snapshot.data?.github.where((e) => e.type == DonationType.monthly && e.price >= 5 && e.comment == "").toList() ?? []; - final kupakSupporters = snapshot.data?.github.where((e) => e.type == DonationType.monthly && e.price == 2).toList() ?? []; - final onetimeSupporters = snapshot.data?.github.where((e) => e.type == DonationType.once && e.price >= 5).toList() ?? []; + final highlightedSupporters = snapshot.data?.github + .where((e) => + e.type == DonationType.monthly && + e.price >= 5 && + e.comment != "") + .toList() ?? + []; + final tintaSupporters = snapshot.data?.github + .where((e) => + e.type == DonationType.monthly && + e.price >= 5 && + e.comment == "") + .toList() ?? + []; + final kupakSupporters = snapshot.data?.github + .where((e) => e.type == DonationType.monthly && e.price == 2) + .toList() ?? + []; + final onetimeSupporters = snapshot.data?.github + .where((e) => e.type == DonationType.once && e.price >= 5) + .toList() ?? + []; final patreonSupporters = snapshot.data?.patreon ?? []; return Scaffold( @@ -35,11 +51,15 @@ class SupportersScreen extends StatelessWidget { ), if (snapshot.hasData) SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0).add(const EdgeInsets.only(bottom: 24.0)), + padding: const EdgeInsets.symmetric(horizontal: 16.0) + .add(const EdgeInsets.only(bottom: 24.0)), sliver: SliverToBoxAdapter( child: Text( snapshot.data!.description, - style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0, color: AppColors.of(context).text.withOpacity(.7)), + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 20.0, + color: AppColors.of(context).text.withOpacity(.7)), ), ), ), @@ -86,7 +106,8 @@ class SupportersScreen extends StatelessWidget { icon: const Icon(FilcIcons.kupak), title: Text( "Kupak", - style: TextStyle(foreground: GradientStyles.kupakPaint), + style: + TextStyle(foreground: GradientStyles.kupakPaint), ), glow: Colors.lightGreen, supporters: kupakSupporters, diff --git a/filcnaplo_mobile_ui/lib/screens/error_report_screen.dart b/filcnaplo_mobile_ui/lib/screens/error_report_screen.dart index d4f87ef..535fb93 100755 --- a/filcnaplo_mobile_ui/lib/screens/error_report_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/error_report_screen.dart @@ -9,7 +9,7 @@ import 'error_report_screen.i18n.dart'; class ErrorReportScreen extends StatelessWidget { final FlutterErrorDetails details; - const ErrorReportScreen(this.details, {Key? key}) : super(key: key); + const ErrorReportScreen(this.details, {super.key}); @override Widget build(BuildContext context) { @@ -21,8 +21,8 @@ class ErrorReportScreen extends StatelessWidget { child: Column( children: [ const Align( - child: BackButton(), alignment: Alignment.topLeft, + child: BackButton(), ), const Spacer(), const Icon( @@ -57,7 +57,9 @@ class ErrorReportScreen extends StatelessWidget { height: 110.0, width: double.infinity, padding: const EdgeInsets.all(12.0), - decoration: BoxDecoration(borderRadius: BorderRadius.circular(12.0), color: Colors.black.withOpacity(.2)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Colors.black.withOpacity(.2)), child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Text( @@ -69,7 +71,9 @@ class ErrorReportScreen extends StatelessWidget { IconButton( icon: const Icon(FeatherIcons.info), onPressed: () { - showDialog(context: context, builder: (context) => StacktracePopup(details)); + showDialog( + context: context, + builder: (context) => StacktracePopup(details)); }, ) ], @@ -79,10 +83,12 @@ class ErrorReportScreen extends StatelessWidget { width: double.infinity, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(const EdgeInsets.symmetric(vertical: 14.0)), + padding: MaterialStateProperty.all( + const EdgeInsets.symmetric(vertical: 14.0)), backgroundColor: MaterialStateProperty.all(Colors.white), shape: MaterialStateProperty.all( - RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0)), ), ), child: Text( @@ -106,7 +112,7 @@ class ErrorReportScreen extends StatelessWidget { Future reportProblem(BuildContext context) async { final report = ErrorReport( - os: Platform.operatingSystem + " " + Platform.operatingSystemVersion, + os: "${Platform.operatingSystem} ${Platform.operatingSystemVersion}", error: details.exceptionAsString(), version: const String.fromEnvironment("APPVER", defaultValue: "?"), stack: details.stack.toString(), @@ -119,7 +125,7 @@ class ErrorReportScreen extends StatelessWidget { class StacktracePopup extends StatelessWidget { final FlutterErrorDetails details; - const StacktracePopup(this.details, {Key? key}) : super(key: key); + const StacktracePopup(this.details, {super.key}); @override Widget build(BuildContext context) { @@ -150,13 +156,20 @@ class StacktracePopup extends StatelessWidget { "error".i18n, details.exceptionAsString(), ), - ErrorDetail("os".i18n, Platform.operatingSystem + " " + Platform.operatingSystemVersion), - ErrorDetail("version".i18n, const String.fromEnvironment("APPVER", defaultValue: "?")), - ErrorDetail("stack".i18n, stack.substring(0, min(stack.length, 5000))) + ErrorDetail("os".i18n, + "${Platform.operatingSystem} ${Platform.operatingSystemVersion}"), + ErrorDetail( + "version".i18n, + const String.fromEnvironment("APPVER", + defaultValue: "?")), + ErrorDetail( + "stack".i18n, stack.substring(0, min(stack.length, 5000))) ]), ), TextButton( - child: Text("done".i18n, style: TextStyle(color: Theme.of(context).colorScheme.secondary)), + child: Text("done".i18n, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary)), onPressed: () { Navigator.of(context).pop(); }) @@ -172,7 +185,7 @@ class ErrorDetail extends StatelessWidget { final String title; final String content; - const ErrorDetail(this.title, this.content, {Key? key}) : super(key: key); + const ErrorDetail(this.title, this.content, {super.key}); @override Widget build(BuildContext context) { @@ -186,13 +199,17 @@ class ErrorDetail extends StatelessWidget { style: const TextStyle(fontWeight: FontWeight.bold), ), Container( + padding: + const EdgeInsets.symmetric(horizontal: 6.5, vertical: 4.0), + margin: const EdgeInsets.only(top: 4.0), + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(4.0)), child: Text( content, - style: const TextStyle(fontFamily: 'SpaceMono', color: Colors.white), - ), - padding: const EdgeInsets.symmetric(horizontal: 6.5, vertical: 4.0), - margin: const EdgeInsets.only(top: 4.0), - decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(4.0))) + style: const TextStyle( + fontFamily: 'SpaceMono', color: Colors.white), + )) ], ), ); diff --git a/filcnaplo_mobile_ui/lib/screens/error_screen.dart b/filcnaplo_mobile_ui/lib/screens/error_screen.dart index d33198f..f581e2e 100755 --- a/filcnaplo_mobile_ui/lib/screens/error_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/error_screen.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class ErrorScreen extends StatelessWidget { - const ErrorScreen(this.details, {Key? key}) : super(key: key); + const ErrorScreen(this.details, {super.key}); final FlutterErrorDetails details; @@ -23,7 +23,8 @@ class ErrorScreen extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(12.0), - child: Icon(FeatherIcons.alertTriangle, size: 48.0, color: AppColors.of(context).red), + child: Icon(FeatherIcons.alertTriangle, + size: 48.0, color: AppColors.of(context).red), ), const Padding( padding: EdgeInsets.all(12.0), @@ -47,7 +48,7 @@ class ErrorScreen extends StatelessWidget { physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, child: SelectableText( - (details.exceptionAsString() + '\n'), + ('${details.exceptionAsString()}\n'), style: const TextStyle(fontFamily: "monospace"), ), ), diff --git a/filcnaplo_mobile_ui/lib/screens/login/login_button.dart b/filcnaplo_mobile_ui/lib/screens/login/login_button.dart index f2e69e8..4990124 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/login_button.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/login_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class LoginButton extends StatelessWidget { - const LoginButton({Key? key, required this.onPressed, required this.child}) : super(key: key); + const LoginButton({super.key, required this.onPressed, required this.child}); final void Function()? onPressed; final Widget? child; @@ -9,12 +9,6 @@ class LoginButton extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialButton( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 15.0, - ), - child: child, - ), elevation: 0, focusElevation: 0, hoverElevation: 0, @@ -24,6 +18,12 @@ class LoginButton extends StatelessWidget { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), color: Colors.white, textColor: Colors.black, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 15.0, + ), + child: child, + ), ); } } diff --git a/filcnaplo_mobile_ui/lib/screens/login/login_input.dart b/filcnaplo_mobile_ui/lib/screens/login/login_input.dart index 68088a3..c18c04c 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/login_input.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/login_input.dart @@ -5,12 +5,11 @@ enum LoginInputStyle { username, password, school } class LoginInput extends StatefulWidget { const LoginInput( - {Key? key, + {super.key, required this.style, this.controller, this.focusNode, - this.onClear}) - : super(key: key); + this.onClear}); final Function()? onClear; final LoginInputStyle style; diff --git a/filcnaplo_mobile_ui/lib/screens/login/login_screen.dart b/filcnaplo_mobile_ui/lib/screens/login/login_screen.dart index 22b1c5c..e5248e5 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/login_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/login_screen.dart @@ -14,15 +14,15 @@ import 'package:flutter/services.dart'; import 'login_screen.i18n.dart'; class LoginScreen extends StatefulWidget { - const LoginScreen({Key? key, this.back = false}) : super(key: key); + const LoginScreen({super.key, this.back = false}); final bool back; @override - _LoginScreenState createState() => _LoginScreenState(); + LoginScreenState createState() => LoginScreenState(); } -class _LoginScreenState extends State { +class LoginScreenState extends State { final usernameController = TextEditingController(); final passwordController = TextEditingController(); final schoolController = SchoolInputController(); @@ -109,15 +109,19 @@ class _LoginScreenState extends State { child: ClipRect( child: Container( // Png shadow *hack* + width: MediaQuery.of(context).size.width / 4, + margin: const EdgeInsets.only( + left: 12.0, right: 12.0, bottom: 12.0), + // Png shadow *hack* child: Stack( children: [ Padding( padding: const EdgeInsets.only(top: 8.0), child: Opacity( + opacity: 0.3, child: Image.asset( "assets/icons/ic_splash.png", - color: Colors.black), - opacity: 0.3), + color: Colors.black)), ), BackdropFilter( filter: @@ -126,9 +130,6 @@ class _LoginScreenState extends State { ) ], ), - width: MediaQuery.of(context).size.width / 4, - margin: const EdgeInsets.only( - left: 12.0, right: 12.0, bottom: 12.0), ), ), ), @@ -246,6 +247,14 @@ class _LoginScreenState extends State { Padding( padding: const EdgeInsets.only(top: 42.0), child: Visibility( + visible: _loginState != LoginState.inProgress, + replacement: const Padding( + padding: EdgeInsets.symmetric(vertical: 6.0), + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ), child: LoginButton( child: Text("login".i18n, maxLines: 1, @@ -255,14 +264,6 @@ class _LoginScreenState extends State { )), onPressed: () => _loginAPI(context: context), ), - visible: _loginState != LoginState.inProgress, - replacement: const Padding( - padding: EdgeInsets.symmetric(vertical: 6.0), - child: CircularProgressIndicator( - valueColor: - AlwaysStoppedAnimation(Colors.white), - ), - ), ), ), if (_loginState == LoginState.missingFields || @@ -306,6 +307,7 @@ class _LoginScreenState extends State { return setState(() => _loginState = LoginState.missingFields); } + // ignore: no_leading_underscores_for_local_identifiers void _callAPI() { loginAPI( username: username, diff --git a/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input.dart b/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input.dart index f3cf683..db151bb 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input.dart @@ -6,16 +6,17 @@ import 'package:flutter/material.dart'; import 'package:filcnaplo_kreta_api/models/school.dart'; class SchoolInput extends StatefulWidget { - const SchoolInput({Key? key, required this.controller, required this.scroll}) : super(key: key); + const SchoolInput( + {super.key, required this.controller, required this.scroll}); final SchoolInputController controller; final ScrollController scroll; @override - _SchoolInputState createState() => _SchoolInputState(); + SchoolInputState createState() => SchoolInputState(); } -class _SchoolInputState extends State { +class SchoolInputState extends State { final _focusNode = FocusNode(); final _layerLink = LayerLink(); late SchoolInputOverlay overlay; @@ -33,10 +34,13 @@ class _SchoolInputState extends State { // Show school list when focused _focusNode.addListener(() { if (_focusNode.hasFocus) { - WidgetsBinding.instance.addPostFrameCallback((_) => overlay.createOverlayEntry(context)); + WidgetsBinding.instance + .addPostFrameCallback((_) => overlay.createOverlayEntry(context)); Future.delayed(const Duration(milliseconds: 100)).then((value) { if (mounted && widget.scroll.hasClients) { - widget.scroll.animateTo(widget.scroll.offset + 500, duration: const Duration(milliseconds: 500), curve: Curves.ease); + widget.scroll.animateTo(widget.scroll.offset + 500, + duration: const Duration(milliseconds: 500), + curve: Curves.ease); } }); } else { @@ -52,7 +56,8 @@ class _SchoolInputState extends State { return; } - List results = searchSchools(widget.controller.schools ?? [], text); + List results = + searchSchools(widget.controller.schools ?? [], text); setState(() { overlay.children = results .map((School e) => SchoolInputTile( diff --git a/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_overlay.dart b/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_overlay.dart index 833e361..74976e2 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_overlay.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_overlay.dart @@ -17,20 +17,20 @@ class SchoolInputOverlay { RenderBox renderBox = context.findRenderObject()! as RenderBox; var size = renderBox.size; return SchoolInputOverlayWidget( - children: children, size: size, layerLink: layerLink, + children: children, ); } } class SchoolInputOverlayWidget extends StatelessWidget { const SchoolInputOverlayWidget({ - Key? key, + super.key, required this.children, required this.size, required this.layerLink, - }) : super(key: key); + }); final Size size; final List? children; @@ -48,7 +48,8 @@ class SchoolInputOverlayWidget extends StatelessWidget { offset: Offset(0.0, size.height + 5.0), child: Material( color: Theme.of(context).colorScheme.background, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0)), elevation: 4.0, shadowColor: Colors.black, child: (children?.length ?? 0) > 0 diff --git a/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_tile.dart b/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_tile.dart index a364c23..e61f79d 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_tile.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/school_input/school_input_tile.dart @@ -2,8 +2,7 @@ import 'package:filcnaplo_kreta_api/models/school.dart'; import 'package:flutter/material.dart'; class SchoolInputTile extends StatelessWidget { - const SchoolInputTile({Key? key, required this.school, this.onTap}) - : super(key: key); + const SchoolInputTile({super.key, required this.school, this.onTap}); final School school; final Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/screens/navigation/nabar.dart b/filcnaplo_mobile_ui/lib/screens/navigation/nabar.dart index 97f5684..0bfbf2d 100755 --- a/filcnaplo_mobile_ui/lib/screens/navigation/nabar.dart +++ b/filcnaplo_mobile_ui/lib/screens/navigation/nabar.dart @@ -2,7 +2,11 @@ import 'package:filcnaplo_mobile_ui/screens/navigation/navbar_item.dart'; import 'package:flutter/material.dart'; class Navbar extends StatelessWidget { - const Navbar({Key? key, required this.selectedIndex, required this.onSelected, required this.items}) : super(key: key); + const Navbar( + {super.key, + required this.selectedIndex, + required this.onSelected, + required this.items}); final int selectedIndex; final void Function(int index) onSelected; diff --git a/filcnaplo_mobile_ui/lib/screens/navigation/navbar_item.dart b/filcnaplo_mobile_ui/lib/screens/navigation/navbar_item.dart index c28796b..168f664 100755 --- a/filcnaplo_mobile_ui/lib/screens/navigation/navbar_item.dart +++ b/filcnaplo_mobile_ui/lib/screens/navigation/navbar_item.dart @@ -11,11 +11,11 @@ class NavItem { class NavbarItem extends StatelessWidget { const NavbarItem({ - Key? key, + super.key, required this.item, required this.active, required this.onTap, - }) : super(key: key); + }); final NavItem item; final bool active; diff --git a/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart b/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart index f5017de..5d15fea 100755 --- a/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/navigation/navigation_screen.dart @@ -29,7 +29,7 @@ import 'package:filcnaplo_premium/providers/goal_provider.dart'; import 'package:filcnaplo/api/providers/ad_provider.dart'; class NavigationScreen extends StatefulWidget { - const NavigationScreen({Key? key}) : super(key: key); + const NavigationScreen({super.key}); static NavigationScreenState? of(BuildContext context) => context.findAncestorStateOfType(); @@ -234,6 +234,7 @@ class NavigationScreenState extends State _navigatorState.currentState?.pushReplacementNamed(page); }); + // ignore: deprecated_member_use return WillPopScope( onWillPop: () async { if (_navigatorState.currentState?.canPop() ?? false) { diff --git a/filcnaplo_mobile_ui/lib/screens/navigation/status_bar.dart b/filcnaplo_mobile_ui/lib/screens/navigation/status_bar.dart index d79078f..afba4a3 100755 --- a/filcnaplo_mobile_ui/lib/screens/navigation/status_bar.dart +++ b/filcnaplo_mobile_ui/lib/screens/navigation/status_bar.dart @@ -6,13 +6,13 @@ import 'package:filcnaplo/api/providers/status_provider.dart'; import 'status_bar.i18n.dart'; class StatusBar extends StatefulWidget { - const StatusBar({Key? key}) : super(key: key); + const StatusBar({super.key}); @override - _StatusBarState createState() => _StatusBarState(); + StatusBarState createState() => StatusBarState(); } -class _StatusBarState extends State { +class StatusBarState extends State { late StatusProvider statusProvider; @override diff --git a/filcnaplo_mobile_ui/lib/screens/news/news_screen.dart b/filcnaplo_mobile_ui/lib/screens/news/news_screen.dart index 164afa3..21ac4b6 100755 --- a/filcnaplo_mobile_ui/lib/screens/news/news_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/news/news_screen.dart @@ -12,7 +12,7 @@ import 'package:provider/provider.dart'; import 'package:filcnaplo/api/providers/news_provider.dart'; class NewsScreen extends StatelessWidget { - const NewsScreen({Key? key}) : super(key: key); + const NewsScreen({super.key}); @override Widget build(BuildContext context) { diff --git a/filcnaplo_mobile_ui/lib/screens/news/news_tile.dart b/filcnaplo_mobile_ui/lib/screens/news/news_tile.dart index e4ce1e5..6de437f 100755 --- a/filcnaplo_mobile_ui/lib/screens/news/news_tile.dart +++ b/filcnaplo_mobile_ui/lib/screens/news/news_tile.dart @@ -3,7 +3,7 @@ import 'package:filcnaplo/models/news.dart'; import 'package:filcnaplo/utils/format.dart'; class NewsTile extends StatelessWidget { - const NewsTile(this.news, {Key? key, this.onTap}) : super(key: key); + const NewsTile(this.news, {super.key, this.onTap}); final News news; final Function()? onTap; diff --git a/filcnaplo_mobile_ui/lib/screens/news/news_view.dart b/filcnaplo_mobile_ui/lib/screens/news/news_view.dart index ff12f7f..6f4ad38 100755 --- a/filcnaplo_mobile_ui/lib/screens/news/news_view.dart +++ b/filcnaplo_mobile_ui/lib/screens/news/news_view.dart @@ -11,7 +11,7 @@ import 'package:provider/provider.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart'; class NewsView extends StatelessWidget { - const NewsView(this.news, {Key? key}) : super(key: key); + const NewsView(this.news, {super.key}); final News news; diff --git a/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_tile.dart b/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_tile.dart index 21715d3..9686a22 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_tile.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_tile.dart @@ -3,7 +3,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; class AccountTile extends StatelessWidget { - const AccountTile({Key? key, this.onTap, this.onTapMenu, this.profileImage, this.name, this.username}) : super(key: key); + const AccountTile( + {super.key, + this.onTap, + this.onTapMenu, + this.profileImage, + this.name, + this.username}); final void Function()? onTap; final void Function()? onTapMenu; @@ -30,7 +36,8 @@ class AccountTile extends StatelessWidget { child: IconButton( splashRadius: 24.0, onPressed: onTapMenu, - icon: Icon(FeatherIcons.moreVertical, color: AppColors.of(context).text.withOpacity(0.8)), + icon: Icon(FeatherIcons.moreVertical, + color: AppColors.of(context).text.withOpacity(0.8)), ), ) : null, diff --git a/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_view.dart b/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_view.dart index b26f38d..a5dfd18 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_view.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/accounts/account_view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'package:filcnaplo/models/user.dart'; import 'package:filcnaplo_mobile_ui/common/bottom_card.dart'; import 'package:filcnaplo_mobile_ui/common/detail.dart'; @@ -8,7 +10,7 @@ import 'package:intl/intl.dart'; import 'account_view.i18n.dart'; class AccountView extends StatelessWidget { - const AccountView(this.user, {Key? key}) : super(key: key); + const AccountView(this.user, {super.key}); final User user; diff --git a/filcnaplo_mobile_ui/lib/screens/settings/debug/subject_icon_gallery.dart b/filcnaplo_mobile_ui/lib/screens/settings/debug/subject_icon_gallery.dart index e8ed7fc..e4116a5 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/debug/subject_icon_gallery.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/debug/subject_icon_gallery.dart @@ -3,7 +3,7 @@ import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:flutter/material.dart'; class SubjectIconGallery extends StatelessWidget { - const SubjectIconGallery({Key? key}) : super(key: key); + const SubjectIconGallery({super.key}); @override Widget build(BuildContext context) { @@ -61,7 +61,7 @@ class SubjectIconGallery extends StatelessWidget { } class SubjectIconItem extends StatelessWidget { - const SubjectIconItem(this.name, {Key? key}) : super(key: key); + const SubjectIconItem(this.name, {super.key}); final String name; diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_names.i18n.dart b/filcnaplo_mobile_ui/lib/screens/settings/modify_names.i18n.dart similarity index 100% rename from filcnaplo_premium/lib/ui/mobile/settings/modify_names.i18n.dart rename to filcnaplo_mobile_ui/lib/screens/settings/modify_names.i18n.dart diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart b/filcnaplo_mobile_ui/lib/screens/settings/modify_subject_names.dart similarity index 96% rename from filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart rename to filcnaplo_mobile_ui/lib/screens/settings/modify_subject_names.dart index d20f7cd..134940e 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_subject_names.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/modify_subject_names.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/api/providers/user_provider.dart'; @@ -22,8 +24,7 @@ import 'package:provider/provider.dart'; import 'modify_names.i18n.dart'; class MenuRenamedSubjects extends StatelessWidget { - const MenuRenamedSubjects({Key? key, required this.settings}) - : super(key: key); + const MenuRenamedSubjects({super.key, required this.settings}); final SettingsProvider settings; @@ -80,7 +81,7 @@ class MenuRenamedSubjects extends StatelessWidget { } class ModifySubjectNames extends StatefulWidget { - const ModifySubjectNames({Key? key}) : super(key: key); + const ModifySubjectNames({super.key}); @override State createState() => _ModifySubjectNamesState(); @@ -380,12 +381,12 @@ class _ModifySubjectNamesState extends State { class RenamedSubjectItem extends StatelessWidget { const RenamedSubjectItem({ - Key? key, + super.key, required this.subject, required this.renamedTo, required this.modifyCallback, required this.removeCallback, - }) : super(key: key); + }); final GradeSubject subject; final String renamedTo; diff --git a/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart index 5c60a9d..6f90f82 100644 --- a/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/notifications_screen.dart @@ -10,7 +10,7 @@ import 'package:provider/provider.dart'; import 'notifications_screen.i18n.dart'; class MenuNotifications extends StatelessWidget { - const MenuNotifications({Key? key, required this.settings}) : super(key: key); + const MenuNotifications({super.key, required this.settings}); final SettingsProvider settings; diff --git a/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart b/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart index 89a9c30..0144dc4 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/privacy_view.dart @@ -4,7 +4,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'settings_screen.i18n.dart'; class PrivacyView extends StatelessWidget { - const PrivacyView({Key? key}) : super(key: key); + const PrivacyView({super.key}); static void show(BuildContext context) => showDialog( context: context, diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_helper.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_helper.dart index e4c459f..0f141f6 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_helper.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_helper.dart @@ -1,4 +1,4 @@ -// ignore_for_file: prefer_function_declarations_over_variables +// ignore_for_file: prefer_function_declarations_over_variables, library_private_types_in_public_api import 'dart:io'; @@ -27,7 +27,7 @@ import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart'; import 'package:flutter_material_color_picker/flutter_material_color_picker.dart'; import 'package:filcnaplo/models/icon_pack.dart'; import 'package:filcnaplo/utils/format.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/theme.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/theme_screen.dart'; class SettingsHelper { static const Map langMap = { @@ -319,7 +319,7 @@ class SettingsHelper { // Rounding modal class RoundingSetting extends StatefulWidget { - const RoundingSetting({Key? key}) : super(key: key); + const RoundingSetting({super.key}); @override _RoundingSettingState createState() => _RoundingSettingState(); @@ -405,7 +405,7 @@ class _RoundingSettingState extends State { // Bell Delay Modal class BellDelaySetting extends StatefulWidget { - const BellDelaySetting({Key? key}) : super(key: key); + const BellDelaySetting({super.key}); @override State createState() => _BellDelaySettingState(); @@ -524,7 +524,7 @@ class _BellDelaySettingState extends State } class GradeColorsSetting extends StatefulWidget { - const GradeColorsSetting({Key? key}) : super(key: key); + const GradeColorsSetting({super.key}); @override _GradeColorsSettingState createState() => _GradeColorsSettingState(); @@ -616,7 +616,7 @@ class _GradeColorsSettingState extends State { } class LiveActivityColorSetting extends StatefulWidget { - const LiveActivityColorSetting({Key? key}) : super(key: key); + const LiveActivityColorSetting({super.key}); @override _LiveActivityColorSettingState createState() => diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart index dd341b1..55832aa 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers, use_build_context_synchronously, deprecated_member_use + import 'package:filcnaplo/api/providers/update_provider.dart'; import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; import 'package:filcnaplo_kreta_api/providers/event_provider.dart'; @@ -20,16 +22,17 @@ import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_i import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; -import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.dart'; import 'package:filcnaplo_mobile_ui/common/system_chrome.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart'; import 'package:filcnaplo_mobile_ui/screens/news/news_screen.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/accounts/account_tile.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/accounts/account_view.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/debug/subject_icon_gallery.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/modify_subject_names.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/notifications_screen.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/privacy_view.dart'; import 'package:filcnaplo_mobile_ui/screens/settings/settings_helper.dart'; +import 'package:filcnaplo_premium/ui/mobile/settings/app_icon_screen.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -39,21 +42,19 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'settings_screen.i18n.dart'; import 'package:flutter/services.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/nickname.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart'; -import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/user/nickname.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/user/profile_pic.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/welcome_message.dart'; class SettingsScreen extends StatefulWidget { - const SettingsScreen({Key? key}) : super(key: key); + const SettingsScreen({super.key}); @override - _SettingsScreenState createState() => _SettingsScreenState(); + SettingsScreenState createState() => SettingsScreenState(); } -class _SettingsScreenState extends State +class SettingsScreenState extends State with SingleTickerProviderStateMixin { int devmodeCountdown = 5; bool __ss = false; // secret settings @@ -75,6 +76,8 @@ class _SettingsScreenState extends State Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), + Provider.of(context, listen: false) + .restoreRecipients(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), Provider.of(context, listen: false).restore(), @@ -685,7 +688,17 @@ class _SettingsScreenState extends State activeColor: Theme.of(context).colorScheme.secondary, ), ), - const PremiumIconPackSelector(), + PanelButton( + onPressed: () { + SettingsHelper.iconPack(context); + }, + title: Text("icon_pack".i18n), + leading: const Icon(FeatherIcons.grid), + trailing: Text( + settings.iconPack.name.capital(), + style: const TextStyle(fontSize: 14.0), + ), + ), // if ios show live activity color option if (defaultTargetPlatform == TargetPlatform.iOS) @@ -834,17 +847,20 @@ class _SettingsScreenState extends State MenuRenamedTeachers( settings: settings, ), - PanelButton( - onPressed: () { - SoonAlert.show(context: context); - }, - title: Text('app_icon'.i18n), - leading: const Icon(FeatherIcons.edit), - // trailing: Text( - // 'default'.i18n, - // style: const TextStyle(fontSize: 14.0), - // ), + PremiumCustomAppIconMenu( + settings: settings, ), + // PanelButton( + // onPressed: () { + // SoonAlert.show(context: context); + // }, + // title: Text('app_icon'.i18n), + // leading: const Icon(FeatherIcons.edit), + // // trailing: Text( + // // 'default'.i18n, + // // style: const TextStyle(fontSize: 14.0), + // // ), + // ), ], ), ), diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart index b584b6d..77936cd 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart @@ -5,7 +5,7 @@ extension SettingsLocalization on String { { "en_en": { "personal_details": "Personal Details", - "open_dkt": "Open DKT", + "open_dkt": "Open DCS", "edit_nickname": "Edit Nickname", "edit_profile_picture": "Edit Profile Picture", "remove_profile_picture": "Remove Profile Picture", @@ -157,7 +157,7 @@ extension SettingsLocalization on String { }, "de_de": { "personal_details": "Persönliche Angaben", - "open_dkt": "Öffnen DKT", + "open_dkt": "Öffnen RDZ", "edit_nickname": "Spitznamen bearbeiten", "edit_profile_picture": "Profilbild bearbeiten", "remove_profile_picture": "Profilbild entfernen", diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart b/filcnaplo_mobile_ui/lib/screens/settings/theme_screen.dart similarity index 97% rename from filcnaplo_premium/lib/ui/mobile/settings/theme.dart rename to filcnaplo_mobile_ui/lib/screens/settings/theme_screen.dart index 763acf2..d46fc0e 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/theme_screen.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously, deprecated_member_use + import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/models/shared_theme.dart'; import 'package:filcnaplo/theme/colors/accent.dart'; @@ -15,18 +17,18 @@ import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_tile.dart'; import 'package:filcnaplo_premium/models/premium_scopes.dart'; import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:filcnaplo_premium/providers/share_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/colorpicker.dart'; +import 'package:filcnaplo_kreta_api/providers/share_provider.dart'; +import 'package:filcnaplo/ui/flutter_colorpicker/colorpicker.dart'; import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; -import 'theme.i18n.dart'; +import 'theme_screen.i18n.dart'; import 'package:share_plus/share_plus.dart'; class PremiumCustomAccentColorSetting extends StatefulWidget { - const PremiumCustomAccentColorSetting({Key? key}) : super(key: key); + const PremiumCustomAccentColorSetting({super.key}); @override State createState() => @@ -898,8 +900,10 @@ class _PremiumCustomAccentColorSettingState class ColorTab extends StatelessWidget { const ColorTab( - {Key? key, required this.tab, required this.color, this.unlocked = true}) - : super(key: key); + {super.key, + required this.tab, + required this.color, + this.unlocked = true}); final Tab tab; final Color color; @@ -936,8 +940,7 @@ class ColorTab extends StatelessWidget { class PremiumColorPickerItem extends StatelessWidget { const PremiumColorPickerItem( - {Key? key, required this.label, this.onTap, required this.color}) - : super(key: key); + {super.key, required this.label, this.onTap, required this.color}); final String label; final void Function()? onTap; @@ -948,6 +951,7 @@ class PremiumColorPickerItem extends StatelessWidget { return Material( type: MaterialType.transparency, child: InkWell( + onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), child: Row( @@ -970,7 +974,6 @@ class PremiumColorPickerItem extends StatelessWidget { ], ), ), - onTap: onTap, ), ); } diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart b/filcnaplo_mobile_ui/lib/screens/settings/theme_screen.i18n.dart similarity index 100% rename from filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart rename to filcnaplo_mobile_ui/lib/screens/settings/theme_screen.i18n.dart diff --git a/filcnaplo_premium/lib/ui/mobile/settings/nickname.dart b/filcnaplo_mobile_ui/lib/screens/settings/user/nickname.dart similarity index 93% rename from filcnaplo_premium/lib/ui/mobile/settings/nickname.dart rename to filcnaplo_mobile_ui/lib/screens/settings/user/nickname.dart index 865537d..3d1dcd5 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/nickname.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/user/nickname.dart @@ -14,7 +14,7 @@ import 'package:provider/provider.dart'; class UserMenuNickname extends StatelessWidget { late User u; - UserMenuNickname(this.u, {Key? key}) : super(key: key); + UserMenuNickname(this.u, {super.key}); @override Widget build(BuildContext context) { @@ -39,7 +39,7 @@ class UserMenuNickname extends StatelessWidget { class UserNicknameEditor extends StatefulWidget { late User u; - UserNicknameEditor(this.u, {Key? key}) : super(key: key); + UserNicknameEditor(this.u, {super.key}); @override State createState() => _UserNicknameEditorState(); diff --git a/filcnaplo_premium/lib/ui/mobile/settings/profile_pic.dart b/filcnaplo_mobile_ui/lib/screens/settings/user/profile_pic.dart similarity index 94% rename from filcnaplo_premium/lib/ui/mobile/settings/profile_pic.dart rename to filcnaplo_mobile_ui/lib/screens/settings/user/profile_pic.dart index 154f168..f72db46 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/profile_pic.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/user/profile_pic.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:convert'; import 'dart:developer'; import 'dart:io'; @@ -20,7 +22,7 @@ import 'package:image_crop/image_crop.dart'; class UserMenuProfilePic extends StatelessWidget { late User u; - UserMenuProfilePic(this.u, {Key? key}) : super(key: key); + UserMenuProfilePic(this.u, {super.key}); @override Widget build(BuildContext context) { @@ -44,7 +46,7 @@ class UserMenuProfilePic extends StatelessWidget { class UserProfilePicEditor extends StatefulWidget { late User u; - UserProfilePicEditor(this.u, {Key? key}) : super(key: key); + UserProfilePicEditor(this.u, {super.key}); @override State createState() => _UserProfilePicEditorState(); diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart index 01900ff..1e7056e 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart @@ -10,13 +10,13 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class AllSumBody extends StatefulWidget { - const AllSumBody({Key? key}) : super(key: key); + const AllSumBody({super.key}); @override - _AllSumBodyState createState() => _AllSumBodyState(); + AllSumBodyState createState() => AllSumBodyState(); } -class _AllSumBodyState extends State { +class AllSumBodyState extends State { late UserProvider user; late GradeProvider gradeProvider; late HomeworkProvider homeworkProvider; @@ -30,14 +30,15 @@ class _AllSumBodyState extends State { int avgDropValue = 0; bool animation = false; - List getSubjectGrades(GradeSubject subject, {int days = 0}) => gradeProvider - .grades - .where((e) => - e.subject == subject && - e.type == GradeType.midYear && - (days == 0 || - e.date.isBefore(DateTime.now().subtract(Duration(days: days))))) - .toList(); + List getSubjectGrades(GradeSubject subject, {int days = 0}) => + gradeProvider.grades + .where((e) => + e.subject == subject && + e.type == GradeType.midYear && + (days == 0 || + e.date + .isBefore(DateTime.now().subtract(Duration(days: days))))) + .toList(); @override void initState() { diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart index d56e3e4..cd0b4a1 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/grades_page.dart @@ -28,13 +28,13 @@ List faces = [ ]; class GradesBody extends StatefulWidget { - const GradesBody({Key? key}) : super(key: key); + const GradesBody({super.key}); @override - _GradesBodyState createState() => _GradesBodyState(); + GradesBodyState createState() => GradesBodyState(); } -class _GradesBodyState extends State { +class GradesBodyState extends State { late UserProvider user; late GradeProvider gradeProvider; late SettingsProvider settings; diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/lessons_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/lessons_page.dart index a90a02b..b606c62 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/lessons_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/lessons_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: no_leading_underscores_for_local_identifiers + import 'dart:math'; import 'package:filcnaplo/api/providers/user_provider.dart'; @@ -37,13 +39,13 @@ class SubjectAbsence { } class LessonsBody extends StatefulWidget { - const LessonsBody({Key? key}) : super(key: key); + const LessonsBody({super.key}); @override - _LessonsBodyState createState() => _LessonsBodyState(); + LessonsBodyState createState() => LessonsBodyState(); } -class _LessonsBodyState extends State { +class LessonsBodyState extends State { late UserProvider user; late AbsenceProvider absenceProvider; late SettingsProvider settingsProvider; diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart index 7702c7a..f2bae43 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/personality_page.dart @@ -13,13 +13,13 @@ import 'package:path_provider/path_provider.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; class PersonalityBody extends StatefulWidget { - const PersonalityBody({Key? key}) : super(key: key); + const PersonalityBody({super.key}); @override - _PersonalityBodyState createState() => _PersonalityBodyState(); + PersonalityBodyState createState() => PersonalityBodyState(); } -class _PersonalityBodyState extends State { +class PersonalityBodyState extends State { late UserProvider user; bool isRevealed = false; diff --git a/filcnaplo_mobile_ui/lib/screens/summary/pages/start_page.dart b/filcnaplo_mobile_ui/lib/screens/summary/pages/start_page.dart index 90e807c..fdfc98e 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/pages/start_page.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/pages/start_page.dart @@ -9,13 +9,13 @@ import 'package:provider/provider.dart'; import 'package:wtf_sliding_sheet/wtf_sliding_sheet.dart'; class StartBody extends StatefulWidget { - const StartBody({Key? key}) : super(key: key); + const StartBody({super.key}); @override - _StartBodyState createState() => _StartBodyState(); + StartBodyState createState() => StartBodyState(); } -class _StartBodyState extends State { +class StartBodyState extends State { late UserProvider user; late GradeProvider gradeProvider; late SettingsProvider settings; diff --git a/filcnaplo_mobile_ui/lib/screens/summary/summary_screen.dart b/filcnaplo_mobile_ui/lib/screens/summary/summary_screen.dart index 7798761..a35b421 100644 --- a/filcnaplo_mobile_ui/lib/screens/summary/summary_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/summary/summary_screen.dart @@ -19,13 +19,13 @@ class SummaryScreen extends StatefulWidget { final bool isBottomSheet; const SummaryScreen({ - Key? key, + super.key, this.currentPage = 'personality', this.isBottomSheet = false, - }) : super(key: key); + }); @override - _SummaryScreenState createState() => _SummaryScreenState(); + SummaryScreenState createState() => SummaryScreenState(); static show( {required BuildContext context, @@ -34,7 +34,7 @@ class SummaryScreen extends StatefulWidget { builder: (context) => SummaryScreen(currentPage: currentPage))); } -class _SummaryScreenState extends State +class SummaryScreenState extends State with SingleTickerProviderStateMixin { late UserProvider user; late SettingsProvider settings; diff --git a/filcnaplo_mobile_ui/pubspec.yaml b/filcnaplo_mobile_ui/pubspec.yaml index fdfc57d..d02517f 100644 --- a/filcnaplo_mobile_ui/pubspec.yaml +++ b/filcnaplo_mobile_ui/pubspec.yaml @@ -46,10 +46,22 @@ dependencies: rounded_expansion_tile: git: url: https://github.com/kimaah/rounded_expansion_tile.git - go_router: ^10.1.2 + go_router: ^12.1.3 + flutter_expandable_fab: ^2.0.0 + intl: ^0.18.1 + i18n_extension: ^10.0.1 + auto_size_text: ^3.0.0 + connectivity_plus: ^5.0.2 + collection: ^1.18.0 + share_plus: ^7.2.1 + image_picker: ^0.8.9 + path_provider: ^2.1.1 + image_crop: + git: + url: https://github.com/kimaah/image_crop.git dev_dependencies: - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 flutter: uses-material-design: true \ No newline at end of file diff --git a/filcnaplo_premium b/filcnaplo_premium new file mode 160000 index 0000000..078142a --- /dev/null +++ b/filcnaplo_premium @@ -0,0 +1 @@ +Subproject commit 078142a3dc2fce9eddb6f608923055943ec4d5e6 diff --git a/filcnaplo_premium/README.md b/filcnaplo_premium/README.md deleted file mode 100644 index c7f8a35..0000000 --- a/filcnaplo_premium/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Premium ✨ - -A collection of features only accessible for premium subscribers. diff --git a/filcnaplo_premium/analysis_options.yaml b/filcnaplo_premium/analysis_options.yaml deleted file mode 100644 index 16f5f56..0000000 --- a/filcnaplo_premium/analysis_options.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/filcnaplo_premium/lib/api/auth.dart b/filcnaplo_premium/lib/api/auth.dart deleted file mode 100644 index 45ddf5c..0000000 --- a/filcnaplo_premium/lib/api/auth.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_premium/models/premium_scopes.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:home_widget/home_widget.dart'; - -class PremiumAuth { - final SettingsProvider _settings; - - PremiumAuth({required SettingsProvider settings}) : _settings = settings; - - initAuth() { - finishAuth("igen"); - // try { - // _sub ??= uriLinkStream.listen( - // (Uri? uri) { - // if (uri != null) { - // final accessToken = uri.queryParameters['access_token']; - // if (accessToken != null) { - // finishAuth(accessToken); - // } - // } - // }, - // onError: (err) { - // log("ERROR: initAuth: $err"); - // }, - // ); - - // launchUrl( - // Uri.parse("https://api.filcnaplo.hu/oauth"), - // mode: LaunchMode.externalApplication, - // ); - // } catch (err, sta) { - // log("ERROR: initAuth: $err\n$sta"); - // } - } - - Future finishAuth(String accessToken) async { - try { - // final res = await http.get(Uri.parse("${FilcAPI.premiumScopesApi}?access_token=${Uri.encodeComponent(accessToken)}")); - // final scopes = ((jsonDecode(res.body) as Map)["scopes"] as List).cast(); - // log("[INFO] Premium auth finish: ${scopes.join(',')}"); - await _settings.update(premiumAccessToken: accessToken); - final result = await refreshAuth(); - if (Platform.isAndroid) updateWidget(); - return result; - } catch (err, sta) { - log("[ERROR] Premium auth failed: $err\n$sta"); - } - - await _settings.update(premiumAccessToken: "", premiumScopes: []); - if (Platform.isAndroid) updateWidget(); - return false; - } - - Future updateWidget() async { - try { - return HomeWidget.updateWidget(name: 'widget_timetable.WidgetTimetable'); - } on PlatformException catch (exception) { - if (kDebugMode) { - print('Error Updating Widget After Auth. $exception'); - } - } - return false; - } - - Future refreshAuth({bool removePremium = false}) async { - await _settings.update( - premiumAccessToken: "igen", - premiumScopes: [PremiumScopes.all], - premiumLogin: "igen", - ); - return true; - //if (!removePremium) { - //if (_settings.premiumAccessToken == "") { - // await _settings.update(premiumScopes: [], premiumLogin: ""); - // return false; - //} - - // Skip premium check when disconnected - // try { - // final status = await InternetAddress.lookup('github.com'); - // if (status.isEmpty) return false; - // } on SocketException catch (_) { - // return false; - // } - - //for (int tries = 0; tries < 3; tries++) { - // try { - // final res = await http.post(Uri.parse(FilcAPI.premiumApi), body: { - // "access_token": _settings.premiumAccessToken, - // }); -// - // if (res.body == "") throw "empty body"; - - // final premium = PremiumResult.fromJson(jsonDecode(res.body) as Map); - // Activation succeeded - // log("[INFO] Premium activated: ${premium.scopes.join(',')}"); - // await _settings.update( - // premiumAccessToken: premium.accessToken, - // premiumScopes: premium.scopes, - // premiumLogin: premium.login, - // ); - // return true; - // } catch (err, sta) { - // log("[ERROR] Premium activation failed: $err\n$sta"); - // } - - // await Future.delayed(const Duration(seconds: 1)); - // - //} - - // Activation failed - //await _settings.update( - // premiumAccessToken: "igen", - // premiumScopes: [PremiumScopes.all], - // premiumLogin: "igen"); - //return false; - } -} diff --git a/filcnaplo_premium/lib/models/premium_result.dart b/filcnaplo_premium/lib/models/premium_result.dart deleted file mode 100644 index 41270ea..0000000 --- a/filcnaplo_premium/lib/models/premium_result.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:filcnaplo_premium/models/premium_scopes.dart'; - -class PremiumResult { - final String accessToken; - final List scopes; - final String login; - - PremiumResult({ - required this.accessToken, - required this.scopes, - required this.login, - }); - - factory PremiumResult.fromJson(Map json) { - return PremiumResult( - accessToken: json["access_token"] ?? "igen", - scopes: (json["scopes"] ?? [PremiumScopes.all]).cast(), - login: json["login"] ?? "igen", - ); - } -} diff --git a/filcnaplo_premium/lib/models/premium_scopes.dart b/filcnaplo_premium/lib/models/premium_scopes.dart deleted file mode 100644 index f793723..0000000 --- a/filcnaplo_premium/lib/models/premium_scopes.dart +++ /dev/null @@ -1,33 +0,0 @@ -class PremiumScopes { - /// VIP - static const all = "filc.premium.*"; - - /// Kupak - - /// Custom nickname - static const nickname = "filc.premium.NICKNAME"; - - /// Advanced grade statistics - static const gradeStats = "filc.premium.GRADE_STATS"; - - /// Advanced theme customization - static const customColors = "filc.premium.CUSTOM_COLORS"; - - /// Icon pack customization for subjects - static const customIcons = "filc.premium.CUSTOM_ICONS"; - - /// Modify subject names - static const renameSubjects = "filc.premium.RENAME_SUBJECTS"; - static const renameTeachers = "filc.premium.RENAME_TEACHERS"; - - /// Tinta - - /// Timetable homescreen widget - static const timetableWidget = "filc.premium.TIMETALBE_WIDGET"; - - /// Goal Planner - static const goalPlanner = "filc.premium.GOAL_PLANNER"; - - /// Fullscreen weekly timetable view - static const fsTimetable = "filc.premium.FS_TIMETABLE"; -} diff --git a/filcnaplo_premium/lib/providers/goal_provider.dart b/filcnaplo_premium/lib/providers/goal_provider.dart deleted file mode 100644 index 1529903..0000000 --- a/filcnaplo_premium/lib/providers/goal_provider.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:filcnaplo/api/providers/database_provider.dart'; -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo_kreta_api/models/subject.dart'; -import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; -import 'package:flutter/widgets.dart'; - -class GoalProvider extends ChangeNotifier { - final DatabaseProvider _db; - final UserProvider _user; - - late bool _done = false; - late GradeSubject? _doneSubject; - - bool get hasDoneGoals => _done; - GradeSubject? get doneSubject => _doneSubject; - - GoalProvider({ - required DatabaseProvider database, - required UserProvider user, - }) : _db = database, - _user = user; - - Future fetchDone({required GradeProvider gradeProvider}) async { - var goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!); - var beforeAvgs = await _db.userQuery.subjectGoalBefores(userId: _user.id!); - - List subjects = gradeProvider.grades - .map((e) => e.subject) - .toSet() - .toList() - ..sort((a, b) => a.name.compareTo(b.name)); - - goalAvgs.forEach((k, v) { - if (beforeAvgs[k] == v) { - _done = true; - _doneSubject = subjects.where((e) => e.id == k).toList()[0]; - - notifyListeners(); - } - }); - } - - void lock() { - _done = false; - _doneSubject = null; - } - - Future clearGoal(GradeSubject subject) async { - final goalPlans = await _db.userQuery.subjectGoalPlans(userId: _user.id!); - final goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!); - final goalBeforeGrades = - await _db.userQuery.subjectGoalBefores(userId: _user.id!); - final goalPinDates = - await _db.userQuery.subjectGoalPinDates(userId: _user.id!); - - goalPlans.remove(subject.id); - goalAvgs.remove(subject.id); - goalBeforeGrades.remove(subject.id); - goalPinDates.remove(subject.id); - - await _db.userStore.storeSubjectGoalPlans(goalPlans, userId: _user.id!); - await _db.userStore.storeSubjectGoalAverages(goalAvgs, userId: _user.id!); - await _db.userStore - .storeSubjectGoalBefores(goalBeforeGrades, userId: _user.id!); - await _db.userStore - .storeSubjectGoalPinDates(goalPinDates, userId: _user.id!); - } -} diff --git a/filcnaplo_premium/lib/providers/premium_provider.dart b/filcnaplo_premium/lib/providers/premium_provider.dart deleted file mode 100644 index 2f5fdfd..0000000 --- a/filcnaplo_premium/lib/providers/premium_provider.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_premium/api/auth.dart'; -import 'package:flutter/widgets.dart'; - -class PremiumProvider extends ChangeNotifier { - final SettingsProvider _settings; - List get scopes => _settings.premiumScopes; - bool hasScope(String scope) => true; - String get accessToken => _settings.premiumAccessToken; - String get login => _settings.premiumLogin; - bool get hasPremium => true; - - late final PremiumAuth _auth; - PremiumAuth get auth => _auth; - - PremiumProvider({required SettingsProvider settings}) : _settings = settings { - _auth = PremiumAuth(settings: _settings); - _settings.addListener(() { - notifyListeners(); - }); - } - - Future activate({bool removePremium = false}) async { - await _auth.refreshAuth(removePremium: removePremium); - notifyListeners(); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_complete_modal.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_complete_modal.dart deleted file mode 100644 index cce1654..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_complete_modal.dart +++ /dev/null @@ -1,252 +0,0 @@ -import 'package:filcnaplo/api/providers/database_provider.dart'; -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo/theme/colors/colors.dart'; -import 'package:filcnaplo_kreta_api/models/subject.dart'; -import 'package:filcnaplo_mobile_ui/common/average_display.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_state_screen.i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class GoalCompleteModal extends StatelessWidget { - const GoalCompleteModal( - this.subject, { - Key? key, - required this.user, - required this.database, - required this.goalAverage, - required this.beforeAverage, - required this.averageDifference, - }) : super(key: key); - - final UserProvider user; - final DatabaseProvider database; - final GradeSubject subject; - - final double goalAverage; - final double beforeAverage; - final double averageDifference; - - @override - Widget build(BuildContext context) { - return Dialog( - elevation: 0, - backgroundColor: Colors.transparent, - child: Container( - padding: const EdgeInsets.all(16.0), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.circular(20.0), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: double.infinity, - decoration: BoxDecoration( - image: const DecorationImage( - image: AssetImage('assets/images/static_confetti.png'), - fit: BoxFit.fitWidth, - alignment: Alignment.topCenter, - ), - color: Colors.white, - borderRadius: BorderRadius.circular(10.0), - ), - padding: const EdgeInsets.all(6.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - goalAverage.toStringAsFixed(1), - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontSize: 64.0, - fontWeight: FontWeight.w800, - ), - ), - // const SizedBox(width: 10.0), - // Icon( - // SubjectIcon.resolveVariant( - // subject: subject, context: context), - // color: Colors.white, - // size: 64.0, - // ), - ], - ), - ), - const SizedBox(height: 10.0), - Text( - 'congrats_title'.i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 27.0, - fontWeight: FontWeight.w700, - height: 1.2, - color: AppColors.of(context).text, - ), - ), - Text( - 'goal_reached'.i18n.fill(['20']), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.w500, - height: 1.1, - color: AppColors.of(context).text, - ), - ), - const SizedBox(height: 18.0), - Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'started_at'.i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 17.0, - fontWeight: FontWeight.w500, - color: AppColors.of(context).text, - ), - ), - const SizedBox(width: 5.0), - AverageDisplay( - average: beforeAverage, - ), - ], - ), - Text( - 'improved_by'.i18n.fill([ - averageDifference.toStringAsFixed(2) + '%', - ]), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 17.0, - fontWeight: FontWeight.w500, - color: AppColors.of(context).text, - ), - ), - ], - ), - const SizedBox(height: 20.0), - Column( - children: [ - GestureDetector( - onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Hamarosan...")), - ); - }, - child: Container( - width: double.infinity, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - gradient: const LinearGradient( - colors: [ - Color(0xFFCAECFA), - Color(0xFFF4D9EE), - Color(0xFFF3EFDA), - ], - stops: [0.0, 0.53, 1.0], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - 'detailed_stats'.i18n, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w700, - color: Color(0xFF691A9B), - ), - ), - ), - ), - ), - const SizedBox(height: 10.0), - GestureDetector( - onTap: () { - Navigator.of(context).pop(); - }, - child: Container( - width: double.infinity, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - color: const Color.fromARGB(38, 131, 131, 131), - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - 'later'.i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w700, - color: AppColors.of(context).text, - ), - ), - ), - ), - ), - ], - ), - ], - ), - ), - ); - - // return Padding( - // padding: const EdgeInsets.symmetric(vertical: 100.0, horizontal: 32.0), - // child: Material( - // borderRadius: BorderRadius.circular(12.0), - // child: Padding( - // padding: const EdgeInsets.all(12.0), - // child: Column( - // children: [ - // // content or idk - // ], - // ), - // ), - // ), - // ); - } - - static Future show( - GradeSubject subject, { - required BuildContext context, - }) async { - UserProvider user = Provider.of(context, listen: false); - DatabaseProvider db = Provider.of(context, listen: false); - - var goalAvgRes = await db.userQuery.subjectGoalAverages(userId: user.id!); - var beforeAvgRes = await db.userQuery.subjectGoalBefores(userId: user.id!); - - //DateTime goalPinDate = DateTime.parse((await db.userQuery.subjectGoalPinDates(userId: user.id!))[widget.subject.id]!); - - String? goalAvgStr = goalAvgRes[subject.id]; - String? beforeAvgStr = beforeAvgRes[subject.id]; - double goalAvg = double.parse(goalAvgStr ?? '0.0'); - double beforeAvg = double.parse(beforeAvgStr ?? '0.0'); - - double avgDifference = ((goalAvg - beforeAvg) / beforeAvg.abs()) * 100; - - return showDialog( - context: context, - builder: (context) => GoalCompleteModal( - subject, - user: user, - database: db, - goalAverage: goalAvg, - beforeAverage: beforeAvg, - averageDifference: avgDifference, - ), - barrierDismissible: false, - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart deleted file mode 100644 index 6fdfe54..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_input.dart +++ /dev/null @@ -1,180 +0,0 @@ -import 'package:filcnaplo/models/settings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:provider/provider.dart'; - -class GoalInput extends StatelessWidget { - const GoalInput( - {Key? key, - required this.currentAverage, - required this.value, - required this.onChanged}) - : super(key: key); - - final double currentAverage; - final double value; - final void Function(double value) onChanged; - - void offsetToValue(Offset offset, Size size) { - double v = ((offset.dx / size.width * 4 + 1) * 10).round() / 10; - v = v.clamp(1.5, 5); - v = v.clamp(((currentAverage * 10).round() / 10), 5); - setValue(v); - } - - void setValue(double v) { - if (v != value) { - HapticFeedback.lightImpact(); - } - onChanged(v); - } - - @override - Widget build(BuildContext context) { - SettingsProvider settings = Provider.of(context); - - List presets = [2, 3, 4, 5]; - presets = presets.where((e) => gradeToAvg(e) > currentAverage).toList(); - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - LayoutBuilder(builder: (context, size) { - return GestureDetector( - onTapDown: (details) { - offsetToValue(details.localPosition, size.biggest); - }, - onHorizontalDragUpdate: (details) { - offsetToValue(details.localPosition, size.biggest); - }, - child: SizedBox( - height: 32.0, - width: double.infinity, - child: Padding( - padding: const EdgeInsets.only(right: 20.0), - child: CustomPaint( - painter: GoalSliderPainter( - value: (value - 1) / 4, settings: settings), - ), - ), - ), - ); - }), - const SizedBox(height: 12.0), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: presets.map((e) { - final pv = (value * 10).round() / 10; - final selected = gradeToAvg(e) == pv; - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(99.0), - color: - gradeColor(e, settings).withOpacity(selected ? 1.0 : 0.2), - border: Border.all(color: gradeColor(e, settings), width: 4), - ), - child: Material( - type: MaterialType.transparency, - child: InkWell( - borderRadius: BorderRadius.circular(99.0), - onTap: () => setValue(gradeToAvg(e)), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 2.0, horizontal: 24.0), - child: Text( - e.toString(), - style: TextStyle( - color: - selected ? Colors.white : gradeColor(e, settings), - fontWeight: FontWeight.bold, - fontSize: 24.0, - ), - ), - ), - ), - ), - ), - ); - }).toList(), - ) - ], - ); - } -} - -class GoalSliderPainter extends CustomPainter { - final double value; - final SettingsProvider settings; - - GoalSliderPainter({required this.value, required this.settings}); - - @override - void paint(Canvas canvas, Size size) { - final radius = size.height / 2; - const cpadding = 4; - final rect = Rect.fromLTWH(0, 0, size.width + radius, size.height); - final vrect = Rect.fromLTWH(0, 0, size.width * value + radius, size.height); - canvas.drawRRect( - RRect.fromRectAndRadius( - rect, - const Radius.circular(99.0), - ), - Paint()..color = Colors.black.withOpacity(.1), - ); - canvas.drawRRect( - RRect.fromRectAndRadius( - vrect, - const Radius.circular(99.0), - ), - Paint() - ..shader = LinearGradient(colors: [ - settings.gradeColors[0], - settings.gradeColors[1], - settings.gradeColors[2], - settings.gradeColors[3], - settings.gradeColors[4], - ]).createShader(rect), - ); - canvas.drawOval( - Rect.fromCircle( - center: Offset(size.width * value, size.height / 2), - radius: radius - cpadding), - Paint()..color = Colors.white, - ); - for (int i = 1; i < 4; i++) { - canvas.drawOval( - Rect.fromCircle( - center: Offset(size.width / 4 * i, size.height / 2), radius: 4), - Paint()..color = Colors.white.withOpacity(.5), - ); - } - } - - @override - bool shouldRepaint(GoalSliderPainter oldDelegate) { - return oldDelegate.value != value; - } -} - -double gradeToAvg(int grade) { - return grade - 0.5; -} - -Color gradeColor(int grade, SettingsProvider settings) { - // return [ - // const Color(0xffFF3B30), - // const Color(0xffFF9F0A), - // const Color(0xffFFD60A), - // const Color(0xff34C759), - // const Color(0xff247665), - // ].elementAt(grade.clamp(1, 5) - 1); - return [ - settings.gradeColors[0], - settings.gradeColors[1], - settings.gradeColors[2], - settings.gradeColors[3], - settings.gradeColors[4], - ].elementAt(grade.clamp(1, 5) - 1); -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart deleted file mode 100644 index 31e445b..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner.dart +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Maintainer: DarK - * Translated from C version - * Minimal Working Fixed @ 2022.12.25 - * ##Please do NOT modify if you don't know whats going on## - * - * Issue: #59 - * - * Future changes / ideas: - * - `best` should be configurable - */ -import 'dart:math'; -import 'package:filcnaplo_kreta_api/models/category.dart'; -import 'package:filcnaplo_kreta_api/models/grade.dart'; -import 'package:filcnaplo_kreta_api/models/subject.dart'; -import 'package:filcnaplo_kreta_api/models/teacher.dart'; -import 'package:flutter/foundation.dart' show listEquals; - -/// Generate list of grades that achieve the wanted goal. -/// After generating possible options, it (when doing so would NOT result in empty list) filters with two criteria: -/// - Plan should not contain more than 15 grades -/// - Plan should not contain only one type of grade -/// -/// **Usage**: -/// -/// ```dart -/// List GoalPlanner(double goal, List grades).solve().plan -/// ``` -class GoalPlanner { - final double goal; - final List grades; - List plans = []; - GoalPlanner(this.goal, this.grades); - - bool _allowed(int grade) => grade > goal; - - void _generate(Generator g) { - // Exit condition 1: Generator has working plan. - if (g.currentAvg.avg >= goal) { - plans.add(Plan(g.plan)); - return; - } - // Exit condition 2: Generator plan will never work. - if (!_allowed(g.gradeToAdd)) { - return; - } - - for (int i = g.max; i >= 0; i--) { - int newGradeToAdd = g.gradeToAdd - 1; - List newPlan = - GoalPlannerHelper._addToList(g.plan, g.gradeToAdd, i); - - Avg newAvg = GoalPlannerHelper._addToAvg(g.currentAvg, g.gradeToAdd, i); - int newN = GoalPlannerHelper.howManyNeeded( - newGradeToAdd, - grades + - newPlan - .map((e) => Grade( - id: '', - date: DateTime(0), - value: GradeValue(e, '', '', 100), - teacher: Teacher.fromString(''), - description: '', - form: '', - groupId: '', - type: GradeType.midYear, - subject: GradeSubject.fromJson({}), - mode: Category.fromJson({}), - seenDate: DateTime(0), - writeDate: DateTime(0), - )) - .toList(), - goal); - - _generate(Generator(newGradeToAdd, newN, newAvg, newPlan)); - } - } - - List solve() { - _generate( - Generator( - 5, - GoalPlannerHelper.howManyNeeded( - 5, - grades, - goal, - ), - Avg(GoalPlannerHelper.averageEvals(grades), - GoalPlannerHelper.weightSum(grades)), - [], - ), - ); - - // Calculate Statistics - for (var e in plans) { - e.sum = e.plan.fold(0, (int a, b) => a + b); - e.avg = e.sum / e.plan.length; - e.sigma = sqrt( - e.plan.map((i) => pow(i - e.avg, 2)).fold(0, (num a, b) => a + b) / - e.plan.length); - } - - // filter without aggression - if (plans.where((e) => e.plan.length < 30).isNotEmpty) { - plans.removeWhere((e) => !(e.plan.length < 30)); - } - if (plans.where((e) => e.sigma > 1).isNotEmpty) { - plans.removeWhere((e) => !(e.sigma > 1)); - } - - return plans; - } -} - -class Avg { - final double avg; - final double n; - - Avg(this.avg, this.n); -} - -class Generator { - final int gradeToAdd; - final int max; - final Avg currentAvg; - final List plan; - - Generator(this.gradeToAdd, this.max, this.currentAvg, this.plan); -} - -class Plan { - final List plan; - int sum = 0; - double avg = 0; - int med = 0; // currently - int mod = 0; // unused - double sigma = 0; - - Plan(this.plan); - - String get dbString { - var finalString = ''; - for (var i in plan) { - finalString += "$i,"; - } - return finalString; - } - - @override - bool operator ==(other) => other is Plan && listEquals(plan, other.plan); - - @override - int get hashCode => Object.hashAll(plan); -} - -class GoalPlannerHelper { - static Avg _addToAvg(Avg base, int grade, int n) => - Avg((base.avg * base.n + grade * n) / (base.n + n), base.n + n); - - static List _addToList(List l, T e, int n) { - if (n == 0) return l; - List tmp = l; - for (int i = 0; i < n; i++) { - tmp = tmp + [e]; - } - return tmp; - } - - static int howManyNeeded(int grade, List base, double goal) { - double avg = averageEvals(base); - double wsum = weightSum(base); - if (avg >= goal) return 0; - if (grade * 1.0 == goal) return -1; - int candidate = (wsum * (avg - goal) / (goal - grade)).floor(); - return (candidate * grade + avg * wsum) / (candidate + wsum) < goal - ? candidate + 1 - : candidate; - } - - static double averageEvals(List grades, {bool finalAvg = false}) { - double average = grades - .map((e) => e.value.value * e.value.weight / 100.0) - .fold(0.0, (double a, double b) => a + b) / - weightSum(grades, finalAvg: finalAvg); - return average.isNaN ? 0.0 : average; - } - - static double weightSum(List grades, {bool finalAvg = false}) => grades - .map((e) => finalAvg ? 1 : e.value.weight / 100) - .fold(0, (a, b) => a + b); -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.dart deleted file mode 100644 index 258a0f1..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.dart +++ /dev/null @@ -1,429 +0,0 @@ -import 'package:filcnaplo/api/providers/database_provider.dart'; -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo/helpers/average_helper.dart'; -import 'package:filcnaplo/helpers/subject.dart'; -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_kreta_api/models/grade.dart'; -import 'package:filcnaplo_kreta_api/models/group_average.dart'; -import 'package:filcnaplo_kreta_api/models/subject.dart'; -import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; -import 'package:filcnaplo_mobile_ui/common/average_display.dart'; -import 'package:filcnaplo_mobile_ui/common/round_border_icon.dart'; -import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner_screen.i18n.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/route_option.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -enum PlanResult { - available, // There are possible solutions - unreachable, // The solutions are too hard don't even try - unsolvable, // There are no solutions - reached, // Goal already reached -} - -class GoalPlannerScreen extends StatefulWidget { - final GradeSubject subject; - - const GoalPlannerScreen({Key? key, required this.subject}) : super(key: key); - - @override - State createState() => _GoalPlannerScreenState(); -} - -class _GoalPlannerScreenState extends State { - late GradeProvider gradeProvider; - late GradeCalculatorProvider calculatorProvider; - late SettingsProvider settingsProvider; - late DatabaseProvider dbProvider; - late UserProvider user; - - bool gradeCalcMode = false; - - List getSubjectGrades(GradeSubject subject) => !gradeCalcMode - ? gradeProvider.grades.where((e) => e.subject == subject).toList() - : calculatorProvider.grades.where((e) => e.subject == subject).toList(); - - double goalValue = 4.0; - List grades = []; - - Plan? recommended; - Plan? fastest; - Plan? selectedRoute; - List otherPlans = []; - - @override - void initState() { - super.initState(); - user = Provider.of(context, listen: false); - dbProvider = Provider.of(context, listen: false); - } - - Future> fetchGoalPlans() async { - return await dbProvider.userQuery.subjectGoalPlans(userId: user.id!); - } - - Future> fetchGoalAverages() async { - return await dbProvider.userQuery.subjectGoalAverages(userId: user.id!); - } - - // haha bees lol - Future> fetchGoalBees() async { - return await dbProvider.userQuery.subjectGoalBefores(userId: user.id!); - } - - Future> fetchGoalPinDates() async { - return await dbProvider.userQuery.subjectGoalPinDates(userId: user.id!); - } - - PlanResult getResult() { - final currentAvg = GoalPlannerHelper.averageEvals(grades); - - recommended = null; - fastest = null; - otherPlans = []; - - if (currentAvg >= goalValue) return PlanResult.reached; - - final planner = GoalPlanner(goalValue, grades); - final plans = planner.solve(); - - plans.sort((a, b) => (a.avg - (2 * goalValue + 5) / 3) - .abs() - .compareTo(b.avg - (2 * goalValue + 5) / 3)); - - try { - final singleSolution = plans.every((e) => e.sigma == 0); - recommended = - plans.where((e) => singleSolution ? true : e.sigma > 0).first; - plans.removeWhere((e) => e == recommended); - } catch (_) {} - - plans.sort((a, b) => a.plan.length.compareTo(b.plan.length)); - - try { - fastest = plans.removeAt(0); - } catch (_) {} - - if ((recommended?.plan.length ?? 0) - (fastest?.plan.length ?? 0) >= 3) { - recommended = fastest; - } - - if (recommended == null) { - recommended = null; - fastest = null; - otherPlans = []; - selectedRoute = null; - return PlanResult.unsolvable; - } - - if (recommended!.plan.length > 10) { - recommended = null; - fastest = null; - otherPlans = []; - selectedRoute = null; - return PlanResult.unreachable; - } - - otherPlans = List.from(plans); - - return PlanResult.available; - } - - void getGrades() { - grades = getSubjectGrades(widget.subject).toList(); - } - - @override - Widget build(BuildContext context) { - gradeProvider = Provider.of(context); - calculatorProvider = Provider.of(context); - settingsProvider = Provider.of(context); - - getGrades(); - - final currentAvg = GoalPlannerHelper.averageEvals(grades); - - final result = getResult(); - - List subjectGrades = getSubjectGrades(widget.subject); - - double avg = AverageHelper.averageEvals(subjectGrades); - - var nullavg = GroupAverage(average: 0.0, subject: widget.subject, uid: "0"); - double groupAverage = gradeProvider.groupAverages - .firstWhere((e) => e.subject == widget.subject, orElse: () => nullavg) - .average; - - return Scaffold( - body: SafeArea( - child: ListView( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 220.0, - right: 15.0, - left: 2.0, - ), - children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // const BackButton(), - // Padding( - // padding: const EdgeInsets.only(right: 15.0), - // child: Row( - // children: [ - // Text( - // 'goal_planner_title'.i18n, - // style: const TextStyle( - // fontWeight: FontWeight.w500, fontSize: 18.0), - // ), - // const SizedBox( - // width: 5, - // ), - // const BetaChip(), - // ], - // ), - // ), - // ], - // ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - children: [ - const BackButton(), - RoundBorderIcon( - icon: Icon( - SubjectIcon.resolveVariant( - context: context, - subject: widget.subject, - ), - size: 18, - weight: 1.5, - ), - ), - const SizedBox( - width: 5.0, - ), - Text( - (widget.subject.isRenamed - ? widget.subject.renamedTo - : widget.subject.name) ?? - 'goal_planner_title'.i18n, - style: const TextStyle( - fontSize: 20.0, - fontWeight: FontWeight.w700, - ), - ), - ], - ), - Row( - children: [ - if (groupAverage != 0) - AverageDisplay(average: groupAverage, border: true), - const SizedBox(width: 6.0), - AverageDisplay(average: avg), - ], - ), - ], - ), - const SizedBox(height: 12.0), - Padding( - padding: const EdgeInsets.only(left: 22.0, right: 22.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "set_a_goal".i18n, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), - ), - const SizedBox(height: 4.0), - Text( - goalValue.toString(), - style: TextStyle( - fontWeight: FontWeight.w900, - fontSize: 48.0, - color: gradeColor(goalValue.round(), settingsProvider), - ), - ), - // Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Text( - // "select_subject".i18n, - // style: const TextStyle( - // fontWeight: FontWeight.bold, - // fontSize: 20.0, - // ), - // ), - // const SizedBox(height: 4.0), - // Column( - // children: [ - // Icon( - // SubjectIcon.resolveVariant( - // context: context, - // subject: widget.subject, - // ), - // size: 48.0, - // ), - // Text( - // (widget.subject.isRenamed - // ? widget.subject.renamedTo - // : widget.subject.name) ?? - // '', - // style: const TextStyle( - // fontSize: 17.0, - // fontWeight: FontWeight.w500, - // ), - // ) - // ], - // ) - // ], - // ) - const SizedBox(height: 24.0), - Text( - "pick_route".i18n, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), - ), - const SizedBox(height: 12.0), - if (recommended != null) - RouteOption( - plan: recommended!, - mark: RouteMark.recommended, - selected: selectedRoute == recommended!, - onSelected: () => setState(() { - selectedRoute = recommended; - }), - ), - if (fastest != null && fastest != recommended) - RouteOption( - plan: fastest!, - mark: RouteMark.fastest, - selected: selectedRoute == fastest!, - onSelected: () => setState(() { - selectedRoute = fastest; - }), - ), - ...otherPlans.map((e) => RouteOption( - plan: e, - selected: selectedRoute == e, - onSelected: () => setState(() { - selectedRoute = e; - }), - )), - if (result != PlanResult.available) Text(result.name.i18n), - ], - ), - ), - ], - ), - ), - bottomSheet: MediaQuery.removePadding( - context: context, - removeBottom: false, - removeTop: true, - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - child: Container( - padding: const EdgeInsets.only(top: 24.0), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: - const BorderRadius.vertical(top: Radius.circular(24.0)), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(.1), - blurRadius: 8.0, - ) - ]), - child: SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - GoalInput( - value: goalValue, - currentAverage: currentAvg, - onChanged: (v) => setState(() { - selectedRoute = null; - goalValue = v; - }), - ), - const SizedBox(height: 24.0), - SizedBox( - width: double.infinity, - child: RawMaterialButton( - onPressed: () async { - if (selectedRoute == null) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('${"pick_route".i18n}...'))); - } - - final goalPlans = await fetchGoalPlans(); - final goalAvgs = await fetchGoalAverages(); - final goalBeforeGrades = await fetchGoalBees(); - final goalPinDates = await fetchGoalPinDates(); - - goalPlans[widget.subject.id] = - selectedRoute!.dbString; - goalAvgs[widget.subject.id] = - goalValue.toStringAsFixed(2); - goalBeforeGrades[widget.subject.id] = - avg.toStringAsFixed(2); - goalPinDates[widget.subject.id] = - DateTime.now().toIso8601String(); - // goalPlans[widget.subject.id] = '1,2,3,4,5,'; - // goalAvgs[widget.subject.id] = '3.69'; - // goalBeforeGrades[widget.subject.id] = '3.69'; - // goalPinDates[widget.subject.id] = - // DateTime.now().toIso8601String(); - - await dbProvider.userStore.storeSubjectGoalPlans( - goalPlans, - userId: user.id!); - await dbProvider.userStore.storeSubjectGoalAverages( - goalAvgs, - userId: user.id!); - await dbProvider.userStore.storeSubjectGoalBefores( - goalBeforeGrades, - userId: user.id!); - await dbProvider.userStore.storeSubjectGoalPinDates( - goalPinDates, - userId: user.id!); - - Navigator.of(context).pop(); - }, - fillColor: Theme.of(context).colorScheme.primary, - shape: const StadiumBorder(), - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - "track_it".i18n, - style: const TextStyle( - color: Colors.white, - fontSize: 20.0, - fontWeight: FontWeight.w600, - ), - ), - ), - ) - ], - ), - ), - ), - ), - ), - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart deleted file mode 100644 index 0e99214..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_planner_screen.i18n.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:i18n_extension/i18n_extension.dart'; - -extension Localization on String { - static final _t = Translations.byLocale("hu_hu") + - { - "en_en": { - "goal_planner_title": "Goal Planning", - "set_a_goal": "Your Goal", - "select_subject": "Subject", - "pick_route": "Pick a Route", - "track_it": "Track it!", - "recommended": "Recommended", - "fastest": "Fastest", - "unsolvable": "Unsolvable :(", - "unreachable": "Unreachable :(", - }, - "hu_hu": { - "goal_planner_title": "Cél követés", - "set_a_goal": "Kitűzött cél", - "select_subject": "Tantárgy", - "pick_route": "Válassz egy utat", - "track_it": "Követés!", - "recommended": "Ajánlott", - "fastest": "Leggyorsabb", - "unsolvable": "Megoldhatatlan :(", - "unreachable": "Elérhetetlen :(", - }, - "de_de": { - "goal_planner_title": "Zielplanung", - "set_a_goal": "Dein Ziel", - "select_subject": "Thema", - "pick_route": "Wähle einen Weg", - "track_it": "Verfolge es!", - "recommended": "Empfohlen", - "fastest": "Am schnellsten", - "unsolvable": "Unlösbar :(", - "unreachable": "Unerreichbar :(", - }, - }; - - String get i18n => localize(this, _t); - String fill(List params) => localizeFill(this, params); - String plural(int value) => localizePlural(value, this, _t); - String version(Object modifier) => localizeVersion(modifier, this, _t); -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_state_screen.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_state_screen.dart deleted file mode 100644 index ba577c8..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_state_screen.dart +++ /dev/null @@ -1,469 +0,0 @@ -import 'package:filcnaplo/api/providers/database_provider.dart'; -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo/helpers/average_helper.dart'; -import 'package:filcnaplo/helpers/subject.dart'; -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_kreta_api/models/grade.dart'; -import 'package:filcnaplo_kreta_api/models/subject.dart'; -import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; -import 'package:filcnaplo_mobile_ui/common/action_button.dart'; -import 'package:filcnaplo_mobile_ui/common/average_display.dart'; -import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; -import 'package:filcnaplo_mobile_ui/common/progress_bar.dart'; -import 'package:filcnaplo_mobile_ui/common/round_border_icon.dart'; -import 'package:filcnaplo_premium/providers/goal_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_state_screen.i18n.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/route_option.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 'goal_planner_screen.dart'; -import 'graph.dart'; - -class GoalStateScreen extends StatefulWidget { - final GradeSubject subject; - - const GoalStateScreen({Key? key, required this.subject}) : super(key: key); - - @override - State createState() => _GoalStateScreenState(); -} - -class _GoalStateScreenState extends State { - late UserProvider user; - late DatabaseProvider db; - late GradeProvider gradeProvider; - late SettingsProvider settingsProvider; - - double currAvg = 0.0; - double goalAvg = 0.0; - double beforeAvg = 0.0; - double afterAvg = 0.0; - double avgDifference = 0; - - Plan? plan; - - late Widget gradeGraph; - - DateTime goalPinDate = DateTime.now(); - - void fetchGoalAverages() async { - var goalAvgRes = await db.userQuery.subjectGoalAverages(userId: user.id!); - var beforeAvgRes = await db.userQuery.subjectGoalBefores(userId: user.id!); - - goalPinDate = DateTime.parse((await db.userQuery - .subjectGoalPinDates(userId: user.id!))[widget.subject.id]!); - - String? goalAvgStr = goalAvgRes[widget.subject.id]; - String? beforeAvgStr = beforeAvgRes[widget.subject.id]; - goalAvg = double.parse(goalAvgStr ?? '0.0'); - beforeAvg = double.parse(beforeAvgStr ?? '0.0'); - - avgDifference = ((goalAvg - beforeAvg) / beforeAvg.abs()) * 100; - - setState(() {}); - } - - void fetchGoalPlan() async { - var planRes = await db.userQuery.subjectGoalPlans(userId: user.id!); - List prePlan = planRes[widget.subject.id]!.split(','); - prePlan.removeLast(); - - plan = Plan( - prePlan.map((e) => int.parse(e)).toList(), - ); - - setState(() {}); - } - - List getSubjectGrades(GradeSubject subject) => - gradeProvider.grades.where((e) => (e.subject == subject)).toList(); - - List getAfterGoalGrades(GradeSubject subject) => gradeProvider.grades - .where((e) => (e.subject == subject && e.date.isAfter(goalPinDate))) - .toList(); - - @override - void initState() { - super.initState(); - user = Provider.of(context, listen: false); - db = Provider.of(context, listen: false); - - WidgetsBinding.instance.addPostFrameCallback((_) { - fetchGoalAverages(); - fetchGoalPlan(); - }); - } - - @override - Widget build(BuildContext context) { - gradeProvider = Provider.of(context); - settingsProvider = Provider.of(context); - - var subjectGrades = getSubjectGrades(widget.subject).toList(); - currAvg = AverageHelper.averageEvals(subjectGrades); - - var afterGoalGrades = getAfterGoalGrades(widget.subject).toList(); - afterAvg = AverageHelper.averageEvals(afterGoalGrades); - - Color averageColor = currAvg >= 1 && currAvg <= 5 - ? ColorTween( - begin: settingsProvider.gradeColors[currAvg.floor() - 1], - end: settingsProvider.gradeColors[currAvg.ceil() - 1]) - .transform(currAvg - currAvg.floor())! - : Theme.of(context).colorScheme.secondary; - - gradeGraph = Padding( - padding: const EdgeInsets.only( - top: 12.0, - bottom: 8.0, - ), - child: Panel( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: const EdgeInsets.only(top: 16.0, right: 12.0), - child: GoalGraph(afterGoalGrades, - dayThreshold: 5, classAvg: goalAvg), - ), - const SizedBox(height: 5.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'look_at_graph'.i18n, - style: const TextStyle( - fontWeight: FontWeight.w700, - fontSize: 23.0, - ), - ), - Text( - 'thats_progress'.i18n, - style: const TextStyle( - fontWeight: FontWeight.w400, - fontSize: 20.0, - ), - ), - const SizedBox(height: 15.0), - ProgressBar( - value: currAvg / goalAvg, - backgroundColor: averageColor, - height: 16.0, - ), - const SizedBox(height: 8.0), - ], - ), - ), - ], - ), - ), - ); - - return Scaffold( - body: ListView( - padding: EdgeInsets.zero, - children: [ - Container( - decoration: const BoxDecoration( - // image: DecorationImage( - // image: - // AssetImage('assets/images/subject_covers/math_light.png'), - // fit: BoxFit.fitWidth, - // alignment: Alignment.topCenter, - // ), - ), - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Theme.of(context).scaffoldBackgroundColor.withOpacity(0.2), - Theme.of(context).scaffoldBackgroundColor, - ], - stops: const [ - 0.1, - 0.22, - ], - ), - ), - child: Padding( - padding: const EdgeInsets.only( - top: 60.0, - left: 2.0, - right: 2.0, - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const BackButton(), - IconButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0)), - title: Text("attention".i18n), - content: Text("attention_body".i18n), - actions: [ - ActionButton( - label: "delete".i18n, - onTap: () async { - // clear the goal - await Provider.of(context, - listen: false) - .clearGoal(widget.subject); - // close the modal and the goal page - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }, - ), - ], - ), - ); - }, - icon: const Icon(FeatherIcons.x), - ), - ], - ), - const SizedBox(height: 22.0), - Column( - children: [ - RoundBorderIcon( - icon: Icon( - SubjectIcon.resolveVariant( - context: context, - subject: widget.subject, - ), - size: 26.0, - weight: 2.5, - ), - padding: 8.0, - width: 2.5, - ), - const SizedBox( - height: 10.0, - ), - Text( - (widget.subject.isRenamed - ? widget.subject.renamedTo - : widget.subject.name) ?? - 'goal_planner_title'.i18n, - style: const TextStyle( - fontSize: 30.0, - fontWeight: FontWeight.w700, - ), - ), - Text( - 'almost_there'.i18n, - style: const TextStyle( - fontSize: 22.0, - fontWeight: FontWeight.w400, - height: 1.0, - ), - ), - ], - ), - const SizedBox(height: 28.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - 'started_with'.i18n, - style: const TextStyle( - fontWeight: FontWeight.w700, - fontSize: 20.0, - ), - ), - const SizedBox(width: 5.0), - AverageDisplay(average: beforeAvg), - ], - ), - Row( - children: [ - Text( - 'current'.i18n, - style: const TextStyle( - fontWeight: FontWeight.w700, - fontSize: 20.0, - ), - ), - const SizedBox(width: 5.0), - AverageDisplay(average: currAvg), - const SizedBox(width: 5.0), - // ide majd kell average difference - ], - ), - ], - ), - ), - const SizedBox(height: 10.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Panel( - padding: const EdgeInsets.all(18.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'your_goal'.i18n, - style: const TextStyle( - fontSize: 23.0, - fontWeight: FontWeight.w700, - ), - ), - RawMaterialButton( - onPressed: () async { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (context) => - GoalPlannerScreen( - subject: widget.subject))); - }, - fillColor: Colors.black, - shape: const StadiumBorder(), - padding: const EdgeInsets.symmetric( - horizontal: 18.0), - child: Text( - "change_it".i18n, - style: const TextStyle( - height: 1.0, - color: Colors.white, - fontSize: 14.0, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - goalAvg.toString(), - style: const TextStyle( - height: 1.1, - fontSize: 42.0, - fontWeight: FontWeight.w800, - ), - ), - const SizedBox(width: 10.0), - Center( - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 5.0, - horizontal: 8.0, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(45.0), - color: avgDifference.isNegative - ? Colors.redAccent.shade400 - .withOpacity(.15) - : Colors.greenAccent.shade700 - .withOpacity(.15), - ), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Icon( - avgDifference.isNegative - ? FeatherIcons.chevronDown - : FeatherIcons.chevronUp, - color: avgDifference.isNegative - ? Colors.redAccent.shade400 - : Colors.greenAccent.shade700, - size: 18.0, - ), - const SizedBox(width: 5.0), - Text( - avgDifference.toStringAsFixed(2) + - '%', - textAlign: TextAlign.center, - style: TextStyle( - color: avgDifference.isNegative - ? Colors.redAccent.shade400 - : Colors.greenAccent.shade700, - fontSize: 22.0, - height: 0.8, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ), - ], - ), - ], - ), - ), - ), - const SizedBox(height: 5.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: gradeGraph, - ), - const SizedBox(height: 5.0), - Padding( - padding: const EdgeInsets.only( - left: 12.0, - right: 12.0, - top: 5.0, - bottom: 8.0, - ), - child: Panel( - padding: const EdgeInsets.all(18.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'you_need'.i18n, - style: const TextStyle( - fontSize: 23.0, - fontWeight: FontWeight.w700, - ), - ), - ], - ), - const SizedBox(height: 8.0), - plan != null - ? RouteOptionRow( - plan: plan!, - ) - : const Text(''), - ], - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_state_screen.i18n.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_state_screen.i18n.dart deleted file mode 100644 index 19c56ed..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/goal_state_screen.i18n.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:i18n_extension/i18n_extension.dart'; - -extension Localization on String { - static final _t = Translations.byLocale("hu_hu") + - { - "en_en": { - // base page - "goal_planner_title": "Goal Planning", - "almost_there": "Almost there! Keep going!", - "started_with": "Started with:", - "current": "Current:", - "your_goal": "Your goal:", - "change_it": "Change it", - "look_at_graph": "Look at this graph!", - "thats_progress": - "Now that's what I call progress! Push a little more, you're almost there..", - "you_need": "You need:", - // done modal - "congrats_title": "🎉 Congratulations!", - "goal_reached": "You reached your goal after %s days!", - "started_at": "You started at", - "improved_by": "and improved your grade by %s", - "detailed_stats": "See my detailed stats", - "later": "Yay! I'll see my stats later.", - // sure delete modal - "delete": "Delete", - "attention": "Attention!", - "attention_body": - "Your goal and progress will be lost forever and cannot be restored.", - }, - "hu_hu": { - // base page - "goal_planner_title": "Cél követés", - "almost_there": "Majdnem megvan! Így tovább!", - "started_with": "Így kezdődött:", - "current": "Jelenlegi:", - "your_goal": "Célod:", - "change_it": "Megváltoztatás", - "look_at_graph": "Nézd meg ezt a grafikont!", - "thats_progress": - "Ezt nevezem haladásnak! Hajts még egy kicsit, már majdnem kész..", - "you_need": "Szükséges:", - // done modal - "congrats_title": "🎉 Gratulálunk!", - "goal_reached": "%s nap után érted el a célod!", - "started_at": "Átlagod kezdéskor:", - "improved_by": "%s-os javulást értél el!", - "detailed_stats": "Részletes statisztikám", - "later": "Hurrá! Megnézem máskor.", - // sure delete modal - "delete": "Törlés", - "attention": "Figyelem!", - "attention_body": - "A kitűzött célod és haladásod örökre elveszik és nem lesz visszaállítható.", - }, - "de_de": { - // base page - "goal_planner_title": "Zielplanung", - "almost_there": "Fast dort! Weitermachen!", - "started_with": "Begann mit:", - "current": "Aktuell:", - "your_goal": "Dein Ziel:", - "change_it": "Ändern Sie es", - "look_at_graph": "Schauen Sie sich diese Grafik an!", - "thats_progress": - "Das nenne ich Fortschritt! Drücken Sie noch ein wenig, Sie haben es fast geschafft..", - "you_need": "Du brauchst:", - // done modal - "congrats_title": "🎉 Glückwunsch!", - "goal_reached": "Du hast dein Ziel nach %s Tagen erreicht!", - "started_at": "Gesamtbewertung:", - "improved_by": "Sie haben %s Verbesserung erreicht!", - "detailed_stats": "Detaillierte Statistiken", - "later": "Hurra! Ich schaue später nach.", - // sure delete modal - "delete": "Löschen", - "attention": "Achtung!", - "attention_body": - "Ihr Ziel und Ihr Fortschritt gehen für immer verloren und können nicht wiederhergestellt werden.", - }, - }; - - String get i18n => localize(this, _t); - String fill(List params) => localizeFill(this, params); - String plural(int value) => localizePlural(value, this, _t); - String version(Object modifier) => localizeVersion(modifier, this, _t); -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart deleted file mode 100644 index 1a1142a..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/grade_display.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class GradeDisplay extends StatelessWidget { - const GradeDisplay({Key? key, required this.grade}) : super(key: key); - - final int grade; - - @override - Widget build(BuildContext context) { - SettingsProvider settings = Provider.of(context); - - return Container( - width: 36, - height: 36, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: gradeColor(grade, settings).withOpacity(.3), - ), - child: Center( - child: Text( - grade.toInt().toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 22.0, - color: gradeColor(grade, settings), - ), - ), - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/graph.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/graph.dart deleted file mode 100644 index 724b84a..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/graph.dart +++ /dev/null @@ -1,249 +0,0 @@ -import 'dart:math'; - -import 'package:filcnaplo/helpers/average_helper.dart'; -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo/theme/colors/colors.dart'; -import 'package:filcnaplo_kreta_api/models/grade.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/graph.i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:fl_chart/fl_chart.dart'; -import 'package:provider/provider.dart'; - -class GoalGraph extends StatefulWidget { - const GoalGraph(this.data, {Key? key, this.dayThreshold = 7, this.classAvg}) - : super(key: key); - - final List data; - final int dayThreshold; - final double? classAvg; - - @override - _GoalGraphState createState() => _GoalGraphState(); -} - -class _GoalGraphState extends State { - late SettingsProvider settings; - - List getSpots(List data) { - List subjectData = []; - List> sortedData = [[]]; - - // Sort by date descending - data.sort((a, b) => -a.writeDate.compareTo(b.writeDate)); - - // Sort data to points by treshold - for (var element in data) { - if (sortedData.last.isNotEmpty && - sortedData.last.last.writeDate.difference(element.writeDate).inDays > - widget.dayThreshold) { - sortedData.add([]); - } - for (var dataList in sortedData) { - dataList.add(element); - } - } - - // Create FlSpots from points - for (var dataList in sortedData) { - double average = AverageHelper.averageEvals(dataList); - - if (dataList.isNotEmpty) { - subjectData.add(FlSpot( - dataList[0].writeDate.month + - (dataList[0].writeDate.day / 31) + - ((dataList[0].writeDate.year - data.last.writeDate.year) * 12), - double.parse(average.toStringAsFixed(2)), - )); - } - } - - return subjectData; - } - - @override - Widget build(BuildContext context) { - settings = Provider.of(context); - - List subjectSpots = []; - List ghostSpots = []; - List extraLinesV = []; - List extraLinesH = []; - - // Filter data - List data = widget.data - .where((e) => e.value.weight != 0) - .where((e) => e.type == GradeType.midYear) - .where((e) => e.gradeType?.name == "Osztalyzat") - .toList(); - - // Filter ghost data - List ghostData = widget.data - .where((e) => e.value.weight != 0) - .where((e) => e.type == GradeType.ghost) - .toList(); - - // Calculate average - double average = AverageHelper.averageEvals(data); - - // Calculate graph color - Color averageColor = average >= 1 && average <= 5 - ? ColorTween( - begin: settings.gradeColors[average.floor() - 1], - end: settings.gradeColors[average.ceil() - 1]) - .transform(average - average.floor())! - : Theme.of(context).colorScheme.secondary; - - subjectSpots = getSpots(data); - - // naplo/#73 - if (subjectSpots.isNotEmpty) { - ghostSpots = getSpots(data + ghostData); - - // hax - ghostSpots = ghostSpots - .where((e) => e.x >= subjectSpots.map((f) => f.x).reduce(max)) - .toList(); - ghostSpots = ghostSpots.map((e) => FlSpot(e.x + 0.1, e.y)).toList(); - ghostSpots.add(subjectSpots.firstWhere( - (e) => e.x >= subjectSpots.map((f) => f.x).reduce(max), - orElse: () => const FlSpot(-1, -1))); - ghostSpots.removeWhere( - (element) => element.x == -1 && element.y == -1); // naplo/#74 - } - - // Horizontal line displaying the class average - if (widget.classAvg != null && - widget.classAvg! > 0.0 && - settings.graphClassAvg) { - extraLinesH.add(HorizontalLine( - y: widget.classAvg!, - color: AppColors.of(context).text.withOpacity(.75), - )); - } - - // LineChart is really cute because it tries to render it's contents outside of it's rect. - return widget.data.length <= 2 - ? SizedBox( - height: 150, - child: Center( - child: Text( - "not_enough_grades".i18n, - textAlign: TextAlign.center, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - ) - : ClipRect( - child: SizedBox( - child: subjectSpots.length > 1 - ? Padding( - padding: const EdgeInsets.only(top: 8.0, right: 8.0), - child: LineChart( - LineChartData( - extraLinesData: ExtraLinesData( - verticalLines: extraLinesV, - horizontalLines: extraLinesH), - lineBarsData: [ - LineChartBarData( - preventCurveOverShooting: true, - spots: subjectSpots, - isCurved: true, - colors: [averageColor], - barWidth: 8, - isStrokeCapRound: true, - dotData: FlDotData(show: false), - belowBarData: BarAreaData( - show: true, - colors: [ - averageColor.withOpacity(0.7), - averageColor.withOpacity(0.3), - averageColor.withOpacity(0.2), - averageColor.withOpacity(0.1), - ], - gradientColorStops: [0.1, 0.6, 0.8, 1], - gradientFrom: const Offset(0, 0), - gradientTo: const Offset(0, 1), - ), - ), - if (ghostData.isNotEmpty && ghostSpots.isNotEmpty) - LineChartBarData( - preventCurveOverShooting: true, - spots: ghostSpots, - isCurved: true, - colors: [AppColors.of(context).text], - barWidth: 8, - isStrokeCapRound: true, - dotData: FlDotData(show: false), - belowBarData: BarAreaData( - show: true, - colors: [ - AppColors.of(context).text.withOpacity(0.7), - AppColors.of(context).text.withOpacity(0.3), - AppColors.of(context).text.withOpacity(0.2), - AppColors.of(context).text.withOpacity(0.1), - ], - gradientColorStops: [0.1, 0.6, 0.8, 1], - gradientFrom: const Offset(0, 0), - gradientTo: const Offset(0, 1), - ), - ), - ], - minY: 1, - maxY: 5, - gridData: FlGridData( - show: true, - horizontalInterval: 1, - // checkToShowVerticalLine: (_) => false, - // getDrawingHorizontalLine: (_) => FlLine( - // color: AppColors.of(context).text.withOpacity(.15), - // strokeWidth: 2, - // ), - // getDrawingVerticalLine: (_) => FlLine( - // color: AppColors.of(context).text.withOpacity(.25), - // strokeWidth: 2, - // ), - ), - lineTouchData: LineTouchData( - touchTooltipData: LineTouchTooltipData( - tooltipBgColor: Colors.grey.shade800, - fitInsideVertically: true, - fitInsideHorizontally: true, - ), - handleBuiltInTouches: true, - touchSpotThreshold: 20.0, - getTouchedSpotIndicator: (_, spots) { - return List.generate( - spots.length, - (index) => TouchedSpotIndicatorData( - FlLine( - color: Colors.grey.shade900, - strokeWidth: 3.5, - ), - FlDotData( - getDotPainter: (a, b, c, d) => - FlDotCirclePainter( - strokeWidth: 0, - color: Colors.grey.shade900, - radius: 10.0, - ), - ), - ), - ); - }, - ), - borderData: FlBorderData( - show: false, - border: Border.all( - color: Theme.of(context).scaffoldBackgroundColor, - width: 4, - ), - ), - ), - ), - ) - : null, - height: 158, - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/graph.i18n.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/graph.i18n.dart deleted file mode 100644 index 50e2ea8..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/graph.i18n.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:i18n_extension/i18n_extension.dart'; - -extension Localization on String { - static final _t = Translations.byLocale("hu_hu") + - { - "en_en": { - "not_enough_grades": "Not enough data to show a graph here.", - }, - "hu_hu": { - "not_enough_grades": "Nem szereztél még elég jegyet grafikon mutatáshoz.", - }, - "de_de": { - "not_enough_grades": "Noch nicht genug Noten, um die Grafik zu zeigen.", - }, - }; - - String get i18n => localize(this, _t); - String fill(List params) => localizeFill(this, params); - String plural(int value) => localizePlural(value, this, _t); - String version(Object modifier) => localizeVersion(modifier, this, _t); -} diff --git a/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart b/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart deleted file mode 100644 index dfc4b8b..0000000 --- a/filcnaplo_premium/lib/ui/mobile/goal_planner/route_option.dart +++ /dev/null @@ -1,202 +0,0 @@ -import 'package:filcnaplo/theme/colors/colors.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner_screen.i18n.dart'; -import 'package:filcnaplo_premium/ui/mobile/goal_planner/grade_display.dart'; -import 'package:flutter/material.dart'; - -enum RouteMark { recommended, fastest } - -class RouteOption extends StatelessWidget { - const RouteOption( - {Key? key, - required this.plan, - this.mark, - this.selected = false, - required this.onSelected}) - : super(key: key); - - final Plan plan; - final RouteMark? mark; - final bool selected; - final void Function() onSelected; - - Widget markLabel() { - const style = TextStyle(fontWeight: FontWeight.bold); - - switch (mark!) { - case RouteMark.recommended: - return Text("recommended".i18n, style: style); - case RouteMark.fastest: - return Text("fastest".i18n, style: style); - } - } - - Color markColor(BuildContext context) { - switch (mark) { - case RouteMark.recommended: - return const Color.fromARGB(255, 104, 93, 255); - case RouteMark.fastest: - return const Color.fromARGB(255, 255, 91, 146); - default: - return Theme.of(context).colorScheme.primary; - } - } - - @override - Widget build(BuildContext context) { - List gradeWidgets = []; - - for (int i = 5; i > 1; i--) { - final count = plan.plan.where((e) => e == i).length; - - if (count > 4) { - gradeWidgets.add(Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "${count}x", - style: TextStyle( - fontSize: 22.0, - fontWeight: FontWeight.w500, - color: AppColors.of(context).text.withOpacity(.7), - ), - ), - const SizedBox(width: 4.0), - GradeDisplay(grade: i), - ], - )); - } else { - gradeWidgets - .addAll(List.generate(count, (_) => GradeDisplay(grade: i))); - } - - if (count > 0) { - gradeWidgets.add(SizedBox( - height: 36.0, - width: 32.0, - child: Center( - child: Icon(Icons.add, - color: AppColors.of(context).text.withOpacity(.5))), - )); - } - } - - gradeWidgets.removeLast(); - - return Padding( - padding: const EdgeInsets.only(bottom: 12.0), - child: SizedBox( - width: double.infinity, - child: Card( - surfaceTintColor: - selected ? markColor(context).withOpacity(.2) : Colors.white, - margin: EdgeInsets.zero, - elevation: 5, - shadowColor: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - side: selected - ? BorderSide(color: markColor(context), width: 4.0) - : BorderSide.none, - ), - child: InkWell( - borderRadius: BorderRadius.circular(16.0), - onTap: onSelected, - child: Padding( - padding: const EdgeInsets.only( - top: 16.0, bottom: 16.0, left: 20.0, right: 12.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (mark != null) ...[ - Chip( - label: markLabel(), - visualDensity: VisualDensity.compact, - backgroundColor: - selected ? markColor(context) : Colors.transparent, - labelPadding: const EdgeInsets.symmetric(horizontal: 8.0), - labelStyle: - TextStyle(color: selected ? Colors.white : null), - shape: StadiumBorder( - side: BorderSide( - color: markColor(context), - width: 3.0, - ), - ), - ), - const SizedBox(height: 6.0), - ], - Wrap( - spacing: 4.0, - runSpacing: 8.0, - children: gradeWidgets, - ), - ], - ), - ), - ), - ), - ), - ); - } -} - -class RouteOptionRow extends StatelessWidget { - const RouteOptionRow({ - Key? key, - required this.plan, - this.mark, - }) : super(key: key); - - final Plan plan; - final RouteMark? mark; - - @override - Widget build(BuildContext context) { - List gradeWidgets = []; - - for (int i = 5; i > 1; i--) { - final count = plan.plan.where((e) => e == i).length; - - if (count > 4) { - gradeWidgets.add(Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "${count}x", - style: TextStyle( - fontSize: 22.0, - fontWeight: FontWeight.w500, - color: AppColors.of(context).text.withOpacity(.7), - ), - ), - const SizedBox(width: 4.0), - GradeDisplay(grade: i), - ], - )); - } else { - gradeWidgets - .addAll(List.generate(count, (_) => GradeDisplay(grade: i))); - } - - if (count > 0) { - gradeWidgets.add(SizedBox( - height: 36.0, - width: 32.0, - child: Center( - child: Icon(Icons.add, - color: AppColors.of(context).text.withOpacity(.5))), - )); - } - } - - gradeWidgets.removeLast(); - - return Wrap( - spacing: 4.0, - runSpacing: 8.0, - children: gradeWidgets, - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/premium/activation_view/activation_dashboard.dart b/filcnaplo_premium/lib/ui/mobile/premium/activation_view/activation_dashboard.dart deleted file mode 100644 index 7d6448f..0000000 --- a/filcnaplo_premium/lib/ui/mobile/premium/activation_view/activation_dashboard.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:filcnaplo/theme/colors/colors.dart'; -import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_feather_icons/flutter_feather_icons.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:flutter/services.dart'; -import 'package:provider/provider.dart'; - -class ActivationDashboard extends StatefulWidget { - const ActivationDashboard({super.key}); - - @override - State createState() => _ActivationDashboardState(); -} - -class _ActivationDashboardState extends State { - bool manualActivationLoading = false; - - Future onManualActivation() async { - final data = await Clipboard.getData("text/plain"); - if (data == null || data.text == null || data.text == "") { - return; - } - setState(() { - manualActivationLoading = true; - }); - final result = - await context.read().auth.finishAuth(data.text!); - setState(() { - manualActivationLoading = false; - }); - - if (!result && mounted) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text( - "Sikertelen aktiválás. Kérlek próbáld újra később!", - style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), - ), - backgroundColor: Colors.red, - )); - } - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Spacer(), - Center( - child: SvgPicture.asset( - "assets/images/github.svg", - height: 64.0, - ), - ), - const SizedBox(height: 32.0), - const Text( - "Jelentkezz be a GitHub felületén és adj hozzáférést a Filcnek, hogy aktiváld a Premiumot.", - textAlign: TextAlign.center, - style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0), - ), - const SizedBox(height: 12.0), - Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14.0)), - child: const Padding( - padding: EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(FeatherIcons.alertTriangle, - size: 20.0, color: Colors.orange), - SizedBox(width: 12.0), - Text( - "Figyelem!", - style: TextStyle( - fontSize: 18.0, fontWeight: FontWeight.bold), - ), - ], - ), - SizedBox(height: 6.0), - Text( - "Csak akkor érzékeli a Filc a támogatói státuszod, ha nem állítod privátra!", - style: TextStyle(fontSize: 16.0), - ), - ], - ), - ), - ), - const SizedBox(height: 12.0), - Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14.0)), - child: const Padding( - padding: EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(FeatherIcons.alertTriangle, - size: 20.0, color: Colors.orange), - SizedBox(width: 12.0), - Text( - "Figyelem!", - style: TextStyle( - fontSize: 18.0, fontWeight: FontWeight.bold), - ), - ], - ), - SizedBox(height: 6.0), - Text( - "Ha friss támogató vagy, 5-10 percbe telhet az aktiválás. Kérlek gyere vissza később, és próbáld újra!", - style: TextStyle(fontSize: 16.0), - ), - ], - ), - ), - ), - const SizedBox(height: 12.0), - Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14.0)), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Ha bejelentkezés után nem lép vissza az alkalmazásba automatikusan, aktiváld a támogatásod manuálisan", - style: - TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500), - ), - const SizedBox(height: 6.0), - Center( - child: TextButton.icon( - onPressed: onManualActivation, - style: ButtonStyle( - foregroundColor: MaterialStatePropertyAll( - Theme.of(context).colorScheme.secondary), - overlayColor: MaterialStatePropertyAll(Theme.of(context) - .colorScheme - .secondary - .withOpacity(.1)), - ), - icon: manualActivationLoading - ? const SizedBox( - child: CircularProgressIndicator(), - height: 16.0, - width: 16.0, - ) - : const Icon(FeatherIcons.key, size: 20.0), - label: const Padding( - padding: EdgeInsets.only(left: 8.0), - child: Text( - "Aktiválás tokennel", - style: TextStyle(fontSize: 16.0), - ), - ), - ), - ), - ], - ), - ), - ), - const Spacer(), - Padding( - padding: const EdgeInsets.only(bottom: 24.0), - child: Center( - child: TextButton.icon( - onPressed: () { - Navigator.of(context).pop(); - }, - style: ButtonStyle( - foregroundColor: - MaterialStatePropertyAll(AppColors.of(context).text), - overlayColor: MaterialStatePropertyAll( - AppColors.of(context).text.withOpacity(.1)), - ), - icon: const Icon(FeatherIcons.arrowLeft, size: 20.0), - label: const Text( - "Vissza", - style: TextStyle(fontSize: 16.0), - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/premium/activation_view/activation_view.dart b/filcnaplo_premium/lib/ui/mobile/premium/activation_view/activation_view.dart deleted file mode 100644 index c0ee3fa..0000000 --- a/filcnaplo_premium/lib/ui/mobile/premium/activation_view/activation_view.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:animations/animations.dart'; -import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/premium/activation_view/activation_dashboard.dart'; -import 'package:flutter/material.dart'; -import 'package:lottie/lottie.dart'; -import 'package:provider/provider.dart'; - -class PremiumActivationView extends StatefulWidget { - const PremiumActivationView({super.key}); - - @override - State createState() => _PremiumActivationViewState(); -} - -class _PremiumActivationViewState extends State with SingleTickerProviderStateMixin { - late AnimationController animation; - bool activated = false; - - @override - void initState() { - super.initState(); - context.read().auth.initAuth(); - - animation = AnimationController(vsync: this, duration: const Duration(seconds: 2)); - } - - @override - void dispose() { - animation.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final premium = context.watch(); - - if (premium.hasPremium && !activated) { - activated = true; - animation.forward(); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Future.delayed(const Duration(seconds: 2)).then((value) { - if (mounted) Navigator.of(context).pop(); - }); - }); - } - - return Scaffold( - body: PageTransitionSwitcher( - transitionBuilder: (child, primaryAnimation, secondaryAnimation) => SharedAxisTransition( - animation: primaryAnimation, - secondaryAnimation: secondaryAnimation, - transitionType: SharedAxisTransitionType.horizontal, - fillColor: Colors.transparent, - child: child, - ), - child: premium.hasPremium - ? Center( - child: SizedBox( - width: 400, - child: Lottie.network("https://assets2.lottiefiles.com/packages/lf20_wkebwzpz.json", controller: animation), - ), - ) - : const SafeArea(child: ActivationDashboard()), - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/premium/premium_inline.dart b/filcnaplo_premium/lib/ui/mobile/premium/premium_inline.dart deleted file mode 100644 index 07d0285..0000000 --- a/filcnaplo_premium/lib/ui/mobile/premium/premium_inline.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; -import 'package:flutter/material.dart'; - -enum PremiumInlineFeature { nickname, theme, widget, goal, stats } - -const Map _featureAssets = { - PremiumInlineFeature.nickname: "assets/images/premium_nickname_inline_showcase.png", - PremiumInlineFeature.theme: "assets/images/premium_theme_inline_showcase.png", - PremiumInlineFeature.widget: "assets/images/premium_widget_inline_showcase.png", - PremiumInlineFeature.goal: "assets/images/premium_goal_inline_showcase.png", - PremiumInlineFeature.stats: "assets/images/premium_stats_inline_showcase.png", -}; - -const Map _featuresInline = { - PremiumInlineFeature.nickname: PremiumFeature.profile, - PremiumInlineFeature.theme: PremiumFeature.customcolors, - PremiumInlineFeature.widget: PremiumFeature.widget, - PremiumInlineFeature.goal: PremiumFeature.goalplanner, - PremiumInlineFeature.stats: PremiumFeature.gradestats, -}; - -class PremiumInline extends StatelessWidget { - const PremiumInline({super.key, required this.features}); - - final List features; - - String _getAsset() { - for (int i = 0; i < features.length; i++) { - if (DateTime.now().day % features.length == i) { - return _featureAssets[features[i]]!; - } - } - - return _featureAssets[features[0]]!; - } - - PremiumFeature _getFeature() { - for (int i = 0; i < features.length; i++) { - if (DateTime.now().day % features.length == i) { - return _featuresInline[features[i]]!; - } - } - - return _featuresInline[features[0]]!; - } - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Image.asset(_getAsset()), - Positioned.fill( - child: Material( - type: MaterialType.transparency, - child: InkWell( - borderRadius: BorderRadius.circular(16.0), - onTap: () { - PremiumLockedFeatureUpsell.show(context: context, feature: _getFeature()); - }, - ), - ), - ), - ], - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart b/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart deleted file mode 100644 index 701791d..0000000 --- a/filcnaplo_premium/lib/ui/mobile/premium/upsell.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'package:filcnaplo/icons/filc_icons.dart'; -import 'package:filcnaplo_mobile_ui/premium/premium_screen.dart'; -import 'package:flutter/material.dart'; - -enum PremiumFeature { - gradestats, - customcolors, - profile, - iconpack, - subjectrename, - teacherrename, - weeklytimetable, - goalplanner, - widget, -} - -enum PremiumFeatureLevel { kupak, tinta } - -const Map _featureLevels = { - PremiumFeature.gradestats: PremiumFeatureLevel.kupak, - PremiumFeature.customcolors: PremiumFeatureLevel.kupak, - PremiumFeature.profile: PremiumFeatureLevel.kupak, - PremiumFeature.iconpack: PremiumFeatureLevel.kupak, - PremiumFeature.subjectrename: PremiumFeatureLevel.kupak, - PremiumFeature.weeklytimetable: PremiumFeatureLevel.tinta, - PremiumFeature.goalplanner: PremiumFeatureLevel.tinta, - PremiumFeature.widget: PremiumFeatureLevel.tinta, -}; - -const Map _featureAssets = { - PremiumFeature.gradestats: "assets/images/premium_stats_showcase.png", - PremiumFeature.customcolors: "assets/images/premium_theme_showcase.png", - PremiumFeature.profile: "assets/images/premium_nickname_showcase.png", - PremiumFeature.weeklytimetable: - "assets/images/premium_timetable_showcase.png", - PremiumFeature.goalplanner: "assets/images/premium_goal_showcase.png", - PremiumFeature.widget: "assets/images/premium_widget_showcase.png", -}; - -const Map _featureTitles = { - PremiumFeature.gradestats: "Találtál egy prémium funkciót.", - PremiumFeature.customcolors: "Több személyre szabás kell?", - PremiumFeature.profile: "Nem tetszik a neved?", - PremiumFeature.iconpack: "Jobban tetszettek a régi ikonok?", - PremiumFeature.subjectrename: - "Sokáig tart elolvasni, hogy \"Földrajz természettudomány\"?", - PremiumFeature.weeklytimetable: "Szeretnéd egyszerre az egész hetet látni?", - PremiumFeature.goalplanner: "Kövesd a céljaidat, sok-sok statisztikával.", - PremiumFeature.widget: "Órák a kezdőképernyőd kényelméből.", -}; - -const Map _featureDescriptions = { - PremiumFeature.gradestats: - "Támogass Kupak szinten, hogy több statisztikát láthass. ", - PremiumFeature.customcolors: - "Támogass Kupak szinten, és szabd személyre az elemek, a háttér, és a panelek színeit.", - PremiumFeature.profile: - "Kupak szinten változtathatod a nevedet, sőt, akár a profilképedet is.", - PremiumFeature.iconpack: - "Támogass Kupak szinten, hogy ikon témát választhass.", - PremiumFeature.subjectrename: - "Támogass Kupak szinten, hogy átnevezhesd Föcire.", - PremiumFeature.weeklytimetable: - "Támogass Tinta szinten a heti órarend funkcióért.", - PremiumFeature.goalplanner: "A célkövetéshez támogass Tinta szinten.", - PremiumFeature.widget: - "Támogass Tinta szinten, és helyezz egy widgetet a kezdőképernyődre.", -}; - -class PremiumLockedFeatureUpsell extends StatelessWidget { - const PremiumLockedFeatureUpsell({super.key, required this.feature}); - - static void show( - {required BuildContext context, required PremiumFeature feature}) => - showDialog( - context: context, - builder: (context) => PremiumLockedFeatureUpsell(feature: feature)); - - final PremiumFeature feature; - - IconData _getIcon() => _featureLevels[feature] == PremiumFeatureLevel.kupak - ? FilcIcons.kupak - : FilcIcons.tinta; - Color _getColor(BuildContext context) => - _featureLevels[feature] == PremiumFeatureLevel.kupak - ? const Color(0xffC8A708) - : Theme.of(context).brightness == Brightness.light - ? const Color(0xff691A9B) - : const Color(0xffA66FC8); - String? _getAsset() => _featureAssets[feature]; - String _getTitle() => _featureTitles[feature]!; - String _getDescription() => _featureDescriptions[feature]!; - - @override - Widget build(BuildContext context) { - final Color color = _getColor(context); - - return Dialog( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Title Bar - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Icon(_getIcon()), - ), - IconButton( - onPressed: () => Navigator.of(context).pop(), - icon: const Icon(Icons.close), - ), - ], - ), - - // Image showcase - if (_getAsset() != null) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Image.asset(_getAsset()!), - ), - - // Dialog title - Padding( - padding: const EdgeInsets.only(top: 12.0), - child: Text( - _getTitle(), - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20.0, - ), - ), - ), - - // Dialog description - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - _getDescription(), - style: const TextStyle( - fontSize: 16.0, - ), - ), - ), - - // CTA button - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: SizedBox( - width: double.infinity, - child: TextButton( - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(color.withOpacity(.25)), - foregroundColor: MaterialStatePropertyAll(color), - overlayColor: - MaterialStatePropertyAll(color.withOpacity(.1))), - onPressed: () { - Navigator.of(context, rootNavigator: true) - .push(MaterialPageRoute(builder: (context) { - return const PremiumScreen(); - })); - }, - child: const Text( - "Vigyél oda!", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18.0, - ), - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart b/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart deleted file mode 100644 index 2857946..0000000 --- a/filcnaplo_premium/lib/ui/mobile/settings/icon_pack.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; -import 'package:filcnaplo_mobile_ui/screens/settings/settings_helper.dart'; -import 'package:filcnaplo_premium/models/premium_scopes.dart'; -import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; -import 'package:flutter/material.dart'; -import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart'; -import 'package:flutter_feather_icons/flutter_feather_icons.dart'; -import 'package:provider/provider.dart'; -import 'package:filcnaplo/utils/format.dart'; - -class PremiumIconPackSelector extends StatelessWidget { - const PremiumIconPackSelector({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final settings = Provider.of(context); - - return PanelButton( - onPressed: () { - if (!Provider.of(context, listen: false) - .hasScope(PremiumScopes.customIcons)) { - PremiumLockedFeatureUpsell.show( - context: context, feature: PremiumFeature.iconpack); - return; - } - - SettingsHelper.iconPack(context); - }, - title: Text("icon_pack".i18n), - leading: const Icon(FeatherIcons.grid), - trailing: Text( - settings.iconPack.name.capital(), - style: const TextStyle(fontSize: 14.0), - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart b/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart deleted file mode 100644 index c9f9a55..0000000 --- a/filcnaplo_premium/lib/ui/mobile/settings/modify_teacher_names.dart +++ /dev/null @@ -1,437 +0,0 @@ -import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:filcnaplo/api/providers/database_provider.dart'; -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo/theme/colors/colors.dart'; -import 'package:filcnaplo/utils/format.dart'; -import 'package:filcnaplo_kreta_api/models/teacher.dart'; -import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; -import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; -import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart'; -import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; -import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; -import 'package:filcnaplo_premium/models/premium_scopes.dart'; -import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/premium/upsell.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 'modify_names.i18n.dart'; - -class MenuRenamedTeachers extends StatelessWidget { - const MenuRenamedTeachers({Key? key, required this.settings}) - : super(key: key); - - final SettingsProvider settings; - - @override - Widget build(BuildContext context) { - return PanelButton( - padding: const EdgeInsets.only(left: 14.0), - onPressed: () { - if (!Provider.of(context, listen: false) - .hasScope(PremiumScopes.renameTeachers)) { - PremiumLockedFeatureUpsell.show( - context: context, feature: PremiumFeature.teacherrename); - return; - } - - Navigator.of(context, rootNavigator: true).push( - CupertinoPageRoute(builder: (context) => const ModifyTeacherNames()), - ); - }, - title: Text( - "rename_teachers".i18n, - style: TextStyle( - color: AppColors.of(context) - .text - .withOpacity(settings.renamedTeachersEnabled ? 1.0 : .5)), - ), - leading: settings.renamedTeachersEnabled - ? const Icon(FeatherIcons.users) - : Icon(FeatherIcons.users, - color: AppColors.of(context).text.withOpacity(.25)), - trailingDivider: true, - trailing: Switch( - onChanged: (v) async { - if (!Provider.of(context, listen: false) - .hasScope(PremiumScopes.renameTeachers)) { - PremiumLockedFeatureUpsell.show( - context: context, feature: PremiumFeature.teacherrename); - return; - } - - settings.update(renamedTeachersEnabled: v); - await Provider.of(context, listen: false) - .convertBySettings(); - await Provider.of(context, listen: false) - .convertBySettings(); - await Provider.of(context, listen: false) - .convertBySettings(); - }, - value: settings.renamedTeachersEnabled, - activeColor: Theme.of(context).colorScheme.secondary, - ), - ); - } -} - -class ModifyTeacherNames extends StatefulWidget { - const ModifyTeacherNames({Key? key}) : super(key: key); - - @override - State createState() => _ModifyTeacherNamesState(); -} - -class _ModifyTeacherNamesState extends State { - final GlobalKey _scaffoldKey = GlobalKey(); - final _teacherName = TextEditingController(); - String? selectedTeacherId; - - late List teachers; - late UserProvider user; - late DatabaseProvider dbProvider; - late SettingsProvider settings; - - @override - void initState() { - super.initState(); - teachers = (Provider.of(context, listen: false) - .grades - .map((e) => e.teacher) - .toSet() - .toList() - ..sort((a, b) => a.name.compareTo(b.name))); - user = Provider.of(context, listen: false); - dbProvider = Provider.of(context, listen: false); - } - - Future> fetchRenamedTeachers() async { - return await dbProvider.userQuery.renamedTeachers(userId: user.id!); - } - - void showRenameDialog() { - showDialog( - context: context, - builder: (context) => StatefulBuilder(builder: (context, setS) { - return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(14.0))), - title: Text("rename_teacher".i18n), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - DropdownButton2( - items: teachers - .map((item) => DropdownMenuItem( - value: item.id, - child: Text( - item.name, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: AppColors.of(context).text, - ), - overflow: TextOverflow.ellipsis, - ), - )) - .toList(), - onChanged: (String? v) async { - final renamedSubs = await fetchRenamedTeachers(); - - setS(() { - selectedTeacherId = v; - - if (renamedSubs.containsKey(selectedTeacherId)) { - _teacherName.text = renamedSubs[selectedTeacherId]!; - } else { - _teacherName.text = ""; - } - }); - }, - iconSize: 14, - iconEnabledColor: AppColors.of(context).text, - iconDisabledColor: AppColors.of(context).text, - underline: const SizedBox(), - itemHeight: 40, - itemPadding: const EdgeInsets.only(left: 14, right: 14), - buttonWidth: 50, - dropdownWidth: 300, - dropdownPadding: null, - buttonDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - ), - dropdownDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - ), - dropdownElevation: 8, - scrollbarRadius: const Radius.circular(40), - scrollbarThickness: 6, - scrollbarAlwaysShow: true, - offset: const Offset(-10, -10), - buttonSplashColor: Colors.transparent, - customButton: Container( - width: double.infinity, - decoration: BoxDecoration( - border: Border.all(color: Colors.grey, width: 2), - borderRadius: BorderRadius.circular(12.0), - ), - padding: const EdgeInsets.symmetric( - vertical: 12.0, horizontal: 8.0), - child: Text( - selectedTeacherId == null - ? "select_teacher".i18n - : teachers - .firstWhere( - (element) => element.id == selectedTeacherId) - .name, - style: Theme.of(context).textTheme.titleSmall!.copyWith( - fontWeight: FontWeight.w700, - color: AppColors.of(context).text.withOpacity(0.75)), - overflow: TextOverflow.ellipsis, - maxLines: 2, - textAlign: TextAlign.center, - ), - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 8.0), - child: Icon(FeatherIcons.arrowDown, size: 32), - ), - TextField( - controller: _teacherName, - decoration: InputDecoration( - border: OutlineInputBorder( - borderSide: - const BorderSide(color: Colors.grey, width: 1.5), - borderRadius: BorderRadius.circular(12.0), - ), - focusedBorder: OutlineInputBorder( - borderSide: - const BorderSide(color: Colors.grey, width: 1.5), - borderRadius: BorderRadius.circular(12.0), - ), - contentPadding: const EdgeInsets.symmetric(horizontal: 12.0), - hintText: "modified_name".i18n, - suffixIcon: IconButton( - icon: const Icon( - FeatherIcons.x, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _teacherName.text = ""; - }); - }, - ), - ), - ), - ], - ), - actions: [ - TextButton( - child: Text( - "cancel".i18n, - style: const TextStyle(fontWeight: FontWeight.w500), - ), - onPressed: () { - Navigator.of(context).maybePop(); - }, - ), - TextButton( - child: Text( - "done".i18n, - style: const TextStyle(fontWeight: FontWeight.w500), - ), - onPressed: () async { - if (selectedTeacherId != null) { - final renamedSubs = await fetchRenamedTeachers(); - - renamedSubs[selectedTeacherId!] = _teacherName.text; - await dbProvider.userStore - .storeRenamedTeachers(renamedSubs, userId: user.id!); - await Provider.of(context, listen: false) - .convertBySettings(); - await Provider.of(context, listen: false) - .convertBySettings(); - await Provider.of(context, listen: false) - .convertBySettings(); - } - Navigator.of(context).pop(true); - setState(() {}); - }, - ), - ], - ); - }), - ).then((val) { - _teacherName.text = ""; - selectedTeacherId = null; - }); - } - - @override - Widget build(BuildContext context) { - settings = Provider.of(context); - return Scaffold( - key: _scaffoldKey, - appBar: AppBar( - surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, - leading: BackButton(color: AppColors.of(context).text), - title: Text( - "modify_teachers".i18n, - style: TextStyle(color: AppColors.of(context).text), - ), - ), - body: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Panel( - // child: SwitchListTile( - - // title: Text("italics_toggle".i18n), - // onChanged: (value) => - // settings.update(renamedTeachersItalics: value), - // value: settings.renamedTeachersItalics, - // ), - // ), - // const SizedBox( - // height: 20, - // ), - InkWell( - onTap: showRenameDialog, - borderRadius: BorderRadius.circular(12.0), - child: Container( - width: double.infinity, - decoration: BoxDecoration( - border: Border.all(color: Colors.grey, width: 2), - borderRadius: BorderRadius.circular(12.0), - ), - padding: const EdgeInsets.symmetric( - vertical: 18.0, horizontal: 12.0), - child: Center( - child: Text( - "rename_new_teacher".i18n, - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 18, - color: AppColors.of(context).text.withOpacity(.85), - ), - ), - ), - ), - ), - const SizedBox( - height: 30, - ), - FutureBuilder>( - future: fetchRenamedTeachers(), - builder: (context, snapshot) { - if (!snapshot.hasData || snapshot.data!.isEmpty) { - return Container(); - } - - return Panel( - title: Text("renamed_teachers".i18n), - child: Column( - children: snapshot.data!.keys.map( - (key) { - Teacher? teacher = teachers - .firstWhere((element) => key == element.id); - String renameTo = snapshot.data![key]!; - return RenamedTeacherItem( - teacher: teacher, - renamedTo: renameTo, - modifyCallback: () { - setState(() { - selectedTeacherId = teacher.id; - _teacherName.text = renameTo; - }); - showRenameDialog(); - }, - removeCallback: () { - setState(() { - Map subs = - Map.from(snapshot.data!); - subs.remove(key); - dbProvider.userStore.storeRenamedTeachers( - subs, - userId: user.id!); - }); - }, - ); - }, - ).toList(), - ), - ); - }, - ), - ], - ), - ), - )); - } -} - -class RenamedTeacherItem extends StatelessWidget { - const RenamedTeacherItem({ - Key? key, - required this.teacher, - required this.renamedTo, - required this.modifyCallback, - required this.removeCallback, - }) : super(key: key); - - final Teacher teacher; - final String renamedTo; - final void Function() modifyCallback; - final void Function() removeCallback; - - @override - Widget build(BuildContext context) { - return ListTile( - minLeadingWidth: 32.0, - dense: true, - contentPadding: - const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), - visualDensity: VisualDensity.compact, - onTap: () {}, - leading: Icon(FeatherIcons.user, - color: AppColors.of(context).text.withOpacity(.75)), - title: InkWell( - onTap: modifyCallback, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - teacher.name.capital(), - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 14, - color: AppColors.of(context).text.withOpacity(.75)), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - Text( - renamedTo, - style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - trailing: InkWell( - onTap: removeCallback, - child: Icon(FeatherIcons.trash, - color: AppColors.of(context).red.withOpacity(.75)), - ), - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/settings/share_theme.dart b/filcnaplo_premium/lib/ui/mobile/settings/share_theme.dart deleted file mode 100644 index cbbe917..0000000 --- a/filcnaplo_premium/lib/ui/mobile/settings/share_theme.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:filcnaplo/models/settings.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class PremiumShareTheme extends StatefulWidget { - const PremiumShareTheme({Key? key}) : super(key: key); - - @override - State createState() => _PremiumShareThemeState(); -} - -class _PremiumShareThemeState extends State - with TickerProviderStateMixin { - late final SettingsProvider settingsProvider; - - @override - void initState() { - super.initState(); - settingsProvider = Provider.of(context, listen: false); - } - - @override - Widget build(BuildContext context) { - return const Scaffold(); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart b/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart deleted file mode 100644 index 456510d..0000000 --- a/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'package:filcnaplo/api/providers/user_provider.dart'; -import 'package:filcnaplo/models/settings.dart'; -import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; -import 'package:filcnaplo_premium/models/premium_scopes.dart'; -import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_feather_icons/flutter_feather_icons.dart'; -import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart'; -import 'package:provider/provider.dart'; -import 'package:i18n_extension/i18n_extension.dart'; - -// ignore: must_be_immutable -class WelcomeMessagePanelButton extends StatelessWidget { - late SettingsProvider settingsProvider; - late UserProvider user; - - WelcomeMessagePanelButton(this.settingsProvider, this.user, {Key? key}) - : super(key: key); - - @override - Widget build(BuildContext context) { - String finalName = ((user.nickname ?? '') != '' - ? user.nickname - : (user.displayName ?? '') != '' - ? user.displayName - : 'János') ?? - 'János'; - - return PanelButton( - onPressed: () { - if (!Provider.of(context, listen: false) - .hasScope(PremiumScopes.all)) { - PremiumLockedFeatureUpsell.show( - context: context, feature: PremiumFeature.profile); - return; - } - showDialog( - context: context, - builder: (context) => WelcomeMessageEditor(settingsProvider)); - }, - title: Text("welcome_msg".i18n), - leading: const Icon(FeatherIcons.smile), - trailing: Container( - constraints: const BoxConstraints(maxWidth: 100), - child: Text( - settingsProvider.welcomeMessage.replaceAll(' ', '') != '' - ? localizeFill( - settingsProvider.welcomeMessage, - [finalName], - ) - : 'default'.i18n, - style: const TextStyle(fontSize: 14.0), - textAlign: TextAlign.end, - softWrap: true, - overflow: TextOverflow.ellipsis, - ), - ), - ); - } -} - -// ignore: must_be_immutable -class WelcomeMessageEditor extends StatefulWidget { - late SettingsProvider settingsProvider; - - WelcomeMessageEditor(this.settingsProvider, {Key? key}) : super(key: key); - - @override - State createState() => _WelcomeMessageEditorState(); -} - -class _WelcomeMessageEditorState extends State { - final _welcomeMsg = TextEditingController(); - - @override - void initState() { - super.initState(); - _welcomeMsg.text = - widget.settingsProvider.welcomeMessage.replaceAll('%s', '%name%'); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text("edit_welcome_msg".i18n), - content: TextField( - controller: _welcomeMsg, - autofocus: true, - decoration: InputDecoration( - border: const OutlineInputBorder(), - label: Text('welcome_msg'.i18n), - suffixIcon: IconButton( - icon: const Icon(FeatherIcons.x), - onPressed: () { - setState(() { - _welcomeMsg.text = ""; - }); - }, - ), - ), - ), - actions: [ - TextButton( - child: Text( - "cancel".i18n, - style: const TextStyle(fontWeight: FontWeight.w500), - ), - onPressed: () { - Navigator.of(context).maybePop(); - }, - ), - TextButton( - child: Text( - "done".i18n, - style: const TextStyle(fontWeight: FontWeight.w500), - ), - onPressed: () { - // var trimmed = _welcomeMsg.text.trim(); - - // var defLen = trimmed.length; - // var replacedLen = trimmed.replaceAll('%s', '').length; - - // if (defLen - 2 > replacedLen) { - // print('fuck yourself rn'); - // } - var finalText = _welcomeMsg.text - .trim() - .replaceFirst('%name%', '\$s') - .replaceFirst('%user%', '\$s') - .replaceFirst('%username%', '\$s') - .replaceFirst('%me%', '\$s') - .replaceFirst('%profile%', '\$s') - .replaceAll('%', '') - .replaceFirst('\$s', '%s'); - // .replaceAll('\$s', 's'); - - widget.settingsProvider - .update(welcomeMessage: finalText, store: true); - Navigator.of(context).pop(true); - }, - ), - ], - ); - } -} diff --git a/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable_button.dart b/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable_button.dart deleted file mode 100644 index db5e22d..0000000 --- a/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable_button.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:filcnaplo/theme/colors/colors.dart'; -import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart'; -import 'package:filcnaplo_mobile_ui/common/system_chrome.dart'; -import 'package:filcnaplo_premium/models/premium_scopes.dart'; -import 'package:filcnaplo_premium/providers/premium_provider.dart'; -import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; -import 'package:filcnaplo_premium/ui/mobile/timetable/fs_timetable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_feather_icons/flutter_feather_icons.dart'; -import 'package:provider/provider.dart'; -import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.i18n.dart'; - -class PremiumFSTimetableButton extends StatelessWidget { - const PremiumFSTimetableButton( - {Key? key, required this.controller, required this.tabcontroller}) - : super(key: key); - - final TimetableController controller; - final TabController tabcontroller; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: IconButton( - splashRadius: 24.0, - onPressed: () { - if (!Provider.of(context, listen: false) - .hasScope(PremiumScopes.fsTimetable)) { - PremiumLockedFeatureUpsell.show( - context: context, feature: PremiumFeature.weeklytimetable); - return; - } - - // If timetable empty, show empty - if (tabcontroller.length == 0) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text("empty_timetable".i18n), - duration: const Duration(seconds: 2), - )); - return; - } - - Navigator.of(context, rootNavigator: true) - .push(PageRouteBuilder( - pageBuilder: (context, animation, secondaryAnimation) => - PremiumFSTimetable( - controller: controller, - ), - )) - .then((_) { - SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp]); - setSystemChrome(context); - }); - }, - icon: Icon(FeatherIcons.trello, color: AppColors.of(context).text), - ), - ); - } -} diff --git a/filcnaplo_premium/pubspec.yaml b/filcnaplo_premium/pubspec.yaml deleted file mode 100644 index 1244ef7..0000000 --- a/filcnaplo_premium/pubspec.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: filcnaplo_premium -publish_to: "none" - -environment: - sdk: ">=2.17.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - cupertino_icons: ^1.0.2 - - # Filcnaplo main dep - filcnaplo: - path: ../filcnaplo/ - filcnaplo_kreta_api: - path: ../filcnaplo_kreta_api/ - filcnaplo_mobile_ui: - path: "../filcnaplo_mobile_ui/" - - provider: ^5.0.0 - flutter_feather_icons: ^2.0.0+1 - uni_links: ^0.5.1 - url_launcher: ^6.1.6 - dropdown_button2: ^1.8.9 - home_widget: ^0.1.6 - image_picker: ^0.8.6 - image_crop: - git: - url: https://github.com/kimaah/image_crop.git - lottie: ^1.4.3 - animations: ^2.0.1 - flutter_svg: ^1.1.6 - -dev_dependencies: - flutter_lints: ^1.0.0 - -flutter: - uses-material-design: true