215 lines
6.6 KiB
C#
215 lines
6.6 KiB
C#
using Microsoft.JSInterop;
|
|
using RpgRoller.Contracts;
|
|
|
|
namespace RpgRoller.Components.Pages;
|
|
|
|
public sealed class WorkspaceSessionCoordinator(
|
|
WorkspaceState state,
|
|
WorkspaceFeedbackService feedback,
|
|
IJSRuntime js,
|
|
RpgRollerApiClient apiClient,
|
|
WorkspaceQueryService workspaceQuery,
|
|
Func<bool> isAdminRoute,
|
|
Func<Task> redirectToPlayAsync,
|
|
Func<Guid?, Task> reloadCampaignsAsync,
|
|
Func<Task> reloadCharacterCampaignOptionsAsync,
|
|
Func<Task> refreshCampaignScopeAsync,
|
|
Func<Task> syncStateEventsAsync,
|
|
Func<Task> stopStateEventsAsync,
|
|
Func<Task> ensureAdminUsersLoadedAsync,
|
|
Action resetCampaignLogDetailState,
|
|
Func<string?, Task> onLoggedOutAsync)
|
|
{
|
|
public async Task InitializeAsync()
|
|
{
|
|
var storedPanel = await js.InvokeAsync<string?>("rpgRollerApi.getSessionValue", MobilePanelSessionKey);
|
|
if (string.Equals(storedPanel, "log", StringComparison.OrdinalIgnoreCase))
|
|
state.MobilePanel = "log";
|
|
|
|
var storedRollVisibility =
|
|
await js.InvokeAsync<string?>("rpgRollerApi.getSessionValue", RollVisibilitySessionKey);
|
|
state.RollVisibility = NormalizeRollVisibility(storedRollVisibility);
|
|
|
|
Guid? preferredCampaignId = null;
|
|
if (!isAdminRoute())
|
|
{
|
|
var storedCampaignId = await js.InvokeAsync<string?>("rpgRollerApi.getSessionValue", CampaignSessionKey);
|
|
if (Guid.TryParse(storedCampaignId, out var parsedCampaignId))
|
|
preferredCampaignId = parsedCampaignId;
|
|
}
|
|
|
|
await CheckHealthAsync();
|
|
|
|
var reloaded = await ReloadAuthenticatedSessionAsync(preferredCampaignId);
|
|
if (!reloaded)
|
|
await onLoggedOutAsync("Session expired. Please log in again.");
|
|
}
|
|
|
|
public async Task RetryAfterHealthIssueAsync()
|
|
{
|
|
await CheckHealthAsync();
|
|
if (!state.HasHealthIssue && state.User is not null)
|
|
{
|
|
var reloaded = await ReloadAuthenticatedSessionAsync(state.SelectedCampaignId);
|
|
if (!reloaded)
|
|
await onLoggedOutAsync("Session expired. Please log in again.");
|
|
}
|
|
}
|
|
|
|
public async Task LoadKnownUsernamesAsync()
|
|
{
|
|
try
|
|
{
|
|
var usernames = await workspaceQuery.GetUsernamesAsync();
|
|
state.KnownUsernames = usernames.OrderBy(username => username, StringComparer.OrdinalIgnoreCase).ToList();
|
|
}
|
|
catch (ApiRequestException ex)
|
|
{
|
|
state.KnownUsernames = [];
|
|
feedback.SetStatus(ex.Message, true);
|
|
}
|
|
}
|
|
|
|
public async Task LogoutAsync()
|
|
{
|
|
if (state.IsMutating)
|
|
return;
|
|
|
|
state.IsMutating = true;
|
|
try
|
|
{
|
|
await apiClient.RequestWithoutPayloadAsync("POST", "/api/auth/logout");
|
|
}
|
|
catch (ApiRequestException)
|
|
{
|
|
}
|
|
finally
|
|
{
|
|
state.IsMutating = false;
|
|
}
|
|
|
|
ClearAuthenticatedState();
|
|
await stopStateEventsAsync();
|
|
await onLoggedOutAsync("Logged out.");
|
|
}
|
|
|
|
public async Task OnRollVisibilityChangedAsync(string visibility)
|
|
{
|
|
state.RollVisibility = NormalizeRollVisibility(visibility);
|
|
await js.InvokeVoidAsync("rpgRollerApi.setSessionValue", RollVisibilitySessionKey, state.RollVisibility);
|
|
}
|
|
|
|
public void ClearAuthenticatedState()
|
|
{
|
|
state.User = null;
|
|
state.ActiveCharacterId = null;
|
|
state.SelectedCampaignId = null;
|
|
state.SelectedCampaign = null;
|
|
state.Campaigns = [];
|
|
state.CharacterCampaignOptions = [];
|
|
state.SelectedCharacterSkills = [];
|
|
state.SelectedCharacterSkillGroups = [];
|
|
state.CampaignLog = [];
|
|
state.CampaignLogCursor = null;
|
|
resetCampaignLogDetailState();
|
|
state.SelectedCharacterId = null;
|
|
state.LastRoll = null;
|
|
state.KnownUsernames = [];
|
|
state.ShowCreateCharacterModal = false;
|
|
state.ShowEditCharacterModal = false;
|
|
state.CanEditCharacterOwner = false;
|
|
state.CreateCharacterInitialModel = new();
|
|
state.EditCharacterInitialModel = new();
|
|
state.CreateCharacterFormVersion = 0;
|
|
state.EditCharacterFormVersion = 0;
|
|
state.AdminUsers = [];
|
|
state.HasLoadedAdminUsers = false;
|
|
state.IsAdminDataLoading = false;
|
|
feedback.ClearToasts();
|
|
}
|
|
|
|
private async Task CheckHealthAsync()
|
|
{
|
|
state.HasHealthIssue = false;
|
|
state.HealthIssueMessage = string.Empty;
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private async Task LoadRulesetsAsync()
|
|
{
|
|
try
|
|
{
|
|
state.Rulesets = (await workspaceQuery.GetRulesetsAsync()).ToList();
|
|
}
|
|
catch (ApiRequestException ex)
|
|
{
|
|
feedback.SetStatus(ex.Message, true);
|
|
}
|
|
}
|
|
|
|
private async Task<bool> ReloadAuthenticatedSessionAsync(Guid? preferredCampaignId)
|
|
{
|
|
var me = await TryGetMeAsync();
|
|
if (me is null)
|
|
{
|
|
ClearAuthenticatedState();
|
|
await stopStateEventsAsync();
|
|
return false;
|
|
}
|
|
|
|
state.User = me.User;
|
|
state.ActiveCharacterId = me.ActiveCharacterId;
|
|
if (!await EnsureRouteAccessAsync())
|
|
return true;
|
|
|
|
if (isAdminRoute())
|
|
{
|
|
await stopStateEventsAsync();
|
|
state.ConnectionState = "offline";
|
|
await ensureAdminUsersLoadedAsync();
|
|
return true;
|
|
}
|
|
|
|
await LoadRulesetsAsync();
|
|
await reloadCampaignsAsync(preferredCampaignId ?? me.CurrentCampaignId);
|
|
await reloadCharacterCampaignOptionsAsync();
|
|
await refreshCampaignScopeAsync();
|
|
await syncStateEventsAsync();
|
|
|
|
return true;
|
|
}
|
|
|
|
private async Task<MeResponse?> TryGetMeAsync()
|
|
{
|
|
try
|
|
{
|
|
return await workspaceQuery.GetMeAsync();
|
|
}
|
|
catch (ApiRequestException ex) when (ex.StatusCode == 401)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private async Task<bool> EnsureRouteAccessAsync()
|
|
{
|
|
if (state.IsCurrentUserAdmin || !isAdminRoute())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
state.AdminUsers = [];
|
|
state.HasLoadedAdminUsers = false;
|
|
await redirectToPlayAsync();
|
|
return false;
|
|
}
|
|
|
|
private static string NormalizeRollVisibility(string? visibility)
|
|
{
|
|
return string.Equals(visibility, "private", StringComparison.OrdinalIgnoreCase) ? "private" : "public";
|
|
}
|
|
|
|
private const string CampaignSessionKey = "campaign";
|
|
private const string MobilePanelSessionKey = "play-panel";
|
|
private const string RollVisibilitySessionKey = "roll-visibility";
|
|
} |