Merge pull request #80 from refilc/dev

Dev
This commit is contained in:
Márton Kiss 2023-12-25 22:09:31 +01:00 committed by GitHub
commit 588bc2567e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
230 changed files with 2241 additions and 4586 deletions

4
.gitignore vendored
View File

@ -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

3
.gitmodules vendored
View File

@ -0,0 +1,3 @@
[submodule "filcnaplo_premium"]
path = filcnaplo_premium
url = git@github.com:refilc/naplo-plus.git

View File

@ -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

View File

@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="hu.refilc.naplo">
<application android:name="${applicationName}" android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
android:requestLegacyExternalStorage="true" android:enableOnBackInvokedCallback="false">
<activity android:exported="true" android:name="hu.refilc.naplo.MainActivity"
android:launchMode="singleTop" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
@ -13,6 +13,7 @@
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<!-- commented bc of dynamic app icons -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
@ -39,6 +40,69 @@
</activity>
<meta-data android:name="flutterEmbedding" android:value="2" />
<!-- custom app icon thingies from here -->
<!-- <activity-alias
android:label="reFilc"
android:icon="@drawable/launch_ic_refilc_default"
android:name=".MainActivityAlias"
android:enabled="true"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:label="reFilc"
android:icon="@drawable/launch_ic_refilc_default"
android:name=".MainActivityAliasrefilc_default"
android:enabled="false"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:label="reFilc"
android:icon="@drawable/launch_ic_refilc_overcomplicated"
android:name=".MainActivityAliasrefilc_overcomplicated"
android:enabled="false"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:label="reFilc"
android:icon="@drawable/launch_ic_refilc_concept"
android:name=".MainActivityAliasrefilc_concept"
android:enabled="false"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:label="reFilc"
android:icon="@drawable/launch_ic_refilc_pride"
android:name=".MainActivityAliasrefilc_pride"
android:enabled="false"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias> -->
<!-- end of custom app icon thingies -->
<receiver android:name="hu.refilc.naplo.widget_timetable.WidgetTimetable"
android:exported="true">
<intent-filter>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -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 = "<group>"; };
317DE77A294F6FFB002E323E /* livecard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = livecard.entitlements; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B4461202AE6E0FF00AAE6FD /* refilc_default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_default@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_default@2x.png"; sourceTree = "<group>"; };
3B4461212AE6E0FF00AAE6FD /* refilc_concept@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_concept@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_concept@2x.png"; sourceTree = "<group>"; };
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 = "<group>"; };
3B4461232AE6E0FF00AAE6FD /* refilc_overcomplicated@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_overcomplicated@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_overcomplicated@2x.png"; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
3B4461262AE6E0FF00AAE6FD /* refilc_pride@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "refilc_pride@2x.png"; path = "../../../../../../../../../Downloads/iloveimg-resized/refilc_pride@2x.png"; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -122,6 +138,21 @@
path = livecard;
sourceTree = "<group>";
};
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 = "<group>";
};
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;
};

View File

@ -2,6 +2,59 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>refilc_pride</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>refilc_pride</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>refilc_overcomplicated</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>refilc_overcomplicated</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>refilc_concept</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>refilc_concept</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>refilc_default</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>refilc_default</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundleIcons</key>
<dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconName</key>
<string></string>
<key>CFBundleIconFiles</key>
<array>
<string></string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.fetch</string>

View File

@ -163,6 +163,8 @@ Future loginAPI({
Provider.of<ExamProvider>(context, listen: false).fetch(),
Provider.of<HomeworkProvider>(context, listen: false).fetch(),
Provider.of<MessageProvider>(context, listen: false).fetchAll(),
Provider.of<MessageProvider>(context, listen: false)
.fetchAllRecipients(),
Provider.of<NoteProvider>(context, listen: false).fetch(),
Provider.of<EventProvider>(context, listen: false).fetch(),
Provider.of<AbsenceProvider>(context, listen: false).fetch(),

View File

@ -34,7 +34,8 @@ Future<void> syncAll(BuildContext context) {
print("INFO Syncing all");
UserProvider user = Provider.of<UserProvider>(context, listen: false);
StatusProvider statusProvider = Provider.of<StatusProvider>(context, listen: false);
StatusProvider statusProvider =
Provider.of<StatusProvider>(context, listen: false);
List<Future<void>> tasks = [];
int taski = 0;
@ -47,10 +48,14 @@ Future<void> syncAll(BuildContext context) {
tasks = [
syncStatus(Provider.of<GradeProvider>(context, listen: false).fetch()),
syncStatus(Provider.of<TimetableProvider>(context, listen: false).fetch(week: Week.current())),
syncStatus(Provider.of<TimetableProvider>(context, listen: false)
.fetch(week: Week.current())),
syncStatus(Provider.of<ExamProvider>(context, listen: false).fetch()),
syncStatus(Provider.of<HomeworkProvider>(context, listen: false).fetch(from: DateTime.now().subtract(const Duration(days: 30)))),
syncStatus(Provider.of<HomeworkProvider>(context, listen: false)
.fetch(from: DateTime.now().subtract(const Duration(days: 30)))),
syncStatus(Provider.of<MessageProvider>(context, listen: false).fetchAll()),
syncStatus(
Provider.of<MessageProvider>(context, listen: false).fetchAllRecipients()),
syncStatus(Provider.of<NoteProvider>(context, listen: false).fetch()),
syncStatus(Provider.of<EventProvider>(context, listen: false).fetch()),
syncStatus(Provider.of<AbsenceProvider>(context, listen: false).fetch()),
@ -58,14 +63,17 @@ Future<void> syncAll(BuildContext context) {
// Sync student
syncStatus(() async {
if (user.user == null) return;
Map? studentJson = await Provider.of<KretaClient>(context, listen: false).getAPI(KretaAPI.student(user.instituteCode!));
Map? studentJson = await Provider.of<KretaClient>(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<DatabaseProvider>(context, listen: false).store.storeUser(user.user!);
await Provider.of<DatabaseProvider>(context, listen: false)
.store
.storeUser(user.user!);
}()),
];

View File

@ -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) {

View File

@ -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<Database> 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": "{}",

View File

@ -122,6 +122,19 @@ class UserDatabaseQuery {
return messages;
}
Future<List<SendRecipient>> getRecipients({required String userId}) async {
List<Map> 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<SendRecipient> recipients = (jsonDecode(recipientsJson) as List)
.map((e) =>
SendRecipient.fromJson(e, SendRecipientType.fromJson(e['tipus'])))
.toList();
return recipients;
}
Future<List<Note>> getNotes({required String userId}) async {
List<Map> userData =
await db.query("user_data", where: "id = ?", whereArgs: [userId]);

View File

@ -86,6 +86,13 @@ class UserDatabaseStore {
where: "id = ?", whereArgs: [userId]);
}
Future<void> storeRecipients(List<SendRecipient> 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<void> storeNotes(List<Note> notes, {required String userId}) async {
String notesJson = jsonEncode(notes.map((e) => e.json).toList());
await db.update("user_data", {"notes": notesJson},

View File

@ -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<String> download(BuildContext context, {bool overwrite = false}) async {
Future<String> 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<KretaClient>(context, listen: false).getAPI(downloadUrl, rawResponse: true);
Uint8List data = await Provider.of<KretaClient>(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<String> download(BuildContext context, {bool overwrite = false}) async {
Future<String> 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<UserProvider>(context, listen: false).instituteCode ?? "");
Uint8List data = await Provider.of<KretaClient>(context, listen: false).getAPI(url, rawResponse: true);
String url = downloadUrl(
Provider.of<UserProvider>(context, listen: false).instituteCode ?? "");
Uint8List data = await Provider.of<KretaClient>(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;
}
}

View File

@ -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

View File

@ -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<

View File

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

View File

@ -35,6 +35,7 @@ import 'package:provider/provider.dart';
const List<FilterType> homeFilters = [
FilterType.all,
FilterType.grades,
FilterType.exams,
FilterType.messages,
FilterType.absences
];

View File

@ -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<Color> onColorChanged;

View File

@ -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<FilcColorPicker> {
class FilcColorPickerState extends State<FilcColorPicker> {
final idController = TextEditingController();
late final ShareProvider shareProvider;

View File

@ -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<Color> onColorChanged;
@ -330,10 +331,10 @@ class ColorPickerInput extends StatefulWidget {
final bool disable;
@override
_ColorPickerInputState createState() => _ColorPickerInputState();
ColorPickerInputState createState() => ColorPickerInputState();
}
class _ColorPickerInputState extends State<ColorPickerInput> {
class ColorPickerInputState extends State<ColorPickerInput> {
TextEditingController textEditingController = TextEditingController();
int inputColor = 0;
@ -346,11 +347,7 @@ class _ColorPickerInputState extends State<ColorPickerInput> {
@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<HSVColor> onColorChanged;

View File

@ -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';

View File

@ -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;

View File

@ -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;

View File

@ -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<Message>? messages;

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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<GradeSubjectView> {
// Providers
late GradeProvider gradeProvider;
late GradeCalculatorProvider calculatorProvider;
late SettingsProvider settingsProvider;
late double average;
late Widget gradeGraph;
@ -142,6 +144,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
Widget build(BuildContext context) {
gradeProvider = Provider.of<GradeProvider>(context);
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
settingsProvider = Provider.of<SettingsProvider>(context);
List<Grade> subjectGrades = getSubjectGrades(widget.subject).toList();
average = AverageHelper.averageEvals(subjectGrades);
@ -244,7 +247,8 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
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(

View File

@ -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<GradesPage> {
int avgDropValue = 0;
List<Grade> 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<Grade> 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<GradeSubject> subjects = gradeProvider.grades
@ -144,7 +145,8 @@ class _GradesPageState extends State<GradesPage> {
Expanded(
child: StatisticsTile(
outline: true,
title: AutoSizeText( // https://discord.com/channels/1111649116020285532/1153397476578050130
title: AutoSizeText(
// https://discord.com/channels/1111649116020285532/1153397476578050130
"classavg".i18n,
textAlign: TextAlign.center,
maxLines: 2,
@ -208,7 +210,7 @@ class _GradesPageState extends State<GradesPage> {
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
PremiumAverageSelector(
AverageSelector(
value: avgDropValue,
onChanged: (value) {
setState(() {

View File

@ -57,9 +57,9 @@ class _LoginScreenState extends State<LoginScreen> {
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<LoginScreen> {
),
),
SchoolInput(
scroll: _scrollController,
controller: schoolController,
),
scroll: _scrollController,
controller: schoolController,
),
],
),
),
@ -321,8 +321,8 @@ class _LoginScreenState extends State<LoginScreen> {
if (username == "" ||
password ==
"" /*||
schoolController.selectedSchool == null*/
"" ||
schoolController.selectedSchool == null
) {
return setState(() => _loginState = LoginState.missingFields);
}
@ -332,8 +332,7 @@ class _LoginScreenState extends State<LoginScreen> {
loginAPI(
username: username,
password: password,
instituteCode: 'shit',
// instituteCode: schoolController.selectedSchool!.instituteCode,
instituteCode: schoolController.selectedSchool!.instituteCode,
context: context,
onLogin: (user) {
ElegantNotification.success(

View File

@ -62,6 +62,8 @@ class _SidebarState extends State<Sidebar> {
Provider.of<ExamProvider>(context, listen: false).restore(),
Provider.of<HomeworkProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false)
.restoreRecipients(),
Provider.of<NoteProvider>(context, listen: false).restore(),
Provider.of<EventProvider>(context, listen: false).restore(),
Provider.of<AbsenceProvider>(context, listen: false).restore(),

View File

@ -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<SettingsScreen>
Provider.of<ExamProvider>(context, listen: false).restore(),
Provider.of<HomeworkProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false)
.restoreRecipients(),
Provider.of<NoteProvider>(context, listen: false).restore(),
Provider.of<EventProvider>(context, listen: false).restore(),
Provider.of<AbsenceProvider>(context, listen: false).restore(),
@ -400,9 +401,9 @@ class _SettingsScreenState extends State<SettingsScreen>
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<SettingsScreen>
.secondary,
),
),
const PremiumIconPackSelector(),
// we need icon pack selector here
// const PremiumIconPackSelector(),
],
),
),

