using System; using System.Linq; using System.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using Vanguard.Api; using Vanguard.App; using Vanguard.Auth; namespace Vanguard.UI { /// Экран входа/регистрации и выбора адреса сервера. public sealed class LoginView { private readonly AppConfig _config; private readonly AuthService _auth; private readonly Action _onAuthenticated; private readonly RectTransform _root; private readonly InputField _serverUrl; private readonly InputField _userName; private readonly InputField _password; private readonly Button _loginButton; private readonly Button _registerButton; private readonly Text _status; private bool _busy; public LoginView( Transform parent, AppConfig config, AuthService auth, Action onAuthenticated) { _config = config; _auth = auth; _onAuthenticated = onAuthenticated; _root = UiKit.CreatePanel(parent, "LoginScreen", new Color(0.035f, 0.045f, 0.065f, 1f)); var card = UiKit.CreateCenteredCard( _root, "LoginCard", new Vector2(640f, 790f), new Color(0.07f, 0.085f, 0.115f, 1f)); UiKit.AddVerticalLayout(card, 36, 10); var title = UiKit.CreateText( card, "Title", "VANGUARD", 54, new Color(0.86f, 0.90f, 1f)); title.fontStyle = FontStyle.Bold; UiKit.SetPreferredHeight(title, 64f); var subtitle = UiKit.CreateText( card, "Subtitle", "ReMaster · сетевой клиент", 20, new Color(0.62f, 0.68f, 0.82f)); UiKit.SetPreferredHeight(subtitle, 34f); AddCaption(card, "Сервер"); _serverUrl = UiKit.CreateInput(card, "ServerUrl", "http://192.168.31.158:18080"); _serverUrl.text = _config.ServerBaseUrl; UiKit.SetPreferredHeight(_serverUrl, 54f); AddCaption(card, "Имя игрока"); _userName = UiKit.CreateInput(card, "UserName", "3–32 символа"); UiKit.SetPreferredHeight(_userName, 54f); AddCaption(card, "Пароль"); _password = UiKit.CreateInput(card, "Password", "8–128 символов", password: true); UiKit.SetPreferredHeight(_password, 54f); _loginButton = UiKit.CreateButton(card, "LoginButton", "Войти", OnLoginClicked); UiKit.SetPreferredHeight(_loginButton, 56f); _registerButton = UiKit.CreateButton(card, "RegisterButton", "Создать аккаунт", OnRegisterClicked, 21); UiKit.SetPreferredHeight(_registerButton, 50f); _status = UiKit.CreateText( card, "Status", string.Empty, 18, new Color(0.70f, 0.75f, 0.86f), TextAnchor.UpperCenter); _status.horizontalOverflow = HorizontalWrapMode.Wrap; UiKit.SetPreferredHeight(_status, 72f); var footer = UiKit.CreateText( card, "Footer", "Стенд: 192.168.31.158:18080 · локально: localhost:5080", 15, new Color(0.45f, 0.50f, 0.62f)); UiKit.SetPreferredHeight(footer, 26f); } public void Show(string status = null) { UiKit.SetActive(_root, true); if (!string.IsNullOrEmpty(status)) SetStatus(status, new Color(0.70f, 0.75f, 0.86f)); } public void Hide() => UiKit.SetActive(_root, false); private static void AddCaption(Transform parent, string text) { var caption = UiKit.CreateText( parent, "Caption_" + text, text, 17, new Color(0.62f, 0.68f, 0.82f), TextAnchor.MiddleLeft); UiKit.SetPreferredHeight(caption, 24f); } private async void OnLoginClicked() { await SubmitAsync(register: false); } private async void OnRegisterClicked() { await SubmitAsync(register: true); } private async Task SubmitAsync(bool register) { if (_busy) return; var userName = (_userName.text ?? string.Empty).Trim(); var password = _password.text ?? string.Empty; if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) { SetStatus("Введите имя игрока и пароль.", new Color(1f, 0.70f, 0.35f)); return; } _config.SetServerBaseUrl(_serverUrl.text); _serverUrl.text = _config.ServerBaseUrl; SetBusy(true, register ? "Создаём аккаунт…" : "Подключаемся…"); try { if (register) await _auth.RegisterAsync(userName, password); else await _auth.LoginAsync(userName, password); _password.text = string.Empty; SetStatus("Готово.", new Color(0.45f, 0.85f, 0.55f)); _onAuthenticated(); } catch (ApiException exception) { SetStatus(FormatError(exception), new Color(1f, 0.55f, 0.45f)); } catch (Exception exception) { Debug.LogException(exception); SetStatus("Не удалось связаться с сервером. Проверьте адрес.", new Color(1f, 0.55f, 0.45f)); } finally { SetBusy(false, null); } } private static string FormatError(ApiException exception) { if (exception.StatusCode == 0) return "Нет связи с сервером. Проверьте адрес и запущен ли сервер."; if (exception.Violations.Count > 0) { var details = string.Join("\n", exception.Violations.Take(3).Select(item => item.Message)); return exception.Message + "\n" + details; } return string.IsNullOrEmpty(exception.Message) ? "Сервер вернул ошибку " + exception.StatusCode + "." : exception.Message; } private void SetBusy(bool busy, string status) { _busy = busy; _loginButton.interactable = !busy; _registerButton.interactable = !busy; _serverUrl.interactable = !busy; _userName.interactable = !busy; _password.interactable = !busy; if (status != null) SetStatus(status, new Color(0.70f, 0.75f, 0.86f)); } private void SetStatus(string text, Color color) { _status.text = text ?? string.Empty; _status.color = color; } } }