Merge pull request #51 from StewKI/bugfixes

Bugfixes
This commit is contained in:
2026-03-10 15:15:21 +01:00
committed by GitHub
18 changed files with 139 additions and 15 deletions

View File

@@ -0,0 +1,6 @@
using AipsCore.Application.Abstract.MessageBroking;
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
namespace AipsCore.Application.Common.Message.UserCanceledRequestToJoin;
public record UserCanceledRequestToJoinMessage(UserCanceledRequestToJoinCommand Command): IMessage;

View File

@@ -0,0 +1,19 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Abstract.MessageBroking;
namespace AipsCore.Application.Common.Message.UserCanceledRequestToJoin;
public class UserCanceledRequestToJoinMessageHandler : IMessageHandler<UserCanceledRequestToJoinMessage>
{
private readonly IDispatcher _dispatcher;
public UserCanceledRequestToJoinMessageHandler(IDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public async Task Handle(UserCanceledRequestToJoinMessage message, CancellationToken cancellationToken)
{
await _dispatcher.Execute(message.Command, cancellationToken);
}
}

View File

@@ -0,0 +1,5 @@
using AipsCore.Application.Abstract.Command;
namespace AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
public record UserCanceledRequestToJoinCommand(string WhiteboardId, string UserId): ICommand;

View File

@@ -0,0 +1,40 @@
using AipsCore.Application.Abstract.Command;
using AipsCore.Domain.Abstract;
using AipsCore.Domain.Common.Validation;
using AipsCore.Domain.Models.User.ValueObjects;
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
using AipsCore.Domain.Models.WhiteboardMembership.External;
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
namespace AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
public class CancelJoinRequestCommandHandler : ICommandHandler<UserCanceledRequestToJoinCommand>
{
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
private readonly IUnitOfWork _unitOfWork;
public CancelJoinRequestCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork)
{
_whiteboardMembershipRepository = whiteboardMembershipRepository;
_unitOfWork = unitOfWork;
}
public async Task Handle(UserCanceledRequestToJoinCommand command, CancellationToken cancellationToken = default)
{
var whiteboardId = new WhiteboardId(command.WhiteboardId);
var userId = new UserId(command.UserId);
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
if (membership is null)
{
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
}
membership.UpdateStatus(WhiteboardMembershipStatus.Cancelled);
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -2,6 +2,7 @@ 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.Command.UserCanceledRequestToJoin;
using AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
using AipsRT.Model.Memberships;
@@ -21,14 +22,14 @@ public class WhiteboardHub : Hub
private readonly WhiteboardManager _whiteboardManager;
private readonly IMessagingService _messagingService;
private readonly MembershipService _membershipService;
private readonly GetUserService _getUserService;
private readonly UserService _userService;
public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService, MembershipService membershipService, GetUserService getUserService)
public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService, MembershipService membershipService, UserService userService)
{
_whiteboardManager = whiteboardManager;
_messagingService = messagingService;
_membershipService = membershipService;
_getUserService = getUserService;
_userService = userService;
}
public override async Task OnDisconnectedAsync(Exception? exception)
@@ -107,7 +108,7 @@ public class WhiteboardHub : Hub
if (user == null)
{
user = await _getUserService.GetUser(userId);
user = await _userService.GetUser(userId);
}
await Clients.User(ownerId.ToString()).SendAsync("UserWaitingForApproval", user);
@@ -147,6 +148,7 @@ public class WhiteboardHub : Hub
if (whiteboard != null)
{
await _messagingService.CancelJoinRequest(new UserCanceledRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), userId.ToString()));
await Clients.User(whiteboard.OwnerId.ToString()).SendAsync("UserCanceledJoinRequest", userId.ToString());
}
}

View File

@@ -1,13 +1,14 @@
using AipsCore.Application.Abstract;
using AipsCore.Application.Models.User.Query.GetUser;
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
namespace AipsRT.Model.Users;
public class GetUserService
public class UserService
{
private readonly IDispatcher _dispatcher;
public GetUserService(IDispatcher dispatcher)
public UserService(IDispatcher dispatcher)
{
_dispatcher = dispatcher;
}

View File

@@ -28,7 +28,8 @@ public class GetWhiteboardService
{
WhiteboardId = entity.Id,
OwnerId = entity.OwnerId,
Owner = new User(entity.Owner.Id, entity.Owner.UserName!, entity.Owner.Email!)
Owner = new User(entity.Owner.Id, entity.Owner.UserName!, entity.Owner.Email!),
Code = entity.Code,
};
foreach (var membership in entity.Memberships)

View File

@@ -9,13 +9,14 @@ public class Whiteboard
public Guid OwnerId { get; set; }
public User Owner { get; set; } = null!;
public string Code {get; set;}
public List<User> Users { get; } = [];
public List<User> ActiveUsers { get; } = [];
public void AddActiveUser(User user) => ActiveUsers.Add(user);
public void RemoveActiveUser(Guid userId)
=> ActiveUsers.RemoveAll(u => u.UserId == userId);
public void RemoveActiveUser(Guid userId) => ActiveUsers.RemoveAll(u => u.UserId == userId);
public List<Shape> Shapes { get; } = [];
@@ -48,5 +49,11 @@ public class Whiteboard
TextShapes.Add(shape);
}
public void AddUser(User user) => Users.Add(user);
public void AddUser(User user)
{
if (!Users.Contains(user))
{
Users.Add(user);
}
}
}

View File