View File

@ -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";

View File

@ -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<dynamic> sendFilesAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
Map<String, String>? body,
}) async {
Map<String, String> 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<void> refreshLogin() async {
if (_loginRefreshing) return;
_loginRefreshing = true;

View File

@ -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<Lesson> _day = List.castFrom(days[i]);
List<Lesson> day0 = List.castFrom(days[i]);
List<int> lessonIndexes = _getIndexes(_day);
List<int> 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<int>.generate(
maxIndex - minIndex + 1, (int i) => minIndex + i)) {
List<Lesson> indexLessons = _getLessonsByIndex(_day, i);
List<Lesson> 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;
}

View File

@ -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';

View File

@ -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((Map recipient) => Recipient.fromJson(recipient)).toList(),
attachments: (message["csatolmanyok"] as List).cast<Map>().map((Map attachment) => Attachment.fromJson(attachment)).toList(),
recipients: (message["cimzettLista"] as List)
.cast<Map>()
.map((Map recipient) => Recipient.fromJson(recipient))
.toList(),
attachments: (message["csatolmanyok"] as List)
.cast<Map>()
.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<Attachment>? attachments;
List<SendRecipient> 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 }

View File

@ -1,3 +1,5 @@
// ignore_for_file: no_leading_underscores_for_local_identifiers
import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart';
class Week {

View File

@ -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<UserProvider>(_context, listen: false).user!.id)
: {};
Map<String, String> renamedTeachers =
(await _database.query.getSettings(_database)).renamedTeachersEnabled
? await _database.userQuery.renamedTeachers(
userId:
// ignore: use_build_context_synchronously
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};

