Extract workspace feedback service
This commit is contained in:
@@ -35,7 +35,7 @@ Frontend:
|
||||
- `RpgRoller/Components/Pages/Home.razor`: minimal gateway page (loading/auth/workspace switch)
|
||||
- `RpgRoller/Components/Pages/Home.razor.cs`: single `Home` code-behind with only gateway/session-view orchestration
|
||||
- `RpgRoller/Components/Pages/Workspace.razor`: authenticated workspace UI and workspace-specific state/logic
|
||||
- `RpgRoller/Components/Pages/WorkspaceState.cs` and `WorkspaceToast.cs`: extracted workspace UI state, toast records, and pure computed projections while `Workspace` remains the behavior owner
|
||||
- `RpgRoller/Components/Pages/WorkspaceState.cs`, `WorkspaceToast.cs`, and `WorkspaceFeedbackService.cs`: extracted workspace UI state, toast records, and feedback/status handling while `Workspace` remains the behavior owner
|
||||
- `RpgRoller/Components/**/*.razor.cs`: component code-behind classes (state, handlers, parameters, injected dependencies); `.razor` files remain markup-focused
|
||||
- `RpgRoller/Components/Pages/Home.Models.cs`: reusable `FormState<TModel>` + page form models
|
||||
- `RpgRoller/Components/Pages/HomeControls/`: auth, campaign management, play-screen, and modal controls extracted from `Home.razor`
|
||||
|
||||
@@ -1077,41 +1077,17 @@ public partial class Workspace : IAsyncDisposable
|
||||
AdminUsers = [];
|
||||
HasLoadedAdminUsers = false;
|
||||
IsAdminDataLoading = false;
|
||||
Toasts.Clear();
|
||||
Feedback.ClearToasts();
|
||||
}
|
||||
|
||||
private void SetStatus(string message, bool isError)
|
||||
{
|
||||
AddToast(message, isError);
|
||||
Announce(message);
|
||||
Feedback.SetStatus(message, isError);
|
||||
}
|
||||
|
||||
private void Announce(string message)
|
||||
{
|
||||
LiveAnnouncement = message;
|
||||
}
|
||||
|
||||
private void AddToast(string message, bool isError)
|
||||
{
|
||||
var toastId = Guid.NewGuid();
|
||||
Toasts.Add(new WorkspaceToast(toastId, message, isError));
|
||||
_ = DismissToastLaterAsync(toastId);
|
||||
}
|
||||
|
||||
private async Task DismissToastLaterAsync(Guid toastId)
|
||||
{
|
||||
await Task.Delay(ToastDurationMs);
|
||||
|
||||
if (Toasts.RemoveAll(toast => toast.Id == toastId) == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
Feedback.Announce(message);
|
||||
}
|
||||
|
||||
private void ToggleScreenMenu()
|
||||
@@ -1220,6 +1196,7 @@ public partial class Workspace : IAsyncDisposable
|
||||
private bool IsPlayScreen => State.IsPlayScreen;
|
||||
private bool IsManagementScreen => State.IsManagementScreen;
|
||||
private bool IsAdminScreen => State.IsAdminScreen;
|
||||
private WorkspaceFeedbackService Feedback => m_Feedback ??= new(State, () => InvokeAsync(StateHasChanged));
|
||||
private IReadOnlyList<AppHeaderMenuItem> HeaderMenuItems
|
||||
{
|
||||
get
|
||||
@@ -1250,5 +1227,6 @@ public partial class Workspace : IAsyncDisposable
|
||||
private const string MobilePanelSessionKey = "play-panel";
|
||||
private const string RollVisibilitySessionKey = "roll-visibility";
|
||||
private const int CampaignLogWindowSize = 25;
|
||||
private const int ToastDurationMs = 3200;
|
||||
|
||||
private WorkspaceFeedbackService? m_Feedback;
|
||||
}
|
||||
|
||||
54
RpgRoller/Components/Pages/WorkspaceFeedbackService.cs
Normal file
54
RpgRoller/Components/Pages/WorkspaceFeedbackService.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace RpgRoller.Components.Pages;
|
||||
|
||||
public sealed class WorkspaceFeedbackService
|
||||
{
|
||||
public WorkspaceFeedbackService(WorkspaceState state, Func<Task> requestRefreshAsync)
|
||||
{
|
||||
m_State = state;
|
||||
m_RequestRefreshAsync = requestRefreshAsync;
|
||||
}
|
||||
|
||||
public void SetStatus(string message, bool isError)
|
||||
{
|
||||
Announce(message);
|
||||
AddToast(message, isError);
|
||||
}
|
||||
|
||||
public void Announce(string message)
|
||||
{
|
||||
m_State.LiveAnnouncement = message;
|
||||
}
|
||||
|
||||
public void ClearToasts()
|
||||
{
|
||||
m_State.Toasts.Clear();
|
||||
}
|
||||
|
||||
private void AddToast(string message, bool isError)
|
||||
{
|
||||
var toastId = Guid.NewGuid();
|
||||
m_State.Toasts.Add(new WorkspaceToast(toastId, message, isError));
|
||||
_ = DismissToastLaterAsync(toastId);
|
||||
}
|
||||
|
||||
private async Task DismissToastLaterAsync(Guid toastId)
|
||||
{
|
||||
await Task.Delay(ToastDurationMs);
|
||||
|
||||
if (m_State.Toasts.RemoveAll(toast => toast.Id == toastId) == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await m_RequestRefreshAsync();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Func<Task> m_RequestRefreshAsync;
|
||||
private readonly WorkspaceState m_State;
|
||||
|
||||
private const int ToastDurationMs = 3200;
|
||||
}
|
||||
Reference in New Issue
Block a user