diff --git a/dotnet/AipsRT/Hubs/WhiteboardHub.cs b/dotnet/AipsRT/Hubs/WhiteboardHub.cs index ec22db5..d6abb50 100644 --- a/dotnet/AipsRT/Hubs/WhiteboardHub.cs +++ b/dotnet/AipsRT/Hubs/WhiteboardHub.cs @@ -1,6 +1,9 @@ -using AipsCore.Application.Abstract.MessageBroking; -using AipsCore.Application.Common.Message.MoveShape; +using AipsCore.Application.Abstract; using AipsCore.Application.Models.Shape.Command.MoveShape; +using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin; +using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin; +using AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus; +using AipsCore.Domain.Models.WhiteboardMembership.Enums; using AipsRT.Model.Whiteboard; using AipsRT.Model.Whiteboard.Shapes; using AipsRT.Model.Whiteboard.Structs; @@ -15,27 +18,95 @@ public class WhiteboardHub : Hub { private readonly WhiteboardManager _whiteboardManager; private readonly IMessagingService _messagingService; + private readonly IDispatcher _dispatcher; - public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService) + public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService, IDispatcher dispatcher) { _whiteboardManager = whiteboardManager; _messagingService = messagingService; + _dispatcher = dispatcher; } public async Task JoinWhiteboard(Guid whiteboardId) { if (!_whiteboardManager.WhiteboardExists(whiteboardId)) + { await _whiteboardManager.AddWhiteboard(whiteboardId); + } await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString()); + + var whiteboard = _whiteboardManager.GetWhiteboard(whiteboardId)!; - var state = _whiteboardManager.GetWhiteboard(whiteboardId)!; + var userId = CurrentUserId; + var ownerId = whiteboard.OwnerId; - _whiteboardManager.AddUserToWhiteboard(Guid.Parse(Context.UserIdentifier!), whiteboardId); + WhiteboardMembershipStatus status; + + if (userId == ownerId) + { + status = WhiteboardMembershipStatus.Accepted; + } + else + { + status = await _dispatcher.Execute(new GetMembershipStatusQuery(whiteboardId.ToString(), userId.ToString())); + } - await Clients.Caller.SendAsync("InitWhiteboard", state); - await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId) - .SendAsync("Joined", Context.UserIdentifier!); + if (status == WhiteboardMembershipStatus.Accepted) + { + _whiteboardManager.AddUserToWhiteboard(userId, whiteboardId); + whiteboard.AcceptUser(userId); + + var state = _whiteboardManager.GetWhiteboard(whiteboardId)!; + await Clients.Caller.SendAsync("InitWhiteboard", state); + + await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId).SendAsync("Joined", Context.UserIdentifier!); + } + else + { + _whiteboardManager.AddPendingUser(userId, whiteboardId); + + await Clients.Caller.SendAsync("WaitingForApproval", userId.ToString()); + + await Clients.User(ownerId.ToString()).SendAsync("UserWaitingForApproval", userId.ToString()); + } + } + + public async Task AcceptUser(Guid targetUserId) + { + var whiteboard = CurrentWhiteboard; + + await _messagingService.AcceptedUser(new AcceptUserRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), targetUserId.ToString())); + + _whiteboardManager.MovePendingToAccepted(targetUserId, whiteboard.WhiteboardId); + + await Clients.User(targetUserId.ToString()).SendAsync("Accepted"); + await Clients.User(targetUserId.ToString()).SendAsync("InitWhiteboard", whiteboard); + } + + public async Task RejectUser(Guid targetUserId) + { + var whiteboard = CurrentWhiteboard; + + await _messagingService.RejectedUser(new RejectUserRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), targetUserId.ToString())); + + _whiteboardManager.RemovePendingUser(targetUserId, whiteboard.WhiteboardId); + + await Clients.User(targetUserId.ToString()).SendAsync("Rejected"); + } + + public async Task CancelJoinRequest() + { + var userId = CurrentUserId; + var whiteboard = _whiteboardManager.GetWhiteboardForUser(userId); + + if (whiteboard != null) + { + _whiteboardManager.RemovePendingUser(userId, whiteboard.WhiteboardId); + + await Clients.User(whiteboard.OwnerId.ToString()) + .SendAsync("UserCanceledJoinRequest", userId.ToString()); + } } public async Task LeaveWhiteboard(Guid whiteboardId) @@ -60,6 +131,8 @@ public class WhiteboardHub : Hub public async Task AddRectangle(Rectangle rectangle) { + if (!_whiteboardManager.IsAccepted(CurrentUserId)) return; + rectangle.OwnerId = CurrentUserId; var whiteboard = CurrentWhiteboard; @@ -72,6 +145,8 @@ public class WhiteboardHub : Hub public async Task AddArrow(Arrow arrow) { + if (!_whiteboardManager.IsAccepted(CurrentUserId)) return; + arrow.OwnerId = CurrentUserId; var whiteboard = CurrentWhiteboard; @@ -84,6 +159,8 @@ public class WhiteboardHub : Hub public async Task AddLine(Line line) { + if (!_whiteboardManager.IsAccepted(CurrentUserId)) return; + line.OwnerId = CurrentUserId; var whiteboard = CurrentWhiteboard; @@ -96,6 +173,8 @@ public class WhiteboardHub : Hub public async Task AddTextShape(TextShape textShape) { + if (!_whiteboardManager.IsAccepted(CurrentUserId)) return; + textShape.OwnerId = CurrentUserId; var whiteboard = CurrentWhiteboard; @@ -108,6 +187,8 @@ public class WhiteboardHub : Hub public async Task MoveShape(MoveShapeCommand moveShape) { + if (!_whiteboardManager.IsAccepted(CurrentUserId)) return; + var whiteboard = CurrentWhiteboard; var shape = whiteboard.Shapes.Find(s => s.Id.ToString() == moveShape.ShapeId); @@ -125,6 +206,8 @@ public class WhiteboardHub : Hub public async Task PlaceShape(MoveShapeCommand moveShape) { + if (!_whiteboardManager.IsAccepted(CurrentUserId)) return; + await MoveShape(moveShape); await _messagingService.MoveShape(moveShape); diff --git a/dotnet/AipsRT/Model/Whiteboard/GetWhiteboardService.cs b/dotnet/AipsRT/Model/Whiteboard/GetWhiteboardService.cs index 1ae70f7..b891c3c 100644 --- a/dotnet/AipsRT/Model/Whiteboard/GetWhiteboardService.cs +++ b/dotnet/AipsRT/Model/Whiteboard/GetWhiteboardService.cs @@ -1,6 +1,8 @@ using AipsCore.Application.Abstract; +using AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus; using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardInfoRT; using AipsCore.Domain.Models.Shape.Enums; +using AipsCore.Domain.Models.WhiteboardMembership.Enums; using AipsRT.Model.Whiteboard.Shapes.Map; namespace AipsRT.Model.Whiteboard; diff --git a/dotnet/AipsRT/Model/Whiteboard/Whiteboard.cs b/dotnet/AipsRT/Model/Whiteboard/Whiteboard.cs index 074a955..2af62cb 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Whiteboard.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Whiteboard.cs @@ -7,6 +7,9 @@ public class Whiteboard public Guid WhiteboardId { get; set; } public Guid OwnerId { get; set; } + + public HashSet AcceptedUsers { get; } = new(); + public HashSet PendingUsers { get; } = new(); public List Shapes { get; } = []; @@ -38,4 +41,16 @@ public class Whiteboard Shapes.Add(shape); TextShapes.Add(shape); } + + public void AddPendingUser(Guid userId) => PendingUsers.Add(userId); + + public void AcceptUser(Guid userId) + { + PendingUsers.Remove(userId); + AcceptedUsers.Add(userId); + } + + public void RejectUser(Guid userId) => PendingUsers.Remove(userId); + + public bool IsAccepted(Guid userId) => AcceptedUsers.Contains(userId); } \ No newline at end of file diff --git a/dotnet/AipsRT/Model/Whiteboard/WhiteboardManager.cs b/dotnet/AipsRT/Model/Whiteboard/WhiteboardManager.cs index 10c19dc..a6d45d2 100644 --- a/dotnet/AipsRT/Model/Whiteboard/WhiteboardManager.cs +++ b/dotnet/AipsRT/Model/Whiteboard/WhiteboardManager.cs @@ -47,13 +47,42 @@ public class WhiteboardManager return _userInWhiteboards[userId]; } - public void RemoveUserFromWhiteboard(Guid userId, Guid whiteboardId) + public void RemoveUserFromWhiteboard(Guid userId) { - _userInWhiteboards.TryRemove(whiteboardId, out _); + _userInWhiteboards.TryRemove(userId, out _); } public Whiteboard? GetWhiteboardForUser(Guid userId) { return GetWhiteboard(GetUserWhiteboard(userId)); } + + public void AddPendingUser(Guid userId, Guid whiteboardId) + { + var wb = GetWhiteboard(whiteboardId)!; + wb.AddPendingUser(userId); + _userInWhiteboards[userId] = whiteboardId; + } + + public void MovePendingToAccepted(Guid userId, Guid whiteboardId) + { + var wb = GetWhiteboard(whiteboardId)!; + wb.AcceptUser(userId); + } + + public void RemovePendingUser(Guid userId, Guid whiteboardId) + { + var whiteboard = GetWhiteboard(whiteboardId)!; + whiteboard.RejectUser(userId); + _userInWhiteboards.TryRemove(userId, out _); + } + + public bool IsAccepted(Guid userId) + { + if (!_userInWhiteboards.TryGetValue(userId, out var wbId)) + return false; + + var whiteboard = GetWhiteboard(wbId); + return whiteboard?.IsAccepted(userId) ?? false; + } } \ No newline at end of file diff --git a/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs b/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs index 3bbf790..3c10fff 100644 --- a/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs +++ b/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs @@ -1,5 +1,7 @@ using AipsCore.Application.Models.Shape.Command.CreateTextShape; using AipsCore.Application.Models.Shape.Command.MoveShape; +using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin; +using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin; using AipsRT.Model.Whiteboard.Shapes; namespace AipsRT.Services.Interfaces; @@ -12,4 +14,7 @@ public interface IMessagingService Task CreateTextShape(Guid whiteboardId, TextShape textShape); Task MoveShape(MoveShapeCommand moveShape); + + Task AcceptedUser(AcceptUserRequestToJoinCommand command); + Task RejectedUser(RejectUserRequestToJoinCommand command); } \ No newline at end of file diff --git a/dotnet/AipsRT/Services/MessagingService.cs b/dotnet/AipsRT/Services/MessagingService.cs index 3ad2aa6..9c241ad 100644 --- a/dotnet/AipsRT/Services/MessagingService.cs +++ b/dotnet/AipsRT/Services/MessagingService.cs @@ -1,14 +1,18 @@ using AipsCore.Application.Abstract.MessageBroking; +using AipsCore.Application.Common.Message.AcceptUserRequestToJoin; using AipsCore.Application.Common.Message.AddArrow; using AipsCore.Application.Common.Message.AddLine; using AipsCore.Application.Common.Message.AddRectangle; using AipsCore.Application.Common.Message.AddTextShape; using AipsCore.Application.Common.Message.MoveShape; +using AipsCore.Application.Common.Message.RejectUserRequestToJoin; using AipsCore.Application.Models.Shape.Command.CreateArrow; using AipsCore.Application.Models.Shape.Command.CreateLine; using AipsCore.Application.Models.Shape.Command.CreateRectangle; using AipsCore.Application.Models.Shape.Command.CreateTextShape; using AipsCore.Application.Models.Shape.Command.MoveShape; +using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin; +using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin; using AipsRT.Model.Whiteboard.Shapes; using AipsRT.Services.Interfaces; @@ -100,4 +104,16 @@ public class MessagingService : IMessagingService var message = new MoveShapeMessage(moveShape); await _messagePublisher.PublishAsync(message); } + + public async Task AcceptedUser(AcceptUserRequestToJoinCommand command) + { + var message = new AcceptUserRequestToJoinMessage(command); + await _messagePublisher.PublishAsync(message); + } + + public async Task RejectedUser(RejectUserRequestToJoinCommand command) + { + var message = new RejectUserRequestToJoinMessage(command); + await _messagePublisher.PublishAsync(message); + } } \ No newline at end of file