using System.Collections.ObjectModel; using CORP_MAUI.Models; using CORP_MAUI.Services; namespace CORP_MAUI.Pages; public partial class ChatPage : ContentPage { private readonly ObservableCollection _messages; private User _currentUser = new(); private User _recipient; private System.Timers.Timer _typingTimer = new(); public ChatPage(User recipient) { InitializeComponent(); _recipient = recipient; _messages = []; MessagesCollectionView.ItemsSource = _messages; // Устанавливаем аватар получателя RecipientAvatar.Source = _recipient.DisplayAvatarSource; // Подписываемся на события WebSocket ServiceProvider.WebSocketService.MessageReceived += OnMessageReceived; ServiceProvider.WebSocketService.MessageHistoryReceived += OnMessageHistoryReceived; ServiceProvider.WebSocketService.ConnectionStatusChanged += OnConnectionStatusChanged; // Инициализация _ = InitializeAsync(); } private async Task InitializeAsync() { try { // Загружаем аватар получателя если еще не загружен if (!_recipient.IsAvatarLoaded) { await ServiceProvider.ApiService.LoadUserAvatarAsync(_recipient); RecipientAvatar.Source = _recipient.DisplayAvatarSource; } // Получаем текущего пользователя User? user = await ServiceProvider.ApiService.GetCurrentUserAsync(); if (user != null) { _currentUser = user; } // Отображаем информацию о получателе RecipientName.Text = _recipient.FullName; RecipientStatus.Text = _recipient.IsOnline ? "В сети" : "Не в сети"; // Запрашиваем историю сообщений через WebSocket if (ServiceProvider.WebSocketService.IsConnected) { await ServiceProvider.WebSocketService.RequestMessageHistoryAsync(_recipient.Id); } else { await DisplayAlert("Предупреждение", "WebSocket не подключен. История сообщений недоступна.", "OK"); } } catch (Exception ex) { await DisplayAlert("Ошибка", $"Не удалось загрузить данные: {ex.Message}", "OK"); } } private async void OnSendMessageClicked(object sender, EventArgs e) { string? content = MessageEntry.Text?.Trim(); if (string.IsNullOrEmpty(content)) return; try { Message message = new Message { SenderId = _currentUser.Id, SenderName = _currentUser.FullName ?? _currentUser.Username, ReceiverId = _recipient.Id, ReceiverName = _recipient.FullName ?? _recipient.Username, Content = content, Timestamp = DateTime.Now, Type = MessageType.Personal }; // Отправляем только через WebSocket if (!ServiceProvider.WebSocketService.IsConnected) { await DisplayAlert("Ошибка", "WebSocket не подключен. Невозможно отправить сообщение.", "OK"); return; } await ServiceProvider.WebSocketService.SendMessageAsync(message); // Добавляем сообщение в список _messages.Add(new MessageViewModel { SenderId = message.SenderId, SenderName = message.SenderName, Content = message.Content, Timestamp = message.Timestamp, IsMyMessage = true }); // Очищаем поле ввода MessageEntry.Text = string.Empty; // Прокручиваем к последнему сообщению MessagesCollectionView.ScrollTo(_messages.Last(), position: ScrollToPosition.End, animate: true); } catch (Exception ex) { await DisplayAlert("Ошибка", $"Не удалось отправить сообщение: {ex.Message}", "OK"); } } private void OnMessageTextChanged(object sender, TextChangedEventArgs e) { // Отправляем индикатор набора текста if (!string.IsNullOrEmpty(e.NewTextValue) && ServiceProvider.WebSocketService.IsConnected) { _ = ServiceProvider.WebSocketService.SendTypingIndicatorAsync(_recipient.Id, true); // Отменяем предыдущий таймер _typingTimer?.Stop(); _typingTimer?.Dispose(); // Создаем новый таймер для отправки статуса "не печатает" _typingTimer = new System.Timers.Timer(2000); _typingTimer.Elapsed += async (s, args) => { await ServiceProvider.WebSocketService.SendTypingIndicatorAsync(_recipient.Id, false); _typingTimer?.Dispose(); }; _typingTimer.Start(); } } private void OnMessageReceived(object? sender, Message message) { // Проверяем, что сообщение от нашего собеседника if (message.SenderId == _recipient.Id) { MainThread.BeginInvokeOnMainThread(() => { _messages.Add(new MessageViewModel { Id = message.Id, SenderId = message.SenderId, SenderName = message.SenderName, Content = message.Content, Timestamp = message.Timestamp, IsMyMessage = false }); MessagesCollectionView.ScrollTo(_messages.Last(), position: ScrollToPosition.End, animate: true); }); } } private void OnMessageHistoryReceived(object? sender, List history) { MainThread.BeginInvokeOnMainThread(() => { _messages.Clear(); foreach (Message message in history.OrderBy(m => m.Timestamp)) { _messages.Add(new MessageViewModel { Id = message.Id, SenderId = message.SenderId, SenderName = message.SenderName, Content = message.Content, Timestamp = message.Timestamp, IsMyMessage = message.SenderId == _currentUser?.Id }); } // Прокручиваем к последнему сообщению if (_messages.Any()) { MessagesCollectionView.ScrollTo(_messages.Last(), position: ScrollToPosition.End, animate: false); } }); } private void OnConnectionStatusChanged(object? sender, bool isConnected) { MainThread.BeginInvokeOnMainThread(() => { ConnectionIndicator.IsVisible = !isConnected; }); } private async void OnBackButtonClicked(object sender, EventArgs e) { // Возвращаемся к списку сотрудников (предыдущая страница) await Navigation.PopAsync(); } protected override void OnDisappearing() { base.OnDisappearing(); // Отписываемся от событий ServiceProvider.WebSocketService.MessageReceived -= OnMessageReceived; ServiceProvider.WebSocketService.MessageHistoryReceived -= OnMessageHistoryReceived; ServiceProvider.WebSocketService.ConnectionStatusChanged -= OnConnectionStatusChanged; _typingTimer?.Dispose(); } } public class MessageViewModel { public int Id { get; set; } public int SenderId { get; set; } public string SenderName { get; set; } = string.Empty; public string Content { get; set; } = string.Empty; public DateTime Timestamp { get; set; } public bool IsMyMessage { get; set; } }