import 'dart:ui'; import 'package:chepuhagram/presentation/screens/contacts_screen.dart'; import 'package:chepuhagram/presentation/screens/account_setup_screen.dart'; import 'package:chepuhagram/presentation/screens/key_recovery_screen.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../logic/auth_provider.dart'; import '/core/theme_manager.dart'; import 'dart:io'; // Import for File class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); State createState() => _LoginScreenState(); } class _LoginScreenState extends State with SingleTickerProviderStateMixin { final _formKey = GlobalKey(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _totpController = TextEditingController(); bool _showTotpField = false; String? _errorMessage; late AnimationController _animationController; late Animation _fadeAnimation; late Animation _slideAnimation; void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 900), ); _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeIn, )); _slideAnimation = Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( CurvedAnimation( parent: _animationController, curve: Curves.fastOutSlowIn, )); _animationController.forward(); } Widget build(BuildContext context) { final authProvider = context.watch(); final themeProv = context.watch(); final colorScheme = Theme.of(context).colorScheme; return Scaffold( body: Stack( children: [ // Background Wallpaper if (themeProv.wallpaperPath != null) Container( decoration: BoxDecoration( image: DecorationImage( image: FileImage(File(themeProv.wallpaperPath!)), fit: BoxFit.cover, ), ), ), // Blur Overlay if (themeProv.wallpaperPath != null) BackdropFilter( filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: Container( color: Colors.black.withOpacity(0.1), ), ), Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SlideTransition( position: _slideAnimation, child: FadeTransition( opacity: _fadeAnimation, child: Column( children: [ Icon( Icons.messenger_outline, size: 80, color: colorScheme.primary, ), const SizedBox(height: 16), Text( "Чепухаграм", textAlign: TextAlign.center, style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: colorScheme.primary, ), ), ], ), ), ), const SizedBox(height: 48), // Поле Логин _buildTextField( controller: _usernameController, label: "Логин", icon: Icons.person_outline, validator: (value) => value!.isEmpty ? "Введите логин" : null, ), const SizedBox(height: 16), // Поле Пароль _buildTextField( controller: _passwordController, label: "Пароль", icon: Icons.lock_outline, obscureText: true, validator: (value) => value!.length < 6 ? "Минимум 6 символов" : null, ), const SizedBox(height: 16), // Поле TOTP, если требуется if (_showTotpField) _buildTextField( controller: _totpController, label: "TOTP код", icon: Icons.security, validator: (value) => value!.isEmpty ? "Введите TOTP код" : null, ), if (_showTotpField) const SizedBox(height: 16), // Сообщение об ошибке if (_errorMessage != null) Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10), decoration: BoxDecoration( color: colorScheme.errorContainer.withOpacity(0.3), borderRadius: BorderRadius.circular(12), ), child: Text( _errorMessage!, style: TextStyle( color: colorScheme.onErrorContainer, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ), if (_errorMessage != null) const SizedBox(height: 16), const SizedBox(height: 24), // Кнопка Входа ElevatedButton( onPressed: authProvider.isLoading ? null : _submit, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 18), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), backgroundColor: colorScheme.primary, foregroundColor: colorScheme.onPrimary, ), child: authProvider.isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), ) : const Text("Войти", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), ), ], ), ), ), ), ], ), ); } Widget _buildTextField({ required TextEditingController controller, required String label, required IconData icon, bool obscureText = false, String? Function(String?)? validator, }) { final colorScheme = Theme.of(context).colorScheme; return ClipRRect( borderRadius: BorderRadius.circular(16), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( decoration: BoxDecoration( color: colorScheme.surfaceVariant.withOpacity(0.35), borderRadius: BorderRadius.circular(16), border: Border.all( color: colorScheme.outlineVariant.withOpacity(0.15), width: 1, ), ), child: TextFormField( controller: controller, obscureText: obscureText, validator: validator, decoration: InputDecoration( labelText: label, labelStyle: TextStyle(color: colorScheme.outline), prefixIcon: Icon(icon, color: colorScheme.outline), border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), ), ), ), ), ); } void _submit() async { // Сначала убираем фокус с полей, чтобы клавиатура скрылась FocusScope.of(context).unfocus(); await Future.delayed(const Duration(milliseconds: 200)); if (!_formKey.currentState!.validate()) return; try { final authProvider = context.read(); final success = await authProvider.login( _usernameController.text, _passwordController.text, totpCode: _showTotpField ? _totpController.text : null, ); if (success && mounted) { await authProvider.initRealtime(); // Определяем путь пользователя после входа if (authProvider.needsSetup) { // Путь А: Первичная настройка Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const AccountSetupScreen()), ); } else if (authProvider.needsKeyRecovery) { // Путь В: Восстановление ключей Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const KeyRecoveryScreen()), ); } else { // Путь Б: Нормальный вход Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const ContactsScreen()), ); } } } catch (e) { final error = e.toString().replaceAll('Exception: ', ''); if (mounted) { setState(() { _errorMessage = error; if (error.contains('TOTP код требуется')) { _showTotpField = true; } }); } } } void dispose() { _usernameController.dispose(); _passwordController.dispose(); _totpController.dispose(); _animationController.dispose(); super.dispose(); } }