Refactor Home UI controls and add dice to campaign log entries

This commit is contained in:
2026-02-26 09:22:29 +01:00
parent 96238a9341
commit 2d1bf9b9b7
20 changed files with 774 additions and 255 deletions

View File

@@ -78,7 +78,7 @@ public partial class Home
private string? SelectedCampaignName => SelectedCampaign?.Name;
private CharacterSummary? SelectedCharacter => SelectedCampaign?.Characters.FirstOrDefault(c => c.Id == SelectedCharacterId);
private SkillSummary? SelectedSkill => SelectedCampaign?.Skills.FirstOrDefault(s => s.Id == SelectedSkillId);
private string? ActiveCharacterName => SelectedCampaign?.Characters.FirstOrDefault(c => c.Id == ActiveCharacterId)?.Name;
private string? ActiveCharacterName => SelectedCampaign?.Characters.FirstOrDefault(c => c.Id == SelectedCharacterId)?.Name;
private bool IsCurrentUserGm => SelectedCampaign is not null && User is not null && SelectedCampaign.Gm.Id == User.Id;
private bool IsSelectedCampaignD6 => string.Equals(SelectedCampaign?.RulesetId, "d6", StringComparison.OrdinalIgnoreCase);
@@ -103,6 +103,8 @@ public partial class Home
_ => "offline"
};
private string AppCssClass => User is not null && CurrentScreen == "play" ? "rr-app app-play" : "rr-app";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
HasInteractiveRenderStarted = true;
@@ -264,6 +266,7 @@ public partial class Home
CampaignLog = (await RequestAsync<IReadOnlyList<CampaignLogEntry>>("GET", $"/api/campaigns/{SelectedCampaignId.Value}/log")).ToList();
SyncSelectedCharacter();
SyncSelectedSkill();
await EnsureSelectedCharacterActiveAsync();
}
catch (ApiRequestException ex) when (ex.StatusCode == 401)
{
@@ -619,27 +622,6 @@ public partial class Home
}
}
private async Task ActivateCharacterAsync(Guid characterId)
{
IsMutating = true;
try
{
await RequestWithoutPayloadAsync("POST", $"/api/characters/{characterId}/activate");
ActiveCharacterId = characterId;
SelectedCharacterId = characterId;
await RefreshCampaignScopeAsync();
SetStatus("Active character updated.", false);
}
catch (ApiRequestException ex)
{
SetStatus(ex.Message, true);
}
finally
{
IsMutating = false;
}
}
private void OpenCreateSkillModal()
{
SkillForm.Name = string.Empty;
@@ -808,10 +790,17 @@ public partial class Home
}
}
private void SelectCharacter(Guid characterId)
private Task OnRollVisibilityChanged(string visibility)
{
RollVisibility = string.Equals(visibility, "private", StringComparison.OrdinalIgnoreCase) ? "private" : "public";
return Task.CompletedTask;
}
private async Task SelectCharacterAsync(Guid characterId)
{
SelectedCharacterId = characterId;
SyncSelectedSkill();
await EnsureSelectedCharacterActiveAsync();
}
private void SelectSkill(Guid skillId)
@@ -829,6 +818,35 @@ public partial class Home
return User is not null && character.OwnerUserId == User.Id;
}
private async Task EnsureSelectedCharacterActiveAsync()
{
if (!SelectedCharacterId.HasValue || SelectedCampaign is null)
{
return;
}
var character = SelectedCampaign.Characters.FirstOrDefault(c => c.Id == SelectedCharacterId.Value);
if (character is null || !CanActivateCharacter(character))
{
return;
}
if (ActiveCharacterId == character.Id)
{
return;
}
try
{
await RequestWithoutPayloadAsync("POST", $"/api/characters/{character.Id}/activate");
ActiveCharacterId = character.Id;
}
catch (ApiRequestException ex)
{
SetStatus(ex.Message, true);
}
}
private bool CanEditSkill(SkillSummary skill)
{
if (SelectedCampaign is null)
@@ -1033,82 +1051,6 @@ public partial class Home
return $"{skill.DiceRollDefinition} | wild {skill.WildDice}, {fumble}";
}
private static string RollDieGlyph(int roll)
{
return roll switch
{
1 => "\u2680",
2 => "\u2681",
3 => "\u2682",
4 => "\u2683",
5 => "\u2684",
6 => "\u2685",
_ => roll.ToString()
};
}
private static string RollDieCssClass(RollDieResult die)
{
var classes = new List<string> { "die-chip" };
if (die.Wild)
{
classes.Add("wild");
}
if (die.Crit)
{
classes.Add("crit");
}
if (die.Fumble)
{
classes.Add("fumble");
}
if (die.Removed)
{
classes.Add("removed");
}
if (die.Added)
{
classes.Add("added");
}
return string.Join(" ", classes);
}
private static string RollDieTitle(RollDieResult die)
{
var labels = new List<string> { $"Roll {die.Roll}" };
if (die.Wild)
{
labels.Add("wild");
}
if (die.Crit)
{
labels.Add("critical");
}
if (die.Fumble)
{
labels.Add("fumble");
}
if (die.Removed)
{
labels.Add("removed");
}
if (die.Added)
{
labels.Add("added");
}
return string.Join(", ", labels);
}
private string RollerLabel(CampaignLogEntry entry)
{
if (User is not null && entry.RollerUserId == User.Id)
@@ -1184,22 +1126,6 @@ public partial class Home
return "private-generic";
}
private static string InitialsFor(string value)
{
var words = value.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (words.Length == 0)
{
return "?";
}
if (words.Length == 1)
{
return words[0].Length >= 2 ? words[0][..2].ToUpperInvariant() : words[0].ToUpperInvariant();
}
return string.Concat(words[0][0], words[1][0]).ToUpperInvariant();
}
private void ClearAuthenticatedState()
{
User = null;