Skip to content
Merged

Dev #237

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
104ab1a
Update .gitignore
patchzyy Feb 17, 2026
42f81be
first rewrite
WantToBeeMe Feb 17, 2026
5b99f89
Add leaderboard page and podium card component
patchzyy Feb 17, 2026
c3f0a41
Add leaderboard caching and loading UI
patchzyy Feb 18, 2026
36520ff
Update LeaderboardPage.axaml
patchzyy Feb 18, 2026
0dcc009
Add friend/online flags and join-room UI
patchzyy Feb 18, 2026
a99abb0
split the Dolphin Installer. it still needs some changes. but it was …
WantToBeeMe Feb 18, 2026
31a1826
add settings singal bus and move ofver to the IFileSystem service
WantToBeeMe Feb 18, 2026
152e64f
small
WantToBeeMe Feb 19, 2026
efd1ee0
how to add new settings
WantToBeeMe Feb 19, 2026
684eb73
more changes
WantToBeeMe Feb 21, 2026
c465f62
move settings files
WantToBeeMe Feb 21, 2026
858f18d
guard clauses
WantToBeeMe Feb 21, 2026
67e7280
add tests for the settings
WantToBeeMe Feb 21, 2026
657a707
tooltip improvements
WantToBeeMe Feb 21, 2026
e4e93f8
more
WantToBeeMe Feb 21, 2026
6fc08bd
tooltips dont collide
WantToBeeMe Feb 21, 2026
48dfde8
Update SettingsExtensions.cs
WantToBeeMe Feb 21, 2026
6379e40
rename to types
WantToBeeMe Feb 21, 2026
e724b19
changed the VR History component to always be visible regardless of w…
Feb 21, 2026
5bdb15f
Merge pull request #234 from hyperlexus/dev
WantToBeeMe Feb 22, 2026
65e2dbc
Merge branch 'dev' into task/settings-rewrite-in-to-DI
WantToBeeMe Feb 22, 2026
296b888
Merge branch 'task/settings-rewrite-in-to-DI' into UI-improvements
WantToBeeMe Feb 22, 2026
e6fbd84
make ktichen sink fpage and move settings page 1 folder down
WantToBeeMe Feb 22, 2026
90d222b
fix spelling mistakes
WantToBeeMe Feb 22, 2026
9ab49c9
Add drag-and-drop reordering for mods
patchzyy Feb 22, 2026
fabc666
fix tests
WantToBeeMe Feb 22, 2026
9a86aee
removed lock overkill
WantToBeeMe Feb 22, 2026
a79d696
Merge branch 'task/settings-rewrite-in-to-DI' into UI-improvements
WantToBeeMe Feb 22, 2026
2f2f642
kitchen sink blocks
WantToBeeMe Feb 22, 2026
7a11c92
.
WantToBeeMe Feb 22, 2026
21888b6
more things
WantToBeeMe Feb 22, 2026
29cd758
.
WantToBeeMe Feb 22, 2026
73df0dc
cool
WantToBeeMe Feb 22, 2026
bf1eb9e
combine leaderboard Player Items
WantToBeeMe Feb 22, 2026
7d7931d
make cache based on DI cache
WantToBeeMe Feb 22, 2026
9509bd9
Localize 'Leaderboard' page title
patchzyy Feb 23, 2026
77e4cc5
use filesystem + update comment
patchzyy Feb 23, 2026
8d18424
Merge pull request #231 from TeamWheelWizard/task/settings-rewrite-in…
patchzyy Feb 23, 2026
c01e8d5
Merge branch 'main' into Leaderboard
patchzyy Feb 23, 2026
d636e19
Merge remote-tracking branch 'origin/dev' into Leaderboard
patchzyy Feb 23, 2026
e8153b8
Change to use new settingsmanager
patchzyy Feb 23, 2026
c77b405
Merge pull request #233 from TeamWheelWizard/Leaderboard
patchzyy Feb 23, 2026
ad9b4a2
Start on gridmod
patchzyy Feb 23, 2026
01b2d25
Update GridModPanel.axaml
patchzyy Feb 23, 2026
2f68e67
move folder structure
WantToBeeMe Feb 24, 2026
00bad8c
Merge branch 'dev' into tuff-stuff
WantToBeeMe Feb 24, 2026
631c828
Merge pull request #236 from TeamWheelWizard/tuff-stuff
WantToBeeMe Feb 24, 2026
eb9c308
Merge branch 'dev' into UI-improvements
WantToBeeMe Feb 24, 2026
303e4f9
resolving conflicts
WantToBeeMe Feb 24, 2026
f097ff0
.
WantToBeeMe Feb 24, 2026
bf17062
more moves
WantToBeeMe Feb 24, 2026
a3ada7a
rooms page animation
WantToBeeMe Feb 24, 2026
f41672d
Image caching and UI tweaks; ModManager cleanup
patchzyy Feb 25, 2026
e62a8c7
Fix ModsPage focus handling and vertical alignment
patchzyy Feb 25, 2026
1ecd607
Notify Mods property and tighten tooltip exit logic
patchzyy Feb 25, 2026
d95526b
Update FriendsPage.axaml
patchzyy Feb 25, 2026
0b95e66
Random commit because github got stuck while building branch
patchzyy Feb 25, 2026
d5ca1b6
Revert "Random commit because github got stuck while building branch"
patchzyy Feb 25, 2026
9e26123
Merge pull request #235 from TeamWheelWizard/UI-improvements
patchzyy Feb 25, 2026
38059ea
Bump app version to 2.4.1
patchzyy Feb 25, 2026
f58dc3c
Update .NET SDK version and rollForward
patchzyy Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ FodyWeavers.xsd
# Local History for Visual Studio Code
.history/

