Compare commits

...

71 Commits
4.2.3 ... 4.3.1

Author SHA1 Message Date
Márton Kiss
acbffd6d72 Merge pull request #69 from refilc/dev
69-es szamu pr haha 😏
2023-10-08 17:00:13 +02:00
Kima
b19da9cb66 finally fixed kreten error shit sdfadfsdfhsdfgh 2023-10-08 16:55:06 +02:00
Kima
fd80aec51f added upgrade-pub.sh to upgrade pub packages 2023-10-08 16:24:59 +02:00
Kima
5776622276 settings account tiles now showing nickname 2023-10-08 12:44:45 +02:00
Kima
69ea3e1a7f update version string 2023-10-08 12:03:08 +02:00
Kima
3d451f6f91 fixed the fix lol 2023-10-08 11:52:39 +02:00
Kima
387e2e46db fixed i18n error spam on home screen (welcome msg) 2023-10-08 11:51:09 +02:00
Kima
a7f1681902 someone commented birthday greeting, fuck yourself 2023-10-08 11:48:09 +02:00
Kima
4b127bdf56 fixed homewrok not showing up 2023-10-08 11:46:00 +02:00
Kima
dbb6125ab5 fixed live card not showing on android 2023-10-08 11:42:27 +02:00
Kima
b6d4ac6168 maybe fixed error shit 2023-10-08 11:33:09 +02:00
Kima
9d20d08838 fixed goal planner done popup 2023-10-07 19:41:33 +02:00
Kima
f39ac80770 fixed app bricking with welcome message thing 2023-10-05 21:54:57 +02:00
Kima
41991b3b66 change log message bc livecard is shit but idk why 2023-10-05 21:30:24 +02:00
ReinerRego
53ec3e567a Merge pull request #68 from TMarccci/dev
Xcode 15, iOS 17 - Bug Fix+Others
2023-10-05 00:07:41 +02:00
Tihanyi Marcell
176608bfc5 Xcode 15, iOS 17 - Bug Fix+Others 2023-10-04 23:25:00 +02:00
ReinerRego
92b4094550 started fixing desktop ui 2023-10-04 14:10:17 +02:00
Márton Kiss
45c6fccb89 Merge pull request #66 from TMarccci/master
Added a few subjects to helper
2023-09-28 21:37:56 +02:00
Tihanyi Marcell
014329d7fe Added a few subjects to helper 2023-09-28 21:28:30 +02:00
Márton Kiss
dca01d0ed0 Merge pull request #65 from sleddev/dev
timetable italics + instituteCode search + theme preview background color
2023-09-28 21:17:50 +02:00
SledDev
493adaa5f2 fixed background color in theme preview 2023-09-28 20:15:09 +02:00
SledDev
8451983163 find schools by instituteCode 2023-09-28 19:41:43 +02:00
SledDev
2b7e143812 fixed fullscreen timetable italics 2023-09-28 19:12:56 +02:00
Pearoo
9845a35c6e Fix english absence error + comment bugreports 2023-09-28 18:17:24 +02:00
Márton Kiss
b92ac9ffbc Merge pull request #63 from TMarccci/master
oopsie
2023-09-27 23:17:42 +02:00
Tihanyi Marcell
f435f31523 oopsie 2023-09-27 23:16:52 +02:00
Márton Kiss
6abc61db7b Merge pull request #62 from TMarccci/master
Live Activity Fixes
2023-09-27 23:06:39 +02:00
Márton Kiss
cd8ab8bfdb Merge pull request #61 from refilc/dev
Dev
2023-09-27 23:04:56 +02:00
Kima
24a6393aca changed version number 2023-09-27 23:04:21 +02:00
Kima
64fb22de74 grade colors sharing works fine ahh 2023-09-27 23:02:28 +02:00
Kima
14ce4c82fb fixed test account crash 2023-09-27 21:56:19 +02:00
Tihanyi Marcell
7d1f4e992c Fixes 2023-09-27 21:47:24 +02:00
Kima
17b8b96767 commented shit in desktop code 2023-09-27 21:38:48 +02:00
Kima
7ac5d64d19 Merge branch 'dev' of github.com:refilc/naplo into dev 2023-09-27 21:36:21 +02:00
Kima
d9bd555e6a added grade colors to theme share 2023-09-27 21:36:18 +02:00
Márton Kiss
b5bf994b8d Merge pull request #59 from refilc/dev
Dev
2023-09-26 23:01:10 +02:00
ReinerRego
8b216bd27b desktop fix 2023-09-26 22:48:52 +02:00
Kima
a9bd11a4d7 fixed profile crash 2023-09-26 22:11:27 +02:00
ReinerRego
129a95f359 Merge branch 'dev' of github.com:refilc/naplo into dev 2023-09-26 21:54:42 +02:00
ReinerRego
8b65d4d519 desktop login fix 2023-09-26 21:53:58 +02:00
Kima
353297bc90 changed version string 2023-09-26 21:16:19 +02:00
Kima
63d24f7e89 removed dev releases from auto-update 2023-09-26 21:01:47 +02:00
Márton Kiss
4b93c891c8 Merge pull request #58 from refilc/dev
Dev
2023-09-26 20:32:21 +02:00
Kima
6f85a4ebc1 fixed settings top padding 2023-09-26 18:04:44 +02:00
Kima
b37af9f5a5 fixed kreten status shit maybe idk 2023-09-25 22:46:24 +02:00
ReinerRego
3a26fd9ec9 changed the fucking linix title 2023-09-25 22:45:28 +02:00
Kima
9cda8c5d29 added custom welcome message to home screen 2023-09-25 18:31:32 +02:00
Kima
6212489001 added translation for dkt 2023-09-25 18:30:20 +02:00
Kima
b4ff2fa815 added welcome message to settings 2023-09-24 20:12:10 +02:00
Kima
a11ebce6fb also added test accounts to dev branch 2023-09-22 21:50:26 +02:00
Kima
fa75c1ec06 fixed share alert text 2023-09-22 20:52:13 +02:00
Kima
79677d657e updated version string 2023-09-19 19:12:28 +02:00
Kima
50d1803a18 added token revoke to logout 2023-09-19 18:58:08 +02:00
Kima
34f9929b16 removed "add spaces" text bc not relevant anymore 2023-09-19 18:57:57 +02:00
Pearoo
7b517b333a Revert "Rename everything filcnaplo-related to refilc"
This reverts commit d1a9625d93.
2023-09-19 18:16:57 +02:00
Pearoo
d1a9625d93 Rename everything filcnaplo-related to refilc 2023-09-19 18:16:03 +02:00
Kima
056bf7ab58 made ad tile image circular 2023-09-18 20:33:20 +02:00
Kima
c4dc03f41d fixed warns and timetable thing again 2023-09-18 20:22:42 +02:00
Kima
6d7a21dc02 fixed theme share warn dialog 2023-09-18 19:42:36 +02:00
Márton Kiss
9406efee8d Merge pull request #54 from refilc/master
master back to dev bc idiots modified things in it
2023-09-18 19:37:33 +02:00
Márton Kiss
151e97b243 Merge pull request #53 from refilc/dev
Dev
2023-09-18 19:36:20 +02:00
Kima
1ba0f4d8d3 handle timetable shit better ig 2023-09-18 19:33:29 +02:00
Kima
4b8dbf0691 Merge branch 'dev' of github.com:refilc/naplo into dev 2023-09-18 19:30:01 +02:00
Kima
98d7468f24 tried to fix timetable and handle kreten api error 2023-09-18 19:29:55 +02:00
ReinerRego
fa46d6cbd3 Merge branch 'dev' of github.com:refilc/naplo into dev 2023-09-18 19:20:35 +02:00
Márton Kiss
a0c68a4e5a nem 2023-09-18 17:53:10 +02:00
Pearoo
f2f35b2fbd Add smurfcat 2023-09-18 10:18:04 +02:00
Pearoo
a782dbd22f Add smurfcat 2023-09-18 10:16:58 +02:00
ReinerRego
6293bb4017 aligment again 2023-09-17 17:05:04 +02:00
ReinerRego
03a5057269 changed aligment bc stupid and color 2023-09-17 17:03:07 +02:00
ReinerRego
4a15514ba0 changed dependency idk 2023-09-17 17:01:54 +02:00
57 changed files with 1078 additions and 241 deletions

View File

@@ -136,7 +136,7 @@
android:layout_below="@id/iv_1" android:layout_below="@id/iv_1"
android:layout_marginHorizontal="15dp" android:layout_marginHorizontal="15dp"
android:fontFamily="@font/montserrat_medium" android:fontFamily="@font/montserrat_medium"
android:text="A widget használatához, bejelentkezés szükséges." android:text="A widget használatához bejelentkezés szükséges."
android:textColor="@color/black" android:textColor="@color/black"
android:paddingTop="10dp" android:paddingTop="10dp"
android:textSize="17sp" android:textSize="17sp"

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
# platform :ios, '11.0' platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -39,5 +39,8 @@ end
post_install do |installer| post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target) flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
end
end end
end end

View File

@@ -221,9 +221,9 @@
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3127F74F28EAEC8A00C2EFB3 /* Embed Foundation Extensions */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
71459C0EB905E05018E3D78F /* [CP] Embed Pods Frameworks */, 71459C0EB905E05018E3D78F /* [CP] Embed Pods Frameworks */,
3127F74F28EAEC8A00C2EFB3 /* Embed Foundation Extensions */,
); );
buildRules = ( buildRules = (
); );
@@ -478,7 +478,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 195; CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 48XS7JAZB7; DEVELOPMENT_TEAM = 4DKAF249F3;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = reFilc; INFOPLIST_KEY_CFBundleDisplayName = reFilc;
@@ -488,7 +488,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -510,7 +510,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 48XS7JAZB7; DEVELOPMENT_TEAM = 4DKAF249F3;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = livecard/Info.plist; INFOPLIST_FILE = livecard/Info.plist;
@@ -526,7 +526,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -552,7 +552,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 48XS7JAZB7; DEVELOPMENT_TEAM = 4DKAF249F3;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = livecard/Info.plist; INFOPLIST_FILE = livecard/Info.plist;
@@ -567,7 +567,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -592,7 +592,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 48XS7JAZB7; DEVELOPMENT_TEAM = 4DKAF249F3;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = livecard/Info.plist; INFOPLIST_FILE = livecard/Info.plist;
@@ -607,7 +607,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -736,7 +736,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 195; CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 48XS7JAZB7; DEVELOPMENT_TEAM = 4DKAF249F3;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = reFilc; INFOPLIST_KEY_CFBundleDisplayName = reFilc;
@@ -746,7 +746,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -764,7 +764,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 195; CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 48XS7JAZB7; DEVELOPMENT_TEAM = 4DKAF249F3;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = reFilc; INFOPLIST_KEY_CFBundleDisplayName = reFilc;
@@ -774,7 +774,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

View File