@@ -27,7 +27,7 @@ builder.Services.AddSingleton<IErrorMessageHandleStrategy, RtErrorHandleStrategy
builder.Services.AddHostedService<ErrorSubscriberBackgroundService>();
builder.Services.AddTransient<MembershipService>();
builder.Services.AddTransient<GetUserService>();
builder.Services.AddTransient<UserService>();
builder.Services.AddScoped<GetWhiteboardService>();
builder.Services.AddSingleton<WhiteboardManager>();

View File

@@ -2,6 +2,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 AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
using AipsRT.Model.Whiteboard.Shapes;
namespace AipsRT.Services.Interfaces;
@@ -17,4 +18,5 @@ public interface IMessagingService
Task AcceptedUser(AcceptUserRequestToJoinCommand command);
Task RejectedUser(RejectUserRequestToJoinCommand command);
Task CancelJoinRequest(UserCanceledRequestToJoinCommand command);
}

View File

@@ -6,6 +6,7 @@ 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.Common.Message.UserCanceledRequestToJoin;
using AipsCore.Application.Models.Shape.Command.CreateArrow;
using AipsCore.Application.Models.Shape.Command.CreateLine;
using AipsCore.Application.Models.Shape.Command.CreateRectangle;
@@ -13,6 +14,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 AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
using AipsRT.Model.Whiteboard.Shapes;
using AipsRT.Services.Interfaces;
@@ -116,4 +118,10 @@ public class MessagingService : IMessagingService
var message = new RejectUserRequestToJoinMessage(command);
await _messagePublisher.PublishAsync(message);
}
public async Task CancelJoinRequest(UserCanceledRequestToJoinCommand command)
{
var message = new UserCanceledRequestToJoinMessage(command);
await _messagePublisher.PublishAsync(message);
}
}

View File

@@ -18,9 +18,16 @@ public class RtErrorHandleStrategy : IErrorMessageHandleStrategy
public async Task Handle(ErrorMessage message, CancellationToken cancellationToken)
{
var activeUsers = _whiteboardManager.GetWhiteboard(message.WhiteboardId)!.ActiveUsers;
await _whiteboardManager.LoadWhiteboard(message.WhiteboardId);
var whiteboard = _whiteboardManager.GetWhiteboard(message.WhiteboardId)!;
foreach (var user in activeUsers)
{
whiteboard.AddActiveUser(user);
}
await _hubContext.Clients
.Group(whiteboard.WhiteboardId.ToString())

View File

@@ -6,6 +6,7 @@ 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.Common.Message.UserCanceledRequestToJoin;
namespace AipsWorker.Messages;
@@ -21,7 +22,8 @@ public class MessageTypesProvider : IMessageTypesProvider
typeof(AddTextShapeMessage),
typeof(MoveShapeMessage),
typeof(AcceptUserRequestToJoinMessage),
typeof(RejectUserRequestToJoinMessage)
typeof(RejectUserRequestToJoinMessage),
typeof(UserCanceledRequestToJoinMessage)
];
}
}

View File

@@ -23,6 +23,12 @@ const colors = ['#4f9dff', '#ff4f4f', '#4fff4f', '#ffff4f', '#ff4fff', '#ffffff'
const isReadOnly = computed(() => sessionStore.selectedTool === 'hand' && !!sessionStore.selectedShape)
const isOwner = computed(() => {
const wb = sessionStore.whiteboard
if (!wb || !auth.user) return false
return wb.ownerId === auth.user.userId
})
const showProperties = computed(() => {
if (['rectangle', 'arrow', 'line', 'text'].includes(sessionStore.selectedTool)) return true
if (sessionStore.selectedTool === 'hand' && sessionStore.selectedShape) return true
@@ -61,7 +67,7 @@ const displayTextSize = computed(() => {
const showCopiedTooltip = ref(false)
const whiteboardCode = computed(() => infoStore.getCurrentWhiteboard()?.code || '')
const whiteboardCode = computed(() => sessionStore.whiteboard!.code)
const copyCodeToClipboard = async () => {
console.info(whiteboardCode.value)
@@ -166,7 +172,7 @@ const copyCodeToClipboard = async () => {
</div>
</div>
<div class="position-relative mb-2">
<div v-if="isOwner" class="position-relative mb-2">
<button
class="btn btn-sm btn-outline-primary w-100"
@click="copyCodeToClipboard"

View File

@@ -22,8 +22,23 @@ export class SignalRService {
}
async start(): Promise<void> {
if (this.connection.state === HubConnectionState.Disconnected) {
if (this.connection.state !== HubConnectionState.Disconnected) return;
try {
await this.connection.start();
} catch (err: any) {
if (err.statusCode === 401 || err.message?.includes("401")) {
const authStore = useAuthStore();
try {
await authStore.tryRefresh();
await this.connection.start();
} catch (refreshErr) {
throw refreshErr;
}
} else {
throw err;
}
}
}

View File

@@ -151,6 +151,7 @@ export const useAuthStore = defineStore('auth', () => {
isAuthenticated,
isLoading,
error,
tryRefresh,
initialize,
login,
signup,

View File

@@ -113,6 +113,7 @@ export const useWhiteboardStore = defineStore('whiteboard', () => {
whiteboardHubService.onAccepted(() => {
const infoStore = useWhiteboardsStore()
infoStore.stopWaitingToJoin()
isLoading.value = false
})
whiteboardHubService.onRejected(() => {

View File

@@ -33,6 +33,7 @@ export interface TextShape extends Shape {
export interface Whiteboard {
whiteboardId: string
ownerId: string
code: string
rectangles: Rectangle[]
arrows: Arrow[]
lines: Line[]