Skip to content

Commit 72332ea

Browse files
committed
Fix account controls aside stacking and user-scoped profile isolation - PR_26163_053-account-controls-user-scope-and-aside-stack
1 parent cad7819 commit 72332ea

9 files changed

Lines changed: 802 additions & 640 deletions

File tree

account/user-controls-page.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createControlsToolApiRepository } from "../toolbox/controls/controls-api-client.js";
2+
import { getSessionCurrent } from "../src/engine/api/session-api-client.js";
23
import InputService from "../src/engine/input/InputService.js";
34
import InputCaptureService from "../src/engine/input/InputCaptureService.js";
45
import { gamepadProfileInputNames } from "../src/engine/input/GamepadInputClassifier.js";
@@ -139,6 +140,7 @@ export class AccountUserControlsPage {
139140
this.profiles = [];
140141
this.editingProfile = null;
141142
this.selectedInputDevice = null;
143+
this.sessionUserKey = "";
142144
this.viewingDefaultFamily = "";
143145
this.devicePollingTimer = null;
144146
this.inputCapture = null;
@@ -158,6 +160,7 @@ export class AccountUserControlsPage {
158160

159161
init() {
160162
this.inputService.attach();
163+
this.sessionUserKey = this.currentSessionUserKey();
161164
this.profiles = this.readProfiles();
162165
this.selectedInputDevice = this.readSelectedInputDevice();
163166
this.renderDeviceSelect();
@@ -182,6 +185,8 @@ export class AccountUserControlsPage {
182185
list.addEventListener("input", (event) => this.handleListInput(event));
183186
list.addEventListener("dblclick", (event) => this.handleListDoubleClick(event));
184187
});
188+
window.addEventListener("gamefoundry:mock-db-session-user-changed", () => this.refreshForSessionUser());
189+
window.addEventListener("gamefoundry:mock-db-session-mode-changed", () => this.refreshForSessionUser());
185190
this.startDevicePolling();
186191
}
187192

@@ -197,6 +202,30 @@ export class AccountUserControlsPage {
197202
}
198203
}
199204