View File

@ -38,17 +38,19 @@ class ExamProvider with ChangeNotifier {
// for renamed subjects
Future<void> convertBySettings() async {
final _database = Provider.of<DatabaseProvider>(_context, listen: false);
final database = Provider.of<DatabaseProvider>(_context, listen: false);
Map<String, String> 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<UserProvider>(_context, listen: false).user!.id)
: {};
Map<String, String> 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<UserProvider>(_context, listen: false).user!.id)
: {};

View File

@ -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<Message> _messages;
late List<SendRecipient> _recipients;
late BuildContext _context;
List<Message> get messages => _messages;
List<SendRecipient> get recipients => _recipients;
MessageProvider({
List<Message> initialMessages = const [],
required BuildContext context,
}) {
_messages = List.castFrom(initialMessages);
_recipients = [];
_context = context;
if (_messages.isEmpty) restore();
if (_recipients.isEmpty) restoreRecipients();
}
Future<void> restore() async {
@ -27,27 +36,33 @@ class MessageProvider with ChangeNotifier {
// Load messages from the database
if (userId != null) {
var dbMessages = await Provider.of<DatabaseProvider>(_context, listen: false).userQuery.getMessages(userId: userId);
var dbMessages =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getMessages(userId: userId);
_messages = dbMessages;
notifyListeners();
}
}
// Fetches all types of Messages
Future<void> fetchAll() => Future.forEach(MessageType.values, (MessageType v) => fetch(type: v));
Future<void> fetchAll() =>
Future.forEach(MessageType.values, (MessageType v) => fetch(type: v));
// Fetches Messages from the Kreta API then stores them in the database
Future<void> 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<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Messages for User null";
// Get messages
List? messagesJson = await Provider.of<KretaClient>(_context, listen: false).getAPI(KretaAPI.messages(messageType));
List? messagesJson = await Provider.of<KretaClient>(_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<Map>()[index];
Map? messageJson = await Provider.of<KretaClient>(_context, listen: false).getAPI(KretaAPI.message(message["azonosito"].toString()));
if (messageJson != null) messages.add(Message.fromJson(messageJson, forceType: type));
Map? messageJson =
await Provider.of<KretaClient>(_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<DatabaseProvider>(_context, listen: false).userStore.storeMessages(_messages, userId: userId);
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeMessages(_messages, userId: userId);
notifyListeners();
}
// restore recipients
Future<void> restoreRecipients() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load messages from the database
if (userId != null) {
var dbRecipients =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getRecipients(userId: userId);
_recipients = dbRecipients;
notifyListeners();
}
}
// fetch all recipients
Future<void> fetchAllRecipients() => Future.forEach(
AddresseeType.values, (AddresseeType v) => fetchRecipients(type: v));
// fetch recipients
Future<void> fetchRecipients(
{AddresseeType type = AddresseeType.teachers}) async {
Map<AddresseeType, SendRecipientType> addressable = {};
// check user
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Messages for User null";
// get categories
List? availableCategoriesJson =
await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.recipientCategories);
// print(availableCategoriesJson);
// get recipients
List? recipientTeachersJson =
await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.recipientTeachers);
List? recipientDirectorateJson =
await Provider.of<KretaClient>(_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<SendRecipient> 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<void> storeRecipients(
List<SendRecipient> 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<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Recipients for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeRecipients(_recipients, userId: userId);
notifyListeners();
}
// send message
Future<String?> sendMessage({
required List<SendRecipient> recipients,
String subject = "Nincs tárgy",
required String messageText,
}) async {
List<Object> recipientList = [];
User? user = Provider.of<UserProvider>(_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<String, String> headers = {
"content-type": "application/json",
};
var res = await Provider.of<KretaClient>(_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';
}
}

View File

@ -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}');

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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),
),
),
);
}