@@ -6,7 +6,7 @@
<string>development</string> <string>development</string>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.refilcnaplo.livecard</string> <string>group.refilc2.livecard</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@@ -14,7 +14,7 @@ class LessonData {
var nextRoom: String var nextRoom: String
init?() { init?() {
let sharedDefault = UserDefaults(suiteName: "group.refilc.livecard")! let sharedDefault = UserDefaults(suiteName: "group.refilc2.livecard")!
self.color = sharedDefault.string(forKey: "color")! self.color = sharedDefault.string(forKey: "color")!
self.icon = sharedDefault.string(forKey: "icon")! self.icon = sharedDefault.string(forKey: "icon")!

View File

@@ -6,7 +6,7 @@
<string>development</string> <string>development</string>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.refilcnaplo.livecard</string> <string>group.refilc2.livecard</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@@ -91,14 +91,15 @@ struct LockScreenLiveActivityView: View {
Text(timerInterval: lesson!.date, countsDown: true) Text(timerInterval: lesson!.date, countsDown: true)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.frame(width: 85) .frame(width: 85)
.font(.title) .font(.title2)
.monospacedDigit() .monospacedDigit()
.padding(.trailing, CGFloat(24)) .padding(.trailing, CGFloat(24))
} }
.activityBackgroundTint( .activityBackgroundTint(
lesson!.color == "#676767" lesson!.color != "#676767"
? nil ? Color(hex: lesson!.color)
: Color(hex: lesson!.color) // Ha nem megy hat nem megy
: Color.clear
) )
} }
} }
@@ -118,12 +119,22 @@ struct LiveCardWidget: Widget {
DynamicIslandExpandedRegion(.leading) { DynamicIslandExpandedRegion(.leading) {
VStack { VStack {
Spacer() Spacer()
ProgressView(
timerInterval: lesson!.date,
countsDown: true,
label: {
Image(systemName: lesson!.icon) Image(systemName: lesson!.icon)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: CGFloat(30), height: CGFloat(30)) .frame(width: CGFloat(32), height: CGFloat(32))
.padding(.leading, CGFloat(6)) },
.padding(.bottom, CGFloat(6)) currentValueLabel: {
Image(systemName: lesson!.icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: CGFloat(32), height: CGFloat(32))
}
).progressViewStyle(.circular)
} }
} }
DynamicIslandExpandedRegion(.center) { DynamicIslandExpandedRegion(.center) {
@@ -136,7 +147,7 @@ struct LiveCardWidget: Widget {
Text(lesson!.description) Text(lesson!.description)
.lineLimit(2) .lineLimit(2)
.font(.caption) .font(.caption)
} }.padding(EdgeInsets(top: 0.0, leading: 5.0, bottom: 0.0, trailing: 0.0))
} }
DynamicIslandExpandedRegion(.trailing) { DynamicIslandExpandedRegion(.trailing) {
VStack { VStack {
@@ -156,7 +167,8 @@ struct LiveCardWidget: Widget {
Image(systemName: lesson!.icon) Image(systemName: lesson!.icon)
} }
.font(.caption2) .font(.caption2)
} compactTrailing: { }
compactTrailing: {
Text(timerInterval: lesson!.date, countsDown: true) Text(timerInterval: lesson!.date, countsDown: true)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.frame(width: 40) .frame(width: 40)
@@ -164,22 +176,29 @@ struct LiveCardWidget: Widget {
/// Collapsed /// Collapsed
} minimal: { } minimal: {
VStack(alignment: .center) { VStack(alignment: .center, content: {
ProgressView(
timerInterval: lesson!.date,
countsDown: true,
label: {
Image(systemName: lesson!.icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: CGFloat(12), height: CGFloat(12))
},
currentValueLabel: {
Image(systemName: lesson!.icon) Image(systemName: lesson!.icon)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: CGFloat(12), height: CGFloat(12)) .frame(width: CGFloat(12), height: CGFloat(12))
Text(timerInterval: lesson!.date, countsDown: true)
.multilineTextAlignment(.center)
.monospacedDigit()
.font(.system(size: CGFloat(10)))
} }
).progressViewStyle(.circular)
})
} }
.keylineTint( .keylineTint(
lesson!.color == "#676767" lesson!.color != "#676767"
? nil ? Color(hex: lesson!.color)
: Color(hex: lesson!.color) : Color.clear
) )
} }
} }

View File

@@ -40,6 +40,11 @@ class FilcAPI {
static const allThemes = "$themeGet/all"; static const allThemes = "$themeGet/all";
static const themeByID = "$themeGet/"; static const themeByID = "$themeGet/";
static const gradeColorsShare = "$baseUrl/v2/shared/grade-colors/add";
static const gradeColorsGet = "$baseUrl/v2/shared/grade-colors/get";
static const allGradeColors = "$gradeColorsGet/all";
static const gradeColorsByID = "$gradeColorsGet/";
static Future<bool> checkConnectivity() async => static Future<bool> checkConnectivity() async =>
(await Connectivity().checkConnectivity()) != ConnectivityResult.none; (await Connectivity().checkConnectivity()) != ConnectivityResult.none;
@@ -53,9 +58,14 @@ class FilcAPI {
.map((json) => School.fromJson(json)) .map((json) => School.fromJson(json))
.toList(); .toList();
schools.add(School( schools.add(School(
city: "Tiszabura", city: "Stockholm",
instituteCode: "supporttest-reni-tiszabura-teszt01", instituteCode: "refilc-test-sweden",
name: "FILC Éles Reni tiszabura-teszt", name: "reFilc Test SE - Leo Ekström High School",
));
schools.add(School(
city: "Madrid",
instituteCode: "refilc-test-spain",
name: "reFilc Test ES - Emilio Obrero University",
)); ));
return schools; return schools;
} else { } else {
@@ -204,6 +214,9 @@ class FilcAPI {
theme.json['panels_color'] = theme.panelsColor.value.toString(); theme.json['panels_color'] = theme.panelsColor.value.toString();
theme.json['accent_color'] = theme.accentColor.value.toString(); theme.json['accent_color'] = theme.accentColor.value.toString();
// set linked grade colors
theme.json['grade_colors_id'] = theme.gradeColors.id;
http.Response res = await http.post( http.Response res = await http.post(
Uri.parse(themeShare), Uri.parse(themeShare),
body: theme.json, body: theme.json,
@@ -230,7 +243,49 @@ class FilcAPI {
throw "HTTP ${res.statusCode}: ${res.body}"; throw "HTTP ${res.statusCode}: ${res.body}";
} }
} on Exception catch (error, stacktrace) { } on Exception catch (error, stacktrace) {
log("ERROR: FilcAPI.addSharedTheme: $error $stacktrace"); log("ERROR: FilcAPI.getSharedTheme: $error $stacktrace");
}
return null;
}
static Future<void> addSharedGradeColors(
SharedGradeColors gradeColors) async {
try {
gradeColors.json.remove('json');
gradeColors.json['is_public'] = gradeColors.isPublic.toString();
gradeColors.json['five_color'] = gradeColors.fiveColor.value.toString();
gradeColors.json['four_color'] = gradeColors.fourColor.value.toString();
gradeColors.json['three_color'] = gradeColors.threeColor.value.toString();
gradeColors.json['two_color'] = gradeColors.twoColor.value.toString();
gradeColors.json['one_color'] = gradeColors.oneColor.value.toString();
http.Response res = await http.post(
Uri.parse(gradeColorsShare),
body: gradeColors.json,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
);
if (res.statusCode != 201) {
throw "HTTP ${res.statusCode}: ${res.body}";
}
log('Shared grade colors successfully with ID: ${gradeColors.id}');
} on Exception catch (error, stacktrace) {
log("ERROR: FilcAPI.addSharedGradeColors: $error $stacktrace");
}
}
static Future<Map?> getSharedGradeColors(String id) async {
try {
http.Response res = await http.get(Uri.parse(gradeColorsByID + id));
if (res.statusCode == 200) {
return (jsonDecode(res.body) as Map);
} else {
throw "HTTP ${res.statusCode}: ${res.body}";
}
} on Exception catch (error, stacktrace) {
log("ERROR: FilcAPI.getSharedGradeColors: $error $stacktrace");
} }
return null; return null;
} }

View File

@@ -1,6 +1,7 @@
// ignore_for_file: avoid_print, use_build_context_synchronously // ignore_for_file: avoid_print, use_build_context_synchronously
import 'package:filcnaplo/utils/jwt.dart'; import 'package:filcnaplo/utils/jwt.dart';
import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/event_provider.dart'; import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart'; import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
@@ -20,6 +21,7 @@ import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:filcnaplo/api/nonce.dart'; import 'package:filcnaplo/api/nonce.dart';
import 'package:uuid/uuid.dart';
enum LoginState { enum LoginState {
missingFields, missingFields,
@@ -47,6 +49,58 @@ Future loginAPI({
void Function(User)? onLogin, void Function(User)? onLogin,
void Function()? onSuccess, void Function()? onSuccess,
}) async { }) async {
Future testLogin(School school) async {
var user = User(
username: username,
password: password,
instituteCode: instituteCode,
name: 'Teszt Lajos',
student: Student(
birth: DateTime.now(),
id: const Uuid().v4(),
name: 'Teszt Lajos',
school: school,
yearId: '1',
parents: ['Teszt András', 'Teszt Linda'],
json: {"a": "b"},
address: '1117 Budapest, Gábor Dénes utca 4.',
),
role: Role.parent,
);
if (onLogin != null) onLogin(user);
// store test user in db
await Provider.of<DatabaseProvider>(context, listen: false)
.store
.storeUser(user);
Provider.of<UserProvider>(context, listen: false).addUser(user);
Provider.of<UserProvider>(context, listen: false).setUser(user.id);
if (onSuccess != null) onSuccess();
return LoginState.success;
}
// if institute matches one of test things do test login
if (instituteCode == 'refilc-test-sweden') {
School school = School(
city: "Stockholm",
instituteCode: "refilc-test-sweden",
name: "reFilc Test SE - Leo Ekström High School",
);
await testLogin(school);
} else if (instituteCode == 'refilc-test-spain') {
School school = School(
city: "Madrid",
instituteCode: "refilc-test-spain",
name: "reFilc Test ES - Emilio Obrero University",
);
await testLogin(school);
} else {
// normal login from here
Provider.of<KretaClient>(context, listen: false).userAgent = Provider.of<KretaClient>(context, listen: false).userAgent =
Provider.of<SettingsProvider>(context, listen: false).config.userAgent; Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
@@ -129,5 +183,7 @@ Future loginAPI({
} }
} }
} }
}
return LoginState.failed; return LoginState.failed;
} }

View File

