безопасная обертка IsUserInvisibleAsync

This commit is contained in:
max
2026-04-17 23:03:15 +03:00
parent bccbf12a11
commit e8c9a55fe9
2 changed files with 31 additions and 10 deletions
@@ -102,7 +102,11 @@ public sealed class ChatHub : Hub
var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, Context.ConnectionAborted); var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, Context.ConnectionAborted);
if (!isInvisible) if (!isInvisible)
{ {
await BroadcastPresenceToVisibleUsersAsync("user_online", new { userId }, _userContext.UserId); await BroadcastPresenceToVisibleUsersAsync(
"user_online",
new { userId },
_userContext.UserId,
Context.ConnectionAborted);
} }
} }
await base.OnConnectedAsync(); await base.OnConnectedAsync();
@@ -119,13 +123,22 @@ public sealed class ChatHub : Hub
if (set.Count == 0) if (set.Count == 0)
{ {
_userConnections.TryRemove(userId, out _); _userConnections.TryRemove(userId, out _);
var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, Context.ConnectionAborted); try
if (!isInvisible)
{ {
await BroadcastPresenceToVisibleUsersAsync( // Connection is tearing down: ConnectionAborted is already signaled and will cancel I/O.
"user_offline", var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, CancellationToken.None);
new { userId, lastSeen = DateTime.UtcNow }, if (!isInvisible)
_userContext.UserId); {
await BroadcastPresenceToVisibleUsersAsync(
"user_offline",
new { userId, lastSeen = DateTime.UtcNow },
_userContext.UserId,
CancellationToken.None);
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Presence broadcast on disconnect failed for {UserId}", userId);
} }
} }
} }
@@ -141,7 +154,11 @@ public sealed class ChatHub : Hub
return profile?.IsInvisible ?? false; return profile?.IsInvisible ?? false;
} }
private async Task BroadcastPresenceToVisibleUsersAsync(string eventName, object payload, Guid sourceUserId) private async Task BroadcastPresenceToVisibleUsersAsync(
string eventName,
object payload,
Guid sourceUserId,
CancellationToken cancellationToken)
{ {
var recipients = _userConnections.Keys var recipients = _userConnections.Keys
.Where(id => id != sourceUserId.ToString()) .Where(id => id != sourceUserId.ToString())
@@ -153,7 +170,7 @@ public sealed class ChatHub : Hub
if (recipients.Count == 0) if (recipients.Count == 0)
return; return;
var recipientProfiles = await _profileRepository.GetAsync(recipients, Context.ConnectionAborted); var recipientProfiles = await _profileRepository.GetAsync(recipients, cancellationToken);
var invisibleRecipientIds = recipientProfiles var invisibleRecipientIds = recipientProfiles
.Where(p => p.IsInvisible) .Where(p => p.IsInvisible)
.Select(p => p.UserId) .Select(p => p.UserId)
@@ -125,7 +125,11 @@ export const useChatStore = create<ChatState>((set, get) => ({
set({ isLoadingMessages: true }); set({ isLoadingMessages: true });
const currentMessages = state.messages[chatId] || []; const currentMessages = state.messages[chatId] || [];
const cursor = !reset && currentMessages.length > 0 ? currentMessages[0].sequenceId.toString() : undefined; const firstSeq = currentMessages[0]?.sequenceId;
const cursor =
!reset && currentMessages.length > 0 && firstSeq != null
? String(firstSeq)
: undefined;
const fetched = await ChatApi.getMessages(chatId, cursor); const fetched = await ChatApi.getMessages(chatId, cursor);