wired-tools: hoist inspection selection (+ live state + action version) to the store

Five more useStates leave WiredCreatorToolsView: selectedFurni,
selectedFurniLiveState, selectedUser, selectedUserLiveState, and the
monotonic selectedUserActionVersion counter. All five now live in
useWiredCreatorToolsUiStore; the room-event listeners
(useObjectSelectedEvent, the per-kind useMessageEvent + useNitroEvent
handlers, the per-action effects that bump the version counter) stay
in the component because they need React's subscription lifecycle —
they just call the store actions instead of setState.

Same persistence benefit as the previous monitorSnapshot pass: the
currently-inspected target survives a panel close/reopen instead of
being dropped to null on remount. Live-state setters and the action
version counter accept Updater<T> so the many `previousValue => ...`
call sites stayed verbatim.

Tests: six new cases (setSelectedFurni + null clear, functional
updater on FurniLiveState, paired setSelectedUser + LiveState,
monotonic ActionVersion via updater, close/reopen persistence). The
test fixtures use the real interface shapes — InspectionFurniSelection
includes a renderer-typed `info: AvatarInfoFurni` that is cast
through `as never` so the test doesn't have to construct the full
avatar info shape. 187/187 passing.
This commit is contained in:
simoleo89
2026-05-16 12:25:31 +02:00
parent 7758af710e
commit 8182e06be4
3 changed files with 130 additions and 8 deletions
@@ -17,7 +17,12 @@ const INITIAL = {
variableManageTypeFilter: 'ALL',
variableManageSort: 'highest_value',
variableManagePage: 1,
monitorSnapshot: createEmptyMonitorSnapshot()
monitorSnapshot: createEmptyMonitorSnapshot(),
selectedFurni: null,
selectedFurniLiveState: null,
selectedUser: null,
selectedUserLiveState: null,
selectedUserActionVersion: 0
};
describe('useWiredCreatorToolsUiStore', () =>
@@ -46,6 +51,11 @@ describe('useWiredCreatorToolsUiStore', () =>
expect(state.variableManageSort).toBe('highest_value');
expect(state.variableManagePage).toBe(1);
expect(state.monitorSnapshot).toEqual(createEmptyMonitorSnapshot());
expect(state.selectedFurni).toBeNull();
expect(state.selectedFurniLiveState).toBeNull();
expect(state.selectedUser).toBeNull();
expect(state.selectedUserLiveState).toBeNull();
expect(state.selectedUserActionVersion).toBe(0);
});
describe('setIsVisible', () =>
@@ -233,4 +243,75 @@ describe('useWiredCreatorToolsUiStore', () =>
expect(useWiredCreatorToolsUiStore.getState().monitorSnapshot.usageCurrentWindow).toBe(3);
});
});
describe('inspection selection', () =>
{
const furniSelection = {
objectId: 42,
category: 10,
info: { id: 42, name: 'sofa', description: '', image: null } as never
};
const userSelection = {
kind: 'user' as const,
roomIndex: 7,
name: 'simoleo',
figure: 'hd-180-1.lg-3023-110',
gender: 'M',
userId: 99,
level: 12,
posture: 'std'
} as never;
it('setSelectedFurni stores the picked furni selection', () =>
{
useWiredCreatorToolsUiStore.getState().setSelectedFurni(furniSelection);
expect(useWiredCreatorToolsUiStore.getState().selectedFurni).toEqual(furniSelection);
});
it('setSelectedFurni(null) clears the selection (deselect path)', () =>
{
useWiredCreatorToolsUiStore.getState().setSelectedFurni(furniSelection);
useWiredCreatorToolsUiStore.getState().setSelectedFurni(null);
expect(useWiredCreatorToolsUiStore.getState().selectedFurni).toBeNull();
});
it('setSelectedFurniLiveState accepts a functional updater', () =>
{
const initial = { positionX: 1, positionY: 2, altitude: 3, rotation: 4, state: 5 };
useWiredCreatorToolsUiStore.getState().setSelectedFurniLiveState(initial);
useWiredCreatorToolsUiStore.getState().setSelectedFurniLiveState(prev => (prev ? { ...prev, state: prev.state + 1 } : null));
expect(useWiredCreatorToolsUiStore.getState().selectedFurniLiveState).toEqual({ ...initial, state: 6 });
});
it('setSelectedUser + setSelectedUserLiveState write the user selection / live state', () =>
{
useWiredCreatorToolsUiStore.getState().setSelectedUser(userSelection);
useWiredCreatorToolsUiStore.getState().setSelectedUserLiveState({ positionX: 5, positionY: 6, altitude: 0, direction: 2 });
expect(useWiredCreatorToolsUiStore.getState().selectedUser).toEqual(userSelection);
expect(useWiredCreatorToolsUiStore.getState().selectedUserLiveState).toEqual({ positionX: 5, positionY: 6, altitude: 0, direction: 2 });
});
it('setSelectedUserActionVersion bumps the monotonic counter via functional updater', () =>
{
useWiredCreatorToolsUiStore.getState().setSelectedUserActionVersion(prev => prev + 1);
useWiredCreatorToolsUiStore.getState().setSelectedUserActionVersion(prev => prev + 1);
useWiredCreatorToolsUiStore.getState().setSelectedUserActionVersion(prev => prev + 1);
expect(useWiredCreatorToolsUiStore.getState().selectedUserActionVersion).toBe(3);
});
it('the selection persists across the panel close/reopen lifecycle', () =>
{
useWiredCreatorToolsUiStore.getState().setSelectedFurni(furniSelection);
useWiredCreatorToolsUiStore.getState().setIsVisible(false);
useWiredCreatorToolsUiStore.getState().setIsVisible(true);
expect(useWiredCreatorToolsUiStore.getState().selectedFurni).toEqual(furniSelection);
});
});
});