feat: enhance user presence and channel interaction features

- Fix userlist not refreshing after creating a new channel.
- Implement unmute timer with a background job to automatically unmute users.
- Improve userlist display by filtering invisible users and ensuring proper transitions between statuses.
- Add clickable usernames, @mentions, and #channels for easier navigation.
- Embed theme colors from source sites for a more cohesive UI.
- Introduce a stateful userlist that updates incrementally via SignalR events.
- Restrict auto-opening of files to safe types only, enhancing security.
- Refactor user management into a dedicated service to reduce code duplication.
- Add a MuteExpirationService to handle timed mutes.
- Update documentation with Mermaid diagrams for major flows.
This commit is contained in:
HueByte
2026-02-24 20:56:40 +01:00
parent 8647b05c12
commit 0c16f44db6
21 changed files with 451 additions and 49 deletions
+26 -2
View File
@@ -107,7 +107,31 @@ public class ChatService : IChatService
if (isNewJoin)
{
await BroadcastToAllAsync(b => b.SendUserJoinedAsync(channelName, username, connectionId));
// Fetch presence data so clients can update their lists incrementally
UserPresenceDto? presence = null;
try
{
using var presenceScope = _scopeFactory.CreateScope();
var presenceDb = presenceScope.ServiceProvider.GetRequiredService<EchoHubDbContext>();
var user = await presenceDb.Users.FindAsync(userId);
if (user is not null)
{
presence = new UserPresenceDto(
user.Username, user.DisplayName, user.NicknameColor,
user.Status, user.StatusMessage, user.Role);
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Failed to fetch presence for {User} on join", username);
}
// Don't broadcast join for invisible users — they still get history but stay hidden
if (presence is null || presence.Status != UserStatus.Invisible)
{
await BroadcastToAllAsync(b => b.SendUserJoinedAsync(channelName, username, presence, connectionId));
}
_logger.LogInformation("{User} joined channel '{Channel}'", username, channelName);
}
@@ -272,7 +296,7 @@ public class ChatService : IChatService
var db = scope.ServiceProvider.GetRequiredService<EchoHubDbContext>();
return await db.Users
.Where(u => onlineUsernames.Contains(u.Username))
.Where(u => onlineUsernames.Contains(u.Username) && u.Status != UserStatus.Invisible)
.Select(u => new UserPresenceDto(
u.Username,
u.DisplayName,