@@ -51,17 +51,18 @@ class LiveCardProvider extends ChangeNotifier {
_liveActivitiesPlugin.areActivitiesEnabled().then((value) { _liveActivitiesPlugin.areActivitiesEnabled().then((value) {
// Console log // Console log
if (kDebugMode) { if (kDebugMode) {
print("Live card enabled: $value"); print("iOS LiveActivity enabled: $value");
} }
if (value) { if (value) {
_liveActivitiesPlugin.init(appGroupId: "group.refilc.livecard"); _liveActivitiesPlugin.init(appGroupId: "group.refilc2.livecard");
_liveActivitiesPlugin.getAllActivitiesIds().then((value) { _liveActivitiesPlugin.getAllActivitiesIds().then((value) {
_latestActivityId = value.isNotEmpty ? value.first : null; _latestActivityId = value.isNotEmpty ? value.first : null;
}); });
} }
}); });
}
_timer = Timer.periodic(const Duration(seconds: 1), (timer) => update()); _timer = Timer.periodic(const Duration(seconds: 1), (timer) => update());
_delay = settings.bellDelayEnabled _delay = settings.bellDelayEnabled
@@ -69,7 +70,6 @@ class LiveCardProvider extends ChangeNotifier {
: Duration.zero; : Duration.zero;
update(); update();
} }
}
@override @override
void dispose() { void dispose() {
@@ -88,7 +88,7 @@ class LiveCardProvider extends ChangeNotifier {
// Debugging // Debugging
static DateTime _now() { static DateTime _now() {
// return DateTime(2023, 8, 31, 8, 0); // return DateTime(2023, 9, 27, 9, 30);
return DateTime.now(); return DateTime.now();
} }
@@ -120,7 +120,7 @@ class LiveCardProvider extends ChangeNotifier {
"index": "index":
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "", currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
"title": currentLesson != null "title": currentLesson != null
? ShortSubject.resolve(subject: currentLesson?.subject).capital() ? currentLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: currentLesson?.subject).capital()
: "", : "",
"subtitle": currentLesson?.room.replaceAll("_", " ") ?? "", "subtitle": currentLesson?.room.replaceAll("_", " ") ?? "",
"description": currentLesson?.description ?? "", "description": currentLesson?.description ?? "",
@@ -131,7 +131,7 @@ class LiveCardProvider extends ChangeNotifier {
_delay.inMilliseconds) _delay.inMilliseconds)
.toString(), .toString(),
"nextSubject": nextLesson != null "nextSubject": nextLesson != null
? ShortSubject.resolve(subject: nextLesson?.subject).capital() ? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "", : "",
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "", "nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
}; };
@@ -160,7 +160,7 @@ class LiveCardProvider extends ChangeNotifier {
_delay.inMilliseconds) _delay.inMilliseconds)
.toString(), .toString(),
"nextSubject": (nextLesson != null "nextSubject": (nextLesson != null
? ShortSubject.resolve(subject: nextLesson?.subject) ? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "") : "")
.capital(), .capital(),
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "", "nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
@@ -196,7 +196,7 @@ class LiveCardProvider extends ChangeNotifier {
} }
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('ERROR: Unable to create or update iOS LiveCard!'); print('ERROR: Unable to create or update iOS LiveActivity!');
} }
} }
} }

View File

