import 'package:flutter/material.dart'; import 'package:app_links/app_links.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_event.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_state.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:hl_lieferservice/util.dart'; import 'dart:async'; class LoginPage extends StatefulWidget { final bool sessionExpired; const LoginPage({super.key, this.sessionExpired = false}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final _loginFormKey = GlobalKey(); bool _isLoading = false; late AppLinks _appLinks; StreamSubscription? _linkSubscription; @override void initState() { super.initState(); _appLinks = AppLinks(); } @override void dispose() { _linkSubscription?.cancel(); super.dispose(); } void _onPressLogin() async { setState(() => _isLoading = true); try { debugPrint("🔵 Setting up deep link listener..."); final completer = Completer(); // Listen for deep links BEFORE opening browser _linkSubscription = _appLinks.uriLinkStream.listen( (Uri uri) { debugPrint("🟢 Deep link received: $uri"); if (uri.scheme == 'myapp' && !completer.isCompleted) { completer.complete(uri); } }, onError: (err) { debugPrint("🔴 Deep link error: $err"); if (!completer.isCompleted) { completer.completeError(err); } }, ); // Small delay to ensure listener is ready await Future.delayed(const Duration(milliseconds: 500)); final loginUrl = Uri.parse('${getConfig().backendUrl}/login'); final launched = await launchUrl( loginUrl, mode: LaunchMode.externalApplication, ); if (!launched) { throw Exception('Could not launch browser'); } debugPrint("🔵 Browser opened. Waiting for callback..."); // Wait for the deep link callback final callbackUri = await completer.future.timeout( const Duration(minutes: 5), onTimeout: () { debugPrint("⏱️ Timeout - no callback received"); throw TimeoutException('Login timeout'); }, ); final sessionId = callbackUri.queryParameters['session_id']!; debugPrint("✅ Success! Callback: $callbackUri"); debugPrint("✅ Session ID: $sessionId"); await _linkSubscription?.cancel(); _linkSubscription = null; if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Login erfolgreich!'), backgroundColor: Colors.green, ), ); context.read().add(SetAuthenticatedEvent(sessionId: sessionId)); } } on TimeoutException { debugPrint("❌ Timeout"); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Login Timeout')), ); } } catch (e) { debugPrint("❌ Error: $e"); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Fehler: $e')), ); } } finally { await _linkSubscription?.cancel(); _linkSubscription = null; if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Column( children: [ if (widget.sessionExpired) MaterialBanner( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), content: const Text( "Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.", style: TextStyle(color: Colors.white), ), backgroundColor: Colors.orange.shade800, leading: const Icon(Icons.warning_amber_rounded, color: Colors.white), actions: [const SizedBox.shrink()], ), Expanded( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(50), child: Column( children: [ Image.asset( "assets/holzleitner_Logo_2017_RZ_transparent.png", ), const Padding( padding: EdgeInsets.only(top: 20), child: Text( "Auslieferservice", style: TextStyle( fontWeight: FontWeight.w400, fontSize: 20, ), ), ), ], ), ), Form( key: _loginFormKey, child: FractionallySizedBox( widthFactor: 0.8, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only(top: 15, bottom: 15), child: BlocBuilder( builder: (context, authState) { final isBusy = _isLoading || authState is Authenticating; if (!isBusy) { return OutlinedButton( onPressed: _onPressLogin, child: const Text( "Anmelden mit Holzleitner Login", ), ); } return Column( children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text( authState is Authenticating ? 'Anmeldung wird abgeschlossen…' : 'Warte auf Login...', ), ], ); }, ), ), ], ), ), ), ], ), ), ), ], ), ); } }