безопасная обертка IsUserInvisibleAsync
This commit is contained in:
@@ -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
|
||||||
|
{
|
||||||
|
// Connection is tearing down: ConnectionAborted is already signaled and will cancel I/O.
|
||||||
|
var isInvisible = await IsUserInvisibleAsync(_userContext.UserId, CancellationToken.None);
|
||||||
if (!isInvisible)
|
if (!isInvisible)
|
||||||
{
|
{
|
||||||
await BroadcastPresenceToVisibleUsersAsync(
|
await BroadcastPresenceToVisibleUsersAsync(
|
||||||
"user_offline",
|
"user_offline",
|
||||||
new { userId, lastSeen = DateTime.UtcNow },
|
new { userId, lastSeen = DateTime.UtcNow },
|
||||||
_userContext.UserId);
|
_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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user