4
.gitignore
vendored
@ -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
@ -0,0 +1,3 @@
|
||||
[submodule "filcnaplo_premium"]
|
||||
path = filcnaplo_premium
|
||||
url = git@github.com:refilc/naplo-plus.git
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 13 KiB |
BIN
filcnaplo/assets/launch_icons/refilc_concept.png
Normal file
After Width: | Height: | Size: 145 KiB |
BIN
filcnaplo/assets/launch_icons/refilc_default.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
filcnaplo/assets/launch_icons/refilc_overcomplicated.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
filcnaplo/assets/launch_icons/refilc_pride.png
Normal file
After Width: | Height: | Size: 133 KiB |
@ -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;
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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(),
|
||||
|
@ -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!);
|
||||
}()),
|
||||
];
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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": "{}",
|
||||
|
@ -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]);
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import 'package:provider/provider.dart';
|
||||
const List<FilterType> homeFilters = [
|
||||
FilterType.all,
|
||||
FilterType.grades,
|
||||
FilterType.exams,
|
||||
FilterType.messages,
|
||||
FilterType.absences
|
||||
];
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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';
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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(() {
|
||||
|
@ -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(
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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 }
|
||||
|
@ -1,3 +1,5 @@
|
||||
// ignore_for_file: no_leading_underscores_for_local_identifiers
|
||||
|
||||
import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart';
|
||||
|
||||
class Week {
|
||||
|
@ -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)
|
||||
: {};
|
||||
|
||||
|
@ -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)
|
||||
: {};
|
||||
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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}');
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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: () {
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|