using EchoHub.Core.Constants; using EchoHub.Core.DTOs; using EchoHub.Core.Models; using Microsoft.AspNetCore.SignalR.Client; namespace EchoHub.Client.Services; public sealed class EchoHubConnection : IAsyncDisposable { private readonly HubConnection _connection; public event Action? OnMessageReceived; public event Action? OnUserJoined; public event Action? OnUserLeft; public event Action? OnChannelUpdated; public event Action? OnUserStatusChanged; public event Action? OnUserKicked; public event Action? OnUserBanned; public event Action? OnMessageDeleted; public event Action? OnChannelNuked; public event Action? OnForceDisconnect; public event Action? OnError; public event Action? OnConnectionStateChanged; public event Action? OnReconnected; public bool IsConnected => _connection.State == HubConnectionState.Connected; public EchoHubConnection(string serverUrl, ApiClient apiClient) { var hubUrl = serverUrl.TrimEnd('/') + HubConstants.ChatHubPath; _connection = new HubConnectionBuilder() .WithUrl(hubUrl, options => { options.AccessTokenProvider = () => apiClient.GetValidTokenAsync(); }) .WithAutomaticReconnect() .Build(); RegisterHandlers(); _connection.Reconnecting += _ => { OnConnectionStateChanged?.Invoke("Reconnecting..."); return Task.CompletedTask; }; _connection.Reconnected += _ => { OnConnectionStateChanged?.Invoke("Connected"); OnReconnected?.Invoke(); return Task.CompletedTask; }; _connection.Closed += _ => { OnConnectionStateChanged?.Invoke("Disconnected"); return Task.CompletedTask; }; } private void RegisterHandlers() { _connection.On(nameof(Core.Contracts.IEchoHubClient.ReceiveMessage), message => { OnMessageReceived?.Invoke(message); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.UserJoined), (channelName, username) => { OnUserJoined?.Invoke(channelName, username); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.UserLeft), (channelName, username) => { OnUserLeft?.Invoke(channelName, username); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.ChannelUpdated), channel => { OnChannelUpdated?.Invoke(channel); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.UserStatusChanged), presence => { OnUserStatusChanged?.Invoke(presence); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.UserKicked), (channelName, username, reason) => { OnUserKicked?.Invoke(channelName, username, reason); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.UserBanned), (username, reason) => { OnUserBanned?.Invoke(username, reason); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.MessageDeleted), (channelName, messageId) => { OnMessageDeleted?.Invoke(channelName, messageId); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.ChannelNuked), channelName => { OnChannelNuked?.Invoke(channelName); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.ForceDisconnect), reason => { OnForceDisconnect?.Invoke(reason); }); _connection.On(nameof(Core.Contracts.IEchoHubClient.Error), message => { OnError?.Invoke(message); }); } public async Task ConnectAsync() { OnConnectionStateChanged?.Invoke("Connecting..."); await _connection.StartAsync(); OnConnectionStateChanged?.Invoke("Connected"); } public async Task DisconnectAsync() { await _connection.StopAsync(); OnConnectionStateChanged?.Invoke("Disconnected"); } public async Task> JoinChannelAsync(string channelName) { return await _connection.InvokeAsync>("JoinChannel", channelName); } public async Task LeaveChannelAsync(string channelName) { await _connection.InvokeAsync("LeaveChannel", channelName); } public async Task SendMessageAsync(string channelName, string content) { await _connection.InvokeAsync("SendMessage", channelName, content); } public async Task> GetHistoryAsync(string channelName, int count = HubConstants.DefaultHistoryCount) { return await _connection.InvokeAsync>("GetChannelHistory", channelName, count); } public async Task UpdateStatusAsync(UserStatus status, string? statusMessage = null) { await _connection.InvokeAsync("UpdateStatus", status, statusMessage); } public async Task> GetOnlineUsersAsync(string channelName) { return await _connection.InvokeAsync>("GetOnlineUsers", channelName); } public async ValueTask DisposeAsync() { await _connection.DisposeAsync(); } }