From 6d7a21dc02dd12554673a2c21a6c7524af3c7f1f Mon Sep 17 00:00:00 2001 From: Kima Date: Mon, 18 Sep 2023 19:42:36 +0200 Subject: [PATCH 01/13] fixed theme share warn dialog --- .../lib/ui/mobile/settings/theme.dart | 31 ++++++++++++++++--- .../lib/ui/mobile/settings/theme.i18n.dart | 3 ++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart b/filcnaplo_premium/lib/ui/mobile/settings/theme.dart index 33cff7a..bcd0468 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/theme.dart @@ -8,6 +8,7 @@ import 'package:filcnaplo/ui/widgets/message/message_tile.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/models/homework.dart'; import 'package:filcnaplo_kreta_api/models/message.dart'; +import 'package:filcnaplo_mobile_ui/common/action_button.dart'; import 'package:filcnaplo_mobile_ui/common/filter_bar.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart'; @@ -268,10 +269,32 @@ class _PremiumCustomAccentColorSettingState // ), // ), // ); - SharedTheme theme = - await shareProvider.shareCurrentTheme(context); - Share.share(theme.id, - subject: 'reFilc Téma / reFilc Theme'); + showDialog( + context: context, + builder: (context) => WillPopScope( + onWillPop: () async => false, + child: AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0)), + title: Text("attention".i18n), + content: Text("share_disclaimer".i18n), + actions: [ + ActionButton( + label: "understand".i18n, + onTap: () async { + Navigator.of(context).pop(); + SharedTheme theme = await shareProvider + .shareCurrentTheme(context); + Share.share( + theme.id, + subject: 'share_subj_theme'.i18n, + ); + }, + ), + ], + ), + ), + ); }, icon: const Icon( FeatherIcons.share2, diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart b/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart index 40ac863..e02b33c 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart @@ -14,6 +14,7 @@ extension SettingsLocalization on String { "enter_id": "Enter ID", "theme_id": "Theme ID...", "theme_not_found": "Theme not found!", + "share_subj_theme": "reFilc Theme", }, "hu_hu": { "theme_prev": "Előnézet", @@ -26,6 +27,7 @@ extension SettingsLocalization on String { "enter_id": "ID megadása", "theme_id": "Téma azonosító...", "theme_not_found": "A téma nem található!", + "share_subj_theme": "reFilc Téma", }, "de_de": { "theme_prev": "Vorschau", @@ -39,6 +41,7 @@ extension SettingsLocalization on String { "enter_id": "Geben Sie die ID ein", "theme_id": "Themen-ID...", "theme_not_found": "Thema nicht gefunden!", + "share_subj_theme": "reFilc Thema", }, }; From c4dc03f41d5b68f166bcd12ccb1f3ebef7a06dab Mon Sep 17 00:00:00 2001 From: Kima Date: Mon, 18 Sep 2023 20:22:42 +0200 Subject: [PATCH 02/13] fixed warns and timetable thing again --- filcnaplo/lib/models/ad.dart | 1 - filcnaplo_kreta_api/lib/client/client.dart | 7 +++++++ filcnaplo_kreta_api/lib/providers/timetable_provider.dart | 6 +++--- filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart | 3 --- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/filcnaplo/lib/models/ad.dart b/filcnaplo/lib/models/ad.dart index dcc4b0c..09eba03 100644 --- a/filcnaplo/lib/models/ad.dart +++ b/filcnaplo/lib/models/ad.dart @@ -20,7 +20,6 @@ class Ad { }); factory Ad.fromJson(Map json) { - print(json); return Ad( title: json['title'] ?? 'Ad', description: json['description'] ?? '', diff --git a/filcnaplo_kreta_api/lib/client/client.dart b/filcnaplo_kreta_api/lib/client/client.dart index f150e3b..7f1f42a 100644 --- a/filcnaplo_kreta_api/lib/client/client.dart +++ b/filcnaplo_kreta_api/lib/client/client.dart @@ -26,6 +26,8 @@ class KretaClient { late final UserProvider _user; late final StatusProvider _status; + bool _loginRefreshing = false; + KretaClient({ this.accessToken, required SettingsProvider settings, @@ -164,6 +166,9 @@ class KretaClient { } Future refreshLogin() async { + if (_loginRefreshing) return; + _loginRefreshing = true; + User? loginUser = _user.user; if (loginUser == null) return; @@ -215,5 +220,7 @@ class KretaClient { } } } + + _loginRefreshing = false; } } diff --git a/filcnaplo_kreta_api/lib/providers/timetable_provider.dart b/filcnaplo_kreta_api/lib/providers/timetable_provider.dart index 1b9a3ef..b7d235f 100644 --- a/filcnaplo_kreta_api/lib/providers/timetable_provider.dart +++ b/filcnaplo_kreta_api/lib/providers/timetable_provider.dart @@ -5,7 +5,7 @@ import 'package:filcnaplo_kreta_api/client/api.dart'; import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/models/lesson.dart'; import 'package:filcnaplo_kreta_api/models/week.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; class TimetableProvider with ChangeNotifier { Map> lessons = {}; @@ -69,14 +69,14 @@ class TimetableProvider with ChangeNotifier { .getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end)); if (lessonsJson == null) { + if (kDebugMode) print('Cannot fetch Lessons for User ${user.id}'); + return; // throw "Cannot fetch Lessons for User ${user.id}"; } else { List lessonsList = lessonsJson.map((e) => Lesson.fromJson(e)).toList(); - if (lessons.isEmpty) return; - lessons[week] = lessonsList; await store(); diff --git a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart index 12add1b..e9c0fde 100644 --- a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart @@ -13,9 +13,6 @@ class AdTile extends StatelessWidget { @override Widget build(BuildContext context) { - print('geic'); - print(ad); - return Padding( padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0), child: PanelButton( From 056bf7ab580270786c9cc4a54829d5ae1c7f9242 Mon Sep 17 00:00:00 2001 From: Kima Date: Mon, 18 Sep 2023 20:33:20 +0200 Subject: [PATCH 03/13] made ad tile image circular --- .../lib/common/widgets/ad/ad_tile.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart index e9c0fde..fb9c943 100644 --- a/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart +++ b/filcnaplo_mobile_ui/lib/common/widgets/ad/ad_tile.dart @@ -35,12 +35,15 @@ class AdTile extends StatelessWidget { ], ), leading: ad.logoUrl != null - ? Image.network( - ad.logoUrl.toString(), - errorBuilder: (context, error, stackTrace) { - ad.logoUrl = null; - return const SizedBox(); - }, + ? ClipRRect( + borderRadius: BorderRadius.circular(50.0), + child: Image.network( + ad.logoUrl.toString(), + errorBuilder: (context, error, stackTrace) { + ad.logoUrl = null; + return const SizedBox(); + }, + ), ) : null, trailing: const Icon(FeatherIcons.externalLink), From 34f9929b16c7284ce6a3a2fb14f7bf75ad62ae60 Mon Sep 17 00:00:00 2001 From: Kima Date: Tue, 19 Sep 2023 18:57:57 +0200 Subject: [PATCH 04/13] removed "add spaces" text bc not relevant anymore --- .../lib/screens/login/login_screen.i18n.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/filcnaplo_mobile_ui/lib/screens/login/login_screen.i18n.dart b/filcnaplo_mobile_ui/lib/screens/login/login_screen.i18n.dart index 6e24d7c..c73dff0 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/login_screen.i18n.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/login_screen.i18n.dart @@ -13,7 +13,8 @@ extension Localization on String { "welcome": "Welcome, %s!", "missing_fields": "Missing Fields!", "invalid_grant": - "Invalid Username/Password! (Try adding spaces after Username)", + // "Invalid Username/Password! (Try adding spaces after Username)", + "Invalid Username/Password!", "error": "Failed to log in.", "schools_error": "Failed to get schools." }, @@ -27,7 +28,8 @@ extension Localization on String { "welcome": "Üdv, %s!", "missing_fields": "Hiányzó adatok!", "invalid_grant": - "Helytelen Felhasználónév/Jelszó! (Próbálj szóközöket írni a Felhasználónév után)", + // "Helytelen Felhasználónév/Jelszó! (Próbálj szóközöket írni a Felhasználónév után)", + "Helytelen Felhasználónév/Jelszó!", "error": "Sikertelen bejelentkezés.", "schools_error": "Nem sikerült lekérni az iskolákat." }, From 50d1803a182fa3a0300d82b8ede30683eb6970bf Mon Sep 17 00:00:00 2001 From: Kima Date: Tue, 19 Sep 2023 18:58:08 +0200 Subject: [PATCH 05/13] added token revoke to logout --- filcnaplo/lib/models/user.dart | 9 +++++ .../lib/screens/navigation/sidebar.dart | 7 ++-- filcnaplo_kreta_api/lib/client/api.dart | 2 ++ filcnaplo_kreta_api/lib/client/client.dart | 34 +++++++++++++++---- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/filcnaplo/lib/models/user.dart b/filcnaplo/lib/models/user.dart index 2cf88e2..9de5718 100644 --- a/filcnaplo/lib/models/user.dart +++ b/filcnaplo/lib/models/user.dart @@ -93,4 +93,13 @@ class User { "refresh_user_data": "false", }; } + + static Map logoutBody({ + required String refreshToken, + }) { + return { + "refresh_token": refreshToken, + "client_id": KretaAPI.clientId, + }; + } } diff --git a/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart b/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart index 1c0f5fa..5421f06 100644 --- a/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart +++ b/filcnaplo_desktop_ui/lib/screens/navigation/sidebar.dart @@ -176,13 +176,16 @@ class _SidebarState extends State { String? userId = user.id; if (userId == null) return; - // Delete User + // revoke refresh token + await Provider.of(context, listen: false).logout(); + + // delete user from app user.removeUser(userId); await Provider.of(context, listen: false) .store .removeUser(userId); - // If no other Users left, go back to LoginScreen + // if no other users left, go back to login screen if (user.getUsers().isNotEmpty) { user.setUser(user.getUsers().first.id); restore().then((_) => user.setUser(user.getUsers().first.id)); diff --git a/filcnaplo_kreta_api/lib/client/api.dart b/filcnaplo_kreta_api/lib/client/api.dart index f3c18d0..4c2f775 100644 --- a/filcnaplo_kreta_api/lib/client/api.dart +++ b/filcnaplo_kreta_api/lib/client/api.dart @@ -3,6 +3,7 @@ import 'package:intl/intl.dart'; class KretaAPI { // IDP API 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"; @@ -86,6 +87,7 @@ class BaseKreta { class KretaApiEndpoints { static const token = "/connect/token"; + static const revoke = "/connect/revocation"; static const nonce = "/nonce"; static const notes = "/ellenorzo/V3/Sajat/Feljegyzesek"; static const events = "/ellenorzo/V3/Sajat/FaliujsagElemek"; diff --git a/filcnaplo_kreta_api/lib/client/client.dart b/filcnaplo_kreta_api/lib/client/client.dart index 7f1f42a..d4ed845 100644 --- a/filcnaplo_kreta_api/lib/client/client.dart +++ b/filcnaplo_kreta_api/lib/client/client.dart @@ -187,13 +187,15 @@ class KretaClient { 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, - )); + Map? loginRes = await postAPI( + KretaAPI.login, + headers: headers, + body: User.loginBody( + username: loginUser.username, + password: loginUser.password, + instituteCode: loginUser.instituteCode, + ), + ); if (loginRes != null) { if (loginRes.containsKey("access_token")) { @@ -223,4 +225,22 @@ class KretaClient { _loginRefreshing = false; } + + Future logout() async { + User? loginUser = _user.user; + if (loginUser == null) return; + + Map headers = { + "content-type": "application/x-www-form-urlencoded", + }; + + await postAPI( + KretaAPI.logout, + headers: headers, + body: User.logoutBody( + refreshToken: refreshToken!, + ), + json: false, + ); + } } From 79677d657e2af7eb43b7a07cfca0bf8710cfa154 Mon Sep 17 00:00:00 2001 From: Kima Date: Tue, 19 Sep 2023 19:12:28 +0200 Subject: [PATCH 06/13] updated version string --- filcnaplo/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filcnaplo/pubspec.yaml b/filcnaplo/pubspec.yaml index f56bc27..d6d03a7 100644 --- a/filcnaplo/pubspec.yaml +++ b/filcnaplo/pubspec.yaml @@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez" homepage: https://refilc.hu publish_to: "none" -version: 4.2.3+223 +version: 4.2.4+224 environment: sdk: ">=2.17.0 <3.0.0" From fa75c1ec062c6c3bc2e5d42a2e4650285b4d7ee6 Mon Sep 17 00:00:00 2001 From: Kima Date: Fri, 22 Sep 2023 20:52:13 +0200 Subject: [PATCH 07/13] fixed share alert text --- .../lib/ui/mobile/settings/theme.i18n.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart b/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart index e02b33c..f578ff3 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart @@ -14,7 +14,10 @@ extension SettingsLocalization on String { "enter_id": "Enter ID", "theme_id": "Theme ID...", "theme_not_found": "Theme not found!", - "share_subj_theme": "reFilc Theme", + "attention": "Attention!", + "share_disclaimer": + "By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.", + "understand": "I understand", }, "hu_hu": { "theme_prev": "Előnézet", @@ -27,7 +30,10 @@ extension SettingsLocalization on String { "enter_id": "ID megadása", "theme_id": "Téma azonosító...", "theme_not_found": "A téma nem található!", - "share_subj_theme": "reFilc Téma", + "attention": "Figyelem!", + "share_disclaimer": + "A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.", + "understand": "Értem", }, "de_de": { "theme_prev": "Vorschau", @@ -41,7 +47,10 @@ extension SettingsLocalization on String { "enter_id": "Geben Sie die ID ein", "theme_id": "Themen-ID...", "theme_not_found": "Thema nicht gefunden!", - "share_subj_theme": "reFilc Thema", + "attention": "Achtung!", + "share_disclaimer": + "Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.", + "understand": "Ich verstehe", }, }; From a11ebce6fb934b890da7d20cf0ed3424779be975 Mon Sep 17 00:00:00 2001 From: Kima Date: Fri, 22 Sep 2023 21:50:26 +0200 Subject: [PATCH 08/13] also added test accounts to dev branch --- filcnaplo/lib/api/client.dart | 11 +- filcnaplo/lib/api/login.dart | 190 ++++++++++++++++++++++------------ 2 files changed, 130 insertions(+), 71 deletions(-) diff --git a/filcnaplo/lib/api/client.dart b/filcnaplo/lib/api/client.dart index bf290be..0a78545 100644 --- a/filcnaplo/lib/api/client.dart +++ b/filcnaplo/lib/api/client.dart @@ -53,9 +53,14 @@ class FilcAPI { .map((json) => School.fromJson(json)) .toList(); schools.add(School( - city: "Tiszabura", - instituteCode: "supporttest-reni-tiszabura-teszt01", - name: "FILC Éles Reni tiszabura-teszt", + city: "Stockholm", + instituteCode: "refilc-test-sweden", + name: "reFilc Test SE - Leo Ekström High School", + )); + schools.add(School( + city: "Madrid", + instituteCode: "refilc-test-spain", + name: "reFilc Test ES - Emilio Obrero University", )); return schools; } else { diff --git a/filcnaplo/lib/api/login.dart b/filcnaplo/lib/api/login.dart index 4afc7ce..0b9543e 100644 --- a/filcnaplo/lib/api/login.dart +++ b/filcnaplo/lib/api/login.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_print, use_build_context_synchronously import 'package:filcnaplo/utils/jwt.dart'; +import 'package:filcnaplo_kreta_api/models/school.dart'; import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; import 'package:filcnaplo_kreta_api/providers/event_provider.dart'; import 'package:filcnaplo_kreta_api/providers/exam_provider.dart'; @@ -20,6 +21,7 @@ import 'package:filcnaplo_kreta_api/models/week.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:filcnaplo/api/nonce.dart'; +import 'package:uuid/uuid.dart'; enum LoginState { missingFields, @@ -47,87 +49,139 @@ Future loginAPI({ void Function(User)? onLogin, void Function()? onSuccess, }) async { - Provider.of(context, listen: false).userAgent = - Provider.of(context, listen: false).config.userAgent; + Future testLogin(School school) async { + var user = User( + username: username, + password: password, + instituteCode: instituteCode, + name: 'Teszt Lajos', + student: Student( + birth: DateTime.now(), + id: const Uuid().v4(), + name: 'Teszt Lajos', + school: school, + yearId: '1', + parents: ['Teszt András', 'Teszt Linda'], + ), + role: Role.parent, + ); - Map headers = { - "content-type": "application/x-www-form-urlencoded", - }; + if (onLogin != null) onLogin(user); - String nonceStr = await Provider.of(context, listen: false) - .getAPI(KretaAPI.nonce, json: false); + // store test user in db + await Provider.of(context, listen: false) + .store + .storeUser(user); + Provider.of(context, listen: false).addUser(user); + Provider.of(context, listen: false).setUser(user.id); - Nonce nonce = getNonce(nonceStr, username, instituteCode); - headers.addAll(nonce.header()); + if (onSuccess != null) onSuccess(); - Map? res = await Provider.of(context, listen: false) - .postAPI(KretaAPI.login, - headers: headers, - body: User.loginBody( - username: username, - password: password, - instituteCode: instituteCode, - )); - if (res != null) { - if (res.containsKey("error")) { - if (res["error"] == "invalid_grant") { - return LoginState.invalidGrant; - } - } else { - if (res.containsKey("access_token")) { - try { - Provider.of(context, listen: false).accessToken = - res["access_token"]; - Map? studentJson = - await Provider.of(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"])!, - ); + return LoginState.success; + } - if (onLogin != null) onLogin(user); + // if institute matches one of test things do test login + if (instituteCode == 'refilc-test-sweden') { + School school = School( + city: "Stockholm", + instituteCode: "refilc-test-sweden", + name: "reFilc Test SE - Leo Ekström High School", + ); - // Store User in the database - await Provider.of(context, listen: false) - .store - .storeUser(user); - Provider.of(context, listen: false).addUser(user); - Provider.of(context, listen: false).setUser(user.id); + await testLogin(school); + } else if (instituteCode == 'refilc-test-spain') { + School school = School( + city: "Madrid", + instituteCode: "refilc-test-spain", + name: "reFilc Test ES - Emilio Obrero University", + ); - // Get user data + await testLogin(school); + } else { + // normal login from here + Provider.of(context, listen: false).userAgent = + Provider.of(context, listen: false).config.userAgent; + + Map headers = { + "content-type": "application/x-www-form-urlencoded", + }; + + String nonceStr = await Provider.of(context, listen: false) + .getAPI(KretaAPI.nonce, json: false); + + Nonce nonce = getNonce(nonceStr, username, instituteCode); + headers.addAll(nonce.header()); + + Map? res = await Provider.of(context, listen: false) + .postAPI(KretaAPI.login, + headers: headers, + body: User.loginBody( + username: username, + password: password, + instituteCode: instituteCode, + )); + if (res != null) { + if (res.containsKey("error")) { + if (res["error"] == "invalid_grant") { + return LoginState.invalidGrant; + } + } else { + if (res.containsKey("access_token")) { try { - await Future.wait([ - Provider.of(context, listen: false).fetch(), - Provider.of(context, listen: false) - .fetch(week: Week.current()), - Provider.of(context, listen: false).fetch(), - Provider.of(context, listen: false).fetch(), - Provider.of(context, listen: false).fetchAll(), - Provider.of(context, listen: false).fetch(), - Provider.of(context, listen: false).fetch(), - Provider.of(context, listen: false).fetch(), - ]); + Provider.of(context, listen: false).accessToken = + res["access_token"]; + Map? studentJson = + await Provider.of(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(context, listen: false) + .store + .storeUser(user); + Provider.of(context, listen: false).addUser(user); + Provider.of(context, listen: false).setUser(user.id); + + // Get user data + try { + await Future.wait([ + Provider.of(context, listen: false).fetch(), + Provider.of(context, listen: false) + .fetch(week: Week.current()), + Provider.of(context, listen: false).fetch(), + Provider.of(context, listen: false).fetch(), + Provider.of(context, listen: false).fetchAll(), + Provider.of(context, listen: false).fetch(), + Provider.of(context, listen: false).fetch(), + Provider.of(context, listen: false).fetch(), + ]); + } catch (error) { + print("WARNING: failed to fetch user data: $error"); + } + + if (onSuccess != null) onSuccess(); + + return LoginState.success; } catch (error) { - print("WARNING: failed to fetch user data: $error"); + print("ERROR: loginAPI: $error"); + // maybe check debug mode + // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("ERROR: $error"))); + return LoginState.failed; } - - 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; } From b4ff2fa815293ac504d7ce99f98b30d8d486340d Mon Sep 17 00:00:00 2001 From: Kima Date: Sun, 24 Sep 2023 20:12:10 +0200 Subject: [PATCH 09/13] added welcome message to settings --- filcnaplo/lib/database/init.dart | 1 + filcnaplo/lib/models/settings.dart | 13 +- .../lib/screens/settings/settings_screen.dart | 2 + .../settings/settings_screen.i18n.dart | 9 ++ .../ui/mobile/settings/welcome_message.dart | 142 ++++++++++++++++++ 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart diff --git a/filcnaplo/lib/database/init.dart b/filcnaplo/lib/database/init.dart index 4f7d1ed..274dbce 100644 --- a/filcnaplo/lib/database/init.dart +++ b/filcnaplo/lib/database/init.dart @@ -33,6 +33,7 @@ const settingsDB = DatabaseStruct("settings", { "renamed_subjects_italics": int, "renamed_teachers_enabled": int, "renamed_teachers_italics": int, "live_activity_color": String, + "welcome_message": String, }); // DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING // YOU'VE BEEN WARNED!!! diff --git a/filcnaplo/lib/models/settings.dart b/filcnaplo/lib/models/settings.dart index 3e42b26..05989b6 100644 --- a/filcnaplo/lib/models/settings.dart +++ b/filcnaplo/lib/models/settings.dart @@ -76,6 +76,7 @@ class SettingsProvider extends ChangeNotifier { bool _renamedTeachersEnabled; bool _renamedTeachersItalics; Color _liveActivityColor; + String _welcomeMessage; SettingsProvider({ DatabaseProvider? database, @@ -120,6 +121,7 @@ class SettingsProvider extends ChangeNotifier { required bool renameTeachersEnabled, required bool renameTeachersItalics, required Color liveActivityColor, + required String welcomeMessage, }) : _database = database, _language = language, _startPage = startPage, @@ -161,7 +163,8 @@ class SettingsProvider extends ChangeNotifier { _renamedSubjectsItalics = renameSubjectsItalics, _renamedTeachersEnabled = renameTeachersEnabled, _renamedTeachersItalics = renameTeachersItalics, - _liveActivityColor = liveActivityColor; + _liveActivityColor = liveActivityColor, + _welcomeMessage = welcomeMessage; factory SettingsProvider.fromMap(Map map, {required DatabaseProvider database}) { @@ -223,6 +226,7 @@ class SettingsProvider extends ChangeNotifier { renameTeachersEnabled: map["renamed_teachers_enabled"] == 1, renameTeachersItalics: map["renamed_teachers_italics"] == 1, liveActivityColor: Color(map["live_activity_color"]), + welcomeMessage: map["welcome_message"], ); } @@ -272,6 +276,7 @@ class SettingsProvider extends ChangeNotifier { "renamed_teachers_enabled": _renamedTeachersEnabled ? 1 : 0, "renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0, "live_activity_color": _liveActivityColor.value, + "welcome_message": _welcomeMessage, }; } @@ -325,6 +330,7 @@ class SettingsProvider extends ChangeNotifier { renameTeachersEnabled: false, renameTeachersItalics: false, liveActivityColor: const Color(0xFF676767), + welcomeMessage: '', ); } @@ -373,6 +379,7 @@ class SettingsProvider extends ChangeNotifier { bool get renamedTeachersEnabled => _renamedTeachersEnabled; bool get renamedTeachersItalics => _renamedTeachersItalics; Color get liveActivityColor => _liveActivityColor; + String get welcomeMessage => _welcomeMessage; Future update({ bool store = true, @@ -417,6 +424,7 @@ class SettingsProvider extends ChangeNotifier { bool? renamedTeachersEnabled, bool? renamedTeachersItalics, Color? liveActivityColor, + String? welcomeMessage, }) async { if (language != null && language != _language) _language = language; if (startPage != null && startPage != _startPage) _startPage = startPage; @@ -535,6 +543,9 @@ class SettingsProvider extends ChangeNotifier { if (liveActivityColor != null && liveActivityColor != _liveActivityColor) { _liveActivityColor = liveActivityColor; } + if (welcomeMessage != null && welcomeMessage != _welcomeMessage) { + _welcomeMessage = welcomeMessage; + } if (store) await _database?.store.storeSettings(this); notifyListeners(); } diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart index 6a6339c..8d199b9 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart @@ -43,6 +43,7 @@ import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart'; import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart'; +import 'package:filcnaplo_premium/ui/mobile/settings/welcome_message.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({Key? key}) : super(key: key); @@ -454,6 +455,7 @@ class _SettingsScreenState extends State Material( type: MaterialType.transparency, child: MenuNotifications(settings: settings)), + WelcomeMessagePanelButton(settings, user), ], ), ), diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart index 3ff52aa..d6c242d 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.i18n.dart @@ -73,6 +73,9 @@ extension SettingsLocalization on String { "devsettings": "Developer Settings", "devmode": "Developer Mode", "copy_jwt": "Copy JWT", + "welcome_msg": "Welcome Message", + "default": "Default", + "edit_welcome_msg": "Edit welcome message", }, "hu_hu": { "personal_details": "Személyes információk", @@ -144,6 +147,9 @@ extension SettingsLocalization on String { "devsettings": "Fejlesztői Beállítások", "devmode": "Fejlesztői mód", "copy_jwt": "JWT másolása", + "welcome_msg": "Üdvözlő üzenet", + "default": "Alapértelmezett", + "edit_welcome_msg": "Üdvözlő üzenet szerkesztése", }, "de_de": { "personal_details": "Persönliche Angaben", @@ -214,6 +220,9 @@ extension SettingsLocalization on String { "devsettings": "Entwickleroptionen", "devmode": "Entwicklermodus", "copy_jwt": "JWT kopieren", + "welcome_msg": "Willkommensnachricht", + "default": "Standard", + "edit_welcome_msg": "Begrüßungsnachricht bearbeiten", }, }; diff --git a/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart b/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart new file mode 100644 index 0000000..efb834f --- /dev/null +++ b/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart @@ -0,0 +1,142 @@ +import 'package:filcnaplo/api/providers/user_provider.dart'; +import 'package:filcnaplo/models/settings.dart'; +import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; +import 'package:filcnaplo_premium/models/premium_scopes.dart'; +import 'package:filcnaplo_premium/providers/premium_provider.dart'; +import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; +import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart'; +import 'package:provider/provider.dart'; +import 'package:i18n_extension/i18n_extension.dart'; + +// ignore: must_be_immutable +class WelcomeMessagePanelButton extends StatelessWidget { + late SettingsProvider settingsProvider; + late UserProvider user; + + WelcomeMessagePanelButton(this.settingsProvider, this.user, {Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + String finalName = ((user.nickname ?? '') != '' + ? user.nickname + : (user.displayName ?? '') != '' + ? user.displayName + : 'János') ?? + 'János'; + + return PanelButton( + onPressed: () { + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.all)) { + PremiumLockedFeatureUpsell.show( + context: context, feature: PremiumFeature.profile); + return; + } + showDialog( + context: context, + builder: (context) => WelcomeMessageEditor(settingsProvider)); + }, + title: Text("welcome_msg".i18n), + leading: const Icon(FeatherIcons.smile), + trailing: SizedBox( + width: 100, + child: Expanded( + child: Text( + settingsProvider.welcomeMessage != '' + ? localizeFill( + settingsProvider.welcomeMessage, + [finalName, finalName, finalName], + ) + : 'default'.i18n, + style: const TextStyle(fontSize: 14.0), + textAlign: TextAlign.end, + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ); + } +} + +// ignore: must_be_immutable +class WelcomeMessageEditor extends StatefulWidget { + late SettingsProvider settingsProvider; + + WelcomeMessageEditor(this.settingsProvider, {Key? key}) : super(key: key); + + @override + State createState() => _WelcomeMessageEditorState(); +} + +class _WelcomeMessageEditorState extends State { + final _welcomeMsg = TextEditingController(); + + @override + void initState() { + super.initState(); + _welcomeMsg.text = + widget.settingsProvider.welcomeMessage.replaceAll('%s', '%name%'); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text("edit_welcome_msg".i18n), + content: TextField( + controller: _welcomeMsg, + autofocus: true, + decoration: InputDecoration( + border: const OutlineInputBorder(), + label: Text('welcome_msg'.i18n), + suffixIcon: IconButton( + icon: const Icon(FeatherIcons.x), + onPressed: () { + setState(() { + _welcomeMsg.text = ""; + }); + }, + ), + ), + ), + actions: [ + TextButton( + child: Text( + "cancel".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () { + Navigator.of(context).maybePop(); + }, + ), + TextButton( + child: Text( + "done".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () { + // var trimmed = _welcomeMsg.text.trim(); + + // var defLen = trimmed.length; + // var replacedLen = trimmed.replaceAll('%s', '').length; + + // if (defLen - 2 > replacedLen) { + // print('fuck yourself rn'); + // } + var finalText = _welcomeMsg.text + .trim() + .replaceAll('%s', '') + .replaceFirst('%name%', '%s'); + + widget.settingsProvider + .update(welcomeMessage: finalText, store: true); + Navigator.of(context).pop(true); + }, + ), + ], + ); + } +} From 62124890015a72e64d51484b90787e7b313d3d33 Mon Sep 17 00:00:00 2001 From: Kima Date: Mon, 25 Sep 2023 18:30:20 +0200 Subject: [PATCH 10/13] added translation for dkt --- .../lib/screens/settings/settings_screen.i18n.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.i18n.dart b/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.i18n.dart index 530a2bf..fb5102f 100644 --- a/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.i18n.dart +++ b/filcnaplo_desktop_ui/lib/screens/settings/settings_screen.i18n.dart @@ -5,7 +5,7 @@ extension SettingsLocalization on String { { "en_en": { "personal_details": "Personal Details", - "open_dkt": "Open DKT", + "open_dkt": "Open DCS", "edit_nickname": "Edit Nickname", "edit_profile_picture": "Edit Profile Picture", "remove_profile_picture": "Remove Profile Picture", @@ -39,7 +39,8 @@ extension SettingsLocalization on String { "done": "Done", "reset": "Reset", "open": "Open", - "data_collected": "Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier", + "data_collected": + "Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier", "Analytics": "Analytics", "Anonymous Usage Analytics": "Anonymous Usage Analytics", "graph_class_avg": "Class average on graph", @@ -98,7 +99,8 @@ extension SettingsLocalization on String { "done": "Kész", "reset": "Visszaállítás", "open": "Megnyitás", - "data_collected": "Gyűjtött adat: Platform (pl. Android), App verzió (pl. 3.0.0), Egyedi telepítési azonosító", + "data_collected": + "Gyűjtött adat: Platform (pl. Android), App verzió (pl. 3.0.0), Egyedi telepítési azonosító", "Analytics": "Analitika", "Anonymous Usage Analytics": "Névtelen használati analitika", "graph_class_avg": "Osztályátlag a grafikonon", @@ -123,7 +125,7 @@ extension SettingsLocalization on String { }, "de_de": { "personal_details": "Persönliche Angaben", - "open_dkt": "Öffnen DKT", + "open_dkt": "Öffnen RDZ", "edit_nickname": "Spitznamen bearbeiten", "edit_profile_picture": "Profilbild bearbeiten", "remove_profile_picture": "Profilbild entfernen", @@ -157,7 +159,8 @@ extension SettingsLocalization on String { "done": "Fertig", "reset": "Zurücksetzen", "open": "Öffnen", - "data_collected": "Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung", + "data_collected": + "Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung", "Analytics": "Analytik", "Anonymous Usage Analytics": "Anonyme Nutzungsanalyse", "graph_class_avg": "Klassendurchschnitt in der Grafik", From 9cda8c5d299b5ac3a234c9e9669fa6df02d64620 Mon Sep 17 00:00:00 2001 From: Kima Date: Mon, 25 Sep 2023 18:31:32 +0200 Subject: [PATCH 11/13] added custom welcome message to home screen --- .../lib/pages/home/home_page.dart | 39 +++++++++++-------- .../ui/mobile/settings/welcome_message.dart | 4 +- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/filcnaplo_mobile_ui/lib/pages/home/home_page.dart b/filcnaplo_mobile_ui/lib/pages/home/home_page.dart index 02298d7..158472c 100755 --- a/filcnaplo_mobile_ui/lib/pages/home/home_page.dart +++ b/filcnaplo_mobile_ui/lib/pages/home/home_page.dart @@ -30,6 +30,7 @@ import 'package:provider/provider.dart'; import 'home_page.i18n.dart'; import 'package:filcnaplo/ui/filter/widgets.dart'; import 'package:filcnaplo/ui/filter/sort.dart'; +import 'package:i18n_extension/i18n_extension.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -94,6 +95,13 @@ class _HomePageState extends State with TickerProviderStateMixin { void setGreeting() { DateTime now = DateTime.now(); + List nameParts = user.displayName?.split(" ") ?? ["?"]; + if (!settings.presentationMode) { + firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0]; + } else { + firstName = "János"; + } + if (now.isBefore(DateTime(now.year, DateTime.august, 31)) && now.isAfter(DateTime(now.year, DateTime.june, 14))) { greeting = "goodrest"; @@ -104,16 +112,16 @@ class _HomePageState extends State with TickerProviderStateMixin { Future.delayed(const Duration(seconds: 1)) .then((value) => mounted ? _confettiController?.play() : null); } - } else if (now.month == user.student?.birth.month && - now.day == user.student?.birth.day) { - greeting = "happybirthday"; + // } else if (now.month == user.student?.birth.month && + // now.day == user.student?.birth.day) { + // greeting = "happybirthday"; - if (NavigationScreen.of(context)?.init("confetti") ?? false) { - _confettiController = - ConfettiController(duration: const Duration(seconds: 3)); - Future.delayed(const Duration(seconds: 1)) - .then((value) => mounted ? _confettiController?.play() : null); - } + // if (NavigationScreen.of(context)?.init("confetti") ?? false) { + // _confettiController = + // ConfettiController(duration: const Duration(seconds: 3)); + // Future.delayed(const Duration(seconds: 1)) + // .then((value) => mounted ? _confettiController?.play() : null); + // } } else if (now.isAfter(DateTime(now.year, DateTime.may, 28)) && now.isBefore(DateTime(now.year, DateTime.may, 30))) { greeting = "refilcopen"; @@ -130,6 +138,12 @@ class _HomePageState extends State with TickerProviderStateMixin { greeting = "merryxmas"; } else if (now.month == DateTime.january && now.day == 1) { greeting = "happynewyear"; + } else if (settings.welcomeMessage.replaceAll(' ', '') != '') { + greeting = settings.welcomeMessage; + greeting = localizeFill( + settings.welcomeMessage, + [firstName], + ); } else if (now.hour >= 18) { greeting = "goodevening"; } else if (now.hour >= 10) { @@ -155,13 +169,6 @@ class _HomePageState extends State with TickerProviderStateMixin { setGreeting(); - List nameParts = user.displayName?.split(" ") ?? ["?"]; - if (!settings.presentationMode) { - firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0]; - } else { - firstName = "János"; - } - return Scaffold( body: Stack( children: [ diff --git a/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart b/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart index efb834f..5e412f9 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/welcome_message.dart @@ -45,10 +45,10 @@ class WelcomeMessagePanelButton extends StatelessWidget { width: 100, child: Expanded( child: Text( - settingsProvider.welcomeMessage != '' + settingsProvider.welcomeMessage.replaceAll(' ', '') != '' ? localizeFill( settingsProvider.welcomeMessage, - [finalName, finalName, finalName], + [finalName], ) : 'default'.i18n, style: const TextStyle(fontSize: 14.0), From b37af9f5a5f07b074d5c42a72e8974e40060033e Mon Sep 17 00:00:00 2001 From: Kima Date: Mon, 25 Sep 2023 22:46:24 +0200 Subject: [PATCH 12/13] fixed kreten status shit maybe idk --- filcnaplo/lib/api/login.dart | 2 ++ .../lib/api/providers/status_provider.dart | 30 +++++++++++++++++++ filcnaplo_premium/lib/api/auth.dart | 6 ++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/filcnaplo/lib/api/login.dart b/filcnaplo/lib/api/login.dart index 0b9543e..f0834c8 100644 --- a/filcnaplo/lib/api/login.dart +++ b/filcnaplo/lib/api/login.dart @@ -62,6 +62,8 @@ Future loginAPI({ school: school, yearId: '1', parents: ['Teszt András', 'Teszt Linda'], + json: {"a": "b"}, + address: '1117 Budapest, Gábor Dénes utca 4.', ), role: Role.parent, ); diff --git a/filcnaplo/lib/api/providers/status_provider.dart b/filcnaplo/lib/api/providers/status_provider.dart index 5822366..a556a40 100644 --- a/filcnaplo/lib/api/providers/status_provider.dart +++ b/filcnaplo/lib/api/providers/status_provider.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/widgets.dart'; import 'package:http/http.dart' as http; @@ -12,6 +14,7 @@ class StatusProvider extends ChangeNotifier { StatusProvider() { _handleNetworkChanges(); + _handleDNSFailure(); Connectivity().checkConnectivity().then((value) => _networkType = value); } @@ -24,6 +27,7 @@ class StatusProvider extends ChangeNotifier { _networkType = event; if (event == ConnectivityResult.none) { if (!_stack.contains(Status.network)) { + _stack.remove(Status.apiError); _stack.insert(0, Status.network); notifyListeners(); } @@ -36,6 +40,31 @@ class StatusProvider extends ChangeNotifier { }); } + void _handleDNSFailure() { + try { + InternetAddress.lookup('api.refilc.hu').then((status) { + if (status.isEmpty) { + if (!_stack.contains(Status.network)) { + _stack.remove(Status.apiError); + _stack.insert(0, Status.network); + notifyListeners(); + } + } else { + if (_stack.contains(Status.network)) { + _stack.remove(Status.network); + notifyListeners(); + } + } + }); + } on SocketException catch (_) { + if (!_stack.contains(Status.network)) { + _stack.remove(Status.apiError); + _stack.insert(0, Status.network); + notifyListeners(); + } + } + } + void triggerRequest(http.Response res) { if (res.headers.containsKey("x-maintenance-mode") || res.statusCode == 503) { @@ -50,6 +79,7 @@ class StatusProvider extends ChangeNotifier { } } + if (_stack.contains(Status.network)) return; if (res.body == "invalid_grant" || res.body.replaceAll(' ', '') == '' || res.statusCode == 400) { diff --git a/filcnaplo_premium/lib/api/auth.dart b/filcnaplo_premium/lib/api/auth.dart index 3408919..45ddf5c 100644 --- a/filcnaplo_premium/lib/api/auth.dart +++ b/filcnaplo_premium/lib/api/auth.dart @@ -82,12 +82,12 @@ class PremiumAuth { //} // Skip premium check when disconnected - //try { + // try { // final status = await InternetAddress.lookup('github.com'); // if (status.isEmpty) return false; - //} on SocketException catch (_) { + // } on SocketException catch (_) { // return false; - //} + // } //for (int tries = 0; tries < 3; tries++) { // try { From 6f85a4ebc1a5fd81d388dc7af0b0b26cb648c073 Mon Sep 17 00:00:00 2001 From: Kima Date: Tue, 26 Sep 2023 18:04:44 +0200 Subject: [PATCH 13/13] fixed settings top padding --- filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart index 8d199b9..8d1df64 100755 --- a/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart +++ b/filcnaplo_mobile_ui/lib/screens/settings/settings_screen.dart @@ -207,7 +207,7 @@ class _SettingsScreenState extends State opacity: 1 - _hideContainersController.value, child: Column( children: [ - const SizedBox(height: 32.0), + const SizedBox(height: 45.0), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,