205+
currentSessionUserKey() {
206+
try {
207+
return normalizeText(getSessionCurrent()?.userKey);
208+
} catch {
209+
return "";
210+
}
211+
}
212+
213+
refreshForSessionUser() {
214+
this.clearInputCapture({ restore: true });
215+
this.repository = createControlsToolApiRepository();
216+
this.sessionUserKey = this.currentSessionUserKey();
217+
this.profiles = this.readProfiles();
218+
this.selectedInputDevice = this.readSelectedInputDevice();
219+
this.editingProfile = null;
220+
this.viewingDefaultFamily = "";
221+
this.renderDeviceSelect();
222+
this.renderProfiles();
223+
this.renderSelectedInputDevices();
224+
this.setStatus(this.sessionUserKey
225+
? "User Controls loaded for the selected user."
226+
: "Signed out. User Controls profile data cleared.");
227+
}
228+
200229
availableGamepads() {
201230
this.inputService.update();
202231
return this.inputService.getGamepads();
@@ -276,6 +305,9 @@ export class AccountUserControlsPage {
276305
}
277306

278307
readProfiles() {
308+
if (!this.currentSessionUserKey()) {
309+
return [];
310+
}
279311
let result = this.repository.listControllerProfiles();
280312
if (Array.isArray(result)) {
281313
return result.map((profile) => this.normalizeProfile(profile));
@@ -286,6 +318,9 @@ export class AccountUserControlsPage {
286318
}
287319

288320
readSelectedInputDevice() {
321+
if (!this.currentSessionUserKey()) {
322+
return null;
323+
}
289324
let result = this.repository.getSelectedInputDevice();
290325
if (result?.error) {
291326
this.repository = createControlsToolApiRepository();
@@ -295,6 +330,10 @@ export class AccountUserControlsPage {
295330
}
296331

297332
saveSelectedInputDevice(selection) {
333+
if (!this.currentSessionUserKey()) {
334+
this.setStatus("WARN: Log in to save User Controls.");
335+
return false;
336+
}
298337
let result = this.repository.saveSelectedInputDevice(selection);
299338
if (result?.error) {
300339
this.repository = createControlsToolApiRepository();
@@ -310,6 +349,10 @@ export class AccountUserControlsPage {
310349
}
311350

312351
saveProfiles(nextProfiles) {
352+
if (!this.currentSessionUserKey()) {
353+
this.setStatus("WARN: Log in to save User Controls.");
354+
return false;
355+
}
313356
const normalizedProfiles = nextProfiles.map((profile) => this.normalizeProfile(profile));
314357
let result = this.repository.replaceControllerProfiles(normalizedProfiles);
315358
if (!Array.isArray(result?.profiles) && result?.error) {

assets/theme-v2/partials/account-side-nav.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<aside class="side-menu" aria-label="Account pages" data-account-side-nav>
2-
<div class="accordion-stack accordion-stack--left-right" data-account-side-nav-accordion-layout="left-right">
3-
<details class="vertical-accordion" open data-account-side-nav-accordion="left">
2+
<div class="accordion-stack" data-account-side-nav-accordion-layout="stacked">
3+
<details class="vertical-accordion" open data-account-side-nav-accordion="pages">
44
<summary>Account Pages</summary>
55
<div class="accordion-body content-stack content-stack--compact">
66
<a data-account-side-nav-link data-route="account" href="/account/index.html">Account Home</a>
@@ -11,7 +11,7 @@
1111
<a data-account-side-nav-link data-route="account-user-controls" href="/account/user-controls.html">User Controls</a>
1212
</div>
1313
</details>
14-
<details class="vertical-accordion" data-account-side-nav-accordion="right">
14+
<details class="vertical-accordion" data-account-side-nav-accordion="guidance">
1515
<summary>Account Guidance</summary>
1616
<div class="accordion-body content-stack content-stack--compact">
1717
<p class="status">Manage profile, security, preferences, achievements, and controls from Account pages.</p>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# PR_26163_053-account-controls-user-scope-and-aside-stack
2+
3+
## Branch Validation
4+
5+
- Current branch: `main`
6+
- Expected branch: `main`
7+
- Branch validation: PASS
8+
9+
## Requirement Checklist
10+
11+
- PASS - Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` before implementation.
12+
- PASS - Created PR scope `PR_26163_053-account-controls-user-scope-and-aside-stack`.
13+
- PASS - Account side nav accordions now stack top/bottom in one aside column. Evidence: `assets/theme-v2/partials/account-side-nav.html` uses `class="accordion-stack"` and `data-account-side-nav-accordion-layout="stacked"`.
14+
- PASS - Account Pages and Account Guidance stack top/bottom. Evidence: targeted Playwright measures both accordions with matching x-position and Guidance below Pages.
15+
- PASS - User Controls profiles created by one user are not visible or editable by another user. Evidence: targeted Playwright creates User 1 and User 2 profiles, then verifies each user sees only their own profile.
16+
- PASS - Logout clears active user runtime state. Evidence: `account/user-controls-page.js` listens for session user/mode changes, clears capture state, reloads profiles and selected device from the active session, and clears profile UI when signed out.
17+
- PASS - User-control profiles are stored/read by user ownership through database adapter tables. Evidence: `input-mapping-mock-repository.js` filters `player_controller_profiles` and `selected_input_devices` by the active `playerId`.
18+
- PASS - Scope stayed limited to account side-nav layout, account/user-controls profile ownership/session behavior, targeted tests, and required reports.
19+
20+
## Changed Files
21+
22+
- `account/user-controls-page.js`
23+
- `assets/theme-v2/partials/account-side-nav.html`
24+
- `src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js`
25+
- `src/dev-runtime/server/mock-api-router.mjs`
26+
- `tests/playwright/tools/InputMappingV2Tool.spec.mjs`
27+
- `docs_build/dev/reports/playwright_v8_coverage_report.txt`
28+
- `docs_build/dev/reports/PR_26163_053-account-controls-user-scope-and-aside-stack.md`
29+
- `docs_build/dev/reports/codex_review.diff`
30+
- `docs_build/dev/reports/codex_changed_files.txt`
31+
32+
## Implementation Notes
33+
34+
- `account-side-nav.html` now uses the same single-column accordion stack pattern as the working Toolbox side menu pattern.
35+
- `account/user-controls-page.js` reads the current session user, refuses profile persistence when no user is active, clears stale UI state on logout/session changes, and reloads User Controls data for the new active user.
36+
- `input-mapping-mock-repository.js` no longer falls back to User 1 for user-owned controller profile writes. User-owned writes require an active user, while game-owned mapping/custom-action audit fields use the Forge Bot audit user when unauthenticated.
37+
- `mock-api-router.mjs` passes the active session user as a live getter so existing repository instances resolve ownership against the current session.
38+
39+
## Search Evidence
40+
41+
- PASS - `rg -n "left-right|data-account-side-nav-accordion-layout" assets/theme-v2/partials/account-side-nav.html tests/playwright/tools/InputMappingV2Tool.spec.mjs` shows the active account side nav layout as `stacked`, with no active `left-right` account-side-nav partial usage.
42+
- PASS - `rg -n "<style|style=|onclick=|onchange=|oninput=|onsubmit=" assets/theme-v2/partials/account-side-nav.html account/user-controls.html account/user-controls-page.js` returned no matches.
43+
- PASS - `rg -n "Login required to save User Controls profiles|Signed out\\. User Controls profile data cleared" src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js account/user-controls-page.js` confirms signed-out profile writes and stale signed-out UI state are handled explicitly.
44+
45+
## Impacted Lanes
46+
47+
- Account/User Controls runtime lane.
48+
- Account side-nav Theme V2 partial lane.
49+
- Dev-runtime mock DB repository/session ownership lane.
50+
- Targeted Playwright account and controls lane.
51+
- Workspace V2 validation lane.
52+
53+
## Skipped Lanes
54+
55+
- Full samples smoke: SKIP. The PR does not change samples, sample JSON contracts, game runtime behavior, or sample launch paths; the user explicitly requested not to run full samples smoke.
56+
- Engine core lane: SKIP. No `src/engine/input` or runtime engine behavior changed.
57+
- Broad visual regression lane: SKIP. The layout change is isolated to the account side-nav partial and covered by targeted Playwright assertions.
58+
59+
## Validation Performed
60+
61+
- PASS - Branch check: `git branch --show-current` returned `main`.
62+
- PASS - Syntax check: `node --check account/user-controls-page.js`.
63+
- PASS - Syntax check: `node --check src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js`.
64+
- PASS - Syntax check: `node --check src/dev-runtime/server/mock-api-router.mjs`.
65+
- PASS - Syntax check: `node --check tests/playwright/tools/InputMappingV2Tool.spec.mjs`.
66+
- PASS - Whitespace/static patch check: `git diff --check -- <changed files>`.
67+
- PASS - Targeted Playwright: `npx playwright test tests/playwright/tools/InputMappingV2Tool.spec.mjs --grep "User Controls|Account navigation"` passed 3 tests.
68+
- PASS - Workspace V2 validation: `npm run test:workspace-v2` passed 5 tests.
69+
- PASS/WARN - Playwright V8 coverage report produced at `docs_build/dev/reports/playwright_v8_coverage_report.txt`.
70+
71+
## Playwright Result
72+
73+
- PASS - Targeted Account/User Controls and Account Navigation coverage passed.
74+
- PASS - `npm run test:workspace-v2` passed.
75+
76+
## V8 Coverage
77+
78+
- PASS - Browser runtime coverage collected for `account/user-controls-page.js`: 93% function coverage reported.
79+
- WARN - `src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js` and `src/dev-runtime/server/mock-api-router.mjs` are Node/dev-runtime files and are not collected by browser V8 coverage. Their behavior is covered by the targeted Playwright/API flow and workspace validation.
80+
81+
## Manual Validation Steps
82+
83+
1. Open `/account/user-controls.html` as User 1.
84+
2. Confirm Account Pages and Account Guidance appear stacked vertically in the side nav.
85+
3. Create a User Controls profile for User 1 and save it.
86+
4. Log out and confirm the User Controls profile UI clears.
87+
5. Log in as User 2 and confirm User 1 profiles are absent.
88+
6. Create a User 2 profile, save it, and switch back to User 1.
89+
7. Confirm User 1 can see/edit only User 1 profiles and User 2 can see/edit only User 2 profiles.
90+
91+
## Samples Validation Decision
92+
93+
- SKIP - Full samples smoke was not run because this PR is scoped to account side-nav layout and User Controls ownership/session behavior, with no samples or game runtime changes.
94+
95+
## Completion
96+
97+
- PASS - Every requested item was implemented, validated, and explicitly marked PASS or documented as a safe skipped lane.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
account/user-controls.html
21
account/user-controls-page.js
3-
assets/theme-v2/css/typography.css
2+
assets/theme-v2/partials/account-side-nav.html
3+
src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js
4+
src/dev-runtime/server/mock-api-router.mjs
45
tests/playwright/tools/InputMappingV2Tool.spec.mjs
56
docs_build/dev/reports/playwright_v8_coverage_report.txt
6-
docs_build/dev/reports/PR_26163_052-user-controls-device-selection-and-capture-fix.md
7+
docs_build/dev/reports/PR_26163_053-account-controls-user-scope-and-aside-stack.md
78
docs_build/dev/reports/codex_review.diff
89
docs_build/dev/reports/codex_changed_files.txt

0 commit comments

Comments
 (0)