// lib/ui/settings/pinepods_login.dart import 'package:flutter/material.dart'; import 'package:pinepods_mobile/bloc/settings/settings_bloc.dart'; import 'package:pinepods_mobile/l10n/L.dart'; import 'package:pinepods_mobile/services/pinepods/pinepods_service.dart'; import 'package:pinepods_mobile/services/pinepods/login_service.dart'; import 'package:pinepods_mobile/ui/widgets/restart_widget.dart'; import 'package:pinepods_mobile/ui/settings/settings_section_label.dart'; import 'package:provider/provider.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; class PinepodsLoginWidget extends StatefulWidget { const PinepodsLoginWidget({Key? key}) : super(key: key); @override State createState() => _PinepodsLoginWidgetState(); } class _PinepodsLoginWidgetState extends State { final _serverController = TextEditingController(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _mfaController = TextEditingController(); bool _isLoading = false; bool _showMfaField = false; String _errorMessage = ''; bool _isLoggedIn = false; String? _connectedServer; String? _tempServerUrl; String? _tempUsername; int? _tempUserId; String? _tempMfaSessionToken; @override void initState() { super.initState(); // Initialize UI based on saved settings _loadSavedSettings(); } void _loadSavedSettings() { var settingsBloc = Provider.of(context, listen: false); var settings = settingsBloc.currentSettings; // Check if we have PinePods settings setState(() { _isLoggedIn = false; _connectedServer = null; // We'll add these properties to AppSettings in the next step if (settings.pinepodsServer != null && settings.pinepodsServer!.isNotEmpty && settings.pinepodsApiKey != null && settings.pinepodsApiKey!.isNotEmpty) { _isLoggedIn = true; _connectedServer = settings.pinepodsServer; } }); } Future _connectToPinepods() async { if (!_showMfaField && (_serverController.text.isEmpty || _usernameController.text.isEmpty || _passwordController.text.isEmpty)) { setState(() { _errorMessage = 'Please fill in all fields'; }); return; } if (_showMfaField && _mfaController.text.isEmpty) { setState(() { _errorMessage = 'Please enter your MFA code'; }); return; } setState(() { _isLoading = true; _errorMessage = ''; }); try { if (_showMfaField && _tempMfaSessionToken != null) { // Complete MFA login flow final mfaCode = _mfaController.text.trim(); final result = await PinepodsLoginService.completeMfaLogin( serverUrl: _tempServerUrl!, username: _tempUsername!, mfaSessionToken: _tempMfaSessionToken!, mfaCode: mfaCode, ); if (result.isSuccess) { // Save the connection details including user ID var settingsBloc = Provider.of(context, listen: false); settingsBloc.setPinepodsServer(result.serverUrl!); settingsBloc.setPinepodsApiKey(result.apiKey!); settingsBloc.setPinepodsUserId(result.userId!); setState(() { _isLoggedIn = true; _connectedServer = _tempServerUrl; _showMfaField = false; _tempServerUrl = null; _tempUsername = null; _tempUserId = null; _tempMfaSessionToken = null; _isLoading = false; }); } else { setState(() { _errorMessage = result.errorMessage ?? 'MFA verification failed'; _isLoading = false; }); } } else { // Initial login flow final serverUrl = _serverController.text.trim(); final username = _usernameController.text.trim(); final password = _passwordController.text; final result = await PinepodsLoginService.login( serverUrl, username, password, ); if (result.isSuccess) { // Save the connection details including user ID var settingsBloc = Provider.of(context, listen: false); settingsBloc.setPinepodsServer(result.serverUrl!); settingsBloc.setPinepodsApiKey(result.apiKey!); settingsBloc.setPinepodsUserId(result.userId!); setState(() { _isLoggedIn = true; _connectedServer = serverUrl; _isLoading = false; }); } else if (result.requiresMfa) { // Store MFA session info and show MFA field setState(() { _tempServerUrl = result.serverUrl; _tempUsername = result.username; _tempUserId = result.userId; _tempMfaSessionToken = result.mfaSessionToken; _showMfaField = true; _isLoading = false; _errorMessage = 'Please enter your MFA code'; }); } else { setState(() { _errorMessage = result.errorMessage ?? 'Login failed'; _isLoading = false; }); } } } catch (e) { setState(() { _errorMessage = 'Error: ${e.toString()}'; _isLoading = false; }); } } void _resetMfa() { setState(() { _showMfaField = false; _tempServerUrl = null; _tempUsername = null; _tempUserId = null; _tempMfaSessionToken = null; _mfaController.clear(); _errorMessage = ''; }); } void _logOut() async { var settingsBloc = Provider.of(context, listen: false); // Clear all PinePods user data settingsBloc.setPinepodsServer(null); settingsBloc.setPinepodsApiKey(null); settingsBloc.setPinepodsUserId(null); settingsBloc.setPinepodsUsername(null); settingsBloc.setPinepodsEmail(null); setState(() { _isLoggedIn = false; _connectedServer = null; }); // Wait for the settings to be processed and then restart the app WidgetsBinding.instance.addPostFrameCallback((_) async { await Future.delayed(const Duration(milliseconds: 100)); if (mounted) { // Restart the entire app to reset all state RestartWidget.restartApp(context); } }); } @override Widget build(BuildContext context) { // Add a divider label for the PinePods section return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SettingsDividerLabel(label: 'PinePods Server'), const Divider(), if (_isLoggedIn) ...[ // Show connected status ListTile( title: const Text('PinePods Connection'), subtitle: Text(_connectedServer ?? ''), trailing: TextButton( onPressed: _logOut, child: const Text('Log Out'), ), ), ] else ...[ // Show login form Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: _serverController, decoration: const InputDecoration( labelText: 'Server URL', hintText: 'https://your-pinepods-server.com', ), ), const SizedBox(height: 16), TextField( controller: _usernameController, decoration: const InputDecoration( labelText: 'Username', ), ), const SizedBox(height: 16), TextField( controller: _passwordController, decoration: const InputDecoration( labelText: 'Password', ), obscureText: true, enabled: !_showMfaField, ), // MFA Field (shown when MFA is required) if (_showMfaField) ...[ const SizedBox(height: 16), TextField( controller: _mfaController, decoration: InputDecoration( labelText: 'MFA Code', hintText: 'Enter 6-digit code', suffixIcon: IconButton( icon: const Icon(Icons.close), onPressed: _resetMfa, tooltip: 'Cancel MFA', ), ), keyboardType: TextInputType.number, maxLength: 6, ), ], if (_errorMessage.isNotEmpty) ...[ const SizedBox(height: 16), Text( _errorMessage, style: TextStyle(color: Theme.of(context).colorScheme.error), ), ], const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isLoading ? null : _connectToPinepods, child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, ), ) : Text(_showMfaField ? 'Verify MFA Code' : 'Connect'), ), ), ], ), ), ], ], ); } @override void dispose() { _serverController.dispose(); _usernameController.dispose(); _passwordController.dispose(); _mfaController.dispose(); super.dispose(); } }