Reintroduces separate contexts for users, channels, connections (now split into sessions and connections) and user-channel associations. It builds which is as much assurance as I can give about the stability of this commit, but its also the bare minimum of what i like to commit sooooo A lot of things still need to be broadcast through events throughout the application in order to keep states consistent but we'll cross that bridge when we get to it. I really need to stop using that phrase thingy, I'm overusing it.
176 lines
6.5 KiB
C#
176 lines
6.5 KiB
C#
using SharpChat.Users;
|
|
|
|
namespace SharpChat.Channels;
|
|
|
|
public class ChannelsUsersContext(ChannelsContext channelsCtx, UsersContext usersCtx) {
|
|
private readonly Dictionary<string, Dictionary<long, DateTimeOffset>> UserChannels = [];
|
|
private readonly Dictionary<long, HashSet<string>> ChannelUsers = [];
|
|
private readonly Dictionary<string, long> UserLastChannel = [];
|
|
private readonly Lock @lock = new();
|
|
|
|
private void UpdateUserLastChannel(string userId, long? channelId = null) {
|
|
if(channelId.HasValue
|
|
&& UserLastChannel.TryGetValue(userId, out long lastChannelId)
|
|
&& channelId.Value != lastChannelId)
|
|
return;
|
|
|
|
if(UserChannels.TryGetValue(userId, out var userChannels) && userChannels.Count > 0)
|
|
UserLastChannel[userId] = userChannels.OrderByDescending(kvp => kvp.Value).First().Key;
|
|
else
|
|
UserLastChannel.Remove(userId);
|
|
}
|
|
|
|
public void RemoveChannel(Channel channel)
|
|
=> RemoveChannel(channel.Id);
|
|
|
|
public void RemoveChannel(long channelId) {
|
|
lock(@lock) {
|
|
if(!ChannelUsers.TryGetValue(channelId, out var channelUsers))
|
|
return;
|
|
ChannelUsers.Remove(channelId);
|
|
|
|
foreach(string userId in channelUsers)
|
|
if(UserChannels.TryGetValue(userId, out var userChannels)) {
|
|
userChannels.Remove(channelId);
|
|
UpdateUserLastChannel(userId, channelId);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RemoveUser(User user)
|
|
=> RemoveUser(user.UserId);
|
|
|
|
public void RemoveUser(string userId) {
|
|
lock(@lock) {
|
|
if(!UserChannels.TryGetValue(userId, out var userChannels))
|
|
return;
|
|
UserChannels.Remove(userId);
|
|
UserLastChannel.Remove(userId);
|
|
|
|
foreach(long channelId in userChannels.Keys)
|
|
if(ChannelUsers.TryGetValue(channelId, out var channelUsers))
|
|
channelUsers.Remove(userId);
|
|
}
|
|
}
|
|
|
|
public void RecordChannelUserActivity(Channel channel, User user)
|
|
=> RecordChannelUserActivity(channel.Id, user.UserId);
|
|
public void RecordChannelUserActivity(long channelId, User user)
|
|
=> RecordChannelUserActivity(channelId, user.UserId);
|
|
public void RecordChannelUserActivity(Channel channel, string userId)
|
|
=> RecordChannelUserActivity(channel.Id, userId);
|
|
|
|
public void RecordChannelUserActivity(long channelId, string userId) {
|
|
lock(@lock) {
|
|
if(!UserChannels.TryGetValue(userId, out var userChannels))
|
|
throw new ArgumentException("Attempted to register activity for non-existent user.", nameof(userId));
|
|
|
|
userChannels[channelId] = DateTimeOffset.UtcNow;
|
|
UserLastChannel[userId] = channelId;
|
|
}
|
|
}
|
|
|
|
public void AddChannelUser(Channel channel, User user)
|
|
=> AddChannelUser(channel.Id, user.UserId);
|
|
|
|
public void AddChannelUser(long channelId, string userId) {
|
|
lock(@lock) {
|
|
if(!ChannelUsers.TryGetValue(channelId, out var channelUsers))
|
|
ChannelUsers.Add(channelId, channelUsers = []);
|
|
if(!UserChannels.ContainsKey(userId))
|
|
UserChannels.Add(userId, []);
|
|
|
|
channelUsers.Add(userId);
|
|
RecordChannelUserActivity(channelId, userId);
|
|
}
|
|
}
|
|
|
|
public void RemoveChannelUser(Channel channel, User user)
|
|
=> RemoveChannelUser(channel.Id, user.UserId);
|
|
public void RemoveChannelUser(long channelId, User user)
|
|
=> RemoveChannelUser(channelId, user.UserId);
|
|
public void RemoveChannelUser(Channel channel, string userId)
|
|
=> RemoveChannelUser(channel.Id, userId);
|
|
|
|
public void RemoveChannelUser(long channelId, string userId) {
|
|
lock(@lock) {
|
|
ChannelUsers.Remove(channelId);
|
|
UserChannels.Remove(userId);
|
|
UpdateUserLastChannel(userId, channelId);
|
|
}
|
|
}
|
|
|
|
public bool HasChannelUser(Channel channel, User user)
|
|
=> HasChannelUser(channel.Id, user.UserId);
|
|
public bool HasChannelUser(long channelId, User user)
|
|
=> HasChannelUser(channelId, user.UserId);
|
|
public bool HasChannelUser(Channel channel, string userId)
|
|
=> HasChannelUser(channel.Id, userId);
|
|
|
|
public bool HasChannelUser(long channelId, string userId) {
|
|
lock(@lock)
|
|
return ChannelUsers.TryGetValue(channelId, out var channelUsers) && channelUsers.Contains(userId);
|
|
}
|
|
|
|
public long? GetUserLastChannelId(User user)
|
|
=> GetUserLastChannelId(user.UserId);
|
|
|
|
public long? GetUserLastChannelId(string userId) {
|
|
lock(@lock)
|
|
return UserLastChannel.TryGetValue(userId, out long channelId) ? channelId : null;
|
|
}
|
|
|
|
public Channel? GetUserLastChannel(User user)
|
|
=> GetUserLastChannel(user.UserId);
|
|
|
|
public Channel? GetUserLastChannel(string userId) {
|
|
lock(@lock) {
|
|
long? channelId = GetUserLastChannelId(userId);
|
|
return channelId.HasValue ? channelsCtx.GetChannel(channelId.Value) : null;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<string> GetChannelUserIds(Channel channel)
|
|
=> GetChannelUserIds(channel.Id);
|
|
|
|
public IEnumerable<string> GetChannelUserIds(long channelId) {
|
|
lock(@lock)
|
|
return [.. GetChannelUserIdsInternal(channelId)];
|
|
}
|
|
|
|
private HashSet<string> GetChannelUserIdsInternal(long channelId) {
|
|
return ChannelUsers.TryGetValue(channelId, out var channelUsers) ? channelUsers : [];
|
|
}
|
|
|
|
public IEnumerable<User> GetChannelUsers(Channel channel) {
|
|
lock(@lock)
|
|
return usersCtx.GetUsers(GetChannelUserIdsInternal(channel.Id));
|
|
}
|
|
|
|
public IEnumerable<User> GetChannelUsers(long channelId) {
|
|
lock(@lock)
|
|
return usersCtx.GetUsers(GetChannelUserIdsInternal(channelId));
|
|
}
|
|
|
|
public IEnumerable<long> GetUserChannelIds(User user)
|
|
=> GetUserChannelIds(user.UserId);
|
|
|
|
public IEnumerable<long> GetUserChannelIds(string userId) {
|
|
lock(@lock)
|
|
return [.. GetUserChannelIdsInternal(userId)];
|
|
}
|
|
|
|
private IEnumerable<long> GetUserChannelIdsInternal(string userId) {
|
|
return UserChannels.TryGetValue(userId, out var userChannels) ? userChannels.Keys : [];
|
|
}
|
|
|
|
public IEnumerable<Channel> GetUserChannels(User user) {
|
|
lock(@lock)
|
|
return channelsCtx.GetChannels(GetUserChannelIdsInternal(user.UserId));
|
|
}
|
|
|
|
public IEnumerable<Channel> GetUserChannels(string userId) {
|
|
lock(@lock)
|
|
return channelsCtx.GetChannels(GetUserChannelIdsInternal(userId));
|
|
}
|
|
}
|