diff --git a/Server/Components/Devices/DevicesFrame.razor b/Server/Components/Devices/DevicesFrame.razor
index bc4c3cee6..67b7326dd 100644
--- a/Server/Components/Devices/DevicesFrame.razor
+++ b/Server/Components/Devices/DevicesFrame.razor
@@ -86,7 +86,7 @@
- @foreach (var device in _devicesForPage)
+ @foreach (var device in DisplayedDevices)
{
diff --git a/Server/Components/Devices/DevicesFrame.razor.cs b/Server/Components/Devices/DevicesFrame.razor.cs
index 9b73f7fa5..9d6a7288c 100644
--- a/Server/Components/Devices/DevicesFrame.razor.cs
+++ b/Server/Components/Devices/DevicesFrame.razor.cs
@@ -1,7 +1,9 @@
using Immense.SimpleMessenger;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
+using Microsoft.Build.Framework;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
using Remotely.Server.Enums;
using Remotely.Server.Hubs;
using Remotely.Server.Models.Messages;
@@ -29,14 +31,15 @@ public partial class DevicesFrame : AuthComponentBase
private readonly string _deviceGroupAll = Guid.NewGuid().ToString();
private readonly string _deviceGroupNone = Guid.NewGuid().ToString();
private readonly List _deviceGroups = new();
- private readonly List _devicesForPage = new();
private readonly SemaphoreSlim _devicesLock = new(1,1);
private readonly List _filteredDevices = new();
+ private readonly List _prependedDevices = new();
private readonly List _sortableProperties = new();
private int _currentPage = 1;
private int _devicesPerPage = 25;
private string? _filter;
private bool _hideOfflineDevices = true;
+ private string _lastFilterState = string.Empty;
private string? _selectedGroupId;
private string _selectedSortProperty = "DeviceName";
private ListSortDirection _sortDirection;
@@ -44,29 +47,42 @@ public partial class DevicesFrame : AuthComponentBase
[Inject]
private ISelectedCardsStore CardStore { get; init; } = null!;
- [Inject]
- private ITerminalStore TerminalStore { get; init; } = null!;
-
[Inject]
private ICircuitConnection CircuitConnection { get; init; } = null!;
+ private string CurrentFilterState
+ {
+ get
+ {
+ return
+ $"{_filter}" +
+ $"{_selectedGroupId}|" +
+ $"{_selectedSortProperty}|" +
+ $"{_sortDirection}|" +
+ $"{_hideOfflineDevices}|" +
+ $"{_currentPage}|" +
+ $"{_devicesPerPage}|";
+ }
+ }
[Inject]
private IDataService DataService { get; init; } = null!;
+ private Device[] DisplayedDevices => GetDisplayedDevices();
+
+ [Inject]
+ private ILogger Logger { get; init; } = null!;
+
+ [Inject]
+ private ITerminalStore TerminalStore { get; init; } = null!;
+
[Inject]
private IToastService ToastService { get; init; } = null!;
private int TotalPages => (int)Math.Max(1, Math.Ceiling((decimal)_filteredDevices.Count / _devicesPerPage));
- private async Task HandleDisplayNotificationMessage(DisplayNotificationMessage message)
- {
- TerminalStore.AddTerminalLine(message.ConsoleText);
- ToastService.ShowToast(message.ToastText, classString: message.ClassName);
- await InvokeAsync(StateHasChanged);
- }
-
public async Task Refresh()
{
+ _lastFilterState = string.Empty;
await LoadDevices();
await InvokeAsync(StateHasChanged);
}
@@ -104,42 +120,6 @@ await Register(
await LoadDevices();
}
- private async Task HandleScriptResultMessage(ScriptResultMessage message)
- {
- await AddScriptResult(message.ScriptResult);
- }
-
- private async Task HandleDeviceStateChangedMessage(DeviceStateChangedMessage message)
- {
- await _devicesLock.WaitAsync();
-
- try
- {
- var device = message.Device;
-
- foreach (var collection in new[] { _allDevices, _devicesForPage })
- {
- var index = collection.FindIndex(x => x.ID == device.ID);
- if (index > -1)
- {
- collection[index] = device;
- }
- }
-
- Debouncer.Debounce(TimeSpan.FromSeconds(2), Refresh);
- }
- finally
- {
- _devicesLock.Release();
- }
- }
-
- protected override async Task OnAfterRenderAsync(bool firstRender)
- {
- await base.OnAfterRenderAsync(firstRender);
- await FilterDevices();
- }
-
private async Task AddScriptResult(ScriptResult result)
{
var deviceResult = await DataService.GetDevice(result.DeviceID);
@@ -167,88 +147,99 @@ private async Task AddScriptResult(ScriptResult result)
private async Task ClearSelectedCard()
{
await Messenger.Send(
- new DeviceCardStateChangedMessage(string.Empty, DeviceCardState.Normal),
+ new DeviceCardStateChangedMessage(string.Empty, DeviceCardState.Normal),
CircuitConnection.ConnectionId);
}
- private async Task FilterDevices()
+ private void FilterAndSortDevices()
{
- await _devicesLock.WaitAsync();
- try
- {
- _filteredDevices.Clear();
- var appendDevices = new List();
+ _filteredDevices.Clear();
+ _prependedDevices.Clear();
- foreach (var device in _allDevices)
+ foreach (var device in _allDevices)
+ {
+ if (CardStore.SelectedDevices.Contains(device.ID))
{
- if (CardStore.SelectedDevices.Contains(device.ID))
- {
- appendDevices.Add(device);
- }
+ _prependedDevices.Add(device);
+ }
- if (!device.IsOnline && _hideOfflineDevices)
- {
- continue;
- }
+ if (!device.IsOnline && _hideOfflineDevices)
+ {
+ continue;
+ }
- if (!string.IsNullOrWhiteSpace(_filter) &&
- device.Alias?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
- device.CurrentUser?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
- device.DeviceName?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
- device.Notes?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
- device.Platform?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
- device.Tags?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true)
- {
- continue;
- }
+ if (!string.IsNullOrWhiteSpace(_filter) &&
+ device.Alias?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
+ device.CurrentUser?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
+ device.DeviceName?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
+ device.Notes?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
+ device.Platform?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true &&
+ device.Tags?.Contains(_filter, StringComparison.OrdinalIgnoreCase) != true)
+ {
+ continue;
+ }
- if (_selectedGroupId == _deviceGroupAll ||
- _selectedGroupId == device.DeviceGroupID ||
- (
- _selectedGroupId == _deviceGroupNone &&
- string.IsNullOrWhiteSpace(device.DeviceGroupID
- )))
- {
- _filteredDevices.Add(device);
- }
+ if (_selectedGroupId == _deviceGroupAll ||
+ _selectedGroupId == device.DeviceGroupID ||
+ (
+ _selectedGroupId == _deviceGroupNone &&
+ string.IsNullOrWhiteSpace(device.DeviceGroupID
+ )))
+ {
+ _filteredDevices.Add(device);
}
+ }
- if (!string.IsNullOrWhiteSpace(_selectedSortProperty))
+ if (!string.IsNullOrWhiteSpace(_selectedSortProperty))
+ {
+ var direction = _sortDirection == ListSortDirection.Ascending ? 1 : -1;
+ _filteredDevices.Sort((a, b) =>
{
- var direction = _sortDirection == ListSortDirection.Ascending ? 1 : -1;
- _filteredDevices.Sort((a, b) =>
+ if (a.IsOnline != b.IsOnline)
{
- if (a.IsOnline != b.IsOnline)
- {
- return b.IsOnline.CompareTo(a.IsOnline);
- }
+ return b.IsOnline.CompareTo(a.IsOnline);
+ }
- var propInfo = _sortableProperties.Find(x => x.Name == _selectedSortProperty);
+ var propInfo = _sortableProperties.Find(x => x.Name == _selectedSortProperty);
- var valueA = propInfo?.GetValue(a);
- var valueB = propInfo?.GetValue(b);
+ var valueA = propInfo?.GetValue(a);
+ var valueB = propInfo?.GetValue(b);
- return Comparer.Default.Compare(valueA, valueB) * direction;
- });
+ return Comparer.Default.Compare(valueA, valueB) * direction;
+ });
+ }
+ }
+
+ private Device[] GetDisplayedDevices()
+ {
+ _devicesLock.Wait();
+ try
+ {
+ if (CurrentFilterState != _lastFilterState)
+ {
+ _lastFilterState = CurrentFilterState;
+ FilterAndSortDevices();
}
var skipCount = (_currentPage - 1) * _devicesPerPage;
var devicesForPage = _filteredDevices
- .Except(appendDevices)
+ .Except(_prependedDevices)
.Skip(skipCount)
.Take(_devicesPerPage);
- _devicesForPage.Clear();
- _devicesForPage.AddRange(appendDevices.Concat(devicesForPage));
-
+ return _prependedDevices.Concat(devicesForPage).ToArray();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Error while filtering devices.");
+ ToastService.ShowToast2("Filter devices failed", ToastType.Error);
+ return Array.Empty();
}
finally
{
_devicesLock.Release();
}
}
-
-
private string GetDisplayName(PropertyInfo propInfo)
{
return propInfo.GetCustomAttribute()?.Name ?? propInfo.Name;
@@ -259,12 +250,58 @@ private string GetSortIcon()
return $"oi-sort-{_sortDirection.ToString().ToLower()}";
}
+ private async Task HandleDeviceStateChangedMessage(DeviceStateChangedMessage message)
+ {
+ await _devicesLock.WaitAsync();
+
+ try
+ {
+ var device = message.Device;
+
+ var collections = new[] { _allDevices, _filteredDevices };
+
+ foreach (var collection in collections)
+ {
+ var index = collection.FindIndex(x => x.ID == device.ID);
+ if (index > -1)
+ {
+ collection[index] = device;
+ }
+ else
+ {
+ collection.Add(device);
+ }
+ }
+
+ Debouncer.Debounce(
+ TimeSpan.FromSeconds(2),
+ async () =>
+ {
+ await InvokeAsync(StateHasChanged);
+ });
+ }
+ finally
+ {
+ _devicesLock.Release();
+ }
+ }
+
+ private async Task HandleDisplayNotificationMessage(DisplayNotificationMessage message)
+ {
+ TerminalStore.AddTerminalLine(message.ConsoleText);
+ ToastService.ShowToast(message.ToastText, classString: message.ClassName);
+ await InvokeAsync(StateHasChanged);
+ }
private async Task HandleRefreshClicked()
{
await Refresh();
ToastService.ShowToast("Devices refreshed.");
}
+ private async Task HandleScriptResultMessage(ScriptResultMessage message)
+ {
+ await AddScriptResult(message.ScriptResult);
+ }
private async Task LoadDevices()
{
EnsureUserSet();
@@ -284,8 +321,6 @@ private async Task LoadDevices()
{
_devicesLock.Release();
}
-
- await FilterDevices();
}
private void PageDown()
{
diff --git a/Shared/Entities/Device.cs b/Shared/Entities/Device.cs
index e37e74c3d..b77533d42 100644
--- a/Shared/Entities/Device.cs
+++ b/Shared/Entities/Device.cs
@@ -50,7 +50,6 @@ public class Device
[Display(Name = "Last Online")]
public DateTimeOffset LastOnline { get; set; }
- [Sortable]
[Display(Name = "MAC Addresses")]
public string[] MacAddresses { get; set; } = Array.Empty();