Skip to content

Commit

Permalink
Move chat sessions to their own cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbound committed Aug 4, 2023
1 parent a3b0fa9 commit 13e3a6a
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 47 deletions.
24 changes: 8 additions & 16 deletions Server/Components/Devices/ChatCard.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public partial class ChatCard : AuthComponentBase, IDisposable
[Inject]
private IClientAppState AppState { get; init; } = null!;

[Inject]
private IChatSessionCache ChatSessionCache { get; init; } = null!;

[Inject]
private ICircuitConnection CircuitConnection { get; init; } = null!;

Expand All @@ -35,7 +38,6 @@ public partial class ChatCard : AuthComponentBase, IDisposable

public void Dispose()
{
AppState.PropertyChanged -= AppState_PropertyChanged;
Messenger.Unregister<ChatReceivedMessage, string>(this, CircuitConnection.ConnectionId);
GC.SuppressFinalize(this);
}
Expand All @@ -48,7 +50,6 @@ protected override void OnAfterRender(bool firstRender)
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
AppState.PropertyChanged += AppState_PropertyChanged;
await Messenger.Register<ChatReceivedMessage, string>(
this,
CircuitConnection.ConnectionId,
Expand All @@ -62,9 +63,7 @@ private async Task HandleChatMessageReceived(ChatReceivedMessage message)
return;
}

var session = AppState.DevicesFrameChatSessions.Find(x => x.DeviceId == message.DeviceId);

if (session is null)
if (!ChatSessionCache.TryGetSession(message.DeviceId, out var session))
{
return;
}
Expand Down Expand Up @@ -96,18 +95,11 @@ private async Task HandleChatMessageReceived(ChatReceivedMessage message)
JsInterop.ScrollToEnd(_chatMessagesWindow);
}

private async void AppState_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == Session.SessionId)
{
await InvokeAsync(StateHasChanged);
}
}

private void CloseChatCard()
private async Task CloseChatCard()
{
AppState.DevicesFrameChatSessions.RemoveAll(x => x.DeviceId == Session.DeviceId);
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameChatSessions));
_ = ChatSessionCache.TryRemove($"{Session.DeviceId}", out _);
var message = new ChatSessionsChangedMessage();
await Messenger.Send(message, CircuitConnection.ConnectionId);
}
private async Task EvaluateInputKeypress(KeyboardEventArgs args)
{
Expand Down
4 changes: 2 additions & 2 deletions Server/Components/Devices/ChatFrame.razor
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
@attribute [Authorize]
@inherits AuthComponentBase

@if (AppState.DevicesFrameChatSessions.Any())
@if (_chatSessions.Any())
{
<div class="chat-frame">
@foreach (var session in AppState.DevicesFrameChatSessions)
@foreach (var session in _chatSessions)
{
<ChatCard @key="session.SessionId" Session="session" />
}
Expand Down
34 changes: 20 additions & 14 deletions Server/Components/Devices/ChatFrame.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,47 @@

namespace Remotely.Server.Components.Devices;

public partial class ChatFrame : AuthComponentBase, IDisposable
public partial class ChatFrame : AuthComponentBase, IAsyncDisposable
{
private ICollection<ChatSession> _chatSessions = Array.Empty<ChatSession>();

[Inject]
private IClientAppState AppState { get; init; } = null!;

[Inject]
private IChatSessionCache ChatCache { get; init; } = null!;

[Inject]
private ICircuitConnection CircuitConnection { get; init; } = null!;

[Inject]
private IMessenger Messenger { get; init; } = null!;

public void Dispose()
public async ValueTask DisposeAsync()
{
AppState.PropertyChanged -= AppState_PropertyChanged;
Messenger.Unregister<ChatReceivedMessage, string>(this, CircuitConnection.ConnectionId);
await Messenger.Unregister<ChatSessionsChangedMessage, string>(this, CircuitConnection.ConnectionId);
await Messenger.Unregister<ChatReceivedMessage, string>(this, CircuitConnection.ConnectionId);
GC.SuppressFinalize(this);
}

protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
AppState.PropertyChanged += AppState_PropertyChanged;
_chatSessions = ChatCache.GetAllSessions();
await Messenger.Register<ChatSessionsChangedMessage, string>(
this,
CircuitConnection.ConnectionId,
HandleChatSessionsChanged);
await Messenger.Register<ChatReceivedMessage, string>(
this,
CircuitConnection.ConnectionId,
HandleChatMessageReceived);
await base.OnInitializedAsync();
}

private async Task HandleChatMessageReceived(ChatReceivedMessage message)
{
if (AppState.DevicesFrameChatSessions.Exists(x => x.DeviceId == message.DeviceId) ||
message.DidDisconnect)
if (message.DidDisconnect ||
ChatCache.ContainsKey(message.DeviceId))
{
return;
}
Expand All @@ -64,16 +72,14 @@ private async Task HandleChatMessageReceived(ChatReceivedMessage message)
Origin = ChatHistoryItemOrigin.Device
});

AppState.DevicesFrameChatSessions.Add(newChat);
ChatCache.AddOrUpdate(message.DeviceId, newChat, (k, v) => newChat);

await InvokeAsync(StateHasChanged);
}

