Skip to content

Commit

Permalink
Better Favorites (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
DEATHB4DEFEAT authored Dec 31, 2024
1 parent 4060063 commit 082bd4f
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 62 deletions.
8 changes: 6 additions & 2 deletions SS14.Launcher/Assets/Locale/en-US/text.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
97 changes: 82 additions & 15 deletions SS14.Launcher/Models/Data/DataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ public Guid? SelectedLoginId
}

public IObservableCache<FavoriteServer, string> FavoriteServers => _favoriteServers;
public readonly HashSet<string> ExpandedServers = new();
public IObservableCache<LoginInfo, Guid> Logins => _logins;
public IObservableCache<InstalledEngineVersion, string> EngineInstallations => _engineInstallations;
public IEnumerable<InstalledEngineModule> EngineModules => _modules;
Expand All @@ -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)
/// <summary>
/// Moves a favorite server up or down a specified amount in the list
/// </summary>
/// <param name="move">How much to move it up or down</param>
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)
Expand Down Expand Up @@ -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<int>();
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(
Expand Down Expand Up @@ -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
);
Expand Down
13 changes: 6 additions & 7 deletions SS14.Launcher/Models/Data/FavoriteServer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Text.Json.Serialization;
using ReactiveUI;

Expand All @@ -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()
Expand All @@ -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")]
Expand All @@ -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.
/// </summary>
public DateTimeOffset RaiseTime
public int Position
{
get => _raiseTime;
set => this.RaiseAndSetIfChanged(ref _raiseTime, value);
get => _position;
set => this.RaiseAndSetIfChanged(ref _position, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE FavoriteServer
ADD Position INTEGER NOT NULL DEFAULT 0;
ALTER TABLE FavoriteServer
DROP COLUMN RaiseTime;
6 changes: 3 additions & 3 deletions SS14.Launcher/Models/ServerStatus/ServerStatusCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ public ServerStatusData GetStatusFor(string serverAddress)
/// <summary>
/// Do the initial status update for a server status. This only acts once.
/// </summary>
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();
Expand Down
20 changes: 8 additions & 12 deletions SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Check warning on line 40 in SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 40 in SS14.Launcher/ViewModels/MainWindowTabs/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
})
.Sort(Comparer<ServerEntryViewModel>.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<ServerEntryViewModel>.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(_ =>
Expand Down
38 changes: 34 additions & 4 deletions SS14.Launcher/ViewModels/MainWindowTabs/ServerEntryViewModel.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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
}
}
}
26 changes: 15 additions & 11 deletions SS14.Launcher/Views/AddFavoriteDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -12,16 +13,19 @@
Padding="4"
WindowStartupLocation="CenterOwner">

<DockPanel LastChildFill="true">
<DockPanel DockPanel.Dock="Bottom">
<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Name="SubmitButton" Content="Add" />
<TextBlock Text="{loc:Loc add-favorite-window-address-invalid}" VerticalAlignment="Center" Name="TxtInvalid" />
<v:ScaledControl>
<DockPanel LastChildFill="true">
<DockPanel DockPanel.Dock="Bottom">
<Button DockPanel.Dock="Right" Name="SubmitButton" Classes="OpenLeft" Content="{loc:Loc add-favorite-window-submit-button}" />
<Button DockPanel.Dock="Right" Name="FetchButton" Classes="OpenRight" Content="{loc:Loc add-favorite-window-fetch-button}" />
<TextBlock Text="{loc:Loc add-favorite-window-address-invalid}" VerticalAlignment="Center" Name="TxtInvalid" />
</DockPanel>
<Grid Margin="4" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
<TextBlock VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="0" Grid.Row="0" Text="{loc:Loc add-favorite-window-label-name}" />
<TextBlock VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="0" Grid.Row="1" Text="{loc:Loc add-favorite-window-label-address}" />
<TextBox Grid.Column="1" Grid.Row="0" Name="NameBox" Watermark="{loc:Loc add-favorite-window-example-name}" />
<TextBox Grid.Column="1" Grid.Row="1" Name="AddressBox" Watermark="ss14://example.com" />
</Grid>
</DockPanel>
<Grid Margin="4" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
<TextBlock VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="0" Grid.Row="0" Text="{loc:Loc add-favorite-window-label-name}" />
<TextBlock VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="0" Grid.Row="1" Text="{loc:Loc add-favorite-window-label-address}" />
<TextBox Grid.Column="1" Grid.Row="0" Name="NameBox" Watermark="{loc:Loc add-favorite-window-example-name}" />
<TextBox Grid.Column="1" Grid.Row="1" Name="AddressBox" Watermark="ss14://example.com" />
</Grid>
</DockPanel>
</v:ScaledControl>
</Window>
Loading

0 comments on commit 082bd4f

Please sign in to comment.