Merge pull request #45 from StewKI/feature-join-whiteboard-by-code
Feature join whiteboard by code
This commit is contained in:
@@ -21,7 +21,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Application\Models\Shape\Command\DeleteShape\" />
|
||||
<Folder Include="Domain\Models\WhiteboardMembership\Validation\" />
|
||||
<Folder Include="Infrastructure\Persistence\Db\Migrations\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
using AipsCore.Application.Abstract.MessageBroking;
|
||||
using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||
|
||||
namespace AipsCore.Application.Common.Message.AcceptUserRequestToJoin;
|
||||
|
||||
public record AcceptUserRequestToJoinMessage(AcceptUserRequestToJoinCommand Command) : IMessage;
|
||||
@@ -0,0 +1,19 @@
|
||||
using AipsCore.Application.Abstract;
|
||||
using AipsCore.Application.Abstract.MessageBroking;
|
||||
|
||||
namespace AipsCore.Application.Common.Message.AcceptUserRequestToJoin;
|
||||
|
||||
public class AcceptUserRequestToJoinMessageHandler : IMessageHandler<AcceptUserRequestToJoinMessage>
|
||||
{
|
||||
private readonly IDispatcher _dispatcher;
|
||||
|
||||
public AcceptUserRequestToJoinMessageHandler(IDispatcher dispatcher)
|
||||
{
|
||||
_dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
public async Task Handle(AcceptUserRequestToJoinMessage message, CancellationToken cancellationToken)
|
||||
{
|
||||
await _dispatcher.Execute(message.Command, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using AipsCore.Application.Abstract.MessageBroking;
|
||||
using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||
|
||||
namespace AipsCore.Application.Common.Message.RejectUserRequestToJoin;
|
||||
|
||||
public record RejectUserRequestToJoinMessage(RejectUserRequestToJoinCommand Command): IMessage;
|
||||
@@ -0,0 +1,19 @@
|
||||
using AipsCore.Application.Abstract;
|
||||
using AipsCore.Application.Abstract.MessageBroking;
|
||||
|
||||
namespace AipsCore.Application.Common.Message.RejectUserRequestToJoin;
|
||||
|
||||
public class RejectUserRequestToJoinMessageHandler : IMessageHandler<RejectUserRequestToJoinMessage>
|
||||
{
|
||||
private readonly IDispatcher _dispatcher;
|
||||
|
||||
public RejectUserRequestToJoinMessageHandler(IDispatcher dispatcher)
|
||||
{
|
||||
_dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
public async Task Handle(RejectUserRequestToJoinMessage message, CancellationToken cancellationToken)
|
||||
{
|
||||
await _dispatcher.Execute(message.Command, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||
|
||||
public record AcceptUserRequestToJoinCommand(string WhiteboardId, string UserId): ICommand;
|
||||
@@ -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.AcceptUserRequestToJoin;
|
||||
|
||||
public class AcceptUserRequestToJoinCommandHandler : ICommandHandler<AcceptUserRequestToJoinCommand>
|
||||
{
|
||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public AcceptUserRequestToJoinCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork)
|
||||
{
|
||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task Handle(AcceptUserRequestToJoinCommand 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.Accepted);
|
||||
|
||||
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.AddUserToWhiteboard;
|
||||
|
||||
public record AddUserToWhiteboardCommand(
|
||||
string UserId,
|
||||
string WhiteboardId)
|
||||
: ICommand;
|
||||
@@ -1,12 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.AddUserToWhiteboard;
|
||||
|
||||
public static class AddUserToWhiteboardCommandErrors
|
||||
{
|
||||
public static ValidationError WhiteboardDoesNotExist(WhiteboardId whiteboardId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_not_exists",
|
||||
Message: $"Whiteboard with id '{whiteboardId}' does not exist.");
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using AipsCore.Application.Abstract;
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.External;
|
||||
using AipsCore.Domain.Models.User.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.External;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.AddUserToWhiteboard;
|
||||
|
||||
public class AddUserToWhiteboardCommandHandler
|
||||
: ICommandHandler<AddUserToWhiteboardCommand>
|
||||
{
|
||||
private readonly IWhiteboardRepository _whiteboardRepository;
|
||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IDispatcher _dispatcher;
|
||||
|
||||
public AddUserToWhiteboardCommandHandler(
|
||||
IWhiteboardRepository whiteboardRepository,
|
||||
IWhiteboardMembershipRepository whiteboardMembershipRepository,
|
||||
IUserRepository userRepository,
|
||||
IUnitOfWork unitOfWork,
|
||||
IDispatcher dispatcher)
|
||||
{
|
||||
_whiteboardRepository = whiteboardRepository;
|
||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||
_userRepository = userRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
_dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
private Domain.Models.Whiteboard.Whiteboard? _whiteboard;
|
||||
private Domain.Models.User.User? _user;
|
||||
|
||||
public async Task Handle(AddUserToWhiteboardCommand command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_whiteboard = await _whiteboardRepository.GetByIdAsync(new WhiteboardId(command.WhiteboardId), cancellationToken);
|
||||
_user = await _userRepository.GetByIdAsync(new UserId(command.UserId), cancellationToken);
|
||||
|
||||
Validate(command);
|
||||
|
||||
await _whiteboard!.AddUserAsync(_user!, _whiteboardMembershipRepository, cancellationToken);
|
||||
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private void Validate(AddUserToWhiteboardCommand command)
|
||||
{
|
||||
if (_whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(new WhiteboardId(command.WhiteboardId)));
|
||||
}
|
||||
|
||||
if (_user is null)
|
||||
{
|
||||
throw new ValidationException(UserErrors.NotFound(new UserId(command.UserId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard;
|
||||
|
||||
public static class BanUserFromWhiteboardCommandErrors
|
||||
{
|
||||
public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_membership_not_found",
|
||||
Message: $"User with id '{userId.IdValue}' is not a member of whiteboard with id '{whiteboardId.IdValue}'");
|
||||
|
||||
public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_not_found",
|
||||
Message: $"Whiteboard with id '{whiteboardId.IdValue}' not found.");
|
||||
}
|
||||
@@ -4,8 +4,10 @@ using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.External;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard;
|
||||
|
||||
@@ -37,14 +39,14 @@ public class BanUserFromWhiteboardCommandHandler : ICommandHandler<BanUserFromWh
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
whiteboard.BanUser(_userContext.GetCurrentUserId(), membership);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||
|
||||
public record JoinWithCodeCommand(string Code): ICommand<JoinWithCodeDto>;
|
||||
@@ -0,0 +1,65 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
using AipsCore.Application.Abstract.UserContext;
|
||||
using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.External;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||
|
||||
public class JoinWithCodeCommandHandler : ICommandHandler<JoinWithCodeCommand, JoinWithCodeDto>
|
||||
{
|
||||
private readonly IWhiteboardRepository _whiteboardRepository;
|
||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IUserContext _userContext;
|
||||
|
||||
public JoinWithCodeCommandHandler(
|
||||
IWhiteboardRepository whiteboardRepository,
|
||||
IWhiteboardMembershipRepository whiteboardMembershipRepository,
|
||||
IUnitOfWork unitOfWork,
|
||||
IUserContext userContext)
|
||||
{
|
||||
_whiteboardRepository = whiteboardRepository;
|
||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
_userContext = userContext;
|
||||
}
|
||||
|
||||
public async Task<JoinWithCodeDto> Handle(JoinWithCodeCommand command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var userId = _userContext.GetCurrentUserId();
|
||||
|
||||
var code = new WhiteboardCode(command.Code);
|
||||
var whiteboard = await _whiteboardRepository.GetByCodeAsync(code, cancellationToken);
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(code));
|
||||
}
|
||||
|
||||
if (!whiteboard.ShouldRequestToJoin(userId))
|
||||
{
|
||||
return new JoinWithCodeDto(whiteboard.Id.IdValue, WhiteboardMembershipStatus.Accepted);
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboard.Id, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
membership = whiteboard.RequestJoin(userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
whiteboard.RequestReJoin(membership);
|
||||
}
|
||||
|
||||
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new JoinWithCodeDto(whiteboard.Id.IdValue, membership.Status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||
|
||||
public record JoinWithCodeDto(string WhiteboardId, WhiteboardMembershipStatus Status);
|
||||
@@ -1,18 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard;
|
||||
|
||||
public static class KickUserFromWhiteboardCommandErrors
|
||||
{
|
||||
public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_membership_not_found",
|
||||
Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'");
|
||||
|
||||
public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_not_found",
|
||||
Message: $"Whiteboard with id '{whiteboardId}' not found.");
|
||||
}
|
||||
@@ -4,8 +4,10 @@ using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.External;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard;
|
||||
|
||||
@@ -37,14 +39,14 @@ public class KickUserFromWhiteboardCommandHandler : ICommandHandler<KickUserFrom
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
whiteboard.KickUser(_userContext.GetCurrentUserId(), membership);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||
|
||||
public record RejectUserRequestToJoinCommand(string WhiteboardId, string UserId): ICommand;
|
||||
@@ -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.RejectUserRequestToJoin;
|
||||
|
||||
public class RejectUserRequestToJoinCommandHandler : ICommandHandler<RejectUserRequestToJoinCommand>
|
||||
{
|
||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public RejectUserRequestToJoinCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork)
|
||||
{
|
||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task Handle(RejectUserRequestToJoinCommand 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.Rejected);
|
||||
|
||||
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard;
|
||||
|
||||
public static class UnbanUserFromWhiteboardCommandErrors
|
||||
{
|
||||
public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_membership_not_found",
|
||||
Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'");
|
||||
|
||||
public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId)
|
||||
=> new ValidationError(
|
||||
Code: "whiteboard_not_found",
|
||||
Message: $"Whiteboard with id '{whiteboardId}' not found.");
|
||||
}
|
||||
@@ -4,8 +4,10 @@ using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.External;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard;
|
||||
|
||||
@@ -37,14 +39,14 @@ public class UnbanUserFromWhiteboardCommandHandler : ICommandHandler<UnbanUserFr
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
whiteboard.UnbanUser(_userContext.GetCurrentUserId(), membership);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
using AipsCore.Application.Abstract.Query;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
|
||||
|
||||
public record GetMembershipStatusQuery(string WhiteboardId, string UserId): IQuery<WhiteboardMembershipStatus>;
|
||||
@@ -0,0 +1,34 @@
|
||||
using AipsCore.Application.Abstract.Query;
|
||||
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.Query.GetMembershipStatus;
|
||||
|
||||
public class GetMembershipStatusQueryHandler : IQueryHandler<GetMembershipStatusQuery, WhiteboardMembershipStatus>
|
||||
{
|
||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||
|
||||
public GetMembershipStatusQueryHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository)
|
||||
{
|
||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||
}
|
||||
|
||||
public async Task<WhiteboardMembershipStatus> Handle(GetMembershipStatusQuery query, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var userId = new UserId(query.UserId);
|
||||
var whiteboardId = new WhiteboardId(query.WhiteboardId);
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
return membership.Status;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using AipsCore.Application.Abstract.Query;
|
||||
using AipsCore.Application.Abstract.UserContext;
|
||||
using AipsCore.Domain.Models.Whiteboard.Enums;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
using AipsCore.Infrastructure.Persistence.Db;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -32,7 +33,7 @@ public class GetRecentWhiteboardsQueryHandler : IQueryHandler<GetRecentWhiteboar
|
||||
.Include(m => m.Whiteboard)
|
||||
.Where(m => (
|
||||
m.UserId == userIdGuid &&
|
||||
m.IsBanned == false &&
|
||||
m.Status != WhiteboardMembershipStatus.Banned &&
|
||||
m.Whiteboard != null &&
|
||||
m.Whiteboard.State != WhiteboardState.Deleted
|
||||
))
|
||||
|
||||
@@ -34,6 +34,7 @@ public class GetWhiteboardInfoRTQueryHandler
|
||||
return _context.Whiteboards
|
||||
.Where(w => w.Id == whiteboardId)
|
||||
.Include(w => w.Memberships)
|
||||
.ThenInclude(m => m.User)
|
||||
.Include(w => w.Owner)
|
||||
.Include(w => w.Shapes);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
||||
|
||||
public record CreateWhiteboardMembershipCommand(
|
||||
string WhiteboardId,
|
||||
bool IsBanned,
|
||||
bool EditingEnabled,
|
||||
bool CanJoin)
|
||||
: ICommand<WhiteboardMembershipId>;
|
||||
@@ -1,42 +0,0 @@
|
||||
using AipsCore.Application.Abstract.Command;
|
||||
using AipsCore.Application.Abstract.UserContext;
|
||||
using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
||||
|
||||
public class CreateWhiteboardMembershipCommandHandler : ICommandHandler<CreateWhiteboardMembershipCommand, WhiteboardMembershipId>
|
||||
{
|
||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||
private readonly IUserContext _userContext;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public CreateWhiteboardMembershipCommandHandler(
|
||||
IWhiteboardMembershipRepository whiteboardMembershipRepository,
|
||||
IUserContext userContext,
|
||||
IUnitOfWork unitOfWork)
|
||||
{
|
||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||
_userContext = userContext;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<WhiteboardMembershipId> Handle(CreateWhiteboardMembershipCommand command, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var userId = _userContext.GetCurrentUserId();
|
||||
|
||||
var whiteboardMembership = Domain.Models.WhiteboardMembership.WhiteboardMembership.Create(
|
||||
command.WhiteboardId,
|
||||
userId.IdValue,
|
||||
command.IsBanned,
|
||||
command.EditingEnabled,
|
||||
command.CanJoin,
|
||||
DateTime.UtcNow);
|
||||
|
||||
await _whiteboardMembershipRepository.SaveAsync(whiteboardMembership, cancellationToken);
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return whiteboardMembership.Id;
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@ namespace AipsCore.Domain.Models.Whiteboard.External;
|
||||
public interface IWhiteboardRepository
|
||||
: IAbstractRepository<Whiteboard, WhiteboardId>, ISoftDeletableRepository<WhiteboardId>
|
||||
{
|
||||
Task<bool> WhiteboardCodeExists(WhiteboardCode whiteboardCode);
|
||||
Task<bool> WhiteboardCodeExistsAsync(WhiteboardCode whiteboardCode);
|
||||
Task<Whiteboard?> GetByCodeAsync(WhiteboardCode whiteboardCode, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -7,6 +7,38 @@ namespace AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
|
||||
public class WhiteboardErrors : AbstractErrors<Whiteboard, WhiteboardId>
|
||||
{
|
||||
public static ValidationError NotFound(WhiteboardCode whiteboardCode)
|
||||
{
|
||||
const string code = "not_found";
|
||||
string message = $"Whiteboard with code '{whiteboardCode.CodeValue}' was not found!";
|
||||
|
||||
return CreateValidationError(code,message);
|
||||
}
|
||||
|
||||
public static ValidationError CannotJoin(WhiteboardCode whiteboardCode)
|
||||
{
|
||||
const string code = "cannot_join_whiteboard";
|
||||
string message = $"Cannot join the whiteboard with code '{whiteboardCode.CodeValue}'";
|
||||
|
||||
return CreateValidationError(code,message);
|
||||
}
|
||||
|
||||
public static ValidationError UserBanned(UserId userId)
|
||||
{
|
||||
const string code = "user_banned_from_whiteboard";
|
||||
string message = $"User with id '{userId}' is banned from this whiteboard.";
|
||||
|
||||
return CreateValidationError(code,message);
|
||||
}
|
||||
|
||||
public static ValidationError UserAlreadyTryingToJoin(UserId userId)
|
||||
{
|
||||
const string code = "user_already_trying_to_join_whiteboard";
|
||||
string message = $"User with id '{userId}' is already trying to join the whiteboard.";
|
||||
|
||||
return CreateValidationError(code,message);
|
||||
}
|
||||
|
||||
public static ValidationError UserAlreadyAdded(UserId userId)
|
||||
{
|
||||
string code = "user_already_added";
|
||||
|
||||
@@ -36,7 +36,7 @@ public record WhiteboardCode : AbstractValueObject
|
||||
{
|
||||
whiteboardCode = Generate();
|
||||
|
||||
codeExists = await whiteboardRepository.WhiteboardCodeExists(whiteboardCode);
|
||||
codeExists = await whiteboardRepository.WhiteboardCodeExistsAsync(whiteboardCode);
|
||||
} while (codeExists);
|
||||
|
||||
return whiteboardCode;
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
using System.Runtime.InteropServices.Swift;
|
||||
using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.Enums;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Domain.Models.Whiteboard;
|
||||
|
||||
public partial class Whiteboard : DomainModel<WhiteboardId>
|
||||
public partial class Whiteboard
|
||||
{
|
||||
public async Task AddUserAsync(
|
||||
User.User user,
|
||||
IWhiteboardMembershipRepository membershipRepository,
|
||||
CancellationToken cancellationToken = default)
|
||||
public WhiteboardMembership.WhiteboardMembership RequestJoin(UserId userId)
|
||||
{
|
||||
var membership
|
||||
= await membershipRepository.GetByWhiteboardAndUserAsync(this.Id, user.Id, cancellationToken);
|
||||
|
||||
if (membership is not null)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.UserAlreadyAdded(user.Id));
|
||||
return WhiteboardMembership.WhiteboardMembership.Create(
|
||||
Id.IdValue,
|
||||
userId.IdValue,
|
||||
false,
|
||||
DetermineJoinStatus(),
|
||||
DateTime.UtcNow);
|
||||
}
|
||||
|
||||
membership = WhiteboardMembership.WhiteboardMembership.Create(
|
||||
this.Id.IdValue,
|
||||
user.Id.IdValue,
|
||||
false,
|
||||
false,
|
||||
this.GetCanJoin(),
|
||||
DateTime.UtcNow
|
||||
);
|
||||
|
||||
await membershipRepository.AddAsync(membership, cancellationToken);
|
||||
public void RequestReJoin(WhiteboardMembership.WhiteboardMembership membership)
|
||||
{
|
||||
switch (membership.Status)
|
||||
{
|
||||
case WhiteboardMembershipStatus.Banned:
|
||||
throw new ValidationException(WhiteboardErrors.UserBanned(membership.UserId));
|
||||
case WhiteboardMembershipStatus.Pending:
|
||||
throw new ValidationException(WhiteboardErrors.UserAlreadyTryingToJoin(membership.UserId));
|
||||
case WhiteboardMembershipStatus.Accepted:
|
||||
break;
|
||||
default:
|
||||
membership.UpdateStatus(DetermineJoinStatus());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetCanJoin()
|
||||
private WhiteboardMembershipStatus DetermineJoinStatus()
|
||||
{
|
||||
return this.JoinPolicy == WhiteboardJoinPolicy.FreeToJoin;
|
||||
return JoinPolicy switch
|
||||
{
|
||||
WhiteboardJoinPolicy.FreeToJoin => WhiteboardMembershipStatus.Accepted,
|
||||
WhiteboardJoinPolicy.RequestToJoin => WhiteboardMembershipStatus.Pending,
|
||||
WhiteboardJoinPolicy.Private => throw new ValidationException(WhiteboardErrors.CannotJoin(Code)),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
|
||||
namespace AipsCore.Domain.Models.Whiteboard;
|
||||
|
||||
public partial class Whiteboard
|
||||
{
|
||||
public void BanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||
{
|
||||
if (WhiteboardOwnerId != currentUserId)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanBanOtherUsers(currentUserId));
|
||||
}
|
||||
|
||||
whiteboardMembership.Ban();
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,11 @@ public partial class Whiteboard
|
||||
{
|
||||
public bool CanUserDelete(UserId userId)
|
||||
{
|
||||
return WhiteboardOwnerId.IdValue == userId.IdValue;
|
||||
return IsOwner(userId);
|
||||
}
|
||||
|
||||
public bool ShouldRequestToJoin(UserId userId)
|
||||
{
|
||||
return !IsOwner(userId);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
|
||||
namespace AipsCore.Domain.Models.Whiteboard;
|
||||
|
||||
public partial class Whiteboard
|
||||
{
|
||||
public void KickUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||
{
|
||||
if (WhiteboardOwnerId != currentUserId)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanKickOtherUsers(currentUserId));
|
||||
}
|
||||
|
||||
whiteboardMembership.Kick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
|
||||
namespace AipsCore.Domain.Models.Whiteboard;
|
||||
|
||||
public partial class Whiteboard
|
||||
{
|
||||
public void BanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||
{
|
||||
if (IsOwner(currentUserId))
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanBanOtherUsers(currentUserId));
|
||||
}
|
||||
|
||||
whiteboardMembership.Ban();
|
||||
}
|
||||
|
||||
public void UnbanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||
{
|
||||
if (IsOwner(currentUserId))
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanUnbanOtherUsers(currentUserId));
|
||||
}
|
||||
|
||||
whiteboardMembership.Unban();
|
||||
}
|
||||
|
||||
public void KickUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||
{
|
||||
if (IsOwner(currentUserId))
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanKickOtherUsers(currentUserId));
|
||||
}
|
||||
|
||||
whiteboardMembership.Kick();
|
||||
}
|
||||
|
||||
private bool IsOwner(UserId userId)
|
||||
{
|
||||
return WhiteboardOwnerId.IdValue == userId.IdValue;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
|
||||
namespace AipsCore.Domain.Models.Whiteboard;
|
||||
|
||||
public partial class Whiteboard
|
||||
{
|
||||
public void UnbanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||
{
|
||||
if (WhiteboardOwnerId != currentUserId)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanUnbanOtherUsers(currentUserId));
|
||||
}
|
||||
|
||||
whiteboardMembership.Unban();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
public enum WhiteboardMembershipStatus
|
||||
{
|
||||
Pending,
|
||||
Accepted,
|
||||
Active,
|
||||
Inactive,
|
||||
Rejected,
|
||||
Cancelled,
|
||||
Kicked,
|
||||
Banned
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using AipsCore.Domain.Abstract.Validation;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
public class WhiteboardMembershipErrors : AbstractErrors<WhiteboardMembership, WhiteboardMembershipId>
|
||||
{
|
||||
public static ValidationError NotFound(WhiteboardId whiteboardId, UserId userId)
|
||||
{
|
||||
const string code = "whiteboard_membership_not_found";
|
||||
string message = $"Whiteboard membership with whiteboard id {whiteboardId.IdValue} and user id {userId.IdValue} not found.";
|
||||
|
||||
return CreateValidationError(code, message);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using AipsCore.Domain.Abstract.Rule;
|
||||
using AipsCore.Domain.Abstract.ValueObject;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
public record WhiteboardMembershipCanJoin : AbstractValueObject
|
||||
{
|
||||
public bool CanJoinValue { get; init; }
|
||||
|
||||
public WhiteboardMembershipCanJoin(bool CanJoinValue)
|
||||
{
|
||||
this.CanJoinValue = CanJoinValue;
|
||||
Validate();
|
||||
}
|
||||
|
||||
protected override ICollection<IRule> GetValidationRules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using AipsCore.Domain.Abstract.Rule;
|
||||
using AipsCore.Domain.Abstract.ValueObject;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
public record WhiteboardMembershipIsBanned : AbstractValueObject
|
||||
{
|
||||
public bool IsBannedValue { get; init; }
|
||||
|
||||
public WhiteboardMembershipIsBanned(bool IsBannedValue)
|
||||
{
|
||||
this.IsBannedValue = IsBannedValue;
|
||||
Validate();
|
||||
}
|
||||
|
||||
protected override ICollection<IRule> GetValidationRules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||
|
||||
public partial class WhiteboardMembership
|
||||
{
|
||||
public void Ban()
|
||||
{
|
||||
IsBanned = new WhiteboardMembershipIsBanned(true);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||
|
||||
public partial class WhiteboardMembership
|
||||
{
|
||||
public void Kick()
|
||||
{
|
||||
CanJoin = new WhiteboardMembershipCanJoin(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||
|
||||
public partial class WhiteboardMembership
|
||||
{
|
||||
public void Ban()
|
||||
{
|
||||
Status = WhiteboardMembershipStatus.Banned;
|
||||
}
|
||||
|
||||
public void Unban()
|
||||
{
|
||||
Status = WhiteboardMembershipStatus.Cancelled;
|
||||
}
|
||||
|
||||
public void Kick()
|
||||
{
|
||||
Status = WhiteboardMembershipStatus.Kicked;
|
||||
}
|
||||
|
||||
public void UpdateStatus(WhiteboardMembershipStatus newStatus)
|
||||
{
|
||||
Status = newStatus;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||
|
||||
public partial class WhiteboardMembership
|
||||
{
|
||||
public void Unban()
|
||||
{
|
||||
IsBanned = new WhiteboardMembershipIsBanned(false);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||
@@ -9,97 +10,84 @@ public partial class WhiteboardMembership : DomainModel<WhiteboardMembershipId>
|
||||
{
|
||||
public WhiteboardId WhiteboardId { get; private set; }
|
||||
public UserId UserId { get; private set; }
|
||||
public WhiteboardMembershipIsBanned IsBanned { get; private set; }
|
||||
public WhiteboardMembershipEditingEnabled EditingEnabled { get; private set; }
|
||||
public WhiteboardMembershipCanJoin CanJoin { get; private set; }
|
||||
public WhiteboardMembershipStatus Status { get; private set; }
|
||||
public WhiteboardMembershipLastInteractedAt LastInteractedAt { get; private set; }
|
||||
|
||||
public WhiteboardMembership(
|
||||
WhiteboardMembershipId id,
|
||||
Whiteboard.Whiteboard owner,
|
||||
Whiteboard.Whiteboard whiteboard,
|
||||
User.User user,
|
||||
WhiteboardMembershipIsBanned isBanned,
|
||||
WhiteboardMembershipEditingEnabled editingEnabled,
|
||||
WhiteboardMembershipCanJoin canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
||||
: base(id)
|
||||
{
|
||||
WhiteboardId = owner.Id;
|
||||
WhiteboardId = whiteboard.Id;
|
||||
UserId = user.Id;
|
||||
IsBanned = isBanned;
|
||||
EditingEnabled = editingEnabled;
|
||||
CanJoin = canJoin;
|
||||
Status = status;
|
||||
LastInteractedAt = lastInteractedAt;
|
||||
}
|
||||
|
||||
public WhiteboardMembership(
|
||||
WhiteboardMembershipId id,
|
||||
WhiteboardId ownerId,
|
||||
WhiteboardId whiteboardId,
|
||||
UserId userId,
|
||||
WhiteboardMembershipIsBanned isBanned,
|
||||
WhiteboardMembershipEditingEnabled editingEnabled,
|
||||
WhiteboardMembershipCanJoin canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
||||
: base(id)
|
||||
{
|
||||
WhiteboardId = ownerId;
|
||||
WhiteboardId = whiteboardId;
|
||||
UserId = userId;
|
||||
IsBanned = isBanned;
|
||||
EditingEnabled = editingEnabled;
|
||||
CanJoin = canJoin;
|
||||
Status = status;
|
||||
LastInteractedAt = lastInteractedAt;
|
||||
}
|
||||
|
||||
public static WhiteboardMembership Create(
|
||||
string id,
|
||||
string ownerId,
|
||||
string whiteboardId,
|
||||
string userId,
|
||||
bool isBanned,
|
||||
bool editingEnabled,
|
||||
bool canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
DateTime lastInteractedAt)
|
||||
{
|
||||
var whiteboardMembershipId = new WhiteboardMembershipId(id);
|
||||
var whiteboardId = new WhiteboardId(ownerId);
|
||||
var whiteboardIdVo = new WhiteboardId(whiteboardId);
|
||||
var userIdVo = new UserId(userId);
|
||||
var isBannedVo = new WhiteboardMembershipIsBanned(isBanned);
|
||||
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
||||
var canJoinVo = new WhiteboardMembershipCanJoin(canJoin);
|
||||
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
||||
|
||||
return new WhiteboardMembership(
|
||||
whiteboardMembershipId,
|
||||
whiteboardId,
|
||||
whiteboardIdVo,
|
||||
userIdVo,
|
||||
isBannedVo,
|
||||
editingEnabledVo,
|
||||
canJoinVo,
|
||||
status,
|
||||
lastInteractedAtVo);
|
||||
}
|
||||
|
||||
public static WhiteboardMembership Create(
|
||||
string ownerId,
|
||||
string whiteboardId,
|
||||
string userId,
|
||||
bool isBanned,
|
||||
bool editingEnabled,
|
||||
bool canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
DateTime lastInteractedAt)
|
||||
{
|
||||
var whiteboardMembershipId = WhiteboardMembershipId.Any();
|
||||
var whiteboardId = new WhiteboardId(ownerId);
|
||||
var whiteboardIdVo = new WhiteboardId(whiteboardId);
|
||||
var userIdVo = new UserId(userId);
|
||||
var isBannedVo = new WhiteboardMembershipIsBanned(isBanned);
|
||||
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
||||
var canJoinVo = new WhiteboardMembershipCanJoin(canJoin);
|
||||
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
||||
|
||||
return new WhiteboardMembership(
|
||||
whiteboardMembershipId,
|
||||
whiteboardId,
|
||||
whiteboardIdVo,
|
||||
userIdVo,
|
||||
isBannedVo,
|
||||
editingEnabledVo,
|
||||
canJoinVo,
|
||||
status,
|
||||
lastInteractedAtVo);
|
||||
}
|
||||
}
|
||||
503
dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260226153018_ReworkedMembership.Designer.cs
generated
Normal file
503
dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260226153018_ReworkedMembership.Designer.cs
generated
Normal file
@@ -0,0 +1,503 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using AipsCore.Infrastructure.Persistence.Db;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
{
|
||||
[DbContext(typeof(AipsDbContext))]
|
||||
[Migration("20260226153018_ReworkedMembership")]
|
||||
partial class ReworkedMembership
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("RefreshTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Shape.Shape", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("AuthorId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)");
|
||||
|
||||
b.Property<int?>("EndPositionX")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("EndPositionY")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PositionX")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PositionY")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("TextSize")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("TextValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("Thickness")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("WhiteboardId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("WhiteboardId");
|
||||
|
||||
b.ToTable("Shapes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.User.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("character varying(8)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("JoinPolicy")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MaxParticipants")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("OwnerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("State")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("Whiteboards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.WhiteboardMembership.WhiteboardMembership", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("EditingEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("LastInteractedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("WhiteboardId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("WhiteboardId");
|
||||
|
||||
b.ToTable("WhiteboardMemberships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Shape.Shape", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "Author")
|
||||
.WithMany("Shapes")
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", "Whiteboard")
|
||||
.WithMany("Shapes")
|
||||
.HasForeignKey("WhiteboardId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Whiteboard");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "Owner")
|
||||
.WithMany("Whiteboards")
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.WhiteboardMembership.WhiteboardMembership", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
||||
.WithMany("Memberships")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", "Whiteboard")
|
||||
.WithMany("Memberships")
|
||||
.HasForeignKey("WhiteboardId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
|
||||
b.Navigation("Whiteboard");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.User.User", b =>
|
||||
{
|
||||
b.Navigation("Memberships");
|
||||
|
||||
b.Navigation("Shapes");
|
||||
|
||||
b.Navigation("Whiteboards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b =>
|
||||
{
|
||||
b.Navigation("Memberships");
|
||||
|
||||
b.Navigation("Shapes");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ReworkedMembership : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CanJoin",
|
||||
table: "WhiteboardMemberships");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsBanned",
|
||||
table: "WhiteboardMemberships");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Status",
|
||||
table: "WhiteboardMemberships",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Status",
|
||||
table: "WhiteboardMemberships");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "CanJoin",
|
||||
table: "WhiteboardMemberships",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsBanned",
|
||||
table: "WhiteboardMemberships",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Authentication.RefreshToken.RefreshToken", b =>
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -214,18 +214,15 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("CanJoin")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("EditingEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsBanned")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("LastInteractedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
@@ -371,7 +368,7 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Authentication.RefreshToken.RefreshToken", b =>
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
||||
.WithMany()
|
||||
|
||||
@@ -58,11 +58,18 @@ public class WhiteboardRepository
|
||||
entity.State = model.State;
|
||||
}
|
||||
|
||||
public async Task<bool> WhiteboardCodeExists(WhiteboardCode whiteboardCode)
|
||||
public async Task<bool> WhiteboardCodeExistsAsync(WhiteboardCode whiteboardCode)
|
||||
{
|
||||
return await Context.Whiteboards.AnyAsync(w => w.Code == whiteboardCode.CodeValue);
|
||||
}
|
||||
|
||||
public async Task<Domain.Models.Whiteboard.Whiteboard?> GetByCodeAsync(WhiteboardCode whiteboardCode, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var entity = await Context.Whiteboards.FirstOrDefaultAsync(w => w.Code == whiteboardCode.CodeValue, cancellationToken);
|
||||
|
||||
return entity != null ? MapToModel(entity) : null;
|
||||
}
|
||||
|
||||
public async Task SoftDeleteAsync(WhiteboardId id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var entity = await Context.Whiteboards.FindAsync([new Guid(id.IdValue)], cancellationToken);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Infrastructure.Persistence.WhiteboardMembership;
|
||||
|
||||
@@ -15,11 +16,9 @@ public class WhiteboardMembership
|
||||
|
||||
public User.User? User { get; set; } = null!;
|
||||
|
||||
public bool IsBanned { get; set; }
|
||||
|
||||
public bool EditingEnabled { get; set; }
|
||||
|
||||
public bool CanJoin { get; set; }
|
||||
public WhiteboardMembershipStatus Status { get; set; }
|
||||
|
||||
public DateTime LastInteractedAt { get; set; }
|
||||
}
|
||||
@@ -24,9 +24,8 @@ public class WhiteboardMembershipRepository
|
||||
entity.Id.ToString(),
|
||||
entity.WhiteboardId.ToString(),
|
||||
entity.UserId.ToString(),
|
||||
entity.IsBanned,
|
||||
entity.EditingEnabled,
|
||||
entity.CanJoin,
|
||||
entity.Status,
|
||||
entity.LastInteractedAt
|
||||
);
|
||||
}
|
||||
@@ -38,18 +37,16 @@ public class WhiteboardMembershipRepository
|
||||
Id = new Guid(model.Id.IdValue),
|
||||
WhiteboardId = new Guid(model.WhiteboardId.IdValue),
|
||||
UserId = new Guid(model.UserId.IdValue),
|
||||
IsBanned = model.IsBanned.IsBannedValue,
|
||||
EditingEnabled = model.EditingEnabled.EditingEnabledValue,
|
||||
CanJoin = model.CanJoin.CanJoinValue,
|
||||
Status = model.Status,
|
||||
LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateEntity(WhiteboardMembership entity, Domain.Models.WhiteboardMembership.WhiteboardMembership model)
|
||||
{
|
||||
entity.IsBanned = model.IsBanned.IsBannedValue;
|
||||
entity.EditingEnabled = model.EditingEnabled.EditingEnabledValue;
|
||||
entity.CanJoin = model.CanJoin.CanJoinValue;
|
||||
entity.Status = model.Status;
|
||||
entity.LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
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.Memberships;
|
||||
using AipsRT.Model.Whiteboard;
|
||||
using AipsRT.Model.Whiteboard.Shapes;
|
||||
using AipsRT.Model.Whiteboard.Structs;
|
||||
@@ -15,27 +19,87 @@ public class WhiteboardHub : Hub
|
||||
{
|
||||
private readonly WhiteboardManager _whiteboardManager;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly MembershipService _membershipService;
|
||||
|
||||
public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService)
|
||||
public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService, MembershipService membershipService)
|
||||
{
|
||||
_whiteboardManager = whiteboardManager;
|
||||
_messagingService = messagingService;
|
||||
_membershipService = membershipService;
|
||||
}
|
||||
|
||||
public async Task JoinWhiteboard(Guid whiteboardId)
|
||||
{
|
||||
if (!_whiteboardManager.WhiteboardExists(whiteboardId))
|
||||
{
|
||||
await _whiteboardManager.LoadWhiteboard(whiteboardId);
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString());
|
||||
|
||||
var whiteboard = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
||||
|
||||
var userId = CurrentUserId;
|
||||
var ownerId = whiteboard.OwnerId;
|
||||
|
||||
WhiteboardMembershipStatus status;
|
||||
|
||||
if (userId == ownerId)
|
||||
{
|
||||
status = WhiteboardMembershipStatus.Accepted;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = await _membershipService.GetMembershipStatus(whiteboardId, userId);
|
||||
}
|
||||
|
||||
if (status == WhiteboardMembershipStatus.Accepted)
|
||||
{
|
||||
_whiteboardManager.AddUserToWhiteboard(userId, whiteboardId);
|
||||
|
||||
var state = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
||||
|
||||
_whiteboardManager.AddUserToWhiteboard(Guid.Parse(Context.UserIdentifier!), whiteboardId);
|
||||
|
||||
await Clients.Caller.SendAsync("InitWhiteboard", state);
|
||||
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId)
|
||||
.SendAsync("Joined", Context.UserIdentifier!);
|
||||
|
||||
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId).SendAsync("Joined", Context.UserIdentifier!);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Clients.Caller.SendAsync("WaitingForApproval", userId.ToString());
|
||||
|
||||
var user = whiteboard.Users.First(u => u.UserId == userId);
|
||||
|
||||
await Clients.User(ownerId.ToString()).SendAsync("UserWaitingForApproval", user);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AcceptUser(Guid targetUserId)
|
||||
{
|
||||
var whiteboard = CurrentWhiteboard;
|
||||
|
||||
await _messagingService.AcceptedUser(new AcceptUserRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), targetUserId.ToString()));
|
||||
|
||||
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()));
|
||||
|
||||
await Clients.User(targetUserId.ToString()).SendAsync("Rejected");
|
||||
}
|
||||
|
||||
public async Task CancelJoinRequest()
|
||||
{
|
||||
var userId = CurrentUserId;
|
||||
var whiteboard = _whiteboardManager.GetWhiteboardForUser(userId);
|
||||
|
||||
if (whiteboard != null)
|
||||
{
|
||||
await Clients.User(whiteboard.OwnerId.ToString()).SendAsync("UserCanceledJoinRequest", userId.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LeaveWhiteboard(Guid whiteboardId)
|
||||
|
||||
21
dotnet/AipsRT/Model/Memberships/MembershipService.cs
Normal file
21
dotnet/AipsRT/Model/Memberships/MembershipService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using AipsCore.Application.Abstract;
|
||||
using AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsRT.Model.Memberships;
|
||||
|
||||
public class MembershipService
|
||||
{
|
||||
private readonly IDispatcher _dispatcher;
|
||||
|
||||
public MembershipService(IDispatcher dispatcher)
|
||||
{
|
||||
_dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
public async Task<WhiteboardMembershipStatus> GetMembershipStatus(Guid whiteboardId, Guid userId)
|
||||
{
|
||||
var query = new GetMembershipStatusQuery(whiteboardId.ToString(), userId.ToString());
|
||||
return await _dispatcher.Execute(query);
|
||||
}
|
||||
}
|
||||
17
dotnet/AipsRT/Model/Users/User.cs
Normal file
17
dotnet/AipsRT/Model/Users/User.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace AipsRT.Model.Users;
|
||||
|
||||
public class User
|
||||
{
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
public string Username { get; private set; }
|
||||
|
||||
public string Email { get; private set; }
|
||||
|
||||
public User(Guid userId, string username, string email)
|
||||
{
|
||||
UserId = userId;
|
||||
Username = username;
|
||||
Email = email;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using AipsCore.Application.Abstract;
|
||||
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;
|
||||
using AipsRT.Model.Users;
|
||||
|
||||
namespace AipsRT.Model.Whiteboard;
|
||||
|
||||
@@ -28,6 +30,11 @@ public class GetWhiteboardService
|
||||
OwnerId = entity.OwnerId,
|
||||
};
|
||||
|
||||
foreach (var membership in entity.Memberships)
|
||||
{
|
||||
whiteboard.AddUser(new User(membership.UserId, membership.User!.UserName!, membership.User.Email!));
|
||||
}
|
||||
|
||||
foreach (var shape in entity.Shapes)
|
||||
{
|
||||
switch (shape.Type)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AipsRT.Model.Whiteboard.Shapes;
|
||||
using AipsRT.Model.Users;
|
||||
|
||||
namespace AipsRT.Model.Whiteboard;
|
||||
|
||||
@@ -8,6 +9,8 @@ public class Whiteboard
|
||||
|
||||
public Guid OwnerId { get; set; }
|
||||
|
||||
public List<User> Users { get; } = [];
|
||||
|
||||
public List<Shape> Shapes { get; } = [];
|
||||
|
||||
public List<Rectangle> Rectangles { get; } = [];
|
||||
@@ -38,4 +41,6 @@ public class Whiteboard
|
||||
Shapes.Add(shape);
|
||||
TextShapes.Add(shape);
|
||||
}
|
||||
|
||||
public void AddUser(User user) => Users.Add(user);
|
||||
}
|
||||
@@ -47,9 +47,9 @@ 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)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using AipsCore.Application.Common.Message.ErrorMessage;
|
||||
using AipsCore.Infrastructure.DI;
|
||||
using AipsRT.Hubs;
|
||||
using AipsRT.Model.Memberships;
|
||||
using AipsRT.Model.Whiteboard;
|
||||
using AipsRT.Services;
|
||||
using AipsRT.Services.Interfaces;
|
||||
@@ -21,6 +22,8 @@ builder.Services.AddAipsMessageHandlers();
|
||||
builder.Services.AddSingleton<IErrorMessageHandleStrategy, RtErrorHandleStrategy>();
|
||||
builder.Services.AddHostedService<ErrorSubscriberBackgroundService>();
|
||||
|
||||
builder.Services.AddTransient<MembershipService>();
|
||||
|
||||
builder.Services.AddScoped<GetWhiteboardService>();
|
||||
builder.Services.AddSingleton<WhiteboardManager>();
|
||||
builder.Services.AddSingleton<IMessagingService, MessagingService>();
|
||||
|
||||
@@ -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(Guid whiteboardId, MoveShapeCommand moveShape);
|
||||
|
||||
Task AcceptedUser(AcceptUserRequestToJoinCommand command);
|
||||
Task RejectedUser(RejectUserRequestToJoinCommand command);
|
||||
}
|
||||
@@ -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(whiteboardId, 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);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using AipsCore.Application.Abstract;
|
||||
using AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard;
|
||||
using AipsCore.Application.Models.Whiteboard.Command.DeleteWhiteboard;
|
||||
using AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||
using AipsCore.Application.Models.Whiteboard.Query.GetRecentWhiteboards;
|
||||
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboard;
|
||||
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardHistory;
|
||||
using AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Whiteboard = AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard;
|
||||
@@ -69,7 +69,7 @@ public class WhiteboardController : ControllerBase
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("join")]
|
||||
public async Task<ActionResult> JoinWhiteboard(CreateWhiteboardMembershipCommand command, CancellationToken cancellationToken)
|
||||
public async Task<ActionResult<JoinWithCodeDto>> JoinWhiteboardWithCode(JoinWithCodeCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _dispatcher.Execute(command, cancellationToken);
|
||||
return Ok(result);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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;
|
||||
|
||||
namespace AipsWorker.Messages;
|
||||
|
||||
@@ -17,7 +19,9 @@ public class MessageTypesProvider : IMessageTypesProvider
|
||||
typeof(AddLineMessage),
|
||||
typeof(AddRectangleMessage),
|
||||
typeof(AddTextShapeMessage),
|
||||
typeof(MoveShapeMessage)
|
||||
typeof(MoveShapeMessage),
|
||||
typeof(AcceptUserRequestToJoinMessage),
|
||||
typeof(RejectUserRequestToJoinMessage)
|
||||
];
|
||||
}
|
||||
}
|
||||
150
front/bun.lock
150
front/bun.lock
@@ -152,15 +152,15 @@
|
||||
|
||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
||||
|
||||
"@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
|
||||
"@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="],
|
||||
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
|
||||
|
||||
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
|
||||
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="],
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="],
|
||||
|
||||
"@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="],
|
||||
"@eslint/js": ["@eslint/js@9.39.4", "", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="],
|
||||
|
||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
|
||||
|
||||
@@ -298,35 +298,35 @@
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@24.10.13", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg=="],
|
||||
"@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.55.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/type-utils": "8.55.0", "@typescript-eslint/utils": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ=="],
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.56.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/type-utils": "8.56.1", "@typescript-eslint/utils": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.55.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.55.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ=="],
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0" } }, "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q=="],
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
|
||||
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.55.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q=="],
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/utils": "8.55.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g=="],
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.55.0", "", {}, "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.55.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.55.0", "@typescript-eslint/tsconfig-utils": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw=="],
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.55.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow=="],
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.56.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA=="],
|
||||
|
||||
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.4", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.2" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "vue": "^3.2.25" } }, "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ=="],
|
||||
|
||||
"@volar/language-core": ["@volar/language-core@2.4.27", "", { "dependencies": { "@volar/source-map": "2.4.27" } }, "sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ=="],
|
||||
"@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="],
|
||||
|
||||
"@volar/source-map": ["@volar/source-map@2.4.27", "", {}, "sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg=="],
|
||||
"@volar/source-map": ["@volar/source-map@2.4.28", "", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="],
|
||||
|
||||
"@volar/typescript": ["@volar/typescript@2.4.27", "", { "dependencies": { "@volar/language-core": "2.4.27", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg=="],
|
||||
"@volar/typescript": ["@volar/typescript@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="],
|
||||
|
||||
"@vue-macros/common": ["@vue-macros/common@3.1.2", "", { "dependencies": { "@vue/compiler-sfc": "^3.5.22", "ast-kit": "^2.1.2", "local-pkg": "^1.1.2", "magic-string-ast": "^1.0.2", "unplugin-utils": "^0.3.0" }, "peerDependencies": { "vue": "^2.7.0 || ^3.2.25" }, "optionalPeers": ["vue"] }, "sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng=="],
|
||||
|
||||
@@ -336,35 +336,35 @@
|
||||
|
||||
"@vue/babel-plugin-resolve-type": ["@vue/babel-plugin-resolve-type@1.5.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/parser": "^7.28.0", "@vue/compiler-sfc": "^3.5.18" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w=="],
|
||||
|
||||
"@vue/compiler-core": ["@vue/compiler-core@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.28", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ=="],
|
||||
"@vue/compiler-core": ["@vue/compiler-core@3.5.29", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.29", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw=="],
|
||||
|
||||
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.28", "", { "dependencies": { "@vue/compiler-core": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA=="],
|
||||
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.29", "", { "dependencies": { "@vue/compiler-core": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg=="],
|
||||
|
||||
"@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.28", "@vue/compiler-dom": "3.5.28", "@vue/compiler-ssr": "3.5.28", "@vue/shared": "3.5.28", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g=="],
|
||||
"@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.29", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.29", "@vue/compiler-dom": "3.5.29", "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA=="],
|
||||
|
||||
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.28", "", { "dependencies": { "@vue/compiler-dom": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g=="],
|
||||
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw=="],
|
||||
|
||||
"@vue/devtools-api": ["@vue/devtools-api@7.7.9", "", { "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g=="],
|
||||
|
||||
"@vue/devtools-core": ["@vue/devtools-core@8.0.6", "", { "dependencies": { "@vue/devtools-kit": "^8.0.6", "@vue/devtools-shared": "^8.0.6", "mitt": "^3.0.1", "nanoid": "^5.1.5", "pathe": "^2.0.3", "vite-hot-client": "^2.1.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-fN7iVtpSQQdtMORWwVZ1JiIAKriinhD+lCHqPw9Rr252ae2TczILEmW0zcAZifPW8HfYcbFkn+h7Wv6kQQCayw=="],
|
||||
"@vue/devtools-core": ["@vue/devtools-core@8.0.7", "", { "dependencies": { "@vue/devtools-kit": "^8.0.7", "@vue/devtools-shared": "^8.0.7" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-PmpiPxvg3Of80ODHVvyckxwEW1Z02VIAvARIZS1xegINn3VuNQLm9iHUmKD+o6cLkMNWV8OG8x7zo0kgydZgdg=="],
|
||||
|
||||
"@vue/devtools-kit": ["@vue/devtools-kit@8.0.6", "", { "dependencies": { "@vue/devtools-shared": "^8.0.6", "birpc": "^2.6.1", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^2.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-9zXZPTJW72OteDXeSa5RVML3zWDCRcO5t77aJqSs228mdopYj5AiTpihozbsfFJ0IodfNs7pSgOGO3qfCuxDtw=="],
|
||||
"@vue/devtools-kit": ["@vue/devtools-kit@8.0.7", "", { "dependencies": { "@vue/devtools-shared": "^8.0.7", "birpc": "^2.6.1", "hookable": "^5.5.3", "perfect-debounce": "^2.0.0" } }, "sha512-H6esJGHGl5q0E9iV3m2EoBQHJ+V83WMW83A0/+Fn95eZ2iIvdsq4+UCS6yT/Fdd4cGZSchx/MdWDreM3WqMsDw=="],
|
||||
|
||||
"@vue/devtools-shared": ["@vue/devtools-shared@8.0.6", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-Pp1JylTqlgMJvxW6MGyfTF8vGvlBSCAvMFaDCYa82Mgw7TT5eE5kkHgDvmOGHWeJE4zIDfCpCxHapsK2LtIAJg=="],
|
||||
"@vue/devtools-shared": ["@vue/devtools-shared@8.0.7", "", {}, "sha512-CgAb9oJH5NUmbQRdYDj/1zMiaICYSLtm+B1kxcP72LBrifGAjUmt8bx52dDH1gWRPlQgxGPqpAMKavzVirAEhA=="],
|
||||
|
||||
"@vue/eslint-config-typescript": ["@vue/eslint-config-typescript@14.6.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.35.1", "fast-glob": "^3.3.3", "typescript-eslint": "^8.35.1", "vue-eslint-parser": "^10.2.0" }, "peerDependencies": { "eslint": "^9.10.0", "eslint-plugin-vue": "^9.28.0 || ^10.0.0", "typescript": ">=4.8.4" }, "optionalPeers": ["typescript"] }, "sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ=="],
|
||||
"@vue/eslint-config-typescript": ["@vue/eslint-config-typescript@14.7.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.56.0", "fast-glob": "^3.3.3", "typescript-eslint": "^8.56.0", "vue-eslint-parser": "^10.4.0" }, "peerDependencies": { "eslint": "^9.10.0 || ^10.0.0", "eslint-plugin-vue": "^9.28.0 || ^10.0.0", "typescript": ">=4.8.4" }, "optionalPeers": ["typescript"] }, "sha512-iegbMINVc+seZ/QxtzWiOBozctrHiF2WvGedruu2EbLujg9VuU0FQiNcN2z1ycuaoKKpF4m2qzB5HDEMKbxtIg=="],
|
||||
|
||||
"@vue/language-core": ["@vue/language-core@3.2.4", "", { "dependencies": { "@volar/language-core": "2.4.27", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.0.0", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" } }, "sha512-bqBGuSG4KZM45KKTXzGtoCl9cWju5jsaBKaJJe3h5hRAAWpZUuj5G+L+eI01sPIkm4H6setKRlw7E85wLdDNew=="],
|
||||
"@vue/language-core": ["@vue/language-core@3.2.5", "", { "dependencies": { "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.0.0", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" } }, "sha512-d3OIxN/+KRedeM5wQ6H6NIpwS3P5gC9nmyaHgBk+rO6dIsjY+tOh4UlPpiZbAh3YtLdCGEX4M16RmsBqPmJV+g=="],
|
||||
|
||||
"@vue/reactivity": ["@vue/reactivity@3.5.28", "", { "dependencies": { "@vue/shared": "3.5.28" } }, "sha512-gr5hEsxvn+RNyu9/9o1WtdYdwDjg5FgjUSBEkZWqgTKlo/fvwZ2+8W6AfKsc9YN2k/+iHYdS9vZYAhpi10kNaw=="],
|
||||
"@vue/reactivity": ["@vue/reactivity@3.5.29", "", { "dependencies": { "@vue/shared": "3.5.29" } }, "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA=="],
|
||||
|
||||
"@vue/runtime-core": ["@vue/runtime-core@3.5.28", "", { "dependencies": { "@vue/reactivity": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-POVHTdbgnrBBIpnbYU4y7pOMNlPn2QVxVzkvEA2pEgvzbelQq4ZOUxbp2oiyo+BOtiYlm8Q44wShHJoBvDPAjQ=="],
|
||||
"@vue/runtime-core": ["@vue/runtime-core@3.5.29", "", { "dependencies": { "@vue/reactivity": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg=="],
|
||||
|
||||
"@vue/runtime-dom": ["@vue/runtime-dom@3.5.28", "", { "dependencies": { "@vue/reactivity": "3.5.28", "@vue/runtime-core": "3.5.28", "@vue/shared": "3.5.28", "csstype": "^3.2.3" } }, "sha512-4SXxSF8SXYMuhAIkT+eBRqOkWEfPu6nhccrzrkioA6l0boiq7sp18HCOov9qWJA5HML61kW8p/cB4MmBiG9dSA=="],
|
||||
"@vue/runtime-dom": ["@vue/runtime-dom@3.5.29", "", { "dependencies": { "@vue/reactivity": "3.5.29", "@vue/runtime-core": "3.5.29", "@vue/shared": "3.5.29", "csstype": "^3.2.3" } }, "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg=="],
|
||||
|
||||
"@vue/server-renderer": ["@vue/server-renderer@3.5.28", "", { "dependencies": { "@vue/compiler-ssr": "3.5.28", "@vue/shared": "3.5.28" }, "peerDependencies": { "vue": "3.5.28" } }, "sha512-pf+5ECKGj8fX95bNincbzJ6yp6nyzuLDhYZCeFxUNp8EBrQpPpQaLX3nNCp49+UbgbPun3CeVE+5CXVV1Xydfg=="],
|
||||
"@vue/server-renderer": ["@vue/server-renderer@3.5.29", "", { "dependencies": { "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "vue": "3.5.29" } }, "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g=="],
|
||||
|
||||
"@vue/shared": ["@vue/shared@3.5.28", "", {}, "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="],
|
||||
"@vue/shared": ["@vue/shared@3.5.29", "", {}, "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg=="],
|
||||
|
||||
"@vue/tsconfig": ["@vue/tsconfig@0.8.1", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g=="],
|
||||
|
||||
@@ -374,7 +374,7 @@
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||
|
||||
"alien-signals": ["alien-signals@3.1.2", "", {}, "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw=="],
|
||||
|
||||
@@ -460,7 +460,7 @@
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="],
|
||||
"eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="],
|
||||
|
||||
"eslint-plugin-oxlint": ["eslint-plugin-oxlint@1.42.0", "", { "dependencies": { "jsonc-parser": "^3.3.1" } }, "sha512-3hB/TDbS0f+8ZnPffl0Z/wZ7Yc5NeY5slrxG60kEWInAA9047pdqRcv+Ckk/5KiL3HS2vWtHvmkavPKSFZVKxA=="],
|
||||
|
||||
@@ -592,7 +592,7 @@
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
"minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
|
||||
|
||||
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||
|
||||
@@ -776,7 +776,7 @@
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.55.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.55.0", "@typescript-eslint/parser": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/utils": "8.55.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw=="],
|
||||
"typescript-eslint": ["typescript-eslint@8.56.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.1", "@typescript-eslint/parser": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ=="],
|
||||
|
||||
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
|
||||
|
||||
@@ -806,19 +806,19 @@
|
||||
|
||||
"vite-plugin-inspect": ["vite-plugin-inspect@11.3.3", "", { "dependencies": { "ansis": "^4.1.0", "debug": "^4.4.1", "error-stack-parser-es": "^1.0.5", "ohash": "^2.0.11", "open": "^10.2.0", "perfect-debounce": "^2.0.0", "sirv": "^3.0.1", "unplugin-utils": "^0.3.0", "vite-dev-rpc": "^1.1.0" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0-0" } }, "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA=="],
|
||||
|
||||
"vite-plugin-vue-devtools": ["vite-plugin-vue-devtools@8.0.6", "", { "dependencies": { "@vue/devtools-core": "^8.0.6", "@vue/devtools-kit": "^8.0.6", "@vue/devtools-shared": "^8.0.6", "sirv": "^3.0.2", "vite-plugin-inspect": "^11.3.3", "vite-plugin-vue-inspector": "^5.3.2" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0-0" } }, "sha512-IiTCIJDb1ZliOT8fPbYXllyfgARzz1+R1r8RN9ScGIDzAB6o8bDME1a9JjrfdSJibL7i8DIPQH+pGv0U7haBeA=="],
|
||||
"vite-plugin-vue-devtools": ["vite-plugin-vue-devtools@8.0.7", "", { "dependencies": { "@vue/devtools-core": "^8.0.7", "@vue/devtools-kit": "^8.0.7", "@vue/devtools-shared": "^8.0.7", "sirv": "^3.0.2", "vite-plugin-inspect": "^11.3.3", "vite-plugin-vue-inspector": "^5.3.2" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0-0 || ^8.0.0-0" } }, "sha512-BWj/ykGpqVAJVdPyHmSTUm44buz3jPv+6jnvuFdQSRH0kAgP1cEIE4doHiFyqHXOmuB5EQVR/nh2g9YRiRNs9g=="],
|
||||
|
||||
"vite-plugin-vue-inspector": ["vite-plugin-vue-inspector@5.3.2", "", { "dependencies": { "@babel/core": "^7.23.0", "@babel/plugin-proposal-decorators": "^7.23.0", "@babel/plugin-syntax-import-attributes": "^7.22.5", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-typescript": "^7.22.15", "@vue/babel-plugin-jsx": "^1.1.5", "@vue/compiler-dom": "^3.3.4", "kolorist": "^1.8.0", "magic-string": "^0.30.4" }, "peerDependencies": { "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" } }, "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q=="],
|
||||
|
||||
"vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
|
||||
|
||||
"vue": ["vue@3.5.28", "", { "dependencies": { "@vue/compiler-dom": "3.5.28", "@vue/compiler-sfc": "3.5.28", "@vue/runtime-dom": "3.5.28", "@vue/server-renderer": "3.5.28", "@vue/shared": "3.5.28" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg=="],
|
||||
"vue": ["vue@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/compiler-sfc": "3.5.29", "@vue/runtime-dom": "3.5.29", "@vue/server-renderer": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA=="],
|
||||
|
||||
"vue-eslint-parser": ["vue-eslint-parser@10.4.0", "", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0 || ^9.0.0", "eslint-visitor-keys": "^4.2.0 || ^5.0.0", "espree": "^10.3.0 || ^11.0.0", "esquery": "^1.6.0", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" } }, "sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg=="],
|
||||
|
||||
"vue-router": ["vue-router@5.0.2", "", { "dependencies": { "@babel/generator": "^7.28.6", "@vue-macros/common": "^3.1.1", "@vue/devtools-api": "^8.0.0", "ast-walker-scope": "^0.8.3", "chokidar": "^5.0.0", "json5": "^2.2.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "muggle-string": "^0.4.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "scule": "^1.3.0", "tinyglobby": "^0.2.15", "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1", "yaml": "^2.8.2" }, "peerDependencies": { "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.17", "pinia": "^3.0.4", "vue": "^3.5.0" }, "optionalPeers": ["@pinia/colada", "@vue/compiler-sfc", "pinia"] }, "sha512-YFhwaE5c5JcJpNB1arpkl4/GnO32wiUWRB+OEj1T0DlDxEZoOfbltl2xEwktNU/9o1sGcGburIXSpbLpPFe/6w=="],
|
||||
"vue-router": ["vue-router@5.0.3", "", { "dependencies": { "@babel/generator": "^7.28.6", "@vue-macros/common": "^3.1.1", "@vue/devtools-api": "^8.0.6", "ast-walker-scope": "^0.8.3", "chokidar": "^5.0.0", "json5": "^2.2.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "muggle-string": "^0.4.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "scule": "^1.3.0", "tinyglobby": "^0.2.15", "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1", "yaml": "^2.8.2" }, "peerDependencies": { "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.17", "pinia": "^3.0.4", "vue": "^3.5.0" }, "optionalPeers": ["@pinia/colada", "@vue/compiler-sfc", "pinia"] }, "sha512-nG1c7aAFac7NYj8Hluo68WyWfc41xkEjaR0ViLHCa3oDvTQ/nIuLJlXJX1NUPw/DXzx/8+OKMng045HHQKQKWw=="],
|
||||
|
||||
"vue-tsc": ["vue-tsc@3.2.4", "", { "dependencies": { "@volar/typescript": "2.4.27", "@vue/language-core": "3.2.4" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-xj3YCvSLNDKt1iF9OcImWHhmYcihVu9p4b9s4PGR/qp6yhW+tZJaypGxHScRyOrdnHvaOeF+YkZOdKwbgGvp5g=="],
|
||||
"vue-tsc": ["vue-tsc@3.2.5", "", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.2.5" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-/htfTCMluQ+P2FISGAooul8kO4JMheOTCbCy4M6dYnYYjqLe3BExZudAua6MSIKSFYQtFOYAll7XobYwcpokGA=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
@@ -850,14 +850,34 @@
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin/@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
"@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.55.0", "", { "dependencies": { "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0" } }, "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.55.0", "", {}, "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.55.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.55.0", "@typescript-eslint/tsconfig-utils": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw=="],
|
||||
|
||||
"@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.55.0", "", {}, "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w=="],
|
||||
|
||||
"@vue-macros/common/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.28", "@vue/compiler-dom": "3.5.28", "@vue/compiler-ssr": "3.5.28", "@vue/shared": "3.5.28", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g=="],
|
||||
|
||||
"@vue/babel-plugin-jsx/@vue/shared": ["@vue/shared@3.5.28", "", {}, "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="],
|
||||
|
||||
"@vue/babel-plugin-resolve-type/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.28", "@vue/compiler-dom": "3.5.28", "@vue/compiler-ssr": "3.5.28", "@vue/shared": "3.5.28", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g=="],
|
||||
|
||||
"@vue/devtools-api/@vue/devtools-kit": ["@vue/devtools-kit@7.7.9", "", { "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA=="],
|
||||
|
||||
"@vue/devtools-core/nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
||||
|
||||
"chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
@@ -872,9 +892,41 @@
|
||||
|
||||
"sass/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"typescript-eslint/@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
|
||||
|
||||
"vite-plugin-vue-inspector/@vue/compiler-dom": ["@vue/compiler-dom@3.5.28", "", { "dependencies": { "@vue/compiler-core": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA=="],
|
||||
|
||||
"vue-router/@vue/devtools-api": ["@vue/devtools-api@8.0.6", "", { "dependencies": { "@vue/devtools-kit": "^8.0.6" } }, "sha512-+lGBI+WTvJmnU2FZqHhEB8J1DXcvNlDeEalz77iYgOdY1jTj1ipSBaKj3sRhYcy+kqA8v/BSuvOz1XJucfQmUA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
"@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.55.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.55.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
|
||||
|
||||
"@vue-macros/common/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.28", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ=="],
|
||||
|
||||
"@vue-macros/common/@vue/compiler-sfc/@vue/compiler-dom": ["@vue/compiler-dom@3.5.28", "", { "dependencies": { "@vue/compiler-core": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA=="],
|
||||
|
||||
"@vue-macros/common/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.28", "", { "dependencies": { "@vue/compiler-dom": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g=="],
|
||||
|
||||
"@vue-macros/common/@vue/compiler-sfc/@vue/shared": ["@vue/shared@3.5.28", "", {}, "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="],
|
||||
|
||||
"@vue/babel-plugin-resolve-type/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.28", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ=="],
|
||||
|
||||
"@vue/babel-plugin-resolve-type/@vue/compiler-sfc/@vue/compiler-dom": ["@vue/compiler-dom@3.5.28", "", { "dependencies": { "@vue/compiler-core": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA=="],
|
||||
|
||||
"@vue/babel-plugin-resolve-type/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.28", "", { "dependencies": { "@vue/compiler-dom": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g=="],
|
||||
|
||||
"@vue/babel-plugin-resolve-type/@vue/compiler-sfc/@vue/shared": ["@vue/shared@3.5.28", "", {}, "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="],
|
||||
|
||||
"@vue/devtools-api/@vue/devtools-kit/@vue/devtools-shared": ["@vue/devtools-shared@7.7.9", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA=="],
|
||||
|
||||
@@ -885,5 +937,21 @@
|
||||
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||
|
||||
"sass/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"typescript-eslint/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
|
||||
|
||||
"vite-plugin-vue-inspector/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.28", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ=="],
|
||||
|
||||
"vite-plugin-vue-inspector/@vue/compiler-dom/@vue/shared": ["@vue/shared@3.5.28", "", {}, "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="],
|
||||
|
||||
"vue-router/@vue/devtools-api/@vue/devtools-kit": ["@vue/devtools-kit@8.0.6", "", { "dependencies": { "@vue/devtools-shared": "^8.0.6", "birpc": "^2.6.1", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^2.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-9zXZPTJW72OteDXeSa5RVML3zWDCRcO5t77aJqSs228mdopYj5AiTpihozbsfFJ0IodfNs7pSgOGO3qfCuxDtw=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"typescript-eslint/@typescript-eslint/parser/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||
|
||||
"vue-router/@vue/devtools-api/@vue/devtools-kit/@vue/devtools-shared": ["@vue/devtools-shared@8.0.6", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-Pp1JylTqlgMJvxW6MGyfTF8vGvlBSCAvMFaDCYa82Mgw7TT5eE5kkHgDvmOGHWeJE4zIDfCpCxHapsK2LtIAJg=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
"bootstrap": "^5.3.8",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.27",
|
||||
"vue-router": "^5.0.1"
|
||||
"vue": "^3.5.29",
|
||||
"vue-router": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node24": "^24.0.4",
|
||||
"@types/node": "^24.10.9",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue/eslint-config-typescript": "^14.6.0",
|
||||
"@types/node": "^24.12.0",
|
||||
"@vitejs/plugin-vue": "^6.0.4",
|
||||
"@vue/eslint-config-typescript": "^14.7.0",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-oxlint": "~1.42.0",
|
||||
"eslint-plugin-vue": "~10.7.0",
|
||||
"jiti": "^2.6.1",
|
||||
@@ -37,8 +37,8 @@
|
||||
"sass-embedded": "^1.97.3",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-vue-devtools": "^8.0.5",
|
||||
"vue-tsc": "^3.2.4"
|
||||
"vite-plugin-vue-devtools": "^8.0.7",
|
||||
"vue-tsc": "^3.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
|
||||
@@ -9,3 +9,14 @@ export enum WhiteboardState {
|
||||
Inactive,
|
||||
Deleted
|
||||
}
|
||||
|
||||
export enum MembershipStatus {
|
||||
Pending,
|
||||
Accepted,
|
||||
Rejected,
|
||||
Active,
|
||||
Inactive,
|
||||
Cancelled,
|
||||
Kicked,
|
||||
Banned
|
||||
}
|
||||
|
||||
@@ -37,6 +37,6 @@ export const authService = {
|
||||
const userId = raw?.userId ?? ''
|
||||
const username = raw?.userName ?? raw?.UserName ?? raw?.username ?? raw?.name ?? ''
|
||||
const email = raw?.email ?? raw?.Email ?? ''
|
||||
return { userId, username, email }
|
||||
return { userId: userId, username, email }
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SignalRService } from '@/services/signalr.ts'
|
||||
import type { Arrow, Line, MoveShapeCommand, Rectangle, TextShape, Whiteboard } from '@/types/whiteboard.ts'
|
||||
import type {User} from "@/types";
|
||||
|
||||
const client = new SignalRService(`/hubs/whiteboard`)
|
||||
|
||||
@@ -76,6 +77,38 @@ export const whiteboardHubService = {
|
||||
client.on<string>('Leaved', callback)
|
||||
},
|
||||
|
||||
onWaitingForApproval(callback: (userId: string) => void) {
|
||||
client.on<string>('WaitingForApproval', callback)
|
||||
},
|
||||
|
||||
onUserWaitingForApproval(callback: (user: User) => void) {
|
||||
client.on<User>('UserWaitingForApproval', callback)
|
||||
},
|
||||
|
||||
onAccepted(callback: () => void) {
|
||||
client.on('Accepted', callback)
|
||||
},
|
||||
|
||||
onRejected(callback: () => void) {
|
||||
client.on('Rejected', callback)
|
||||
},
|
||||
|
||||
onUserCanceledJoinRequest(callback: (userId: string) => void) {
|
||||
client.on<string>('UserCanceledJoinRequest', callback)
|
||||
},
|
||||
|
||||
async acceptUser(userId: string) {
|
||||
await client.invoke('AcceptUser', userId)
|
||||
},
|
||||
|
||||
async rejectUser(userId: string) {
|
||||
await client.invoke('RejectUser', userId)
|
||||
},
|
||||
|
||||
async cancelJoinRequest() {
|
||||
await client.invoke('CancelJoinRequest')
|
||||
},
|
||||
|
||||
offAll() {
|
||||
client.off('InitWhiteboard')
|
||||
client.off('AddedRectangle')
|
||||
@@ -85,5 +118,10 @@ export const whiteboardHubService = {
|
||||
client.off('MovedShape')
|
||||
client.off('Joined')
|
||||
client.off('Leaved')
|
||||
client.off('WaitingForApproval')
|
||||
client.off('UserWaitingForApproval')
|
||||
client.off('Accepted')
|
||||
client.off('Rejected')
|
||||
client.off('UserCanceledJoinRequest')
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type {Whiteboard} from "@/types";
|
||||
import type {JoinResult, Whiteboard} from "@/types";
|
||||
import {api} from './api'
|
||||
|
||||
export const whiteboardService = {
|
||||
@@ -22,6 +22,14 @@ export const whiteboardService = {
|
||||
|
||||
async deleteWhiteboard(id: string): Promise<void> {
|
||||
await api.delete(`/api/Whiteboard/${id}`)
|
||||
},
|
||||
|
||||
async joinWhiteboard(code: string): Promise<JoinResult> {
|
||||
const raw = await api.post<any>(`/api/Whiteboard/join`, {code: code})
|
||||
return {
|
||||
whiteboardId: raw.whiteboardId,
|
||||
status: raw.status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,14 @@ import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import type { Arrow, Line, Rectangle, Shape, ShapeTool, ShapeType, TextShape, Whiteboard } from '@/types/whiteboard.ts'
|
||||
import { whiteboardHubService } from '@/services/whiteboardHubService.ts'
|
||||
import {useWhiteboardsStore} from "@/stores/whiteboards.ts";
|
||||
import router from "@/router";
|
||||
import type {User} from "@/types";
|
||||
|
||||
export const useWhiteboardStore = defineStore('whiteboard', () => {
|
||||
const whiteboard = ref<Whiteboard | null>(null)
|
||||
const pendingUsers = ref<User[]>([])
|
||||
|
||||
const selectedTool = ref<ShapeTool>('hand')
|
||||
const isConnected = ref(false)
|
||||
const isLoading = ref(false)
|
||||
@@ -27,14 +32,24 @@ export const useWhiteboardStore = defineStore('whiteboard', () => {
|
||||
}
|
||||
})
|
||||
|
||||
async function joinWhiteboard(id: string) {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
async function initializeSession(id: string) {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
try{
|
||||
await whiteboardHubService.connect()
|
||||
isConnected.value = true
|
||||
isConnected.value = true;
|
||||
|
||||
registerHubEvents()
|
||||
|
||||
await whiteboardHubService.joinWhiteboard(id)
|
||||
} catch (e: any) {
|
||||
error.value = e?.message ?? 'Failed to join whiteboard'
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function registerHubEvents() {
|
||||
whiteboardHubService.onInitWhiteboard((wb) => {
|
||||
deselectShape()
|
||||
whiteboard.value = wb
|
||||
@@ -69,6 +84,57 @@ export const useWhiteboardStore = defineStore('whiteboard', () => {
|
||||
console.log('User left:', userId)
|
||||
})
|
||||
|
||||
whiteboardHubService.onWaitingForApproval(() => {
|
||||
const infoStore = useWhiteboardsStore()
|
||||
infoStore.startWaitingToJoin()
|
||||
})
|
||||
|
||||
whiteboardHubService.onUserWaitingForApproval((user) => {
|
||||
if (!pendingUsers.value.includes(user)) {
|
||||
pendingUsers.value.push(user)
|
||||
}
|
||||
})
|
||||
|
||||
whiteboardHubService.onAccepted(() => {
|
||||
const infoStore = useWhiteboardsStore()
|
||||
infoStore.stopWaitingToJoin()
|
||||
})
|
||||
|
||||
whiteboardHubService.onRejected(() => {
|
||||
router.push('/')
|
||||
alert('Your request to join was rejected.')
|
||||
})
|
||||
|
||||
whiteboardHubService.onUserCanceledJoinRequest((userId) => {
|
||||
pendingUsers.value = pendingUsers.value.filter(user => user.userId !== userId)
|
||||
})
|
||||
}
|
||||
|
||||
async function approveUser(userId: string) {
|
||||
await whiteboardHubService.acceptUser(userId)
|
||||
pendingUsers.value = pendingUsers.value.filter(user => user.userId !== userId)
|
||||
}
|
||||
|
||||
async function rejectUser(userId: string) {
|
||||
await whiteboardHubService.rejectUser(userId)
|
||||
pendingUsers.value = pendingUsers.value.filter(user => user.userId !== userId)
|
||||
}
|
||||
|
||||
async function cancelJoinRequest() {
|
||||
await whiteboardHubService.cancelJoinRequest()
|
||||
whiteboard.value = null
|
||||
}
|
||||
|
||||
async function joinWhiteboard(id: string) {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await whiteboardHubService.connect()
|
||||
isConnected.value = true
|
||||
|
||||
registerHubEvents()
|
||||
|
||||
await whiteboardHubService.joinWhiteboard(id)
|
||||
} catch (e: any) {
|
||||
error.value = e?.message ?? 'Failed to join whiteboard'
|
||||
@@ -184,6 +250,10 @@ export const useWhiteboardStore = defineStore('whiteboard', () => {
|
||||
toolColor,
|
||||
toolThickness,
|
||||
toolTextSize,
|
||||
pendingUsers,
|
||||
approveUser,
|
||||
rejectUser,
|
||||
cancelJoinRequest,
|
||||
joinWhiteboard,
|
||||
leaveWhiteboard,
|
||||
addRectangle,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Whiteboard } from '@/types'
|
||||
import type {JoinResult, Whiteboard} from '@/types'
|
||||
import { whiteboardService } from '@/services/whiteboardService'
|
||||
|
||||
export const useWhiteboardsStore = defineStore('whiteboards', () => {
|
||||
@@ -8,6 +8,7 @@ export const useWhiteboardsStore = defineStore('whiteboards', () => {
|
||||
const recentWhiteboards = ref<Whiteboard[]>([])
|
||||
const currentWhiteboard = ref<Whiteboard | null>(null)
|
||||
const isLoading = ref(false)
|
||||
const isWaitingToJoin = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
async function getWhiteboardHistory() {
|
||||
@@ -53,6 +54,27 @@ export const useWhiteboardsStore = defineStore('whiteboards', () => {
|
||||
return newWhiteboard.id;
|
||||
}
|
||||
|
||||
async function joinWhiteboardWithCode(code: string): Promise<JoinResult> {
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
return await whiteboardService.joinWhiteboard(code);
|
||||
} catch (err: any) {
|
||||
error.value = err.message ?? 'Failed to join whiteboard';
|
||||
throw err;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function startWaitingToJoin() {
|
||||
isWaitingToJoin.value = true;
|
||||
}
|
||||
|
||||
function stopWaitingToJoin() {
|
||||
isWaitingToJoin.value = false;
|
||||
}
|
||||
|
||||
async function deleteWhiteboard(id: string): Promise<void> {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
@@ -92,10 +114,14 @@ export const useWhiteboardsStore = defineStore('whiteboards', () => {
|
||||
ownedWhiteboards: ownedWhiteboards,
|
||||
recentWhiteboards: recentWhiteboards,
|
||||
isLoading,
|
||||
isWaitingToJoin,
|
||||
error,
|
||||
getWhiteboardHistory: getWhiteboardHistory,
|
||||
getRecentWhiteboards: getRecentWhiteboards,
|
||||
createNewWhiteboard: createNewWhiteboard,
|
||||
joinWhiteboardWithCode: joinWhiteboardWithCode,
|
||||
startWaitingToJoin: startWaitingToJoin,
|
||||
stopWaitingToJoin: stopWaitingToJoin,
|
||||
deleteWhiteboard: deleteWhiteboard,
|
||||
getCurrentWhiteboard: getCurrentWhiteboard,
|
||||
selectWhiteboard: selectWhiteboard,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {type WhiteboardJoinPolicy, WhiteboardState} from "@/enums";
|
||||
import {MembershipStatus, type WhiteboardJoinPolicy, WhiteboardState} from "@/enums";
|
||||
|
||||
export interface User {
|
||||
userId: string
|
||||
@@ -22,6 +22,11 @@ export interface AuthResponse {
|
||||
refreshToken: string
|
||||
}
|
||||
|
||||
export interface JoinResult {
|
||||
whiteboardId: string
|
||||
status: MembershipStatus
|
||||
}
|
||||
|
||||
export interface Whiteboard {
|
||||
id: string
|
||||
ownerId: string
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {ref} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
import WhiteboardHistorySidebar from '@/components/WhiteboardHistorySidebar.vue'
|
||||
import RecentWhiteboardsPanel from '@/components/RecentWhiteboardsPanel.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useWhiteboardsStore } from "@/stores/whiteboards.ts";
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useWhiteboardsStore} from "@/stores/whiteboards.ts";
|
||||
import {MembershipStatus} from "@/enums";
|
||||
|
||||
const auth = useAuthStore()
|
||||
const whiteboards = useWhiteboardsStore()
|
||||
@@ -33,6 +34,27 @@ async function handleCreateNewWhiteboard() {
|
||||
}
|
||||
}
|
||||
|
||||
async function joinWithCode() {
|
||||
if (joinCode.value.length !== 8) {
|
||||
alert('Please enter a valid 8-digit code.')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const joinResult = await whiteboards.joinWhiteboardWithCode(joinCode.value)
|
||||
|
||||
if (joinResult.status === MembershipStatus.Pending) {
|
||||
whiteboards.startWaitingToJoin()
|
||||
} else {
|
||||
whiteboards.stopWaitingToJoin()
|
||||
}
|
||||
|
||||
await router.push({ name: 'whiteboard', params: { id: joinResult.whiteboardId } })
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -80,7 +102,7 @@ async function handleCreateNewWhiteboard() {
|
||||
pattern="[0-9]*"
|
||||
@input="joinCode = joinCode.replace(/\D/g, '')"
|
||||
/>
|
||||
<button class="btn btn-primary w-75 mt-2 d-block mx-auto">Join with code</button>
|
||||
<button class="btn btn-primary w-75 mt-2 d-block mx-auto" @click="joinWithCode">Join with code</button>
|
||||
<div class="text-center">
|
||||
<small class="text-muted my-4 d-inline-block">or</small>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, onBeforeMount, onBeforeUnmount } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth.ts'
|
||||
import { useWhiteboardStore } from '@/stores/whiteboard.ts'
|
||||
import { useWhiteboardsStore } from "@/stores/whiteboards.ts";
|
||||
import WhiteboardToolbar from '@/components/whiteboard/WhiteboardToolbar.vue'
|
||||
@@ -8,6 +9,7 @@ import WhiteboardCanvas from '@/components/whiteboard/WhiteboardCanvas.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const sessionStore = useWhiteboardStore()
|
||||
const infoStore = useWhiteboardsStore()
|
||||
|
||||
@@ -30,13 +32,31 @@ onUnmounted(() => {
|
||||
})
|
||||
|
||||
async function handleLeave() {
|
||||
if (infoStore.isWaitingToJoin) {
|
||||
await sessionStore.cancelJoinRequest()
|
||||
} else {
|
||||
await sessionStore.leaveWhiteboard()
|
||||
}
|
||||
router.back()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="sessionStore.isLoading" class="d-flex flex-column justify-content-center align-items-center vh-100">
|
||||
<div v-if="infoStore.isWaitingToJoin"
|
||||
class="d-flex flex-column justify-content-center align-items-center vh-100 text-center">
|
||||
<div class="spinner-border text-primary mb-4" role="status">
|
||||
<span class="visually-hidden">Waiting...</span>
|
||||
</div>
|
||||
|
||||
<h5 class="mb-3">Waiting for owner's approval</h5>
|
||||
|
||||
<button class="btn btn-outline-danger"
|
||||
@click="handleLeave">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else-if="sessionStore.isLoading" class="d-flex flex-column justify-content-center align-items-center vh-100">
|
||||
<div class="spinner-border text-primary mb-3" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
@@ -54,5 +74,21 @@ async function handleLeave() {
|
||||
<div v-else class="d-flex vh-100">
|
||||
<WhiteboardToolbar @leave="handleLeave" />
|
||||
<WhiteboardCanvas />
|
||||
|
||||
<div v-if="sessionStore.whiteboard?.ownerId === authStore.user?.userId && sessionStore.pendingUsers.length > 0"
|
||||
class="position-fixed top-0 end-0 m-4 p-3 bg-dark border border-primary rounded shadow-lg"
|
||||
style="z-index: 1050; width: 300px;">
|
||||
<h6 class="text-primary mb-3">Pending Join Requests ({{ sessionStore.pendingUsers.length }})</h6>
|
||||
<div class="list-group list-group-flush bg-transparent">
|
||||
<div v-for="user in sessionStore.pendingUsers" :key="user.userId"
|
||||
class="list-group-item bg-transparent text-light border-secondary d-flex justify-content-between align-items-center px-0">
|
||||
<small class="text-truncate" :title="user.username">{{ user.username }}</small>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-success" @click="sessionStore.approveUser(user.userId)">✓</button>
|
||||
<button class="btn btn-danger" @click="sessionStore.rejectUser(user.userId)">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -17,6 +17,9 @@ export default defineConfig({
|
||||
},
|
||||
|
||||
server: {
|
||||
host: true,
|
||||
strictPort: false,
|
||||
allowedHosts: ['.ngrok-free.app'],
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:5266',
|
||||
|
||||
Reference in New Issue
Block a user