private void AppState_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
private async Task HandleChatSessionsChanged(ChatSessionsChangedMessage message)
{
if (e.PropertyName == nameof(AppState.DevicesFrameChatSessions))
{
InvokeAsync(StateHasChanged);
}
_chatSessions = ChatCache.GetAllSessions();
await InvokeAsync(StateHasChanged);
}
}
22 changes: 11 additions & 11 deletions Server/Components/Devices/DeviceCard.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
[Inject]
private IDataService DataService { get; init; } = null!;

[Inject]
private IChatSessionCache ChatCache { get; init; } = null!;

private bool IsExpanded => GetCardState() == DeviceCardState.Expanded;

private bool IsOutdated =>
Expand Down Expand Up @@ -282,21 +285,18 @@ void modalBody(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder build

private void StartChat()
{
var existingSession = AppState.DevicesFrameChatSessions.FirstOrDefault(x => x.DeviceId == Device.ID);
if (existingSession is null)
var session = ChatCache.GetOrAdd(Device.ID, key =>
{
AppState.DevicesFrameChatSessions.Add(new ChatSession()
return new ChatSession()
{
DeviceId = Device.ID,
DeviceId = key,
DeviceName = Device.DeviceName,
IsExpanded = true
});
}
else
{
existingSession.IsExpanded = true;
}
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameChatSessions));
};
});

session.IsExpanded = true;
Messenger.Send(new ChatSessionsChangedMessage(), CircuitConnection.ConnectionId);
}

private async Task StartRemoteControl(bool viewOnly)
Expand Down
3 changes: 3 additions & 0 deletions Server/Models/Messages/ChatSessionsChangedMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Remotely.Server.Models.Messages;

public record ChatSessionsChangedMessage();
3 changes: 2 additions & 1 deletion Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@
services.AddSingleton<IOtpProvider, OtpProvider>();
services.AddSingleton<IEmbeddedServerDataSearcher, EmbeddedServerDataSearcher>();
services.AddSingleton<ILogsManager, LogsManager>();
services.AddSingleton<IThemeProvider, ThemeProvider>();
services.AddScoped<IThemeProvider, ThemeProvider>();
services.AddScoped<IChatSessionCache, ChatSessionCache>();
services.AddSingleton(WeakReferenceMessenger.Default);

services.AddRemoteControlServer(config =>
Expand Down
57 changes: 57 additions & 0 deletions Server/Services/ChatSessionCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Remotely.Shared.ViewModels;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Remotely.Server.Services;

public interface IChatSessionCache
{
void AddOrUpdate(
string deviceId,
ChatSession chatSession,
Func<string, ChatSession, ChatSession> updateFactory);

bool ContainsKey(string deviceId);
ICollection<ChatSession> GetAllSessions();
ChatSession GetOrAdd(string deviceId, Func<string, ChatSession> factory);
bool TryGetSession(string deviceId, [NotNullWhen(true)] out ChatSession? session);
bool TryRemove(string deviceId, [NotNullWhen(true)] out ChatSession? session);
}

public class ChatSessionCache : IChatSessionCache
{
private readonly ConcurrentDictionary<string, ChatSession> _sessions = new();

public void AddOrUpdate(
string deviceId,
ChatSession chatSession,
Func<string, ChatSession, ChatSession> updateFactory)
{
_sessions.AddOrUpdate(deviceId, chatSession, updateFactory);
}

public bool ContainsKey(string deviceId)
{
return _sessions.ContainsKey(deviceId);
}

public ICollection<ChatSession> GetAllSessions() => _sessions.Values;

public ChatSession GetOrAdd(string deviceId, Func<string, ChatSession> factory)
{
return _sessions.GetOrAdd(deviceId, factory);
}

public bool TryGetSession(string deviceId, [NotNullWhen(true)] out ChatSession? session)
{
return _sessions.TryGetValue(deviceId, out session);
}

public bool TryRemove(string deviceId, [NotNullWhen(true)] out ChatSession? session)
{
return _sessions.TryRemove(deviceId, out session);
}
}
3 changes: 0 additions & 3 deletions Server/Services/ClientAppState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace Remotely.Server.Services;

public interface IClientAppState : INotifyPropertyChanged, IInvokePropertyChanged
{
ConcurrentList<ChatSession> DevicesFrameChatSessions { get; }
DeviceCardState DevicesFrameFocusedCardState { get; set; }
string? DevicesFrameFocusedDevice { get; set; }
ConcurrentList<string> DevicesFrameSelectedDevices { get; }
Expand All @@ -29,8 +28,6 @@ public class ClientAppState : ViewModelBase, IClientAppState
private readonly ConcurrentQueue<string> _terminalHistory = new();
private int _terminalHistoryIndex = 0;

public ConcurrentList<ChatSession> DevicesFrameChatSessions { get; } = new();

public DeviceCardState DevicesFrameFocusedCardState
{
get => Get<DeviceCardState>();
Expand Down

0 comments on commit 13e3a6a

Please sign in to comment.