mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 15:36:18 +00:00
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:
@@ -25,11 +25,16 @@ export const WiredCreatorToolsView: FC<{}> = () =>
|
|||||||
const setInspectionType = useWiredCreatorToolsUiStore(s => s.setInspectionType);
|
const setInspectionType = useWiredCreatorToolsUiStore(s => s.setInspectionType);
|
||||||
const variablesType = useWiredCreatorToolsUiStore(s => s.variablesType);
|
const variablesType = useWiredCreatorToolsUiStore(s => s.variablesType);
|
||||||
const [ keepSelected, setKeepSelected ] = useState(false);
|
const [ keepSelected, setKeepSelected ] = useState(false);
|
||||||
const [ selectedFurni, setSelectedFurni ] = useState<InspectionFurniSelection>(null);
|
const selectedFurni = useWiredCreatorToolsUiStore(s => s.selectedFurni);
|
||||||
const [ selectedFurniLiveState, setSelectedFurniLiveState ] = useState<InspectionFurniLiveState>(null);
|
const setSelectedFurni = useWiredCreatorToolsUiStore(s => s.setSelectedFurni);
|
||||||
const [ selectedUser, setSelectedUser ] = useState<InspectionUserSelection>(null);
|
const selectedFurniLiveState = useWiredCreatorToolsUiStore(s => s.selectedFurniLiveState);
|
||||||
const [ selectedUserLiveState, setSelectedUserLiveState ] = useState<InspectionUserLiveState>(null);
|
const setSelectedFurniLiveState = useWiredCreatorToolsUiStore(s => s.setSelectedFurniLiveState);
|
||||||
const [ selectedUserActionVersion, setSelectedUserActionVersion ] = useState(0);
|
const selectedUser = useWiredCreatorToolsUiStore(s => s.selectedUser);
|
||||||
|
const setSelectedUser = useWiredCreatorToolsUiStore(s => s.setSelectedUser);
|
||||||
|
const selectedUserLiveState = useWiredCreatorToolsUiStore(s => s.selectedUserLiveState);
|
||||||
|
const setSelectedUserLiveState = useWiredCreatorToolsUiStore(s => s.setSelectedUserLiveState);
|
||||||
|
const selectedUserActionVersion = useWiredCreatorToolsUiStore(s => s.selectedUserActionVersion);
|
||||||
|
const setSelectedUserActionVersion = useWiredCreatorToolsUiStore(s => s.setSelectedUserActionVersion);
|
||||||
const [ globalClock, setGlobalClock ] = useState(Date.now());
|
const [ globalClock, setGlobalClock ] = useState(Date.now());
|
||||||
const [ roomEnteredAt, setRoomEnteredAt ] = useState(Date.now());
|
const [ roomEnteredAt, setRoomEnteredAt ] = useState(Date.now());
|
||||||
const monitorSnapshot = useWiredCreatorToolsUiStore(s => s.monitorSnapshot);
|
const monitorSnapshot = useWiredCreatorToolsUiStore(s => s.monitorSnapshot);
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ const INITIAL = {
|
|||||||
variableManageTypeFilter: 'ALL',
|
variableManageTypeFilter: 'ALL',
|
||||||
variableManageSort: 'highest_value',
|
variableManageSort: 'highest_value',
|
||||||
variableManagePage: 1,
|
variableManagePage: 1,
|
||||||
monitorSnapshot: createEmptyMonitorSnapshot()
|
monitorSnapshot: createEmptyMonitorSnapshot(),
|
||||||
|
selectedFurni: null,
|
||||||
|
selectedFurniLiveState: null,
|
||||||
|
selectedUser: null,
|
||||||
|
selectedUserLiveState: null,
|
||||||
|
selectedUserActionVersion: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('useWiredCreatorToolsUiStore', () =>
|
describe('useWiredCreatorToolsUiStore', () =>
|
||||||
@@ -46,6 +51,11 @@ describe('useWiredCreatorToolsUiStore', () =>
|
|||||||
expect(state.variableManageSort).toBe('highest_value');
|
expect(state.variableManageSort).toBe('highest_value');
|
||||||
expect(state.variableManagePage).toBe(1);
|
expect(state.variableManagePage).toBe(1);
|
||||||
expect(state.monitorSnapshot).toEqual(createEmptyMonitorSnapshot());
|
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', () =>
|
describe('setIsVisible', () =>
|
||||||
@@ -233,4 +243,75 @@ describe('useWiredCreatorToolsUiStore', () =>
|
|||||||
expect(useWiredCreatorToolsUiStore.getState().monitorSnapshot.usageCurrentWindow).toBe(3);
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createNitroStore } from '../../state/createNitroStore';
|
import { createNitroStore } from '../../state/createNitroStore';
|
||||||
import { createEmptyMonitorSnapshot } from './WiredCreatorTools.helpers';
|
import { createEmptyMonitorSnapshot } from './WiredCreatorTools.helpers';
|
||||||
import { InspectionElementType, MonitorSnapshot, VariablesElementType, WiredToolsTab } from './WiredCreatorTools.types';
|
import { InspectionElementType, InspectionFurniLiveState, InspectionFurniSelection, InspectionUserLiveState, InspectionUserSelection, MonitorSnapshot, VariablesElementType, WiredToolsTab } from './WiredCreatorTools.types';
|
||||||
|
|
||||||
type MonitorSeverityFilter = 'ALL' | 'ERROR' | 'WARNING';
|
type MonitorSeverityFilter = 'ALL' | 'ERROR' | 'WARNING';
|
||||||
type Updater<T> = T | ((prev: T) => T);
|
type Updater<T> = T | ((prev: T) => T);
|
||||||
@@ -37,6 +37,24 @@ interface WiredCreatorToolsUiState
|
|||||||
*/
|
*/
|
||||||
monitorSnapshot: MonitorSnapshot;
|
monitorSnapshot: MonitorSnapshot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspection selection. The room-event listeners
|
||||||
|
* (`useObjectSelectedEvent` and the per-kind `useMessageEvent`
|
||||||
|
* handlers) still live in `WiredCreatorToolsView` — they need React
|
||||||
|
* lifecycle to subscribe/unsubscribe correctly — but the resulting
|
||||||
|
* state lives here so a closed/reopened panel keeps the last
|
||||||
|
* inspected target.
|
||||||
|
*
|
||||||
|
* `*ActionVersion` is a monotonic counter the user-action handlers
|
||||||
|
* bump to force the live-state recomputation effect to re-run even
|
||||||
|
* when neither `selectedUser` nor `roomIndex` changed identity.
|
||||||
|
*/
|
||||||
|
selectedFurni: InspectionFurniSelection | null;
|
||||||
|
selectedFurniLiveState: InspectionFurniLiveState | null;
|
||||||
|
selectedUser: InspectionUserSelection | null;
|
||||||
|
selectedUserLiveState: InspectionUserLiveState | null;
|
||||||
|
selectedUserActionVersion: number;
|
||||||
|
|
||||||
setIsVisible: (next: Updater<boolean>) => void;
|
setIsVisible: (next: Updater<boolean>) => void;
|
||||||
setActiveTab: (next: WiredToolsTab) => void;
|
setActiveTab: (next: WiredToolsTab) => void;
|
||||||
setInspectionType: (next: InspectionElementType) => void;
|
setInspectionType: (next: InspectionElementType) => void;
|
||||||
@@ -57,6 +75,12 @@ interface WiredCreatorToolsUiState
|
|||||||
|
|
||||||
setMonitorSnapshot: (next: MonitorSnapshot) => void;
|
setMonitorSnapshot: (next: MonitorSnapshot) => void;
|
||||||
resetMonitorSnapshot: () => void;
|
resetMonitorSnapshot: () => void;
|
||||||
|
|
||||||
|
setSelectedFurni: (next: InspectionFurniSelection | null) => void;
|
||||||
|
setSelectedFurniLiveState: (next: Updater<InspectionFurniLiveState | null>) => void;
|
||||||
|
setSelectedUser: (next: InspectionUserSelection | null) => void;
|
||||||
|
setSelectedUserLiveState: (next: Updater<InspectionUserLiveState | null>) => void;
|
||||||
|
setSelectedUserActionVersion: (next: Updater<number>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useWiredCreatorToolsUiStore = createNitroStore<WiredCreatorToolsUiState>()((set) => ({
|
export const useWiredCreatorToolsUiStore = createNitroStore<WiredCreatorToolsUiState>()((set) => ({
|
||||||
@@ -80,6 +104,12 @@ export const useWiredCreatorToolsUiStore = createNitroStore<WiredCreatorToolsUiS
|
|||||||
|
|
||||||
monitorSnapshot: createEmptyMonitorSnapshot(),
|
monitorSnapshot: createEmptyMonitorSnapshot(),
|
||||||
|
|
||||||
|
selectedFurni: null,
|
||||||
|
selectedFurniLiveState: null,
|
||||||
|
selectedUser: null,
|
||||||
|
selectedUserLiveState: null,
|
||||||
|
selectedUserActionVersion: 0,
|
||||||
|
|
||||||
setIsVisible: (next) => set(state => ({ isVisible: apply(state.isVisible, next) })),
|
setIsVisible: (next) => set(state => ({ isVisible: apply(state.isVisible, next) })),
|
||||||
setActiveTab: (next) => set({ activeTab: next }),
|
setActiveTab: (next) => set({ activeTab: next }),
|
||||||
setInspectionType: (next) => set({ inspectionType: next }),
|
setInspectionType: (next) => set({ inspectionType: next }),
|
||||||
@@ -99,5 +129,11 @@ export const useWiredCreatorToolsUiStore = createNitroStore<WiredCreatorToolsUiS
|
|||||||
setVariableManagePage: (next) => set(state => ({ variableManagePage: apply(state.variableManagePage, next) })),
|
setVariableManagePage: (next) => set(state => ({ variableManagePage: apply(state.variableManagePage, next) })),
|
||||||
|
|
||||||
setMonitorSnapshot: (next) => set({ monitorSnapshot: next }),
|
setMonitorSnapshot: (next) => set({ monitorSnapshot: next }),
|
||||||
resetMonitorSnapshot: () => set({ monitorSnapshot: createEmptyMonitorSnapshot() })
|
resetMonitorSnapshot: () => set({ monitorSnapshot: createEmptyMonitorSnapshot() }),
|
||||||
|
|
||||||
|
setSelectedFurni: (next) => set({ selectedFurni: next }),
|
||||||
|
setSelectedFurniLiveState: (next) => set(state => ({ selectedFurniLiveState: apply(state.selectedFurniLiveState, next) })),
|
||||||
|
setSelectedUser: (next) => set({ selectedUser: next }),
|
||||||
|
setSelectedUserLiveState: (next) => set(state => ({ selectedUserLiveState: apply(state.selectedUserLiveState, next) })),
|
||||||
|
setSelectedUserActionVersion: (next) => set(state => ({ selectedUserActionVersion: apply(state.selectedUserActionVersion, next) }))
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user