mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Replace ClientAppState with stores.
This commit is contained in:
parent
13e3a6a038
commit
f076089f60
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components.Web;
|
||||
using Remotely.Server.Hubs;
|
||||
using Remotely.Server.Models.Messages;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Shared.Enums;
|
||||
using Remotely.Shared.ViewModels;
|
||||
using System;
|
||||
@ -22,10 +23,7 @@ public partial class ChatCard : AuthComponentBase, IDisposable
|
||||
public required ChatSession Session { get; set; }
|
||||
|
||||
[Inject]
|
||||
private IClientAppState AppState { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IChatSessionCache ChatSessionCache { get; init; } = null!;
|
||||
private IChatSessionStore ChatSessionStore { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ICircuitConnection CircuitConnection { get; init; } = null!;
|
||||
@ -63,7 +61,7 @@ public partial class ChatCard : AuthComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ChatSessionCache.TryGetSession(message.DeviceId, out var session))
|
||||
if (!ChatSessionStore.TryGetSession(message.DeviceId, out var session))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -97,7 +95,7 @@ public partial class ChatCard : AuthComponentBase, IDisposable
|
||||
|
||||
private async Task CloseChatCard()
|
||||
{
|
||||
_ = ChatSessionCache.TryRemove($"{Session.DeviceId}", out _);
|
||||
_ = ChatSessionStore.TryRemove($"{Session.DeviceId}", out _);
|
||||
var message = new ChatSessionsChangedMessage();
|
||||
await Messenger.Send(message, CircuitConnection.ConnectionId);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Remotely.Server.Hubs;
|
||||
using Remotely.Server.Models.Messages;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Shared.Entities;
|
||||
using Remotely.Shared.Enums;
|
||||
using Remotely.Shared.ViewModels;
|
||||
@ -19,10 +19,10 @@ public partial class ChatFrame : AuthComponentBase, IAsyncDisposable
|
||||
private ICollection<ChatSession> _chatSessions = Array.Empty<ChatSession>();
|
||||
|
||||
[Inject]
|
||||
private IClientAppState AppState { get; init; } = null!;
|
||||
private ISelectedCardsStore CardStore { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IChatSessionCache ChatCache { get; init; } = null!;
|
||||
private IChatSessionStore ChatCache { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ICircuitConnection CircuitConnection { get; init; } = null!;
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
@attribute [Authorize]
|
||||
@inherits AuthComponentBase
|
||||
|
||||
<div @ref="_card" class="card border-secondary my-3 mr-3 device-card @_theme @GetCardStateClass(Device)"
|
||||
<div @ref="_card" class="card border-secondary my-3 mr-3 device-card @_theme @GetCardStateClass()"
|
||||
@onclick="ExpandCard"
|
||||
@onclick:stopPropagation
|
||||
@oncontextmenu="ContextMenuOpening"
|
||||
@oncontextmenu:preventDefault="GetCardState() == DeviceCardState.Normal"
|
||||
@oncontextmenu:stopPropagation="GetCardState() == DeviceCardState.Normal">
|
||||
@oncontextmenu:preventDefault="_state == DeviceCardState.Normal"
|
||||
@oncontextmenu:stopPropagation="_state == DeviceCardState.Normal">
|
||||
|
||||
<div class="card-header" @onclick="HandleHeaderClick"
|
||||
@onclick:stopPropagation="GetCardState() == DeviceCardState.Expanded"
|
||||
@onclick:preventDefault="GetCardState() == DeviceCardState.Expanded">
|
||||
@onclick:stopPropagation="_state == DeviceCardState.Expanded"
|
||||
@onclick:preventDefault="_state == DeviceCardState.Expanded">
|
||||
<div>
|
||||
@if (Device.IsOnline)
|
||||
{
|
||||
|
||||
@ -6,6 +6,7 @@ using Remotely.Server.Enums;
|
||||
using Remotely.Server.Hubs;
|
||||
using Remotely.Server.Models.Messages;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Shared.Entities;
|
||||
using Remotely.Shared.Enums;
|
||||
using Remotely.Shared.Utilities;
|
||||
@ -24,6 +25,7 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
private ElementReference _card;
|
||||
private Version _currentVersion = new();
|
||||
private Theme _theme;
|
||||
private DeviceCardState _state;
|
||||
private DeviceGroup[] _deviceGroups = Array.Empty<DeviceGroup>();
|
||||
|
||||
[Parameter]
|
||||
@ -34,7 +36,7 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
|
||||
|
||||
[Inject]
|
||||
private IClientAppState AppState { get; init; } = null!;
|
||||
private ISelectedCardsStore SelectedCards { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IThemeProvider ThemeProvider { get; init; } = null!;
|
||||
@ -46,15 +48,15 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
private IDataService DataService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IChatSessionCache ChatCache { get; init; } = null!;
|
||||
private IChatSessionStore ChatCache { get; init; } = null!;
|
||||
|
||||
private bool IsExpanded => GetCardState() == DeviceCardState.Expanded;
|
||||
private bool IsExpanded => _state == DeviceCardState.Expanded;
|
||||
|
||||
private bool IsOutdated =>
|
||||
Version.TryParse(Device.AgentVersion, out var result) &&
|
||||
result < _currentVersion;
|
||||
|
||||
private bool IsSelected => AppState.DevicesFrameSelectedDevices.Contains(Device.ID);
|
||||
private bool IsSelected => SelectedCards.SelectedDevices.Contains(Device.ID);
|
||||
|
||||
[Inject]
|
||||
private IJsInterop JsInterop { get; init; } = null!;
|
||||
@ -76,7 +78,6 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AppState.PropertyChanged -= AppState_PropertyChanged;
|
||||
Messenger.Unregister<DeviceStateChangedMessage, string>(this, CircuitConnection.ConnectionId);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
@ -88,13 +89,41 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
_theme = await ThemeProvider.GetEffectiveTheme();
|
||||
_currentVersion = UpgradeService.GetCurrentVersion();
|
||||
_deviceGroups = DataService.GetDeviceGroups(UserName);
|
||||
AppState.PropertyChanged += AppState_PropertyChanged;
|
||||
|
||||
await Messenger.Register<DeviceCardStateChangedMessage, string>(
|
||||
this,
|
||||
CircuitConnection.ConnectionId,
|
||||
HandleDeviceCardStateChanged);
|
||||
|
||||
await Messenger.Register<DeviceStateChangedMessage, string>(
|
||||
this,
|
||||
CircuitConnection.ConnectionId,
|
||||
HandleDeviceStateChanged);
|
||||
}
|
||||
|
||||
private async Task HandleDeviceCardStateChanged(DeviceCardStateChangedMessage message)
|
||||
{
|
||||
if (message.DeviceId == Device.ID)
|
||||
{
|
||||
if (message.State == _state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_state = message.State;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_state == DeviceCardState.Normal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_state = DeviceCardState.Normal;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleDeviceStateChanged(DeviceStateChangedMessage message)
|
||||
{
|
||||
if (message.Device.ID != Device.ID)
|
||||
@ -133,19 +162,9 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void AppState_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(AppState.DevicesFrameFocusedCardState) ||
|
||||
e.PropertyName == nameof(AppState.DevicesFrameFocusedDevice) ||
|
||||
e.PropertyName == nameof(AppState.DevicesFrameSelectedDevices))
|
||||
{
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void ContextMenuOpening(MouseEventArgs args)
|
||||
{
|
||||
if (GetCardState() == DeviceCardState.Normal)
|
||||
if (_state == DeviceCardState.Normal)
|
||||
{
|
||||
JsInterop.OpenWindow($"/device-details/{Device.ID}", "_blank");
|
||||
}
|
||||
@ -153,40 +172,24 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
|
||||
private async Task ExpandCard(MouseEventArgs args)
|
||||
{
|
||||
if (AppState.DevicesFrameFocusedDevice == Device.ID)
|
||||
if (_state == DeviceCardState.Expanded)
|
||||
{
|
||||
if (AppState.DevicesFrameFocusedCardState == DeviceCardState.Normal)
|
||||
{
|
||||
AppState.DevicesFrameFocusedCardState = DeviceCardState.Expanded;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AppState.DevicesFrameFocusedDevice = Device.ID;
|
||||
AppState.DevicesFrameFocusedCardState = DeviceCardState.Expanded;
|
||||
await Messenger.Send(
|
||||
new DeviceCardStateChangedMessage(Device.ID, DeviceCardState.Expanded),
|
||||
CircuitConnection.ConnectionId);
|
||||
|
||||
JsInterop.ScrollToElement(_card);
|
||||
|
||||
await CircuitConnection.TriggerHeartbeat(Device.ID);
|
||||
}
|
||||
|
||||
private DeviceCardState GetCardState()
|
||||
|
||||
private string GetCardStateClass()
|
||||
{
|
||||
if (AppState.DevicesFrameFocusedDevice == Device.ID)
|
||||
{
|
||||
return AppState.DevicesFrameFocusedCardState;
|
||||
}
|
||||
|
||||
return DeviceCardState.Normal;
|
||||
}
|
||||
|
||||
private string GetCardStateClass(Device device)
|
||||
{
|
||||
if (AppState.DevicesFrameFocusedDevice == device.ID)
|
||||
{
|
||||
return AppState.DevicesFrameFocusedCardState.ToString().ToLower();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
return $"{_state}".ToLower();
|
||||
}
|
||||
|
||||
private string GetProgressMessage(string key)
|
||||
@ -203,7 +206,7 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
{
|
||||
if (IsExpanded)
|
||||
{
|
||||
SetCardStateNormal();
|
||||
_state = DeviceCardState.Normal;
|
||||
}
|
||||
}
|
||||
private async Task HandleValidSubmit()
|
||||
@ -265,12 +268,6 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
JsInterop.OpenWindow($"/device-details/{Device.ID}", "_blank");
|
||||
}
|
||||
|
||||
private void SetCardStateNormal()
|
||||
{
|
||||
AppState.DevicesFrameFocusedDevice = null;
|
||||
AppState.DevicesFrameFocusedCardState = DeviceCardState.Normal;
|
||||
}
|
||||
|
||||
private void ShowAllDisks()
|
||||
{
|
||||
var disksString = JsonSerializer.Serialize(Device.Drives, JsonSerializerHelper.IndentedOptions);
|
||||
@ -338,13 +335,12 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
AppState.DevicesFrameSelectedDevices.Add(Device.ID);
|
||||
SelectedCards.Add(Device.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppState.DevicesFrameSelectedDevices.Remove(Device.ID);
|
||||
SelectedCards.Remove(Device.ID);
|
||||
}
|
||||
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameSelectedDevices));
|
||||
}
|
||||
|
||||
private async Task UninstallAgent()
|
||||
@ -353,8 +349,7 @@ public partial class DeviceCard : AuthComponentBase, IDisposable
|
||||
if (result)
|
||||
{
|
||||
await CircuitConnection.UninstallAgents(new[] { Device.ID });
|
||||
AppState.DevicesFrameFocusedDevice = null;
|
||||
AppState.DevicesFrameFocusedCardState = DeviceCardState.Normal;
|
||||
_state = DeviceCardState.Normal;
|
||||
await ParentFrame.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ using Remotely.Server.Enums;
|
||||
using Remotely.Server.Hubs;
|
||||
using Remotely.Server.Models.Messages;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Shared.Attributes;
|
||||
using Remotely.Shared.Entities;
|
||||
using Remotely.Shared.Utilities;
|
||||
@ -41,7 +42,10 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable
|
||||
private ListSortDirection _sortDirection;
|
||||
|
||||
[Inject]
|
||||
private IClientAppState AppState { get; init; } = null!;
|
||||
private ISelectedCardsStore CardStore { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ITerminalStore TerminalStore { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ICircuitConnection CircuitConnection { get; init; } = null!;
|
||||
@ -63,13 +67,12 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable
|
||||
Messenger.Unregister<ScriptResultMessage, string>(this, CircuitConnection.ConnectionId);
|
||||
Messenger.Unregister<DeviceStateChangedMessage, string>(this, CircuitConnection.ConnectionId);
|
||||
|
||||
AppState.PropertyChanged -= AppState_PropertyChanged;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private async Task HandleDisplayNotificationMessage(DisplayNotificationMessage message)
|
||||
{
|
||||
AppState.AddTerminalLine(message.ConsoleText);
|
||||
TerminalStore.AddTerminalLine(message.ConsoleText);
|
||||
ToastService.ShowToast(message.ToastText, classString: message.ClassName);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
@ -101,8 +104,6 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable
|
||||
CircuitConnection.ConnectionId,
|
||||
HandleScriptResultMessage);
|
||||
|
||||
AppState.PropertyChanged += AppState_PropertyChanged;
|
||||
|
||||
_deviceGroups.Clear();
|
||||
|
||||
_deviceGroups.AddRange(DataService.GetDeviceGroups(UserName));
|
||||
@ -162,36 +163,27 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
AppState.AddTerminalLine($"{deviceResult.Value.DeviceName} @ {result.TimeStamp}", "font-weight-bold");
|
||||
TerminalStore.AddTerminalLine($"{deviceResult.Value.DeviceName} @ {result.TimeStamp}", "font-weight-bold");
|
||||
|
||||
var stdOut = result.StandardOutput ?? Array.Empty<string>();
|
||||
var stdErr = result.ErrorOutput ?? Array.Empty<string>();
|
||||
|
||||
foreach (var line in stdOut)
|
||||
{
|
||||
AppState.AddTerminalLine(line, "text-info");
|
||||
TerminalStore.AddTerminalLine(line, "text-info");
|
||||
}
|
||||
foreach (var line in stdErr)
|
||||
{
|
||||
AppState.AddTerminalLine(line, "text-danger");
|
||||
TerminalStore.AddTerminalLine(line, "text-danger");
|
||||
}
|
||||
AppState.InvokePropertyChanged(nameof(AppState.TerminalLines));
|
||||
TerminalStore.InvokeLinesChanged();
|
||||
}
|
||||
|
||||
private void AppState_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
private async Task ClearSelectedCard()
|
||||
{
|
||||
if (e.PropertyName == nameof(AppState.DevicesFrameFocusedCardState) ||
|
||||
e.PropertyName == nameof(AppState.DevicesFrameFocusedDevice) ||
|
||||
e.PropertyName == nameof(AppState.DevicesFrameSelectedDevices))
|
||||
{
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearSelectedCard()
|
||||
{
|
||||
AppState.DevicesFrameFocusedDevice = string.Empty;
|
||||
AppState.DevicesFrameFocusedCardState = DeviceCardState.Normal;
|
||||
await Messenger.Send(
|
||||
new DeviceCardStateChangedMessage(string.Empty, DeviceCardState.Normal),
|
||||
CircuitConnection.ConnectionId);
|
||||
}
|
||||
|
||||
private async Task FilterDevices()
|
||||
@ -204,7 +196,7 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable
|
||||
|
||||
foreach (var device in _allDevices)
|
||||
{
|
||||
if (AppState.DevicesFrameSelectedDevices.Contains(device.ID))
|
||||
if (CardStore.SelectedDevices.Contains(device.ID))
|
||||
{
|
||||
appendDevices.Add(device);
|
||||
}
|
||||
@ -328,22 +320,19 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable
|
||||
|
||||
private void SelectAllCards()
|
||||
{
|
||||
if (AppState.DevicesFrameSelectedDevices.Any())
|
||||
if (CardStore.SelectedDevices.Any())
|
||||
{
|
||||
AppState.DevicesFrameSelectedDevices.Clear();
|
||||
CardStore.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var device in _filteredDevices)
|
||||
{
|
||||
if (!AppState.DevicesFrameSelectedDevices.Contains(device.ID))
|
||||
{
|
||||
AppState.DevicesFrameSelectedDevices.Add(device.ID);
|
||||
}
|
||||
_ = CardStore.Add(device.ID);
|
||||
};
|
||||
}
|
||||
|
||||
AppState.InvokePropertyChanged(nameof(AppState.DevicesFrameSelectedDevices));
|
||||
CardStore.InvokeSelectionsChanged();
|
||||
}
|
||||
|
||||
private void ToggleSortDirection()
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<div class="terminal-body border border-secondary bg-dark">
|
||||
<div @ref="_terminalWindow" class="terminal-window px-2 py-1">
|
||||
|
||||
@foreach (var line in AppState.TerminalLines)
|
||||
@foreach (var line in TerminalStore.TerminalLines)
|
||||
{
|
||||
<div @key="line.Id" class="ml-1 terminal-line @line.ClassName" title="@line.Title">
|
||||
@line.Text
|
||||
|
||||
@ -8,6 +8,7 @@ using Remotely.Server.Components.ModalContents;
|
||||
using Remotely.Server.Hubs;
|
||||
using Remotely.Server.Models.Messages;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Shared.Entities;
|
||||
using Remotely.Shared.Enums;
|
||||
using Remotely.Shared.Models;
|
||||
@ -32,7 +33,7 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
private ElementReference _terminalWindow;
|
||||
|
||||
[Inject]
|
||||
private IClientAppState AppState { get; init; } = null!;
|
||||
private ISelectedCardsStore CardStore { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ICircuitConnection CircuitConnection { get; init; } = null!;
|
||||
@ -40,9 +41,6 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
[Inject]
|
||||
private IDataService DataService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IMessenger Messenger { get; init; } = null!;
|
||||
|
||||
private string InputText
|
||||
{
|
||||
get => _inputText ?? string.Empty;
|
||||
@ -62,6 +60,8 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
[Inject]
|
||||
private ILogger<Terminal> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IMessenger Messenger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IModalService ModalService { get; init; } = null!;
|
||||
@ -79,22 +79,23 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
RunOnNextConnect = false,
|
||||
Initiator = User.UserName,
|
||||
InputType = ScriptInputType.OneTimeScript,
|
||||
Devices = DataService.GetDevices(AppState.DevicesFrameSelectedDevices)
|
||||
Devices = DataService.GetDevices(CardStore.SelectedDevices)
|
||||
};
|
||||
|
||||
await DataService.AddScriptRun(scriptRun);
|
||||
|
||||
await CircuitConnection.RunScript(AppState.DevicesFrameSelectedDevices, script.Id, scriptRun.Id, ScriptInputType.OneTimeScript, false);
|
||||
await CircuitConnection.RunScript(CardStore.SelectedDevices, script.Id, scriptRun.Id, ScriptInputType.OneTimeScript, false);
|
||||
|
||||
ToastService.ShowToast($"Running script on {scriptRun.Devices.Count} devices.");
|
||||
});
|
||||
|
||||
[Inject]
|
||||
private ITerminalStore TerminalStore { get; init; } = null!;
|
||||
[Inject]
|
||||
private IToastService ToastService { get; init; } = null!;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AppState.PropertyChanged -= AppState_PropertyChanged;
|
||||
Messenger.Unregister<PowerShellCompletionsMessage, string>(this, CircuitConnection.ConnectionId);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
@ -115,26 +116,7 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
this,
|
||||
CircuitConnection.ConnectionId,
|
||||
HandlePowerShellCompletionsMessage);
|
||||
AppState.PropertyChanged += AppState_PropertyChanged;
|
||||
}
|
||||
|
||||
private async Task HandlePowerShellCompletionsMessage(PowerShellCompletionsMessage message)
|
||||
{
|
||||
var completion = message.Completion;
|
||||
var intent = message.Intent;
|
||||
|
||||
switch (intent)
|
||||
{
|
||||
case CompletionIntent.ShowAll:
|
||||
await DisplayCompletions(completion.CompletionMatches);
|
||||
break;
|
||||
case CompletionIntent.NextResult:
|
||||
ApplyCompletion(completion);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
AppState.InvokePropertyChanged(nameof(AppState.TerminalLines));
|
||||
TerminalStore.TerminalLinesChanged += TerminalStore_TerminalLinesChanged;
|
||||
}
|
||||
|
||||
private void ApplyCompletion(PwshCommandCompletion completion)
|
||||
@ -161,21 +143,12 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void AppState_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(AppState.TerminalLines))
|
||||
{
|
||||
InvokeAsync(StateHasChanged);
|
||||
JsInterop.ScrollToEnd(_terminalWindow);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DisplayCompletions(List<PwshCompletionResult> completionMatches)
|
||||
{
|
||||
var deviceId = AppState.DevicesFrameSelectedDevices.FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(deviceId))
|
||||
{
|
||||
return;
|
||||
var deviceId = CardStore.SelectedDevices.FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(deviceId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceResult = await DataService.GetDevice(deviceId);
|
||||
@ -186,15 +159,16 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
AppState.AddTerminalLine(
|
||||
$"Completions for {deviceResult.Value.DeviceName}",
|
||||
TerminalStore.AddTerminalLine(
|
||||
$"Completions for {deviceResult.Value.DeviceName}",
|
||||
className: "font-weight-bold");
|
||||
|
||||
foreach (var match in completionMatches)
|
||||
{
|
||||
AppState.AddTerminalLine(match.CompletionText, className: "", title: match.ToolTip);
|
||||
TerminalStore.AddTerminalLine(match.CompletionText, className: "", title: match.ToolTip);
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateInputKeypress(KeyboardEventArgs ev)
|
||||
{
|
||||
if (ev.Key.Equals("Enter", StringComparison.OrdinalIgnoreCase))
|
||||
@ -204,14 +178,14 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var devices = AppState.DevicesFrameSelectedDevices.ToArray();
|
||||
var devices = CardStore.SelectedDevices.ToArray();
|
||||
if (!devices.Any())
|
||||
{
|
||||
ToastService.ShowToast("You must select at least one device.", classString: "bg-warning");
|
||||
return;
|
||||
}
|
||||
CircuitConnection.ExecuteCommandOnAgent(_shell, InputText, devices);
|
||||
AppState.AddTerminalHistory(InputText);
|
||||
TerminalStore.AddTerminalHistory(InputText);
|
||||
InputText = string.Empty;
|
||||
}
|
||||
}
|
||||
@ -227,11 +201,11 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
|
||||
if (ev.Key.Equals("ArrowUp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
InputText = AppState.GetTerminalHistory(false);
|
||||
InputText = TerminalStore.GetTerminalHistory(false);
|
||||
}
|
||||
else if (ev.Key.Equals("ArrowDown", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
InputText = AppState.GetTerminalHistory(true);
|
||||
InputText = TerminalStore.GetTerminalHistory(true);
|
||||
}
|
||||
else if (ev.Key.Equals("Tab", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@ -241,7 +215,7 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AppState.DevicesFrameSelectedDevices.Any())
|
||||
if (!CardStore.SelectedDevices.Any())
|
||||
{
|
||||
ToastService.ShowToast("No devices are selected.", classString: "bg-warning");
|
||||
return;
|
||||
@ -256,7 +230,7 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
}
|
||||
else if (ev.CtrlKey && ev.Key.Equals(" ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!AppState.DevicesFrameSelectedDevices.Any())
|
||||
if (!CardStore.SelectedDevices.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -265,8 +239,8 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
}
|
||||
else if (ev.CtrlKey && ev.Key.Equals("q", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AppState.TerminalLines.Clear();
|
||||
AppState.InvokePropertyChanged(nameof(AppState.TerminalLines));
|
||||
TerminalStore.TerminalLines.Clear();
|
||||
TerminalStore.InvokeLinesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,6 +255,25 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
await CircuitConnection.GetPowerShellCompletions(_lastCompletionInput, _lastCursorIndex, CompletionIntent.NextResult, forward);
|
||||
}
|
||||
|
||||
private async Task HandlePowerShellCompletionsMessage(PowerShellCompletionsMessage message)
|
||||
{
|
||||
var completion = message.Completion;
|
||||
var intent = message.Intent;
|
||||
|
||||
switch (intent)
|
||||
{
|
||||
case CompletionIntent.ShowAll:
|
||||
await DisplayCompletions(completion.CompletionMatches);
|
||||
break;
|
||||
case CompletionIntent.NextResult:
|
||||
ApplyCompletion(completion);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
TerminalStore.InvokeLinesChanged();
|
||||
}
|
||||
|
||||
private async Task ShowAllCompletions()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_lastCompletionInput))
|
||||
@ -303,7 +296,7 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AppState.DevicesFrameSelectedDevices.Any())
|
||||
if (!CardStore.SelectedDevices.Any())
|
||||
{
|
||||
ToastService.ShowToast("You must select at least one device.", classString: "bg-warning");
|
||||
return;
|
||||
@ -342,6 +335,12 @@ public partial class Terminal : AuthComponentBase, IDisposable
|
||||
"starting on the remote device."
|
||||
});
|
||||
}
|
||||
|
||||
private async void TerminalStore_TerminalLinesChanged(object? sender, EventArgs e)
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
JsInterop.ScrollToEnd(_terminalWindow);
|
||||
}
|
||||
private void ToggleTerminalOpen()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_terminalOpenClass))
|
||||
|
||||
@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Remotely.Server.Models;
|
||||
using Remotely.Server.Models.Messages;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Shared;
|
||||
using Remotely.Shared.Entities;
|
||||
using Remotely.Shared.Enums;
|
||||
@ -71,7 +72,7 @@ public class CircuitConnection : CircuitHandler, ICircuitConnection
|
||||
{
|
||||
private readonly IHubContext<AgentHub, IAgentHubClient> _agentHubContext;
|
||||
private readonly IApplicationConfig _appConfig;
|
||||
private readonly IClientAppState _appState;
|
||||
private readonly ISelectedCardsStore _cardStore;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ICircuitManager _circuitManager;
|
||||
private readonly IDataService _dataService;
|
||||
@ -86,7 +87,7 @@ public class CircuitConnection : CircuitHandler, ICircuitConnection
|
||||
public CircuitConnection(
|
||||
IAuthService authService,
|
||||
IDataService dataService,
|
||||
IClientAppState appState,
|
||||
ISelectedCardsStore cardStore,
|
||||
IHubContext<AgentHub, IAgentHubClient> agentHubContext,
|
||||
IApplicationConfig appConfig,
|
||||
ICircuitManager circuitManager,
|
||||
@ -99,7 +100,7 @@ public class CircuitConnection : CircuitHandler, ICircuitConnection
|
||||
{
|
||||
_dataService = dataService;
|
||||
_agentHubContext = agentHubContext;
|
||||
_appState = appState;
|
||||
_cardStore = cardStore;
|
||||
_appConfig = appConfig;
|
||||
_authService = authService;
|
||||
_circuitManager = circuitManager;
|
||||
@ -160,7 +161,7 @@ public class CircuitConnection : CircuitHandler, ICircuitConnection
|
||||
|
||||
public Task GetPowerShellCompletions(string inputText, int currentIndex, CompletionIntent intent, bool? forward)
|
||||
{
|
||||
var device = _appState.DevicesFrameSelectedDevices.FirstOrDefault();
|
||||
var device = _cardStore.SelectedDevices.FirstOrDefault();
|
||||
if (device is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
||||
5
Server/Models/Messages/DeviceCardStateChangedMessage.cs
Normal file
5
Server/Models/Messages/DeviceCardStateChangedMessage.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using Remotely.Server.Enums;
|
||||
|
||||
namespace Remotely.Server.Models.Messages;
|
||||
|
||||
public record DeviceCardStateChangedMessage(string DeviceId, DeviceCardState State);
|
||||
@ -31,6 +31,7 @@ using Microsoft.AspNetCore.RateLimiting;
|
||||
using RatePolicyNames = Remotely.Server.RateLimiting.PolicyNames;
|
||||
using Remotely.Shared.Entities;
|
||||
using Immense.SimpleMessenger;
|
||||
using Remotely.Server.Services.Stores;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var configuration = builder.Configuration;
|
||||
@ -204,14 +205,15 @@ services.AddScoped<ILoaderService, LoaderService>();
|
||||
services.AddScoped(x => (CircuitHandler)x.GetRequiredService<ICircuitConnection>());
|
||||
services.AddSingleton<ICircuitManager, CircuitManager>();
|
||||
services.AddScoped<IAuthService, AuthService>();
|
||||
services.AddScoped<IClientAppState, ClientAppState>();
|
||||
services.AddScoped<ISelectedCardsStore, SelectedCardsStore>();
|
||||
services.AddScoped<IExpiringTokenService, ExpiringTokenService>();
|
||||
services.AddScoped<IScriptScheduleDispatcher, ScriptScheduleDispatcher>();
|
||||
services.AddSingleton<IOtpProvider, OtpProvider>();
|
||||
services.AddSingleton<IEmbeddedServerDataSearcher, EmbeddedServerDataSearcher>();
|
||||
services.AddSingleton<ILogsManager, LogsManager>();
|
||||
services.AddScoped<IThemeProvider, ThemeProvider>();
|
||||
services.AddScoped<IChatSessionCache, ChatSessionCache>();
|
||||
services.AddScoped<IChatSessionStore, ChatSessionStore>();
|
||||
services.AddScoped<ITerminalStore, TerminalStore>();
|
||||
services.AddSingleton(WeakReferenceMessenger.Default);
|
||||
|
||||
services.AddRemoteControlServer(config =>
|
||||
|
||||
@ -5,13 +5,13 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Remotely.Server.Services;
|
||||
namespace Remotely.Server.Services.Stores;
|
||||
|
||||
public interface IChatSessionCache
|
||||
public interface IChatSessionStore
|
||||
{
|
||||
void AddOrUpdate(
|
||||
string deviceId,
|
||||
ChatSession chatSession,
|
||||
string deviceId,
|
||||
ChatSession chatSession,
|
||||
Func<string, ChatSession, ChatSession> updateFactory);
|
||||
|
||||
bool ContainsKey(string deviceId);
|
||||
@ -21,13 +21,13 @@ public interface IChatSessionCache
|
||||
bool TryRemove(string deviceId, [NotNullWhen(true)] out ChatSession? session);
|
||||
}
|
||||
|
||||
public class ChatSessionCache : IChatSessionCache
|
||||
public class ChatSessionStore : IChatSessionStore
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, ChatSession> _sessions = new();
|
||||
|
||||
public void AddOrUpdate(
|
||||
string deviceId,
|
||||
ChatSession chatSession,
|
||||
string deviceId,
|
||||
ChatSession chatSession,
|
||||
Func<string, ChatSession, ChatSession> updateFactory)
|
||||
{
|
||||
_sessions.AddOrUpdate(deviceId, chatSession, updateFactory);
|
||||
70
Server/Services/Stores/SelectedCardsStore.cs
Normal file
70
Server/Services/Stores/SelectedCardsStore.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Remotely.Server.Services.Stores;
|
||||
|
||||
public interface ISelectedCardsStore
|
||||
{
|
||||
event EventHandler? SelectedDevicesChanged;
|
||||
IEnumerable<string> SelectedDevices { get; }
|
||||
bool Add(string deviceId);
|
||||
bool Contains(string deviceId);
|
||||
|
||||
bool Remove(string deviceId);
|
||||
void Clear();
|
||||
void InvokeSelectionsChanged();
|
||||
}
|
||||
|
||||
public class SelectedCardsStore : ISelectedCardsStore
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly HashSet<string> _selectedDevices = new();
|
||||
|
||||
public IEnumerable<string> SelectedDevices
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _selectedDevices.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler? SelectedDevicesChanged;
|
||||
|
||||
public bool Add(string deviceId)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _selectedDevices.Add(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_selectedDevices.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(string deviceId)
|
||||
{
|
||||
lock ( _lock)
|
||||
{
|
||||
return _selectedDevices.Contains(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvokeSelectionsChanged()
|
||||
{
|
||||
SelectedDevicesChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public bool Remove(string deviceId)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _selectedDevices.Remove(deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,13 @@
|
||||
using Remotely.Server.Enums;
|
||||
using Remotely.Shared.Enums;
|
||||
using Remotely.Shared.Primitives;
|
||||
using Remotely.Shared.ViewModels;
|
||||
using Remotely.Shared.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Server.Services;
|
||||
namespace Remotely.Server.Services.Stores;
|
||||
|
||||
public interface IClientAppState : INotifyPropertyChanged, IInvokePropertyChanged
|
||||
public interface ITerminalStore
|
||||
{
|
||||
DeviceCardState DevicesFrameFocusedCardState { get; set; }
|
||||
string? DevicesFrameFocusedDevice { get; set; }
|
||||
ConcurrentList<string> DevicesFrameSelectedDevices { get; }
|
||||
event EventHandler? TerminalLinesChanged;
|
||||
ConcurrentQueue<TerminalLineItem> TerminalLines { get; }
|
||||
|
||||
void AddTerminalHistory(string content);
|
||||
@ -21,29 +15,18 @@ public interface IClientAppState : INotifyPropertyChanged, IInvokePropertyChange
|
||||
void AddTerminalLine(string content, string className = "", string title = "");
|
||||
|
||||
string GetTerminalHistory(bool forward);
|
||||
}
|
||||
|
||||
public class ClientAppState : ViewModelBase, IClientAppState
|
||||
void InvokeLinesChanged();
|
||||
}
|
||||
public class TerminalStore : ITerminalStore
|
||||
{
|
||||
private readonly ConcurrentQueue<string> _terminalHistory = new();
|
||||
private int _terminalHistoryIndex = 0;
|
||||
|
||||
public DeviceCardState DevicesFrameFocusedCardState
|
||||
{
|
||||
get => Get<DeviceCardState>();
|
||||
set => Set(value);
|
||||
}
|
||||
|
||||
public string? DevicesFrameFocusedDevice
|
||||
{
|
||||
get => Get<string>();
|
||||
set => Set(value);
|
||||
}
|
||||
|
||||
public ConcurrentList<string> DevicesFrameSelectedDevices { get; } = new();
|
||||
public event EventHandler? TerminalLinesChanged;
|
||||
|
||||
public ConcurrentQueue<TerminalLineItem> TerminalLines { get; } = new();
|
||||
|
||||
public void AddTerminalHistory(string content)
|
||||
{
|
||||
while (_terminalHistory.Count > 500)
|
||||
@ -92,4 +75,9 @@ public class ClientAppState : ViewModelBase, IClientAppState
|
||||
}
|
||||
return _terminalHistory.ElementAt(_terminalHistoryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvokeLinesChanged()
|
||||
{
|
||||
TerminalLinesChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,17 @@
|
||||
#nullable enable
|
||||
using Castle.Core.Logging;
|
||||
using Immense.RemoteControl.Server.Services;
|
||||
using Immense.SimpleMessenger;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Remotely.Server.Hubs;
|
||||
using Remotely.Server.Services;
|
||||
using Remotely.Server.Services.Stores;
|
||||
using Remotely.Server.Tests.Mocks;
|
||||
using Remotely.Shared.Dtos;
|
||||
using Remotely.Shared.Extensions;
|
||||
using Remotely.Shared.Interfaces;
|
||||
using Remotely.Shared.Models;
|
||||
using Remotely.Tests;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Remotely.Server.Tests;
|
||||
@ -31,7 +23,7 @@ public class CircuitConnectionTests
|
||||
private TestData _testData;
|
||||
private IDataService _dataService;
|
||||
private Mock<IAuthService> _authService;
|
||||
private Mock<IClientAppState> _clientAppState;
|
||||
private Mock<ISelectedCardsStore> _clientAppState;
|
||||
private HubContextFixture<AgentHub, IAgentHubClient> _agentHubContextFixture;
|
||||
private Mock<IApplicationConfig> _appConfig;
|
||||
private Mock<ICircuitManager> _circuitManager;
|
||||
@ -52,7 +44,7 @@ public class CircuitConnectionTests
|
||||
|
||||
_dataService = IoCActivator.ServiceProvider.GetRequiredService<IDataService>();
|
||||
_authService = new Mock<IAuthService>();
|
||||
_clientAppState = new Mock<IClientAppState>();
|
||||
_clientAppState = new Mock<ISelectedCardsStore>();
|
||||
_agentHubContextFixture = new HubContextFixture<AgentHub, IAgentHubClient>();
|
||||
_appConfig = new Mock<IApplicationConfig>();
|
||||
_circuitManager = new Mock<ICircuitManager>();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user