Compare commits

...

105 Commits

Author SHA1 Message Date
Márton Kiss
d642f19834 Merge pull request #130 from refilc/dev
dev to master
2024-08-22 22:08:37 +02:00
Kima
51b25395c1 version change 2024-08-22 22:08:11 +02:00
Kima
4474562538 login fix 2024-08-22 22:04:05 +02:00
zypherift
d93dce7857 maybe fix 2024-08-22 21:57:42 +02:00
zypherift
d6fe2812c7 fine tune anim speed 2024-08-22 21:27:22 +02:00
zypherift
ed0f69d155 add animation to fade in (pain) 2024-08-22 21:23:20 +02:00
zypherift
c4a17633f8 fix that, and add new progress indicator 2024-08-22 20:58:12 +02:00
zypherift
d426d4866a fix this 2024-08-22 20:57:56 +02:00
zypherift
89adf5a26f change trans from E-kreta to e-KRETA 2024-08-22 20:47:07 +02:00
zypherift
117ee63b18 Merge branch 'dev' of github.com:refilc/naplo into dev 2024-08-22 20:45:16 +02:00
zypherift
d27b5f8a51 re-revert new login 2024-08-22 20:45:14 +02:00
Kima
9671c250b9 Revert "change kretenlogin to widget"
This reverts commit 2d5c270641.
2024-08-21 23:33:15 +02:00
zypherift
2d5c270641 change kretenlogin to widget 2024-08-21 23:22:27 +02:00
zypherift
eda093a9b5 change trans 2024-08-21 21:54:49 +02:00
Kima
544e9c214a some modifications in login refresh 2024-08-18 13:27:52 +02:00
Kima
5c3dbcbd52 fixed privacy button and back button on new login screen 2024-08-18 13:00:40 +02:00
Kima
cd5f86db00 Merge branch 'dev' of github.com:refilc/naplo into dev 2024-08-17 23:34:43 +02:00
Kima
9adfe636d6 maybe finally fixed ios login problem (i hope so) 2024-08-17 23:34:39 +02:00
zypherift
210e8ce0d4 change gradient start 2024-08-16 18:14:14 +02:00
zypherift
b1a7deca4a change height again, and make text proportional 2024-08-16 17:49:44 +02:00
zypherift
fa96770c9c fix padding :3 2024-08-16 17:46:14 +02:00
zypherift
5be67693c2 recolor navbar, and change padding on login btn part 2024-08-16 17:38:26 +02:00
zypherift
9ee5e8a35e image again 2024-08-16 16:58:53 +02:00
zypherift
3372c1ffde Merge branch 'dev' of github.com:refilc/naplo into dev 2024-08-16 16:42:13 +02:00
zypherift
94989687fa add higher kualiti images 2024-08-16 16:40:03 +02:00
Kima
52b9b4f5db maybe fixed ios login 2024-08-16 14:40:48 +02:00
Kima
75a2fa3726 changed location of privacy button 2024-08-16 11:48:41 +02:00
Márton Kiss
2c1bde9398 Merge pull request #128 from refilc/dev
dev to master
2024-08-16 01:06:26 +02:00
Kima
fd9794f3bf changed build number 2024-08-16 00:49:00 +02:00
Kima
a673d3f1b3 finally the new login is completely working with refresh token as well 2024-08-16 00:25:46 +02:00
Kima
f2c8e869b5 moved news 2024-08-14 23:42:26 +02:00
Kima
b4f2d38e99 changed launch mode to in-app 2024-08-14 23:28:57 +02:00
Kima
d842b2d588 sticker map shit 2024-08-14 23:27:30 +02:00
zypherift
f4fd9a3c2f forgot this icon 2024-08-14 23:20:25 +02:00
zypherift
a215bd7313 create trans(lation), and finish url opening 2024-08-14 23:17:30 +02:00
zypherift
3211279a53 added trans, changed icon 2024-08-14 23:05:22 +02:00
zypherift
2d4e281682 rename classes, and create new file for other 2024-08-14 23:01:15 +02:00
zypherift
148f945f3b replace images with new ones 2024-08-14 21:11:12 +02:00
Kima
aaa783ac45 updated build number 2024-08-14 13:22:51 +02:00
Kima
d70f92d4e5 changed login button 2024-08-14 12:32:32 +02:00
Kima
0f08400d63 shit 2024-08-14 11:32:38 +02:00
Kima
140a8f8e78 revert shit in rfplus 2024-08-14 11:30:59 +02:00
zypherift
17c9d1c447 what 2024-08-14 02:54:03 +02:00
zypherift
d8e23d8fa9 asd 2024-08-14 02:44:29 +02:00
zypherift
c4491eb0c6 merged new login screen with web login 2024-08-14 02:44:05 +02:00
zypherift
4945468e02 yes 2024-08-14 02:09:14 +02:00
Kima
9845ae9539 changed version number 2024-08-14 00:55:24 +02:00
Kima
3a2fc67cb2 new login system complete, that's it for today :3 2024-08-14 00:53:12 +02:00
Kima
148a43663c made kreten web login work hah 2024-08-13 01:08:05 +02:00
Márton Kiss
4a45131359 Merge pull request #126 from refilc/dev
dev to master (v5.0.3)
2024-06-21 22:54:49 +02:00
Kima
4128018a59 blah blah blah 2024-06-21 22:39:53 +02:00
Kima
0d509c90b1 finished everything shake 2024-06-21 22:30:17 +02:00
Kima
3ef58974c9 added shake to current error report screen as well 2024-06-21 21:47:15 +02:00
Kima
df75fadfea added shake 2024-06-21 21:16:24 +02:00
Kima
4254a7998a build number change 2024-06-20 14:17:31 +02:00
Kima
17faa545e5 changed some repos to custom forks because android sucks 2024-06-20 14:09:26 +02:00
Kima
506fb82dd0 changed lots of thing bc pub upgrade 2024-06-20 13:53:10 +02:00
Kima
bc8eb1910e upgraded pub things 2024-06-20 12:06:50 +02:00
Kima
fd1b15df77 just don't touch fl_chart 2024-06-19 23:34:45 +02:00
Kima
dd86c7436a fix in package versioning 2024-06-19 23:24:37 +02:00
Kima
8860a0269c fixed warnings after update 2024-06-19 22:34:56 +02:00
Kima
ce02dda46c updated flutter and pub packages 2024-06-19 22:23:16 +02:00
Kima
f16a52d0fb changed required sdk version and app version 2024-06-19 22:17:12 +02:00
Kima
cc8ce40222 fixed warnings 2024-06-19 22:10:28 +02:00
Kima
b6a933fe85 removed total average calculatro from refilcplus and added grade export viewing 2024-06-19 22:09:00 +02:00
Kima
767eba3776 well idgaf anymore, it should be good like this (subject average thing) 2024-06-19 22:02:12 +02:00
Kima
320499a466 prevent double taps on new weird exam popup 2024-06-19 21:35:49 +02:00
Kima
52c2f3090d fixed grade provider translation issues 2024-06-19 21:28:04 +02:00
Kima
d4df0170a3 fixed final grades ugly ui 2024-06-19 21:23:19 +02:00
Kima
796b35e27c made lesson countdown rotatable 2024-06-19 21:15:47 +02:00
Kima
051cdc895e fixed issues 2024-06-19 20:28:47 +02:00
Kima
e23bdac995 fixed sort issue 2024-06-19 16:43:56 +02:00
Kima
de0e8e1317 fixed good student mode percentage bug 2024-06-18 21:26:43 +02:00
Kima
b5b0046ef5 things™️ 2024-06-18 14:44:53 +02:00
Kima
92f16e054d finished new badge thingie 2024-06-17 22:47:30 +02:00
Kima
9470c848bf added new feature badge and unseen new feature list to settings 2024-06-17 22:19:43 +02:00
Kima
c40026e594 removed no import warning from grade exporting 2024-06-17 17:58:18 +02:00
Márton Kiss
93eaa5a74b Merge pull request #125 from refilc/dev
dev to master (v5.0.2 final)
2024-06-16 22:02:12 +02:00
Kima
5503b41be3 fix 2024-06-16 21:54:59 +02:00
Kima
bd716e1717 fixed shit 2024-06-16 21:48:32 +02:00
Márton Kiss
7f8b716712 Merge pull request #124 from refilc/master
master back to dev
2024-06-16 21:43:53 +02:00
Márton Kiss
1a7b59f2fc Merge pull request #123 from refilc/dev
dev to master (v5.0.2)
2024-06-16 21:43:07 +02:00
Kima
9f62e44b52 Revert "add wearos button"
This reverts commit 9d863e1ec0.
2024-06-16 21:42:37 +02:00
Kima
7c34552aa5 Revert "what"
This reverts commit 9b947256c8.
2024-06-16 21:40:35 +02:00
Kima
57d784443a changed version number 2024-06-16 21:36:22 +02:00
Kima
e73ee5a1f2 translation for grade importing 2024-06-16 21:36:00 +02:00
Kima
4277f0662a added back premium check 2024-06-16 21:33:33 +02:00
Kima
604e9dcaad Merge branch 'dev' of github.com:refilc/naplo into dev 2024-06-16 21:32:33 +02:00
Kima
c0dd84c665 finished grade importing and exporting totally 2024-06-16 21:32:27 +02:00
zypherift
7dc33d3b87 Merge branch 'dev' of github.com:refilc/naplo into dev 2024-06-16 20:09:12 +02:00
zypherift
9bee0daeb5 kill me 2024-06-16 20:08:59 +02:00
Kima
dd8a4430a9 translation fixes 2024-06-16 20:01:22 +02:00
zypherift
47934620ea fix again 2024-06-14 15:16:03 +02:00
zypherift
9e815aff2f maybe fix token spam 2024-06-14 15:05:17 +02:00
zypherift
d8393b24e1 fix multiple schools appearing in personal details menu 2024-06-14 15:02:10 +02:00
zypherift
1abe990847 yes 2024-06-14 14:55:35 +02:00
zypherift
3a92716019 add ger/eng export translation 2024-06-14 14:54:34 +02:00
zypherift
9b947256c8 what 2024-06-14 14:09:34 +02:00
zypherift
0586da3742 Merge branch 'master' of github.com:refilc/naplo 2024-06-14 14:09:28 +02:00
zypherift
9d863e1ec0 add wearos button 2024-06-14 14:08:13 +02:00
zypherift
e080800aa8 Merge pull request #121 from balint1414/dev
Hiba ablak szövegének megváltoztatása.
2024-06-14 11:35:08 +02:00
balint1414
e6f7728e42 Hiba ablak szövegének megváltoztatása. (3) 2024-06-14 11:27:29 +02:00
balint1414
ac11da1744 Hiba ablak szövegének megváltoztatása. (2) 2024-06-14 10:58:26 +02:00
balint1414
cad7bd19dc Hiba ablak szövegének megváltoztatása. (2) 2024-06-14 10:56:15 +02:00
balint1414
e1870b08e5 Hiba ablak szövegének megváltoztatása. 2024-06-14 10:44:10 +02:00
104 changed files with 2525 additions and 1443 deletions

View File

