diff --git a/refilc/assets/images/showcase1.png b/refilc/assets/images/showcase1.png index e9465f4..c4f522a 100644 Binary files a/refilc/assets/images/showcase1.png and b/refilc/assets/images/showcase1.png differ diff --git a/refilc/assets/images/showcase2.png b/refilc/assets/images/showcase2.png index e1bc254..ae54a47 100644 Binary files a/refilc/assets/images/showcase2.png and b/refilc/assets/images/showcase2.png differ diff --git a/refilc/assets/images/showcase3.png b/refilc/assets/images/showcase3.png index 6d18a00..72e6ec5 100644 Binary files a/refilc/assets/images/showcase3.png and b/refilc/assets/images/showcase3.png differ diff --git a/refilc/assets/images/showcase4.png b/refilc/assets/images/showcase4.png index 873c834..5c54167 100644 Binary files a/refilc/assets/images/showcase4.png and b/refilc/assets/images/showcase4.png differ diff --git a/refilc/pubspec.yaml b/refilc/pubspec.yaml index fa73090..a7fa3f0 100644 --- a/refilc/pubspec.yaml +++ b/refilc/pubspec.yaml @@ -3,7 +3,7 @@ description: "Egy nem hivatalos e-KRÉTA kliens, diákoktól diákoknak." homepage: https://refilc.hu publish_to: "none" -version: 5.0.4+272 +version: 5.0.4+274 environment: sdk: ">=3.3.2 <=3.4.3" diff --git a/refilc_kreta_api/lib/client/client.dart b/refilc_kreta_api/lib/client/client.dart index a198731..e9ff255 100644 --- a/refilc_kreta_api/lib/client/client.dart +++ b/refilc_kreta_api/lib/client/client.dart @@ -257,26 +257,28 @@ class KretaClient { refreshToken ??= loginUser.refreshToken; - print("REFRESH TOKEN BELOW"); - print(refreshToken); + // print("REFRESH TOKEN BELOW"); + // print(refreshToken); if (refreshToken != null) { - print("REFRESHING LOGIN"); + // print("REFRESHING LOGIN"); Map? res = await postAPI(KretaAPI.login, headers: headers, body: User.refreshBody( refreshToken: loginUser.refreshToken, instituteCode: loginUser.instituteCode, )); - print("REFRESH RESPONSE BELOW"); - print(res); + // 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); + // _user.removeUser(loginUser.id); + // await _database.store.removeUser(loginUser.id); + + print("invalid refresh token (invalid_grant)"); // return error return "refresh_token_expired"; diff --git a/refilc_mobile_ui/lib/plus/components/active_sponsor_card.dart b/refilc_mobile_ui/lib/plus/components/active_sponsor_card.dart index af904a6..72663df 100644 --- a/refilc_mobile_ui/lib/plus/components/active_sponsor_card.dart +++ b/refilc_mobile_ui/lib/plus/components/active_sponsor_card.dart @@ -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: diff --git a/refilc_mobile_ui/lib/screens/login/kreten_login.dart b/refilc_mobile_ui/lib/screens/login/kreten_login.dart index 8396f8c..c203a75 100644 --- a/refilc_mobile_ui/lib/screens/login/kreten_login.dart +++ b/refilc_mobile_ui/lib/screens/login/kreten_login.dart @@ -3,56 +3,101 @@ import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; -class KretenLoginScreen extends StatefulWidget { - const KretenLoginScreen({super.key, required this.onLogin}); +class KretenLoginWidget extends StatefulWidget { + const KretenLoginWidget({super.key, required this.onLogin}); // final String selectedSchool; final void Function(String code) onLogin; @override - State createState() => _KretenLoginScreenState(); + State createState() => _KretenLoginWidgetState(); } -class _KretenLoginScreenState extends State { +class _KretenLoginWidgetState extends State + 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; + }); - // final String instituteCode = widget.selectedSchool; - if (!url.startsWith( - 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=')) { - return; + // final String instituteCode = widget.selectedSchool; + // if (!n.url.startsWith( + // 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=')) { + // return; + // } + + List 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; } - - List 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(); + }, + onPageStarted: (url) async { + // setState(() { + // loadingPercentage = 0; + // currentUrl = url; // }); - // Navigator.of(context).pop(); + + // // final String instituteCode = widget.selectedSchool; + // if (!url.startsWith( + // 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=')) { + // return; + // } + + // List 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(() { @@ -78,24 +123,57 @@ class _KretenLoginScreenState extends State { // 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(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(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 + ), + ], + ); + }, + ), + ), + ], ); } } diff --git a/refilc_mobile_ui/lib/screens/login/login_screen.dart b/refilc_mobile_ui/lib/screens/login/login_screen.dart index e574253..9de9d59 100644 --- a/refilc_mobile_ui/lib/screens/login/login_screen.dart +++ b/refilc_mobile_ui/lib/screens/login/login_screen.dart @@ -3,14 +3,19 @@ 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'; import 'package:refilc_mobile_ui/screens/login/kreten_login.dart'; //new library for new web login class LoginScreen extends StatefulWidget { @@ -27,8 +32,8 @@ class LoginScreenState extends State { final passwordController = TextEditingController(); final schoolController = SchoolInputController(); final _scrollController = ScrollController(); - // new controllers final codeController = TextEditingController(); + LoginState _loginState = LoginState.normal; bool showBack = false; @@ -80,7 +85,7 @@ class LoginScreenState extends State { 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; + bool selected = false; return Scaffold( body: Container( @@ -196,7 +201,7 @@ class LoginScreenState extends State { ], ), Container( - height: 300, + height: 280, width: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( @@ -218,81 +223,135 @@ class LoginScreenState extends State { child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16), - child: 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, + 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, ), ), ), - const SizedBox(height: 8), ], ), ), @@ -388,13 +447,6 @@ class LoginScreenState extends State { // }), // ); // } - - // setState(() => _loginState = LoginState.inProgress); - // _callAPI(); - // } - - // new login api - // ignore: non_constant_identifier_names void _NewLoginAPI({required BuildContext context}) { String code = codeController.text; diff --git a/refilc_mobile_ui/lib/screens/login/login_screen.i18n.dart b/refilc_mobile_ui/lib/screens/login/login_screen.i18n.dart index f8a07f8..da6b0e3 100644 --- a/refilc_mobile_ui/lib/screens/login/login_screen.i18n.dart +++ b/refilc_mobile_ui/lib/screens/login/login_screen.i18n.dart @@ -33,7 +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\ne-KRÉTA account", + "login_w_kreta_acc": "Log in with your e-KRÉTA account", }, "hu_hu": { "username": "Felhasználónév", @@ -65,7 +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": "Belépés e-KRÉTA\nfiókkal", + "login_w_kreta_acc": "Bejelentkezés e-KRÉTA fiókkal", }, "de_de": { "username": "Benutzername", @@ -97,7 +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\nanmelden", + "login_w_kreta_acc": "Mit e-KRÉTA-Konto anmelden", }, };