import 'dart:async'; import 'package:flutter/material.dart'; import 'package:moonwell_launcher/app/design_system/mw_authentication.dart'; import 'package:moonwell_launcher/app/design_system/mw_shells.dart'; import 'package:moonwell_launcher/app/home_screen/home_screen.dart'; import 'package:moonwell_launcher/features/launcher/data/launcher_api_client.dart'; import 'package:moonwell_launcher/features/preferences/domain/repositories/preferences_repository.dart'; import 'package:moonwell_launcher/service_container.dart'; import 'package:window_manager/window_manager.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _usernameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _passwordConfirmationController = TextEditingController(); final _inviteCodeController = TextEditingController(); MwAuthenticationMode _mode = MwAuthenticationMode.login; bool _termsAccepted = false; bool _isLoading = false; String? _error; String? _success; @override void dispose() { _usernameController.dispose(); _emailController.dispose(); _passwordController.dispose(); _passwordConfirmationController.dispose(); _inviteCodeController.dispose(); super.dispose(); } Future _submit() { return _mode == MwAuthenticationMode.login ? _login() : _register(); } Future _login() async { final username = _usernameController.text.trim(); final password = _passwordController.text.trim(); if (username.isEmpty || password.isEmpty) { setState(() => _error = 'Введите логин и пароль.'); return; } setState(() { _isLoading = true; _error = null; _success = null; }); try { final api = getIt(); final preferencesRepository = getIt(); final session = await api.login(username: username, password: password); final manifest = await api.fetchManifest(session.accessToken); await preferencesRepository.setLauncherSession(session); if (!mounted) return; await Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (_, _, _) => HomeScreen(session: session, manifest: manifest), transitionsBuilder: (_, animation, _, child) { return FadeTransition(opacity: animation, child: child); }, transitionDuration: const Duration(milliseconds: 400), ), ); } catch (e) { if (!mounted) return; setState(() { _isLoading = false; _error = _formatError(e); }); } } Future _register() async { final username = _usernameController.text.trim(); final email = _emailController.text.trim(); final password = _passwordController.text; final passwordConfirmation = _passwordConfirmationController.text; final validationError = _validateRegistration( username: username, email: email, password: password, passwordConfirmation: passwordConfirmation, ); if (validationError != null) { setState(() => _error = validationError); return; } setState(() { _isLoading = true; _error = null; _success = null; }); try { final message = await getIt().register( username: username, email: email, password: password, passwordConfirmation: passwordConfirmation, terms: _termsAccepted, inviteCode: _inviteCodeController.text, ); if (!mounted) return; setState(() { _mode = MwAuthenticationMode.login; _isLoading = false; _termsAccepted = false; _passwordConfirmationController.clear(); _inviteCodeController.clear(); _success = message; }); } catch (error) { if (!mounted) return; setState(() { _isLoading = false; _error = _formatError(error); }); } } String? _validateRegistration({ required String username, required String email, required String password, required String passwordConfirmation, }) { if (!RegExp(r'^[A-Za-z0-9]{3,32}$').hasMatch(username)) { return 'Логин должен содержать от 3 до 32 латинских букв или цифр.'; } if (!RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$').hasMatch(email)) { return 'Введите корректный email.'; } if (password.length < 8 || password.length > 32) { return 'Пароль должен содержать от 8 до 32 символов.'; } if (password != passwordConfirmation) { return 'Пароли не совпадают.'; } if (!_termsAccepted) { return 'Примите правила сервера и пользовательское соглашение.'; } return null; } void _setMode(MwAuthenticationMode mode) { setState(() { _mode = mode; _error = null; _success = null; }); } String _formatError(Object error) { final raw = error.toString(); if (raw.startsWith('Exception: ')) return raw.substring(11); return raw; } @override Widget build(BuildContext context) { return MwLoginShell( form: MwAuthenticationForm( usernameController: _usernameController, emailController: _emailController, passwordController: _passwordController, passwordConfirmationController: _passwordConfirmationController, inviteCodeController: _inviteCodeController, mode: _mode, onModeChanged: _setMode, termsAccepted: _termsAccepted, onTermsChanged: (value) => setState(() => _termsAccepted = value), loading: _isLoading, error: _error, success: _success, onSubmit: _submit, ), onDrag: windowManager.startDragging, onDoubleTap: () => unawaited(_toggleMaximized()), onMinimize: windowManager.minimize, onMaximizeRestore: () => unawaited(_toggleMaximized()), onClose: () => unawaited(windowManager.destroy()), ); } Future _toggleMaximized() async { if (await windowManager.isMaximized()) { await windowManager.unmaximize(); } else { await windowManager.maximize(); } } }