Skip to content
Snippets Groups Projects
Commit 8d1f9959 authored by esantangelo's avatar esantangelo
Browse files

commit

parent 30e87cd9
No related branches found
No related tags found
3 merge requests!20merge from develop,!18Feature/chat signalr,!16Feature/chat signalr
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Dtos;
using Tsi1.BusinessLayer.Interfaces;
using Tsi1.DataLayer;
using Tsi1.DataLayer.Entities;
namespace Tsi1.Api.SignalR
{
public class ChatHub : Hub
{
private readonly IMapper _mapper;
private readonly IHubContext<PresenceHub> _presenceHub;
private readonly PresenceTracker _tracker;
private readonly IMessageService _messageService;
private readonly IChatService _chatService;
public ChatHub(IMapper mapper, IHubContext<PresenceHub> presenceHub,
PresenceTracker tracker, IMessageService messageService, IChatService chatService)
{
_tracker = tracker;
_presenceHub = presenceHub;
_mapper = mapper;
_messageService = messageService;
_chatService = chatService
}
public override async Task OnConnectedAsync()
{
var httpContext = Context.GetHttpContext();
//var otherUser = httpContext.Request.Query["user"].ToString();
var otherUserId = int.Parse(httpContext.Request.Query["id"].ToString());
//var userName = Context.User.FindFirst("Username").Value;
var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var tenantId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
var groupName = GetGroupName(userId, otherUserId);
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await AddToGroup(groupName);
var messages = await _messageService.GetMessages(userId, otherUserId, tenantId);
await Clients.Caller.SendAsync("ReceiveMessages", messages);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await RemoveFromGroup();
await base.OnDisconnectedAsync(exception);
}
public async Task SendMessage(MessageCreateDto newMessage)
{
var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var tenantId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value);
if (userId == newMessage.ReceiverId)
throw new HubException("You cannot send messages to yourself");
var groupName = GetGroupName(userId, newMessage.ReceiverId);
var group = await _chatService.GetGroupByName(groupName);
if (group == null)
{
throw new HubException($"No existe el grupo {groupName}");
}
if (!group.Connections.Any(x => x.UserId == newMessage.ReceiverId))
{
var connections = await _tracker.GetConnectionsForUser(newMessage.ReceiverId);
if (connections != null)
{
await _presenceHub.Clients.Clients(connections).SendAsync("NewMessageReceived", userId);
}
}
newMessage.SenderId = userId;
var messageDto = await _messageService.Send(newMessage);
await Clients.Group(groupName).SendAsync("NewMessage", messageDto);
}
private async Task<Group> AddToGroup(string groupName)
{
var group = await _chatService.GetGroupByName(groupName);
if (group == null)
{
throw new HubException($"No existe el grupo {groupName}");
}
var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var connection = new Connection
{
ConnectionId = Context.ConnectionId,
UserId = userId,
};
if (group == null)
{
group = new Group
{
Name = groupName,
};
}
group.Connections.Add(connection);
var result = await _chatService.CreateGroup(group);
if (result.HasError)
{
throw new HubException($"Error al crear el grupo {group}");
}
return group;
}
private async Task<bool> RemoveFromGroup()
{
await _chatService.RemoveConnectionFromGroup(Context.ConnectionId);
return true;
}
private string GetGroupName(int callerId, int otherId)
{
var compare = callerId < otherId;
return compare ? $"{callerId}-{otherId}" : $"{otherId}-{callerId}";
}
}
}
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Tsi1.Api.SignalR
{
public class PresenceHub : Hub
{
private readonly PresenceTracker _tracker;
public PresenceHub(PresenceTracker tracker)
{
_tracker = tracker;
}
public override async Task OnConnectedAsync()
{
var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var isOnline = await _tracker.UserConnected(userId, Context.ConnectionId);
if (isOnline)
await Clients.Others.SendAsync("UserIsOnline", userId);
var currentUsers = await _tracker.GetOnlineUsers();
await Clients.Caller.SendAsync("GetOnlineUsers", currentUsers);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var userId = int.Parse(Context.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
var isOffline = await _tracker.UserDisconnected(userId, Context.ConnectionId);
if (isOffline)
await Clients.Others.SendAsync("UserIsOffline", userId);
await base.OnDisconnectedAsync(exception);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Tsi1.Api.SignalR
{
public class PresenceTracker
{
private static readonly Dictionary<int, List<string>> OnlineUsers =
new Dictionary<int, List<string>>();
public Task<bool> UserConnected(int userId, string connectionId)
{
bool isOnline = false;
lock (OnlineUsers)
{
if (OnlineUsers.ContainsKey(userId))
{
OnlineUsers[userId].Add(connectionId);
}
else
{
OnlineUsers.Add(userId, new List<string> { connectionId });
isOnline = true;
}
}
return Task.FromResult(isOnline);
}
public Task<bool> UserDisconnected(int userId, string connectionId)
{
bool isOffline = false;
lock (OnlineUsers)
{
if (!OnlineUsers.ContainsKey(userId)) return Task.FromResult(isOffline);
OnlineUsers[userId].Remove(connectionId);
if (OnlineUsers[userId].Count == 0)
{
OnlineUsers.Remove(userId);
isOffline = true;
}
}
return Task.FromResult(isOffline);
}
public Task<int[]> GetOnlineUsers()
{
int[] onlineUsers;
lock (OnlineUsers)
{
onlineUsers = OnlineUsers.OrderBy(k => k.Key).Select(k => k.Key).ToArray();
}
return Task.FromResult(onlineUsers);
}
public Task<List<string>> GetConnectionsForUser(int userId)
{
List<string> connectionIds;
lock (OnlineUsers)
{
connectionIds = OnlineUsers.GetValueOrDefault(userId);
}
return Task.FromResult(connectionIds);
}
}
}
...@@ -176,6 +176,8 @@ namespace Tsi1.Api ...@@ -176,6 +176,8 @@ namespace Tsi1.Api
{ {
endpoints.MapControllers(); endpoints.MapControllers();
endpoints.MapHub<VideoHub>("hubs/video"); endpoints.MapHub<VideoHub>("hubs/video");
endpoints.MapHub<PresenceHub>("hubs/presence");
endpoints.MapHub<ChatHub>("hubs/chat");
}); });
} }
} }
......
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.DataLayer.Entities;
namespace Tsi1.BusinessLayer.Interfaces
{
public interface IChatService
{
Task<Group> GetGroupByName(string groupName);
Task<ServiceResult<Group>> CreateGroup(Group group);
Task<bool> RemoveConnectionFromGroup(string connectionId);
}
}
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Tsi1.BusinessLayer.Helpers;
using Tsi1.BusinessLayer.Interfaces;
using Tsi1.DataLayer;
using Tsi1.DataLayer.Entities;
namespace Tsi1.BusinessLayer.Services
{
public class ChatService : IChatService
{
private readonly Tsi1Context _tsi1Context;
public ChatService(Tsi1Context tsi1Context)
{
_tsi1Context = tsi1Context;
}
public async Task<ServiceResult<Group>> CreateGroup(Group group)
{
var result = new ServiceResult<Group>();
_tsi1Context.Groups.Add(group);
try
{
await _tsi1Context.SaveChangesAsync();
}
catch (Exception)
{
result.HasError = true;
}
return result;
}
//public async Task<ServiceResult<Group>> GetGroupByName(string groupName)
public async Task<Group> GetGroupByName(string groupName)
{
var result = new ServiceResult<Group>();
var group = await _tsi1Context.Groups.FirstOrDefaultAsync(x => x.Name == groupName);
return group;
}
public async Task<bool> RemoveConnectionFromGroup(string connectionId)
{
var connection = await _tsi1Context.Connections.FirstOrDefaultAsync(x => x.ConnectionId == connectionId);
if (connection != null)
{
connection.Group = null;
await _tsi1Context.SaveChangesAsync();
}
return true;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.DataLayer.Entities
{
public class Connection
{
public string ConnectionId { get; set; }
public int UserId { get; set; }
public Group Group { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Tsi1.DataLayer.Entities
{
public class Group
{
public Group()
{
Connections = new List<Connection>();
}
public string Name { get; set; }
public ICollection<Connection> Connections { get; set; }
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Text;
using Tsi1.DataLayer.Entities;
namespace Tsi1.DataLayer.EntityConfiguration
{
public class ConnectionConfiguration : IEntityTypeConfiguration<Connection>
{
public void Configure(EntityTypeBuilder<Connection> builder)
{
builder.HasKey(x => x.ConnectionId);
builder.Property(x => x.UserId)
.IsRequired();
builder.HasOne(x => x.Group)
.WithMany(x => x.Connections);
}
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Text;
using Tsi1.DataLayer.Entities;
namespace Tsi1.DataLayer.EntityConfiguration
{
public class GroupConfiguration : IEntityTypeConfiguration<Group>
{
public void Configure(EntityTypeBuilder<Group> builder)
{
builder.HasKey(x => x.Name);
}
}
}
...@@ -21,6 +21,8 @@ namespace Tsi1.DataLayer ...@@ -21,6 +21,8 @@ namespace Tsi1.DataLayer
public DbSet<PostMessage> PostMessages { get; set; } public DbSet<PostMessage> PostMessages { get; set; }
public DbSet<Tenant> Tenants { get; set; } public DbSet<Tenant> Tenants { get; set; }
public DbSet<ForumUser> ForumUsers { get; set; } public DbSet<ForumUser> ForumUsers { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Connection> Connections { get; set; }
public Tsi1Context(DbContextOptions options) : base(options) { } public Tsi1Context(DbContextOptions options) : base(options) { }
...@@ -39,6 +41,8 @@ namespace Tsi1.DataLayer ...@@ -39,6 +41,8 @@ namespace Tsi1.DataLayer
modelBuilder.ApplyConfiguration(new PostMessageConfiguration()); modelBuilder.ApplyConfiguration(new PostMessageConfiguration());
modelBuilder.ApplyConfiguration(new TenantConfiguration()); modelBuilder.ApplyConfiguration(new TenantConfiguration());
modelBuilder.ApplyConfiguration(new ForumUserConfiguration()); modelBuilder.ApplyConfiguration(new ForumUserConfiguration());
modelBuilder.ApplyConfiguration(new GroupConfiguration());
modelBuilder.ApplyConfiguration(new ConnectionConfiguration());
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment