diff --git a/SS14.Launcher/Assets/Locale/en-US/text.ftl b/SS14.Launcher/Assets/Locale/en-US/text.ftl index 64c2512..3858dff 100644 --- a/SS14.Launcher/Assets/Locale/en-US/text.ftl +++ b/SS14.Launcher/Assets/Locale/en-US/text.ftl @@ -10,7 +10,9 @@ account-drop-down-add-account = Add account ## Localization for the "add favorite server" dialog window -add-favorite-window-title = Add Favorite Server +add-favorite-window-title = Favorite Server +add-favorite-window-fetch-button = Get name from server +add-favorite-window-submit-button = Confirm add-favorite-window-address-invalid = Address is invalid add-favorite-window-label-name = Name: add-favorite-window-label-address = Address: @@ -261,6 +263,7 @@ filters-rp-high-desc = High ## Strings for entries in the server list (including home page) server-entry-connect = Connect +server-entry-update-info = Edit server-entry-add-favorite = Add Favorite server-entry-remove-favorite = Remove Favorite server-entry-offline = OFFLINE @@ -275,7 +278,8 @@ server-entry-description-fetching = Fetching server status… server-entry-description-error = Error while fetching server description server-entry-description-none = No server description provided server-fetched-from-hub = Fetched from { $hub } -server-entry-raise = Raise to top +server-entry-raise = Raise +server-entry-lower = Lower ## Strings for the "Development" tab ## These aren't shown to users so they're not very important diff --git a/SS14.Launcher/Models/Data/DataManager.cs b/SS14.Launcher/Models/Data/DataManager.cs index 4b61dd6..6ec4f56 100644 --- a/SS14.Launcher/Models/Data/DataManager.cs +++ b/SS14.Launcher/Models/Data/DataManager.cs @@ -124,6 +124,7 @@ public Guid? SelectedLoginId } public IObservableCache FavoriteServers => _favoriteServers; + public readonly HashSet ExpandedServers = new(); public IObservableCache Logins => _logins; public IObservableCache EngineInstallations => _engineInstallations; public IEnumerable EngineModules => _modules; @@ -142,23 +143,73 @@ public Guid? SelectedLoginId public void AddFavoriteServer(FavoriteServer server) { if (_favoriteServers.Lookup(server.Address).HasValue) - { throw new ArgumentException("A server with that address is already a favorite."); - } _favoriteServers.AddOrUpdate(server); } + public void EditFavoriteServer(FavoriteServer server) + { + if (!_favoriteServers.Lookup(server.Address).HasValue) + throw new ArgumentException("That server's address is not a favorite"); + + _favoriteServers.AddOrUpdate(server); + } + + public void EditFavoriteServer(FavoriteServer server, string address, string name) + { + if (!_favoriteServers.Lookup(server.Address).HasValue) + throw new ArgumentException("That server's address is not a favorite"); + + RemoveFavoriteServer(server); + if (ExpandedServers.Contains(server.Address)) + { + ExpandedServers.Remove(server.Address); + ExpandedServers.Add(address); + } + AddFavoriteServer(new FavoriteServer(name, address, server.Position)); + } + public void RemoveFavoriteServer(FavoriteServer server) { _favoriteServers.Remove(server); } - public void RaiseFavoriteServer(FavoriteServer server) + /// + /// Moves a favorite server up or down a specified amount in the list + /// + /// How much to move it up or down + public void ReorderFavoriteServer(FavoriteServer server, int move) { - _favoriteServers.Remove(server); - server.RaiseTime = DateTimeOffset.UtcNow; - _favoriteServers.AddOrUpdate(server); + var servers = _favoriteServers.Items.OrderBy(f => f.Position).ToList(); + var index = servers.IndexOf(server); + if (index == -1) + throw new ArgumentException("That server is not a favorite"); + + if (index + move < 0 || index + move >= servers.Count) + return; + + servers[index] = servers[index + move]; + servers[index + move] = server; + + for (var i = 0; i < servers.Count; i++) + servers[i].Position = i; + + _favoriteServers.Edit(inner => + { + inner.Clear(); + inner.AddOrUpdate(servers); + }); + + CommitConfig(); + } + + public void EditExpandedServers(string address, bool expanded) + { + if (expanded) + ExpandedServers.Add(address); + else + ExpandedServers.Remove(address); } public void AddEngineInstallation(InstalledEngineVersion version) @@ -261,10 +312,26 @@ private void LoadSqliteConfig(SqliteConnection sqliteConnection) })); // Favorites - _favoriteServers.AddOrUpdate( - sqliteConnection.Query<(string addr, string name, DateTimeOffset raiseTime)>( - "SELECT Address,Name,RaiseTime FROM FavoriteServer") - .Select(l => new FavoriteServer(l.name, l.addr, l.raiseTime))); + // _favoriteServers.AddOrUpdate( + // sqliteConnection.Query<(string addr, string name, int position)>( + // "SELECT Address,Name,Position FROM FavoriteServer") + // .Select(l => new FavoriteServer(l.name, l.addr, l.position))); + // The above but assign every duplicate position a new one and sort them + var favorites = sqliteConnection.Query<(string addr, string name, int position)>( + "SELECT Address,Name,Position FROM FavoriteServer"); + var positions = new HashSet(); + List<(string, string, int)> tmp = new(); + foreach (var (addr, name, position) in favorites) + { + var pos = position; + while (positions.Contains(pos)) + pos++; + + positions.Add(pos); + tmp.Add((addr, name, pos)); + } + _favoriteServers.AddOrUpdate(tmp.OrderBy(s => s.Item3) + .Select(s => new FavoriteServer(s.Item2, s.Item1, s.Item3))); // Engine installations _engineInstallations.AddOrUpdate( @@ -386,17 +453,17 @@ private void ChangeFavoriteServer(ChangeReason reason, FavoriteServer server) var data = new { server.Address, - server.RaiseTime, - server.Name + Position = server.Position, + server.Name, }; AddDbCommand(con => { con.Execute(reason switch { - ChangeReason.Add => "INSERT INTO FavoriteServer VALUES (@Address, @Name, @RaiseTime)", - ChangeReason.Update => "UPDATE FavoriteServer SET Name = @Name, RaiseTime = @RaiseTime WHERE Address = @Address", + ChangeReason.Add => "INSERT INTO FavoriteServer VALUES (@Address, @Name, @Position)", + ChangeReason.Update => "UPDATE FavoriteServer SET Name = @Name, Position = @Position WHERE Address = @Address", ChangeReason.Remove => "DELETE FROM FavoriteServer WHERE Address = @Address", - _ => throw new ArgumentOutOfRangeException(nameof(reason), reason, null) + _ => throw new ArgumentOutOfRangeException(nameof(reason), reason, null), }, data ); diff --git a/SS14.Launcher/Models/Data/FavoriteServer.cs b/SS14.Launcher/Models/Data/FavoriteServer.cs index eca1fa1..6c8fbbc 100644 --- a/SS14.Launcher/Models/Data/FavoriteServer.cs +++ b/SS14.Launcher/Models/Data/FavoriteServer.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json.Serialization; using ReactiveUI; @@ -7,7 +6,7 @@ namespace SS14.Launcher.Models.Data; public sealed class FavoriteServer : ReactiveObject { private string? _name; - private DateTimeOffset _raiseTime; + private int _position; // For serialization. public FavoriteServer() @@ -21,11 +20,11 @@ public FavoriteServer(string? name, string address) Address = address; } - public FavoriteServer(string? name, string address, DateTimeOffset raiseTime) + public FavoriteServer(string? name, string address, int position) { Name = name; Address = address; - RaiseTime = raiseTime; + Position = position; } [JsonPropertyName("name")] @@ -43,9 +42,9 @@ public string? Name /// Defaults to 0, this is fine. /// This isn't saved in JSON because the launcher apparently doesn't use JSON for these anymore. /// - public DateTimeOffset RaiseTime + public int Position { - get => _raiseTime; - set => this.RaiseAndSetIfChanged(ref _raiseTime, value); + get => _position; + set => this.RaiseAndSetIfChanged(ref _position, value); } } diff --git a/SS14.Launcher/Models/Data/Migrations/Script0007_FavoritesUsePosition.sql b/SS14.Launcher/Models/Data/Migrations/Script0007_FavoritesUsePosition.sql new file mode 100644 index 0000000..1797e75 --- /dev/null +++ b/SS14.Launcher/Models/Data/Migrations/Script0007_FavoritesUsePosition.sql @@ -0,0 +1,4 @@ +ALTER TABLE FavoriteServer +ADD Position INTEGER NOT NULL DEFAULT 0; +ALTER TABLE FavoriteServer +DROP COLUMN RaiseTime; diff --git a/SS14.Launcher/Models/ServerStatus/ServerStatusCache.cs b/SS14.Launcher/Models/ServerStatus/ServerStatusCache.cs index 1712df4..e159cf9 100644 --- a/SS14.Launcher/Models/ServerStatus/ServerStatusCache.cs +++ b/SS14.Launcher/Models/ServerStatus/ServerStatusCache.cs @@ -50,16 +50,16 @@ public ServerStatusData GetStatusFor(string serverAddress) /// /// Do the initial status update for a server status. This only acts once. /// - public void InitialUpdateStatus(ServerStatusData data) + public async Task InitialUpdateStatus(ServerStatusData data) { var reg = _cachedData[data.Address]; if (reg.DidInitialStatusUpdate) return; - UpdateStatusFor(reg); + await UpdateStatusFor(reg); } - private async void UpdateStatusFor(CacheReg reg) + private async Task UpdateStatusFor(CacheReg reg) { reg.DidInitialStatusUpdate = true; await reg.Semaphore.WaitAsync(); diff --git a/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs b/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs index 363c6aa..463c747 100644 --- a/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs +++ b/SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs @@ -32,21 +32,17 @@ public HomePageViewModel(MainWindowViewModel mainWindowViewModel) _cfg.FavoriteServers .Connect() - .Select(x => new ServerEntryViewModel(MainWindowViewModel, _statusCache.GetStatusFor(x.Address), x, _statusCache, _cfg) { ViewedInFavoritesPane = true }) + .Select(x => + new ServerEntryViewModel(MainWindowViewModel, _statusCache.GetStatusFor(x.Address), x, _statusCache, _cfg) + { ViewedInFavoritesPane = true, IsExpanded = _cfg.ExpandedServers.Contains(x.Address) }) .OnItemAdded(a => { - if (IsSelected) - { - _statusCache.InitialUpdateStatus(a.CacheData); - } + if (IsSelected) _statusCache.InitialUpdateStatus(a.CacheData); }) - .Sort(Comparer.Create((a, b) => { - var dc = a.Favorite!.RaiseTime.CompareTo(b.Favorite!.RaiseTime); - if (dc != 0) - { - return -dc; - } - return string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase); + .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); })) .Bind(out var favorites) .Subscribe(_ => diff --git a/SS14.Launcher/ViewModels/MainWindowTabs/ServerEntryViewModel.cs b/SS14.Launcher/ViewModels/MainWindowTabs/ServerEntryViewModel.cs index aecb724..7e32b7d 100644 --- a/SS14.Launcher/ViewModels/MainWindowTabs/ServerEntryViewModel.cs +++ b/SS14.Launcher/ViewModels/MainWindowTabs/ServerEntryViewModel.cs @@ -1,10 +1,13 @@ using System; using System.ComponentModel; +using Avalonia.Controls; +using Avalonia.VisualTree; using Microsoft.Toolkit.Mvvm.ComponentModel; using Microsoft.Toolkit.Mvvm.Messaging; using SS14.Launcher.Localization; using SS14.Launcher.Models.Data; using SS14.Launcher.Models.ServerStatus; +using SS14.Launcher.Views; using static SS14.Launcher.Utility.HubUtility; namespace SS14.Launcher.ViewModels.MainWindowTabs; @@ -166,10 +169,15 @@ public void FavoriteButtonPressed() public void FavoriteRaiseButtonPressed() { if (IsFavorite) - { - // Usual business, raise priority - _cfg.RaiseFavoriteServer(_cfg.FavoriteServers.Lookup(Address).Value); - } + _cfg.ReorderFavoriteServer(_cfg.FavoriteServers.Lookup(Address).Value, 1); + + _cfg.CommitConfig(); + } + + public void FavoriteLowerButtonPressed() + { + if (IsFavorite) + _cfg.ReorderFavoriteServer(_cfg.FavoriteServers.Lookup(Address).Value, -1); _cfg.CommitConfig(); } @@ -230,4 +238,26 @@ private void OnCacheDataOnPropertyChanged(object? _, PropertyChangedEventArgs ar break; } } + + public async void UpdateFavoriteInfo() + { + if (Favorite == null + || _windowVm.Control?.GetVisualRoot() is not Window window) + return; + + var (name, address) = await new AddFavoriteDialog(Favorite.Name ?? "", Favorite.Address).ShowDialog<(string name, string address)>(window); + + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(address)) + return; + + try + { + _cfg.EditFavoriteServer(new(Name, Address), address, name); + _cfg.CommitConfig(); + } + catch (ArgumentException) + { + // Ignored + } + } } diff --git a/SS14.Launcher/Views/AddFavoriteDialog.xaml b/SS14.Launcher/Views/AddFavoriteDialog.xaml index dfe7cf4..daa7b17 100644 --- a/SS14.Launcher/Views/AddFavoriteDialog.xaml +++ b/SS14.Launcher/Views/AddFavoriteDialog.xaml @@ -3,6 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:loc="clr-namespace:SS14.Launcher.Localization" + xmlns:v="clr-namespace:SS14.Launcher.Views" mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="100" MinWidth="500" MinHeight="130" Width="500" Height="130" @@ -12,16 +13,19 @@ Padding="4" WindowStartupLocation="CenterOwner"> - - -