diff --git a/Directory.Build.props b/Directory.Build.props
index 18f9fc75..b02e2e6e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,54 @@
false
+ Debug;Release;Debug-Windows;Debug-Linux;Debug-macOS;Release-Windows;Release-Linux;Release-macOS
+ AnyCPU
+
+
+
+ $(DefineConstants);DEBUG;TRACE;WINDOWS
+ win-x64
+ false
+ true
+ full
+
+
+
+ $(DefineConstants);DEBUG;TRACE;LINUX
+ linux-x64
+ false
+ true
+ full
+
+
+
+ $(DefineConstants);DEBUG;TRACE;MACOS
+ osx-arm64
+ false
+ true
+ full
+
+
+
+
+ $(DefineConstants);TRACE;WINDOWS
+ win-x64
+ true
+
+
+
+ $(DefineConstants);TRACE;LINUX
+ linux-x64
+ true
+
+
+
+ $(DefineConstants);TRACE;MACOS
+ osx-arm64
+ true
+
+
all
diff --git a/Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml b/Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml
index f9fc8280..d20b9492 100644
--- a/Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml
+++ b/Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml
@@ -31,19 +31,19 @@
Wheel Wizard's home page
- https://raw.githubusercontent.com/TeamWheelWizard/.github/d8eb58433cea25d438f4d6fb79bfececd5ccbc73/images/screenshots/home_page.png
+ https://raw.githubusercontent.com/TeamWheelWizard/.github/261be51961d07ac217d343f4a03231b57e603866/images/screenshots/home_page.png
Wheel Wizard's room details page
- https://raw.githubusercontent.com/TeamWheelWizard/.github/d8eb58433cea25d438f4d6fb79bfececd5ccbc73/images/screenshots/rooms_page.png
+ https://raw.githubusercontent.com/TeamWheelWizard/.github/d1c065020c1a526d80eb08f53423543f0f5c3439/images/screenshots/rooms_page.png
Wheel Wizard's profile page
- https://raw.githubusercontent.com/TeamWheelWizard/.github/d8eb58433cea25d438f4d6fb79bfececd5ccbc73/images/screenshots/profile_page.png
+ https://raw.githubusercontent.com/TeamWheelWizard/.github/d1c065020c1a526d80eb08f53423543f0f5c3439/images/screenshots/profile_page.png
- Wheel Wizard's mod browser
- https://raw.githubusercontent.com/TeamWheelWizard/.github/d8eb58433cea25d438f4d6fb79bfececd5ccbc73/images/screenshots/mods_browser.png
+ Wheel Wizard's Mii management page
+ https://raw.githubusercontent.com/TeamWheelWizard/.github/d1c065020c1a526d80eb08f53423543f0f5c3439/images/screenshots/mii_page.png
@@ -55,6 +55,11 @@
+
+
+
+
+
diff --git a/README.md b/README.md
index ec6b4800..c89880fc 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,8 @@ Feel free to join our community on [Discord](https://discord.gg/vZ7T2wJnsq) for
+
+
---
diff --git a/WheelWizard.Test/Features/GameBananaTests.cs b/WheelWizard.Test/Features/GameBananaTests.cs
index 6b20ef20..5d59fb34 100644
--- a/WheelWizard.Test/Features/GameBananaTests.cs
+++ b/WheelWizard.Test/Features/GameBananaTests.cs
@@ -89,7 +89,7 @@ public async Task GetModSearchResults_WithApiError_ReturnsFailure()
_apiCaller
.CallApiAsync(Arg.Any>>>())
- .Returns(Fail(expectedError));
+ .Returns(Fail(expectedError));
// Act
var result = await _service.GetModSearchResults(searchTerm, page);
@@ -128,9 +128,7 @@ public async Task GetModDetails_WithApiError_ReturnsFailure()
var modId = 123;
var expectedError = "API Error";
- _apiCaller
- .CallApiAsync(Arg.Any>>>())
- .Returns(Fail(expectedError));
+ _apiCaller.CallApiAsync(Arg.Any>>>()).Returns(Fail(expectedError));
// Act
var result = await _service.GetModDetails(modId);
diff --git a/WheelWizard.Test/Features/MiiDbServiceTests.cs b/WheelWizard.Test/Features/MiiDbServiceTests.cs
index 3d471544..b2cbafc1 100644
--- a/WheelWizard.Test/Features/MiiDbServiceTests.cs
+++ b/WheelWizard.Test/Features/MiiDbServiceTests.cs
@@ -1,8 +1,8 @@
using NSubstitute.ExceptionExtensions;
using Testably.Abstractions;
using WheelWizard.Shared;
-using WheelWizard.WiiManagement;
-using WheelWizard.WiiManagement.Domain.Mii;
+using WheelWizard.WiiManagement.MiiManagement;
+using WheelWizard.WiiManagement.MiiManagement.Domain.Mii;
namespace WheelWizard.Test.Features
{
@@ -30,13 +30,13 @@ private OperationResult CreateValidMii(uint id = 1, string name = "TestMii"
var height = MiiScale.Create(60);
var weight = MiiScale.Create(50);
var miiFacial = MiiFacialFeatures.Create(MiiFaceShape.Bread, MiiSkinColor.Brown, MiiFacialFeature.Beard, false, false);
- var miiHair = MiiHair.Create(1, HairColor.Black, false);
- var miiEyebrows = MiiEyebrow.Create(3, 3, EyebrowColor.Black, 3, 3, 3);
- var miiEyes = MiiEye.Create(3, 3, 3, EyeColor.Black, 3, 3);
- var miiNose = MiiNose.Create(NoseType.Default, 3, 3);
- var miiLips = MiiLip.Create(3, LipColor.Pink, 3, 3);
- var miiGlasses = MiiGlasses.Create(GlassesType.None, GlassesColor.Blue, 3, 3);
- var miiFacialHair = MiiFacialHair.Create(MustacheType.None, BeardType.None, MustacheColor.Black, 3, 3);
+ var miiHair = MiiHair.Create(1, MiiHairColor.Black, false);
+ var miiEyebrows = MiiEyebrow.Create(3, 3, MiiHairColor.Black, 3, 3, 3);
+ var miiEyes = MiiEye.Create(3, 3, 3, MiiEyeColor.Black, 3, 3);
+ var miiNose = MiiNose.Create(MiiNoseType.Default, 3, 3);
+ var miiLips = MiiLip.Create(3, MiiLipColor.Pink, 3, 3);
+ var miiGlasses = MiiGlasses.Create(MiiGlassesType.None, MiiGlassesColor.Blue, 3, 3);
+ var miiFacialHair = MiiFacialHair.Create(MiiMustacheType.None, MiiBeardType.None, MiiHairColor.Black, 3, 3);
var miiMole = MiiMole.Create(true, 3, 3, 3);
var creatorName = MiiName.Create("Creator");
var miiFavoriteColor = MiiFavoriteColor.Red;
@@ -57,9 +57,7 @@ private OperationResult CreateValidMii(uint id = 1, string name = "TestMii"
creatorName,
};
if (EveryResult.Any(r => r.IsFailure))
- {
- return Fail(EveryResult.First(r => r.IsFailure).Error);
- }
+ return EveryResult.First(r => r.IsFailure).Error!;
return Ok(
new Mii
@@ -68,7 +66,7 @@ private OperationResult CreateValidMii(uint id = 1, string name = "TestMii"
MiiId = miiId,
Height = height.Value,
Weight = weight.Value,
- MiiFacial = miiFacial.Value,
+ MiiFacialFeatures = miiFacial.Value,
MiiHair = miiHair.Value,
MiiEyebrows = miiEyebrows.Value,
MiiEyes = miiEyes.Value,
@@ -131,7 +129,6 @@ public void MiiSerializer_Deserialize_ShouldFail_ForInvalidDataLength()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal(result.IsFailure, true);
}
[Fact]
@@ -145,7 +142,6 @@ public void MiiSerializer_Deserialize_ShouldFail_ForNullData()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal(result.IsFailure, true);
}
[Fact]
@@ -280,7 +276,7 @@ public void GetByClientId_ShouldReturnFailure_WhenDeserializationFails()
// Arrange
uint targetId = 666;
var badBytes = new byte[MiiSerializer.MiiBlockSize];
- for (int i = 0; i < badBytes.Length; i++)
+ for (var i = 0; i < badBytes.Length; i++)
{
badBytes[i] = (byte)(i % 256);
}
@@ -292,7 +288,6 @@ public void GetByClientId_ShouldReturnFailure_WhenDeserializationFails()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal(result.IsFailure, true);
_repositoryService.Received(1).GetRawBlockByAvatarId(targetId);
}
@@ -350,7 +345,7 @@ public void Update_ShouldReturnFailure_WhenRepositoryUpdateFails()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal(repoError.Error, result.Error); // Propagate the exact error
+ Assert.Equal(repoError, result.Error); // Propagate the exact error
_repositoryService.Received(1).UpdateBlockByClientId(miiToUpdate.MiiId, Arg.Is(b => b.SequenceEqual(expectedBytes)));
}
@@ -385,8 +380,8 @@ public void UpdateName_ShouldReturnSuccess_WhenGetAndUpdateSucceed()
{
// Arrange
uint targetId = 333;
- string oldName = "OldName";
- string newName = "NewName";
+ var oldName = "OldName";
+ var newName = "NewName";
var miiResult = CreateValidMii(targetId, oldName);
Assert.True(miiResult.IsSuccess, "Setup Failed: Could not create original Mii");
var originalMii = miiResult.Value;
@@ -420,7 +415,7 @@ public void UpdateName_ShouldReturnFailure_WhenGetByClientIdFails_NotFound()
{
// Arrange
uint targetId = 404;
- string newName = "NewName";
+ var newName = "NewName";
_repositoryService.GetRawBlockByAvatarId(targetId).Returns((byte[]?)null);
// Act
@@ -428,7 +423,7 @@ public void UpdateName_ShouldReturnFailure_WhenGetByClientIdFails_NotFound()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal("Mii block not found or invalid.", result.Error.Message); // Error from GetByClientId
+ Assert.Equal("Mii block not found", result.Error.Message); // Error from GetByClientId
_repositoryService.Received(1).GetRawBlockByAvatarId(targetId);
_repositoryService.DidNotReceive().UpdateBlockByClientId(Arg.Any(), Arg.Any());
}
@@ -438,7 +433,7 @@ public void UpdateName_ShouldReturnFailure_WhenGetByClientIdFails_Deserializatio
{
// Arrange
uint targetId = 666;
- string newName = "NewName";
+ var newName = "NewName";
var badBytes = new byte[MiiSerializer.MiiBlockSize]; // Correct size, bad content
_repositoryService.GetRawBlockByAvatarId(targetId).Returns(badBytes);
@@ -457,8 +452,8 @@ public void UpdateName_ShouldReturnFailure_WhenNewNameIsInvalid()
{
// Arrange
uint targetId = 555;
- string oldName = "ValidOld";
- string invalidNewName = "ThisNameIsDefinitelyTooLongForTheMii"; // > 10 chars
+ var oldName = "ValidOld";
+ var invalidNewName = "ThisNameIsDefinitelyTooLongForTheMii"; // > 10 chars
var miiResult = CreateValidMii(targetId, oldName);
Assert.True(miiResult.IsSuccess, "Setup Failed: Could not create original Mii");
var originalBytes = GetSerializedBytes(miiResult.Value);
@@ -470,7 +465,6 @@ public void UpdateName_ShouldReturnFailure_WhenNewNameIsInvalid()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal(result.IsFailure, true);
_repositoryService.Received(1).GetRawBlockByAvatarId(targetId);
_repositoryService.DidNotReceive().UpdateBlockByClientId(Arg.Any(), Arg.Any());
}
@@ -480,8 +474,8 @@ public void UpdateName_ShouldReturnFailure_WhenRepositoryUpdateFails()
{
// Arrange
uint targetId = 777;
- string oldName = "Old";
- string newName = "New";
+ var oldName = "Old";
+ var newName = "New";
var miiResult = CreateValidMii(targetId, oldName);
Assert.True(miiResult.IsSuccess, "Setup Failed: Could not create original Mii");
var originalBytes = GetSerializedBytes(miiResult.Value);
@@ -497,7 +491,7 @@ public void UpdateName_ShouldReturnFailure_WhenRepositoryUpdateFails()
// Assert
Assert.True(result.IsFailure);
- Assert.Equal(repoError.Error, result.Error); // Error from the repository update propagated
+ Assert.Equal(repoError, result.Error); // Error from the repository update propagated
_repositoryService.Received(1).GetRawBlockByAvatarId(targetId);
_repositoryService.Received(1).UpdateBlockByClientId(targetId, Arg.Any());
}
@@ -507,7 +501,7 @@ public void UpdateName_ShouldHandleExceptionDuringGet()
{
// Arrange
uint targetId = 111;
- string newName = "New";
+ var newName = "New";
var expectedException = new TimeoutException("Timeout contacting repository");
_repositoryService.GetRawBlockByAvatarId(targetId).Throws(expectedException);
@@ -523,8 +517,8 @@ public void UpdateName_ShouldHandleExceptionDuringUpdate()
{
// Arrange
uint targetId = 222;
- string oldName = "Old";
- string newName = "New";
+ var oldName = "Old";
+ var newName = "New";
var miiResult = CreateValidMii(targetId, oldName);
Assert.True(miiResult.IsSuccess, "Setup Failed: Could not create original Mii");
var originalBytes = GetSerializedBytes(miiResult.Value);
diff --git a/WheelWizard.Test/Features/MiiSerializerTests.cs b/WheelWizard.Test/Features/MiiSerializerTests.cs
index f9101e58..f41fce5c 100644
--- a/WheelWizard.Test/Features/MiiSerializerTests.cs
+++ b/WheelWizard.Test/Features/MiiSerializerTests.cs
@@ -1,4 +1,5 @@
using WheelWizard.WiiManagement;
+using WheelWizard.WiiManagement.MiiManagement;
namespace WheelWizard.Test.Features;
@@ -94,7 +95,7 @@ public void RoundTrip_Serialization_ShouldBeConsistent(string base64Data)
Assert.Equal(mii.Name.ToString(), miiRoundTrip.Name.ToString());
Assert.Equal(mii.Height.Value, miiRoundTrip.Height.Value);
Assert.Equal(mii.Weight.Value, miiRoundTrip.Weight.Value);
- Assert.Equal(mii.MiiFacial.FaceShape, miiRoundTrip.MiiFacial.FaceShape);
+ Assert.Equal(mii.MiiFacialFeatures.FaceShape, miiRoundTrip.MiiFacialFeatures.FaceShape);
Assert.Equal(mii.MiiEyes.Type, miiRoundTrip.MiiEyes.Type);
Assert.Equal(mii.MiiGlasses.Type, miiRoundTrip.MiiGlasses.Type);
Assert.Equal(mii.CreatorName.ToString(), miiRoundTrip.CreatorName.ToString());
diff --git a/WheelWizard.Test/Features/WhWzDataTests.cs b/WheelWizard.Test/Features/WhWzDataTests.cs
index 952749b7..f1f9366b 100644
--- a/WheelWizard.Test/Features/WhWzDataTests.cs
+++ b/WheelWizard.Test/Features/WhWzDataTests.cs
@@ -41,7 +41,7 @@ public async Task GetStatusAsync_ReturnsFailure_WhenApiCallFails()
// Arrange
var expectedError = new OperationError { Message = "API call failed" };
- _apiCaller.CallApiAsync(Arg.Any>>>()).Returns(Fail(expectedError));
+ _apiCaller.CallApiAsync(Arg.Any>>>()).Returns(expectedError);
// Act
var result = await _service.GetStatusAsync();
@@ -59,7 +59,7 @@ public async Task LoadBadgesAsync_ReturnsSuccess_WhenApiCallSucceeds()
{
{ "FC1", [BadgeVariant.WhWzDev, BadgeVariant.Translator] },
{ "FC2", [BadgeVariant.RrDev] },
- { "FC3", [BadgeVariant.None, BadgeVariant.GoldWinner] },
+ { "FC3", [BadgeVariant.None, BadgeVariant.Firestarter_GoldWinner] },
};
_apiCaller.CallApiAsync(Arg.Any>>>>()).Returns(Ok(badgeData));
@@ -77,9 +77,7 @@ public async Task LoadBadgesAsync_ReturnsFailure_WhenApiCallFails()
// Arrange
var expectedError = new OperationError { Message = "API call failed" };
- _apiCaller
- .CallApiAsync(Arg.Any>>>>())
- .Returns(Fail>(expectedError));
+ _apiCaller.CallApiAsync(Arg.Any>>>>()).Returns(expectedError);
// Act
var result = await _service.LoadBadgesAsync();
@@ -162,7 +160,10 @@ public async Task LoadBadgesAsync_OverwritesExistingBadges_WhenCalledMultipleTim
Assert.Contains(BadgeVariant.WhWzDev, initialBadges);
// Arrange - Second load with different data
- var updatedBadgeData = new Dictionary { { "FC1", [BadgeVariant.Translator, BadgeVariant.GoldWinner] } };
+ var updatedBadgeData = new Dictionary
+ {
+ { "FC1", [BadgeVariant.Translator, BadgeVariant.Firestarter_GoldWinner] },
+ };
_apiCaller
.CallApiAsync(Arg.Any>>>>())
@@ -175,7 +176,7 @@ public async Task LoadBadgesAsync_OverwritesExistingBadges_WhenCalledMultipleTim
var updatedBadges = _service.GetBadges("FC1");
Assert.Equal(2, updatedBadges.Length);
Assert.Contains(BadgeVariant.Translator, updatedBadges);
- Assert.Contains(BadgeVariant.GoldWinner, updatedBadges);
+ Assert.Contains(BadgeVariant.Firestarter_GoldWinner, updatedBadges);
Assert.DoesNotContain(BadgeVariant.WhWzDev, updatedBadges);
}
}
diff --git a/WheelWizard.Test/GlobalUsings.cs b/WheelWizard.Test/GlobalUsings.cs
index c728b1f1..59a26d6c 100644
--- a/WheelWizard.Test/GlobalUsings.cs
+++ b/WheelWizard.Test/GlobalUsings.cs
@@ -1,2 +1,3 @@
global using NSubstitute;
+global using static WheelWizard.Shared.OperationError;
global using static WheelWizard.Shared.OperationResult;
diff --git a/WheelWizard.Test/Shared/OperationResult/OperationResultTests.cs b/WheelWizard.Test/Shared/OperationResult/OperationResultTests.cs
index cc906e10..c142b698 100644
--- a/WheelWizard.Test/Shared/OperationResult/OperationResultTests.cs
+++ b/WheelWizard.Test/Shared/OperationResult/OperationResultTests.cs
@@ -1,4 +1,6 @@
-using WheelWizard.Shared;
+using WheelWizard.Helpers;
+using WheelWizard.Shared;
+using WheelWizard.Shared.MessageTranslations;
namespace WheelWizard.Test.Shared.OperationResultTests;
@@ -52,12 +54,29 @@ public void CreateFailureResult_ShouldHaveCorrectState()
var error = new OperationError { Message = "Error message" };
// Act
- var operationResult = Fail(error);
+ OperationResult operationResult = error;
// Assert
Assert.Equal(error, operationResult.Error);
Assert.True(operationResult.IsFailure);
Assert.False(operationResult.IsSuccess);
+ Assert.Null(operationResult.Error!.MessageTranslation);
+ }
+
+ [Fact(DisplayName = "Create failure result, should have correct state with translation")]
+ public void CreateFailureResult_ShouldHaveCorrectStateWithTranslation()
+ {
+ const string errorMessage = "Error message";
+ const MessageTranslation errorTranslation = MessageTranslation.Error_StanderdError;
+
+ // Act
+ OperationResult operationResult = Fail(errorMessage, errorTranslation);
+
+ // Assert
+ Assert.True(operationResult.IsFailure);
+ Assert.False(operationResult.IsSuccess);
+ Assert.Equal(errorMessage, operationResult.Error.Message);
+ Assert.Equal(errorTranslation, operationResult.Error.MessageTranslation);
}
[Fact(DisplayName = "Implicit result from error, should have correct state")]
@@ -129,7 +148,7 @@ public void CreateFailureGenericResult_ShouldHaveCorrectState()
var error = new OperationError { Message = "Error message" };
// Act
- var operationResult = Fail(error);
+ OperationResult operationResult = error;
// Assert
Assert.Equal(error, operationResult.Error);
@@ -150,6 +169,7 @@ public void ImplicitGenericResultFromError_ShouldHaveCorrectState()
Assert.Equal(error, operationResult.Error);
Assert.True(operationResult.IsFailure);
Assert.False(operationResult.IsSuccess);
+ Assert.Null(operationResult.Error!.MessageTranslation);
}
[Fact(DisplayName = "Implicit generic result from value, should have correct state")]
@@ -168,22 +188,6 @@ public void ImplicitGenericResultFromValue_ShouldHaveCorrectState()
Assert.Equal(value, operationResult.Value);
}
- [Fact(DisplayName = "Implicit result from string, should have failed state")]
- public void ImplicitResultFromString_ShouldHaveFailedState()
- {
- // Arrange
- const string errorMessage = "Error message";
-
- // Act
- OperationResult operationResult = errorMessage;
-
- // Assert
- Assert.NotNull(operationResult.Error);
- Assert.True(operationResult.IsFailure);
- Assert.False(operationResult.IsSuccess);
- Assert.Equal(errorMessage, operationResult.Error?.Message);
- }
-
[Fact(DisplayName = "Implicit result from exception, should have failed state")]
public void ImplicitResultFromException_ShouldHaveFailedState()
{
@@ -198,22 +202,7 @@ public void ImplicitResultFromException_ShouldHaveFailedState()
Assert.True(operationResult.IsFailure);
Assert.False(operationResult.IsSuccess);
Assert.Equal(exception.Message, operationResult.Error?.Message);
- }
-
- [Fact(DisplayName = "Implicit generic result from string, should have correct failed state")]
- public void ImplicitGenericResultFromString_ShouldHaveCorrectFailedState()
- {
- // Arrange
- const string errorMessage = "Error message";
-
- // Act
- OperationResult