Extract workspace play coordinator
This commit is contained in:
@@ -75,57 +75,7 @@ public partial class Workspace : IAsyncDisposable
|
||||
await EnsureSelectedCharacterActiveAsync();
|
||||
}
|
||||
|
||||
private async Task RefreshCampaignLogAsync(Guid? afterRollId = null)
|
||||
{
|
||||
if (!SelectedCampaignId.HasValue || !IsPlayScreen)
|
||||
{
|
||||
CampaignLog = [];
|
||||
CampaignLogCursor = null;
|
||||
ResetCampaignLogDetailState();
|
||||
return;
|
||||
}
|
||||
|
||||
var previousLogCount = CampaignLog.Count;
|
||||
var page = await WorkspaceQuery.GetCampaignLogPageAsync(SelectedCampaignId.Value, afterRollId, CampaignLogWindowSize);
|
||||
Guid? newestRollId = null;
|
||||
if (!afterRollId.HasValue || page.ResetRequired)
|
||||
{
|
||||
CampaignLog = page.Entries.ToList();
|
||||
}
|
||||
else if (page.Entries.Length > 0)
|
||||
{
|
||||
CampaignLog.AddRange(page.Entries);
|
||||
if (CampaignLog.Count > CampaignLogWindowSize)
|
||||
CampaignLog = CampaignLog.TakeLast(CampaignLogWindowSize).ToList();
|
||||
}
|
||||
|
||||
var shouldAutoExpandNewest = afterRollId.HasValue && page.Entries.Length > 0;
|
||||
if (!shouldAutoExpandNewest &&
|
||||
!afterRollId.HasValue &&
|
||||
CurrentCampaignState is not null &&
|
||||
previousLogCount == 0 &&
|
||||
page.Entries.Length > 0)
|
||||
{
|
||||
shouldAutoExpandNewest = true;
|
||||
}
|
||||
|
||||
if (shouldAutoExpandNewest)
|
||||
{
|
||||
newestRollId = page.Entries[^1].RollId;
|
||||
ExpandedCampaignLogRollId = newestRollId;
|
||||
FreshCampaignLogRollId = newestRollId;
|
||||
}
|
||||
else if (!afterRollId.HasValue)
|
||||
{
|
||||
FreshCampaignLogRollId = null;
|
||||
}
|
||||
|
||||
CampaignLogCursor = page.Cursor ?? afterRollId;
|
||||
TrimCampaignLogDetails();
|
||||
|
||||
if (newestRollId.HasValue)
|
||||
await EnsureRollDetailLoadedAsync(newestRollId.Value);
|
||||
}
|
||||
private Task RefreshCampaignLogAsync(Guid? afterRollId = null) => Play.RefreshCampaignLogAsync(afterRollId);
|
||||
|
||||
private async Task RefreshCampaignScopeAsync()
|
||||
{
|
||||
@@ -226,188 +176,41 @@ public partial class Workspace : IAsyncDisposable
|
||||
|
||||
private Task DeleteCharacterAsync(CharacterSummary character) => CampaignsFlow.DeleteCharacterAsync(character);
|
||||
|
||||
private async Task SelectCharacterAsync(Guid characterId)
|
||||
{
|
||||
SelectedCharacterId = characterId;
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
await EnsureSelectedCharacterActiveAsync();
|
||||
}
|
||||
private Task SelectCharacterAsync(Guid characterId) => Play.SelectCharacterAsync(characterId);
|
||||
|
||||
private bool CanEditCharacter(CharacterSummary character) => CampaignsFlow.CanEditCharacter(character);
|
||||
|
||||
private bool CanDeleteCharacter(CharacterSummary character) => CampaignsFlow.CanDeleteCharacter(character);
|
||||
|
||||
private static bool CanActivateCharacter(CharacterSummary character, UserSummary? user)
|
||||
{
|
||||
return user is not null && character.OwnerUserId == user.Id;
|
||||
}
|
||||
private Task EnsureSelectedCharacterActiveAsync() => Play.EnsureSelectedCharacterActiveAsync();
|
||||
|
||||
private async Task EnsureSelectedCharacterActiveAsync()
|
||||
{
|
||||
if (!SelectedCharacterId.HasValue || SelectedCampaign is null)
|
||||
return;
|
||||
private Task RefreshSelectedCharacterSheetAsync() => Play.RefreshSelectedCharacterSheetAsync();
|
||||
|
||||
var character = SelectedCampaign.Characters.FirstOrDefault(c => c.Id == SelectedCharacterId.Value);
|
||||
if (character is null || !CanActivateCharacter(character, User) || ActiveCharacterId == character.Id)
|
||||
return;
|
||||
private Task ToggleRollDetailAsync(Guid rollId) => Play.ToggleRollDetailAsync(rollId);
|
||||
|
||||
try
|
||||
{
|
||||
await ApiClient.RequestWithoutPayloadAsync("POST", $"/api/characters/{character.Id}/activate");
|
||||
ActiveCharacterId = character.Id;
|
||||
}
|
||||
catch (ApiRequestException ex)
|
||||
{
|
||||
SetStatus(ex.Message, true);
|
||||
}
|
||||
}
|
||||
private Task OnSkillCreatedAsync(Guid id) => Play.OnSkillCreatedAsync(id);
|
||||
|
||||
private async Task RefreshSelectedCharacterSheetAsync()
|
||||
{
|
||||
if (!SelectedCharacterId.HasValue || SelectedCampaign is null || !IsPlayScreen)
|
||||
{
|
||||
SelectedCharacterSkills = [];
|
||||
SelectedCharacterSkillGroups = [];
|
||||
return;
|
||||
}
|
||||
private Task OnSkillUpdatedAsync(Guid id) => Play.OnSkillUpdatedAsync(id);
|
||||
|
||||
var sheet = await WorkspaceQuery.GetCharacterSheetAsync(SelectedCharacterId.Value);
|
||||
SelectedCharacterSkillGroups = sheet.SkillGroups
|
||||
.OrderBy(group => group.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
SelectedCharacterSkills = sheet.Skills
|
||||
.OrderBy(skill => skill.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
private Task OnSkillGroupCreatedAsync(Guid id) => Play.OnSkillGroupCreatedAsync(id);
|
||||
|
||||
private async Task ToggleRollDetailAsync(Guid rollId)
|
||||
{
|
||||
if (ExpandedCampaignLogRollId == rollId)
|
||||
{
|
||||
ExpandedCampaignLogRollId = null;
|
||||
if (FreshCampaignLogRollId == rollId)
|
||||
FreshCampaignLogRollId = null;
|
||||
return;
|
||||
}
|
||||
private Task OnSkillGroupUpdatedAsync(Guid id) => Play.OnSkillGroupUpdatedAsync(id);
|
||||
|
||||
ExpandedCampaignLogRollId = rollId;
|
||||
FreshCampaignLogRollId = null;
|
||||
await EnsureRollDetailLoadedAsync(rollId);
|
||||
}
|
||||
private Task OnSkillDeletedAsync(Guid id) => Play.OnSkillDeletedAsync(id);
|
||||
|
||||
private async Task OnSkillCreatedAsync(Guid _)
|
||||
{
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Skill created.", false);
|
||||
}
|
||||
private Task OnSkillGroupDeletedAsync(Guid id) => Play.OnSkillGroupDeletedAsync(id);
|
||||
|
||||
private async Task OnSkillUpdatedAsync(Guid _)
|
||||
{
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Skill updated.", false);
|
||||
}
|
||||
private Task OnCharacterPanelErrorAsync(string message) => Play.OnCharacterPanelErrorAsync(message);
|
||||
|
||||
private async Task OnSkillGroupCreatedAsync(Guid _)
|
||||
{
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Skill group created.", false);
|
||||
}
|
||||
private Task OnCampaignLogPanelErrorAsync(string message) => Play.OnCampaignLogPanelErrorAsync(message);
|
||||
|
||||
private async Task OnSkillGroupUpdatedAsync(Guid _)
|
||||
{
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Skill group updated.", false);
|
||||
}
|
||||
private Task RollSkillAsync(Guid skillId) => Play.RollSkillAsync(skillId);
|
||||
|
||||
private async Task OnSkillDeletedAsync(Guid _)
|
||||
{
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Skill deleted.", false);
|
||||
}
|
||||
|
||||
private async Task OnSkillGroupDeletedAsync(Guid _)
|
||||
{
|
||||
await RefreshSelectedCharacterSheetAsync();
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Skill group deleted.", false);
|
||||
}
|
||||
|
||||
private Task OnCharacterPanelErrorAsync(string message)
|
||||
{
|
||||
SetStatus(message, true);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task OnCampaignLogPanelErrorAsync(string message)
|
||||
{
|
||||
SetStatus(message, true);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task RollSkillAsync(Guid skillId)
|
||||
{
|
||||
if (SelectedCampaign is null)
|
||||
{
|
||||
SetStatus("No campaign selected.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
IsMutating = true;
|
||||
try
|
||||
{
|
||||
var roll = await ApiClient.RequestAsync<RollResult>("POST", $"/api/skills/{skillId}/roll", new RollSkillRequest(RollVisibility));
|
||||
await HandleRecordedRollAsync(roll);
|
||||
}
|
||||
catch (ApiRequestException ex)
|
||||
{
|
||||
SetStatus(ex.Message, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsMutating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnCustomRollCreatedAsync(RollResult roll)
|
||||
{
|
||||
IsMutating = true;
|
||||
try
|
||||
{
|
||||
await HandleRecordedRollAsync(roll);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsMutating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleRecordedRollAsync(RollResult roll)
|
||||
{
|
||||
LastRoll = roll;
|
||||
CampaignLogDetails[roll.RollId] = ToCampaignRollDetail(roll);
|
||||
CampaignLogDetailErrors.Remove(roll.RollId);
|
||||
|
||||
await RefreshCampaignLogAsync(CampaignLogCursor);
|
||||
PromoteFreshRoll(roll.RollId);
|
||||
ResetCampaignStateTracking();
|
||||
SetStatus("Roll recorded.", false);
|
||||
Announce("Roll result updated.");
|
||||
}
|
||||
private Task OnCustomRollCreatedAsync(RollResult roll) => Play.OnCustomRollCreatedAsync(roll);
|
||||
|
||||
private Task OnRollVisibilityChanged(string visibility) => Session.OnRollVisibilityChangedAsync(visibility);
|
||||
|
||||
private bool CanEditSkill(CharacterSheetSkill skill)
|
||||
{
|
||||
if (SelectedCharacter is null)
|
||||
return false;
|
||||
|
||||
return CanEditCharacter(SelectedCharacter);
|
||||
}
|
||||
private bool CanEditSkill(CharacterSheetSkill skill) => Play.CanEditSkill(skill);
|
||||
|
||||
[JSInvokable]
|
||||
public async Task OnStateEventReceived(CampaignStateSnapshot state)
|
||||
@@ -571,85 +374,13 @@ public partial class Workspace : IAsyncDisposable
|
||||
return $"{skill.DiceRollDefinition}, wild {skill.WildDice}, {fumbleLabel}";
|
||||
}
|
||||
|
||||
private CampaignRollDetail? ResolveRollDetail(Guid rollId)
|
||||
{
|
||||
return CampaignLogDetails.GetValueOrDefault(rollId);
|
||||
}
|
||||
private CampaignRollDetail? ResolveRollDetail(Guid rollId) => Play.ResolveRollDetail(rollId);
|
||||
|
||||
private bool IsRollDetailLoading(Guid rollId)
|
||||
{
|
||||
return CampaignLogDetailsLoading.Contains(rollId);
|
||||
}
|
||||
private bool IsRollDetailLoading(Guid rollId) => Play.IsRollDetailLoading(rollId);
|
||||
|
||||
private string? GetRollDetailError(Guid rollId)
|
||||
{
|
||||
return CampaignLogDetailErrors.GetValueOrDefault(rollId);
|
||||
}
|
||||
private string? GetRollDetailError(Guid rollId) => Play.GetRollDetailError(rollId);
|
||||
|
||||
private void ResetCampaignLogDetailState()
|
||||
{
|
||||
ExpandedCampaignLogRollId = null;
|
||||
FreshCampaignLogRollId = null;
|
||||
CampaignLogDetails.Clear();
|
||||
CampaignLogDetailsLoading.Clear();
|
||||
CampaignLogDetailErrors.Clear();
|
||||
}
|
||||
|
||||
private void TrimCampaignLogDetails()
|
||||
{
|
||||
var visibleRollIds = CampaignLog.Select(entry => entry.RollId).ToHashSet();
|
||||
|
||||
foreach (var rollId in CampaignLogDetails.Keys.Where(rollId => !visibleRollIds.Contains(rollId)).ToArray())
|
||||
CampaignLogDetails.Remove(rollId);
|
||||
|
||||
foreach (var rollId in CampaignLogDetailsLoading.Where(rollId => !visibleRollIds.Contains(rollId)).ToArray())
|
||||
CampaignLogDetailsLoading.Remove(rollId);
|
||||
|
||||
foreach (var rollId in CampaignLogDetailErrors.Keys.Where(rollId => !visibleRollIds.Contains(rollId)).ToArray())
|
||||
CampaignLogDetailErrors.Remove(rollId);
|
||||
|
||||
if (ExpandedCampaignLogRollId.HasValue && !visibleRollIds.Contains(ExpandedCampaignLogRollId.Value))
|
||||
ExpandedCampaignLogRollId = null;
|
||||
|
||||
if (FreshCampaignLogRollId.HasValue && !visibleRollIds.Contains(FreshCampaignLogRollId.Value))
|
||||
FreshCampaignLogRollId = null;
|
||||
}
|
||||
|
||||
private async Task EnsureRollDetailLoadedAsync(Guid rollId)
|
||||
{
|
||||
CampaignLogDetailErrors.Remove(rollId);
|
||||
if (CampaignLogDetails.ContainsKey(rollId) || CampaignLogDetailsLoading.Contains(rollId))
|
||||
return;
|
||||
|
||||
CampaignLogDetailsLoading.Add(rollId);
|
||||
try
|
||||
{
|
||||
CampaignLogDetails[rollId] = await WorkspaceQuery.GetRollDetailAsync(rollId);
|
||||
}
|
||||
catch (ApiRequestException ex)
|
||||
{
|
||||
CampaignLogDetailErrors[rollId] = ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
CampaignLogDetailsLoading.Remove(rollId);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private static CampaignRollDetail ToCampaignRollDetail(RollResult roll)
|
||||
{
|
||||
return new CampaignRollDetail(roll.RollId, roll.Breakdown, roll.Dice.ToArray());
|
||||
}
|
||||
|
||||
private void PromoteFreshRoll(Guid rollId)
|
||||
{
|
||||
if (!CampaignLog.Any(entry => entry.RollId == rollId))
|
||||
return;
|
||||
|
||||
ExpandedCampaignLogRollId = rollId;
|
||||
FreshCampaignLogRollId = rollId;
|
||||
}
|
||||
private void ResetCampaignLogDetailState() => Play.ResetCampaignLogDetailState();
|
||||
|
||||
private void ClearAuthenticatedState() => Session.ClearAuthenticatedState();
|
||||
|
||||
@@ -668,10 +399,7 @@ public partial class Workspace : IAsyncDisposable
|
||||
IsScreenMenuOpen = !IsScreenMenuOpen;
|
||||
}
|
||||
|
||||
private void ResetCampaignStateTracking()
|
||||
{
|
||||
CurrentCampaignState = null;
|
||||
}
|
||||
private void ResetCampaignStateTracking() => Play.ResetCampaignStateTracking();
|
||||
|
||||
private static long GetCharacterVersion(CampaignStateSnapshot snapshot, Guid? characterId)
|
||||
{
|
||||
@@ -764,6 +492,13 @@ public partial class Workspace : IAsyncDisposable
|
||||
private bool IsPlayScreen => State.IsPlayScreen;
|
||||
private bool IsManagementScreen => State.IsManagementScreen;
|
||||
private bool IsAdminScreen => State.IsAdminScreen;
|
||||
private WorkspacePlayCoordinator Play => m_Play ??= new(
|
||||
State,
|
||||
Feedback,
|
||||
ApiClient,
|
||||
WorkspaceQuery,
|
||||
CanEditCharacter,
|
||||
() => InvokeAsync(StateHasChanged));
|
||||
private WorkspaceCampaignCoordinator CampaignsFlow => m_CampaignsFlow ??= new(
|
||||
State,
|
||||
Feedback,
|
||||
@@ -826,6 +561,7 @@ public partial class Workspace : IAsyncDisposable
|
||||
private const string MobilePanelSessionKey = "play-panel";
|
||||
private const int CampaignLogWindowSize = 25;
|
||||
|
||||
private WorkspacePlayCoordinator? m_Play;
|
||||
private WorkspaceCampaignCoordinator? m_CampaignsFlow;
|
||||
private WorkspaceAdminCoordinator? m_Admin;
|
||||
private WorkspaceFeedbackService? m_Feedback;
|
||||
|
||||
Reference in New Issue
Block a user