Новый дизайн, удаление

This commit is contained in:
Халимов Рустам
2026-03-12 22:54:15 +03:00
parent 1ec6b438d2
commit 9daafae9c5
158 changed files with 1511 additions and 254 deletions

View File

@@ -17,6 +17,8 @@ public sealed class ChatHub : Hub
{
// Маппинг userId → список connectionId
private static readonly ConcurrentDictionary<string, HashSet<string>> _userConnections = new();
// chatId → (userId → ParticipantInfo)
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, ParticipantInfo>> _groupCallParticipants = new();
private readonly ISender _sender;
private readonly IUserContext _userContext;
@@ -118,6 +120,22 @@ public sealed class ChatHub : Hub
});
}
[HubMethodName("delete_messages")]
public async Task DeleteMessages(DeleteMessagesHubRequest request)
{
var parsedIds = request.MessageIds
.Select(id => Guid.TryParse(id, out var parsed) ? parsed : Guid.Empty)
.Where(id => id != Guid.Empty)
.ToList();
if (parsedIds.Any())
{
var command = new Vortex.Modules.Chats.Application.Messages.Delete.DeleteMessagesCommand(
request.ChatId, _userContext.UserId, parsedIds, request.DeleteForAll);
await _sender.Send(command);
}
}
[HubMethodName("typing_start")]
public async Task TypingStart(string chatId)
{
@@ -130,12 +148,30 @@ public sealed class ChatHub : Hub
await Clients.Group(chatId).SendAsync("user_stopped_typing", new { ChatId = chatId, UserId = _userContext.UserId });
}
[HubMethodName("join_chat")]
public async Task JoinChat(string chatId)
{
// Simple security check: check if user is member of chat (optional but recommended)
if (Guid.TryParse(chatId, out var chatGuid))
{
var userChats = await _chatRepository.GetUserChatsAsync(_userContext.UserId, Context.ConnectionAborted);
if (userChats.Any(c => c.Id == chatGuid))
{
await Groups.AddToGroupAsync(Context.ConnectionId, chatId);
}
}
}
[HubMethodName("add_reaction")]
public async Task AddReaction(AddReactionRequest request)
{
var command = new Vortex.Modules.Chats.Application.Messages.React.AddReactionCommand(
request.MessageId, _userContext.UserId, request.Emoji, request.ChatId);
await _sender.Send(command);
var result = await _sender.Send(command);
if (result.IsFailure)
{
throw new HubException(result.Error.Description);
}
}
[HubMethodName("remove_reaction")]
@@ -143,7 +179,11 @@ public sealed class ChatHub : Hub
{
var command = new Vortex.Modules.Chats.Application.Messages.React.RemoveReactionCommand(
request.MessageId, _userContext.UserId, request.Emoji, request.ChatId);
await _sender.Send(command);
var result = await _sender.Send(command);
if (result.IsFailure)
{
throw new HubException(result.Error.Description);
}
}
// ────────────────────────────────────────────────────────────────
@@ -255,6 +295,150 @@ public sealed class ChatHub : Hub
});
}
// ────────────────────────────────────────────────────────────────
// Group Call signals
// ────────────────────────────────────────────────────────────────
[HubMethodName("group_call_join")]
public async Task GroupCallJoin(GroupCallJoinRequest request)
{
var chatId = request.ChatId;
var userId = _userContext.UserId.ToString();
var displayName = Context.User?.FindFirstValue("name") ?? Context.User?.FindFirstValue(ClaimTypes.Name) ?? "User";
var avatar = Context.User?.FindFirstValue("avatar");
var username = Context.User?.FindFirstValue("unique_name") ?? Context.User?.Identity?.Name ?? "user";
var userInfo = new ParticipantInfo(userId, username, displayName, avatar);
var participants = _groupCallParticipants.GetOrAdd(chatId, _ => new ConcurrentDictionary<string, ParticipantInfo>());
participants.TryAdd(userId, userInfo);
// Notify others
await Clients.Group(chatId).SendAsync("group_call_user_joined", new {
chatId = chatId,
userId = userId,
userInfo = userInfo
});
// Send current participants to joiner (excluding self)
var others = participants.Values.Where(p => p.Id != userId).ToList();
await Clients.Caller.SendAsync("group_call_participants", new {
chatId = chatId,
participants = others
});
}
[HubMethodName("group_call_leave")]
public async Task GroupCallLeave(string chatId)
{
var userId = _userContext.UserId.ToString();
if (_groupCallParticipants.TryGetValue(chatId, out var participants))
{
participants.TryRemove(userId, out _);
if (participants.IsEmpty)
{
_groupCallParticipants.TryRemove(chatId, out _);
}
}
await Clients.Group(chatId).SendAsync("group_call_user_left", new {
chatId = chatId,
userId = userId
});
}
[HubMethodName("group_call_offer")]
public async Task GroupCallOffer(GroupCallOfferRequest request)
{
await SendToUserAsync(request.TargetUserId, "group_call_offer", new
{
chatId = request.ChatId,
from = _userContext.UserId.ToString(),
offer = request.Offer
});
}
[HubMethodName("group_call_answer")]
public async Task GroupCallAnswer(GroupCallAnswerRequest request)
{
await SendToUserAsync(request.TargetUserId, "group_call_answer", new
{
chatId = request.ChatId,
from = _userContext.UserId.ToString(),
answer = request.Answer
});
}
[HubMethodName("group_ice_candidate")]
public async Task GroupIceCandidate(GroupIceCandidateRequest request)
{
await SendToUserAsync(request.TargetUserId, "group_ice_candidate", new
{
chatId = request.ChatId,
from = _userContext.UserId.ToString(),
candidate = request.Candidate
});
}
[HubMethodName("group_call_renegotiate")]
public async Task GroupRenegotiate(GroupRenegotiateRequest request)
{
await SendToUserAsync(request.TargetUserId, "group_call_renegotiate", new
{
chatId = request.ChatId,
from = _userContext.UserId.ToString(),
offer = request.Offer
});
}
[HubMethodName("group_call_renegotiate_answer")]
public async Task GroupRenegotiateAnswer(GroupRenegotiateAnswerRequest request)
{
await SendToUserAsync(request.TargetUserId, "group_call_renegotiate_answer", new
{
chatId = request.ChatId,
from = _userContext.UserId.ToString(),
answer = request.Answer
});
}
[HubMethodName("screen_share_started")]
public async Task ScreenShareStarted(string chatId)
{
var userId = _userContext.UserId.ToString();
if (_groupCallParticipants.TryGetValue(chatId, out var participants))
{
if (participants.TryGetValue(userId, out var info))
{
participants[userId] = info with { IsSharingScreen = true };
}
}
await Clients.Group(chatId).SendAsync("screen_share_started", new {
chatId = chatId,
userId = userId
});
}
[HubMethodName("screen_share_stopped")]
public async Task ScreenShareStopped(string chatId)
{
var userId = _userContext.UserId.ToString();
if (_groupCallParticipants.TryGetValue(chatId, out var participants))
{
if (participants.TryGetValue(userId, out var info))
{
participants[userId] = info with { IsSharingScreen = false };
}
}
await Clients.Group(chatId).SendAsync("screen_share_stopped", new {
chatId = chatId,
userId = userId
});
}
// ────────────────────────────────────────────────────────────────
// Records
// ────────────────────────────────────────────────────────────────
@@ -278,4 +462,12 @@ public sealed class ChatHub : Hub
public record CallTypeChangedRequest(string TargetUserId, string CallType);
public record AddReactionRequest(Guid MessageId, Guid ChatId, string Emoji);
public record RemoveReactionRequest(Guid MessageId, Guid ChatId, string Emoji);
public record DeleteMessagesHubRequest(Guid ChatId, List<string> MessageIds, bool DeleteForAll);
public record GroupCallJoinRequest(string ChatId, string CallType);
public record ParticipantInfo(string Id, string Username, string DisplayName, string? Avatar, bool IsSharingScreen = false);
public record GroupCallOfferRequest(string ChatId, string TargetUserId, object Offer);
public record GroupCallAnswerRequest(string ChatId, string TargetUserId, object Answer);
public record GroupIceCandidateRequest(string ChatId, string TargetUserId, object Candidate);
public record GroupRenegotiateRequest(string ChatId, string TargetUserId, object Offer);
public record GroupRenegotiateAnswerRequest(string ChatId, string TargetUserId, object Answer);
}