@@ -58,7 +58,7 @@ android {
defaultConfig {
applicationId "hu.refilc.naplo"
minSdkVersion 21
minSdkVersion 24
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 696 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 KiB

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 KiB

After

Width:  |  Height:  |  Size: 1009 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 KiB

After

Width:  |  Height:  |  Size: 549 KiB

View File

@@ -55,7 +55,7 @@ class FilcAPI {
static const stripeSheet = "$payment/stripe-sheet";
static Future<bool> checkConnectivity() async =>
(await Connectivity().checkConnectivity()) != ConnectivityResult.none;
(await Connectivity().checkConnectivity())[0] != ConnectivityResult.none;
static Future<List<School>?> getSchools() async {
try {

View File

@@ -1,5 +1,6 @@
// ignore_for_file: avoid_print, use_build_context_synchronously
import 'package:flutter/foundation.dart';
import 'package:refilc/utils/jwt.dart';
import 'package:refilc_kreta_api/models/school.dart';
import 'package:refilc_kreta_api/providers/absence_provider.dart';
@@ -66,6 +67,7 @@ Future loginAPI({
address: '1117 Budapest, Gábor Dénes utca 4.',
),
role: Role.parent,
refreshToken: '',
);
if (onLogin != null) onLogin(user);
@@ -107,7 +109,9 @@ Future loginAPI({
default:
// normal login from here
Provider.of<KretaClient>(context, listen: false).userAgent =
Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
Provider.of<SettingsProvider>(context, listen: false)
.config
.userAgent;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
@@ -127,6 +131,7 @@ Future loginAPI({
password: password,
instituteCode: instituteCode,
));
if (res != null) {
if (res.containsKey("error")) {
if (res["error"] == "invalid_grant") {
@@ -148,6 +153,7 @@ Future loginAPI({
name: student.name,
student: student,
role: JwtUtils.getRoleFromJWT(res["access_token"])!,
refreshToken: '',
);
if (onLogin != null) onLogin(user);
@@ -157,7 +163,8 @@ Future loginAPI({
.store
.storeUser(user);
Provider.of<UserProvider>(context, listen: false).addUser(user);
Provider.of<UserProvider>(context, listen: false).setUser(user.id);
Provider.of<UserProvider>(context, listen: false)
.setUser(user.id);
// Get user data
try {
@@ -167,7 +174,8 @@ Future loginAPI({
.fetch(week: Week.current()),
Provider.of<ExamProvider>(context, listen: false).fetch(),
Provider.of<HomeworkProvider>(context, listen: false).fetch(),
Provider.of<MessageProvider>(context, listen: false).fetchAll(),
Provider.of<MessageProvider>(context, listen: false)
.fetchAll(),
Provider.of<MessageProvider>(context, listen: false)
.fetchAllRecipients(),
Provider.of<NoteProvider>(context, listen: false).fetch(),
@@ -195,3 +203,112 @@ Future loginAPI({
return LoginState.failed;
}
// new login api
Future newLoginAPI({
required String code,
required BuildContext context,
void Function(User)? onLogin,
void Function()? onSuccess,
}) async {
// actual login (token grant) logic
Provider.of<KretaClient>(context, listen: false).userAgent =
Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"accept": "*/*",
"user-agent": "eKretaStudent/264745 CFNetwork/1494.0.7 Darwin/23.4.0",
};
Map? res = await Provider.of<KretaClient>(context, listen: false)
.postAPI(KretaAPI.login, headers: headers, body: {
"code": code,
"code_verifier": "DSpuqj_HhDX4wzQIbtn8lr8NLE5wEi1iVLMtMK0jY6c",
"redirect_uri":
"https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect",
"client_id": KretaAPI.clientId,
"grant_type": "authorization_code",
});
if (res != null) {
if (kDebugMode) {
print(res);
}
if (res.containsKey("error")) {
if (res["error"] == "invalid_grant") {
print("ERROR: invalid_grant");
return;
}
} else {
if (res.containsKey("access_token")) {
try {
Provider.of<KretaClient>(context, listen: false).accessToken =
res["access_token"];
Provider.of<KretaClient>(context, listen: false).refreshToken =
res["refresh_token"];
String instituteCode =
JwtUtils.getInstituteFromJWT(res["access_token"])!;
String username = JwtUtils.getUsernameFromJWT(res["access_token"])!;
Role role = JwtUtils.getRoleFromJWT(res["access_token"])!;
Map? studentJson =
await Provider.of<KretaClient>(context, listen: false)
.getAPI(KretaAPI.student(instituteCode));
Student student = Student.fromJson(studentJson!);
var user = User(
username: username,
password: '',
instituteCode: instituteCode,
name: student.name,
student: student,
role: role,
refreshToken: res["refresh_token"],
);
if (onLogin != null) onLogin(user);
// Store User in the database
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);
// Get user data
try {
await Future.wait([
Provider.of<GradeProvider>(context, listen: false).fetch(),
Provider.of<TimetableProvider>(context, listen: false)
.fetch(week: Week.current()),
Provider.of<ExamProvider>(context, listen: false).fetch(),
Provider.of<HomeworkProvider>(context, listen: false).fetch(),
Provider.of<MessageProvider>(context, listen: false).fetchAll(),
Provider.of<MessageProvider>(context, listen: false)
.fetchAllRecipients(),
Provider.of<NoteProvider>(context, listen: false).fetch(),
Provider.of<EventProvider>(context, listen: false).fetch(),
Provider.of<AbsenceProvider>(context, listen: false).fetch(),
]);
} catch (error) {
print("WARNING: failed to fetch user data: $error");
}
if (onSuccess != null) onSuccess();
return LoginState.success;
} catch (error) {
print("ERROR: loginAPI: $error");
// maybe check debug mode
// ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("ERROR: $error")));
return LoginState.failed;
}
}
}
}
return LoginState.failed;
}

View File

@@ -15,7 +15,7 @@ class StatusProvider extends ChangeNotifier {
StatusProvider() {
_handleNetworkChanges();
_handleDNSFailure();
Connectivity().checkConnectivity().then((value) => _networkType = value);
Connectivity().checkConnectivity().then((value) => _networkType = value[0]);
}
Status? getStatus() => _stack.isNotEmpty ? _stack[0] : null;
@@ -24,8 +24,8 @@ class StatusProvider extends ChangeNotifier {
void _handleNetworkChanges() {
Connectivity().onConnectivityChanged.listen((event) {
_networkType = event;
if (event == ConnectivityResult.none) {
_networkType = event[0];
if (event[0] == ConnectivityResult.none) {
if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network);

View File

@@ -31,6 +31,7 @@ import 'package:provider/provider.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart' as mobile;
import 'package:refilc_mobile_ui/screens/login/login_route.dart' as mobile;
import 'package:refilc_mobile_ui/screens/login/login_screen.dart' as mobile;
// import 'package:refilc_mobile_ui/screens/login/kreten_login.dart' as mobileTest;
import 'package:refilc_mobile_ui/screens/navigation/navigation_screen.dart'
as mobile;
import 'package:refilc_mobile_ui/screens/settings/settings_route.dart'
@@ -80,7 +81,8 @@ class App extends StatelessWidget {
CorePalette? corePalette;
final status = StatusProvider();
final kreta = KretaClient(user: user, settings: settings, status: status);
final kreta = KretaClient(
user: user, settings: settings, database: database, status: status);
final timetable =
TimetableProvider(user: user, database: database, kreta: kreta);
final premium = PlusProvider(settings: settings);

View File

@@ -54,6 +54,7 @@ const settingsDB = DatabaseStruct("settings", {
"new_colors": int,
"uwu_mode": int,
"new_popups": int,
"unseen_new_features": String,
// quick settings
"q_timetable_lesson_num": int, "q_timetable_sub_tiles": int,
"q_subjects_sub_tiles": int,
@@ -65,6 +66,7 @@ const usersDB = DatabaseStruct("users", {
"institute_code": String, "student": String, "role": int,
"nickname": String, "picture": String, // premium only (it's now plus btw)
"grade_streak": int,
"refresh_token": String,
});
const userDataDB = DatabaseStruct("user_data", {
"id": String, "grades": String, "timetable": String, "exams": String,
@@ -137,7 +139,8 @@ Future<Database> initDB(DatabaseProvider database) async {
"role": 0,
"nickname": "",
"picture": "",
"grade_streak": 0
"grade_streak": 0,
"refresh_token": "",
},
);
await migrateDB(db, struct: userDataDB, defaultValues: {

View File

@@ -8,10 +8,10 @@ class DatabaseStruct {
String typeName = "";
switch (type.runtimeType) {
case int:
case const (int):
typeName = "integer";
break;
case String:
case const (String):
typeName = "text";
break;
}

View File

@@ -71,9 +71,11 @@ class NotificationsHelper {
// Refresh kreta login for current user
final status = StatusProvider();
KretaClient kretaClientForUser = KretaClient(
user: userProviderForUser,
settings: settingsProvider,
status: status);
user: userProviderForUser,
settings: settingsProvider,
database: database,
status: status,
);
await kretaClientForUser.refreshLogin();
// Process notifications for current user
@@ -95,7 +97,7 @@ class NotificationsHelper {
/*
ezt a kódot nagyon szépen megírta az AI, picit szerkesztgettem is rajta
ezt a kódot nagyon szépen megírta az AI, picit szerkesztgettem is rajta //pearoo what did you do - zypherift
nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén kima bántani fog
Future<void> liveNotification(UserProvider currentuserProvider, KretaClient currentKretaClient) async {
@@ -655,7 +657,7 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
lesson.lessonIndex,
lesson.name,
dayTitle(lesson.date),
lesson.substituteTeacher!.isRenamed
(lesson.substituteTeacher?.isRenamed ?? false)
? lesson.substituteTeacher!.renamedTo!
: lesson.substituteTeacher!.name
],

View File

@@ -6,10 +6,9 @@ import 'package:share_plus/share_plus.dart';
class ShareHelper {
static Future<void> shareText(String text, {String? subject}) =>
Share.share(text, subject: subject);
// ignore: deprecated_member_use
static Future<void> shareFile(String path, {String? text, String? subject}) =>
// ignore: deprecated_member_use
Share.shareFiles([path], text: text, subject: subject);
Share.shareXFiles([XFile(path)], text: text, subject: subject);
static Future<void> shareAttachment(Attachment attachment,
{required BuildContext context}) async {

View File

@@ -0,0 +1,130 @@
import 'dart:math';
class Uwuifier {
final Map<String, double> _spacesModifier;
final double _wordsModifier;
final List<String> faces = [
"OwO",
"UwU",
">w<",
"^w^",
"^-^",
":3",
];
final List<List<String>> uwuMap = [
['(?:r|l)', 'w'],
['(?:R|L)', 'W'],
['na', 'nya'],
['ne', 'nye'],
['NA', 'NYA'],
['NE', 'NYE'],
['Na', 'Nya'],
['Ne', 'Nye'],
['no', 'nyo'],
['NO', 'NYO'],
['No', 'Nyo'],
['nO', 'NYo'],
['ove', 'uv'],
['no', 'nwo'],
];
final Map<String, String> _uwuCache = {};
Uwuifier({
Map<String, double>? spaces,
double? words,
}) : _spacesModifier = spaces ?? {
'faces': 0.05,
'actions': 0.0,
'stutters': 0.1,
},
_wordsModifier = words ?? 1.0;
String uwuifyWords(String sentence) {
final words = sentence.split(' ');
final uwuifiedSentence = words.map((word) {
if (isAt(word) || isUri(word)) return word;
var seed = Random().nextDouble();
for (final uwuMapEntry in uwuMap) {
final oldWord = RegExp(uwuMapEntry[0], caseSensitive: false);
final newWord = uwuMapEntry[1];
if (seed > _wordsModifier) continue;
word = word.replaceAll(oldWord, newWord);
}
return word;
}).join(' ');
return uwuifiedSentence;
}
String uwuifySpaces(String sentence) {
final words = sentence.split(' ');
final faceThreshold = _spacesModifier['faces']!;
final actionThreshold = _spacesModifier['actions']! + faceThreshold;
final stutterThreshold = _spacesModifier['stutters']! + actionThreshold;
final uwuifiedSentence = words.map((word) {
final seed = Random().nextDouble();
final firstCharacter = word[0];
if (seed <= faceThreshold && faces.isNotEmpty) {
word += ' ${faces[Random().nextInt(faces.length)]}';
} else if (seed <= actionThreshold) {
// Skip actions
} else if (seed <= stutterThreshold && !isUri(word)) {
if (Random().nextInt(10) == 0) {
final stutter = Random().nextInt(3);
return '${firstCharacter * (stutter + 1)}-$word';
}
}
return word;
}).join(' ');
return uwuifiedSentence;
}
String uwuifySentence(String sentence) {
if (_uwuCache.containsKey(sentence)) {
return _uwuCache[sentence]!;
}
var uwuifiedSentence = uwuifyWords(sentence);
uwuifiedSentence = uwuifySpaces(uwuifiedSentence);
_uwuCache[sentence] = uwuifiedSentence;
return uwuifiedSentence;
}
bool isAt(String value) {
return value.startsWith('@');
}
bool isUri(String? value) {
if (value == null) return false;
final split = RegExp(
r'''(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?''')
.firstMatch(value);
if (split == null) return false;
final scheme = split.group(1);
final authority = split.group(2);
final path = split.group(3);
if (!(scheme?.isNotEmpty == true && path?.isNotEmpty == true)) return false;
if (authority != null && authority.isNotEmpty) {
if (!(path?.isEmpty == true || path!.startsWith('/'))) return false;
} else if (path?.startsWith('//') == true) {
return false;
}
if (!RegExp(r'''^[a-z][a-z0-9+\-\.]*$''', caseSensitive: false)
.hasMatch(scheme!.toLowerCase())) {
return false;
}
return true;
}
}

View File

@@ -15,6 +15,8 @@ import 'package:refilc/utils/service_locator.dart';
import 'package:refilc_mobile_ui/screens/error_screen.dart';
import 'package:refilc_mobile_ui/screens/error_report_screen.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:shake_flutter/models/shake_report_configuration.dart';
import 'package:shake_flutter/shake_flutter.dart';
import 'helpers/live_activity_helper.dart';
@@ -38,6 +40,28 @@ void main() async {
BackgroundFetch.registerHeadlessTask(backgroundHeadlessTask);
// setting up things for shakebugs
// List<ShakePickerItem> pickerItems = [
// ShakePickerItem('Bug', 'Hiba', tag: 'bug'),
// ShakePickerItem('Suggestion', 'Fejlesztési javaslat', tag: 'suggestion'),
// ShakePickerItem('Question', 'Kérdés', tag: 'question')
// ];
// ShakePicker picker =
// ShakePicker('Feedback type', 'Visszajelzés típusa', pickerItems);
// ShakeTitle title = ShakeTitle('Title', 'Leírás', required: true);
// ShakeInspectButton inspect = ShakeInspectButton();
// ShakeAttachments attachments = ShakeAttachments();
// List<ShakeFormComponent> components = [picker, title, inspect, attachments];
// ShakeForm form = ShakeForm(components);
// Shake.setShakeForm(form);
// shakebugs initialization
// Shake.setInvokeShakeOnScreenshot(true);
Shake.start('Y44AwzfY6091xO2Nr0w59RHSpNxJhhiSFGs4enmoJwelN82ZRzTLE5X');
// pre-cache required icons
const todaySvg = SvgAssetLoader('assets/svg/menu_icons/today_selected.svg');
const gradesSvg = SvgAssetLoader('assets/svg/menu_icons/grades_selected.svg');
@@ -170,6 +194,18 @@ Widget errorBuilder(FlutterErrorDetails details) {
Navigator.of(context, rootNavigator: true)
.push(MaterialPageRoute(builder: (context) {
if (kReleaseMode) {
// silent report to shakebugs
ShakeReportConfiguration configuration = ShakeReportConfiguration();
configuration.blackBoxData = true;
configuration.activityHistoryData = true;
configuration.screenshot = true;
configuration.video = false;
Shake.silentReport(
configuration: configuration,
description:
'Silent Report #${DateTime.now().year}${DateTime.now().month}${DateTime.now().day}',
);
// show error report screen
return ErrorReportScreen(details);
} else {
return ErrorScreen(details);
@@ -244,7 +280,8 @@ void backgroundHeadlessTask(HeadlessTask task) {
LiveActivityHelper().backgroundJob();
} else {
NotificationsHelper().backgroundJob();
} BackgroundFetch.finish(task.taskId);
}
BackgroundFetch.finish(task.taskId);
}
Future<void> initAdditionalBackgroundFetch() async {

View File

@@ -60,7 +60,7 @@ class TodoItem {
);
}
get toJson => {
Map<String, dynamic> get toJson => {
'id': id,
'title': title,
'content': content,

View File

@@ -107,6 +107,7 @@ class SettingsProvider extends ChangeNotifier {
bool _newColors;
bool _uwuMode;
bool _newPopups;
List<String> _unseenNewFeatures;
// quick settings
bool _qTimetableLessonNum;
bool _qTimetableSubTiles;
@@ -180,6 +181,7 @@ class SettingsProvider extends ChangeNotifier {
required bool newColors,
required bool uwuMode,
required bool newPopups,
required List<String> unseenNewFeatures,
required bool qTimetableLessonNum,
required bool qTimetableSubTiles,
required bool qSubjectsSubTiles,
@@ -250,6 +252,7 @@ class SettingsProvider extends ChangeNotifier {
_newColors = newColors,
_uwuMode = uwuMode,
_newPopups = newPopups,
_unseenNewFeatures = unseenNewFeatures,
_qTimetableLessonNum = qTimetableLessonNum,
_qTimetableSubTiles = qTimetableSubTiles,
_qSubjectsSubTiles = qSubjectsSubTiles;
@@ -339,6 +342,7 @@ class SettingsProvider extends ChangeNotifier {
newColors: map['new_colors'] == 1,
uwuMode: map['uwu_mode'] == 1,
newPopups: map['new_popups'] == 1,
unseenNewFeatures: jsonDecode(map["unseen_new_features"]).cast<String>(),
qTimetableLessonNum: map['q_timetable_lesson_num'] == 1,
qTimetableSubTiles: map['q_timetable_sub_tiles'] == 1,
qSubjectsSubTiles: map['q_subjects_sub_tiles'] == 1,
@@ -416,6 +420,7 @@ class SettingsProvider extends ChangeNotifier {
"new_colors": _newColors ? 1 : 0,
"uwu_mode": _uwuMode ? 1 : 0,
"new_popups": _newPopups ? 1 : 0,
"unseen_new_features": jsonEncode(_unseenNewFeatures),
"q_timetable_lesson_num": _qTimetableLessonNum ? 1 : 0,
"q_timetable_sub_tiles": _qTimetableSubTiles ? 1 : 0,
"q_subjects_sub_tiles": _qSubjectsSubTiles ? 1 : 0,
@@ -497,6 +502,7 @@ class SettingsProvider extends ChangeNotifier {
newColors: true,
uwuMode: false,
newPopups: true,
unseenNewFeatures: ['grade_exporting'],
qTimetableLessonNum: true,
qTimetableSubTiles: true,
qSubjectsSubTiles: true,
@@ -569,6 +575,7 @@ class SettingsProvider extends ChangeNotifier {
bool get newColors => _newColors;
bool get uwuMode => _uwuMode;
bool get newPopups => _newPopups;
List<String> get unseenNewFeatures => _unseenNewFeatures;
bool get qTimetableLessonNum => _qTimetableLessonNum;
bool get qTimetableSubTiles => _qTimetableSubTiles;
bool get qSubjectsSubTiles => _qSubjectsSubTiles;
@@ -637,6 +644,7 @@ class SettingsProvider extends ChangeNotifier {
bool? newColors,
bool? uwuMode,
bool? newPopups,
List<String>? unseenNewFeatures,
bool? qTimetableLessonNum,
bool? qTimetableSubTiles,
bool? qSubjectsSubTiles,
@@ -828,6 +836,9 @@ class SettingsProvider extends ChangeNotifier {
if (newPopups != null && newPopups != _newPopups) {
_newPopups = newPopups;
}
if (unseenNewFeatures != null && unseenNewFeatures != _unseenNewFeatures) {
_unseenNewFeatures = unseenNewFeatures;
}
if (qTimetableLessonNum != null &&
qTimetableLessonNum != _qTimetableLessonNum) {
_qTimetableLessonNum = qTimetableLessonNum;

View File

@@ -17,6 +17,8 @@ class User {
String nickname;
String picture;
int gradeStreak;
// new login method
String refreshToken;
String get displayName => nickname != '' ? nickname : name;
bool get hasStreak => gradeStreak > 0;
@@ -32,6 +34,7 @@ class User {
this.nickname = "",
this.picture = "",
this.gradeStreak = 0,
required this.refreshToken,
}) {
if (id != null) {
this.id = id;
@@ -61,6 +64,7 @@ class User {
nickname: map["nickname"] ?? "",
picture: map["picture"] ?? "",
gradeStreak: map["grade_streak"] ?? 0,
refreshToken: map["refresh_token"] ?? "",
);
}
@@ -75,6 +79,8 @@ class User {
"role": role.index,
"nickname": nickname,
"picture": picture,
"grade_streak": gradeStreak,
"refresh_token": refreshToken,
};
}

View File

@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:shake_flutter/models/shake_theme.dart';
import 'package:shake_flutter/shake_flutter.dart';
Future<bool?> updateWidget() async {
try {
@@ -22,7 +24,9 @@ class ThemeModeObserver extends ChangeNotifier {
ThemeMode get themeMode => _themeMode;
bool get updateNavbarColor => _updateNavbarColor;
ThemeModeObserver({ThemeMode initialTheme = ThemeMode.system, bool updateNavbarColor = true})
ThemeModeObserver(
{ThemeMode initialTheme = ThemeMode.system,
bool updateNavbarColor = true})
: _themeMode = initialTheme,
_updateNavbarColor = updateNavbarColor;
@@ -31,5 +35,12 @@ class ThemeModeObserver extends ChangeNotifier {
_updateNavbarColor = updateNavbarColor;
if (Platform.isAndroid) updateWidget();
notifyListeners();
// change shake theme as well
ShakeTheme darkTheme = ShakeTheme();
darkTheme.accentColor = "#FFFFFF";
ShakeTheme lightTheme = ShakeTheme();
lightTheme.accentColor = "#000000";
Shake.setShakeTheme(mode == ThemeMode.dark ? darkTheme : lightTheme);
}
}

View File

@@ -125,8 +125,6 @@ class AppTheme {
onTertiary:
(newTertiary.computeLuminance() > 0.5 ? Colors.black : Colors.white)
.withOpacity(.9),
background: highlightColor,
onBackground: Colors.black.withOpacity(.9),
brightness: Brightness.light,
error: lightColors.red,
onError: Colors.white.withOpacity(.9),
@@ -141,9 +139,9 @@ class AppTheme {
indicatorColor:
accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
iconTheme:
MaterialStateProperty.all(IconThemeData(color: lightColors.text)),
WidgetStateProperty.all(IconThemeData(color: lightColors.text)),
backgroundColor: highlightColor,
labelTextStyle: MaterialStateProperty.all(TextStyle(
labelTextStyle: WidgetStateProperty.all(TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w500,
color: lightColors.text.withOpacity(0.8),
@@ -250,8 +248,6 @@ class AppTheme {
onTertiary:
(newTertiary.computeLuminance() > 0.5 ? Colors.black : Colors.white)
.withOpacity(.9),
background: highlightColor,
onBackground: Colors.white.withOpacity(.9),
brightness: Brightness.dark,
error: darkColors.red,
onError: Colors.black.withOpacity(.9),
@@ -266,9 +262,9 @@ class AppTheme {
indicatorColor:
accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
iconTheme:
MaterialStateProperty.all(IconThemeData(color: darkColors.text)),
WidgetStateProperty.all(IconThemeData(color: darkColors.text)),
backgroundColor: highlightColor,
labelTextStyle: MaterialStateProperty.all(TextStyle(
labelTextStyle: WidgetStateProperty.all(TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w500,
color: darkColors.text.withOpacity(0.8),

View File

@@ -259,7 +259,7 @@ Widget filterItemBuilder(
? const EdgeInsets.symmetric(vertical: 8.0)
: const EdgeInsets.symmetric(vertical: 4.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.vertical(
top: separated || isAfterSeparated
? const Radius.circular(16.0)

View File

@@ -364,7 +364,7 @@ class ColorPickerInputState extends State<ColorPickerInput> {
controller: textEditingController,
style: TextStyle(
fontSize: 18,
color: Theme.of(context).colorScheme.onBackground,
color: Theme.of(context).colorScheme.onSurface,
),
inputFormatters: [
UpperCaseTextFormatter(),

View File

@@ -39,4 +39,16 @@ class JwtUtils {
}
return null;
}
static String? getInstituteFromJWT(String jwt) {
var jwtData = decodeJwt(jwt);
return jwtData?["kreta:institute_code"];
}
static String? getUsernameFromJWT(String jwt) {
var jwtData = decodeJwt(jwt);
return jwtData?["kreta:user_name"];
}
}

View File

@@ -3,10 +3,10 @@ description: "Egy nem hivatalos e-KRÉTA kliens, diákoktól diákoknak."
homepage: https://refilc.hu
publish_to: "none"
version: 5.0.1+265
version: 5.0.4+274
environment:
sdk: ">=2.17.0 <=3.3.2"
sdk: ">=3.3.2 <=3.4.3"
dependencies:
flutter:
@@ -25,9 +25,9 @@ dependencies:
flutter_localizations:
sdk: flutter
i18n_extension: ^11.0.11
i18n_extension: ^12.0.1
sqflite: ^2.2.0+2
intl: ^0.18.1
intl: ^0.19.0
provider: ^6.1.1
http: ^1.1.2
uuid: ^4.2.1
@@ -39,40 +39,46 @@ dependencies:
# ref: master
path_provider: ^2.0.2
permission_handler: ^11.0.1
share_plus: ^7.0.2
connectivity_plus: ^5.0.2
share_plus: ^9.0.0
connectivity_plus: ^6.0.3
flutter_displaymode: ^0.6.0
quick_actions: ^1.0.1
animated_list_plus: ^0.5.0
dynamic_color: ^1.2.2
material_color_utilities: ^0.8.0
crypto: ^3.0.2
elegant_notification: ^1.6.1
elegant_notification: ^2.2.0
flutter_feather_icons: ^2.0.0+1
live_activities: ^1.7.4
animated_flip_counter: ^0.2.5
animated_flip_counter: ^0.3.4
lottie: ^3.1.0
rive: ^0.12.4
animated_background: ^2.0.0
dropdown_button2: ^2.3.9
home_widget: ^0.4.1
home_widget:
git:
url: https://github.com/refilc/home_widget.git
ref: flutter-beta
flutter_expandable_fab: ^2.0.0
uni_links: ^0.5.1
url_launcher: ^6.1.6
workmanager: ^0.5.1
workmanager:
git:
url: https://github.com/refilc/flutter_workmanager.git
ref: v0.5.1
flutter_svg: ^2.0.10+1
image_picker: ^1.0.7
animations: ^2.0.1
background_fetch: ^1.1.5
flutter_local_notifications: ^16.2.0
package_info_plus: ^5.0.1
screenshot: ^2.1.0
flutter_local_notifications: ^17.1.2
package_info_plus: ^8.0.0
screenshot: ^3.0.0
flutter_staggered_grid_view: ^0.7.0
sqflite_common_ffi_web: ^0.4.0
image_crop:
git:
url: https://github.com/kimaah/image_crop.git
googleapis: ^12.0.0
googleapis: ^13.2.0
google_sign_in: ^6.2.1
extension_google_sign_in_as_googleapis_auth: ^2.0.12
maps_launcher: ^2.2.0
@@ -82,9 +88,10 @@ dependencies:
xml: ^6.5.0
carousel_slider: ^4.2.1
flutter_portal: ^1.1.4
shake_flutter: ^17.0.0
dev_dependencies:
flutter_lints: ^3.0.1
flutter_lints: ^4.0.0
flutter_launcher_icons: "^0.13.1"
flutter_native_splash: "^2.3.10"
sqflite_common_ffi: ^2.0.0+3

View File

@@ -41,7 +41,7 @@ class FilterBar extends StatelessWidget implements PreferredSizeWidget {
color: Theme.of(context).colorScheme.secondary.withOpacity(0.25),
borderRadius: BorderRadius.circular(45.0),
),
overlayColor: MaterialStateProperty.all(const Color(0x00000000)),
overlayColor: WidgetStateProperty.all(const Color(0x00000000)),
// Tabs
padding: EdgeInsets.zero,
tabs: items,

View File

@@ -287,7 +287,7 @@ class AbsencesPageState extends State<AbsencesPage>
return FadeThroughTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
fillColor: Theme.of(context).colorScheme.background,
fillColor: Theme.of(context).colorScheme.surface,
child: child,
);
},

View File

@@ -68,7 +68,7 @@ class LoginScreenState extends State<LoginScreen> {
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.red),
),
onActionPressed: () {},
// onActionPressed: () {},
onCloseButtonPressed: () {},
onDismiss: () {},
onProgressFinished: () {},
@@ -340,7 +340,7 @@ class LoginScreenState extends State<LoginScreen> {
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.black),
),
onActionPressed: () {},
// onActionPressed: () {},
onCloseButtonPressed: () {},
onDismiss: () {},
onProgressFinished: () {},

View File

@@ -776,7 +776,7 @@ class SettingsScreenState extends State<SettingsScreen>
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.background),
.surface),
child: Material(
type: MaterialType.transparency,
child: SwitchListTile(

View File

@@ -2,7 +2,7 @@ name: refilc_desktop_ui
publish_to: "none"
environment:
sdk: ">=2.17.0 <=3.3.2"
sdk: ">=3.3.2 <=3.4.3"
dependencies:
flutter:
@@ -26,22 +26,22 @@ dependencies:
provider: ^6.1.1
url_launcher: ^6.2.5
flutter_linkify: ^6.0.0
flutter_markdown: ^0.6.20+1
flutter_markdown: ^0.7.2+1
animations: ^2.0.11
confetti: ^0.7.0
auto_size_text: ^3.0.0
flutter_acrylic: ^1.1.3
elegant_notification: ^1.13.0
elegant_notification: ^2.2.0
flutter_staggered_grid_view: ^0.7.0
i18n_extension: ^11.0.12
i18n_extension: ^12.0.1
flutter_expandable_fab: ^2.0.0
collection: ^1.18.0
animated_list_plus: ^0.5.2
intl: ^0.18.1
intl: ^0.19.0
flutter_custom_tabs: ^2.0.0+1
dev_dependencies:
flutter_lints: ^3.0.1
flutter_lints: ^4.0.0
flutter:
uses-material-design: true

View File

@@ -5,7 +5,7 @@ class KretaAPI {
static const login = BaseKreta.kretaIdp + KretaApiEndpoints.token;
static const logout = BaseKreta.kretaIdp + KretaApiEndpoints.revoke;
static const nonce = BaseKreta.kretaIdp + KretaApiEndpoints.nonce;
static const clientId = "kreta-ellenorzo-mobile-android";
static const clientId = "kreta-ellenorzo-student-mobile-ios";
// ELLENORZO API
static String notes(String iss) =>

View File

@@ -1,15 +1,16 @@
// ignore_for_file: avoid_print
// ignore_for_file: avoid_print, use_build_context_synchronously
import 'dart:convert';
import 'dart:io';
import 'package:refilc/api/login.dart';
import 'package:refilc/api/nonce.dart';
// import 'package:refilc/api/login.dart';
// import 'package:refilc/api/nonce.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/status_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc/utils/jwt.dart';
// import 'package:refilc/utils/jwt.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;
@@ -24,6 +25,7 @@ class KretaClient {
late final SettingsProvider _settings;
late final UserProvider _user;
late final DatabaseProvider _database;
late final StatusProvider _status;
bool _loginRefreshing = false;
@@ -32,9 +34,11 @@ class KretaClient {
this.accessToken,
required SettingsProvider settings,
required UserProvider user,
required DatabaseProvider database,
required StatusProvider status,
}) : _settings = settings,
_user = user,
_database = database,
_status = status,
userAgent = settings.config.userAgent {
var ioclient = HttpClient();
@@ -80,8 +84,8 @@ class KretaClient {
_status.triggerRequest(res);
if (res.statusCode == 401) {
await refreshLogin();
headerMap.remove("authorization");
await refreshLogin();
} else {
break;
}
@@ -212,8 +216,8 @@ class KretaClient {
res = await request.send();
if (res.statusCode == 401) {
await refreshLogin();
headerMap.remove("authorization");
await refreshLogin();
} else {
break;
}
@@ -232,65 +236,76 @@ class KretaClient {
}
}
Future<void> refreshLogin() async {
if (_loginRefreshing) return;
Future<String?> refreshLogin() async {
if (_loginRefreshing) return null;
_loginRefreshing = true;
User? loginUser = _user.user;
if (loginUser == null) return;
if (loginUser == null) return null;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"accept": "*/*",
"user-agent": "eKretaStudent/264745 CFNetwork/1494.0.7 Darwin/23.4.0",
};
String nonceStr = await getAPI(KretaAPI.nonce, json: false);
Nonce nonce =
getNonce(nonceStr, loginUser.username, loginUser.instituteCode);
headers.addAll(nonce.header());
if (_settings.presentationMode) {
print("DEBUG: refreshLogin: ${loginUser.id}");
} else {
print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}");
}
Map? loginRes = await postAPI(
KretaAPI.login,
headers: headers,
body: User.loginBody(
username: loginUser.username,
password: loginUser.password,
instituteCode: loginUser.instituteCode,
),
);
refreshToken ??= loginUser.refreshToken;
if (loginRes != null) {
if (loginRes.containsKey("access_token")) {
accessToken = loginRes["access_token"];
}
if (loginRes.containsKey("refresh_token")) {
refreshToken = loginRes["refresh_token"];
}
// Update role
loginUser.role =
JwtUtils.getRoleFromJWT(accessToken ?? "") ?? Role.student;
}
// print("REFRESH TOKEN BELOW");
// print(refreshToken);
if (refreshToken != null) {
Map? refreshRes = await postAPI(KretaAPI.login,
// print("REFRESHING LOGIN");
Map? res = await postAPI(KretaAPI.login,
headers: headers,
body: User.refreshBody(
refreshToken: refreshToken!,
instituteCode: loginUser.instituteCode));
if (refreshRes != null) {
if (refreshRes.containsKey("id_token")) {
idToken = refreshRes["id_token"];
refreshToken: loginUser.refreshToken,
instituteCode: loginUser.instituteCode,
));
// print("REFRESH RESPONSE BELOW");
// print(res);
if (res != null) {
if (res.containsKey("error")) {
// remove user if refresh token expired
if (res["error"] == "invalid_grant") {
// remove user from app
// _user.removeUser(loginUser.id);
// await _database.store.removeUser(loginUser.id);
print("invalid refresh token (invalid_grant)");
// return error
return "refresh_token_expired";
}
}
if (res.containsKey("access_token")) {
accessToken = res["access_token"];
}
if (res.containsKey("refresh_token")) {
refreshToken = res["refresh_token"];
loginUser.refreshToken = res["refresh_token"];
_database.store.storeUser(loginUser);
_user.refresh();
}
if (res.containsKey("id_token")) {
idToken = res["id_token"];
}
_loginRefreshing = false;
} else {
_loginRefreshing = false;
}
} else {
_loginRefreshing = false;
}
_loginRefreshing = false;
return null;
}
Future<void> logout() async {

View File

@@ -1,6 +1,7 @@
// ignore_for_file: no_leading_underscores_for_local_identifiers
import 'package:refilc/utils/format.dart';
import 'package:uuid/uuid.dart';
import 'category.dart';
import 'subject.dart';
import 'teacher.dart';
@@ -75,6 +76,40 @@ class Grade {
);
}
factory Grade.fromExportJson(Map json) {
return Grade(
id: const Uuid().v4(),
date: json["date"] != null ? DateTime.parse(json["date"]) : DateTime(0),
value: GradeValue(
json["value"] ?? 0,
json["value_name"] ?? "",
json["value_name"] ?? "",
json["weight"] ?? 0,
percentage: false,
),
teacher: Teacher.fromString((json["teacher"] ?? "").trim()),
description: json["description"] ?? "",
type: json["type"] != null
? Category.getGradeType(json["type"]
.replaceAll("midYear", "evkozi_jegy_ertekeles")
.replaceAll("halfYear", "felevi_jegy_ertekeles")
.replaceAll("endYear", "evvegi_jegy_ertekeles"))
: GradeType.unknown,
groupId: const Uuid().v4(),
subject: GradeSubject(
id: const Uuid().v4(),
category: Category.fromJson({}),
name: json["subject"] ?? ""),
mode: Category.fromJson({}),
writeDate:
json["date"] != null ? DateTime.parse(json["date"]) : DateTime(0),
seenDate:
json["date"] != null ? DateTime.parse(json["date"]) : DateTime(0),
form: "",
json: json,
);
}
bool compareTo(dynamic other) {
if (runtimeType != other.runtimeType) return false;

View File

@@ -111,13 +111,16 @@ class GradeProvider with ChangeNotifier {
grade.teacher.renamedTo =
renamedTeachers.isNotEmpty ? renamedTeachers[grade.teacher.id] : null;
grade.value.value =
_settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
grade.value.value = _settings.goodStudent
? (grade.value.percentage ? 100 : 5)
: grade.json!["SzamErtek"] ?? 0;
grade.value.valueName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtek"]}'
.replaceAll(RegExp(r'[(]+[12345]?[)]'), '')
.i18n;
: (grade.value.percentage
? '${grade.json!["SzovegesErtek"]}'
: '${grade.json!["SzovegesErtek"]}'
.replaceAll(RegExp(r'[(]+[12345]?[)]'), '')
.i18n);
grade.value.shortName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
@@ -155,8 +158,6 @@ class GradeProvider with ChangeNotifier {
}
}
print(gradeStreak);
user.gradeStreak = gradeStreak;
notifyListeners();
}

View File

@@ -8,21 +8,27 @@ extension Localization on String {
"Elégséges": "Warning but passing",
"Közepes": "Passed",
"": "Good",
"Jeles": "Excellent"
"Jeles": "Excellent",
"Példás": "Excellent",
"Nem írt": "Did not write",
},
"hu_hu": {
"Elégtelen": "Elégtelen",
"Elégséges": "Elégséges",
"Közepes": "Közepes",
"": "",
"Jeles": "Jeles"
"Jeles": "Jeles",
"Példás": "Példás",
"Nem írt": "Nem írt",
},
"de_de": {
"Elégtelen": "Ungenügend",
"Elégséges": "Mangelhaft",
"Közepes": "Ausreichend",
"": "Befriedigend",
"Jeles": "Gut"
"Jeles": "Gut",
"Példás": "Gut",
"Nem írt": "Nicht geschrieben",
},
};

View File

@@ -2,7 +2,7 @@ name: refilc_kreta_api
publish_to: "none"
environment:
sdk: ">=2.17.0 <=3.3.2"
sdk: ">=3.3.2 <=3.4.3"
dependencies:
flutter:
@@ -11,10 +11,10 @@ dependencies:
path: ../refilc/
http: ^1.1.2
provider: ^6.1.1
file_picker: ^6.1.1
intl: ^0.18.1
i18n_extension: ^11.0.11
file_picker: ^8.0.5
intl: ^0.19.0
i18n_extension: ^12.0.1
uuid: ^4.3.3
dev_dependencies:
flutter_lints: ^3.0.1
flutter_lints: ^4.0.0

View File

@@ -14,7 +14,7 @@ class BottomCard extends StatelessWidget {
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
),
child: Column(
mainAxisSize: MainAxisSize.min,

View File

@@ -22,7 +22,7 @@ class RoundedBottomSheet extends StatelessWidget {
return AnimatedContainer(
duration: const Duration(milliseconds: 500),
decoration: BoxDecoration(
color: backgroundColor ?? Theme.of(context).colorScheme.background,
color: backgroundColor ?? Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(borderRadius),
topRight: Radius.circular(borderRadius),

View File

@@ -1,5 +1,6 @@
import 'package:refilc/theme/colors/colors.dart';
import 'package:flutter/material.dart';
import 'chips.i18n.dart';
class BetaChip extends StatelessWidget {
const BetaChip({super.key, this.disabled = false});
@@ -22,7 +23,7 @@ class BetaChip extends StatelessWidget {
padding: const EdgeInsets.only(left: 8, right: 8),
child: Center(
child: Text(
"BETA",
"beta".i18n,
softWrap: true,
style: TextStyle(
fontSize: 10,

View File

@@ -0,0 +1,24 @@
import 'package:i18n_extension/i18n_extension.dart';
extension ScreensLocalization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"new": "NEW",
"beta": "BETA",
},
"hu_hu": {
"new": "ÚJ",
"beta": "BÉTA",
},
"de_de": {
"new": "NEU",
"beta": "BETA",
},
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'chips.i18n.dart';
class NewChip extends StatelessWidget {
const NewChip({super.key, this.disabled = false});
final bool disabled;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color:
disabled ? AppColors.of(context).text.withOpacity(.25) : Colors.red,
borderRadius: BorderRadius.circular(12.0),
),
padding:
const EdgeInsets.only(left: 6.0, right: 8.0, top: 4.0, bottom: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.hotel_class_rounded,
color: disabled
? AppColors.of(context).text.withOpacity(.5)
: Colors.white,
size: 14.0,
),
const SizedBox(width: 2.0),
Text(
'new'.i18n,
style: TextStyle(
color: disabled
? AppColors.of(context).text.withOpacity(.5)
: Colors.white,
fontSize: 12.0,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
}

View File

@@ -49,7 +49,7 @@ class _FilterBarState extends State<FilterBar> {
controller: widget.controller,
isScrollable: widget.scrollable,
physics: const BouncingScrollPhysics(),
// Label
// label
labelStyle: Theme.of(context).textTheme.titleMedium!.copyWith(
fontWeight: FontWeight.w600,
fontSize: 15.0,
@@ -57,15 +57,17 @@ class _FilterBarState extends State<FilterBar> {
labelPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 3),
labelColor: Theme.of(context).colorScheme.secondary,
unselectedLabelColor: AppColors.of(context).text.withOpacity(0.65),
// Indicator
// indicator
indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: const EdgeInsets.symmetric(vertical: 8.0),
indicator: BoxDecoration(
color: Theme.of(context).colorScheme.tertiary.withOpacity(.2),
borderRadius: BorderRadius.circular(45.0),
),
overlayColor: MaterialStateProperty.all(const Color(0x00000000)),
// Tabs
overlayColor: WidgetStateProperty.all(const Color(0x00000000)),
// underline (bottom border)
dividerColor: Colors.transparent,
// tabs
padding: EdgeInsets.zero,
tabs: widget.censored
? censoredItemsWidth

View File

@@ -22,7 +22,7 @@ class OutlinedRoundButton extends StatelessWidget {
width: size,
height: size,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
border: Border.all(
color: Theme.of(context).colorScheme.secondary.withOpacity(0.1),
width: 1.1,

View File

@@ -35,7 +35,7 @@ class Panel extends StatelessWidget {
borderRadius: BorderRadius.circular(16.0),
color: isTransparent
? Colors.transparent
: Theme.of(context).colorScheme.background,
: Theme.of(context).colorScheme.surface,
boxShadow: [
if ((hasShadow && !isTransparent) &&
Provider.of<SettingsProvider>(context, listen: false)
@@ -87,7 +87,7 @@ class PanelHeader extends StatelessWidget {
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0)),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
@@ -113,7 +113,7 @@ class PanelBody extends StatelessWidget {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
@@ -144,7 +144,7 @@ class PanelFooter extends StatelessWidget {
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(16.0),
bottomRight: Radius.circular(16.0)),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)

View File

@@ -9,7 +9,7 @@ void showSlidingBottomSheet(
cornerRadius: 16,
cornerRadiusOnFullscreen: 0,
avoidStatusBar: true,
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
duration: const Duration(milliseconds: 400),
snapSpec: const ss.SnapSpec(
snap: true,
@@ -18,7 +18,7 @@ void showSlidingBottomSheet(
),
headerBuilder: (context, state) {
return Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
@@ -37,7 +37,7 @@ void showSlidingBottomSheet(
},
builder: (context, state) {
return Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.fromLTRB(12.0, 0, 12.0, 8.0),
child: child),

View File

@@ -46,7 +46,7 @@ class SplittedPanel extends StatelessWidget {
decoration: BoxDecoration(
color: isTransparent
? Colors.transparent
: Theme.of(context).colorScheme.background,
: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.vertical(
top: Radius.circular(i == 0 ? 16.0 : 8.0),
bottom: Radius.circular(children!.length == i + 1 ? 16.0 : 8.0),

View File

@@ -10,7 +10,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
double valueFromPercentageInRange({required final double min, max, percentage}) {
double valueFromPercentageInRange(
{required final double min, max, percentage}) {
return percentage * (max - min) + min;
}
@@ -44,7 +45,8 @@ typedef _ViewablePreviewBuilderChildless = Widget Function(
Rect _getRect(GlobalKey globalKey) {
assert(globalKey.currentContext != null);
final RenderBox renderBoxContainer = globalKey.currentContext!.findRenderObject()! as RenderBox;
final RenderBox renderBoxContainer =
globalKey.currentContext!.findRenderObject()! as RenderBox;
final Offset containerOffset = renderBoxContainer.localToGlobal(
renderBoxContainer.paintBounds.topLeft,
);
@@ -101,7 +103,8 @@ class _ViewableState extends State<Viewable> with TickerProviderStateMixin {
final double screenWidth = MediaQuery.of(context).size.width;
final double center = screenWidth / 2;
final bool centerDividesChild = childRect.left < center && childRect.right > center;
final bool centerDividesChild =
childRect.left < center && childRect.right > center;
final double distanceFromCenter = (center - childRect.center.dx).abs();
if (centerDividesChild && distanceFromCenter <= childRect.width / 4) {
return _ViewableLocation.center;
@@ -132,7 +135,7 @@ class _ViewableState extends State<Viewable> with TickerProviderStateMixin {
return ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16.0),
child: Stack(
children: [
@@ -270,7 +273,8 @@ class _DecoyChild extends StatefulWidget {
_DecoyChildState createState() => _DecoyChildState();
}
class _DecoyChildState extends State<_DecoyChild> with TickerProviderStateMixin {
class _DecoyChildState extends State<_DecoyChild>
with TickerProviderStateMixin {
static const Color _lightModeMaskColor = Color(0xFF888888);
static const Color _masklessColor = Color(0xFFFFFFFF);
@@ -327,7 +331,9 @@ class _DecoyChildState extends State<_DecoyChild> with TickerProviderStateMixin
}
Widget _buildAnimation(BuildContext context, Widget? child) {
final Color color = widget.controller.status == AnimationStatus.reverse ? _masklessColor : _mask.value;
final Color color = widget.controller.status == AnimationStatus.reverse
? _masklessColor
: _mask.value;
return Positioned.fromRect(
rect: _rect.value!,
child: ShaderMask(
@@ -373,7 +379,8 @@ class _ViewableRoute<T> extends PopupRoute<T> {
static const Color _kModalBarrierColor = Color(0x6604040F);
static const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335);
static const Duration _kModalPopupTransitionDuration =
Duration(milliseconds: 335);
final List<Widget> _actions;
final _ViewablePreviewBuilderChildless? _builder;
@@ -396,7 +403,8 @@ class _ViewableRoute<T> extends PopupRoute<T> {
static final RectTween _rectTween = RectTween();
static final Animatable<Rect?> _rectAnimatable = _rectTween.chain(_curve);
static final RectTween _rectTweenReverse = RectTween();
static final Animatable<Rect?> _rectAnimatableReverse = _rectTweenReverse.chain(
static final Animatable<Rect?> _rectAnimatableReverse =
_rectTweenReverse.chain(
_curveReverse,
);
static final RectTween _sheetRectTween = RectTween();
@@ -407,10 +415,12 @@ class _ViewableRoute<T> extends PopupRoute<T> {
_curveReverse,
);
static final Tween<double> _sheetScaleTween = Tween<double>();
static final Animatable<double> _sheetScaleAnimatable = _sheetScaleTween.chain(
static final Animatable<double> _sheetScaleAnimatable =
_sheetScaleTween.chain(
_curve,
);
static final Animatable<double> _sheetScaleAnimatableReverse = _sheetScaleTween.chain(
static final Animatable<double> _sheetScaleAnimatableReverse =
_sheetScaleTween.chain(
_curveReverse,
);
final Tween<double> _opacityTween = Tween<double>(begin: 0.0, end: 1.0);
@@ -441,7 +451,8 @@ class _ViewableRoute<T> extends PopupRoute<T> {
return offsetScaled & sizeScaled;
}
static AlignmentDirectional getSheetAlignment(_ViewableLocation contextMenuLocation) {
static AlignmentDirectional getSheetAlignment(
_ViewableLocation contextMenuLocation) {
switch (contextMenuLocation) {
case _ViewableLocation.center:
return AlignmentDirectional.topCenter;
@@ -452,17 +463,24 @@ class _ViewableRoute<T> extends PopupRoute<T> {
}
}
static Rect _getSheetRectBegin(Orientation? orientation, _ViewableLocation contextMenuLocation, Rect childRect, Rect sheetRect) {
static Rect _getSheetRectBegin(Orientation? orientation,
_ViewableLocation contextMenuLocation, Rect childRect, Rect sheetRect) {
switch (contextMenuLocation) {
case _ViewableLocation.center:
final Offset target = orientation == Orientation.portrait ? childRect.bottomCenter : childRect.topCenter;
final Offset target = orientation == Orientation.portrait
? childRect.bottomCenter
: childRect.topCenter;
final Offset centered = target - Offset(sheetRect.width / 2, 0.0);
return centered & sheetRect.size;
case _ViewableLocation.right:
final Offset target = orientation == Orientation.portrait ? childRect.bottomRight : childRect.topRight;
final Offset target = orientation == Orientation.portrait
? childRect.bottomRight
: childRect.topRight;
return (target - Offset(sheetRect.width, 0.0)) & sheetRect.size;
case _ViewableLocation.left:
final Offset target = orientation == Orientation.portrait ? childRect.bottomLeft : childRect.topLeft;
final Offset target = orientation == Orientation.portrait
? childRect.bottomLeft
: childRect.topLeft;
return target & sheetRect.size;
}
}
@@ -478,7 +496,9 @@ class _ViewableRoute<T> extends PopupRoute<T> {
}
void _updateTweenRects() {
final Rect childRect = _scale == null ? _getRect(_childGlobalKey) : _getScaledRect(_childGlobalKey, _scale!);
final Rect childRect = _scale == null
? _getRect(_childGlobalKey)
: _getScaledRect(_childGlobalKey, _scale!);
_rectTween.begin = _previousChildRect;
_rectTween.end = childRect;
@@ -546,21 +566,29 @@ class _ViewableRoute<T> extends PopupRoute<T> {
}
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return Container();
}
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
_lastOrientation = orientation;
if (!animation.isCompleted) {
final bool reverse = animation.status == AnimationStatus.reverse;
final Rect rect = reverse ? _rectAnimatableReverse.evaluate(animation)! : _rectAnimatable.evaluate(animation)!;
final Rect sheetRect = reverse ? _sheetRectAnimatableReverse.evaluate(animation)! : _sheetRectAnimatable.evaluate(animation)!;
final double sheetScale = reverse ? _sheetScaleAnimatableReverse.evaluate(animation) : _sheetScaleAnimatable.evaluate(animation);
final Rect rect = reverse
? _rectAnimatableReverse.evaluate(animation)!
: _rectAnimatable.evaluate(animation)!;
final Rect sheetRect = reverse
? _sheetRectAnimatableReverse.evaluate(animation)!
: _sheetRectAnimatable.evaluate(animation)!;
final double sheetScale = reverse
? _sheetScaleAnimatableReverse.evaluate(animation)
: _sheetScaleAnimatable.evaluate(animation);
return Stack(
children: <Widget>[
Positioned.fromRect(
@@ -623,7 +651,8 @@ class _ContextMenuRouteStatic extends StatefulWidget {
_ContextMenuRouteStaticState createState() => _ContextMenuRouteStaticState();
}
class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with TickerProviderStateMixin {
class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic>
with TickerProviderStateMixin {
static const double _kMinScale = 0.8;
static const double _kSheetScaleThreshold = 0.9;
@@ -639,7 +668,8 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
late Animation<double> _sheetScaleAnimation;
late Animation<double> _sheetOpacityAnimation;
static double _getScale(Orientation orientation, double maxDragDistance, double dy) {
static double _getScale(
Orientation orientation, double maxDragDistance, double dy) {
final double dyDirectional = dy <= 0.0 ? dy : -dy;
return math.max(
_kMinScale,
@@ -659,11 +689,13 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
void _onPanEnd(DragEndDetails details) {
if (details.velocity.pixelsPerSecond.dy.abs() >= kMinFlingVelocity) {
final bool flingIsAway = details.velocity.pixelsPerSecond.dy > 0;
final double finalPosition = flingIsAway ? _moveAnimation.value.dy + 100.0 : 0.0;
final double finalPosition =
flingIsAway ? _moveAnimation.value.dy + 100.0 : 0.0;
if (flingIsAway && _sheetController.status != AnimationStatus.forward) {
_sheetController.forward();
} else if (!flingIsAway && _sheetController.status != AnimationStatus.reverse) {
} else if (!flingIsAway &&
_sheetController.status != AnimationStatus.reverse) {
_sheetController.reverse();
}
@@ -713,20 +745,29 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
widget.onDismiss!(context, _lastScale, _sheetOpacityAnimation.value);
}
Alignment _getChildAlignment(Orientation orientation, _ViewableLocation contextMenuLocation) {
Alignment _getChildAlignment(
Orientation orientation, _ViewableLocation contextMenuLocation) {
switch (contextMenuLocation) {
case _ViewableLocation.center:
return orientation == Orientation.portrait ? Alignment.bottomCenter : Alignment.topRight;
return orientation == Orientation.portrait
? Alignment.bottomCenter
: Alignment.topRight;
case _ViewableLocation.right:
return orientation == Orientation.portrait ? Alignment.bottomCenter : Alignment.topLeft;
return orientation == Orientation.portrait
? Alignment.bottomCenter
: Alignment.topLeft;
case _ViewableLocation.left:
return orientation == Orientation.portrait ? Alignment.bottomCenter : Alignment.topRight;
return orientation == Orientation.portrait
? Alignment.bottomCenter
: Alignment.topRight;
}
}
void _setDragOffset(Offset dragOffset) {
final double endX = _kPadding * dragOffset.dx / _kDamping;
final double endY = dragOffset.dy >= 0.0 ? dragOffset.dy : _kPadding * dragOffset.dy / _kDamping;
final double endY = dragOffset.dy >= 0.0
? dragOffset.dy
: _kPadding * dragOffset.dy / _kDamping;
setState(() {
_dragOffset = dragOffset;
_moveAnimation = Tween<Offset>(
@@ -742,15 +783,20 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
),
);
if (_lastScale <= _kSheetScaleThreshold && _sheetController.status != AnimationStatus.forward && _sheetScaleAnimation.value != 0.0) {
if (_lastScale <= _kSheetScaleThreshold &&
_sheetController.status != AnimationStatus.forward &&
_sheetScaleAnimation.value != 0.0) {
_sheetController.forward();
} else if (_lastScale > _kSheetScaleThreshold && _sheetController.status != AnimationStatus.reverse && _sheetScaleAnimation.value != 1.0) {
} else if (_lastScale > _kSheetScaleThreshold &&
_sheetController.status != AnimationStatus.reverse &&
_sheetScaleAnimation.value != 1.0) {
_sheetController.reverse();
}
});
}
List<Widget> _getChildren(Orientation orientation, _ViewableLocation contextMenuLocation) {
List<Widget> _getChildren(
Orientation orientation, _ViewableLocation contextMenuLocation) {
final Expanded child = Expanded(
child: Align(
alignment: _getChildAlignment(
@@ -781,7 +827,9 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
case _ViewableLocation.center:
return <Widget>[child, spacer, sheet];
case _ViewableLocation.right:
return orientation == Orientation.portrait ? <Widget>[child, spacer, sheet] : <Widget>[sheet, spacer, child];
return orientation == Orientation.portrait
? <Widget>[child, spacer, sheet]
: <Widget>[sheet, spacer, child];
case _ViewableLocation.left:
return <Widget>[child, spacer, sheet];
}
@@ -915,7 +963,8 @@ class _ViewableSheet extends StatelessWidget {
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: CupertinoDynamicColor.resolve(_borderColor, context),
color:
CupertinoDynamicColor.resolve(_borderColor, context),
width: 0.5,
)),
),

View File

@@ -9,7 +9,7 @@ extension Localization on String {
"unexcused": "unexcused %s",
"absence": "absence",
"delay": "delay",
"minute": " minutes of ".one(" minute of "),
"minute": " minute(s) of ",
},
"hu_hu": {
"excused": "igazolt %s",
@@ -25,7 +25,7 @@ extension Localization on String {
"unexcused": "unanerkannt %s",
"absence": "Abwesenheit",
"delay": "Verspätung",
"minute": " Minuten ".one(" Minute "),
"minute": " Minute(n) ",
}
};

View File

@@ -9,7 +9,7 @@ extension Localization on String {
"Mode": "Mode",
"Submit date": "Submit Date",
"show in timetable": "Show in timetable",
"minutes": "minutes".one("minute"),
"minutes": "minute(s)",
"delay": "Delay",
},
"hu_hu": {
@@ -27,7 +27,7 @@ extension Localization on String {
"Mode": "Typ",
"Submit date": "Datum einreichen",
"show in timetable": "im Stundenplan anzeigen",
"minutes": "Minuten".one("Minute"),
"minutes": "Minute(n)",
"delay": "Verspätung",
}
};

View File

@@ -59,7 +59,7 @@ class CertificationTile extends StatelessWidget {
}
return Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(8.0),
child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),

View File

@@ -13,31 +13,43 @@ class CertificationView extends StatelessWidget {
final List<Grade> grades;
final GradeType gradeType;
static show(List<Grade> grades, {required BuildContext context, required GradeType gradeType}) =>
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => CertificationView(grades, gradeType: gradeType)));
static show(List<Grade> grades,
{required BuildContext context, required GradeType gradeType}) =>
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(
builder: (context) =>
CertificationView(grades, gradeType: gradeType)));
@override
Widget build(BuildContext context) {
grades.sort((a, b) => a.subject.name.compareTo(b.subject.name));
List<Widget> tiles = grades.map((e) => CertificationTile(e)).toList();
List<Widget> tiles = grades
.map((e) => CertificationTile(
e,
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 5.0),
))
.toList();
return Scaffold(
body: HeroScrollView(
body: HeroScrollView(
title: getGradeTypeTitle(gradeType),
icon: FeatherIcons.award,
iconSize: 50,
child: ListView(
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
physics: const BouncingScrollPhysics(),
children: [
SafeArea(
child: Panel(
padding:
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
child: Column(
children: tiles,
),
),
)
],
)));
),
),
);
}
}

View File

@@ -38,7 +38,7 @@ class CustomSegmentedControl extends StatelessWidget {
borderRadius: BorderRadius.circular(12.0),
),
thumbDecoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(10.0),
// boxShadow: [
// BoxShadow(

View File

@@ -13,7 +13,7 @@ class EventTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(14.0),
child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),

View File

@@ -31,8 +31,18 @@ class ExamViewable extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (Provider.of<SettingsProvider>(context).newPopups) {
bool pressed = false;
return GestureDetector(
onTap: () => ExamPopup.show(context: context, exam: exam),
onTap: () {
// prevent double tap things
if (pressed) return;
pressed = true;
ExamPopup.show(context: context, exam: exam);
Future.delayed(const Duration(seconds: 2), () {
pressed = false;
});
},
child: ExamTile(
exam,
showSubject: showSubject,
@@ -198,7 +208,7 @@ class ExamPopup extends StatelessWidget {
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(6.0)),
@@ -256,7 +266,7 @@ class ExamPopup extends StatelessWidget {
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(6.0),
bottom: Radius.circular(12.0)),
@@ -338,7 +348,7 @@ class ExamPopup extends StatelessWidget {
// child: Container(
// width: double.infinity,
// decoration: BoxDecoration(
// color: Theme.of(context).colorScheme.background,
// color: Theme.of(context).colorScheme.surface,
// borderRadius: BorderRadius.circular(12.0),
// ),
// padding: const EdgeInsets.all(16.0),

View File

@@ -34,7 +34,7 @@ class ChangedLessonTile extends StatelessWidget {
}
return Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(14.0),
child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),

View File

@@ -417,7 +417,7 @@ class TimetableLessonPopup extends StatelessWidget {
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(6.0),
@@ -521,7 +521,7 @@ class TimetableLessonPopup extends StatelessWidget {
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(6.0),
bottom: Radius.circular(6.0),
@@ -549,7 +549,7 @@ class TimetableLessonPopup extends StatelessWidget {
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.vertical(
top: const Radius.circular(6.0),
bottom: lesson.exam != ''
@@ -580,7 +580,7 @@ class TimetableLessonPopup extends StatelessWidget {
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(6.0),
bottom: Radius.circular(12.0)),
@@ -654,7 +654,7 @@ class TimetableLessonPopup extends StatelessWidget {
// child: Container(
// width: double.infinity,
// decoration: BoxDecoration(
// color: Theme.of(context).colorScheme.background,
// color: Theme.of(context).colorScheme.surface,
// borderRadius: BorderRadius.circular(12.0),
// ),
// padding: const EdgeInsets.all(16.0),

View File

@@ -23,9 +23,9 @@ class MessageViewable extends StatelessWidget {
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
closedShape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
middleColor: Theme.of(context).colorScheme.background,
middleColor: Theme.of(context).colorScheme.surface,
openColor: Theme.of(context).scaffoldBackgroundColor,
closedColor: Theme.of(context).colorScheme.background,
closedColor: Theme.of(context).colorScheme.surface,
transitionType: ContainerTransitionType.fadeThrough,
transitionDuration: const Duration(milliseconds: 400),
useRootNavigator: true,

View File

@@ -17,7 +17,7 @@ extension Localization on String {
"Messages": "Messages",
"Absences": "Absences",
"update_available": "Update Available",
"missed_exams": "You missed %s exams this week.".one("You missed an exam this week."),
"missed_exams": "You missed %s exam(s) this week.",
"missed_exam_contact": "Contact %s, to resolve it!",
},
"hu_hu": {
@@ -34,7 +34,7 @@ extension Localization on String {
"Messages": "Üzenetek",
"Absences": "Hiányok",
"update_available": "Frissítés elérhető",
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról.".one("Ezen a héten hiányoztál egy dolgozatról."),
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról.",
"missed_exam_contact": "Keresd %s-t, ha pótolni szeretnéd!",
},
"de_de": {
@@ -51,7 +51,7 @@ extension Localization on String {
"Messages": "Nachrichten",
"Absences": "Fehlen",
"update_available": "Update verfügbar",
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst.".one("Diese Woche haben Sie eine Prüfung verpasst."),
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst.",
"missed_exam_contact": "Wenden Sie sich an %s, um sie zu erneuern!",
},
};

View File

@@ -48,7 +48,7 @@ class StatisticsTile extends StatelessWidget {
padding: const EdgeInsets.all(18.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)

View File

@@ -84,9 +84,9 @@ class AverageSelectorState extends State<AverageSelector> {
elevation: 8,
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness: MaterialStateProperty.all<double>(6.0),
trackVisibility: MaterialStateProperty.all<bool>(true),
thumbVisibility: MaterialStateProperty.all<bool>(true),
thickness: WidgetStateProperty.all<double>(6.0),
trackVisibility: WidgetStateProperty.all<bool>(true),
thumbVisibility: WidgetStateProperty.all<bool>(true),
),
offset: const Offset(-10, -10),
),

View File

@@ -1,9 +1,13 @@
// ignore_for_file: no_leading_underscores_for_local_identifiers
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:refilc/api/providers/update_provider.dart';
import 'package:refilc/models/settings.dart';
@@ -80,6 +84,7 @@ class GradesPageState extends State<GradesPage> {
late GradeCalculatorProvider calculatorProvider;
late HomeworkProvider homeworkProvider;
late ExamProvider examProvider;
late SettingsProvider settingsProvider;
late String firstName;
late Widget yearlyGraph;
@@ -89,19 +94,18 @@ class GradesPageState extends State<GradesPage> {
int avgDropValue = 0;
bool gradeCalcMode = false;
bool importedViewMode = false;
List<Grade> jsonGrades = [];
List<Grade> getSubjectGrades(GradeSubject subject,
{int days = 0}) =>
!gradeCalcMode
? gradeProvider
.grades
? (importedViewMode ? jsonGrades : gradeProvider.grades)
.where((e) =>
e
.subject ==
subject &&
e.subject == subject &&
e.type == GradeType.midYear &&
(days ==
0 ||
(days == 0 ||
e.date.isBefore(
DateTime.now().subtract(Duration(days: days)))))
.toList()
@@ -110,18 +114,19 @@ class GradesPageState extends State<GradesPage> {
.toList();
void generateTiles() {
List<GradeSubject> subjects = gradeProvider.grades
.map((e) => GradeSubject(
category: e.subject.category,
id: e.subject.id,
name: e.subject.name,
renamedTo: e.subject.renamedTo,
customRounding: e.subject.customRounding,
teacher: e.teacher,
))
.toSet()
.toList()
..sort((a, b) => a.name.compareTo(b.name));
List<GradeSubject> subjects =
(importedViewMode ? jsonGrades : gradeProvider.grades)
.map((e) => GradeSubject(
category: e.subject.category,
id: e.subject.id,
name: e.subject.name,
renamedTo: e.subject.renamedTo,
customRounding: e.subject.customRounding,
teacher: e.teacher,
))
.toSet()
.toList()
..sort((a, b) => a.name.compareTo(b.name));
List<Widget> tiles = [];
Map<GradeSubject, double> subjectAvgs = {};
@@ -159,13 +164,21 @@ class GradesPageState extends State<GradesPage> {
bool hasHomework = homeworkCount > 0;
List<Exam> allExams = examProvider.exams;
allExams.sort((a, b) => a.date.compareTo(b.date));
try {
allExams.sort((a, b) => a.date.compareTo(b.date));
} catch (e) {
if (kDebugMode) {
print('failed to sort exams, reason: flutter');
}
allExams = [];
}
Exam? nearestExam = allExams.firstWhereOrNull((e) =>
e.subject.id == subject.id && e.writeDate.isAfter(DateTime.now()));
bool hasUnder = (hasHomework || nearestExam != null) &&
Provider.of<SettingsProvider>(context).qSubjectsSubTiles;
Provider.of<SettingsProvider>(context, listen: false)
.qSubjectsSubTiles;
return Padding(
padding: i > 1 ? const EdgeInsets.only(top: 9.0) : EdgeInsets.zero,
@@ -192,7 +205,7 @@ class GradesPageState extends State<GradesPage> {
? const Radius.circular(8.0)
: const Radius.circular(16.0),
),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
),
child: Padding(
padding: const EdgeInsets.symmetric(
@@ -220,7 +233,8 @@ class GradesPageState extends State<GradesPage> {
height: 6.0,
),
if (hasHomework &&
Provider.of<SettingsProvider>(context).qSubjectsSubTiles)
Provider.of<SettingsProvider>(context, listen: false)
.qSubjectsSubTiles)
Container(
decoration: BoxDecoration(
boxShadow: [
@@ -242,7 +256,7 @@ class GradesPageState extends State<GradesPage> {
? const Radius.circular(8.0)
: const Radius.circular(16.0),
),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
),
child: Padding(
padding: const EdgeInsets.only(
@@ -293,7 +307,7 @@ class GradesPageState extends State<GradesPage> {
bottomLeft: Radius.circular(16.0),
bottomRight: Radius.circular(16.0),
),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
),
child: ExamViewable(
nearestExam,
@@ -363,8 +377,14 @@ class GradesPageState extends State<GradesPage> {
);
}
// print('rounding:');
// print(settingsProvider.rounding);
double subjectAvg = subjectAvgs.isNotEmpty
? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
? subjectAvgs.values.fold(
0.0,
(double a, double b) =>
a.round().toDouble() + b.round().toDouble()) /
subjectAvgs.length
: 0.0;
final double classAvg = gradeProvider.groupAverages.isNotEmpty
@@ -435,6 +455,7 @@ class GradesPageState extends State<GradesPage> {
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
homeworkProvider = Provider.of<HomeworkProvider>(context);
examProvider = Provider.of<ExamProvider>(context);
settingsProvider = Provider.of<SettingsProvider>(context);
context.watch<PlusProvider>();
@@ -448,25 +469,28 @@ class GradesPageState extends State<GradesPage> {
.fold(0.0, (double a, double b) => a + b) /
gradeProvider.groupAverages.length;
final now = gradeProvider.grades.isNotEmpty
? gradeProvider.grades
.reduce((v, e) => e.date.isAfter(v.date) ? e : v)
.date
: DateTime.now();
final now =
(importedViewMode ? jsonGrades : gradeProvider.grades).isNotEmpty
? (importedViewMode ? jsonGrades : gradeProvider.grades)
.reduce((v, e) => e.date.isAfter(v.date) ? e : v)
.date
: DateTime.now();
final currentStudentAvg = AverageHelper.averageEvals(!gradeCalcMode
? gradeProvider.grades
? (importedViewMode ? jsonGrades : gradeProvider.grades)
.where((e) => e.type == GradeType.midYear)
.toList()
: calculatorProvider.grades);
final prevStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
final prevStudentAvg = AverageHelper.averageEvals((importedViewMode
? jsonGrades
: gradeProvider.grades)
.where((e) => e.type == GradeType.midYear)
.where((e) => e.date.isBefore(now.subtract(const Duration(days: 30))))
.toList());
List<Grade> graphGrades = !gradeCalcMode
? gradeProvider.grades
? (importedViewMode ? jsonGrades : gradeProvider.grades)
.where((e) =>
e.type == GradeType.midYear &&
(avgDropValue == 0 ||
@@ -500,7 +524,7 @@ class GradesPageState extends State<GradesPage> {
// const SizedBox(width: 4.0),
TrendDisplay(
previous: prevStudentAvg, current: currentStudentAvg),
if (gradeProvider.grades
if ((importedViewMode ? jsonGrades : gradeProvider.grades)
.where((e) => e.type == GradeType.midYear)
.isNotEmpty)
AverageDisplay(average: currentStudentAvg),
@@ -632,7 +656,8 @@ class GradesPageState extends State<GradesPage> {
void gradeCalcTotal(BuildContext context) {
calculatorProvider.clear();
calculatorProvider.addAllGrades(gradeProvider.grades);
calculatorProvider
.addAllGrades((importedViewMode ? jsonGrades : gradeProvider.grades));
_sheetController = _scaffoldKey.currentState?.showBottomSheet(
(context) => const RoundedBottomSheet(
@@ -664,7 +689,7 @@ class GradesPageState extends State<GradesPage> {
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: ListTile(
title: Row(
children: [
@@ -676,12 +701,12 @@ class GradesPageState extends State<GradesPage> {
],
),
onTap: () {
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.totalGradeCalculator)) {
PlusLockedFeaturePopup.show(
context: context, feature: PremiumFeature.gradeCalculation);
return;
}
// if (!Provider.of<PlusProvider>(context, listen: false)
// .hasScope(PremiumScopes.totalGradeCalculator)) {
// PlusLockedFeaturePopup.show(
// context: context, feature: PremiumFeature.gradeCalculation);
// return;
// }
// SoonAlert.show(context: context);
gradeCalcTotal(context);
@@ -696,7 +721,65 @@ class GradesPageState extends State<GradesPage> {
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: ListTile(
title: Row(
children: [
const Icon(Icons.toll_rounded),
const SizedBox(
width: 10.0,
),
Text('import_grades'.i18n),
],
),
trailing: importedViewMode ? const Icon(FeatherIcons.x) : null,
onTap: () {
if (importedViewMode) {
importedViewMode = false;
generateTiles();
setState(() {});
Navigator.of(context, rootNavigator: true).pop();
return;
}
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.gradeExporting)) {
PlusLockedFeaturePopup.show(
context: context, feature: PremiumFeature.gradeExporting);
return;
}
// show file picker
FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['json'],
).then((value) {
if (value != null) {
final File file = File(value.files.single.path!);
final String content = file.readAsStringSync();
final List<dynamic> json = jsonDecode(content);
jsonGrades = json.map((e) => Grade.fromJson(e)).toList();
importedViewMode = true;
generateTiles();
setState(() {});
}
});
Navigator.of(context, rootNavigator: true).pop();
},
),
),
const SizedBox(
height: 10.0,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.surface),
child: SwitchListTile(
title: Row(
children: [

View File

@@ -4,8 +4,9 @@ extension Localization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"Grades": "Subjects",
"Ghost Grades": "Grades",
"Grades": "Grades",
"page_title_grades": "Subjects",
"Ghost Grades": "Ghost Grades",
"Subjects": "Your Subjects",
"Subjects_changes": "Subject Differences",
"empty": "You don't have any subjects.",
@@ -17,8 +18,7 @@ extension Localization on String {
"subjectavg": "Subject Average",
"classavg": "Class Average",
"fail_warning": "Failure warning",
"fail_warning_description":
"You are failing %d subjects!".one("You are failing a subject!"),
"fail_warning_description": "You are failing %d subject(s)!",
"data": "Data",
"you_have_hw": "You have %s homework(s) to do",
"grades_cnt": "Grade count: %s",
@@ -29,6 +29,7 @@ extension Localization on String {
"grades": "Grades",
"show_exams_homework": "Exams and Homework",
"grade_calc": "Grade Calculator",
"import_grades": "Import Grades (JSON)",
},
"hu_hu": {
"Grades": "Jegyek",
@@ -56,9 +57,11 @@ extension Localization on String {
"grades": "Jegyek",
"show_exams_homework": "Dolgozatok és házik",
"grade_calc": "Jegy kalkulátor",
"import_grades": "Jegyek importálása (JSON)",
},
"de_de": {
"Grades": "Fächer",
"Grades": "Klassen",
"page_title_grades": "Themen",
"Ghost Grades": "Geist Noten",
"Subjects": "Ihre Themen",
"Subjects_changes": "Betreff Änderungen",
@@ -82,6 +85,7 @@ extension Localization on String {
"grades": "Noten",
"show_exams_homework": "Referate und Hausaufgaben",
"grade_calc": "Noten-Rechner",
"import_grades": "Noten importieren (JSON)",
},
};

View File

@@ -179,7 +179,7 @@ class GradeGraphState extends State<GradeGraph> {
? Alignment.topRight
: Alignment.topLeft,
style: TextStyle(
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.surface,
color: AppColors.of(context).text,
fontSize: 16.0,
fontWeight: FontWeight.w600,
@@ -217,7 +217,7 @@ class GradeGraphState extends State<GradeGraph> {
height: 158,
child: subjectSpots.length > 1
? Padding(
padding: const EdgeInsets.only(top: 8.0, right: 8.0),
padding: const EdgeInsets.only(top: 6.0, right: 0.0),
child: LineChart(
LineChartData(
extraLinesData: ExtraLinesData(
@@ -228,22 +228,32 @@ class GradeGraphState extends State<GradeGraph> {
preventCurveOverShooting: false,
spots: subjectSpots,
isCurved: true,
colors: averageColors.reversed.toList(),
// colors: averageColors.reversed.toList(),
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: averageColors.reversed.toList(),
),
barWidth: 6,
curveSmoothness: 0.2,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
colors: [
averageColor.withOpacity(0.7),
averageColor.withOpacity(0.3),
averageColor.withOpacity(0.2),
averageColor.withOpacity(0.1),
],
gradientColorStops: [0.1, 0.6, 0.8, 1],
gradientFrom: const Offset(0, 0),
gradientTo: const Offset(0, 1),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
averageColor.withOpacity(0.7),
averageColor.withOpacity(0.3),
averageColor.withOpacity(0.2),
averageColor.withOpacity(0.1),
],
stops: const [0.1, 0.6, 0.8, 1],
),
// gradientColorStops: [0.1, 0.6, 0.8, 1],
// gradientFrom: const Offset(0, 0),
// gradientTo: const Offset(0, 1),
),
),
if (ghostData.isNotEmpty && ghostSpots.isNotEmpty)
@@ -251,28 +261,41 @@ class GradeGraphState extends State<GradeGraph> {
preventCurveOverShooting: false,
spots: ghostSpots,
isCurved: true,
colors: [AppColors.of(context).text],
color: AppColors.of(context).text,
barWidth: 6,
curveSmoothness: 0.2,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
colors: [
AppColors.of(context).text.withOpacity(0.7),
AppColors.of(context).text.withOpacity(0.3),
AppColors.of(context).text.withOpacity(0.2),
AppColors.of(context).text.withOpacity(0.1),
],
gradientColorStops: [0.1, 0.6, 0.8, 1],
gradientFrom: const Offset(0, 0),
gradientTo: const Offset(0, 1),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.of(context)
.text
.withOpacity(0.7),
AppColors.of(context)
.text
.withOpacity(0.3),
AppColors.of(context)
.text
.withOpacity(0.2),
AppColors.of(context)
.text
.withOpacity(0.1),
],
stops: const [0.1, 0.6, 0.8, 1],
),
// gradientColorStops: [0.1, 0.6, 0.8, 1],
// gradientFrom: const Offset(0, 0),
// gradientTo: const Offset(0, 1),
),
),
],
minY: 1,
maxY: 5,
gridData: FlGridData(
gridData: const FlGridData(
show: true,
horizontalInterval: 1,
// checkToShowVerticalLine: (_) => false,
@@ -286,8 +309,8 @@ class GradeGraphState extends State<GradeGraph> {
// ),
),
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: Colors.grey.shade800,
touchTooltipData: const LineTouchTooltipData(
// tooltipBgColor: Colors.grey.shade800,
fitInsideVertically: true,
fitInsideHorizontally: true,
),
@@ -321,64 +344,111 @@ class GradeGraphState extends State<GradeGraph> {
),
),
titlesData: FlTitlesData(
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 24,
getTextStyles: (context, value) => TextStyle(
color:
AppColors.of(context).text.withOpacity(.75),
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
margin: 12.0,
getTitles: (value) {
var format = DateFormat(
"MMM", I18n.of(context).locale.toString());
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 25,
getTitlesWidget: (value, meta) {
if (value == meta.max || value == meta.min) {
return Container();
}
String title = format
.format(DateTime(0, value.floor() % 12))
.replaceAll(".", "");
title =
title.substring(0, min(title.length, 4));
var format = DateFormat("MMM",
I18n.of(context).locale.toString());
return title.toUpperCase();
},
interval: () {
List<Grade> tData =
ghostData.isNotEmpty ? ghostData : data;
tData.sort((a, b) =>
a.writeDate.compareTo(b.writeDate));
return ghostData.isNotEmpty
? 3.0
: tData.first.writeDate
.add(const Duration(days: 120))
.isBefore(tData.last.writeDate)
? 2.0
: 2.5;
}(),
checkToShowTitle: (double minValue,
double maxValue,
SideTitles sideTitles,
double appliedInterval,
double value) {
if (value == maxValue || value == minValue) {
return false;
}
return true;
},
),
leftTitles: SideTitles(
showTitles: true,
interval: 1.0,
getTextStyles: (context, value) => TextStyle(
color: AppColors.of(context).text,
fontWeight: FontWeight.bold,
fontSize: 18.0,
String title = format
.format(DateTime(0, value.floor() % 12))
.replaceAll(".", "");
title =
title.substring(0, min(title.length, 4));
return Text(
title.toUpperCase(),
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(.75),
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
);
},
// getTextStyles: (context, value) => TextStyle(
// color: AppColors.of(context)
// .text
// .withOpacity(.75),
// fontWeight: FontWeight.bold,
// fontSize: 14.0,
// ),
// margin: 12.0,
// getTitles: (value) {
// var format = DateFormat("MMM",
// I18n.of(context).locale.toString());
// String title = format
// .format(DateTime(0, value.floor() % 12))
// .replaceAll(".", "");
// title =
// title.substring(0, min(title.length, 4));
// return title.toUpperCase();
// },
interval: () {
List<Grade> tData =
ghostData.isNotEmpty ? ghostData : data;
tData.sort((a, b) =>
a.writeDate.compareTo(b.writeDate));
return ghostData.isNotEmpty
? 3.0
: tData.first.writeDate
.add(const Duration(days: 120))
.isBefore(tData.last.writeDate)
? 2.0
: 2.5;
}(),
// checkToShowTitle: (double minValue,
// double maxValue,
// SideTitles sideTitles,
// double appliedInterval,
// double value) {
// if (value == maxValue || value == minValue) {
// return false;
// }
// return true;
// },
),
margin: 16,
),
rightTitles: SideTitles(showTitles: false),
topTitles: SideTitles(showTitles: false),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 1.0,
reservedSize: 26.0,
getTitlesWidget: (value, meta) => Padding(
padding: const EdgeInsets.only(
left: 6.0, right: 10.0),
child: Text(
value.toInt().toString(),
style: TextStyle(
color: AppColors.of(context).text,
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
),
// getTextStyles: (context, value) => TextStyle(
// color: AppColors.of(context).text,
// fontWeight: FontWeight.bold,
// fontSize: 18.0,
// ),
// margin: 16,
),
),
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
),
),
),

View File

@@ -19,8 +19,7 @@ extension Localization on String {
"Messages": "Messages",
"Absences": "Absences",
"update_available": "Update Available",
"missed_exams": "You missed %s exams this week."
.one("You missed an exam this week."),
"missed_exams": "You missed %s exam(s) this week.",
"missed_exam_contact": "Contact %s to resolve it!",
},
"hu_hu": {
@@ -39,8 +38,7 @@ extension Localization on String {
"Messages": "Üzenetek",
"Absences": "Hiányzások",
"update_available": "Frissítés elérhető",
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról."
.one("Ezen a héten hiányoztál egy dolgozatról."),
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról.",
"missed_exam_contact": "Keresd %s tanár urat/hölgyet, hogy pótold!",
},
"de_de": {
@@ -59,8 +57,7 @@ extension Localization on String {
"Messages": "Nachrichten",
"Absences": "Fehlen",
"update_available": "Update verfügbar",
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst."
.one("Diese Woche haben Sie eine Prüfung verpasst."),
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst.",
"missed_exam_contact": "Wenden Sie sich an %s, um sie zu erneuern!",
},
};

View File

@@ -1,6 +1,7 @@
// ignore_for_file: unnecessary_null_comparison
import 'package:animations/animations.dart';
import 'package:flutter/services.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/helpers/subject.dart';
import 'package:refilc/models/settings.dart';
@@ -43,6 +44,10 @@ class LiveCardStateA extends State<LiveCard> {
_userProvider = Provider.of<UserProvider>(context, listen: false);
liveCard = Provider.of<LiveCardProvider>(context, listen: false);
_userProvider.addListener(liveCard.update);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
}
@override
@@ -419,14 +424,38 @@ class LiveCardStateA extends State<LiveCard> {
if (progressCurrent != null &&
progressMax != null)
GestureDetector(
onTap: () {
showDialog(
onTap: () async {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness:
Brightness.dark,
systemNavigationBarColor:
Colors.transparent,
systemNavigationBarIconBrightness:
Brightness.dark,
));
var result = await showDialog(
barrierColor: Colors.black,
context: context,
builder: (context) => HeadsUpCountdown(
maxTime: maxTime,
elapsedTime: elapsedTime),
);
if (result != null) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
}
},
child: Container(
color: Colors.transparent,
@@ -825,14 +854,38 @@ class LiveCardStateA extends State<LiveCard> {
if (progressCurrent != null &&
progressMax != null)
GestureDetector(
onTap: () {
showDialog(
onTap: () async {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
]);
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness:
Brightness.dark,
systemNavigationBarColor:
Colors.transparent,
systemNavigationBarIconBrightness:
Brightness.dark,
));
var result = await showDialog(
barrierColor: Colors.black,
context: context,
builder: (context) => HeadsUpCountdown(
maxTime: maxTime,
elapsedTime: elapsedTime),
);
if (result != null) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
}
},
child: Container(
color: Colors.transparent,

View File

@@ -5,8 +5,8 @@ extension Localization on String {
{
"en_en": {
"next": "Next",
"remaining min": "%d mins".one("%d min"),
"remaining sec": "%d secs".one("%d sec"),
"remaining min": "%d min(s)",
"remaining sec": "%d sec(s)",
"break": "Break",
"go to room": "Go to room %s.",
"go ground floor": "Go to the ground floor.",
@@ -27,8 +27,8 @@ extension Localization on String {
},
"hu_hu": {
"next": "Következő",
"remaining min": "%d perc".one("%d perc"),
"remaining sec": "%d másodperc".one("%d másodperc"),
"remaining min": "%d perc",
"remaining sec": "%d másodperc",
"break": "Szünet",
"go to room": "Menj a(z) %s terembe.",
"go ground floor": "Menj a földszintre.",
@@ -49,8 +49,8 @@ extension Localization on String {
},
"de_de": {
"next": "Nächste",
"remaining min": "%d Minuten".one("%d Minute"),
"remaining sec": "%d Sekunden".one("%d Sekunden"),
"remaining min": "%d Minute(n)",
"remaining sec": "%d Sekunde(n)",
"break": "Pause",
"go to room": "Geh in den Raum %s.",
"go ground floor": "Geh dir Treppe hinunter.",

View File

@@ -72,10 +72,10 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
? const EdgeInsets.all(12.0)
: EdgeInsets.zero,
decoration: BoxDecoration(
// color: Theme.of(context).colorScheme.background,
// color: Theme.of(context).colorScheme.surface,
color: widget.children != null
? Colors.transparent
: Theme.of(context).colorScheme.background,
: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)

View File

@@ -125,7 +125,7 @@ class MessagesPageState extends State<MessagesPage>
BackButton(
style: ButtonStyle(
splashFactory: NoSplash.splashFactory,
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
padding: WidgetStateProperty.all<EdgeInsetsGeometry>(
EdgeInsets.zero),
),
),

View File

@@ -105,9 +105,9 @@ class SendMessageSheetState extends State<SendMessageSheet> {
elevation: 8,
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness: MaterialStateProperty.all<double>(6.0),
trackVisibility: MaterialStateProperty.all<bool>(true),
thumbVisibility: MaterialStateProperty.all<bool>(true),
thickness: WidgetStateProperty.all<double>(6.0),
trackVisibility: WidgetStateProperty.all<bool>(true),
thumbVisibility: WidgetStateProperty.all<bool>(true),
),
offset: const Offset(-10, -10),
),

View File

@@ -88,6 +88,7 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
// todo tiles
List<Widget> toDoTiles = [];
// TODO: FIX THIS ASAP
if (hw.isNotEmpty &&
Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.unlimitedSelfNotes)) {
@@ -98,13 +99,23 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
'${(e.subject.isRenamed ? e.subject.renamedTo : e.subject.name) ?? ''}, ${e.content.escapeHtml()}',
isTicked: doneItems[e.id] ?? false,
onTap: (p0) async {
// print(p0);
// print(doneItems);
if (!doneItems.containsKey(e.id)) {
doneItems.addAll({e.id: p0});
} else {
doneItems[e.id] = p0;
}
// print(doneItems);
// print(doneItems[e.id]);
// print(user.id);
await databaseProvider.userStore
.storeToDoItem(doneItems, userId: user.id!);
setState(() {});
// print(
// await databaseProvider.userQuery.toDoItems(userId: user.id!));
},
)));
}
@@ -116,10 +127,20 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
description: e.content,
isTicked: e.done,
onTap: (p0) async {
todoItems.firstWhere((element) => element.id == e.id).done = p0;
final todoItemIndex =
todoItems.indexWhere((element) => element.id == e.id);
if (todoItemIndex != -1) {
TodoItem todoItem = todoItems[todoItemIndex];
Map<String, dynamic> todoItemJson = todoItem.toJson;
todoItemJson['done'] = p0;
todoItem = TodoItem.fromJson(todoItemJson);
todoItems[todoItemIndex] = todoItem;
await databaseProvider.userStore
.storeSelfTodoItems(todoItems, userId: user.id!);
}
await databaseProvider.userStore
.storeSelfTodoItems(todoItems, userId: user.id!);
// await databaseProvider.userStore
// .storeSelfTodoItems(todoItems, userId: user.id!);
},
)));
}
@@ -410,7 +431,7 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: ListTile(
title: Row(
children: [
@@ -432,7 +453,7 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: ListTile(
title: Row(
children: [
@@ -456,7 +477,7 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: ListTile(
title: Row(
children: [
@@ -468,13 +489,13 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
],
),
onTap: () {
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.unlimitedSelfNotes)) {
PlusLockedFeaturePopup.show(
context: context, feature: PremiumFeature.selfNotes);
// if (!Provider.of<PlusProvider>(context, listen: false)
// .hasScope(PremiumScopes.unlimitedSelfNotes)) {
// PlusLockedFeaturePopup.show(
// context: context, feature: PremiumFeature.selfNotes);
return;
}
// return;
// }
showTaskCreation(context);
},

View File

@@ -22,7 +22,7 @@ class SelfNoteTile extends StatelessWidget {
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)

View File

@@ -700,7 +700,7 @@ class TimetablePageState extends State<TimetablePage>
],
color: Theme.of(context)
.colorScheme
.background,
.surface,
borderRadius:
BorderRadius.only(
topLeft: index == 0
@@ -786,7 +786,7 @@ class TimetablePageState extends State<TimetablePage>
indicatorPadding:
const EdgeInsets.symmetric(horizontal: 10.0),
indicator: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
// color: Colors.transparent,
// border: Border.all(
// color: AppColors.of(context)
@@ -798,7 +798,7 @@ class TimetablePageState extends State<TimetablePage>
// .withOpacity(0.25),
borderRadius: BorderRadius.circular(16.0),
),
overlayColor: MaterialStateProperty.all(
overlayColor: WidgetStateProperty.all(
const Color(0x00000000)),
// Tabs
padding: const EdgeInsets.symmetric(
@@ -899,7 +899,7 @@ class TimetablePageState extends State<TimetablePage>
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: ListTile(
contentPadding: const EdgeInsets.only(left: 16.0, right: 10.0),
title: Row(
@@ -943,7 +943,7 @@ class TimetablePageState extends State<TimetablePage>
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 16.0, right: 10.0),
title: Row(
@@ -990,7 +990,7 @@ class TimetablePageState extends State<TimetablePage>
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
color: Theme.of(context).colorScheme.surface),
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 16.0, right: 10.0),
title: Row(

View File

@@ -51,7 +51,7 @@ class ActiveSponsorCard extends StatelessWidget {
return const SizedBox();
}
Color? glow = Colors.white; //TODO: only temp fix kima (idk what but die)
Color? glow = Colors.white;
switch (level) {
case PremiumFeatureLevel.cap:

View File

@@ -222,10 +222,11 @@ class PlusScreenState extends State<PlusScreen> {
// ['👥', 'rfp_2'.i18n],
['👋', 'rfp_3'.i18n],
['📓', 'rfp_4'.i18n],
['🎓', 'rfp_6'.i18n],
// ['🎓', 'rfp_6'.i18n],
['📩', 'rfp_17'.i18n],
['🪟', 'rfp_18'.i18n],
['👕', 'rfp_14'.i18n],
['👑', 'rfp_15'.i18n],
['📩', 'rfp_17'.i18n],
['🔜', 'more_soon'.i18n],
],
docsAccepted: docsAccepted,
@@ -389,6 +390,8 @@ class PlusScreenState extends State<PlusScreen> {
),
),
child: CheckboxListTile(
side:
const BorderSide(color: Colors.black, width: 2.0),
contentPadding:
const EdgeInsets.only(left: 15.0, right: 10.0),
value: docsAccepted,

View File

@@ -46,6 +46,7 @@ extension SettingsLocalization on String {
"rfp_15": "Subscriber role in our Discord community",
"rfp_16": "Private leaks and informations about upcoming features",
"rfp_17": "Grade exporting",
"rfp_18": "Viewing exported grades",
// other
"and": " and ",
"every": "Every ",
@@ -98,6 +99,7 @@ extension SettingsLocalization on String {
"rfp_15": "Előfizetői rang a Discord szerverünkön",
"rfp_16": "Privát betekintések és információk közelgő újításokról",
"rfp_17": "Jegy exportálás",
"rfp_18": "Exportált jegyek megtekintése",
// other
"and": " és ",
"every": "Minden ",
@@ -153,6 +155,7 @@ extension SettingsLocalization on String {
"rfp_15": "Abonnentenrolle in unserer Discord-Community",
"rfp_16": "Private Leaks und Informationen über kommende Funktionen",
"rfp_17": "Notenexport",
"rfp_18": "Anzeigen exportierter Noten",
// other
"and": " und ",
"every": "Jeder ",

View File

@@ -110,10 +110,10 @@ class ErrorReportScreen extends StatelessWidget {
height: 48,
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(vertical: 10.0)),
backgroundColor: MaterialStateProperty.all(Colors.black),
shape: MaterialStateProperty.all(
backgroundColor: WidgetStateProperty.all(Colors.black),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
),
@@ -135,18 +135,18 @@ class ErrorReportScreen extends StatelessWidget {
height: 48,
child: OutlinedButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(vertical: 14.0),
),
backgroundColor: MaterialStateProperty.all(
backgroundColor: WidgetStateProperty.all(
const Color(0xFFF3F7FE),
),
shape: MaterialStateProperty.all(
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
),
side: MaterialStateProperty.all(
side: WidgetStateProperty.all(
const BorderSide(width: 1.0, color: Color(0xFFC7D3EB)),
),
),
@@ -276,4 +276,4 @@ class ErrorDetail extends StatelessWidget {
),
);
}
}
}

View File

@@ -5,7 +5,7 @@ extension SettingsLocalization on String {
{
"en_en": {
"ekretaYou": "e-KRÉTA, you",
"description": "An error occurred!",
"description": "Unexpected error while using the application!",
"submit": "Submit",
"goback": "Go back",
"details": "Details",
@@ -15,11 +15,11 @@ extension SettingsLocalization on String {
"stack": "Stack Trace",
"done": "Done",
"smth_went_wrong":
"Something went wrong, it is of course the fault of Educational Development Informatikai Zrt. (e-KRÉTA) in any case! /s",
"An unexpected error occurred while using the app.",
},
"hu_hu": {
"ekretaYou": "e-KRÉTA, te",
"description": "Fasz-emulátor hivatásos balfasz!",
"description": "Váratlan hiba az alkalmazás használata közben!",
"submit": "Hiba jelentése",
"goback": "Vissza",
"details": "Részletek",
@@ -29,11 +29,11 @@ extension SettingsLocalization on String {
"stack": "Stacktrace",
"done": "Kész",
"smth_went_wrong":
"Valami probléma történt, ez természetesen az Educational Development Informatikai Zrt. (e-KRÉTA) hibája minden esetben! /s",
"Nem várt hiba következett be az alkalmazás használata közben.",
},
"de_de": {
"ekretaYou": "e-KRÉTA, du",
"description": "Ein Fehler ist aufgetreten!",
"description": "Unerwarteter Fehler bei der Benutzung der Anwendung!",
"submit": "Abschicken",
"goback": "Zurück",
"details": "Details",
@@ -43,7 +43,7 @@ extension SettingsLocalization on String {
"stack": "Stack Trace",
"done": "Fertig",
"smth_went_wrong":
"Irgendetwas ist schief gelaufen, es ist natürlich auf jeden Fall die Schuld der Educational Development Informatikai Zrt. (e-KRÉTA)! /s",
"Bei der Benutzung der Anwendung ist ein unerwarteter Fehler aufgetreten.",
},
};

View File

@@ -39,7 +39,7 @@ class ErrorScreen extends StatelessWidget {
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0),
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
),
child: CupertinoScrollbar(
child: SingleChildScrollView(

View File

@@ -1,137 +1,104 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:webview_flutter/webview_flutter.dart';
class KretenLoginScreen extends StatefulWidget {
const KretenLoginScreen({super.key});
class KretenLoginWidget extends StatefulWidget {
const KretenLoginWidget({super.key, required this.onLogin});
// final String selectedSchool;
final void Function(String code) onLogin;
@override
State<KretenLoginScreen> createState() => _KretenLoginScreenState();
State<KretenLoginWidget> createState() => _KretenLoginWidgetState();
}
class _KretenLoginScreenState extends State<KretenLoginScreen> {
class _KretenLoginWidgetState extends State<KretenLoginWidget>
with TickerProviderStateMixin {
late final WebViewController controller;
late AnimationController _animationController;
var loadingPercentage = 0;
var currentUrl = '';
bool _hasFadedIn = false;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, // Use the TickerProviderStateMixin
duration: const Duration(milliseconds: 350),
);
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (url) async {
setState(() {
loadingPercentage = 0;
currentUrl = url;
});
onNavigationRequest: (n) async {
if (n.url.startsWith('https://mobil.e-kreta.hu')) {
setState(() {
loadingPercentage = 0;
currentUrl = n.url;
});
List<String> requiredThings = url
.replaceAll(
'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=',
'')
.replaceAll(
'&scope=openid email offline_access kreta-ellenorzo-webapi.public kreta-eugyintezes-webapi.public kreta-fileservice-webapi.public kreta-mobile-global-webapi.public kreta-dkt-webapi.public kreta-ier-webapi.public&state=refilc_student_mobile&session_state=',
':')
.split(':');
String code = requiredThings[0];
// String sessionState = requiredThings[1];
debugPrint('url: $url');
// actual login (token grant) logic
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
"accept": "*/*",
"user-agent":
"eKretaStudent/264745 CFNetwork/1494.0.7 Darwin/23.4.0",
"code_verifier": "THDUSddKOOndwCkqBtVHvRjh2LK0V2kMyLP2QirqVWQ",
};
Map? res = await Provider.of<KretaClient>(context, listen: false)
.postAPI(KretaAPI.login, headers: headers, body: {
"code": code,
"redirect_uri":
"https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect",
"client_id": "kreta-ellenorzo-student-mobile-ios",
"grant_type": "authorization_code",
});
if (res != null) {
print(res);
// if (res.containsKey("error")) {
// if (res["error"] == "invalid_grant") {
// print("ERROR: invalid_grant");
// return;
// }
// } else {
// if (res.containsKey("access_token")) {
// try {
// Provider.of<KretaClient>(context, listen: false).accessToken =
// res["access_token"];
// Map? studentJson =
// await Provider.of<KretaClient>(context, listen: false)
// .getAPI(KretaAPI.student(instituteCode));
// Student student = Student.fromJson(studentJson!);
// var user = User(
// username: username,
// password: password,
// instituteCode: instituteCode,
// name: student.name,
// student: student,
// role: JwtUtils.getRoleFromJWT(res["access_token"])!,
// );
// if (onLogin != null) onLogin(user);
// // Store User in the database
// 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);
// // Get user data
// try {
// await Future.wait([
// Provider.of<GradeProvider>(context, listen: false)
// .fetch(),
// Provider.of<TimetableProvider>(context, listen: false)
// .fetch(week: Week.current()),
// Provider.of<ExamProvider>(context, listen: false).fetch(),
// Provider.of<HomeworkProvider>(context, listen: false)
// .fetch(),
// Provider.of<MessageProvider>(context, listen: false)
// .fetchAll(),
// Provider.of<MessageProvider>(context, listen: false)
// .fetchAllRecipients(),
// Provider.of<NoteProvider>(context, listen: false).fetch(),
// Provider.of<EventProvider>(context, listen: false)
// .fetch(),
// Provider.of<AbsenceProvider>(context, listen: false)
// .fetch(),
// ]);
// } catch (error) {
// print("WARNING: failed to fetch user data: $error");
// }
// if (onSuccess != null) onSuccess();
// return LoginState.success;
// } catch (error) {
// print("ERROR: loginAPI: $error");
// // maybe check debug mode
// // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("ERROR: $error")));
// return LoginState.failed;
// }
// }
// final String instituteCode = widget.selectedSchool;
// if (!n.url.startsWith(
// 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=')) {
// return;
// }
List<String> requiredThings = n.url
.replaceAll(
'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=',
'')
.replaceAll(
'&scope=openid%20email%20offline_access%20kreta-ellenorzo-webapi.public%20kreta-eugyintezes-webapi.public%20kreta-fileservice-webapi.public%20kreta-mobile-global-webapi.public%20kreta-dkt-webapi.public%20kreta-ier-webapi.public&state=refilc_student_mobile&session_state=',
':')
.split(':');
String code = requiredThings[0];
// String sessionState = requiredThings[1];
widget.onLogin(code);
// Future.delayed(const Duration(milliseconds: 500), () {
// Navigator.of(context).pop();
// });
// Navigator.of(context).pop();
return NavigationDecision.prevent;
} else {
return NavigationDecision.navigate;
}
},
onPageStarted: (url) async {
// setState(() {
// loadingPercentage = 0;
// currentUrl = url;
// });
// // final String instituteCode = widget.selectedSchool;
// if (!url.startsWith(
// 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=')) {
// return;
// }
// List<String> requiredThings = url
// .replaceAll(
// 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=',
// '')
// .replaceAll(
// '&scope=openid%20email%20offline_access%20kreta-ellenorzo-webapi.public%20kreta-eugyintezes-webapi.public%20kreta-fileservice-webapi.public%20kreta-mobile-global-webapi.public%20kreta-dkt-webapi.public%20kreta-ier-webapi.public&state=refilc_student_mobile&session_state=',
// ':')
// .split(':');
// String code = requiredThings[0];
// // String sessionState = requiredThings[1];
// widget.onLogin(code);
// // Future.delayed(const Duration(milliseconds: 500), () {
// // Navigator.of(context).pop();
// // });
// // Navigator.of(context).pop();
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
@@ -145,7 +112,7 @@ class _KretenLoginScreenState extends State<KretenLoginScreen> {
))
..loadRequest(
Uri.parse(
'https://idp.e-kreta.hu/connect/authorize?prompt=login&nonce=refilc&response_type=code&code_challenge_method=S256&scope=openid%20email%20offline_access%20kreta-ellenorzo-webapi.public%20kreta-eugyintezes-webapi.public%20kreta-fileservice-webapi.public%20kreta-mobile-global-webapi.public%20kreta-dkt-webapi.public%20kreta-ier-webapi.public&code_challenge=Oj_aVMRJHYsv00mrtGJY72NJa7HY54lVnU2Cb4CWbWw&redirect_uri=https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect&client_id=kreta-ellenorzo-student-mobile-ios&state=refilc_student_mobile'),
'https://idp.e-kreta.hu/connect/authorize?prompt=login&nonce=wylCrqT4oN6PPgQn2yQB0euKei9nJeZ6_ffJ-VpSKZU&response_type=code&code_challenge_method=S256&scope=openid%20email%20offline_access%20kreta-ellenorzo-webapi.public%20kreta-eugyintezes-webapi.public%20kreta-fileservice-webapi.public%20kreta-mobile-global-webapi.public%20kreta-dkt-webapi.public%20kreta-ier-webapi.public&code_challenge=HByZRRnPGb-Ko_wTI7ibIba1HQ6lor0ws4bcgReuYSQ&redirect_uri=https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect&client_id=kreta-ellenorzo-student-mobile-ios&state=refilc_student_mobile'), // &institute_code=${widget.selectedSchool}
);
}
@@ -156,24 +123,57 @@ class _KretenLoginScreenState extends State<KretenLoginScreen> {
// Nonce nonce = getNonce(nonceStr, );
// }
@override
void dispose() {
// Step 3: Dispose of the animation controller
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: const BackButton(),
title: const Text('e-KRÉTA Bejelentkezés'),
),
body: Stack(
children: [
WebViewWidget(
controller: controller,
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
// Trigger the fade-in animation only once when loading reaches 100%
if (loadingPercentage == 100 && !_hasFadedIn) {
_animationController.forward(); // Play the animation
_hasFadedIn =
true; // Set the flag to true, so the animation is not replayed
}
return Stack(
children: [
// Webview that will be displayed only when the loading is 100%
if (loadingPercentage == 100)
FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn,
),
),
],
),
child: WebViewWidget(
controller: controller,
),
),
// Show the CircularProgressIndicator while loading is not 100%
if (loadingPercentage < 100)
Center(
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: loadingPercentage / 100.0),
duration: const Duration(milliseconds: 300),
builder: (context, double value, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
value: value, // Smoothly animates the progress
),
],
);
},
),
),
],
);
}
}

View File

@@ -3,8 +3,10 @@
import 'package:refilc/api/client.dart';
import 'package:refilc/api/login.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
import 'package:refilc_mobile_ui/common/custom_snack_bar.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart';
import 'package:refilc_mobile_ui/common/widgets/absence/absence_display.dart';
import 'package:refilc_mobile_ui/screens/login/login_button.dart';
import 'package:refilc_mobile_ui/screens/login/login_input.dart';
import 'package:refilc_mobile_ui/screens/login/school_input/school_input.dart';
@@ -12,6 +14,9 @@ import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'login_screen.i18n.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:refilc_mobile_ui/screens/login/kreten_login.dart'; //new library for new web login
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key, this.back = false});
@@ -27,6 +32,7 @@ class LoginScreenState extends State<LoginScreen> {
final passwordController = TextEditingController();
final schoolController = SchoolInputController();
final _scrollController = ScrollController();
final codeController = TextEditingController();
LoginState _loginState = LoginState.normal;
bool showBack = false;
@@ -75,223 +81,284 @@ class LoginScreenState extends State<LoginScreen> {
@override
Widget build(BuildContext context) {
precacheImage(const AssetImage('assets/images/showcase1.png'), context);
precacheImage(const AssetImage('assets/images/showcase2.png'), context);
precacheImage(const AssetImage('assets/images/showcase3.png'), context);
precacheImage(const AssetImage('assets/images/showcase4.png'), context);
bool selected = false;
return Scaffold(
body: Container(
decoration: BoxDecoration(color: AppColors.of(context).loginBackground),
decoration: const BoxDecoration(color: Color(0xFFDAE4F7)),
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: _scrollController,
child: Container(
decoration:
BoxDecoration(color: AppColors.of(context).loginBackground),
decoration: const BoxDecoration(color: Color(0xFFDAE4F7)),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(left: 16.0, top: 12.0),
child: ClipOval(
child: Material(
type: MaterialType.transparency,
child: showBack
? BackButton(
color: AppColors.of(context).loginPrimary)
: const SizedBox(height: 48.0),
),
),
),
// app icon
Padding(
padding: EdgeInsets.zero,
child: Image.asset(
'assets/icons/ic_rounded.png',
width: 50.0,
),
),
// texts
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 12.0,
),
child: Text(
'reFilc',
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontSize: 28.0,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
),
child: Text(
'login_w_kreten'.i18n,
textAlign: TextAlign.center,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontSize: 18.0,
fontWeight: FontWeight.w500,
height: 1.2,
),
),
),
const Spacer(
flex: 2,
),
// inputs
Padding(
padding: const EdgeInsets.only(
left: 22.0,
right: 22.0,
top: 0.0,
),
child: AutofillGroup(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
padding: const EdgeInsets.only(left: 24, top: 20),
child: Row(
children: [
// username
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"username".i18n,
maxLines: 1,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
Expanded(
child: Text(
"usernameHint".i18n,
maxLines: 1,
textAlign: TextAlign.right,
style: TextStyle(
color:
AppColors.of(context).loginSecondary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
],
),
Image.asset(
'assets/icons/ic_rounded.png',
width: 30.0,
),
Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: LoginInput(
style: LoginInputStyle.username,
controller: usernameController,
),
const SizedBox(width: 8),
const Text(
'reFilc',
style: TextStyle(
color: Color(0xFF050B15),
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
// password
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"password".i18n,
maxLines: 1,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
Expanded(
child: Text(
"passwordHint".i18n,
maxLines: 1,
textAlign: TextAlign.right,
style: TextStyle(
color:
AppColors.of(context).loginSecondary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
],
),
Material(
type: MaterialType.transparency,
child: showBack
? BackButton(color: AppColors.of(context).text)
: const SizedBox(height: 48.0),
),
Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: LoginInput(
style: LoginInputStyle.password,
controller: passwordController,
),
),
// school
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Text(
"school".i18n,
maxLines: 1,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
SchoolInput(
scroll: _scrollController,
controller: schoolController,
],
)),
Stack(
alignment: Alignment.bottomCenter,
children: [
Column(
//login buttons and ui starts here
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const SizedBox(height: 21),
CarouselSlider(
options: CarouselOptions(
height: MediaQuery.of(context).size.height,
viewportFraction: 1,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 6),
pauseAutoPlayOnTouch: true),
items: [1, 2, 3, 4].map((i) {
return Builder(
builder: (BuildContext context) {
return Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding:
const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Text(
"welcome_title_$i".i18n,
style: const TextStyle(
color: Color(0xFF050B15),
fontSize: 19,
fontFamily: 'Montserrat',
fontWeight: FontWeight.w700,
height: 1.3),
),
const SizedBox(
height: 14.375), //meth
Padding(
padding: const EdgeInsets.only(
right: 20),
child: Text(
"welcome_text_$i".i18n,
style: const TextStyle(
color: Color(0xFF050B15),
fontFamily: 'FigTree',
fontWeight:
FontWeight.w500,
fontSize: 17,
height: 1.3),
),
),
],
)),
const SizedBox(height: 15.625),
Padding(
padding: const EdgeInsets.only(
left: 16, right: 16),
child: Image.asset(
'assets/images/showcase$i.png'))
],
);
},
);
}).toList(),
),
],
),
),
),
// login button
Padding(
padding: const EdgeInsets.only(
top: 35.0,
left: 22.0,
right: 22.0,
),
child: Visibility(
visible: _loginState != LoginState.inProgress,
replacement: const Padding(
padding: EdgeInsets.symmetric(vertical: 6.0),
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.white),
Container(
height: 280,
width: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0x00DAE4F7), Color(0xFFDAE4F7)],
stops: [0, 0.12],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Padding(
padding: EdgeInsets.only(
top: 50,
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
children: [
SizedBox(
height: 48,
width: double.infinity,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16),
child: FilledButton(
style: ButtonStyle(
shape: WidgetStateProperty.all<
RoundedRectangleBorder>(
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12)),
))),
onPressed: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
isScrollControlled:
true, // This ensures the modal accommodates input fields properly
builder: (BuildContext context) {
return Container(
height: MediaQuery.of(context)
.size
.height *
0.9 +
MediaQuery.of(context)
.viewInsets
.bottom,
decoration: const BoxDecoration(
color: Color(0xFFDAE4F7),
borderRadius: BorderRadius.only(
topRight:
Radius.circular(24.0),
topLeft:
Radius.circular(24.0),
),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets
.symmetric(
vertical: 18),
child: Container(
decoration:
const BoxDecoration(
color:
Color(0xFFB9C8E5),
borderRadius:
BorderRadius.only(
topRight:
Radius.circular(
2.0),
topLeft:
Radius.circular(
2.0),
),
),
width: 40,
height: 4,
),
),
Expanded(
child: Padding(
padding:
const EdgeInsets.only(
right: 14,
left: 14,
bottom: 24),
child: ClipRRect(
borderRadius:
BorderRadius
.circular(16),
child: Container(
decoration:
BoxDecoration(
borderRadius:
BorderRadius
.circular(
16),
),
child:
KretenLoginWidget(
onLogin:
(String code) {
codeController
.text = code;
Navigator.of(
context)
.pop();
},
),
),
),
),
)
],
),
);
},
).then((value) {
// After closing the modal bottom sheet, check if the code is set
if (codeController.text.isNotEmpty) {
// Call your API after retrieving the code
_NewLoginAPI(context: context);
}
});
},
child: Text(
"login_w_kreta_acc".i18n,
style: const TextStyle(
fontFamily: 'Montserrat',
fontSize: 16,
fontWeight: FontWeight.w700),
)),
),
),
const SizedBox(height: 19),
// privacy policy
GestureDetector(
onTap: () => PrivacyView.show(context),
child: Text(
'privacy'.i18n,
style: TextStyle(
color: AppColors.of(context).loginSecondary,
fontWeight: FontWeight.w500,
fontSize: 14.0,
),
),
),
],
),
),
),
child: LoginButton(
child: Text("login".i18n,
maxLines: 1,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
)),
onPressed: () => _loginAPI(context: context),
),
),
],
),
// error messages
if (_loginState == LoginState.missingFields ||
_loginState == LoginState.invalidGrant ||
_loginState == LoginState.failed)
@@ -312,8 +379,6 @@ class LoginScreenState extends State<LoginScreen> {
textAlign: TextAlign.center,
),
),
const SizedBox(height: 22.0),
// privacy policy
GestureDetector(
onTap: () => PrivacyView.show(context),
@@ -326,10 +391,6 @@ class LoginScreenState extends State<LoginScreen> {
),
),
),
const Spacer(
flex: 1,
),
],
),
),
@@ -339,24 +400,64 @@ class LoginScreenState extends State<LoginScreen> {
);
}
void _loginAPI({required BuildContext context}) {
String username = usernameController.text;
String password = passwordController.text;
// void _loginAPI({required BuildContext context}) {
// String username = usernameController.text;
// String password = passwordController.text;
tempUsername = username;
// tempUsername = username;
if (username == "" ||
password == "" ||
schoolController.selectedSchool == null) {
return setState(() => _loginState = LoginState.missingFields);
// if (username == "" ||
// password == "" ||
// schoolController.selectedSchool == null) {
// return setState(() => _loginState = LoginState.missingFields);
// }
// // ignore: no_leading_underscores_for_local_identifiers
// void _callAPI() {
// loginAPI(
// username: username,
// password: password,
// instituteCode: schoolController.selectedSchool!.instituteCode,
// context: context,
// onLogin: (user) {
// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
// context: context,
// brightness: Brightness.light,
// content: Text("welcome".i18n.fill([user.name]),
// overflow: TextOverflow.ellipsis),
// ));
// },
// onSuccess: () {
// ScaffoldMessenger.of(context).hideCurrentSnackBar();
// setSystemChrome(context);
// Navigator.of(context).pushReplacementNamed("login_to_navigation");
// }).then(
// (res) => setState(() {
// // if (res == LoginState.invalidGrant &&
// // tempUsername.replaceAll(username, '').length <= 3) {
// // tempUsername = username + ' ';
// // Timer(
// // const Duration(milliseconds: 500),
// // () => _loginAPI(context: context),
// // );
// // // _loginAPI(context: context);
// // } else {
// _loginState = res;
// // }
// }),
// );
// }
void _NewLoginAPI({required BuildContext context}) {
String code = codeController.text;
if (code == "") {
return setState(() => _loginState = LoginState.failed);
}
// ignore: no_leading_underscores_for_local_identifiers
void _callAPI() {
loginAPI(
username: username,
password: password,
instituteCode: schoolController.selectedSchool!.instituteCode,
newLoginAPI(
code: code,
context: context,
onLogin: (user) {
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(

View File

@@ -33,6 +33,7 @@ extension Localization on String {
"welcome_title_4": "Take as many notes as you want.",
"welcome_text_4":
"You can also organise your notes by lesson in the built-in notebook, so you can find everything in one app.",
"login_w_kreta_acc": "Log in with your e-KRÉTA account",
},
"hu_hu": {
"username": "Felhasználónév",
@@ -64,6 +65,7 @@ extension Localization on String {
"welcome_title_4": "Füzetelj annyit, amennyit csak szeretnél.",
"welcome_text_4":
"A beépített jegyzetfüzetbe órák szerint is rendezheted a jegyzeteidet, így mindent megtalálsz egy appban.",
"login_w_kreta_acc": "Bejelentkezés e-KRÉTA fiókkal",
},
"de_de": {
"username": "Benutzername",
@@ -95,6 +97,7 @@ extension Localization on String {
"welcome_title_4": "Take as many notes as you want.",
"welcome_text_4":
"You can also organise your notes by lesson in the built-in notebook, so you can find everything in one app.",
"login_w_kreta_acc": "Mit e-KRÉTA-Konto anmelden",
},
};

View File

@@ -1,618 +0,0 @@
// // import 'dart:async';
// import 'package:refilc/api/client.dart';
// import 'package:refilc/api/login.dart';
// import 'package:refilc/theme/colors/colors.dart';
// import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
// import 'package:refilc_mobile_ui/common/custom_snack_bar.dart';
// import 'package:refilc_mobile_ui/common/system_chrome.dart';
// import 'package:refilc_mobile_ui/common/widgets/absence/absence_display.dart';
// import 'package:refilc_mobile_ui/screens/login/login_button.dart';
// import 'package:refilc_mobile_ui/screens/login/login_input.dart';
// import 'package:refilc_mobile_ui/screens/login/school_input/school_input.dart';
// import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter/services.dart';
// import 'login_screen.i18n.dart';
// import 'package:carousel_slider/carousel_slider.dart';
// import 'package:flutter_svg/flutter_svg.dart';
// class LoginScreen extends StatefulWidget {
// const LoginScreen({super.key, this.back = false});
// final bool back;
// @override
// LoginScreenState createState() => LoginScreenState();
// }
// class LoginScreenState extends State<LoginScreen> {
// final usernameController = TextEditingController();
// final passwordController = TextEditingController();
// final schoolController = SchoolInputController();
// final _scrollController = ScrollController();
// LoginState _loginState = LoginState.normal;
// bool showBack = false;
// // Scaffold Gradient background
// // final LinearGradient _backgroundGradient = const LinearGradient(
// // colors: [
// // Color.fromARGB(255, 61, 122, 244),
// // Color.fromARGB(255, 23, 77, 185),
// // Color.fromARGB(255, 7, 42, 112),
// // ],
// // begin: Alignment(-0.8, -1.0),
// // end: Alignment(0.8, 1.0),
// // stops: [-1.0, 0.0, 1.0],
// // );
// late String tempUsername = '';
// @override
// void initState() {
// super.initState();
// showBack = widget.back;
// SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
// statusBarColor: Colors.transparent,
// statusBarIconBrightness: Brightness.light,
// systemNavigationBarColor: Colors.white,
// systemNavigationBarIconBrightness: Brightness.dark,
// ));
// FilcAPI.getSchools().then((schools) {
// if (schools != null) {
// schoolController.update(() {
// schoolController.schools = schools;
// });
// } else {
// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
// content: Text("schools_error".i18n,
// style: const TextStyle(color: Colors.white)),
// backgroundColor: AppColors.of(context).red,
// context: context,
// ));
// }
// });
// }
// @override
// Widget build(BuildContext context) {
// precacheImage(const AssetImage('assets/images/showcase1.png'), context);
// precacheImage(const AssetImage('assets/images/showcase2.png'), context);
// precacheImage(const AssetImage('assets/images/showcase3.png'), context);
// precacheImage(const AssetImage('assets/images/showcase4.png'), context);
// bool selected = false;
// return Scaffold(
// body: Container(
// decoration: const BoxDecoration(color: Color(0xFFDAE4F7)),
// child: SingleChildScrollView(
// physics: const ClampingScrollPhysics(),
// controller: _scrollController,
// child: Container(
// decoration: const BoxDecoration(color: Color(0xFFDAE4F7)),
// width: MediaQuery.of(context).size.width,
// height: MediaQuery.of(context).size.height,
// child: SafeArea(
// child: Column(
// children: [
// // app icon
// Padding(
// padding: const EdgeInsets.only(left: 24, top: 20),
// child: Row(
// children: [
// Image.asset(
// 'assets/icons/ic_rounded.png',
// width: 30.0,
// ),
// const SizedBox(width: 8),
// const Text(
// 'reFilc',
// style: TextStyle(
// color: Color(0xFF050B15),
// fontSize: 18.0,
// fontWeight: FontWeight.bold,
// fontFamily: 'Montserrat'),
// ),
// Material(
// type: MaterialType.transparency,
// child: showBack
// ? BackButton(color: AppColors.of(context).text)
// : const SizedBox(height: 48.0),
// ),
// ],
// )),
// Stack(
// alignment: Alignment.bottomCenter,
// children: [
// Column(
// //login buttons and ui starts here
// mainAxisAlignment: MainAxisAlignment.end,
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// const SizedBox(height: 21),
// CarouselSlider(
// options: CarouselOptions(
// height: MediaQuery.of(context).size.height,
// viewportFraction: 1,
// autoPlay: true,
// autoPlayInterval: const Duration(seconds: 6),
// pauseAutoPlayOnTouch: true),
// items: [1, 2, 3, 4].map((i) {
// return Builder(
// builder: (BuildContext context) {
// return Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// Padding(
// padding:
// const EdgeInsets.only(left: 24),
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// mainAxisAlignment:
// MainAxisAlignment.start,
// children: [
// Text(
// "welcome_title_$i".i18n,
// style: const TextStyle(
// color: Color(0xFF050B15),
// fontSize: 19,
// fontFamily: 'Montserrat',
// fontWeight: FontWeight.w700,
// height: 1.3),
// ),
// const SizedBox(
// height: 14.375), //meth
// Padding(
// padding: const EdgeInsets.only(
// right: 20),
// child: Text(
// "welcome_text_$i".i18n,
// style: const TextStyle(
// color: Color(0xFF050B15),
// fontFamily: 'FigTree',
// fontWeight:
// FontWeight.w500,
// fontSize: 17,
// height: 1.3),
// ),
// ),
// ],
// )),
// const SizedBox(height: 15.625),
// Padding(
// padding: const EdgeInsets.only(
// left: 16, right: 16),
// child: Image.asset(
// 'assets/images/showcase$i.png'))
// ],
// );
// },
// );
// }).toList(),
// ),
// ],
// ),
// Container(
// height: 300,
// width: double.infinity,
// decoration: const BoxDecoration(
// gradient: LinearGradient(
// colors: [Color(0x00DAE4F7), Color(0xFFDAE4F7)],
// stops: [0, 0.12],
// begin: Alignment.topCenter,
// end: Alignment.bottomCenter,
// ),
// ),
// child: Padding(
// padding: EdgeInsets.only(top: 50, bottom: MediaQuery.of(context).viewInsets.bottom),
// child: Column(
// children: [
// SizedBox(
// height: 48,
// width: double.infinity,
// child: Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 16),
// child: FilledButton(
// style: ButtonStyle(
// shape: MaterialStateProperty.all<
// RoundedRectangleBorder>(
// const RoundedRectangleBorder(
// borderRadius: BorderRadius.all(
// Radius.circular(12)),
// ))),
// onPressed: () {
// showModalBottomSheet(
// backgroundColor: Colors.transparent,
// context: context,
// builder: (BuildContext context) {
// return Container(
// height: MediaQuery.of(context)
// .size
// .height *
// 0.5 + MediaQuery.of(context).viewInsets.bottom,
// decoration: const BoxDecoration(
// color: Color(0xFFDAE4F7),
// borderRadius: BorderRadius.only(
// topRight:
// Radius.circular(24.0),
// topLeft:
// Radius.circular(24.0),
// ),
// ),
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.center,
// mainAxisAlignment:
// MainAxisAlignment.start,
// children: [
// Padding(
// padding:
// const EdgeInsets.only(
// top: 18),
// child: Container(
// decoration:
// const BoxDecoration(
// color:
// Color(0xFFB9C8E5),
// borderRadius:
// BorderRadius.only(
// topRight:
// Radius.circular(
// 2.0),
// topLeft:
// Radius.circular(
// 2.0),
// ),
// ),
// width: 40,
// height: 4,
// ),
// ),
// Container(
// width: double.infinity,
// child: AutofillGroup(
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment
// .end,
// children: [
// // username
// Padding(
// padding:
// const EdgeInsets
// .only(
// bottom:
// 6.0),
// child: Row(
// mainAxisAlignment:
// MainAxisAlignment
// .spaceBetween,
// children: [
// Expanded(
// child: Text(
// "username"
// .i18n,
// maxLines:
// 1,
// style:
// TextStyle(
// color: AppColors.of(context)
// .loginPrimary,
// fontWeight:
// FontWeight.w500,
// fontSize:
// 12.0,
// ),
// ),
// ),
// Expanded(
// child: Text(
// "usernameHint"
// .i18n,
// maxLines:
// 1,
// textAlign:
// TextAlign
// .right,
// style:
// TextStyle(
// color: AppColors.of(context)
// .loginSecondary,
// fontWeight:
// FontWeight.w500,
// fontSize:
// 12.0,
// ),
// ),
// ),
// ],
// ),
// ),
// Padding(
// padding:
// const EdgeInsets
// .only(
// bottom:
// 12.0),
// child: LoginInput(
// style:
// LoginInputStyle
// .username,
// controller:
// usernameController,
// ),
// ),
// // password
// Padding(
// padding:
// const EdgeInsets
// .only(
// bottom:
// 6.0),
// child: Row(
// mainAxisAlignment:
// MainAxisAlignment
// .spaceBetween,
// children: [
// Expanded(
// child: Text(
// "password"
// .i18n,
// maxLines:
// 1,
// style:
// TextStyle(
// color: AppColors.of(context)
// .loginPrimary,
// fontWeight:
// FontWeight.w500,
// fontSize:
// 12.0,
// ),
// ),
// ),
// Expanded(
// child: Text(
// "passwordHint"
// .i18n,
// maxLines:
// 1,
// textAlign:
// TextAlign
// .right,
// style:
// TextStyle(
// color: AppColors.of(context)
// .loginSecondary,
// fontWeight:
// FontWeight.w500,
// fontSize:
// 12.0,
// ),
// ),
// ),
// ],
// ),
// ),
// Padding(
// padding:
// const EdgeInsets
// .only(
// bottom:
// 12.0),
// child: LoginInput(
// style:
// LoginInputStyle
// .password,
// controller:
// passwordController,
// ),
// ),
// // school
// Padding(
// padding:
// const EdgeInsets
// .only(
// bottom:
// 6.0),
// child: Text(
// "school".i18n,
// maxLines: 1,
// style:
// TextStyle(
// color: AppColors.of(
// context)
// .loginPrimary,
// fontWeight:
// FontWeight
// .w500,
// fontSize:
// 12.0,
// ),
// ),
// ),
// SchoolInput(
// scroll:
// _scrollController,
// controller:
// schoolController,
// ),
// ],
// ),
// ),
// ),
// const Padding(
// padding: EdgeInsets.only(
// left: 22.0,
// right: 22.0,
// top: 0.0,
// ),
// ),
// Padding(
// padding:
// const EdgeInsets.only(
// top: 35.0,
// left: 22.0,
// right: 22.0,
// ),
// child: Visibility(
// visible: _loginState !=
// LoginState
// .inProgress,
// replacement:
// const Padding(
// padding: EdgeInsets
// .symmetric(
// vertical:
// 6.0),
// child:
// CircularProgressIndicator(
// valueColor:
// AlwaysStoppedAnimation<
// Color>(
// Colors
// .white),
// ),
// ),
// child: LoginButton(
// child: Text(
// "login".i18n,
// maxLines: 1,
// style:
// const TextStyle(
// fontWeight:
// FontWeight
// .bold,
// fontSize: 20.0,
// )),
// onPressed: () =>
// _loginAPI(
// context:
// context),
// ),
// ),
// ),
// ]),
// );
// },
// );
// },
// child: Text(
// "login".i18n,
// style: const TextStyle(
// fontFamily: 'Montserrat',
// fontSize: 20,
// fontWeight: FontWeight.w700),
// )),
// ),
// ),
// const SizedBox(height: 8),
// ],
// ),
// ),
// ),
// ],
// ),
// if (_loginState == LoginState.missingFields ||
// _loginState == LoginState.invalidGrant ||
// _loginState == LoginState.failed)
// Padding(
// padding: const EdgeInsets.only(
// top: 8.0, left: 12.0, right: 12.0),
// child: Text(
// [
// "missing_fields",
// "invalid_grant",
// "error"
// ][_loginState.index]
// .i18n,
// style: const TextStyle(
// color: Colors.red,
// fontWeight: FontWeight.w500,
// ),
// textAlign: TextAlign.center,
// ),
// ),
// // privacy policy
// GestureDetector(
// onTap: () => PrivacyView.show(context),
// child: Text(
// 'privacy'.i18n,
// style: TextStyle(
// color: AppColors.of(context).loginSecondary,
// fontWeight: FontWeight.w500,
// fontSize: 14.0,
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ),
// );
// }
// void _loginAPI({required BuildContext context}) {
// String username = usernameController.text;
// String password = passwordController.text;
// tempUsername = username;
// if (username == "" ||
// password == "" ||
// schoolController.selectedSchool == null) {
// return setState(() => _loginState = LoginState.missingFields);
// }
// // ignore: no_leading_underscores_for_local_identifiers
// void _callAPI() {
// loginAPI(
// username: username,
// password: password,
// instituteCode: schoolController.selectedSchool!.instituteCode,
// context: context,
// onLogin: (user) {
// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
// context: context,
// brightness: Brightness.light,
// content: Text("welcome".i18n.fill([user.name]),
// overflow: TextOverflow.ellipsis),
// ));
// },
// onSuccess: () {
// ScaffoldMessenger.of(context).hideCurrentSnackBar();
// setSystemChrome(context);
// Navigator.of(context).pushReplacementNamed("login_to_navigation");
// }).then(
// (res) => setState(() {
// // if (res == LoginState.invalidGrant &&
// // tempUsername.replaceAll(username, '').length <= 3) {
// // tempUsername = username + ' ';
// // Timer(
// // const Duration(milliseconds: 500),
// // () => _loginAPI(context: context),
// // );
// // // _loginAPI(context: context);
// // } else {
// _loginState = res;
// // }
// }),
// );
// }
// setState(() => _loginState = LoginState.inProgress);
// _callAPI();
// }
// }

View File

@@ -0,0 +1,513 @@
// import 'dart:async';
import 'package:refilc/api/client.dart';
import 'package:refilc/api/login.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc_mobile_ui/common/custom_snack_bar.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart';
// import 'package:refilc_mobile_ui/screens/login/kreten_login.dart';
import 'package:refilc_mobile_ui/screens/login/login_button.dart';
import 'package:refilc_mobile_ui/screens/login/login_input.dart';
import 'package:refilc_mobile_ui/screens/login/school_input/school_input.dart';
import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'login_screen.i18n.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key, this.back = false});
final bool back;
@override
LoginScreenState createState() => LoginScreenState();
}
class LoginScreenState extends State<LoginScreen> {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
final schoolController = SchoolInputController();
final _scrollController = ScrollController();
// new controllers
final codeController = TextEditingController();
LoginState _loginState = LoginState.normal;
bool showBack = false;
// Scaffold Gradient background
// final LinearGradient _backgroundGradient = const LinearGradient(
// colors: [
// Color.fromARGB(255, 61, 122, 244),
// Color.fromARGB(255, 23, 77, 185),
// Color.fromARGB(255, 7, 42, 112),
// ],
// begin: Alignment(-0.8, -1.0),
// end: Alignment(0.8, 1.0),
// stops: [-1.0, 0.0, 1.0],
// );
late String tempUsername = '';
@override
void initState() {
super.initState();
showBack = widget.back;
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
));
FilcAPI.getSchools().then((schools) {
if (schools != null) {
schoolController.update(() {
schoolController.schools = schools;
});
} else {
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
content: Text("schools_error".i18n,
style: const TextStyle(color: Colors.white)),
backgroundColor: AppColors.of(context).red,
context: context,
));
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(color: AppColors.of(context).loginBackground),
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: _scrollController,
child: Container(
decoration:
BoxDecoration(color: AppColors.of(context).loginBackground),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(left: 16.0, top: 12.0),
child: ClipOval(
child: Material(
type: MaterialType.transparency,
child: showBack
? BackButton(
color: AppColors.of(context).loginPrimary)
: const SizedBox(height: 48.0),
),
),
),
// app icon
Padding(
padding: EdgeInsets.zero,
child: Image.asset(
'assets/icons/ic_rounded.png',
width: 50.0,
),
),
// texts
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 12.0,
),
child: Text(
'reFilc',
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontSize: 28.0,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
),
child: Text(
'login_w_kreten'.i18n,
textAlign: TextAlign.center,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontSize: 18.0,
fontWeight: FontWeight.w500,
height: 1.2,
),
),
),
const Spacer(
flex: 2,
),
// kreten login button
// GestureDetector(
// onTap: () {
// final NavigatorState navigator = Navigator.of(context);
// navigator
// .push(
// MaterialPageRoute(
// builder: (context) => KretenLoginScreen(
// onLogin: (String code) {
// codeController.text = code;
// navigator.pop();
// },
// ),
// ),
// )
// .then((value) {
// if (codeController.text != "") {
// _NewLoginAPI(context: context);
// }
// });
// },
// child: Container(
// width: MediaQuery.of(context).size.width * 0.75,
// height: 50.0,
// decoration: BoxDecoration(
// // image: const DecorationImage(
// // image:
// // AssetImage('assets/images/btn_kreten_login.png'),
// // fit: BoxFit.scaleDown,
// // ),
// borderRadius: BorderRadius.circular(12.0),
// color: const Color(0xFF0097C1),
// ),
// padding: const EdgeInsets.only(
// top: 5.0, left: 5.0, right: 5.0, bottom: 5.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Image.asset(
// 'assets/images/btn_kreten_login.png',
// ),
// const SizedBox(
// width: 10.0,
// ),
// Container(
// width: 1.0,
// height: 30.0,
// color: Colors.white,
// ),
// const SizedBox(
// width: 10.0,
// ),
// Text(
// 'login_w_kreta_acc'.i18n,
// textAlign: TextAlign.center,
// style: const TextStyle(
// color: Colors.white,
// fontWeight: FontWeight.bold,
// fontSize: 15.0,
// ),
// ),
// ],
// )),
// ),
// const Spacer(
// flex: 1,
// ),
// inputs
Padding(
padding: const EdgeInsets.only(
left: 22.0,
right: 22.0,
top: 0.0,
),
child: AutofillGroup(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// username
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"username".i18n,
maxLines: 1,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
Expanded(
child: Text(
"usernameHint".i18n,
maxLines: 1,
textAlign: TextAlign.right,
style: TextStyle(
color:
AppColors.of(context).loginSecondary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: LoginInput(
style: LoginInputStyle.username,
controller: usernameController,
),
),
// password
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"password".i18n,
maxLines: 1,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
Expanded(
child: Text(
"passwordHint".i18n,
maxLines: 1,
textAlign: TextAlign.right,
style: TextStyle(
color:
AppColors.of(context).loginSecondary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: LoginInput(
style: LoginInputStyle.password,
controller: passwordController,
),
),
// school
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Text(
"school".i18n,
maxLines: 1,
style: TextStyle(
color: AppColors.of(context).loginPrimary,
fontWeight: FontWeight.w500,
fontSize: 12.0,
),
),
),
SchoolInput(
scroll: _scrollController,
controller: schoolController,
),
],
),
),
),
// login button
Padding(
padding: const EdgeInsets.only(
top: 35.0,
left: 22.0,
right: 22.0,
),
child: Visibility(
visible: _loginState != LoginState.inProgress,
replacement: const Padding(
padding: EdgeInsets.symmetric(vertical: 6.0),
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.white),
),
),
child: LoginButton(
child: Text("login".i18n,
maxLines: 1,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
)),
onPressed: () => _loginAPI(context: context),
),
),
),
// error messages
if (_loginState == LoginState.missingFields ||
_loginState == LoginState.invalidGrant ||
_loginState == LoginState.failed)
Padding(
padding: const EdgeInsets.only(
top: 8.0, left: 12.0, right: 12.0),
child: Text(
[
"missing_fields",
"invalid_grant",
"error"
][_loginState.index]
.i18n,
style: const TextStyle(
color: Colors.red,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 22.0),
// privacy policy
GestureDetector(
onTap: () => PrivacyView.show(context),
child: Text(
'privacy'.i18n,
style: TextStyle(
color: AppColors.of(context).loginSecondary,
fontWeight: FontWeight.w500,
fontSize: 14.0,
),
),
),
const Spacer(
flex: 1,
),
],
),
),
),
),
),
);
}
// new login api
// ignore: non_constant_identifier_names, unused_element
void _NewLoginAPI({required BuildContext context}) {
String code = codeController.text;
if (code == "") {
return setState(() => _loginState = LoginState.failed);
}
// ignore: no_leading_underscores_for_local_identifiers
void _callAPI() {
newLoginAPI(
code: code,
context: context,
onLogin: (user) {
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
context: context,
brightness: Brightness.light,
content: Text("welcome".i18n.fill([user.name]),
overflow: TextOverflow.ellipsis),
));
},
onSuccess: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
setSystemChrome(context);
Navigator.of(context).pushReplacementNamed("login_to_navigation");
}).then(
(res) => setState(() {
// if (res == LoginState.invalidGrant &&
// tempUsername.replaceAll(username, '').length <= 3) {
// tempUsername = username + ' ';
// Timer(
// const Duration(milliseconds: 500),
// () => _loginAPI(context: context),
// );
// // _loginAPI(context: context);
// } else {
_loginState = res;
// }
}),
);
}
setState(() => _loginState = LoginState.inProgress);
_callAPI();
}
void _loginAPI({required BuildContext context}) {
String username = usernameController.text;
String password = passwordController.text;
tempUsername = username;
if (username == "" ||
password == "" ||
schoolController.selectedSchool == null) {
return setState(() => _loginState = LoginState.missingFields);
}
// ignore: no_leading_underscores_for_local_identifiers
void _callAPI() {
loginAPI(
username: username,
password: password,
instituteCode: schoolController.selectedSchool!.instituteCode,
context: context,
onLogin: (user) {
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
context: context,
brightness: Brightness.light,
content: Text("welcome".i18n.fill([user.name]),
overflow: TextOverflow.ellipsis),
));
},
onSuccess: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
setSystemChrome(context);
Navigator.of(context).pushReplacementNamed("login_to_navigation");
}).then(
(res) => setState(() {
// if (res == LoginState.invalidGrant &&
// tempUsername.replaceAll(username, '').length <= 3) {
// tempUsername = username + ' ';
// Timer(
// const Duration(milliseconds: 500),
// () => _loginAPI(context: context),
// );
// // _loginAPI(context: context);
// } else {
_loginState = res;
// }
}),
);
}
setState(() => _loginState = LoginState.inProgress);
_callAPI();
}
}

View File

@@ -47,7 +47,7 @@ class SchoolInputOverlayWidget extends StatelessWidget {
showWhenUnlinked: false,
offset: Offset(0.0, size.height + 5.0),
child: Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0)),
elevation: 4.0,

View File

@@ -2,6 +2,7 @@
import 'package:refilc_mobile_ui/pages/absences/absences_page.dart';
import 'package:refilc_mobile_ui/pages/grades/grades_page.dart';
import 'package:refilc_mobile_ui/pages/home/home_page.dart';
import 'package:refilc_mobile_ui/pages/messages/messages_page.dart';
import 'package:refilc_mobile_ui/pages/notes/notes_page.dart';
// import 'package:refilc_mobile_ui/pages/messages/messages_page.dart';
import 'package:refilc_mobile_ui/pages/timetable/timetable_page.dart';
@@ -20,8 +21,8 @@ Route navigationRouteHandler(RouteSettings settings) {
return navigationPageRoute((context) => const NotesPage());
case "absences":
return navigationPageRoute((context) => const AbsencesPage());
// case "messages":
// return navigationPageRoute((context) => const MessagesPage());
case "messages":
return navigationPageRoute((context) => const MessagesPage());
// case "absences":
// return navigationPageRoute((context) => const AbsencesPage());
default:

View File

@@ -302,7 +302,7 @@ class NavigationScreenState extends State<NavigationScreen>
children: [
// Status bar
Material(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
child: const StatusBar(),
),

View File

@@ -59,10 +59,6 @@ class AccountView extends StatelessWidget {
const SizedBox(
height: 10.0,
),
// Detail(
// title: "parents".i18n,
// description: user.student.parents.join(", ")),
Detail(title: "school".i18n, description: user.student.school.name),
],
),
);

View File

@@ -8,22 +8,22 @@ extension Localization on String {
"school": "School",
"class": "Class",
"address": "Home address",
"parents": "Parents".one("Parent"),
"parents_phone": "Parents' phone number: ".one("Parent"),
"parents": "Parent(s)",
"parents_phone": "Parents' phone number: ",
},
"hu_hu": {
"birthdate": "Születési dátum",
"school": "Iskola",
"class": "Osztály",
"address": "Lakcím",
"parents": "Szülők".one("Szülő"),
"parents": "Szülő(k)",
},
"de_de": {
"birthdate": "Geburtsdatum",
"school": "Schule",
"class": "Klasse",
"address": "Wohnanschrift",
"parents": "Eltern",
"parents": "Elter(n)",
},
};

View File

@@ -13,7 +13,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:refilc_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:refilc_mobile_ui/common/beta_chip.dart';
import 'package:refilc_mobile_ui/common/chips/beta_chip.dart';
class MenuDesktopSettings extends StatelessWidget {
const MenuDesktopSettings({super.key, required this.settings});

View File

@@ -178,8 +178,8 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
elevation: 8,
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness: MaterialStateProperty.all<double>(6.0),
trackVisibility: MaterialStateProperty.all<bool>(true),
thickness: WidgetStateProperty.all<double>(6.0),
trackVisibility: WidgetStateProperty.all<bool>(true),
),
offset: const Offset(-10, -10),
),

View File

@@ -128,13 +128,34 @@ class SettingsHelper {
// }
// }
static void fontFamily(BuildContext context) {
static void fontFamily(BuildContext context,
{required Function() showDialog}) {
SettingsProvider settings =
Provider.of<SettingsProvider>(context, listen: false);
showBottomSheetMenu(
context,
items: List.generate(fontList.length, (index) {
// if (index == fontList.length) {
// return BottomSheetMenuItem(
// onPressed: showDialog,
// title: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// SettingsLocalization('custom').i18n,
// ),
// if (fontList.contains(settings.fontFamily) == false &&
// settings.fontFamily != '')
// Icon(
// Icons.check_circle,
// color: Theme.of(context).colorScheme.secondary,
// ),
// ],
// ),
// );
// }
String font = fontList[index];
return BottomSheetMenuItem(
onPressed: () {

View File

@@ -30,6 +30,7 @@ import 'package:refilc_mobile_ui/common/profile_image/profile_image.dart';
import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart';
// import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart';
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart';
// import 'package:refilc_mobile_ui/common/system_chrome.dart';
import 'package:refilc_mobile_ui/common/widgets/update/updates_view.dart';
import 'package:refilc_mobile_ui/screens/news/news_screen.dart';
@@ -54,6 +55,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart' as tabs;
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:shake_flutter/enums/shake_screen.dart';
import 'package:shake_flutter/shake_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'debug/subject_icon_gallery.dart';
import 'settings_screen.i18n.dart';
@@ -103,9 +106,11 @@ class SettingsScreenState extends State<SettingsScreen>
Provider.of<NoteProvider>(context, listen: false).restore(),
Provider.of<EventProvider>(context, listen: false).restore(),
Provider.of<AbsenceProvider>(context, listen: false).restore(),
Provider.of<KretaClient>(context, listen: false).refreshLogin(),
]);
Future<String?> refresh() =>
Provider.of<KretaClient>(context, listen: false).refreshLogin();
void buildAccountTiles() {
accountTiles = [];
user.getUsers().forEach((account) {
@@ -141,8 +146,58 @@ class SettingsScreenState extends State<SettingsScreen>
//? ColorUtils.stringToColor(account.name)
//: Theme.of(context).colorScheme.secondary,
),
onTap: () {
onTap: () async {
user.setUser(account.id);
// check if refresh token is still valid
String? err = await refresh();
if (err != null) {
showDialog(
context: context,
builder: (_) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text('oopsie'.i18n),
content: Text('session_expired'.i18n),
actions: [
ActionButton(
label: "Ok",
onTap: () async {
String? userId = user.id;
if (userId == null) return;
// delete user
user.removeUser(userId);
await Provider.of<DatabaseProvider>(context,
listen: false)
.store
.removeUser(userId);
// if no users, show login, else login with back button
if (user.getUsers().isNotEmpty) {
user.setUser(user.getUsers().first.id);
restore().then(
(_) => user.setUser(user.getUsers().first.id));
Navigator.of(context).pop();
Navigator.of(context)
.pushNamed("login_back")
.then((value) {
setSystemChrome(context);
});
} else {
Navigator.of(context).pop();
Navigator.of(context)
.pushNamedAndRemoveUntil("login", (_) => false);
}
})
],
),
);
return;
}
// switch user
restore().then((_) => user.setUser(account.id));
Navigator.of(context).pop();
},
@@ -747,6 +802,72 @@ class SettingsScreenState extends State<SettingsScreen>
// plus subscribe inline
const PlusSettingsInline(),
// const SizedBox(
// height: 16.0,
// ),
// Panel(
// hasShadow: false,
// padding: const EdgeInsets.only(left: 24.0, right: 24.0),
// title: Padding(
// padding: const EdgeInsets.only(left: 24.0),
// child: Text('account_link'.i18n),
// ),
// isTransparent: true,
// child: Column(
// children: [
// // QwID account linking
// PanelButton(
// onPressed: () {
// launchUrl(
// Uri.parse(
// 'https://qwid.qwit.dev/oauth2/authorize?client_id=refilc&response_type=code&scope=*'),
// mode: LaunchMode.externalApplication,
// );
// },
// title: Text("QwID fiók-összekapcsolás".i18n),
// leading: Icon(
// FeatherIcons.link,
// size: 22.0,
// color: AppColors.of(context).text.withOpacity(0.95),
// ),
// trailing: GestureDetector(
// onTap: () {
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// title: const Text("QwID?!"),
// content: const Text(
// "A QwID egy olyan fiók, mellyel az összes QwIT szolgáltatásba beléphetsz és minden adatod egy helyen kezelheted. \"Miért jó ez nekem?\" A QwID fiókba való bejelentkezéssel rengeteg új funkcióhoz férhetsz hozzá, ami sajnos korábban lehetetlen volt egy szimpla e-KRÉTA fiókkal. Fiókhoz kötve megoszthatsz bármilyen adatot a barátaiddal, vagy ha szeretnéd nyilvánosságra is hozhatod jegyeid, reFilc témáid, és még rengeteg dolgot. A QwID fiók abban is segít, hogy egyszerűbben kezelhesd előfizetéseid, valamint fiókodnak köszönhetően rengeteg ajándékot kaphatsz reFilc+ előfizetésed mellé egyéb QwIT és reFilc szolgáltatásokban. \"Miért QwID?\" A név a reFilc mögött álló fejlesztői csapat, a QwIT nevéből, valamint az angol Identity szó rövidítéséből ered. \"Egyéb hasznos tudnivalók?\" A QwID fiókodat bármikor törölheted, ha úgy érzed, hogy nem szeretnéd tovább használni. Bővebb információt az adatkezelésről és az általános feltételekről megtalálsz a regisztrációs oldalon. Fiókod kezeléséhez látogass el a qwid.qwit.dev weboldalra.",
// ),
// actions: [
// TextButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// child: const Text("Szuper!"),
// ),
// ],
// );
// },
// );
// },
// child: Icon(
// FeatherIcons.helpCircle,
// size: 20.0,
// color: AppColors.of(context).text.withOpacity(0.95),
// ),
// ),
// borderRadius: const BorderRadius.vertical(
// top: Radius.circular(12.0),
// bottom: Radius.circular(4.0),
// ),
// ),
// ],
// ),
// ),
// settings submenus
const SizedBox(
height: 16.0,
@@ -837,6 +958,18 @@ class SettingsScreenState extends State<SettingsScreen>
),
],
),
// const SplittedPanel(
// padding: EdgeInsets.only(top: 8.0),
// cardPadding: EdgeInsets.all(4.0),
// children: [
// MenuOtherSettings(
// borderRadius: BorderRadius.vertical(
// top: Radius.circular(12.0),
// bottom: Radius.circular(12.0),
// ),
// ),
// ],
// ),
],
),
),
@@ -844,8 +977,8 @@ class SettingsScreenState extends State<SettingsScreen>
// // icon gallery (debug mode)
if (kDebugMode)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 24.0),
padding: const EdgeInsets.only(
bottom: 16.0, left: 24.0, right: 24.0),
child: Panel(
title: const Text("Debug"),
child: Column(
@@ -868,6 +1001,43 @@ class SettingsScreenState extends State<SettingsScreen>
),
),
// other secion
SplittedPanel(
title: Text("other".i18n),
cardPadding: const EdgeInsets.all(4.0),
children: [
PanelButton(
leading: Icon(
FeatherIcons.map,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
),
title: Text("stickermap".i18n),
onPressed: () => launchUrl(
Uri.parse("https://stickermap.refilc.hu"),
mode: LaunchMode.inAppBrowserView,
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(4.0),
),
),
PanelButton(
leading: Icon(
FeatherIcons.mail,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
),
title: Text("news".i18n),
onPressed: () => _openNews(context),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0),
bottom: Radius.circular(12.0),
),
),
],
),
// // extra settings
// Padding(
// padding:
@@ -900,19 +1070,6 @@ class SettingsScreenState extends State<SettingsScreen>
title: Text("about".i18n),
cardPadding: const EdgeInsets.all(4.0),
children: [
PanelButton(
leading: Icon(
FeatherIcons.mail,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
),
title: Text("news".i18n),
onPressed: () => _openNews(context),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(4.0),
),
),
PanelButton(
leading: Icon(
FeatherIcons.lock,
@@ -925,7 +1082,7 @@ class SettingsScreenState extends State<SettingsScreen>
// mode: LaunchMode.inAppWebView),
onPressed: () => _openPrivacy(context),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0),
top: Radius.circular(12.0),
bottom: Radius.circular(4.0),
),
),
@@ -993,7 +1150,7 @@ class SettingsScreenState extends State<SettingsScreen>
fontWeight: FontWeight.w500,
color: AppColors.of(context).text),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
@@ -1010,7 +1167,7 @@ class SettingsScreenState extends State<SettingsScreen>
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(4.0),
bottom: Radius.circular(12.0),
bottom: Radius.circular(4.0),
),
),
secondary: Icon(
@@ -1052,6 +1209,23 @@ class SettingsScreenState extends State<SettingsScreen>
),
),
),
PanelButton(
leading: Icon(
Icons.feedback_outlined,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
),
title: Text("feedback".i18n),
onPressed: () => {
Shake.setScreenshotIncluded(false),
Shake.show(ShakeScreen.newTicket),
Shake.setScreenshotIncluded(true),
},
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0),
bottom: Radius.circular(12.0),
),
),
],
),
@@ -1074,6 +1248,15 @@ class SettingsScreenState extends State<SettingsScreen>
title: const Text('pushTimetableToCalendar'),
onPressed: () async {},
),
PanelButton(
title: const Text('resetNewBadges'),
onPressed: () async {
Provider.of<SettingsProvider>(context, listen: false)
.update(
unseenNewFeatures: ['grade_exporting'],
);
},
),
],
),
// developer options

View File

@@ -4,6 +4,9 @@ extension SettingsLocalization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"heads_up": "Heads up!",
"export_warning":
"Exported grades are currently not yet viewable in reFilc, you'll only be able to view them manually in JSON format. In the future, this functionality will be extended and you will be able to view the tickets in the app interface.",
"personal_details": "Personal Details",
"open_dkt": "Open DCS",
"edit_nickname": "Edit Nickname",
@@ -124,8 +127,15 @@ extension SettingsLocalization on String {
"new_popups": "New Popups",
"export_method": "Export Method",
"grade_exporting": "Grade Exporting",
"custom": "Custom",
"feedback": "Feedback",
"other": "Other",
"stickermap": "Sticker Map",
},
"hu_hu": {
"heads_up": "Figyelem!",
"export_warning":
"Az exportált jegyek jelenleg még nem megtekinthetők a reFilc-ben, csak te magad tudod átnézni őket JSON formátumban. A jövőben ez a funkció bővülni fog, és a jegyeket meg is tekintheted majd a reFilc felületén.",
"personal_details": "Személyes információk",
"open_dkt": "DKT megnyitása",
"edit_nickname": "Becenév szerkesztése",
@@ -246,8 +256,15 @@ extension SettingsLocalization on String {
"new_popups": "Új felugró ablakok",
"export_method": "Exportálási mód",
"grade_exporting": "Jegy exportálás",
"custom": "Egyedi",
"feedback": "Visszajelzés",
"other": "Egyéb",
"stickermap": "Matrica térkép",
},
"de_de": {
"heads_up": "Achtung!",
"export_warning":
"Exportierte Tickets sind derzeit noch nicht in reFilc einsehbar, Sie können sie nur selbst im JSON- Format überprüfen. In Zukunft wird diese Funktionalität erweitert und Sie werden die Tickets in der reFilc-Oberfläche anzeigen können",
"personal_details": "Persönliche Angaben",
"open_dkt": "Öffnen RDZ",
"edit_nickname": "Spitznamen bearbeiten",
@@ -368,6 +385,10 @@ extension SettingsLocalization on String {
"new_popups": "Neue Popups",
"export_method": "Exportmethode",
"grade_exporting": "Noten exportieren",
"custom": "Benutzerdefiniert",
"feedback": "Feedback",
"other": "Sonstiges",
"stickermap": "Sticker Map",
},
};

View File

@@ -202,7 +202,7 @@ class EditSubjectScreenState extends State<EditSubjectScreen> {
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius:
const BorderRadius.all(Radius.circular(12.0))),
padding: const EdgeInsets.symmetric(vertical: 10.0),
@@ -302,7 +302,7 @@ class EditSubjectScreenState extends State<EditSubjectScreen> {
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius:
const BorderRadius.all(Radius.circular(12.0))),
padding: const EdgeInsets.symmetric(vertical: 10.0),

View File

@@ -2,6 +2,7 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc_mobile_ui/common/chips/new_chip.dart';
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
import 'package:refilc_mobile_ui/screens/settings/settings_helper.dart';
@@ -39,10 +40,20 @@ class MenuExtrasSettings extends StatelessWidget {
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
),
trailing: Icon(
FeatherIcons.chevronRight,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (Provider.of<SettingsProvider>(context)
.unseenNewFeatures
.toSet()
.intersection({'grade_exporting'}).isNotEmpty)
const NewChip(),
Icon(
FeatherIcons.chevronRight,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
)
],
),
borderRadius: borderRadius,
);

View File

@@ -79,7 +79,7 @@ class GradeColorsSettingsScreenState extends State<GradeColorsSettingsScreen> {
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(75.0),
boxShadow: [
BoxShadow(

View File

@@ -454,7 +454,7 @@ class PaintListScreenState extends State<PaintListScreen>
),
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
color: Theme.of(context).colorScheme.surface,
borderRadius:
const BorderRadius.all(Radius.circular(12.0))),
padding: const EdgeInsets.symmetric(vertical: 10.0),

View File

@@ -79,6 +79,8 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
late AnimationController _hideContainersController;
final TextEditingController _customFontFamily = TextEditingController();
late List<Grade> editedShit;
late List<Grade> otherShit;
@@ -840,11 +842,11 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness:
MaterialStateProperty.all<double>(6.0),
WidgetStateProperty.all<double>(6.0),
trackVisibility:
MaterialStateProperty.all<bool>(true),
WidgetStateProperty.all<bool>(true),
thumbVisibility:
MaterialStateProperty.all<bool>(true),
WidgetStateProperty.all<bool>(true),
),
),
customButton: PanelButton(
@@ -927,15 +929,94 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
children: [
PanelButton(
onPressed: () {
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.customFont)) {
PlusLockedFeaturePopup.show(
context: context,
feature: PremiumFeature.fontChange);
return;
}
// if (!Provider.of<PlusProvider>(context, listen: false)
// .hasScope(PremiumScopes.customFont)) {
// PlusLockedFeaturePopup.show(
// context: context,
// feature: PremiumFeature.fontChange);
// return;
// }
SettingsHelper.fontFamily(context);
SettingsHelper.fontFamily(
context,
showDialog: () => showDialog(
context: context,
builder: (context) => AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(14.0))),
contentPadding:
const EdgeInsets.only(top: 10.0),
title: Text("custom".i18n),
content: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _customFontFamily,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.grey, width: 1.5),
borderRadius:
BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.grey, width: 1.5),
borderRadius:
BorderRadius.circular(12.0),
),
contentPadding:
const EdgeInsets.symmetric(
horizontal: 12.0),
hintText: "ff_name".i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
),
onPressed: () {
setState(() {
_customFontFamily.text = "";
});
},
),
),
),
],
),
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(
fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"next".i18n,
style: const TextStyle(
fontWeight: FontWeight.w500),
),
onPressed: () async {
settingsProvider.update(
fontFamily: _customFontFamily.text);
Navigator.of(context).pop(true);
},
),
],
),
),
);
setState(() {});
},
title: Text(

View File

@@ -7,6 +7,8 @@ extension SettingsLocalization on String {
"general": "General",
"personalization": "Personalization",
"extras": "Extras",
"other": "Other",
"stickermap": "reFilc Stickermap",
"surprise_grades": "Surprise Grades",
"cancel": "Cancel",
"done": "Done",
@@ -33,6 +35,8 @@ extension SettingsLocalization on String {
"general": "Általános",
"personalization": "Személyre szabás",
"extras": "Extrák",
"other": "Egyéb",
"stickermap": "reFilc Matricatérkép",
"surprise_grades": "Meglepetés jegyek",
"cancel": "Mégse",
"done": "Kész",
@@ -59,6 +63,8 @@ extension SettingsLocalization on String {
"general": "Allgemeine",
"personalization": "Personalisierung",
"extras": "Extras",
"other": "Andere",
"stickermap": "reFilc Aufkleberkarte",
"surprise_grades": "Überraschende Noten",
"cancel": "Abbrechen",
"done": "Bereit",

View File

@@ -234,7 +234,7 @@ class _PremiumCustomAccentColorSettingState
// bool hasAccess = Provider.of<PlusProvider>(context)
// .hasScope(PremiumScopes.customColors);
bool hasAccess = true;
bool isBackgroundDifferent = Theme.of(context).colorScheme.background !=
bool isBackgroundDifferent = Theme.of(context).colorScheme.surface !=
AppColors.of(context).background;
ThemeMode currentTheme = Theme.of(context).brightness == Brightness.light
@@ -251,8 +251,8 @@ class _PremiumCustomAccentColorSettingState
animation: _openAnimController,
builder: (context, child) {
final backgroundGradientBottomColor = isBackgroundDifferent
? Theme.of(context).colorScheme.background
: HSVColor.fromColor(Theme.of(context).colorScheme.background)
? Theme.of(context).colorScheme.surface
: HSVColor.fromColor(Theme.of(context).colorScheme.surface)
.withValue(currentTheme == ThemeMode.dark
? 0.1 * _openAnimController.value
: 1.0 - (0.1 * _openAnimController.value))
@@ -271,7 +271,7 @@ class _PremiumCustomAccentColorSettingState
stops: const [0.0, 0.75],
colors: isBackgroundDifferent
? [
Theme.of(context).colorScheme.background.withOpacity(1 -
Theme.of(context).colorScheme.surface.withOpacity(1 -
((currentTheme == ThemeMode.dark ? 0.65 : 0.25) *
backgroundAnimation.value)),
backgroundGradientBottomColor,
@@ -383,7 +383,7 @@ class _PremiumCustomAccentColorSettingState
],
colors: [
settings.customBackgroundColor ??
Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.surface,
isBackgroundDifferent
? HSVColor.fromColor(Theme.of(context)
.colorScheme

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