diff --git a/refilc_mobile_ui/lib/screens/login/kreten_login.dart b/refilc_mobile_ui/lib/screens/login/kreten_login.dart index 22c74cc2..997a193d 100644 --- a/refilc_mobile_ui/lib/screens/login/kreten_login.dart +++ b/refilc_mobile_ui/lib/screens/login/kreten_login.dart @@ -39,6 +39,7 @@ class _KretenLoginWidgetState extends State late AnimationController _animationController; var loadingPercentage = 0; var currentUrl = ''; + bool _initialPageLoaded = false; bool _hasFadedIn = false; bool _hasError = false; String _errorMessage = ''; @@ -48,6 +49,28 @@ class _KretenLoginWidgetState extends State static const _loginUrl = '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'; + static final Uri _redirectUri = Uri.parse( + 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect', + ); + + bool _isRedirectUri(Uri uri) { + return uri.scheme == _redirectUri.scheme && + uri.host == _redirectUri.host && + uri.path == _redirectUri.path; + } + + bool _shouldIgnoreError(WebResourceError error) { + if (error.isForMainFrame == false) { + return true; + } + + final String description = error.description.toLowerCase(); + return error.errorCode == -999 || + description.contains('cancelled') || + description.contains('canceled') || + description.contains('frame load interrupted'); + } + @override void initState() { super.initState(); @@ -61,84 +84,63 @@ class _KretenLoginWidgetState extends State ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setNavigationDelegate(NavigationDelegate( onNavigationRequest: (n) async { - if (n.url.startsWith('https://mobil.e-kreta.hu')) { - setState(() { - loadingPercentage = 0; - currentUrl = n.url; - }); - - // 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; + final Uri? uri = Uri.tryParse(n.url); + if (uri != null && _isRedirectUri(uri)) { + final String? code = uri.queryParameters['code']; + if (code != null && code.isNotEmpty) { + _timeoutTimer?.cancel(); + widget.onLogin(code); + return NavigationDecision.prevent; + } } + + return NavigationDecision.navigate; }, onPageStarted: (url) async { - // setState(() { - // loadingPercentage = 0; - // currentUrl = url; - // }); + if (!mounted) return; - // // final String instituteCode = widget.selectedSchool; - // if (!url.startsWith( - // 'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=')) { - // return; - // } + setState(() { + currentUrl = url; + _hasError = false; + _errorMessage = ''; + _hasTimedOut = false; - // 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(':'); + if (!_initialPageLoaded) { + loadingPercentage = 0; + } + }); - // 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(); + _startTimeoutTimer(); }, onProgress: (progress) { + if (!mounted) return; + setState(() { loadingPercentage = progress; }); }, onPageFinished: (url) { _timeoutTimer?.cancel(); + + if (!mounted) return; + setState(() { + currentUrl = url; + _initialPageLoaded = true; + _hasError = false; + _hasTimedOut = false; loadingPercentage = 100; }); }, onWebResourceError: (error) { + if (_shouldIgnoreError(error)) { + return; + } + _timeoutTimer?.cancel(); + + if (!mounted) return; + setState(() { _hasError = true; _errorMessage = error.description; @@ -155,7 +157,7 @@ class _KretenLoginWidgetState extends State void _startTimeoutTimer() { _timeoutTimer?.cancel(); _timeoutTimer = Timer(const Duration(seconds: 15), () { - if (mounted && loadingPercentage < 100 && !_hasError) { + if (mounted && !_initialPageLoaded && !_hasError) { setState(() { _hasTimedOut = true; }); @@ -168,6 +170,7 @@ class _KretenLoginWidgetState extends State _hasError = false; _errorMessage = ''; _hasTimedOut = false; + _initialPageLoaded = false; loadingPercentage = 0; _hasFadedIn = false; }); @@ -244,7 +247,7 @@ class _KretenLoginWidgetState extends State } // Trigger the fade-in animation only once when loading reaches 100% - if (loadingPercentage == 100 && !_hasFadedIn) { + if (_initialPageLoaded && !_hasFadedIn) { _animationController.forward(); // Play the animation _hasFadedIn = true; // Set the flag to true, so the animation is not replayed @@ -252,9 +255,8 @@ class _KretenLoginWidgetState extends State return Stack( children: [ - // Webview that will be displayed only when the loading is 100% - if (loadingPercentage == 100) - FadeTransition( + Positioned.fill( + child: FadeTransition( opacity: Tween(begin: 0, end: 1).animate( CurvedAnimation( parent: _animationController, @@ -265,23 +267,28 @@ class _KretenLoginWidgetState extends State 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 - ), - ], - ); - }, + ), + if (!_initialPageLoaded) + Positioned.fill( + child: ColoredBox( + color: Theme.of(context).colorScheme.surface, + child: 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 == 0 ? null : value, + ), + ], + ); + }, + ), + ), ), ), ],