refactor: finish route-first shell
This commit is contained in:
45
README.md
45
README.md
@@ -6,7 +6,7 @@ RpgRoller is an ASP.NET Core and Blazor Server app for lightweight tabletop camp
|
||||
- `RpgRoller.Tests/`: xUnit coverage for API behavior, services, hosting, payload budgets, and persistence and migration paths
|
||||
- `RpgRoller.sln`: solution used by local development and repo scripts
|
||||
- `POSTMORTEM.md`: architecture analysis of the May 2026 Firefox and RoboForm failure in the authenticated workspace
|
||||
- `TASKS.md`: the current execution plan for the approved frontend routing rewrite
|
||||
- `TASKS.md`: the completed execution log for the route-first authenticated shell rewrite
|
||||
|
||||
Test layout:
|
||||
|
||||
@@ -36,14 +36,17 @@ Backend:
|
||||
|
||||
Frontend:
|
||||
|
||||
- `RpgRoller/Components/App.razor`: current HTML shell and the request-time branch that decides whether `/` serves the static auth page or the interactive app
|
||||
- `RpgRoller/Components/App.razor`: HTML shell that serves the static `/login` auth document or the interactive route set based on request path
|
||||
- `RpgRoller/Components/Routes.razor`: Blazor router and layout hookup
|
||||
- `RpgRoller/Components/Layout/MainLayout.razor`: default layout
|
||||
- `RpgRoller/Components/Pages/LoginPage.razor`: route marker for the static `/login` auth document
|
||||
- `RpgRoller/Components/Pages/PlayPage.razor`, `CampaignsPage.razor`, and `AdminPage.razor`: authenticated route entry points for the interactive workspace
|
||||
- `RpgRoller/Components/Pages/AuthenticatedPageBase.cs`: shared logout-to-`/login` redirect helper for authenticated route pages
|
||||
- `RpgRoller/Components/Pages/Workspace.razor`: authenticated workspace UI with play, campaign management, admin, toasts, and modals
|
||||
- `RpgRoller/Components/Pages/Workspace.razor.cs`: workspace composition root, lifecycle, coordinator wiring, JS-invokable entry points, and menu item construction
|
||||
- `RpgRoller/Components/Pages/Workspace.razor`: authenticated shell with shared header, health banner, toast stack, and route-owned body slot
|
||||
- `RpgRoller/Components/Pages/Workspace.razor.cs`: shell composition root, coordinator wiring, route initialization entry point, JS-invokable state-event hooks, and menu item construction
|
||||
- `RpgRoller/Components/Pages/WorkspaceRouteView.razor`: route-local first-render bootstrapper that initializes the interactive workspace after the page mounts
|
||||
- `RpgRoller/Components/Pages/PlayWorkspaceContent.razor`, `CampaignsWorkspaceContent.razor`, and `AdminWorkspaceContent.razor`: route-owned authenticated page subtrees
|
||||
- `RpgRoller/Components/Pages/CharacterManagementModals.razor`: shared create and edit character modals used by play and campaign-management routes
|
||||
- `RpgRoller/Components/Pages/WorkspaceState.cs`: workspace UI state plus pure computed and formatting projections used directly by the Razor view
|
||||
- `RpgRoller/Components/Pages/WorkspaceSessionCoordinator.cs`, `WorkspaceCampaignCoordinator.cs`, `WorkspaceCampaignScopeCoordinator.cs`, `WorkspacePlayCoordinator.cs`, `WorkspaceAdminCoordinator.cs`, `WorkspaceLiveStateController.cs`, `WorkspaceFeedbackService.cs`, and `WorkspaceToast.cs`: session bootstrap, campaign scope, play and log, admin, live update, and toast concerns used by `Workspace`
|
||||
- `RpgRoller/Components/Pages/HomeControls/StaticAuthPage.razor`: plain HTML login and registration page used at `/login`
|
||||
@@ -55,8 +58,8 @@ Frontend:
|
||||
|
||||
Current repo note:
|
||||
|
||||
- `POSTMORTEM.md` documents why the current authenticated workspace architecture is fragile and why the next major frontend change is a route-first rewrite of the authenticated shell.
|
||||
- `TASKS.md` is the authoritative execution plan for that rewrite and must be kept current while the work proceeds.
|
||||
- `POSTMORTEM.md` documents why the previous authenticated workspace architecture was fragile under Firefox plus RoboForm.
|
||||
- `TASKS.md` records the route-first rewrite that addressed that architecture.
|
||||
|
||||
## Runtime and Persistence
|
||||
|
||||
@@ -95,26 +98,29 @@ Rolemaster support:
|
||||
|
||||
## Current Frontend Architecture
|
||||
|
||||
The current frontend is in an intermediate state that was created while mitigating the Firefox and RoboForm failure documented in `POSTMORTEM.md`.
|
||||
The frontend now uses a route-first authenticated shell that keeps the anonymous auth document outside the interactive Blazor subtree.
|
||||
|
||||
Today, `/` is an auth-aware entry redirect:
|
||||
`/` is an auth-aware entry redirect:
|
||||
|
||||
- anonymous `GET /` redirects to `/login`
|
||||
- authenticated `GET /` redirects to `/play`
|
||||
- `RpgRoller/Components/App.razor` still decides between the static `/login` document and the interactive route set based on the request path, not auth state
|
||||
- `RpgRoller/Components/App.razor` serves the static `/login` document or the interactive route set based on the request path, not auth state
|
||||
|
||||
Inside the authenticated app, `/play`, `/campaigns`, and `/admin` are now real Blazor routes, and the hamburger menu navigates between those URLs. The interactive shell is still structurally transitional, because `Workspace.razor` continues to own all three major authenticated subtrees behind one component.
|
||||
Inside the authenticated app, `/play`, `/campaigns`, and `/admin` are real Blazor routes, and the hamburger menu navigates between those URLs. `Workspace.razor` is now a shared shell only. Each authenticated route owns its own main content subtree through a route-specific component.
|
||||
|
||||
This architecture works functionally but remains structurally fragile because:
|
||||
Interactive bootstrap is now route-local:
|
||||
|
||||
- the HTML shell still branches on request path to keep `/login` static
|
||||
- the authenticated workspace still performs staged startup in `OnAfterRenderAsync`
|
||||
- the app coordinates state across Blazor component state, browser `sessionStorage`, `fetch`, and SSE during early startup
|
||||
- the shared `Workspace` component still conditionally renders play, campaign management, and admin DOM instead of letting each route own its own subtree
|
||||
- `WorkspaceRouteView.razor` performs the first-render JS-dependent session initialization for the authenticated route that mounted
|
||||
- `Workspace.razor.cs` no longer uses `OnAfterRenderAsync` as the shell bootstrap orchestrator
|
||||
- play-specific post-render behavior is limited to page-local controls such as log auto-scroll and modal autofocus inside child components
|
||||
|
||||
## Approved Rewrite Direction
|
||||
Remaining architectural constraints are deliberate:
|
||||
|
||||
The approved remediation direction is a route-first authenticated shell:
|
||||
- `/login` stays plain HTML plus JavaScript so the anonymous auth path avoids Blazor form ownership entirely
|
||||
- authenticated reads and writes still depend on JS interop-backed `fetch`, so first interactive initialization must still happen after mount
|
||||
- live updates still use SSE and route-aware synchronization, with `/play` as the only route that keeps the play log and selected character sheet live
|
||||
|
||||
## Route-First Authenticated Shell
|
||||
|
||||
- `/` becomes an auth-aware entry point that redirects to `/login` or `/play`
|
||||
- `/login` hosts the anonymous auth experience
|
||||
@@ -123,7 +129,7 @@ The approved remediation direction is a route-first authenticated shell:
|
||||
- SSE and heavy play bootstrap stay scoped to `/play`
|
||||
- the large `Workspace` component is split so each route owns a smaller, more stable subtree
|
||||
|
||||
This rewrite is not complete yet. Follow `TASKS.md` for the execution plan.
|
||||
This rewrite is complete. See `TASKS.md` for the execution history and milestone notes.
|
||||
|
||||
## Local Development
|
||||
|
||||
@@ -184,9 +190,10 @@ SQLite migration rule:
|
||||
|
||||
## Frontend Runtime
|
||||
|
||||
- The UI currently runs as Blazor Server with interactive components for the authenticated workspace and a plain HTML plus JavaScript auth page for anonymous users at `/`.
|
||||
- The UI runs as Blazor Server for authenticated routes and as plain HTML plus JavaScript for the anonymous `/login` document.
|
||||
- Static assets are linked through Blazor's `@Assets[...]` pipeline for fingerprinted cache-busting URLs.
|
||||
- Workspace reads are resolved through API requests in `WorkspaceQueryService`; browser interop stays focused on auth forms, session storage, SSE wiring, and small DOM helpers.
|
||||
- Interactive authenticated startup begins in `WorkspaceRouteView.razor` after first render because `RpgRollerApiClient` still depends on JS interop-backed `fetch`.
|
||||
- Live workspace refresh compares separate roster, per-character sheet, and log versions so unrelated changes do not trigger full reloads.
|
||||
- Campaign log data is loaded in bounded slices: campaign summaries, one selected roster, one selected character sheet, and a 25-row incremental log window from `/api/campaigns/{campaignId}/log/page`.
|
||||
- Log rows return compact summary data first and lazy-load full detail from `/api/rolls/{rollId}` when expanded.
|
||||
|
||||
Reference in New Issue
Block a user