Информация о звонках в чате
This commit is contained in:
@@ -29,6 +29,11 @@ public sealed class ChatHub : Hub
|
||||
|
||||
public static int OnlineUsersCount => _userConnections.Count;
|
||||
public static bool IsUserOnline(string userId) => _userConnections.ContainsKey(userId);
|
||||
|
||||
// userId → CallSession (one user can be in only one call at a time)
|
||||
private static readonly ConcurrentDictionary<string, CallSession> _activeSessionsByUser = new();
|
||||
// chatId → (startTime, callType)
|
||||
private static readonly ConcurrentDictionary<string, (DateTime StartTime, string CallType)> _activeGroupCalls = new();
|
||||
|
||||
private readonly ISender _sender;
|
||||
private readonly IUserContext _userContext;
|
||||
@@ -309,11 +314,35 @@ public sealed class ChatHub : Hub
|
||||
username = username
|
||||
}
|
||||
});
|
||||
|
||||
// Track session for history
|
||||
Guid? chatId = null;
|
||||
if (Guid.TryParse(request.ChatId, out var parsedChatId)) chatId = parsedChatId;
|
||||
|
||||
if (!chatId.HasValue)
|
||||
{
|
||||
var userChats = await _chatRepository.GetUserChatsAsync(_userContext.UserId, Context.ConnectionAborted);
|
||||
if (Guid.TryParse(request.TargetUserId, out var targetId))
|
||||
{
|
||||
var personalChat = userChats.FirstOrDefault(c => c.Type == ChatType.Personal && c.Members.Any(m => m.UserId == targetId));
|
||||
if (personalChat != null) chatId = personalChat.Id;
|
||||
}
|
||||
}
|
||||
|
||||
var session = new CallSession(chatId, _userContext.UserId, Guid.Parse(request.TargetUserId), request.CallType, DateTime.UtcNow);
|
||||
_activeSessionsByUser[_userContext.UserId.ToString()] = session;
|
||||
_activeSessionsByUser[request.TargetUserId] = session;
|
||||
}
|
||||
|
||||
[HubMethodName("call_answer")]
|
||||
public async Task CallAnswer(CallAnswerRequest request)
|
||||
{
|
||||
if (_activeSessionsByUser.TryGetValue(_userContext.UserId.ToString(), out var session))
|
||||
{
|
||||
session.IsAnswered = true;
|
||||
session.AnswerTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await SendToUserAsync(request.TargetUserId, "call_answered", new
|
||||
{
|
||||
from = _userContext.UserId.ToString(),
|
||||
@@ -324,6 +353,19 @@ public sealed class ChatHub : Hub
|
||||
[HubMethodName("call_decline")]
|
||||
public async Task CallDecline(TargetUserRequest request)
|
||||
{
|
||||
var currentUserIdStr = _userContext.UserId.ToString();
|
||||
if (_activeSessionsByUser.TryRemove(currentUserIdStr, out var session))
|
||||
{
|
||||
_activeSessionsByUser.TryRemove(request.TargetUserId, out _);
|
||||
if (session.ChatId.HasValue)
|
||||
{
|
||||
// If declined by recipient, it's a "declined" call
|
||||
// If current user is recipient (not the one who started), status is declined
|
||||
string status = _userContext.UserId == session.FromUserId ? "cancelled" : "declined";
|
||||
await CreateCallMessage(session.ChatId.Value, session.FromUserId, session.CallType, status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
await SendToUserAsync(request.TargetUserId, "call_declined", new
|
||||
{
|
||||
from = _userContext.UserId.ToString(),
|
||||
@@ -333,12 +375,53 @@ public sealed class ChatHub : Hub
|
||||
[HubMethodName("call_end")]
|
||||
public async Task CallEnd(TargetUserRequest request)
|
||||
{
|
||||
var currentUserIdStr = _userContext.UserId.ToString();
|
||||
if (_activeSessionsByUser.TryRemove(currentUserIdStr, out var session))
|
||||
{
|
||||
_activeSessionsByUser.TryRemove(request.TargetUserId, out _);
|
||||
if (session.ChatId.HasValue)
|
||||
{
|
||||
int duration = session.IsAnswered && session.AnswerTime.HasValue
|
||||
? (int)(DateTime.UtcNow - session.AnswerTime.Value).TotalSeconds
|
||||
: 0;
|
||||
|
||||
string status = session.IsAnswered ? "completed" : (_userContext.UserId == session.FromUserId ? "cancelled" : "missed");
|
||||
await CreateCallMessage(session.ChatId.Value, session.FromUserId, session.CallType, status, duration);
|
||||
}
|
||||
}
|
||||
|
||||
await SendToUserAsync(request.TargetUserId, "call_ended", new
|
||||
{
|
||||
from = _userContext.UserId.ToString(),
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CreateCallMessage(Guid chatId, Guid senderId, string callType, string status, int duration)
|
||||
{
|
||||
var command = new SendMessageCommand(
|
||||
chatId,
|
||||
senderId,
|
||||
null,
|
||||
"call",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
callType,
|
||||
status,
|
||||
duration
|
||||
);
|
||||
|
||||
await _sender.Send(command);
|
||||
}
|
||||
|
||||
[HubMethodName("ice_candidate")]
|
||||
public async Task IceCandidate(IceCandidateRequest request)
|
||||
{
|
||||
@@ -410,7 +493,11 @@ public sealed class ChatHub : Hub
|
||||
|
||||
var userInfo = new ParticipantInfo(userId, username, displayName, avatar);
|
||||
|
||||
var participants = _groupCallParticipants.GetOrAdd(chatId, _ => new ConcurrentDictionary<string, ParticipantInfo>());
|
||||
var participants = _groupCallParticipants.GetOrAdd(chatId, _ =>
|
||||
{
|
||||
_activeGroupCalls[chatId] = (DateTime.UtcNow, request.CallType);
|
||||
return new ConcurrentDictionary<string, ParticipantInfo>();
|
||||
});
|
||||
var isFirst = participants.IsEmpty;
|
||||
participants.TryAdd(userId, userInfo);
|
||||
|
||||
@@ -468,6 +555,11 @@ public sealed class ChatHub : Hub
|
||||
if (participants.IsEmpty)
|
||||
{
|
||||
_groupCallParticipants.TryRemove(chatId, out _);
|
||||
if (_activeGroupCalls.TryRemove(chatId, out var info))
|
||||
{
|
||||
var duration = (int)(DateTime.UtcNow - info.StartTime).TotalSeconds;
|
||||
await CreateCallMessage(Guid.Parse(chatId), _userContext.UserId, info.CallType, "completed", duration);
|
||||
}
|
||||
await Clients.Group(chatId).SendAsync("group_call_ended", new { chatId = chatId });
|
||||
}
|
||||
}
|
||||
@@ -691,5 +783,25 @@ public sealed class ChatHub : Hub
|
||||
public record GroupRenegotiateRequest(string ChatId, string TargetUserId, object Offer);
|
||||
public record GroupRenegotiateAnswerRequest(string ChatId, string TargetUserId, object Answer);
|
||||
public record FriendSignalRequest(string FriendId);
|
||||
|
||||
public class CallSession
|
||||
{
|
||||
public Guid? ChatId { get; }
|
||||
public Guid FromUserId { get; }
|
||||
public Guid ToUserId { get; }
|
||||
public string CallType { get; }
|
||||
public DateTime StartTime { get; }
|
||||
public bool IsAnswered { get; set; }
|
||||
public DateTime? AnswerTime { get; set; }
|
||||
|
||||
public CallSession(Guid? chatId, Guid fromUserId, Guid toUserId, string callType, DateTime startTime)
|
||||
{
|
||||
ChatId = chatId;
|
||||
FromUserId = fromUserId;
|
||||
ToUserId = toUserId;
|
||||
CallType = callType;
|
||||
StartTime = startTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user