# Avalonia build task artifacts
**/.avalonia-build-tasks/

# Windows Installer files from build outputs
*.cab
*.msi
Expand Down Expand Up @@ -482,3 +485,4 @@ $RECYCLE.BIN/

# Vim temporary swap files
*.swp
AGENTS.md
1 change: 1 addition & 0 deletions Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
</provides>

<releases>
<release version="2.4.1" date="2026-02-25"/>
<release version="2.4.0" date="2026-02-17"/>
<release version="2.3.5" date="2026-01-11"/>
<release version="2.3.4" date="2026-01-11"/>
Expand Down
126 changes: 126 additions & 0 deletions WheelWizard.Test/Features/LinuxDolphinInstallerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using WheelWizard.DolphinInstaller;
using WheelWizard.Shared;

namespace WheelWizard.Test.Features;

public class LinuxDolphinInstallerTests
{
private readonly ILinuxCommandEnvironment _commandEnvironment;
private readonly ILinuxProcessService _processService;
private readonly LinuxDolphinInstaller _installer;

public LinuxDolphinInstallerTests()
{
_commandEnvironment = Substitute.For<ILinuxCommandEnvironment>();
_processService = Substitute.For<ILinuxProcessService>();
_installer = new LinuxDolphinInstaller(_commandEnvironment, _processService);
}

[Fact]
public void IsDolphinInstalledInFlatpak_ReturnsTrue_WhenFlatpakInfoExitCodeIsZero()
{
_processService.Run("flatpak", "info org.DolphinEmu.dolphin-emu").Returns(Ok(0));

var result = _installer.IsDolphinInstalledInFlatpak();

Assert.True(result);
}

[Fact]
public void IsDolphinInstalledInFlatpak_ReturnsFalse_WhenFlatpakInfoExitCodeIsNonZero()
{
_processService.Run("flatpak", "info org.DolphinEmu.dolphin-emu").Returns(Ok(1));

var result = _installer.IsDolphinInstalledInFlatpak();

Assert.False(result);
}

[Fact]
public async Task InstallFlatpak_ReturnsFailure_WhenPackageManagerCannotBeDetected()
{
_commandEnvironment.IsCommandAvailable("flatpak").Returns(false);
_commandEnvironment.DetectPackageManagerInstallCommand().Returns(string.Empty);

var result = await _installer.InstallFlatpak();

Assert.True(result.IsFailure);
Assert.Contains("Unsupported Linux distribution", result.Error.Message);
}

[Fact]
public async Task InstallFlatpak_ReturnsFailure_WhenPkexecIsUnauthorized()
{
_commandEnvironment.IsCommandAvailable("flatpak").Returns(false);
_commandEnvironment.DetectPackageManagerInstallCommand().Returns("apt-get install -y");
_processService
.RunWithProgressAsync("pkexec", "apt-get install -y flatpak", Arg.Any<IProgress<int>?>())
.Returns(Task.FromResult<OperationResult<int>>(Ok(126)));

var result = await _installer.InstallFlatpak();

Assert.True(result.IsFailure);
Assert.Contains("administrator", result.Error.Message);
}

[Fact]
public async Task InstallFlatpak_ReturnsSuccess_WhenInstallCompletesAndCommandBecomesAvailable()
{
_commandEnvironment.IsCommandAvailable("flatpak").Returns(false, true);
_commandEnvironment.DetectPackageManagerInstallCommand().Returns("apt-get install -y");
_processService
.RunWithProgressAsync("pkexec", "apt-get install -y flatpak", Arg.Any<IProgress<int>?>())
.Returns(Task.FromResult<OperationResult<int>>(Ok(0)));

var result = await _installer.InstallFlatpak();

Assert.True(result.IsSuccess);
}

[Fact]
public async Task InstallFlatpakDolphin_ReturnsFailure_WhenDolphinInstallCommandFails()
{
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
_processService
.RunWithProgressAsync("pkexec", "flatpak --system install -y org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
.Returns(Task.FromResult<OperationResult<int>>(Ok(1)));

var result = await _installer.InstallFlatpakDolphin();

Assert.True(result.IsFailure);
Assert.Contains("exit code 1", result.Error.Message);
}

[Fact]
public async Task InstallFlatpakDolphin_ReturnsFailure_WhenWarmupLaunchFails()
{
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
_processService
.RunWithProgressAsync("pkexec", "flatpak --system install -y org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
.Returns(Task.FromResult<OperationResult<int>>(Ok(0)));
_processService
.LaunchAndStopAsync("flatpak", "run org.DolphinEmu.dolphin-emu", TimeSpan.FromSeconds(4))
.Returns(Task.FromResult<OperationResult>(Fail("Launch failed")));

var result = await _installer.InstallFlatpakDolphin();

Assert.True(result.IsFailure);
Assert.Equal("Launch failed", result.Error.Message);
}

[Fact]
public async Task InstallFlatpakDolphin_ReturnsSuccess_WhenInstallAndWarmupSucceed()
{
_commandEnvironment.IsCommandAvailable("flatpak").Returns(true);
_processService
.RunWithProgressAsync("pkexec", "flatpak --system install -y org.DolphinEmu.dolphin-emu", Arg.Any<IProgress<int>?>())
.Returns(Task.FromResult<OperationResult<int>>(Ok(0)));
_processService
.LaunchAndStopAsync("flatpak", "run org.DolphinEmu.dolphin-emu", TimeSpan.FromSeconds(4))
.Returns(Task.FromResult<OperationResult>(Ok()));

var result = await _installer.InstallFlatpakDolphin();

Assert.True(result.IsSuccess);
}
}
149 changes: 149 additions & 0 deletions WheelWizard.Test/Features/Settings/DolphinSettingsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using Testably.Abstractions.Testing;
using WheelWizard.Services;
using WheelWizard.Settings;
using WheelWizard.Settings.Types;

namespace WheelWizard.Test.Features.Settings;

[Collection("SettingsFeature")]
public class DolphinSettingTests
{
[Fact]
public void Constructor_Throws_WhenFileNameIsNotIni()
{
var action = () => new DolphinSetting(typeof(string), ("Dolphin.cfg", "General", "NANDRootPath"), "value");

Assert.Throws<ArgumentException>(action);
}

[Fact]
public void SetFromString_ParsesEnumAndFormatsAsIntegerString()
{
var setting = new DolphinSetting(
typeof(DolphinShaderCompilationMode),
("GFX.ini", "Settings", "ShaderCompilationMode"),
DolphinShaderCompilationMode.Default
);

var result = setting.SetFromString("2", skipSave: true);

Assert.True(result);
Assert.Equal(DolphinShaderCompilationMode.HybridUberShaders, Assert.IsType<DolphinShaderCompilationMode>(setting.Get()));
Assert.Equal("2", setting.GetStringValue());
}

[Fact]
public void Set_ReturnsFalseAndKeepsOldValue_WhenValidationFails()
{
var setting = new DolphinSetting(typeof(int), ("GFX.ini", "Settings", "InternalResolution"), 1).SetValidation(value =>
(int)value! >= 0
);
setting.Set(2);

var result = setting.Set(-1);

Assert.False(result);
Assert.Equal(2, Assert.IsType<int>(setting.Get()));
}

[Fact]
public void SetFromString_Throws_WhenTypeIsUnsupported()
{
var setting = new DolphinSetting(typeof(decimal), ("GFX.ini", "Settings", "Price"), 1m);

Assert.Throws<InvalidOperationException>(() => setting.SetFromString("3.14"));
}
}

[Collection("SettingsFeature")]
public class DolphinSettingManagerTests : IDisposable
{
[Fact]
public void LoadSettings_ReadsExistingValue_FromIniFile()
{
var fileSystem = new MockFileSystem();
var userFolderPath = $"/wheelwizard-user-{Guid.NewGuid():N}";
SettingsTestUtils.InitializeSettingsRuntime(userFolderPath);
var configFolderPath = PathManager.ConfigFolderPath;
var iniPath = Path.Combine(configFolderPath, "Dolphin.ini");
fileSystem.Directory.CreateDirectory(configFolderPath);
fileSystem.File.WriteAllLines(iniPath, ["[General]", "NANDRootPath = /persisted"]);
var manager = new DolphinSettingManager(fileSystem);
var setting = new DolphinSetting(typeof(string), ("Dolphin.ini", "General", "NANDRootPath"), "/default");

manager.RegisterSetting(setting);
manager.LoadSettings();

Assert.Equal("/persisted", Assert.IsType<string>(setting.Get()));
}

[Fact]
public void LoadSettings_WritesDefaultValue_WhenIniEntryIsMissing()
{
var fileSystem = new MockFileSystem();
var userFolderPath = $"/wheelwizard-user-{Guid.NewGuid():N}";
SettingsTestUtils.InitializeSettingsRuntime(userFolderPath);
var configFolderPath = PathManager.ConfigFolderPath;
var iniPath = Path.Combine(configFolderPath, "Dolphin.ini");
fileSystem.Directory.CreateDirectory(configFolderPath);
fileSystem.File.WriteAllLines(iniPath, ["[General]", "OtherSetting = 1"]);
var manager = new DolphinSettingManager(fileSystem);
var setting = new DolphinSetting(typeof(string), ("Dolphin.ini", "General", "NANDRootPath"), "/default");

manager.RegisterSetting(setting);
manager.LoadSettings();

var updatedFile = fileSystem.File.ReadAllText(iniPath);
Assert.Contains("NANDRootPath = /default", updatedFile);
}

[Fact]
public void SaveSettings_UpdatesExistingSettingLine_InIniFile()
{
var fileSystem = new MockFileSystem();
var userFolderPath = $"/wheelwizard-user-{Guid.NewGuid():N}";
SettingsTestUtils.InitializeSettingsRuntime(userFolderPath);
var configFolderPath = PathManager.ConfigFolderPath;
var iniPath = Path.Combine(configFolderPath, "Dolphin.ini");
fileSystem.Directory.CreateDirectory(configFolderPath);
fileSystem.File.WriteAllLines(iniPath, ["[General]", "NANDRootPath = /old"]);
var manager = new DolphinSettingManager(fileSystem);
var setting = new DolphinSetting(typeof(string), ("Dolphin.ini", "General", "NANDRootPath"), "/default");

manager.RegisterSetting(setting);
manager.LoadSettings();
setting.Set("/new", skipSave: true);
manager.SaveSettings(setting);

var updatedFile = fileSystem.File.ReadAllText(iniPath);
Assert.Contains("NANDRootPath = /new", updatedFile);
Assert.DoesNotContain("NANDRootPath = /old", updatedFile);
}

[Fact]
public void ReloadSettings_ReReadsFile_AfterItChangesOnDisk()
{
var fileSystem = new MockFileSystem();
var userFolderPath = $"/wheelwizard-user-{Guid.NewGuid():N}";
SettingsTestUtils.InitializeSettingsRuntime(userFolderPath);
var configFolderPath = PathManager.ConfigFolderPath;
var iniPath = Path.Combine(configFolderPath, "Dolphin.ini");
fileSystem.Directory.CreateDirectory(configFolderPath);
fileSystem.File.WriteAllLines(iniPath, ["[General]", "NANDRootPath = /first"]);
var manager = new DolphinSettingManager(fileSystem);
var setting = new DolphinSetting(typeof(string), ("Dolphin.ini", "General", "NANDRootPath"), "/default");

manager.RegisterSetting(setting);
manager.LoadSettings();
fileSystem.File.WriteAllLines(iniPath, ["[General]", "NANDRootPath = /second"]);
manager.ReloadSettings();

Assert.Equal("/second", Assert.IsType<string>(setting.Get()));
}

public void Dispose()
{
SettingsTestUtils.ResetSettingsRuntime();
SettingsTestUtils.ResetSignalRuntime();
}
}
Loading
Loading