@@ -1,8 +1,10 @@
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
enum Status { network, maintenance, syncing } enum Status { network, maintenance, syncing, apiError }
class StatusProvider extends ChangeNotifier { class StatusProvider extends ChangeNotifier {
final List<Status> _stack = []; final List<Status> _stack = [];
@@ -12,6 +14,7 @@ class StatusProvider extends ChangeNotifier {
StatusProvider() { StatusProvider() {
_handleNetworkChanges(); _handleNetworkChanges();
_handleDNSFailure();
Connectivity().checkConnectivity().then((value) => _networkType = value); Connectivity().checkConnectivity().then((value) => _networkType = value);
} }
@@ -24,6 +27,7 @@ class StatusProvider extends ChangeNotifier {
_networkType = event; _networkType = event;
if (event == ConnectivityResult.none) { if (event == ConnectivityResult.none) {
if (!_stack.contains(Status.network)) { if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network); _stack.insert(0, Status.network);
notifyListeners(); notifyListeners();
} }
@@ -36,8 +40,34 @@ class StatusProvider extends ChangeNotifier {
}); });
} }
void _handleDNSFailure() {
try {
InternetAddress.lookup('api.refilc.hu').then((status) {
if (status.isEmpty) {
if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network);
notifyListeners();
}
} else {
if (_stack.contains(Status.network)) {
_stack.remove(Status.network);
notifyListeners();
}
}
});
} on SocketException catch (_) {
if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network);
notifyListeners();
}
}
}
void triggerRequest(http.Response res) { void triggerRequest(http.Response res) {
if (res.headers.containsKey("x-maintenance-mode") || res.statusCode == 503) { if (res.headers.containsKey("x-maintenance-mode") ||
res.statusCode == 503) {
if (!_stack.contains(Status.maintenance)) { if (!_stack.contains(Status.maintenance)) {
_stack.insert(0, Status.maintenance); _stack.insert(0, Status.maintenance);
notifyListeners(); notifyListeners();
@@ -48,6 +78,23 @@ class StatusProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
} }
if (res.body == 'invalid_grant' ||
res.body.replaceAll(' ', '') == '' ||
res.statusCode == 400) {
if (!_stack.contains(Status.apiError) &&
!_stack.contains(Status.network)) {
if (res.statusCode == 401) return;
_stack.insert(0, Status.apiError);
notifyListeners();
}
} else {
if (_stack.contains(Status.apiError) &&
res.request?.url.path != '/nonce') {
_stack.remove(Status.apiError);
notifyListeners();
}
}
} }
void triggerSync({required int current, required int max}) { void triggerSync({required int current, required int max}) {

View File

@@ -33,9 +33,12 @@ class UpdateProvider extends ChangeNotifier {
// Check for new releases // Check for new releases
if (_releases.isNotEmpty) { if (_releases.isNotEmpty) {
if (!_releases.first.prerelease) {
_available = _releases.first.version _available = _releases.first.version
.compareTo(Version.fromString(currentVersion)) == .compareTo(Version.fromString(currentVersion)) ==
1; 1;
}
// ignore: avoid_print // ignore: avoid_print
if (_available) print("INFO: New update: ${releases.first.version}"); if (_available) print("INFO: New update: ${releases.first.version}");
notifyListeners(); notifyListeners();

View File

@@ -131,6 +131,7 @@ class App extends StatelessWidget {
create: (context) => HomeworkProvider( create: (context) => HomeworkProvider(
context: context, context: context,
database: database, database: database,
user: user,
), ),
), ),
ChangeNotifierProvider<MessageProvider>( ChangeNotifierProvider<MessageProvider>(

View File

@@ -33,6 +33,7 @@ const settingsDB = DatabaseStruct("settings", {
"renamed_subjects_italics": int, "renamed_teachers_enabled": int, "renamed_subjects_italics": int, "renamed_teachers_enabled": int,
"renamed_teachers_italics": int, "renamed_teachers_italics": int,
"live_activity_color": String, "live_activity_color": String,
"welcome_message": String,
}); });
// DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING // DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING
// YOU'VE BEEN WARNED!!! // YOU'VE BEEN WARNED!!!

View File

@@ -195,7 +195,7 @@ class NotificationsHelper {
if (userProvider.getUsers().length == 1) { if (userProvider.getUsers().length == 1) {
await flutterLocalNotificationsPlugin.show( await flutterLocalNotificationsPlugin.show(
absence.id.hashCode, absence.id.hashCode,
"title_absence".i18n, "title_absence".i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528
"body_absence".i18n.fill( "body_absence".i18n.fill(
[ [
DateFormat("yyyy-MM-dd").format(absence.date), DateFormat("yyyy-MM-dd").format(absence.date),
@@ -210,7 +210,7 @@ class NotificationsHelper {
} else { } else {
await flutterLocalNotificationsPlugin.show( await flutterLocalNotificationsPlugin.show(
absence.id.hashCode, absence.id.hashCode,
"title_absence".i18n, "title_absence".i18n, // https://discord.com/channels/1111649116020285532/1153273625206591528
"body_absence_multiuser".i18n.fill( "body_absence_multiuser".i18n.fill(
[ [
userProvider.displayName!, userProvider.displayName!,

View File

@@ -106,6 +106,12 @@ class SubjectIcon {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.globe, material: Icons.translate_outlined), name: "globe"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.globe, material: Icons.translate_outlined), name: "globe");
} else if (RegExp("linux").hasMatch(name)) { } else if (RegExp("linux").hasMatch(name)) {
return SubjectIconData(data: createIcon(material: FilcIcons.linux, cupertino: FilcIcons.linux)); return SubjectIconData(data: createIcon(material: FilcIcons.linux, cupertino: FilcIcons.linux));
} else if (RegExp("adatbazis").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.table_badge_more, material: Icons.table_chart), name: "table.badge.more");
} else if (RegExp("asztali alkalmazasok").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.macwindow, material: Icons.desktop_windows_outlined), name: "macwindow");
} else if (RegExp("projekt").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.person_3_fill, material: Icons.groups_3), name: "person.3.fill");
} }
return SubjectIconData(); return SubjectIconData();

View File

@@ -20,7 +20,6 @@ class Ad {
}); });
factory Ad.fromJson(Map json) { factory Ad.fromJson(Map json) {
print(json);
return Ad( return Ad(
title: json['title'] ?? 'Ad', title: json['title'] ?? 'Ad',
description: json['description'] ?? '', description: json['description'] ?? '',

View File

@@ -76,6 +76,7 @@ class SettingsProvider extends ChangeNotifier {
bool _renamedTeachersEnabled; bool _renamedTeachersEnabled;
bool _renamedTeachersItalics; bool _renamedTeachersItalics;
Color _liveActivityColor; Color _liveActivityColor;
String _welcomeMessage;
SettingsProvider({ SettingsProvider({
DatabaseProvider? database, DatabaseProvider? database,
@@ -120,6 +121,7 @@ class SettingsProvider extends ChangeNotifier {
required bool renameTeachersEnabled, required bool renameTeachersEnabled,
required bool renameTeachersItalics, required bool renameTeachersItalics,
required Color liveActivityColor, required Color liveActivityColor,
required String welcomeMessage,
}) : _database = database, }) : _database = database,
_language = language, _language = language,
_startPage = startPage, _startPage = startPage,
@@ -161,7 +163,8 @@ class SettingsProvider extends ChangeNotifier {
_renamedSubjectsItalics = renameSubjectsItalics, _renamedSubjectsItalics = renameSubjectsItalics,
_renamedTeachersEnabled = renameTeachersEnabled, _renamedTeachersEnabled = renameTeachersEnabled,
_renamedTeachersItalics = renameTeachersItalics, _renamedTeachersItalics = renameTeachersItalics,
_liveActivityColor = liveActivityColor; _liveActivityColor = liveActivityColor,
_welcomeMessage = welcomeMessage;
factory SettingsProvider.fromMap(Map map, factory SettingsProvider.fromMap(Map map,
{required DatabaseProvider database}) { {required DatabaseProvider database}) {
@@ -223,6 +226,7 @@ class SettingsProvider extends ChangeNotifier {
renameTeachersEnabled: map["renamed_teachers_enabled"] == 1, renameTeachersEnabled: map["renamed_teachers_enabled"] == 1,
renameTeachersItalics: map["renamed_teachers_italics"] == 1, renameTeachersItalics: map["renamed_teachers_italics"] == 1,
liveActivityColor: Color(map["live_activity_color"]), liveActivityColor: Color(map["live_activity_color"]),
welcomeMessage: map["welcome_message"],
); );
} }
@@ -272,6 +276,7 @@ class SettingsProvider extends ChangeNotifier {
"renamed_teachers_enabled": _renamedTeachersEnabled ? 1 : 0, "renamed_teachers_enabled": _renamedTeachersEnabled ? 1 : 0,
"renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0, "renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0,
"live_activity_color": _liveActivityColor.value, "live_activity_color": _liveActivityColor.value,
"welcome_message": _welcomeMessage,
}; };
} }
@@ -325,6 +330,7 @@ class SettingsProvider extends ChangeNotifier {
renameTeachersEnabled: false, renameTeachersEnabled: false,
renameTeachersItalics: false, renameTeachersItalics: false,
liveActivityColor: const Color(0xFF676767), liveActivityColor: const Color(0xFF676767),
welcomeMessage: '',
); );
} }
@@ -373,6 +379,7 @@ class SettingsProvider extends ChangeNotifier {
bool get renamedTeachersEnabled => _renamedTeachersEnabled; bool get renamedTeachersEnabled => _renamedTeachersEnabled;
bool get renamedTeachersItalics => _renamedTeachersItalics; bool get renamedTeachersItalics => _renamedTeachersItalics;
Color get liveActivityColor => _liveActivityColor; Color get liveActivityColor => _liveActivityColor;
String get welcomeMessage => _welcomeMessage;
Future<void> update({ Future<void> update({
bool store = true, bool store = true,
@@ -417,6 +424,7 @@ class SettingsProvider extends ChangeNotifier {
bool? renamedTeachersEnabled, bool? renamedTeachersEnabled,
bool? renamedTeachersItalics, bool? renamedTeachersItalics,
Color? liveActivityColor, Color? liveActivityColor,
String? welcomeMessage,
}) async { }) async {
if (language != null && language != _language) _language = language; if (language != null && language != _language) _language = language;
if (startPage != null && startPage != _startPage) _startPage = startPage; if (startPage != null && startPage != _startPage) _startPage = startPage;
@@ -535,6 +543,9 @@ class SettingsProvider extends ChangeNotifier {
if (liveActivityColor != null && liveActivityColor != _liveActivityColor) { if (liveActivityColor != null && liveActivityColor != _liveActivityColor) {
_liveActivityColor = liveActivityColor; _liveActivityColor = liveActivityColor;
} }
if (welcomeMessage != null && welcomeMessage != _welcomeMessage) {
_welcomeMessage = welcomeMessage;
}
if (store) await _database?.store.storeSettings(this); if (store) await _database?.store.storeSettings(this);
notifyListeners(); notifyListeners();
} }

View File

@@ -8,6 +8,7 @@ class SharedTheme {
Color backgroundColor; Color backgroundColor;
Color panelsColor; Color panelsColor;
Color accentColor; Color accentColor;
SharedGradeColors gradeColors;
SharedTheme({ SharedTheme({
required this.json, required this.json,
@@ -17,9 +18,10 @@ class SharedTheme {
required this.backgroundColor, required this.backgroundColor,
required this.panelsColor, required this.panelsColor,
required this.accentColor, required this.accentColor,
required this.gradeColors,
}); });
factory SharedTheme.fromJson(Map json) { factory SharedTheme.fromJson(Map json, SharedGradeColors gradeColors) {
return SharedTheme( return SharedTheme(
json: json, json: json,
id: json['public_id'], id: json['public_id'],
@@ -28,6 +30,45 @@ class SharedTheme {
backgroundColor: Color(json['background_color']), backgroundColor: Color(json['background_color']),
panelsColor: Color(json['panels_color']), panelsColor: Color(json['panels_color']),
accentColor: Color(json['accent_color']), accentColor: Color(json['accent_color']),
gradeColors: gradeColors,
);
}
}
class SharedGradeColors {
Map json;
String id;
bool isPublic;
String nickname;
Color fiveColor;
Color fourColor;
Color threeColor;
Color twoColor;
Color oneColor;
SharedGradeColors({
required this.json,
required this.id,
this.isPublic = false,
this.nickname = 'Anonymous',
required this.fiveColor,
required this.fourColor,
required this.threeColor,
required this.twoColor,
required this.oneColor,
});
factory SharedGradeColors.fromJson(Map json) {
return SharedGradeColors(
json: json,
id: json['public_id'],
isPublic: json['is_public'] ?? false,
nickname: json['nickname'] ?? 'Anonymous',
fiveColor: Color(json['five_color']),
fourColor: Color(json['four_color']),
threeColor: Color(json['three_color']),
twoColor: Color(json['two_color']),
oneColor: Color(json['one_color']),
); );
} }
} }

View File

@@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:filcnaplo_kreta_api/client/api.dart'; import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:filcnaplo_kreta_api/models/student.dart'; import 'package:filcnaplo_kreta_api/models/student.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@@ -43,7 +44,16 @@ class User {
username: map["username"], username: map["username"],
password: map["password"], password: map["password"],
name: map["name"].trim(), name: map["name"].trim(),
student: Student.fromJson(jsonDecode(map["student"])), student: map["student"] != 'null'
? Student.fromJson(jsonDecode(map["student"]))
: Student(
id: const Uuid().v4(),
name: 'Ismeretlen Diák',
school: School(instituteCode: '', name: '', city: ''),
birth: DateTime.now(),
yearId: '1',
parents: [],
),
role: Role.values[map["role"] ?? 0], role: Role.values[map["role"] ?? 0],
nickname: map["nickname"] ?? "", nickname: map["nickname"] ?? "",
picture: map["picture"] ?? "", picture: map["picture"] ?? "",
@@ -93,4 +103,13 @@ class User {
"refresh_user_data": "false", "refresh_user_data": "false",
}; };
} }
static Map<String, Object?> logoutBody({
required String refreshToken,
}) {
return {
"refresh_token": refreshToken,
"client_id": KretaAPI.clientId,
};
}
} }

View File

@@ -101,6 +101,7 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData,
gradeProvider.grades, gradeProvider.lastSeenDate); gradeProvider.grades, gradeProvider.lastSeenDate);
if (settingsProvider.gradeOpeningFun) { if (settingsProvider.gradeOpeningFun) {
items.addAll( items.addAll(
// ignore: use_build_context_synchronously
await getFilterWidgets(FilterType.newGrades, context: context)); await getFilterWidgets(FilterType.newGrades, context: context));
} }
break; break;

View File

@@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) { if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar)); gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "Filc Napló"); gtk_header_bar_set_title(header_bar, "reFilc");
gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else { } else {
gtk_window_set_title(window, "Filc Napló"); gtk_window_set_title(window, "reFilc");
} }
gtk_window_set_default_size(window, 1280, 720); gtk_window_set_default_size(window, 1280, 720);

View File

@@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
homepage: https://refilc.hu homepage: https://refilc.hu
publish_to: "none" publish_to: "none"
version: 4.2.3+223 version: 4.3.1+230
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"
@@ -44,7 +44,7 @@ dependencies:
quick_actions: ^1.0.1 quick_actions: ^1.0.1
animated_list_plus: ^0.5.0 animated_list_plus: ^0.5.0
dynamic_color: ^1.2.2 dynamic_color: ^1.2.2
material_color_utilities: ^0.2.0 material_color_utilities: ^0.5.0
crypto: ^3.0.2 crypto: ^3.0.2
elegant_notification: ^1.6.1 elegant_notification: ^1.6.1
flutter_feather_icons: ^2.0.0+1 flutter_feather_icons: ^2.0.0+1

View File

@@ -144,7 +144,7 @@ class _GradesPageState extends State<GradesPage> {
Expanded( Expanded(
child: StatisticsTile( child: StatisticsTile(
outline: true, outline: true,
title: AutoSizeText( title: AutoSizeText( // https://discord.com/channels/1111649116020285532/1153397476578050130
"classavg".i18n, "classavg".i18n,
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, maxLines: 2,

View File

@@ -7,7 +7,7 @@ import 'package:filcnaplo/api/client.dart';
import 'package:filcnaplo/api/login.dart'; import 'package:filcnaplo/api/login.dart';
import 'package:filcnaplo_mobile_ui/screens/login/login_button.dart'; import 'package:filcnaplo_mobile_ui/screens/login/login_button.dart';
import 'package:filcnaplo_mobile_ui/screens/login/login_input.dart'; import 'package:filcnaplo_mobile_ui/screens/login/login_input.dart';
import 'package:filcnaplo_mobile_ui/screens/login/school_input/school_input.dart'; // import 'package:filcnaplo_desktop_ui/screens/login/school_input/school_input.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart'; import 'package:flutter_acrylic/flutter_acrylic.dart';
@@ -15,11 +15,11 @@ import 'login_screen.i18n.dart';
const LinearGradient _backgroundGradient = LinearGradient( const LinearGradient _backgroundGradient = LinearGradient(
colors: [ colors: [
Color.fromARGB(255, 0, 0, 0), Color.fromARGB(255, 61, 122, 244),
Color.fromARGB(255, 23, 77, 185), Color.fromARGB(255, 23, 77, 185),
Color.fromARGB(255, 7, 42, 112), Color.fromARGB(255, 7, 42, 112),
], ],
begin: Alignment(-0.8, -2), begin: Alignment(-0.8, -2.0),
end: Alignment(0.8, 1.0), end: Alignment(0.8, 1.0),
stops: [-1.0, 0.0, 1.0], stops: [-1.0, 0.0, 1.0],
); );
@@ -36,8 +36,8 @@ class LoginScreen extends StatefulWidget {
class _LoginScreenState extends State<LoginScreen> { class _LoginScreenState extends State<LoginScreen> {
final usernameController = TextEditingController(); final usernameController = TextEditingController();
final passwordController = TextEditingController(); final passwordController = TextEditingController();
final schoolController = SchoolInputController(); // final schoolController = SchoolInputController();
final _scrollController = ScrollController(); // final _scrollController = ScrollController();
LoginState _loginState = LoginState.normal; LoginState _loginState = LoginState.normal;
bool showBack = false; bool showBack = false;
@@ -57,9 +57,9 @@ class _LoginScreenState extends State<LoginScreen> {
FilcAPI.getSchools().then((schools) { FilcAPI.getSchools().then((schools) {
if (schools != null) { if (schools != null) {
schoolController.update(() { // schoolController.update(() {
schoolController.schools = schools; // schoolController.schools = schools;
}); // });
} else { } else {
ElegantNotification.error( ElegantNotification.error(
background: Colors.white, background: Colors.white,
@@ -90,7 +90,7 @@ class _LoginScreenState extends State<LoginScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: Container( body: Container(
decoration: BoxDecoration( decoration: const BoxDecoration(
gradient: _backgroundGradient, gradient: _backgroundGradient,
), ),
child: SafeArea( child: SafeArea(
@@ -238,10 +238,10 @@ class _LoginScreenState extends State<LoginScreen> {
), ),
), ),
), ),
SchoolInput( // SchoolInput(
scroll: _scrollController, // scroll: _scrollController,
controller: schoolController, // controller: schoolController,
), // ),
], ],
), ),
), ),
@@ -320,8 +320,10 @@ class _LoginScreenState extends State<LoginScreen> {
String password = passwordController.text; String password = passwordController.text;
if (username == "" || if (username == "" ||
password == "" || password ==
schoolController.selectedSchool == null) { "" /*||
schoolController.selectedSchool == null*/
) {
return setState(() => _loginState = LoginState.missingFields); return setState(() => _loginState = LoginState.missingFields);
} }
@@ -330,7 +332,8 @@ class _LoginScreenState extends State<LoginScreen> {
loginAPI( loginAPI(
username: username, username: username,
password: password, password: password,
instituteCode: schoolController.selectedSchool!.instituteCode, instituteCode: 'shit',
// instituteCode: schoolController.selectedSchool!.instituteCode,
context: context, context: context,
onLogin: (user) { onLogin: (user) {
ElegantNotification.success( ElegantNotification.success(

View File

@@ -0,0 +1,122 @@
import 'package:filcnaplo_mobile_ui/screens/login/login_input.dart';
import 'package:filcnaplo_mobile_ui/screens/login/school_input/school_input_overlay.dart';
import 'package:filcnaplo_desktop_ui/screens/login/school_input/school_input_tile.dart';
import 'package:filcnaplo_mobile_ui/screens/login/school_input/school_search.dart';
import 'package:flutter/material.dart';
import 'package:filcnaplo_kreta_api/models/school.dart';
class SchoolInput extends StatefulWidget {
const SchoolInput({Key? key, required this.controller, required this.scroll})
: super(key: key);
final SchoolInputController controller;
final ScrollController scroll;
@override
_SchoolInputState createState() => _SchoolInputState();
}
class _SchoolInputState extends State<SchoolInput> {
final _focusNode = FocusNode();
final _layerLink = LayerLink();
late SchoolInputOverlay overlay;
@override
void initState() {
super.initState();
widget.controller.update = (fn) {
if (mounted) setState(fn);
};
overlay = SchoolInputOverlay(layerLink: _layerLink);
// Show school list when focused
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
WidgetsBinding.instance
.addPostFrameCallback((_) => overlay.createOverlayEntry(context));
Future.delayed(const Duration(milliseconds: 100)).then((value) {
if (mounted && widget.scroll.hasClients) {
widget.scroll.animateTo(widget.scroll.offset + 500,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
}
});
} else {
overlay.entry?.remove();
}
});
// LoginInput TextField listener
widget.controller.textController.addListener(() {
String text = widget.controller.textController.text;
if (text.isEmpty) {
overlay.children = null;
return;
}
List<School> results =
searchSchools(widget.controller.schools ?? [], text);
setState(() {
overlay.children = results
.map((School e) => SchoolInputTile(
school: e,
onTap: () => _selectSchool(e),
))
.toList();
});
Overlay.of(context).setState(() {});
});
}
void _selectSchool(School school) {
FocusScope.of(context).requestFocus(FocusNode());
setState(() {
widget.controller.selectedSchool = school;
widget.controller.textController.text = school.name;
});
}
@override
Widget build(BuildContext context) {
return CompositedTransformTarget(
link: _layerLink,
child: widget.controller.schools == null
? Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.15),
borderRadius: BorderRadius.circular(12.0),
),
child: const Center(
child: SizedBox(
height: 28.0,
width: 28.0,
child: CircularProgressIndicator(
color: Colors.white,
),
),
),
)
: LoginInput(
style: LoginInputStyle.school,
focusNode: _focusNode,
onClear: () {
widget.controller.selectedSchool = null;
FocusScope.of(context).requestFocus(_focusNode);
},
controller: widget.controller.textController,
),
);
}
}
class SchoolInputController {
final textController = TextEditingController();
School? selectedSchool;
List<School>? schools;
late void Function(void Function()) update;
}

View File

@@ -0,0 +1,65 @@
import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:flutter/material.dart';
class SchoolInputTile extends StatelessWidget {
const SchoolInputTile({Key? key, required this.school, this.onTap})
: super(key: key);
final School school;
final Function()? onTap;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: GestureDetector(
onPanDown: (e) {
onTap!();
},
child: InkWell(
onTapDown: (e) {},
borderRadius: BorderRadius.circular(6.0),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// School name
Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(
school.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
Row(
children: [
// School id
Expanded(
child: Text(
school.instituteCode,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
// School city
Expanded(
child: Text(
school.city,
textAlign: TextAlign.right,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
],
),
),
),
),
);
}
}

View File

@@ -63,8 +63,12 @@ class NavigationScreenState extends State<NavigationScreen>
await Window.initialize(); await Window.initialize();
} catch (_) {} } catch (_) {}
// Transparent sidebar // Transparent sidebar
if (Platform.isLinux) return;
await Window.setEffect( await Window.setEffect(
effect: WindowEffect.acrylic, effect: Platform.isLinux
? WindowEffect.transparent
: WindowEffect.acrylic,
color: Platform.isMacOS color: Platform.isMacOS
? Colors.transparent ? Colors.transparent
: const Color.fromARGB(27, 27, 27, 27)); : const Color.fromARGB(27, 27, 27, 27));

View File

@@ -176,13 +176,16 @@ class _SidebarState extends State<Sidebar> {
String? userId = user.id; String? userId = user.id;
if (userId == null) return; if (userId == null) return;
// Delete User // revoke refresh token
await Provider.of<KretaClient>(context, listen: false).logout();
// delete user from app
user.removeUser(userId); user.removeUser(userId);
await Provider.of<DatabaseProvider>(context, listen: false) await Provider.of<DatabaseProvider>(context, listen: false)
.store .store
.removeUser(userId); .removeUser(userId);
// If no other Users left, go back to LoginScreen // if no other users left, go back to login screen
if (user.getUsers().isNotEmpty) { if (user.getUsers().isNotEmpty) {
user.setUser(user.getUsers().first.id); user.setUser(user.getUsers().first.id);
restore().then((_) => user.setUser(user.getUsers().first.id)); restore().then((_) => user.setUser(user.getUsers().first.id));

View File

@@ -5,7 +5,7 @@ extension SettingsLocalization on String {
{ {
"en_en": { "en_en": {
"personal_details": "Personal Details", "personal_details": "Personal Details",
"open_dkt": "Open DKT", "open_dkt": "Open DCS",
"edit_nickname": "Edit Nickname", "edit_nickname": "Edit Nickname",
"edit_profile_picture": "Edit Profile Picture", "edit_profile_picture": "Edit Profile Picture",
"remove_profile_picture": "Remove Profile Picture", "remove_profile_picture": "Remove Profile Picture",
@@ -39,7 +39,8 @@ extension SettingsLocalization on String {
"done": "Done", "done": "Done",
"reset": "Reset", "reset": "Reset",
"open": "Open", "open": "Open",
"data_collected": "Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier", "data_collected":
"Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier",
"Analytics": "Analytics", "Analytics": "Analytics",
"Anonymous Usage Analytics": "Anonymous Usage Analytics", "Anonymous Usage Analytics": "Anonymous Usage Analytics",
"graph_class_avg": "Class average on graph", "graph_class_avg": "Class average on graph",
@@ -98,7 +99,8 @@ extension SettingsLocalization on String {
"done": "Kész", "done": "Kész",
"reset": "Visszaállítás", "reset": "Visszaállítás",
"open": "Megnyitás", "open": "Megnyitás",
"data_collected": "Gyűjtött adat: Platform (pl. Android), App verzió (pl. 3.0.0), Egyedi telepítési azonosító", "data_collected":
"Gyűjtött adat: Platform (pl. Android), App verzió (pl. 3.0.0), Egyedi telepítési azonosító",
"Analytics": "Analitika", "Analytics": "Analitika",
"Anonymous Usage Analytics": "Névtelen használati analitika", "Anonymous Usage Analytics": "Névtelen használati analitika",
"graph_class_avg": "Osztályátlag a grafikonon", "graph_class_avg": "Osztályátlag a grafikonon",
@@ -123,7 +125,7 @@ extension SettingsLocalization on String {
}, },
"de_de": { "de_de": {
"personal_details": "Persönliche Angaben", "personal_details": "Persönliche Angaben",
"open_dkt": "Öffnen DKT", "open_dkt": "Öffnen RDZ",
"edit_nickname": "Spitznamen bearbeiten", "edit_nickname": "Spitznamen bearbeiten",
"edit_profile_picture": "Profilbild bearbeiten", "edit_profile_picture": "Profilbild bearbeiten",
"remove_profile_picture": "Profilbild entfernen", "remove_profile_picture": "Profilbild entfernen",
@@ -157,7 +159,8 @@ extension SettingsLocalization on String {
"done": "Fertig", "done": "Fertig",
"reset": "Zurücksetzen", "reset": "Zurücksetzen",
"open": "Öffnen", "open": "Öffnen",
"data_collected": "Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung", "data_collected":
"Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung",
"Analytics": "Analytik", "Analytics": "Analytik",
"Anonymous Usage Analytics": "Anonyme Nutzungsanalyse", "Anonymous Usage Analytics": "Anonyme Nutzungsanalyse",
"graph_class_avg": "Klassendurchschnitt in der Grafik", "graph_class_avg": "Klassendurchschnitt in der Grafik",

View File

@@ -3,6 +3,7 @@ import 'package:intl/intl.dart';
class KretaAPI { class KretaAPI {
// IDP API // IDP API
static const login = BaseKreta.kretaIdp + KretaApiEndpoints.token; static const login = BaseKreta.kretaIdp + KretaApiEndpoints.token;
static const logout = BaseKreta.kretaIdp + KretaApiEndpoints.revoke;
static const nonce = BaseKreta.kretaIdp + KretaApiEndpoints.nonce; static const nonce = BaseKreta.kretaIdp + KretaApiEndpoints.nonce;
static const clientId = "kreta-ellenorzo-mobile-android"; static const clientId = "kreta-ellenorzo-mobile-android";
@@ -86,6 +87,7 @@ class BaseKreta {
class KretaApiEndpoints { class KretaApiEndpoints {
static const token = "/connect/token"; static const token = "/connect/token";
static const revoke = "/connect/revocation";
static const nonce = "/nonce"; static const nonce = "/nonce";
static const notes = "/ellenorzo/V3/Sajat/Feljegyzesek"; static const notes = "/ellenorzo/V3/Sajat/Feljegyzesek";
static const events = "/ellenorzo/V3/Sajat/FaliujsagElemek"; static const events = "/ellenorzo/V3/Sajat/FaliujsagElemek";

View File

@@ -26,6 +26,8 @@ class KretaClient {
late final UserProvider _user; late final UserProvider _user;
late final StatusProvider _status; late final StatusProvider _status;
bool _loginRefreshing = false;
KretaClient({ KretaClient({
this.accessToken, this.accessToken,
required SettingsProvider settings, required SettingsProvider settings,
@@ -89,6 +91,9 @@ class KretaClient {
} }
if (res == null) throw "Login error"; if (res == null) throw "Login error";
if (res.body == 'invalid_grant' || res.body.replaceAll(' ', '') == '') {
throw "Auth error";
}
if (json) { if (json) {
return jsonDecode(res.body); return jsonDecode(res.body);
@@ -161,6 +166,9 @@ class KretaClient {
} }
Future<void> refreshLogin() async { Future<void> refreshLogin() async {
if (_loginRefreshing) return;
_loginRefreshing = true;
User? loginUser = _user.user; User? loginUser = _user.user;
if (loginUser == null) return; if (loginUser == null) return;
@@ -179,13 +187,15 @@ class KretaClient {
print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}"); print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}");
} }
Map? loginRes = await postAPI(KretaAPI.login, Map? loginRes = await postAPI(
KretaAPI.login,
headers: headers, headers: headers,
body: User.loginBody( body: User.loginBody(
username: loginUser.username, username: loginUser.username,
password: loginUser.password, password: loginUser.password,
instituteCode: loginUser.instituteCode, instituteCode: loginUser.instituteCode,
)); ),
);
if (loginRes != null) { if (loginRes != null) {
if (loginRes.containsKey("access_token")) { if (loginRes.containsKey("access_token")) {
@@ -212,5 +222,25 @@ class KretaClient {
} }
} }
} }
_loginRefreshing = false;
}
Future<void> logout() async {
User? loginUser = _user.user;
if (loginUser == null) return;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
};
await postAPI(
KretaAPI.logout,
headers: headers,
body: User.logoutBody(
refreshToken: refreshToken!,
),
json: false,
);
} }
} }

View File

@@ -5,6 +5,7 @@ import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo_kreta_api/client/api.dart'; import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart'; import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -23,10 +24,12 @@ class HomeworkProvider with ChangeNotifier {
List<Homework> initialHomework = const [], List<Homework> initialHomework = const [],
required BuildContext context, required BuildContext context,
required DatabaseProvider database, required DatabaseProvider database,
required UserProvider user,
}) { }) {
_homework = List.castFrom(initialHomework); _homework = List.castFrom(initialHomework);
_context = context; _context = context;
_database = database; _database = database;
_user = user;
if (_homework.isEmpty) restore(); if (_homework.isEmpty) restore();
} }
@@ -83,7 +86,10 @@ class HomeworkProvider with ChangeNotifier {
// error fetcing homework (unknown error) // error fetcing homework (unknown error)
} }
if (homeworkJson == null) throw "Cannot fetch Homework for User ${user.id}"; if (homeworkJson == null) {
if (kDebugMode) print("Cannot fetch Homework for User ${user.id}");
return;
}
List<Homework> homework = []; List<Homework> homework = [];
await Future.forEach(homeworkJson.cast<Map>(), (Map hw) async { await Future.forEach(homeworkJson.cast<Map>(), (Map hw) async {

View File

@@ -5,7 +5,7 @@ import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart'; import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_kreta_api/models/week.dart'; import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart';
class TimetableProvider with ChangeNotifier { class TimetableProvider with ChangeNotifier {
Map<Week, List<Lesson>> lessons = {}; Map<Week, List<Lesson>> lessons = {};
@@ -67,16 +67,22 @@ class TimetableProvider with ChangeNotifier {
String iss = user.instituteCode; String iss = user.instituteCode;
List? lessonsJson = await _kreta List? lessonsJson = await _kreta
.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end)); .getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
if (lessonsJson == null) throw "Cannot fetch Lessons for User ${user.id}";
List<Lesson> lessonsList = lessonsJson.map((e) => Lesson.fromJson(e)).toList();
if (lessons.isEmpty && lessons.isEmpty) return; if (lessonsJson == null) {
if (kDebugMode) print('Cannot fetch Lessons for User ${user.id}');
return;
// throw "Cannot fetch Lessons for User ${user.id}";
} else {
List<Lesson> lessonsList =
lessonsJson.map((e) => Lesson.fromJson(e)).toList();
lessons[week] = lessonsList; lessons[week] = lessonsList;
await store(); await store();
await convertBySettings(); await convertBySettings();
} }
}
// Stores Lessons in the database // Stores Lessons in the database
Future<void> store() async { Future<void> store() async {

View File

@@ -127,7 +127,7 @@ class AbsenceView extends StatelessWidget {
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
onPressed: () { onPressed: () { // https://discord.com/channels/1111649116020285532/1149964760130002945
Navigator.of(context).pop(); Navigator.of(context).pop();
if (outsideContext != null) { if (outsideContext != null) {

View File

@@ -13,9 +13,6 @@ class AdTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print('geic');
print(ad);
return Padding( return Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0), padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
child: PanelButton( child: PanelButton(
@@ -38,12 +35,15 @@ class AdTile extends StatelessWidget {
], ],
), ),
leading: ad.logoUrl != null leading: ad.logoUrl != null
? Image.network( ? ClipRRect(
borderRadius: BorderRadius.circular(50.0),
child: Image.network(
ad.logoUrl.toString(), ad.logoUrl.toString(),
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
ad.logoUrl = null; ad.logoUrl = null;
return const SizedBox(); return const SizedBox();
}, },
),
) )
: null, : null,
trailing: const Icon(FeatherIcons.externalLink), trailing: const Icon(FeatherIcons.externalLink),

View File

@@ -57,7 +57,7 @@ class ChangedLessonTile extends StatelessWidget {
), ),
), ),
title: Text( title: Text(
lesson.substituteTeacher?.name != "" lesson.substituteTeacher?.name != "" || lesson.substituteTeacher?.name != null
? "substituted".i18n ? "substituted".i18n
: "cancelled".i18n, : "cancelled".i18n,
maxLines: 2, maxLines: 2,

View File

@@ -15,7 +15,8 @@ class MissedExamView extends StatelessWidget {
final List<Lesson> missedExams; final List<Lesson> missedExams;
static show(List<Lesson> missedExams, {required BuildContext context}) => showRoundedModalBottomSheet(context, child: MissedExamView(missedExams)); static show(List<Lesson> missedExams, {required BuildContext context}) =>
showRoundedModalBottomSheet(context, child: MissedExamView(missedExams));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -25,7 +26,8 @@ class MissedExamView extends StatelessWidget {
} }
class MissedExamViewTile extends StatelessWidget { class MissedExamViewTile extends StatelessWidget {
const MissedExamViewTile(this.lesson, {Key? key, this.padding}) : super(key: key); const MissedExamViewTile(this.lesson, {Key? key, this.padding})
: super(key: key);
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? padding;
final Lesson lesson; final Lesson lesson;
@@ -33,23 +35,36 @@ class MissedExamViewTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context); SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
String? teacherName = lesson.teacher.isRenamed
? lesson.teacher.renamedTo
: lesson.teacher.name;
return Material( return Material(
type: MaterialType.transparency, type: MaterialType.transparency,
child: Padding( child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), padding: padding ??
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: ListTile( child: ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
leading: Icon( leading: Icon(
SubjectIcon.resolveVariant(subject: lesson.subject, context: context), SubjectIcon.resolveVariant(
subject: lesson.subject, context: context),
color: AppColors.of(context).text.withOpacity(.8), color: AppColors.of(context).text.withOpacity(.8),
size: 32.0, size: 32.0,
), ),
title: Text( title: Text(
"${lesson.subject.renamedTo ?? lesson.subject.name.capital()}${lesson.date.format(context)}", "${lesson.subject.renamedTo ?? lesson.subject.name.capital()}${lesson.date.format(context)}",
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), style: TextStyle(
fontWeight: FontWeight.w600,
fontStyle: lesson.subject.isRenamed &&
settingsProvider.renamedSubjectsItalics
? FontStyle.italic
: null),
), ),
subtitle: Text( subtitle: Text(
"missed_exam_contact".i18n.fill([lesson.teacher]), "missed_exam_contact".i18n.fill([teacherName ?? '']),
style: const TextStyle(fontWeight: FontWeight.w500), style: const TextStyle(fontWeight: FontWeight.w500),
), ),
trailing: const Icon(FeatherIcons.arrowRight), trailing: const Icon(FeatherIcons.arrowRight),

View File

@@ -40,7 +40,7 @@ class AbsenceSubjectView extends StatelessWidget {
TimetablePage.jump(context, lesson: lesson); TimetablePage.jump(context, lesson: lesson);
} else { } else {
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
content: Text("Cannot find lesson".i18n, content: Text("lesson_not_found".i18n,
style: const TextStyle(color: Colors.white)), style: const TextStyle(color: Colors.white)),
backgroundColor: AppColors.of(context).red, backgroundColor: AppColors.of(context).red,
context: context, context: context,

View File

@@ -17,6 +17,7 @@ extension ScreensLocalization on String {
"Subjects": "Subjects", "Subjects": "Subjects",
"attention": "Attention!", "attention": "Attention!",
"attention_body": "Percentage calculations are only an approximation so they may not be accurate.", "attention_body": "Percentage calculations are only an approximation so they may not be accurate.",
"lesson_not_found": "Cannot find lesson",
}, },
"hu_hu": { "hu_hu": {
"Absences": "Hiányzások", "Absences": "Hiányzások",
@@ -32,6 +33,7 @@ extension ScreensLocalization on String {
"Subjects": "Tantárgyak", "Subjects": "Tantárgyak",
"attention": "Figyelem!", "attention": "Figyelem!",
"attention_body": "A százalékos számítások csak közelítések, ezért előfordulhat, hogy nem pontosak.", "attention_body": "A százalékos számítások csak közelítések, ezért előfordulhat, hogy nem pontosak.",
"lesson_not_found": "Nem található óra",
}, },
"de_de": { "de_de": {
"Absences": "Fehlen", "Absences": "Fehlen",
@@ -47,6 +49,7 @@ extension ScreensLocalization on String {
"Subjects": "Fächer", "Subjects": "Fächer",
"attention": "Achtung!", "attention": "Achtung!",
"attention_body": "Prozentberechnungen sind nur eine Annäherung und können daher ungenau sein.", "attention_body": "Prozentberechnungen sind nur eine Annäherung und können daher ungenau sein.",
"lesson_not_found": "Lektion kann nicht gefunden werden",
}, },
}; };

View File

@@ -30,6 +30,7 @@ import 'package:provider/provider.dart';
import 'home_page.i18n.dart'; import 'home_page.i18n.dart';
import 'package:filcnaplo/ui/filter/widgets.dart'; import 'package:filcnaplo/ui/filter/widgets.dart';
import 'package:filcnaplo/ui/filter/sort.dart'; import 'package:filcnaplo/ui/filter/sort.dart';
import 'package:i18n_extension/i18n_extension.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key); const HomePage({Key? key}) : super(key: key);
@@ -94,6 +95,15 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
void setGreeting() { void setGreeting() {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
if (!settings.presentationMode) {
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
} else {
firstName = "János";
}
bool customWelcome = false;
if (now.isBefore(DateTime(now.year, DateTime.august, 31)) && if (now.isBefore(DateTime(now.year, DateTime.august, 31)) &&
now.isAfter(DateTime(now.year, DateTime.june, 14))) { now.isAfter(DateTime(now.year, DateTime.june, 14))) {
greeting = "goodrest"; greeting = "goodrest";
@@ -130,6 +140,14 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
greeting = "merryxmas"; greeting = "merryxmas";
} else if (now.month == DateTime.january && now.day == 1) { } else if (now.month == DateTime.january && now.day == 1) {
greeting = "happynewyear"; greeting = "happynewyear";
} else if (settings.welcomeMessage.replaceAll(' ', '') != '') {
greeting = settings.welcomeMessage;
greeting = localizeFill(
settings.welcomeMessage,
[firstName],
);
customWelcome = true;
} else if (now.hour >= 18) { } else if (now.hour >= 18) {
greeting = "goodevening"; greeting = "goodevening";
} else if (now.hour >= 10) { } else if (now.hour >= 10) {
@@ -139,6 +157,8 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
} else { } else {
greeting = "goodevening"; greeting = "goodevening";
} }
greeting = customWelcome ? greeting : greeting.i18n.fill([firstName]);
} }
@override @override
@@ -155,13 +175,6 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
setGreeting(); setGreeting();
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
if (!settings.presentationMode) {
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
} else {
firstName = "János";
}
return Scaffold( return Scaffold(
body: Stack( body: Stack(
children: [ children: [
@@ -184,7 +197,7 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
title: Padding( title: Padding(
padding: const EdgeInsets.only(left: 24.0), padding: const EdgeInsets.only(left: 24.0),
child: Text( child: Text(
greeting.i18n.fill([firstName]), greeting,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,

View File

@@ -13,7 +13,8 @@ extension Localization on String {
"welcome": "Welcome, %s!", "welcome": "Welcome, %s!",
"missing_fields": "Missing Fields!", "missing_fields": "Missing Fields!",
"invalid_grant": "invalid_grant":
"Invalid Username/Password! (Try adding spaces after Username)", // "Invalid Username/Password! (Try adding spaces after Username)",
"Invalid Username/Password!",
"error": "Failed to log in.", "error": "Failed to log in.",
"schools_error": "Failed to get schools." "schools_error": "Failed to get schools."
}, },
@@ -27,7 +28,8 @@ extension Localization on String {
"welcome": "Üdv, %s!", "welcome": "Üdv, %s!",
"missing_fields": "Hiányzó adatok!", "missing_fields": "Hiányzó adatok!",
"invalid_grant": "invalid_grant":
"Helytelen Felhasználónév/Jelszó! (Próbálj szóközöket írni a Felhasználónév után)", // "Helytelen Felhasználónév/Jelszó! (Próbálj szóközöket írni a Felhasználónév után)",
"Helytelen Felhasználónév/Jelszó!",
"error": "Sikertelen bejelentkezés.", "error": "Sikertelen bejelentkezés.",
"schools_error": "Nem sikerült lekérni az iskolákat." "schools_error": "Nem sikerült lekérni az iskolákat."
}, },

View File

@@ -17,6 +17,8 @@ List<School> searchSchools(List<School> all, String pattern) {
}); });
if (contains == pattern.split(" ").length) results.add(item); if (contains == pattern.split(" ").length) results.add(item);
if (item.instituteCode.toLowerCase().specialChars().contains(pattern)) results.add(item);
} }
results.sort((a, b) => a.name.compareTo(b.name)); results.sort((a, b) => a.name.compareTo(b.name));

View File

@@ -39,7 +39,9 @@ class _StatusBarState extends State<StatusBar> {
height: currentStatus != null ? 28.0 : 0, height: currentStatus != null ? 28.0 : 0,
decoration: BoxDecoration( decoration: BoxDecoration(
color: backgroundColor, color: backgroundColor,
boxShadow: [BoxShadow(color: Theme.of(context).shadowColor, blurRadius: 8.0)], boxShadow: [
BoxShadow(color: Theme.of(context).shadowColor, blurRadius: 8.0)
],
borderRadius: BorderRadius.circular(45.0), borderRadius: BorderRadius.circular(45.0),
), ),
), ),
@@ -53,9 +55,12 @@ class _StatusBarState extends State<StatusBar> {
height: currentStatus != null ? 28.0 : 0, height: currentStatus != null ? 28.0 : 0,
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut, curve: Curves.easeInOut,
width: MediaQuery.of(context).size.width * statusProvider.progress - 16.0, width: MediaQuery.of(context).size.width *
statusProvider.progress -
16.0,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(0.8), color:
Theme.of(context).colorScheme.secondary.withOpacity(0.8),
borderRadius: BorderRadius.circular(45.0), borderRadius: BorderRadius.circular(45.0),
), ),
), ),
@@ -82,6 +87,8 @@ class _StatusBarState extends State<StatusBar> {
return "Syncing data".i18n; return "Syncing data".i18n;
case Status.maintenance: case Status.maintenance:
return "KRETA Maintenance".i18n; return "KRETA Maintenance".i18n;
case Status.apiError:
return "KRETA API error".i18n;
case Status.network: case Status.network:
return "No connection".i18n; return "No connection".i18n;
default: default:
@@ -93,10 +100,13 @@ class _StatusBarState extends State<StatusBar> {
switch (status) { switch (status) {
case Status.maintenance: case Status.maintenance:
return AppColors.of(context).red; return AppColors.of(context).red;
case Status.apiError:
return AppColors.of(context).red;
case Status.network: case Status.network:
case Status.syncing: case Status.syncing:
default: default:
HSLColor color = HSLColor.fromColor(Theme.of(context).scaffoldBackgroundColor); HSLColor color =
HSLColor.fromColor(Theme.of(context).scaffoldBackgroundColor);
if (color.lightness >= 0.5) { if (color.lightness >= 0.5) {
color = color.withSaturation(0.3); color = color.withSaturation(0.3);
color = color.withLightness(color.lightness - 0.1); color = color.withLightness(color.lightness - 0.1);

View File

@@ -6,16 +6,19 @@ extension Localization on String {
"en_en": { "en_en": {
"Syncing data": "Syncing data", "Syncing data": "Syncing data",
"KRETA Maintenance": "KRETA Maintenance", "KRETA Maintenance": "KRETA Maintenance",
"KRETA API error": "KRETA API Error",
"No connection": "No connection", "No connection": "No connection",
}, },
"hu_hu": { "hu_hu": {
"Syncing data": "Adatok frissítése", "Syncing data": "Adatok frissítése",
"KRETA Maintenance": "KRÉTA Karbantartás", "KRETA Maintenance": "KRÉTA Karbantartás",
"KRETA API error": "KRÉTA API Hiba",
"No connection": "Nincs kapcsolat", "No connection": "Nincs kapcsolat",
}, },
"de_de": { "de_de": {
"Syncing data": "Daten aktualisieren", "Syncing data": "Daten aktualisieren",
"KRETA Maintenance": "KRETA Wartung", "KRETA Maintenance": "KRETA Wartung",
"KRETA API error": "KRETA API Fehler",
"No connection": "Keine Verbindung", "No connection": "Keine Verbindung",
}, },
}; };

View File

@@ -51,6 +51,9 @@ class SubjectIconGallery extends StatelessWidget {
SubjectIconItem("Magatartás"), SubjectIconItem("Magatartás"),
SubjectIconItem("Angol nyelv"), SubjectIconItem("Angol nyelv"),
SubjectIconItem("Linux"), SubjectIconItem("Linux"),
SubjectIconItem("Adatbázis"),
SubjectIconItem("Asztali alkalmazások"),
SubjectIconItem("Projekt"),
], ],
), ),
); );

View File

@@ -43,6 +43,7 @@ import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/welcome_message.dart';
class SettingsScreen extends StatefulWidget { class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key? key}) : super(key: key); const SettingsScreen({Key? key}) : super(key: key);
@@ -86,7 +87,9 @@ class _SettingsScreenState extends State<SettingsScreen>
String _firstName; String _firstName;
List<String> _nameParts = account.displayName.split(" "); List<String> _nameParts =
(account.nickname != '' ? account.nickname : account.displayName)
.split(" ");
if (!settings.presentationMode) { if (!settings.presentationMode) {
_firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0]; _firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
} else { } else {
@@ -95,7 +98,10 @@ class _SettingsScreenState extends State<SettingsScreen>
accountTiles.add( accountTiles.add(
AccountTile( AccountTile(
name: Text(!settings.presentationMode ? account.name : "János", name: Text(
!settings.presentationMode
? (account.nickname != '' ? account.nickname : account.name)
: "János",
style: const TextStyle(fontWeight: FontWeight.w500)), style: const TextStyle(fontWeight: FontWeight.w500)),
username: Text( username: Text(
!settings.presentationMode ? account.username : "01234567890"), !settings.presentationMode ? account.username : "01234567890"),
@@ -206,7 +212,7 @@ class _SettingsScreenState extends State<SettingsScreen>
opacity: 1 - _hideContainersController.value, opacity: 1 - _hideContainersController.value,
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 32.0), const SizedBox(height: 45.0),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -274,7 +280,7 @@ class _SettingsScreenState extends State<SettingsScreen>
child: Panel( child: Panel(
child: Column( child: Column(
children: [ children: [
// Account list // account list
...accountTiles, ...accountTiles,
if (accountTiles.isNotEmpty) if (accountTiles.isNotEmpty)
@@ -290,7 +296,7 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
// Account settings // account settings
PanelButton( PanelButton(
onPressed: () { onPressed: () {
Navigator.of(context) Navigator.of(context)
@@ -454,6 +460,7 @@ class _SettingsScreenState extends State<SettingsScreen>
Material( Material(
type: MaterialType.transparency, type: MaterialType.transparency,
child: MenuNotifications(settings: settings)), child: MenuNotifications(settings: settings)),
WelcomeMessagePanelButton(settings, user),
], ],
), ),
), ),

View File

@@ -73,6 +73,9 @@ extension SettingsLocalization on String {
"devsettings": "Developer Settings", "devsettings": "Developer Settings",
"devmode": "Developer Mode", "devmode": "Developer Mode",
"copy_jwt": "Copy JWT", "copy_jwt": "Copy JWT",
"welcome_msg": "Welcome Message",
"default": "Default",
"edit_welcome_msg": "Edit welcome message",
}, },
"hu_hu": { "hu_hu": {
"personal_details": "Személyes információk", "personal_details": "Személyes információk",
@@ -144,6 +147,9 @@ extension SettingsLocalization on String {
"devsettings": "Fejlesztői Beállítások", "devsettings": "Fejlesztői Beállítások",
"devmode": "Fejlesztői mód", "devmode": "Fejlesztői mód",
"copy_jwt": "JWT másolása", "copy_jwt": "JWT másolása",
"welcome_msg": "Üdvözlő üzenet",
"default": "Alapértelmezett",
"edit_welcome_msg": "Üdvözlő üzenet szerkesztése",
}, },
"de_de": { "de_de": {
"personal_details": "Persönliche Angaben", "personal_details": "Persönliche Angaben",
@@ -214,6 +220,9 @@ extension SettingsLocalization on String {
"devsettings": "Entwickleroptionen", "devsettings": "Entwickleroptionen",
"devmode": "Entwicklermodus", "devmode": "Entwicklermodus",
"copy_jwt": "JWT kopieren", "copy_jwt": "JWT kopieren",
"welcome_msg": "Willkommensnachricht",
"default": "Standard",
"edit_welcome_msg": "Begrüßungsnachricht bearbeiten",
}, },
}; };

View File

@@ -82,12 +82,12 @@ class PremiumAuth {
//} //}
// Skip premium check when disconnected // Skip premium check when disconnected
//try { // try {
// final status = await InternetAddress.lookup('github.com'); // final status = await InternetAddress.lookup('github.com');
// if (status.isEmpty) return false; // if (status.isEmpty) return false;
//} on SocketException catch (_) { // } on SocketException catch (_) {
// return false; // return false;
//} // }
//for (int tries = 0; tries < 3; tries++) { //for (int tries = 0; tries < 3; tries++) {
// try { // try {

View File

@@ -22,7 +22,7 @@ class GoalProvider extends ChangeNotifier {
Future<void> fetchDone({required GradeProvider gradeProvider}) async { Future<void> fetchDone({required GradeProvider gradeProvider}) async {
var goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!); var goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!);
var beforeAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!); var beforeAvgs = await _db.userQuery.subjectGoalBefores(userId: _user.id!);
List<Subject> subjects = gradeProvider.grades List<Subject> subjects = gradeProvider.grades
.map((e) => e.subject) .map((e) => e.subject)

View File

@@ -17,8 +17,12 @@ class ShareProvider extends ChangeNotifier {
// Future<void> shareTheme({required SharedTheme theme}) async { // Future<void> shareTheme({required SharedTheme theme}) async {
// } // }
// themes
Future<SharedTheme> shareCurrentTheme(BuildContext context, Future<SharedTheme> shareCurrentTheme(BuildContext context,
{bool isPublic = false, bool shareNick = true}) async { {bool isPublic = false,
bool shareNick = true,
required SharedGradeColors gradeColors}) async {
final SettingsProvider settings = final SettingsProvider settings =
Provider.of<SettingsProvider>(context, listen: false); Provider.of<SettingsProvider>(context, listen: false);
@@ -38,7 +42,7 @@ class ShareProvider extends ChangeNotifier {
const Color(0xFF3D7BF4).value, const Color(0xFF3D7BF4).value,
}; };
SharedTheme theme = SharedTheme.fromJson(themeJson); SharedTheme theme = SharedTheme.fromJson(themeJson, gradeColors);
FilcAPI.addSharedTheme(theme); FilcAPI.addSharedTheme(theme);
return theme; return theme;
@@ -49,9 +53,54 @@ class ShareProvider extends ChangeNotifier {
Map? themeJson = await FilcAPI.getSharedTheme(id); Map? themeJson = await FilcAPI.getSharedTheme(id);
if (themeJson != null) { if (themeJson != null) {
SharedTheme theme = SharedTheme.fromJson(themeJson); Map? gradeColorsJson =
await FilcAPI.getSharedGradeColors(themeJson['grade_colors_id']);
if (gradeColorsJson != null) {
SharedTheme theme = SharedTheme.fromJson(
themeJson, SharedGradeColors.fromJson(gradeColorsJson));
return theme; return theme;
} }
}
return null;
}
// grade colors
Future<SharedGradeColors> shareCurrentGradeColors(
BuildContext context, {
bool isPublic = false,
bool shareNick = true,
}) async {
final SettingsProvider settings =
Provider.of<SettingsProvider>(context, listen: false);
Map gradeColorsJson = {
'public_id': const Uuid().v4(),
'is_public': isPublic,
'nickname': shareNick ? _user.nickname : 'Anonymous',
'five_color': settings.gradeColors[4].value,
'four_color': settings.gradeColors[3].value,
'three_color': settings.gradeColors[2].value,
'two_color': settings.gradeColors[1].value,
'one_color': settings.gradeColors[0].value,
};
SharedGradeColors gradeColors = SharedGradeColors.fromJson(gradeColorsJson);
FilcAPI.addSharedGradeColors(gradeColors);
return gradeColors;
}
Future<SharedGradeColors?> getGradeColorsById(BuildContext context,
{required String id}) async {
Map? gradeColorsJson = await FilcAPI.getSharedGradeColors(id);
if (gradeColorsJson != null) {
SharedGradeColors gradeColors =
SharedGradeColors.fromJson(gradeColorsJson);
return gradeColors;
}
return null; return null;
} }

View File

@@ -8,6 +8,7 @@ import 'package:filcnaplo/ui/widgets/message/message_tile.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart'; import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo_kreta_api/models/message.dart'; import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
import 'package:filcnaplo_mobile_ui/common/filter_bar.dart'; import 'package:filcnaplo_mobile_ui/common/filter_bar.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart';
@@ -268,10 +269,40 @@ class _PremiumCustomAccentColorSettingState
// ), // ),
// ), // ),
// ); // );
showDialog(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("share_disclaimer".i18n),
actions: [
ActionButton(
label: "understand".i18n,
onTap: () async {
Navigator.of(context).pop();
SharedGradeColors gradeColors =
await shareProvider
.shareCurrentGradeColors(context);
SharedTheme theme = SharedTheme theme =
await shareProvider.shareCurrentTheme(context); await shareProvider.shareCurrentTheme(
Share.share(theme.id, context,
subject: 'reFilc Téma / reFilc Theme'); gradeColors: gradeColors,
);
Share.share(
theme.id,
subject: 'share_subj_theme'.i18n,
);
},
),
],
),
),
);
}, },
icon: const Icon( icon: const Icon(
FeatherIcons.share2, FeatherIcons.share2,
@@ -298,7 +329,7 @@ class _PremiumCustomAccentColorSettingState
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
gradient: LinearGradient( gradient: LinearGradient( // https://discord.com/channels/1111649116020285532/1153619667848548452
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
stops: const [ stops: const [
@@ -306,7 +337,8 @@ class _PremiumCustomAccentColorSettingState
0.75 0.75
], ],
colors: [ colors: [
Theme.of(context).colorScheme.background, settings.customBackgroundColor
?? Theme.of(context).colorScheme.background,
isBackgroundDifferent isBackgroundDifferent
? HSVColor.fromColor(Theme.of(context) ? HSVColor.fromColor(Theme.of(context)
.colorScheme .colorScheme
@@ -781,6 +813,18 @@ class _PremiumCustomAccentColorSettingState
setTheme(settings.theme, true); setTheme(settings.theme, true);
}, },
onThemeIdProvided: (theme) { onThemeIdProvided: (theme) {
// changing grade colors
List<Color> colors = [
theme.gradeColors.oneColor,
theme.gradeColors.twoColor,
theme.gradeColors.threeColor,
theme.gradeColors.fourColor,
theme.gradeColors.fiveColor,
];
settings.update(
gradeColors: colors);
// changing theme
setState(() { setState(() {
updateCustomColor( updateCustomColor(
null, null,

View File

@@ -14,6 +14,10 @@ extension SettingsLocalization on String {
"enter_id": "Enter ID", "enter_id": "Enter ID",
"theme_id": "Theme ID...", "theme_id": "Theme ID...",
"theme_not_found": "Theme not found!", "theme_not_found": "Theme not found!",
"attention": "Attention!",
"share_disclaimer":
"By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.",
"understand": "I understand",
}, },
"hu_hu": { "hu_hu": {
"theme_prev": "Előnézet", "theme_prev": "Előnézet",
@@ -26,6 +30,10 @@ extension SettingsLocalization on String {
"enter_id": "ID megadása", "enter_id": "ID megadása",
"theme_id": "Téma azonosító...", "theme_id": "Téma azonosító...",
"theme_not_found": "A téma nem található!", "theme_not_found": "A téma nem található!",
"attention": "Figyelem!",
"share_disclaimer":
"A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.",
"understand": "Értem",
}, },
"de_de": { "de_de": {
"theme_prev": "Vorschau", "theme_prev": "Vorschau",
@@ -39,6 +47,10 @@ extension SettingsLocalization on String {
"enter_id": "Geben Sie die ID ein", "enter_id": "Geben Sie die ID ein",
"theme_id": "Themen-ID...", "theme_id": "Themen-ID...",
"theme_not_found": "Thema nicht gefunden!", "theme_not_found": "Thema nicht gefunden!",
"attention": "Achtung!",
"share_disclaimer":
"Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.",
"understand": "Ich verstehe",
}, },
}; };

View File

@@ -0,0 +1,146 @@
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:provider/provider.dart';
import 'package:i18n_extension/i18n_extension.dart';
// ignore: must_be_immutable
class WelcomeMessagePanelButton extends StatelessWidget {
late SettingsProvider settingsProvider;
late UserProvider user;
WelcomeMessagePanelButton(this.settingsProvider, this.user, {Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
String finalName = ((user.nickname ?? '') != ''
? user.nickname
: (user.displayName ?? '') != ''
? user.displayName
: 'János') ??
'János';
return PanelButton(
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.all)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.profile);
return;
}
showDialog(
context: context,
builder: (context) => WelcomeMessageEditor(settingsProvider));
},
title: Text("welcome_msg".i18n),
leading: const Icon(FeatherIcons.smile),
trailing: Container(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
settingsProvider.welcomeMessage.replaceAll(' ', '') != ''
? localizeFill(
settingsProvider.welcomeMessage,
[finalName],
)
: 'default'.i18n,
style: const TextStyle(fontSize: 14.0),
textAlign: TextAlign.end,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
),
);
}
}
// ignore: must_be_immutable
class WelcomeMessageEditor extends StatefulWidget {
late SettingsProvider settingsProvider;
WelcomeMessageEditor(this.settingsProvider, {Key? key}) : super(key: key);
@override
State<WelcomeMessageEditor> createState() => _WelcomeMessageEditorState();
}
class _WelcomeMessageEditorState extends State<WelcomeMessageEditor> {
final _welcomeMsg = TextEditingController();
@override
void initState() {
super.initState();
_welcomeMsg.text =
widget.settingsProvider.welcomeMessage.replaceAll('%s', '%name%');
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("edit_welcome_msg".i18n),
content: TextField(
controller: _welcomeMsg,
autofocus: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
label: Text('welcome_msg'.i18n),
suffixIcon: IconButton(
icon: const Icon(FeatherIcons.x),
onPressed: () {
setState(() {
_welcomeMsg.text = "";
});
},
),
),
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"done".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
// var trimmed = _welcomeMsg.text.trim();
// var defLen = trimmed.length;
// var replacedLen = trimmed.replaceAll('%s', '').length;
// if (defLen - 2 > replacedLen) {
// print('fuck yourself rn');
// }
var finalText = _welcomeMsg.text
.trim()
.replaceFirst('%name%', '\$s')
.replaceFirst('%user%', '\$s')
.replaceFirst('%username%', '\$s')
.replaceFirst('%me%', '\$s')
.replaceFirst('%profile%', '\$s')
.replaceAll('%', '')
.replaceFirst('\$s', '%s');
// .replaceAll('\$s', 's');
widget.settingsProvider
.update(welcomeMessage: finalText, store: true);
Navigator.of(context).pop(true);
},
),
],
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:filcnaplo/helpers/subject.dart'; import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart'; import 'package:filcnaplo_kreta_api/controllers/timetable_controller.dart';
import 'package:filcnaplo_mobile_ui/common/empty.dart'; import 'package:filcnaplo_mobile_ui/common/empty.dart';
@@ -9,6 +10,7 @@ import 'package:filcnaplo/utils/format.dart';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:i18n_extension/i18n_widget.dart'; import 'package:i18n_extension/i18n_widget.dart';
import 'package:provider/provider.dart';
class PremiumFSTimetable extends StatefulWidget { class PremiumFSTimetable extends StatefulWidget {
const PremiumFSTimetable({Key? key, required this.controller}) const PremiumFSTimetable({Key? key, required this.controller})
@@ -21,6 +23,8 @@ class PremiumFSTimetable extends StatefulWidget {
} }
class _PremiumFSTimetableState extends State<PremiumFSTimetable> { class _PremiumFSTimetableState extends State<PremiumFSTimetable> {
late SettingsProvider settings;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -37,6 +41,8 @@ class _PremiumFSTimetableState extends State<PremiumFSTimetable> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
settings = Provider.of<SettingsProvider>(context);
if (widget.controller.days == null || widget.controller.days!.isEmpty) { if (widget.controller.days == null || widget.controller.days!.isEmpty) {
return const Center(child: Empty()); return const Center(child: Empty());
} }
@@ -174,7 +180,7 @@ class _PremiumFSTimetableState extends State<PremiumFSTimetable> {
style: TextStyle( style: TextStyle(
fontStyle: lessons[lessonIndex] fontStyle: lessons[lessonIndex]
.subject .subject
.isRenamed .isRenamed && settings.renamedSubjectsItalics
? FontStyle.italic ? FontStyle.italic
: null, : null,
), ),

7
upgrade-pub.sh Normal file
View File

@@ -0,0 +1,7 @@
cd filcnaplo && flutter pub upgrade && cd ..
cd filcnaplo_kreta_api && flutter pub upgrade && cd ..
cd filcnaplo_mobile_ui && flutter pub upgrade && cd ..
cd filcnaplo_desktop_ui && flutter pub upgrade && cd ..
cd filcnaplo_premium && flutter pub upgrade && cd ..
echo Upgraded pub.