- add: Custom names of servers

This commit is contained in:
2025-06-22 22:11:27 +03:00
parent 37ca8fecf3
commit 02e1a14571
12 changed files with 260 additions and 52 deletions

View File

@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.Controls;
using Nebula.Launcher.Models;
@@ -24,24 +25,18 @@ public partial class ServerOverviewModel : ViewModelBase
[ObservableProperty] private bool _isFilterVisible;
[ObservableProperty] private ServerListView _currentServerList = new ServerListView();
[ObservableProperty] private ServerListView _currentServerList = new();
public readonly ServerFilter CurrentFilter = new ServerFilter();
public readonly ServerFilter CurrentFilter = new();
public Action? OnSearchChange;
[GenerateProperty] private PopupMessageService PopupMessageService { get; }
[GenerateProperty] private CancellationService CancellationService { get; }
[GenerateProperty] private DebugService DebugService { get; }
[GenerateProperty] private IServiceProvider ServiceProvider { get; }
[GenerateProperty] private ConfigurationService ConfigurationService { get; }
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; }
[GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; }
public ObservableCollection<ServerListTabTemplate> Items { get; private set; }
[ObservableProperty] private ServerListTabTemplate _selectedItem;
[GenerateProperty, DesignConstruct] private ServerViewContainer ServerViewContainer { get; set; }
[GenerateProperty, DesignConstruct] private ServerViewContainer ServerViewContainer { get; }
private Dictionary<string, ServerListView> _viewCache = [];
@@ -126,6 +121,7 @@ public partial class ServerOverviewModel : ViewModelBase
}
CurrentServerList = view;
ApplyFilter();
}
}
@@ -134,25 +130,77 @@ public partial class ServerOverviewModel : ViewModelBase
public class ServerViewContainer
{
private readonly ViewHelperService _viewHelperService;
private List<string> favorites = [];
private readonly List<string> _favorites = [];
private readonly Dictionary<string, string> _customNames = [];
public ServerViewContainer()
{
_viewHelperService = new ViewHelperService();
}
[UsedImplicitly]
public ServerViewContainer(ViewHelperService viewHelperService, ConfigurationService configurationService)
{
_viewHelperService = viewHelperService;
configurationService.SubscribeVarChanged(LauncherConVar.Favorites, OnFavoritesChange, true);
configurationService.SubscribeVarChanged(LauncherConVar.ServerCustomNames, OnCustomNamesChanged, true);
}
private void OnCustomNamesChanged(Dictionary<string,string>? value)
{
var oldNames =
_customNames.ToDictionary(k => k.Key, v => v.Value); //Clone think
_customNames.Clear();
if(value == null)
{
foreach (var (ip,_) in oldNames)
{
if(!_entries.TryGetValue(ip, out var listEntry) || listEntry is not IEntryNameHolder entryNameHolder)
continue;
entryNameHolder.Name = null;
}
return;
}
foreach (var (oldIp, oldName) in oldNames)
{
if(value.TryGetValue(oldIp, out var newName))
{
if (oldName == newName)
value.Remove(newName);
continue;
}
if(!_entries.TryGetValue(oldIp, out var listEntry) ||
listEntry is not IEntryNameHolder entryNameHolder)
continue;
entryNameHolder.Name = null;
}
foreach (var (ip, name) in value)
{
_customNames.Add(ip, name);
if(!_entries.TryGetValue(ip, out var listEntry) || listEntry is not IEntryNameHolder entryNameHolder)
continue;
entryNameHolder.Name = name;
}
}
private void OnFavoritesChange(string[]? value)
{
favorites = new List<string>(value ?? []);
_favorites.Clear();
if(value == null) return;
foreach (var favorite in favorites)
foreach (var favorite in value)
{
_favorites.Add(favorite);
if (_entries.TryGetValue(favorite, out var entry) && entry is IFavoriteEntryModelView favoriteView)
{
favoriteView.IsFavorite = true;
@@ -175,17 +223,20 @@ public class ServerViewContainer
lock (_entries)
{
_customNames.TryGetValue(url.ToString(), out var customName);
if (_entries.TryGetValue(url.ToString(), out entry))
{
return entry;
}
if (serverStatus is not null)
entry = _viewHelperService.GetViewModel<ServerEntryModelView>().WithData(url, serverStatus);
entry = _viewHelperService.GetViewModel<ServerEntryModelView>().WithData(url, customName, serverStatus);
else
entry = _viewHelperService.GetViewModel<ServerCompoundEntryViewModel>().LoadServerEntry(url, CancellationToken.None);
entry = _viewHelperService.GetViewModel<ServerCompoundEntryViewModel>().LoadServerEntry(url, customName, CancellationToken.None);
if(favorites.Contains(url.ToString()) && entry is IFavoriteEntryModelView favoriteEntryModelView)
if(_favorites.Contains(url.ToString()) &&
entry is IFavoriteEntryModelView favoriteEntryModelView)
favoriteEntryModelView.IsFavorite = true;
_entries.Add(url.ToString(), entry);
@@ -205,6 +256,11 @@ public interface IFavoriteEntryModelView
public bool IsFavorite { get; set; }
}
public interface IEntryNameHolder
{
public string? Name { get; set; }
}
public class ServerComparer : IComparer<ServerHubInfo>, IComparer<ServerStatus>, IComparer<(RobustUrl,ServerStatus)>
{
public int Compare(ServerHubInfo? x, ServerHubInfo? y)
@@ -262,4 +318,6 @@ public sealed class ServerFilter
{
return IsMatchByName(name) && IsMatchByTags(itemTags);
}
}
}
public sealed record ServerCustomNameEntry(string Url, string Name);

View File

@@ -0,0 +1,56 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.Views.Popup;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels.Popup;
[ViewModelRegister(typeof(EditServerNameView), false)]
[ConstructGenerator]
public sealed partial class EditServerNameViewModel : PopupViewModelBase
{
[GenerateProperty] public override PopupMessageService PopupMessageService { get; }
[GenerateProperty] public ConfigurationService ConfigurationService { get; }
public override string Title => "Edit server name";
public override bool IsClosable => true;
[ObservableProperty] private string _ipInput;
[ObservableProperty] private string _nameInput;
public void OnEnter()
{
if(string.IsNullOrWhiteSpace(IpInput))
return;
if (string.IsNullOrWhiteSpace(NameInput))
{
RemoveServerName();
Dispose();
return;
}
AddServerName();
Dispose();
}
private void AddServerName()
{
var currentNames = ConfigurationService.GetConfigValue(LauncherConVar.ServerCustomNames)!;
currentNames.Add(IpInput, NameInput);
ConfigurationService.SetConfigValue(LauncherConVar.ServerCustomNames, currentNames);
}
private void RemoveServerName()
{
var currentNames = ConfigurationService.GetConfigValue(LauncherConVar.ServerCustomNames)!;
currentNames.Remove(IpInput);
ConfigurationService.SetConfigValue(LauncherConVar.ServerCustomNames, currentNames);
}
protected override void InitialiseInDesignMode()
{
}
protected override void Initialise()
{
}
}

View File

@@ -18,51 +18,59 @@ namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ServerCompoundEntryView), false)]
[ConstructGenerator]
public sealed partial class ServerCompoundEntryViewModel :
ViewModelBase, IFavoriteEntryModelView, IFilterConsumer, IListEntryModelView
ViewModelBase, IFavoriteEntryModelView, IFilterConsumer, IListEntryModelView, IEntryNameHolder
{
[ObservableProperty] private ServerEntryModelView _currentEntry;
[ObservableProperty] private ServerEntryModelView? _currentEntry;
[ObservableProperty] private Control? _entryControl;
[ObservableProperty] private string _name = "Loading...";
[ObservableProperty] private string _message = "Loading server entry...";
[ObservableProperty] private bool _isFavorite;
[ObservableProperty] private bool _loading = true;
private string? _name;
public string? Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged();
if (CurrentEntry != null)
CurrentEntry.Name = value;
}
}
[GenerateProperty] private RestService RestService { get; }
[GenerateProperty] private IServiceProvider ServiceProvider{ get; }
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; }
private RobustUrl? _url;
protected override void InitialiseInDesignMode()
{
Name = "TEST.TEST";
}
protected override void Initialise()
{
}
public ServerCompoundEntryViewModel LoadServerEntry(RobustUrl url, CancellationToken cancellationToken)
public ServerCompoundEntryViewModel LoadServerEntry(RobustUrl url,string? name, CancellationToken cancellationToken)
{
Task.Run(async () =>
{
try
{
_url = url;
Name = $"Loading {url}...";
Message = "Loading server entry...";
var status = await RestService.GetAsync<ServerStatus>(url.StatusUri, cancellationToken);
await Dispatcher.UIThread.InvokeAsync(() =>
{
CurrentEntry = ServiceProvider.GetService<ServerEntryModelView>()!.WithData(url, status);
CurrentEntry.IsFavorite = IsFavorite;
CurrentEntry.Loading = false;
Loading = false;
});
CurrentEntry = ServiceProvider.GetService<ServerEntryModelView>()!.WithData(url,name, 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;
Message = e.Message;
}
}, cancellationToken);
@@ -71,13 +79,7 @@ public sealed partial class ServerCompoundEntryViewModel :
public void ToggleFavorites()
{
if (_url == null)
return;
IsFavorite = !IsFavorite;
if(IsFavorite)
FavoriteServerListProvider.AddFavorite(_url);
else
FavoriteServerListProvider.RemoveFavorite(_url);
CurrentEntry?.ToggleFavorites();
}

View File

@@ -21,7 +21,7 @@ namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ServerEntryView), false)]
[ConstructGenerator]
public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView
public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView, IEntryNameHolder
{
[ObservableProperty] private string _description = "Fetching info...";
[ObservableProperty] private bool _expandInfo;
@@ -30,6 +30,13 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
[ObservableProperty] private bool _runVisible = true;
[ObservableProperty] private bool _tagDataVisible;
[ObservableProperty] private bool _loading;
[ObservableProperty] private string _realName;
public string? Name
{
get => RealName;
set => RealName = value ?? Status.Name;
}
private ILogger _logger;
private ServerInfo? _serverInfo;
@@ -83,6 +90,8 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
protected override void InitialiseInDesignMode()
{
IsVisible = true;
RealName = "TEST.TEST";
Description = "Server of meow girls! Nya~ \nNyaMeow\nOOOINK!!";
Links.Add(new ServerLink("Discord", "discord", "https://cinka.ru"));
Status = new ServerStatus("Ameba",
@@ -119,14 +128,22 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
OnPropertyChanged(nameof(Status));
}
public ServerEntryModelView WithData(RobustUrl url, ServerStatus serverStatus)
public ServerEntryModelView WithData(RobustUrl url, string? name,ServerStatus serverStatus)
{
Address = url;
SetStatus(serverStatus);
Name = name;
return this;
}
public void EditName()
{
var popup = ViewHelperService.GetViewModel<EditServerNameViewModel>();
popup.IpInput = Address.ToString();
popup.NameInput = Name ?? string.Empty;
PopupMessageService.Popup(popup);
}
public void OpenContentViewer()
{
MainViewModel.RequirePage<ContentBrowserViewModel>().Go(Address, ContentPath.Empty);