diff --git a/.idea/.idea.Nebula/.idea/avalonia.xml b/.idea/.idea.Nebula/.idea/avalonia.xml
index 1aea4d7..50bc6a6 100644
--- a/.idea/.idea.Nebula/.idea/avalonia.xml
+++ b/.idea/.idea.Nebula/.idea/avalonia.xml
@@ -35,6 +35,7 @@
+
diff --git a/Nebula.Launcher/Controls/ServerListView.axaml.cs b/Nebula.Launcher/Controls/ServerListView.axaml.cs
index 4eaf9b8..085e514 100644
--- a/Nebula.Launcher/Controls/ServerListView.axaml.cs
+++ b/Nebula.Launcher/Controls/ServerListView.axaml.cs
@@ -49,7 +49,7 @@ public partial class ServerListView : UserControl
{
if (rawView is ServerEntryModelView serverEntryModelView)
{
- serverEntryModelView.UpdateStatusIfNecessary();
+ //serverEntryModelView.UpdateStatusIfNecessary();
}
}
}
@@ -61,9 +61,10 @@ public partial class ServerListView : UserControl
if(IsLoading)
return;
- foreach (IFilterConsumer? serverView in ServerList.Items)
+ foreach (var serverView in ServerList.Items)
{
- serverView?.ProcessFilter(filter);
+ if(serverView is IFilterConsumer filterConsumer)
+ filterConsumer.ProcessFilter(filter);
}
}
@@ -83,7 +84,8 @@ public partial class ServerListView : UserControl
foreach (var serverEntry in _provider.GetServers())
{
ServerList.Items.Add(serverEntry);
- serverEntry.ProcessFilter(_currentFilter);
+ if(serverEntry is IFilterConsumer serverFilter)
+ serverFilter.ProcessFilter(_currentFilter);
}
foreach (var error in _provider.GetErrors())
diff --git a/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs b/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs
index 114f971..468e067 100644
--- a/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs
+++ b/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs
@@ -23,13 +23,13 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
[GenerateProperty] private IServiceProvider ServiceProvider { get; }
[GenerateProperty] private ServerViewContainer ServerViewContainer { get; }
- private List _serverLists = [];
+ private List _serverLists = [];
private string[] rawServerLists = [];
public bool IsLoaded { get; private set; }
public Action? OnLoaded { get; set; }
public Action? Dirty { get; set; }
- public IEnumerable GetServers()
+ public IEnumerable GetServers()
{
return _serverLists;
}
@@ -44,13 +44,13 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
IsLoaded = false;
_serverLists.Clear();
var servers = GetFavoriteEntries();
-
- _serverLists.AddRange(
- servers.Select(s =>
- ServerViewContainer.Get(s.ToRobustUrl())
- )
+
+ var serverEntries = servers.Select(s =>
+ ServerViewContainer.Get(s.ToRobustUrl())
);
+ _serverLists.AddRange(serverEntries);
+
_serverLists.Add(new AddFavoriteButton(ServiceProvider));
IsLoaded = true;
@@ -67,7 +67,7 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
var servers = GetFavoriteEntries();
servers.Add(robustUrl.ToString());
ConfigurationService.SetConfigValue(LauncherConVar.Favorites, servers.ToArray());
- ServerViewContainer.Get(robustUrl).IsFavorite = true;
+ if(ServerViewContainer.Get(robustUrl) is IFavoriteEntryModelView favoriteView) favoriteView.IsFavorite = true;
}
public void RemoveFavorite(ServerEntryModelView entryModelView)
@@ -76,6 +76,13 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
servers.Remove(entryModelView.Address.ToString());
ConfigurationService.SetConfigValue(LauncherConVar.Favorites, servers.ToArray());
}
+
+ public void RemoveFavorite(RobustUrl url)
+ {
+ var servers = GetFavoriteEntries();
+ servers.Remove(url.ToString());
+ ConfigurationService.SetConfigValue(LauncherConVar.Favorites, servers.ToArray());
+ }
private List GetFavoriteEntries()
{
@@ -103,7 +110,7 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
private void InitialiseInDesignMode(){}
}
-public class AddFavoriteButton: Border, IFilterConsumer{
+public class AddFavoriteButton: Border, IListEntryModelView{
private Button _addFavoriteButton = new Button();
public AddFavoriteButton(IServiceProvider serviceProvider)
@@ -120,8 +127,5 @@ public class AddFavoriteButton: Border, IFilterConsumer{
_addFavoriteButton.Content = "Add Favorite";
Child = _addFavoriteButton;
}
-
- public void ProcessFilter(ServerFilter? serverFilter)
- {
- }
+ public bool IsFavorite { get; set; }
}
\ No newline at end of file
diff --git a/Nebula.Launcher/ServerListProviders/HubServerListProvider.cs b/Nebula.Launcher/ServerListProviders/HubServerListProvider.cs
index c4ef744..6affa62 100644
--- a/Nebula.Launcher/ServerListProviders/HubServerListProvider.cs
+++ b/Nebula.Launcher/ServerListProviders/HubServerListProvider.cs
@@ -24,7 +24,7 @@ public sealed partial class HubServerListProvider : IServerListProvider
public Action? OnLoaded { get; set; }
private CancellationTokenSource? _cts;
- private readonly List _servers = [];
+ private readonly List _servers = [];
private readonly List _errors = [];
public HubServerListProvider With(string hubUrl)
@@ -33,7 +33,7 @@ public sealed partial class HubServerListProvider : IServerListProvider
return this;
}
- public IEnumerable GetServers()
+ public IEnumerable GetServers()
{
return _servers;
}
diff --git a/Nebula.Launcher/ServerListProviders/IServerListProvider.cs b/Nebula.Launcher/ServerListProviders/IServerListProvider.cs
index 13d6ccb..d7d2528 100644
--- a/Nebula.Launcher/ServerListProviders/IServerListProvider.cs
+++ b/Nebula.Launcher/ServerListProviders/IServerListProvider.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Nebula.Launcher.ViewModels;
+using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.ServerListProviders;
@@ -9,7 +10,7 @@ public interface IServerListProvider
public bool IsLoaded { get; }
public Action? OnLoaded { get; set; }
- public IEnumerable GetServers();
+ public IEnumerable GetServers();
public IEnumerable GetErrors();
public void LoadServerList();
diff --git a/Nebula.Launcher/ServerListProviders/TestServerList.cs b/Nebula.Launcher/ServerListProviders/TestServerList.cs
index 5bc0e88..dca0fe0 100644
--- a/Nebula.Launcher/ServerListProviders/TestServerList.cs
+++ b/Nebula.Launcher/ServerListProviders/TestServerList.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Nebula.Launcher.Controls;
using Nebula.Launcher.ViewModels;
+using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.ServerListProviders;
@@ -9,7 +10,7 @@ public sealed class TestServerList : IServerListProvider
{
public bool IsLoaded => true;
public Action? OnLoaded { get; set; }
- public IEnumerable GetServers()
+ public IEnumerable GetServers()
{
return [new ServerEntryModelView(),new ServerEntryModelView()];
}
diff --git a/Nebula.Launcher/ViewModels/Pages/ConfigurationViewModel.cs b/Nebula.Launcher/ViewModels/Pages/ConfigurationViewModel.cs
index 6beed55..61bdc0a 100644
--- a/Nebula.Launcher/ViewModels/Pages/ConfigurationViewModel.cs
+++ b/Nebula.Launcher/ViewModels/Pages/ConfigurationViewModel.cs
@@ -125,12 +125,6 @@ public static class ConfigControlHelper{
public static object? CreateDefaultValue(Type type)
{
- if (type == typeof(string))
- return string.Empty;
- if (type == typeof(int))
- return 0;
- if (type == typeof(float))
- return 0f;
if(type.IsValueType)
return Activator.CreateInstance(type);
diff --git a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs
index b165173..2204f6d 100644
--- a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs
+++ b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs
@@ -29,9 +29,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase, IContentHol
[ObservableProperty] private string _serverText = "";
[ObservableProperty] private string _searchText = "";
[GenerateProperty] private ContentService ContentService { get; } = default!;
- [GenerateProperty] private CancellationService CancellationService { get; } = default!;
[GenerateProperty] private FileService FileService { get; } = default!;
- [GenerateProperty] private DebugService DebugService { get; } = default!;
[GenerateProperty] private PopupMessageService PopupService { get; } = default!;
[GenerateProperty] private IServiceProvider ServiceProvider { get; }
[GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!;
diff --git a/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs b/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs
index de040e0..e006757 100644
--- a/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs
+++ b/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using System.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.Controls;
@@ -89,7 +90,8 @@ public partial class ServerOverviewModel : ViewModelBase
{
foreach (var entry in ServerViewContainer.Items)
{
- entry.ProcessFilter(CurrentFilter);
+ if(entry is IFilterConsumer filterConsumer)
+ filterConsumer.ProcessFilter(CurrentFilter);
}
}
@@ -109,6 +111,7 @@ public partial class ServerOverviewModel : ViewModelBase
public void UpdateRequired()
{
+ ServerViewContainer.Clear();
CurrentServerList.RefreshFromProvider();
CurrentServerList.RequireStatusUpdate();
CurrentServerList.ApplyFilter(CurrentFilter);
@@ -150,25 +153,25 @@ public class ServerViewContainer
foreach (var favorite in favorites)
{
- if (_entries.TryGetValue(favorite, out var entry))
+ if (_entries.TryGetValue(favorite, out var entry) && entry is IFavoriteEntryModelView favoriteView)
{
- entry.IsFavorite = true;
+ favoriteView.IsFavorite = true;
}
}
}
- private readonly Dictionary _entries = new();
+ private readonly Dictionary _entries = new();
- public ICollection Items => _entries.Values;
+ public ICollection Items => _entries.Values;
public void Clear()
{
_entries.Clear();
}
- public ServerEntryModelView Get(RobustUrl url, ServerStatus? serverStatus = null)
+ public IListEntryModelView Get(RobustUrl url, ServerStatus? serverStatus = null)
{
- ServerEntryModelView? entry;
+ IListEntryModelView? entry;
lock (_entries)
{
@@ -177,9 +180,13 @@ public class ServerViewContainer
return entry;
}
- entry = _viewHelperService.GetViewModel().WithData(url, serverStatus);
+ if (serverStatus is not null)
+ entry = _viewHelperService.GetViewModel().WithData(url, serverStatus);
+ else
+ entry = _viewHelperService.GetViewModel().LoadServerEntry(url, CancellationToken.None);
- if(favorites.Contains(url.ToString())) entry.IsFavorite = true;
+ if(favorites.Contains(url.ToString()) && entry is IFavoriteEntryModelView favoriteEntryModelView)
+ favoriteEntryModelView.IsFavorite = true;
_entries.Add(url.ToString(), entry);
}
@@ -188,6 +195,16 @@ public class ServerViewContainer
}
}
+public interface IListEntryModelView
+{
+
+}
+
+public interface IFavoriteEntryModelView
+{
+ public bool IsFavorite { get; set; }
+}
+
public class ServerComparer : IComparer, IComparer, IComparer<(RobustUrl,ServerStatus)>
{
public int Compare(ServerHubInfo? x, ServerHubInfo? y)
diff --git a/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs
new file mode 100644
index 0000000..eb47a6f
--- /dev/null
+++ b/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Threading;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Microsoft.Extensions.DependencyInjection;
+using Nebula.Launcher.ServerListProviders;
+using Nebula.Launcher.ViewModels.Pages;
+using Nebula.Launcher.Views;
+using Nebula.Shared.Models;
+using Nebula.Shared.Services;
+using BindingFlags = System.Reflection.BindingFlags;
+
+namespace Nebula.Launcher.ViewModels;
+
+[ViewModelRegister(typeof(ServerCompoundEntryView), false)]
+[ConstructGenerator]
+public sealed partial class ServerCompoundEntryViewModel :
+ ViewModelBase, IFavoriteEntryModelView, IFilterConsumer, IListEntryModelView
+{
+ [ObservableProperty] private ServerEntryModelView _currentEntry;
+ [ObservableProperty] private Control? _entryControl;
+ [ObservableProperty] private string _name = "Loading...";
+ [ObservableProperty] private bool _isFavorite;
+ [ObservableProperty] private bool _loading = true;
+
+ [GenerateProperty] private RestService RestService { get; }
+ [GenerateProperty] private IServiceProvider ServiceProvider{ get; }
+ [GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; }
+
+ private RobustUrl? _url;
+
+
+ protected override void InitialiseInDesignMode()
+ {
+ }
+
+ protected override void Initialise()
+ {
+ }
+
+ public ServerCompoundEntryViewModel LoadServerEntry(RobustUrl url, CancellationToken cancellationToken)
+ {
+ Task.Run(async () =>
+ {
+ try
+ {
+ _url = url;
+ Name = $"Loading {url}...";
+ var status = await RestService.GetAsync(url.StatusUri, cancellationToken);
+
+ await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ CurrentEntry = ServiceProvider.GetService()!.WithData(url, status);
+ CurrentEntry.IsFavorite = IsFavorite;
+ CurrentEntry.Loading = false;
+ Loading = false;
+ });
+ }
+ catch (Exception e)
+ {
+ var error = new Exception("Unable to load server entry", e);
+ Name = e.Message;
+ }
+ }, cancellationToken);
+
+ return this;
+ }
+
+ public void ToggleFavorites()
+ {
+ if (_url == null)
+ return;
+ IsFavorite = !IsFavorite;
+ if(IsFavorite)
+ FavoriteServerListProvider.AddFavorite(_url);
+ else
+ FavoriteServerListProvider.RemoveFavorite(_url);
+ }
+
+
+ public void ProcessFilter(ServerFilter? serverFilter)
+ {
+ if(CurrentEntry is IFilterConsumer filterConsumer)
+ filterConsumer.ProcessFilter(serverFilter);
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
index b9301d6..31327d9 100644
--- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
+++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
@@ -21,21 +21,20 @@ namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ServerEntryView), false)]
[ConstructGenerator]
-public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer
+public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView
{
[ObservableProperty] private string _description = "Fetching info...";
[ObservableProperty] private bool _expandInfo;
[ObservableProperty] private bool _isFavorite;
[ObservableProperty] private bool _isVisible;
[ObservableProperty] private bool _runVisible = true;
+ [ObservableProperty] private bool _tagDataVisible;
+ [ObservableProperty] private bool _loading;
private ILogger _logger;
- private bool _isStatusFromHub;
private ServerInfo? _serverInfo;
private ContentLogConsumer _currentContentLogConsumer;
private ProcessRunHandler? _currentInstance;
-
- [ObservableProperty] private bool _tagDataVisible;
public LogPopupModelView CurrLog;
public RobustUrl Address { get; private set; }
@@ -119,39 +118,14 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer
foreach (var tag in Status.Tags) Tags.Add(tag);
OnPropertyChanged(nameof(Status));
}
+
- public void UpdateStatusIfNecessary()
- {
- if(_isStatusFromHub) return;
- FetchStatus();
- }
-
- public ServerEntryModelView WithData(RobustUrl url, ServerStatus? serverStatus)
+ public ServerEntryModelView WithData(RobustUrl url, ServerStatus serverStatus)
{
Address = url;
- _isStatusFromHub = serverStatus is not null;
- if (_isStatusFromHub)
- SetStatus(serverStatus!);
- else
- FetchStatus();
-
+ SetStatus(serverStatus);
return this;
}
-
- private async void FetchStatus()
- {
- try
- {
- SetStatus(await RestService.GetAsync(Address.StatusUri, CancellationService.Token));
- }
- catch (Exception e)
- {
- _logger.Error(e);
- Status = new ServerStatus("ErrorLand", $"ERROR: {e.Message}", [], "", -1, -1, -1, false,
- DateTime.Now,
- -1);
- }
- }
public void OpenContentViewer()
{
diff --git a/Nebula.Launcher/Views/ServerCompoundEntryView.axaml b/Nebula.Launcher/Views/ServerCompoundEntryView.axaml
new file mode 100644
index 0000000..95f9220
--- /dev/null
+++ b/Nebula.Launcher/Views/ServerCompoundEntryView.axaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Nebula.Launcher/Views/ServerCompoundEntryView.axaml.cs b/Nebula.Launcher/Views/ServerCompoundEntryView.axaml.cs
new file mode 100644
index 0000000..73376fe
--- /dev/null
+++ b/Nebula.Launcher/Views/ServerCompoundEntryView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Controls;
+using Nebula.Launcher.ViewModels;
+
+namespace Nebula.Launcher.Views;
+
+public partial class ServerCompoundEntryView : UserControl
+{
+ public ServerCompoundEntryView()
+ {
+ InitializeComponent();
+ }
+
+ public ServerCompoundEntryView(ServerCompoundEntryViewModel viewModel) : this()
+ {
+ DataContext = viewModel;
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/RestService.cs b/Nebula.Shared/Services/RestService.cs
index 5cc2c25..1cf0cb4 100644
--- a/Nebula.Shared/Services/RestService.cs
+++ b/Nebula.Shared/Services/RestService.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.Contracts;
using System.Globalization;
using System.Net;
using System.Text;
@@ -24,12 +25,14 @@ public class RestService
_logger = debug.GetLogger(this);
}
+ [Pure]
public async Task GetAsync(Uri uri, CancellationToken cancellationToken) where T : notnull
{
var response = await _client.GetAsync(uri, cancellationToken);
return await ReadResult(response, cancellationToken, uri);
}
+ [Pure]
public async Task GetAsyncDefault(Uri uri, T defaultValue, CancellationToken cancellationToken) where T : notnull
{
try
@@ -43,6 +46,7 @@ public class RestService
}
}
+ [Pure]
public async Task PostAsync(T information, Uri uri, CancellationToken cancellationToken) where K : notnull
{
var json = JsonSerializer.Serialize(information, _serializerOptions);
@@ -51,6 +55,7 @@ public class RestService
return await ReadResult(response, cancellationToken, uri);
}
+ [Pure]
public async Task PostAsync(Stream stream, Uri uri, CancellationToken cancellationToken) where T : notnull
{
using var multipartFormContent =
@@ -60,12 +65,14 @@ public class RestService
return await ReadResult(response, cancellationToken, uri);
}
+ [Pure]
public async Task DeleteAsync(Uri uri, CancellationToken cancellationToken) where T : notnull
{
var response = await _client.DeleteAsync(uri, cancellationToken);
return await ReadResult(response, cancellationToken, uri);
}
+ [Pure]
private async Task ReadResult(HttpResponseMessage response, CancellationToken cancellationToken, Uri uri) where T : notnull
{
var content = await response.Content.ReadAsStringAsync(cancellationToken);
diff --git a/Nebula.sln.DotSettings.user b/Nebula.sln.DotSettings.user
index c0071bb..d5419b8 100644
--- a/Nebula.sln.DotSettings.user
+++ b/Nebula.sln.DotSettings.user
@@ -9,6 +9,8 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
@@ -18,6 +20,7 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded