diff --git a/dotnet/AipsCore/AipsCore.csproj b/dotnet/AipsCore/AipsCore.csproj index a733dcb..021cc81 100644 --- a/dotnet/AipsCore/AipsCore.csproj +++ b/dotnet/AipsCore/AipsCore.csproj @@ -20,6 +20,7 @@ + diff --git a/dotnet/AipsCore/Application/Common/Message/AddArrow/AddArrowMessage.cs b/dotnet/AipsCore/Application/Common/Message/AddArrow/AddArrowMessage.cs new file mode 100644 index 0000000..ee857f3 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/AddArrow/AddArrowMessage.cs @@ -0,0 +1,6 @@ +using AipsCore.Application.Abstract.MessageBroking; +using AipsCore.Application.Models.Shape.Command.CreateArrow; + +namespace AipsCore.Application.Common.Message.AddArrow; + +public record AddArrowMessage(CreateArrowCommand Command) : IMessage; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/AddArrow/AddArrowMessageHandler.cs b/dotnet/AipsCore/Application/Common/Message/AddArrow/AddArrowMessageHandler.cs new file mode 100644 index 0000000..745f4d0 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/AddArrow/AddArrowMessageHandler.cs @@ -0,0 +1,19 @@ +using AipsCore.Application.Abstract; +using AipsCore.Application.Abstract.MessageBroking; + +namespace AipsCore.Application.Common.Message.AddArrow; + +public class AddArrowMessageHandler : IMessageHandler +{ + private readonly IDispatcher _dispatcher; + + public AddArrowMessageHandler(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public async Task Handle(AddArrowMessage message, CancellationToken cancellationToken) + { + await _dispatcher.Execute(message.Command, cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/AddLine/AddLineMessage.cs b/dotnet/AipsCore/Application/Common/Message/AddLine/AddLineMessage.cs new file mode 100644 index 0000000..e7769b5 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/AddLine/AddLineMessage.cs @@ -0,0 +1,6 @@ +using AipsCore.Application.Abstract.MessageBroking; +using AipsCore.Application.Models.Shape.Command.CreateLine; + +namespace AipsCore.Application.Common.Message.AddLine; + +public record AddLineMessage(CreateLineCommand Command) : IMessage; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/AddLine/AddLineMessageHandler.cs b/dotnet/AipsCore/Application/Common/Message/AddLine/AddLineMessageHandler.cs new file mode 100644 index 0000000..3654162 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/AddLine/AddLineMessageHandler.cs @@ -0,0 +1,20 @@ +using AipsCore.Application.Abstract; +using AipsCore.Application.Abstract.MessageBroking; +using AipsCore.Application.Models.Shape.Command.CreateLine; + +namespace AipsCore.Application.Common.Message.AddLine; + +public class AddLineMessageHandler : IMessageHandler +{ + private readonly IDispatcher _dispatcher; + + public AddLineMessageHandler(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public async Task Handle(AddLineMessage message, CancellationToken cancellationToken) + { + await _dispatcher.Execute(message.Command, cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/AddTextShape/AddTextShapeMessage.cs b/dotnet/AipsCore/Application/Common/Message/AddTextShape/AddTextShapeMessage.cs new file mode 100644 index 0000000..d65a896 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/AddTextShape/AddTextShapeMessage.cs @@ -0,0 +1,6 @@ +using AipsCore.Application.Abstract.MessageBroking; +using AipsCore.Application.Models.Shape.Command.CreateTextShape; + +namespace AipsCore.Application.Common.Message.AddTextShape; + +public record AddTextShapeMessage(CreateTextShapeCommand Command) : IMessage; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/AddTextShape/AddTextShapeMessageHandler.cs b/dotnet/AipsCore/Application/Common/Message/AddTextShape/AddTextShapeMessageHandler.cs new file mode 100644 index 0000000..5d90b3e --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/AddTextShape/AddTextShapeMessageHandler.cs @@ -0,0 +1,19 @@ +using AipsCore.Application.Abstract; +using AipsCore.Application.Abstract.MessageBroking; + +namespace AipsCore.Application.Common.Message.AddTextShape; + +public class AddTextShapeMessageHandler : IMessageHandler +{ + private readonly IDispatcher _dispatcher; + + public AddTextShapeMessageHandler(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public async Task Handle(AddTextShapeMessage message, CancellationToken cancellationToken) + { + await _dispatcher.Execute(message.Command, cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/MoveShape/MoveShapeMessage.cs b/dotnet/AipsCore/Application/Common/Message/MoveShape/MoveShapeMessage.cs new file mode 100644 index 0000000..eb97ad7 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/MoveShape/MoveShapeMessage.cs @@ -0,0 +1,6 @@ +using AipsCore.Application.Abstract.MessageBroking; +using AipsCore.Application.Models.Shape.Command.MoveShape; + +namespace AipsCore.Application.Common.Message.MoveShape; + +public record MoveShapeMessage(MoveShapeCommand Command) : IMessage; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Message/MoveShape/MoveShapeMessageHandler.cs b/dotnet/AipsCore/Application/Common/Message/MoveShape/MoveShapeMessageHandler.cs new file mode 100644 index 0000000..28ca809 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Message/MoveShape/MoveShapeMessageHandler.cs @@ -0,0 +1,19 @@ +using AipsCore.Application.Abstract; +using AipsCore.Application.Abstract.MessageBroking; + +namespace AipsCore.Application.Common.Message.MoveShape; + +public class MoveShapeMessageHandler : IMessageHandler +{ + private readonly IDispatcher _dispatcher; + + public MoveShapeMessageHandler(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public async Task Handle(MoveShapeMessage message, CancellationToken cancellationToken) + { + await _dispatcher.Execute(message.Command, cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommand.cs b/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommand.cs index cf96853..d98bede 100644 --- a/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommand.cs +++ b/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommand.cs @@ -4,6 +4,7 @@ using AipsCore.Domain.Models.Shape.ValueObjects; namespace AipsCore.Application.Models.Shape.Command.CreateArrow; public record CreateArrowCommand( + string Id, string WhiteboardId, string AuthorId, int PositionX, @@ -11,4 +12,4 @@ public record CreateArrowCommand( string Color, int EndPositionX, int EndPositionY, - int Thickness) : ICommand; \ No newline at end of file + int Thickness) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommandHandler.cs b/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommandHandler.cs index 80de92a..5e22d7f 100644 --- a/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommandHandler.cs +++ b/dotnet/AipsCore/Application/Models/Shape/Command/CreateArrow/CreateArrowCommandHandler.cs @@ -5,7 +5,7 @@ using AipsCore.Domain.Models.Shape.ValueObjects; namespace AipsCore.Application.Models.Shape.Command.CreateArrow; -public class CreateArrowCommandHandler : ICommandHandler +public class CreateArrowCommandHandler : ICommandHandler { private readonly IShapeRepository _shapeRepository; private readonly IUnitOfWork _unitOfWork; @@ -16,9 +16,10 @@ public class CreateArrowCommandHandler : ICommandHandler Handle(CreateArrowCommand command, CancellationToken cancellationToken = default) + public async Task Handle(CreateArrowCommand command, CancellationToken cancellationToken = default) { var arrow = Domain.Models.Shape.Sub.Arrow.Arrow.Create( + command.Id, command.WhiteboardId, command.AuthorId, command.PositionX, command.PositionY, @@ -28,8 +29,6 @@ public class CreateArrowCommandHandler : ICommandHandler; \ No newline at end of file + int Thickness) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Shape/Command/CreateLine/CreateLineCommandHandler.cs b/dotnet/AipsCore/Application/Models/Shape/Command/CreateLine/CreateLineCommandHandler.cs index c2338bd..c449292 100644 --- a/dotnet/AipsCore/Application/Models/Shape/Command/CreateLine/CreateLineCommandHandler.cs +++ b/dotnet/AipsCore/Application/Models/Shape/Command/CreateLine/CreateLineCommandHandler.cs @@ -5,7 +5,7 @@ using AipsCore.Domain.Models.Shape.ValueObjects; namespace AipsCore.Application.Models.Shape.Command.CreateLine; -public class CreateLineCommandHandler : ICommandHandler +public class CreateLineCommandHandler : ICommandHandler { private readonly IShapeRepository _shapeRepository; private readonly IUnitOfWork _unitOfWork; @@ -16,9 +16,10 @@ public class CreateLineCommandHandler : ICommandHandler Handle(CreateLineCommand command, CancellationToken cancellationToken = default) + public async Task Handle(CreateLineCommand command, CancellationToken cancellationToken = default) { var line = Domain.Models.Shape.Sub.Line.Line.Create( + command.Id, command.WhiteboardId, command.AuthorId, command.PositionX, @@ -30,7 +31,5 @@ public class CreateLineCommandHandler : ICommandHandler; \ No newline at end of file + int TextSize) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Shape/Command/CreateTextShape/CreateTextShapeCommandHandler.cs b/dotnet/AipsCore/Application/Models/Shape/Command/CreateTextShape/CreateTextShapeCommandHandler.cs index a024423..3cf0353 100644 --- a/dotnet/AipsCore/Application/Models/Shape/Command/CreateTextShape/CreateTextShapeCommandHandler.cs +++ b/dotnet/AipsCore/Application/Models/Shape/Command/CreateTextShape/CreateTextShapeCommandHandler.cs @@ -6,7 +6,7 @@ using AipsCore.Domain.Models.Shape.ValueObjects; namespace AipsCore.Application.Models.Shape.Command.CreateTextShape; -public class CreateTextShapeCommandHandler : ICommandHandler +public class CreateTextShapeCommandHandler : ICommandHandler { private readonly IShapeRepository _shapeRepository; private readonly IUnitOfWork _unitOfWork; @@ -17,9 +17,10 @@ public class CreateTextShapeCommandHandler : ICommandHandler Handle(CreateTextShapeCommand command, CancellationToken cancellationToken = default) + public async Task Handle(CreateTextShapeCommand command, CancellationToken cancellationToken = default) { var textShape = TextShape.Create( + command.Id, command.WhiteboardId, command.AuthorId, command.PositionX, @@ -30,7 +31,5 @@ public class CreateTextShapeCommandHandler : ICommandHandler +{ + private readonly IShapeRepository _shapeRepository; + private readonly IUnitOfWork _unitOfWork; + + public MoveShapeCommandHandler(IShapeRepository shapeRepository, IUnitOfWork unitOfWork) + { + _shapeRepository = shapeRepository; + _unitOfWork = unitOfWork; + } + + public async Task Handle(MoveShapeCommand command, CancellationToken cancellationToken = default) + { + var id = new ShapeId(command.ShapeId); + var shape = await _shapeRepository.GetByIdAsync(id, cancellationToken); + + if (shape == null) + { + throw new ValidationException(ShapeErrors.NotFound(id)); + } + + shape.Move(command.NewPositionX, command.NewPositionY); + + await _shapeRepository.SaveAsync(shape, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQuery.cs b/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQuery.cs index 48f6ad5..ae32282 100644 --- a/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQuery.cs +++ b/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQuery.cs @@ -2,4 +2,4 @@ using AipsCore.Application.Abstract.Query; namespace AipsCore.Application.Models.User.Query.GetUser; -public record GetUserQuery(string UserId) : IQuery; \ No newline at end of file +public record GetUserQuery(string UserId) : IQuery; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryDto.cs b/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryDto.cs new file mode 100644 index 0000000..3075eb4 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryDto.cs @@ -0,0 +1,3 @@ +namespace AipsCore.Application.Models.User.Query.GetUser; + +public record GetUserQueryDto(string Id, string Email, string UserName); \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryHandler.cs b/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryHandler.cs index 7de30d1..e621c79 100644 --- a/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryHandler.cs +++ b/dotnet/AipsCore/Application/Models/User/Query/GetUser/GetUserQueryHandler.cs @@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore; namespace AipsCore.Application.Models.User.Query.GetUser; -public class GetUserQueryHandler : IQueryHandler +public class GetUserQueryHandler : IQueryHandler { private readonly AipsDbContext _context; @@ -16,7 +16,7 @@ public class GetUserQueryHandler : IQueryHandler Handle(GetUserQuery query, CancellationToken cancellationToken = default) + public async Task Handle(GetUserQuery query, CancellationToken cancellationToken = default) { var result = await _context.Users .Where(u => u.Id.ToString() == query.UserId) @@ -27,6 +27,6 @@ public class GetUserQueryHandler : IQueryHandler +public abstract partial class Shape : DomainModel { public WhiteboardId WhiteboardId { get; private set; } diff --git a/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.Move.cs b/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.Move.cs new file mode 100644 index 0000000..34ff4be --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.Move.cs @@ -0,0 +1,11 @@ +namespace AipsCore.Domain.Models.Shape.Sub.Arrow; + +public partial class Arrow +{ + public override void Move(int newPositionX, int newPositionY) + { + EndPosition.X += newPositionX - Position.X; + EndPosition.Y += newPositionY - Position.Y; + base.Move(newPositionX, newPositionY); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.cs b/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.cs index e28b7a4..bbcd25e 100644 --- a/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.cs +++ b/dotnet/AipsCore/Domain/Models/Shape/Sub/Arrow/Arrow.cs @@ -6,7 +6,7 @@ using AipsCore.Domain.Models.Whiteboard.ValueObjects; namespace AipsCore.Domain.Models.Shape.Sub.Arrow; -public class Arrow : Shape +public partial class Arrow : Shape { public Position EndPosition { get; private set; } public Thickness Thickness { get; private set; } diff --git a/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.Move.cs b/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.Move.cs new file mode 100644 index 0000000..0a768d7 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.Move.cs @@ -0,0 +1,11 @@ +namespace AipsCore.Domain.Models.Shape.Sub.Line; + +public partial class Line +{ + public override void Move(int newPositionX, int newPositionY) + { + EndPosition.X += newPositionX - Position.X; + EndPosition.Y += newPositionY - Position.Y; + base.Move(newPositionX, newPositionY); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.cs b/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.cs index 204810c..196eee4 100644 --- a/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.cs +++ b/dotnet/AipsCore/Domain/Models/Shape/Sub/Line/Line.cs @@ -6,7 +6,7 @@ using AipsCore.Domain.Models.Whiteboard.ValueObjects; namespace AipsCore.Domain.Models.Shape.Sub.Line; -public class Line : Shape +public partial class Line : Shape { public Position EndPosition { get; private set; } public Thickness Thickness { get; private set; } diff --git a/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.Move.cs b/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.Move.cs new file mode 100644 index 0000000..6bfaeb2 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.Move.cs @@ -0,0 +1,11 @@ +namespace AipsCore.Domain.Models.Shape.Sub.Rectangle; + +public partial class Rectangle +{ + public override void Move(int newPositionX, int newPositionY) + { + EndPosition.X += newPositionX - Position.X; + EndPosition.Y += newPositionY - Position.Y; + base.Move(newPositionX, newPositionY); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.cs b/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.cs index 359ba91..7db397f 100644 --- a/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.cs +++ b/dotnet/AipsCore/Domain/Models/Shape/Sub/Rectangle/Rectangle.cs @@ -6,11 +6,11 @@ using AipsCore.Domain.Models.Whiteboard.ValueObjects; namespace AipsCore.Domain.Models.Shape.Sub.Rectangle; -public class Rectangle : Shape +public partial class Rectangle : Shape { public override ShapeType ShapeType => ShapeType.Rectangle; - public Position EndPosition { get; } + public Position EndPosition { get; set; } public Thickness BorderThickness { get; } diff --git a/dotnet/AipsCore/Domain/Models/Shape/Validation/ShapeErrors.cs b/dotnet/AipsCore/Domain/Models/Shape/Validation/ShapeErrors.cs new file mode 100644 index 0000000..cf0e884 --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Shape/Validation/ShapeErrors.cs @@ -0,0 +1,9 @@ +using AipsCore.Domain.Abstract.Validation; +using AipsCore.Domain.Models.Shape.ValueObjects; + +namespace AipsCore.Domain.Models.Shape.Validation; + +public class ShapeErrors : AbstractErrors +{ + +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Shape/ValueObjects/Position.cs b/dotnet/AipsCore/Domain/Models/Shape/ValueObjects/Position.cs index 22af8a8..bd7b6e1 100644 --- a/dotnet/AipsCore/Domain/Models/Shape/ValueObjects/Position.cs +++ b/dotnet/AipsCore/Domain/Models/Shape/ValueObjects/Position.cs @@ -5,8 +5,8 @@ namespace AipsCore.Domain.Models.Shape.ValueObjects; public record Position : AbstractValueObject { - public int X { get; } - public int Y { get; } + public int X { get; set; } + public int Y { get; set; } public Position(int x, int y) { @@ -20,4 +20,14 @@ public record Position : AbstractValueObject ]; } + + public static Position operator -(Position position, Position otherPosition) + { + return new Position(position.X - otherPosition.X, position.Y - otherPosition.Y); + } + + public static Position operator +(Position position, Position otherPosition) + { + return new Position(position.X + otherPosition.X, position.Y + otherPosition.Y); + } }; \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Shape/Mappers/ShapeMappers.FromEntity.cs b/dotnet/AipsCore/Infrastructure/Persistence/Shape/Mappers/ShapeMappers.FromEntity.cs index 9089d01..32b0479 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/Shape/Mappers/ShapeMappers.FromEntity.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/Shape/Mappers/ShapeMappers.FromEntity.cs @@ -25,6 +25,7 @@ public static partial class ShapeMappers return Rectangle.Create( shape.Id.ToString(), shape.WhiteboardId.ToString(), + shape.AuthorId.ToString(), shape.PositionX, shape.PositionY, shape.Color, shape.EndPositionX!.Value, shape.EndPositionY!.Value, diff --git a/dotnet/AipsRT/Hubs/WhiteboardHub.cs b/dotnet/AipsRT/Hubs/WhiteboardHub.cs index 6322be0..ec22db5 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.Models.Shape.Command.MoveShape; using AipsRT.Model.Whiteboard; using AipsRT.Model.Whiteboard.Shapes; +using AipsRT.Model.Whiteboard.Structs; using AipsRT.Services.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; @@ -41,17 +44,89 @@ public class WhiteboardHub : Hub .SendAsync("Leaved", Context.UserIdentifier!); } + private Guid CurrentUserId => Guid.Parse(Context.UserIdentifier!); + private Whiteboard CurrentWhiteboard => _whiteboardManager.GetWhiteboardForUser(CurrentUserId)!; + + private async Task ResetCurrentUser() + { + await Clients.Caller.SendAsync("InitWhiteboard", CurrentWhiteboard); + } + + private async Task SendToOthers(string methodName, object? arg) + { + await Clients.GroupExcept(CurrentWhiteboard.WhiteboardId.ToString(), Context.ConnectionId) + .SendAsync(methodName, arg); + } + public async Task AddRectangle(Rectangle rectangle) { - var whiteboard = _whiteboardManager.GetWhiteboardForUser(Guid.Parse(Context.UserIdentifier!))!; - - rectangle.OwnerId = Guid.Parse(Context.UserIdentifier!); + rectangle.OwnerId = CurrentUserId; + var whiteboard = CurrentWhiteboard; await _messagingService.CreatedRectangle(whiteboard.WhiteboardId, rectangle); whiteboard.AddRectangle(rectangle); - await Clients.GroupExcept(whiteboard.WhiteboardId.ToString(), Context.ConnectionId) - .SendAsync("AddedRectangle", rectangle); + await SendToOthers("AddedRectangle", rectangle); + } + + public async Task AddArrow(Arrow arrow) + { + arrow.OwnerId = CurrentUserId; + var whiteboard = CurrentWhiteboard; + + await _messagingService.CreatedArrow(whiteboard.WhiteboardId, arrow); + + whiteboard.AddArrow(arrow); + + await SendToOthers("AddedArrow", arrow); + } + + public async Task AddLine(Line line) + { + line.OwnerId = CurrentUserId; + var whiteboard = CurrentWhiteboard; + + await _messagingService.CreateLine(whiteboard.WhiteboardId, line); + + whiteboard.AddLine(line); + + await SendToOthers("AddedLine", line); + } + + public async Task AddTextShape(TextShape textShape) + { + textShape.OwnerId = CurrentUserId; + var whiteboard = CurrentWhiteboard; + + await _messagingService.CreateTextShape(whiteboard.WhiteboardId, textShape); + + whiteboard.AddTextShape(textShape); + + await SendToOthers("AddedTextShape", textShape); + } + + public async Task MoveShape(MoveShapeCommand moveShape) + { + var whiteboard = CurrentWhiteboard; + + var shape = whiteboard.Shapes.Find(s => s.Id.ToString() == moveShape.ShapeId); + + if (shape is null || shape.OwnerId != CurrentUserId) + { + await ResetCurrentUser(); + return; + } + + shape.Move(new Position(moveShape.NewPositionX, moveShape.NewPositionY)); + + await SendToOthers("MovedShape", moveShape); + } + + public async Task PlaceShape(MoveShapeCommand moveShape) + { + await MoveShape(moveShape); + + await _messagingService.MoveShape(moveShape); } } \ No newline at end of file diff --git a/dotnet/AipsRT/Model/Whiteboard/Shapes/Arrow.cs b/dotnet/AipsRT/Model/Whiteboard/Shapes/Arrow.cs index 8019640..6d1a3e3 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Shapes/Arrow.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Shapes/Arrow.cs @@ -7,4 +7,11 @@ public class Arrow : Shape public Position EndPosition { get; set; } public int Thickness { get; set; } + + public override void Move(Position newPosition) + { + var difference = newPosition - EndPosition; + EndPosition += difference; + base.Move(newPosition); + } } \ No newline at end of file diff --git a/dotnet/AipsRT/Model/Whiteboard/Shapes/Line.cs b/dotnet/AipsRT/Model/Whiteboard/Shapes/Line.cs index c80c464..8274a8c 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Shapes/Line.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Shapes/Line.cs @@ -7,4 +7,11 @@ public class Line : Shape public Position EndPosition { get; set; } public int Thickness { get; set; } + + public override void Move(Position newPosition) + { + var difference = newPosition - EndPosition; + EndPosition += difference; + base.Move(newPosition); + } } \ No newline at end of file diff --git a/dotnet/AipsRT/Model/Whiteboard/Shapes/Map/ShapeMappingExtensions.cs b/dotnet/AipsRT/Model/Whiteboard/Shapes/Map/ShapeMappingExtensions.cs index d463a80..dbdb7e6 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Shapes/Map/ShapeMappingExtensions.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Shapes/Map/ShapeMappingExtensions.cs @@ -11,6 +11,7 @@ public static class ShapeMappingExtensions return new Rectangle() { Id = shape.Id, + OwnerId = shape.AuthorId, Position = new Position(shape.PositionX, shape.PositionY), Color = shape.Color, EndPosition = new Position(shape.EndPositionX!.Value, shape.EndPositionY!.Value), @@ -23,6 +24,7 @@ public static class ShapeMappingExtensions return new Arrow() { Id = shape.Id, + OwnerId = shape.AuthorId, Position = new Position(shape.PositionX, shape.PositionY), Color = shape.Color, EndPosition = new Position(shape.EndPositionX!.Value, shape.EndPositionY!.Value), @@ -35,6 +37,7 @@ public static class ShapeMappingExtensions return new Line() { Id = shape.Id, + OwnerId = shape.AuthorId, Position = new Position(shape.PositionX, shape.PositionY), Color = shape.Color, EndPosition = new Position(shape.EndPositionX!.Value, shape.EndPositionY!.Value), @@ -47,6 +50,7 @@ public static class ShapeMappingExtensions return new TextShape() { Id = shape.Id, + OwnerId = shape.AuthorId, Position = new Position(shape.PositionX, shape.PositionY), Color = shape.Color, TextValue = shape.TextValue!, diff --git a/dotnet/AipsRT/Model/Whiteboard/Shapes/Rectangle.cs b/dotnet/AipsRT/Model/Whiteboard/Shapes/Rectangle.cs index bbd1fcd..2f72487 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Shapes/Rectangle.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Shapes/Rectangle.cs @@ -7,4 +7,11 @@ public class Rectangle : Shape public Position EndPosition { get; set; } public int BorderThickness { get; set; } + + public override void Move(Position newPosition) + { + var difference = newPosition - Position; + EndPosition += difference; + base.Move(newPosition); + } } \ No newline at end of file diff --git a/dotnet/AipsRT/Model/Whiteboard/Shapes/Shape.cs b/dotnet/AipsRT/Model/Whiteboard/Shapes/Shape.cs index cf41042..8605ef5 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Shapes/Shape.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Shapes/Shape.cs @@ -11,4 +11,9 @@ public abstract class Shape public Position Position { get; set; } public string Color { get; set; } + + public virtual void Move(Position newPosition) + { + Position = newPosition; + } } \ No newline at end of file diff --git a/dotnet/AipsRT/Model/Whiteboard/Structs/Position.cs b/dotnet/AipsRT/Model/Whiteboard/Structs/Position.cs index 2fc4ab6..6c5eacc 100644 --- a/dotnet/AipsRT/Model/Whiteboard/Structs/Position.cs +++ b/dotnet/AipsRT/Model/Whiteboard/Structs/Position.cs @@ -10,4 +10,14 @@ public struct Position X = x; Y = y; } + + public static Position operator -(Position position, Position otherPosition) + { + return new Position(position.X - otherPosition.X, position.Y - otherPosition.Y); + } + + public static Position operator +(Position position, Position otherPosition) + { + return new Position(position.X + otherPosition.X, position.Y + otherPosition.Y); + } } \ No newline at end of file diff --git a/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs b/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs index de1f413..3bbf790 100644 --- a/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs +++ b/dotnet/AipsRT/Services/Interfaces/IMessagingService.cs @@ -1,3 +1,5 @@ +using AipsCore.Application.Models.Shape.Command.CreateTextShape; +using AipsCore.Application.Models.Shape.Command.MoveShape; using AipsRT.Model.Whiteboard.Shapes; namespace AipsRT.Services.Interfaces; @@ -5,4 +7,9 @@ namespace AipsRT.Services.Interfaces; public interface IMessagingService { Task CreatedRectangle(Guid whiteboardId, Rectangle rectangle); + Task CreatedArrow(Guid whiteboardId, Arrow arrow); + Task CreateLine(Guid whiteboardId, Line line); + Task CreateTextShape(Guid whiteboardId, TextShape textShape); + + Task MoveShape(MoveShapeCommand moveShape); } \ No newline at end of file diff --git a/dotnet/AipsRT/Services/MessagingService.cs b/dotnet/AipsRT/Services/MessagingService.cs index 980fae3..3ad2aa6 100644 --- a/dotnet/AipsRT/Services/MessagingService.cs +++ b/dotnet/AipsRT/Services/MessagingService.cs @@ -1,6 +1,14 @@ using AipsCore.Application.Abstract.MessageBroking; +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.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 AipsRT.Model.Whiteboard.Shapes; using AipsRT.Services.Interfaces; @@ -33,4 +41,63 @@ public class MessagingService : IMessagingService await _messagePublisher.PublishAsync(message); } + + public async Task CreatedArrow(Guid whiteboardId, Arrow arrow) + { + var command = new CreateArrowCommand( + arrow.Id.ToString(), + whiteboardId.ToString(), + arrow.OwnerId.ToString(), + arrow.Position.X, + arrow.Position.Y, + arrow.Color, + arrow.EndPosition.X, + arrow.EndPosition.Y, + arrow.Thickness); + + var message = new AddArrowMessage(command); + + await _messagePublisher.PublishAsync(message); + } + + public async Task CreateLine(Guid whiteboardId, Line line) + { + var command = new CreateLineCommand( + line.Id.ToString(), + whiteboardId.ToString(), + line.OwnerId.ToString(), + line.Position.X, + line.Position.Y, + line.Color, + line.EndPosition.X, + line.EndPosition.Y, + line.Thickness); + + var message = new AddLineMessage(command); + + await _messagePublisher.PublishAsync(message); + } + + public async Task CreateTextShape(Guid whiteboardId, TextShape textShape) + { + var command = new CreateTextShapeCommand( + textShape.Id.ToString(), + whiteboardId.ToString(), + textShape.OwnerId.ToString(), + textShape.Position.X, + textShape.Position.Y, + textShape.Color, + textShape.TextValue, + textShape.TextSize); + + var message = new AddTextShapeMessage(command); + + await _messagePublisher.PublishAsync(message); + } + + public async Task MoveShape(MoveShapeCommand moveShape) + { + var message = new MoveShapeMessage(moveShape); + await _messagePublisher.PublishAsync(message); + } } \ No newline at end of file diff --git a/dotnet/AipsWebApi/Controllers/UserController.cs b/dotnet/AipsWebApi/Controllers/UserController.cs index a22886b..c447bac 100644 --- a/dotnet/AipsWebApi/Controllers/UserController.cs +++ b/dotnet/AipsWebApi/Controllers/UserController.cs @@ -8,6 +8,7 @@ using AipsCore.Application.Models.User.Command.LogOutAll; using AipsCore.Application.Models.User.Command.RefreshLogIn; using AipsCore.Application.Models.User.Command.SignUp; using AipsCore.Application.Models.User.Query.GetMe; +using AipsCore.Application.Models.User.Query.GetUser; using AipsCore.Infrastructure.Persistence.User; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -80,4 +81,14 @@ public class UserController : ControllerBase var result = await _dispatcher.Execute(new GetMeQuery(), cancellationToken); return result; } + + [Authorize] + [HttpGet] + public async Task> GetUser(string userId, CancellationToken cancellationToken) + { + var query = new GetUserQuery(userId); + var result = await _dispatcher.Execute(query, cancellationToken); + + return Ok(result); + } } \ No newline at end of file diff --git a/front/src/components/whiteboard/ShapeOwnerLabel.vue b/front/src/components/whiteboard/ShapeOwnerLabel.vue new file mode 100644 index 0000000..7b5922f --- /dev/null +++ b/front/src/components/whiteboard/ShapeOwnerLabel.vue @@ -0,0 +1,42 @@ + + + diff --git a/front/src/components/whiteboard/ShapeOwnerTooltip.vue b/front/src/components/whiteboard/ShapeOwnerTooltip.vue new file mode 100644 index 0000000..2c11368 --- /dev/null +++ b/front/src/components/whiteboard/ShapeOwnerTooltip.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/front/src/components/whiteboard/WhiteboardCanvas.vue b/front/src/components/whiteboard/WhiteboardCanvas.vue index cc5aafd..cce82e5 100644 --- a/front/src/components/whiteboard/WhiteboardCanvas.vue +++ b/front/src/components/whiteboard/WhiteboardCanvas.vue @@ -1,18 +1,83 @@ diff --git a/front/src/components/whiteboard/WhiteboardToolbar.vue b/front/src/components/whiteboard/WhiteboardToolbar.vue index 904622c..9b50416 100644 --- a/front/src/components/whiteboard/WhiteboardToolbar.vue +++ b/front/src/components/whiteboard/WhiteboardToolbar.vue @@ -1,44 +1,133 @@