View File

@ -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;

View File

@ -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<Widget> items;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -17,7 +17,7 @@ List<String> 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,

View File

@ -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<Widget> 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<FilterBar> {
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<FilterBar> {
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<FilterBar> {
// 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);

View File

@ -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<HeroScrollView> {
class HeroScrollViewState extends State<HeroScrollView> {
late ScrollController _scrollController;
bool showBarTitle = false;
@ -69,6 +68,7 @@ class _HeroScrollViewState extends State<HeroScrollView> {
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<HeroScrollView> {
),
),
],
),
duration: const Duration(milliseconds: 200)),
)),
leading: BackButton(
color: AppColors.of(context).text,
onPressed: () {

View File

@ -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,
),
);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<ExamProvider>(context, listen: false).restore(),
Provider.of<HomeworkProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false)
.restoreRecipients(),
Provider.of<NoteProvider>(context, listen: false).restore(),
Provider.of<EventProvider>(context, listen: false).restore(),
Provider.of<AbsenceProvider>(context, listen: false).restore(),

View File

@ -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<ProfileImage> {
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<ProfileImage> {
),
),
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(

View File

@ -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;

View File

@ -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) {

View File

@ -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());

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:i18n_extension/i18n_widget.dart';
class TrendDisplay<T extends num> 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;

View File

@ -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<Viewable> 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<T> extends PopupRoute<T> {
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<T> extends PopupRoute<T> {
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<Widget>? 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<Widget> actions;

View File

@ -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;

View File

@ -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),

View File

@ -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;

View File

@ -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(

View File

@ -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;

View File

@ -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<AbsenceGroupContainer>();

View File

@ -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<AbsenceViewable> absences;
final bool showDate;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<Grade> grades;
final GradeType gradeType;

View File

@ -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;

View File

@ -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<Grade> 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(),
)));
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

Some files were not shown because too many files have changed in this diff Show More