безопасная обертка 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

View File

@@ -102,7 +102,11 @@ public sealed class ChatHub : Hub
var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, Context.ConnectionAborted);
if (!isInvisible)
{
await BroadcastPresenceToVisibleUsersAsync("user_online", new { userId }, _userContext.UserId);
await BroadcastPresenceToVisibleUsersAsync(
"user_online",
new { userId },
_userContext.UserId,
Context.ConnectionAborted);
}
}
await base.OnConnectedAsync();
@@ -119,13 +123,22 @@ public sealed class ChatHub : Hub
if (set.Count == 0)
{
_userConnections.TryRemove(userId, out _);
var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, Context.ConnectionAborted);
if (!isInvisible)
try
{
await BroadcastPresenceToVisibleUsersAsync(
"user_offline",
new { userId, lastSeen = DateTime.UtcNow },
_userContext.UserId);
// Connection is tearing down: ConnectionAborted is already signaled and will cancel I/O.
var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, CancellationToken.None);
if (!isInvisible)
{
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;
}
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
.Where(id => id != sourceUserId.ToString())
@@ -153,7 +170,7 @@ public sealed class ChatHub : Hub
if (recipients.Count == 0)
return;
var recipientProfiles = await _profileRepository.GetAsync(recipients, Context.ConnectionAborted);
var recipientProfiles = await _profileRepository.GetAsync(recipients, cancellationToken);
var invisibleRecipientIds = recipientProfiles
.Where(p => p.IsInvisible)
.Select(p => p.UserId)

View File

@@ -125,7 +125,11 @@ export const useChatStore = create<ChatState>((set, get) => ({
set({ isLoadingMessages: true });
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);