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`: 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/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/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/**/*.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/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`
|
- `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 = [];
|
AdminUsers = [];
|
||||||
HasLoadedAdminUsers = false;
|
HasLoadedAdminUsers = false;
|
||||||
IsAdminDataLoading = false;
|
IsAdminDataLoading = false;
|
||||||
Toasts.Clear();
|
Feedback.ClearToasts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStatus(string message, bool isError)
|
private void SetStatus(string message, bool isError)
|
||||||
{
|
{
|
||||||
AddToast(message, isError);
|
Feedback.SetStatus(message, isError);
|
||||||
Announce(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Announce(string message)
|
private void Announce(string message)
|
||||||
{
|
{
|
||||||
LiveAnnouncement = message;
|
Feedback.Announce(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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleScreenMenu()
|
private void ToggleScreenMenu()
|
||||||
@@ -1220,6 +1196,7 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
private bool IsPlayScreen => State.IsPlayScreen;
|
private bool IsPlayScreen => State.IsPlayScreen;
|
||||||
private bool IsManagementScreen => State.IsManagementScreen;
|
private bool IsManagementScreen => State.IsManagementScreen;
|
||||||
private bool IsAdminScreen => State.IsAdminScreen;
|
private bool IsAdminScreen => State.IsAdminScreen;
|
||||||
|
private WorkspaceFeedbackService Feedback => m_Feedback ??= new(State, () => InvokeAsync(StateHasChanged));
|
||||||
private IReadOnlyList<AppHeaderMenuItem> HeaderMenuItems
|
private IReadOnlyList<AppHeaderMenuItem> HeaderMenuItems
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -1250,5 +1227,6 @@ public partial class Workspace : IAsyncDisposable
|
|||||||
private const string MobilePanelSessionKey = "play-panel";
|
private const string MobilePanelSessionKey = "play-panel";
|
||||||
private const string RollVisibilitySessionKey = "roll-visibility";
|
private const string RollVisibilitySessionKey = "roll-visibility";
|
||||||
private const int CampaignLogWindowSize = 25;
|
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