From 61ecd21a0e76c1161ec982e794df7d107c101b00 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 21 Mar 2026 18:08:28 -0400 Subject: [PATCH] Byond Server Favoriting --- .../ServerStatus/ClassicServerListCache.cs | 9 +++ .../ClassicServerEntryViewModel.cs | 59 ++++++++++++++++++- .../ClassicServerListTabViewModel.cs | 4 +- .../MainWindowTabs/HomePageViewModel.cs | 37 +++++++++--- .../ClassicServerEntryView.xaml | 33 ++++++++++- .../Views/MainWindowTabs/ServerList.xaml.cs | 9 +-- 6 files changed, 135 insertions(+), 16 deletions(-) diff --git a/SS14.Launcher/Models/ServerStatus/ClassicServerListCache.cs b/SS14.Launcher/Models/ServerStatus/ClassicServerListCache.cs index bfca6460..dd44ee51 100644 --- a/SS14.Launcher/Models/ServerStatus/ClassicServerListCache.cs +++ b/SS14.Launcher/Models/ServerStatus/ClassicServerListCache.cs @@ -26,6 +26,15 @@ public ClassicServerListCache() AllServers = new ReadOnlyObservableCollection(_allServers); } + public ClassicServerStatusData GetStatusFor(string address) + { + foreach (var server in _allServers) + if (server.Address == address) + return server; + + return new ClassicServerStatusData("Unknown Server", address, 0, "Fetching...", ""); + } + public async Task Refresh() { try diff --git a/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerEntryViewModel.cs b/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerEntryViewModel.cs index 74de9feb..acb31a3c 100644 --- a/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerEntryViewModel.cs +++ b/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerEntryViewModel.cs @@ -8,13 +8,18 @@ using SS14.Launcher.Models; using SS14.Launcher.Models.ServerStatus; using SS14.Launcher.Utility; +using SS14.Launcher.Models.Data; +using Microsoft.Toolkit.Mvvm.Messaging; namespace SS14.Launcher.ViewModels.MainWindowTabs; -public class ClassicServerEntryViewModel : ViewModelBase +public class ClassicServerEntryViewModel : ViewModelBase, IRecipient { private readonly MainWindowViewModel _mainWindow; private readonly ClassicServerStatusData _server; + private readonly DataManager? _cfg; + + public FavoriteServer? Favorite { get; } public string Name => _server.Name; public string Address => _server.Address; @@ -32,12 +37,62 @@ public bool IsExpanded public ReactiveCommand ConnectCommand { get; } - public ClassicServerEntryViewModel(MainWindowViewModel mainWindow, ClassicServerStatusData server) + public ClassicServerEntryViewModel(MainWindowViewModel mainWindow, ClassicServerStatusData server, FavoriteServer? favorite = null, DataManager? cfg = null) { _mainWindow = mainWindow; _server = server; + Favorite = favorite; + _cfg = cfg; ConnectCommand = ReactiveCommand.Create(Connect); + + WeakReferenceMessenger.Default.Register(this); + } + + public bool ViewedInFavoritesPane { get; set; } + + public bool IsFavorite => _cfg?.FavoriteServers.Lookup(Address).HasValue ?? false; + + public string FavoriteButtonText => IsFavorite + ? LocalizationManager.Instance.GetString("server-entry-remove-favorite") + : LocalizationManager.Instance.GetString("server-entry-add-favorite"); + + public void FavoriteButtonPressed() + { + if (_cfg == null) return; + if (IsFavorite) + _cfg.RemoveFavoriteServer(_cfg.FavoriteServers.Lookup(Address).Value); + else + { + var fav = new FavoriteServer(Name, Address); + _cfg.AddFavoriteServer(fav); + } + + _cfg.CommitConfig(); + } + + public void FavoriteRaiseButtonPressed() + { + if (_cfg == null || !IsFavorite) + return; + + _cfg.ReorderFavoriteServer(_cfg.FavoriteServers.Lookup(Address).Value, 1); + _cfg.CommitConfig(); + } + + public void FavoriteLowerButtonPressed() + { + if (_cfg == null || !IsFavorite) + return; + + _cfg.ReorderFavoriteServer(_cfg.FavoriteServers.Lookup(Address).Value, -1); + _cfg.CommitConfig(); + } + + public void Receive(FavoritesChanged message) + { + this.RaisePropertyChanged(nameof(IsFavorite)); + this.RaisePropertyChanged(nameof(FavoriteButtonText)); } private void Connect() diff --git a/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerListTabViewModel.cs b/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerListTabViewModel.cs index 0895870e..b74c6621 100644 --- a/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerListTabViewModel.cs +++ b/SS14.Launcher/ViewModels/MainWindowTabs/ClassicServerListTabViewModel.cs @@ -13,6 +13,7 @@ public class ClassicServerListTabViewModel : MainWindowTabViewModel { private readonly MainWindowViewModel _mainWindow; private readonly ClassicServerListCache _cache; + private readonly SS14.Launcher.Models.Data.DataManager _cfg; private readonly LocalizationManager _loc = LocalizationManager.Instance; @@ -37,6 +38,7 @@ public ClassicServerListTabViewModel(MainWindowViewModel mainWindow) { _mainWindow = mainWindow; _cache = Locator.Current.GetRequiredService(); + _cfg = Locator.Current.GetRequiredService(); RefreshPressed = ReactiveCommand.CreateFromTask(_cache.Refresh); // Initial populate if any @@ -59,7 +61,7 @@ private void UpdateList() foreach (var s in sorted) { - AllServers.Add(new ClassicServerEntryViewModel(_mainWindow, s)); + AllServers.Add(new ClassicServerEntryViewModel(_mainWindow, s, null, _cfg)); } } diff --git a/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs b/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs index ab5a3124..6448a242 100644 --- a/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs +++ b/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs @@ -23,28 +23,47 @@ public class HomePageViewModel : MainWindowTabViewModel private readonly DataManager _cfg; private readonly ServerStatusCache _statusCache = new ServerStatusCache(); private readonly ServerListCache _serverListCache; + private readonly ClassicServerListCache _classicServerListCache; public HomePageViewModel(MainWindowViewModel mainWindowViewModel) { MainWindowViewModel = mainWindowViewModel; _cfg = Locator.Current.GetRequiredService(); _serverListCache = Locator.Current.GetRequiredService(); + _classicServerListCache = Locator.Current.GetRequiredService(); _cfg.FavoriteServers .Connect() .Select(x => - new ServerEntryViewModel(MainWindowViewModel, _statusCache.GetStatusFor(x.Address), x, _statusCache, _cfg, Locator.Current.GetRequiredService()) - { ViewedInFavoritesPane = true, IsExpanded = _cfg.ExpandedServers.Contains(x.Address) }) + { + if (x.Address.StartsWith("byond://")) + { + return (IViewModelBase) new ClassicServerEntryViewModel(MainWindowViewModel, _classicServerListCache.GetStatusFor(x.Address), x, _cfg) + { ViewedInFavoritesPane = true, IsExpanded = _cfg.ExpandedServers.Contains(x.Address) }; + } + + return (IViewModelBase) new ServerEntryViewModel(MainWindowViewModel, _statusCache.GetStatusFor(x.Address), x, _statusCache, _cfg, Locator.Current.GetRequiredService()) + { ViewedInFavoritesPane = true, IsExpanded = _cfg.ExpandedServers.Contains(x.Address) }; + }) .OnItemAdded(a => { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - if (IsSelected) _statusCache.InitialUpdateStatus(a.CacheData); + if (IsSelected) + { + if (a is ServerEntryViewModel svm) + _statusCache.InitialUpdateStatus(svm.CacheData); + } #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed }) - .Sort(Comparer.Create((a, b) => + .Sort(Comparer.Create((a, b) => { - var dc = a.Favorite!.Position.CompareTo(b.Favorite!.Position); - return dc != 0 ? -dc : string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase); + var afav = (a as ServerEntryViewModel)?.Favorite ?? (a as ClassicServerEntryViewModel)?.Favorite; + var bfav = (b as ServerEntryViewModel)?.Favorite ?? (b as ClassicServerEntryViewModel)?.Favorite; + var aName = (a as ServerEntryViewModel)?.Name ?? (a as ClassicServerEntryViewModel)?.Name; + var bName = (b as ServerEntryViewModel)?.Name ?? (b as ClassicServerEntryViewModel)?.Name; + + var dc = afav!.Position.CompareTo(bfav!.Position); + return dc != 0 ? -dc : string.Compare(aName, bName, StringComparison.CurrentCultureIgnoreCase); })) .Bind(out var favorites) .Subscribe(_ => @@ -55,7 +74,7 @@ public HomePageViewModel(MainWindowViewModel mainWindowViewModel) Favorites = favorites; } - public ReadOnlyObservableCollection Favorites { get; } + public ReadOnlyObservableCollection Favorites { get; } public ObservableCollection Suggestions { get; } = new(); [Reactive] public bool FavoritesEmpty { get; private set; } = true; @@ -110,13 +129,15 @@ public void RefreshPressed() { _statusCache.Refresh(); _serverListCache.RequestRefresh(); + _classicServerListCache.Refresh(); } public override void Selected() { foreach (var favorite in Favorites) { - _ = _statusCache.InitialUpdateStatus(favorite.CacheData); + if (favorite is ServerEntryViewModel svm) + _ = _statusCache.InitialUpdateStatus(svm.CacheData); } _serverListCache.RequestInitialUpdate(); } diff --git a/SS14.Launcher/Views/MainWindowTabs/ClassicServerEntryView.xaml b/SS14.Launcher/Views/MainWindowTabs/ClassicServerEntryView.xaml index 6a72768a..228cb6db 100644 --- a/SS14.Launcher/Views/MainWindowTabs/ClassicServerEntryView.xaml +++ b/SS14.Launcher/Views/MainWindowTabs/ClassicServerEntryView.xaml @@ -4,12 +4,22 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mainWindowTabs="clr-namespace:SS14.Launcher.ViewModels.MainWindowTabs" xmlns:loc="clr-namespace:SS14.Launcher.Localization" + xmlns:views="clr-namespace:SS14.Launcher.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="50" x:Class="SS14.Launcher.Views.MainWindowTabs.ClassicServerEntryView"> + + + + + @@ -43,7 +53,28 @@ - + + + + + diff --git a/SS14.Launcher/Views/MainWindowTabs/ServerList.xaml.cs b/SS14.Launcher/Views/MainWindowTabs/ServerList.xaml.cs index fd611416..5b87e5c3 100644 --- a/SS14.Launcher/Views/MainWindowTabs/ServerList.xaml.cs +++ b/SS14.Launcher/Views/MainWindowTabs/ServerList.xaml.cs @@ -5,6 +5,7 @@ using Avalonia.Controls; using Avalonia.Metadata; using Serilog; +using SS14.Launcher.ViewModels; using SS14.Launcher.ViewModels.MainWindowTabs; namespace SS14.Launcher.Views.MainWindowTabs; @@ -60,16 +61,16 @@ public bool SpinnerVisible set => SetAndRaise(SpinnerVisibleProperty, ref _spinnerVisible, value); } - public static readonly DirectProperty> ListProperty = - AvaloniaProperty.RegisterDirect>( + public static readonly DirectProperty> ListProperty = + AvaloniaProperty.RegisterDirect>( nameof(List), o => o.List, (o, v) => o.List = v ); - private IReadOnlyCollection _serverList = Array.Empty(); + private IReadOnlyCollection _serverList = Array.Empty(); - public IReadOnlyCollection List + public IReadOnlyCollection List { get => _serverList; set => SetAndRaise(ListProperty, ref _serverList, value);