mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 07:26:19 +00:00
Split useWiredTools into state + actions via useBetween singleton
useWiredTools backs 20 consumers with a 618-line wide state + actions surface; split it along the read/write seam so it's clear at the import site whether a view is rendering Wired data or mutating it. Because the actions need access to setters (setUserVariableAssignments, setFurniVariableAssignments, ...), this isn't the same pure-action shape as doorbell/friend-request. Used the useBetween singleton indirection instead: - useWiredToolsStore (internal) — the entire previous useWiredToolsState body, untouched. State + listeners + effects + actions in one closure. - useWiredToolsState (public, read-only) — useBetween(useWiredToolsStore) filtered to the 12 state fields (accountPreferences, roomSettings, showInspect/Toolbar booleans, variable definitions+assignments, areUserVariablesLoaded). - useWiredToolsActions (public, imperative) — same singleton filtered to the 13 actions (updateAccountPreferences, saveRoomSettings, requestUserVariables, assignXxx/removeXxx/updateXxx variable helpers, openMonitor / openInspectionForFurni / openInspectionForUser). - useWiredTools (deprecated shim) — composes both, preserves the full historical shape so the 20 existing consumers keep working. useBetween ensures all four entry points hit the same instance, so the state + dispatch loop stays a single source of truth. This is also the shape that a future migration to a Zustand slice would inherit cleanly — each public hook becomes a slice subscription.
This commit is contained in:
@@ -1 +1,4 @@
|
|||||||
export * from './useWiredTools';
|
export * from './useWiredTools';
|
||||||
|
export * from './useWiredToolsActions';
|
||||||
|
export * from './useWiredToolsState';
|
||||||
|
export * from './useWiredToolsStore';
|
||||||
|
|||||||
@@ -1,618 +1,18 @@
|
|||||||
import { CreateLinkEvent, GetSessionDataManager, WiredRoomSettingsDataEvent, WiredRoomSettingsRequestComposer, WiredRoomSettingsSaveComposer, WiredUserVariableManageComposer, WiredUserVariableUpdateComposer, WiredUserVariablesDataEvent, WiredUserVariablesRequestComposer } from '@nitrots/nitro-renderer';
|
import { useWiredToolsActions } from './useWiredToolsActions';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useWiredToolsState } from './useWiredToolsState';
|
||||||
import { useBetween } from 'use-between';
|
|
||||||
import { LocalizeText, NotificationAlertType, SendMessageComposer } from '../../api';
|
|
||||||
import { useMessageEvent } from '../events';
|
|
||||||
import { useNotification } from '../notification';
|
|
||||||
import { useRoom } from '../rooms';
|
|
||||||
|
|
||||||
export interface IWiredAccountPreferences
|
export type { IWiredAccountPreferences, IWiredContextVariableDefinition, IWiredFurniVariableAssignment, IWiredFurniVariableDefinition, IWiredRoomSettings, IWiredRoomVariableAssignment, IWiredRoomVariableDefinition, IWiredUserVariableAssignment, IWiredUserVariableDefinition } from './useWiredToolsStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Prefer `useWiredToolsState` (read-only) and
|
||||||
|
* `useWiredToolsActions` (imperative) directly. This shim composes
|
||||||
|
* both into the historical `useWiredTools()` shape so the ~20
|
||||||
|
* existing consumers keep working unchanged.
|
||||||
|
*/
|
||||||
|
export const useWiredTools = () =>
|
||||||
{
|
{
|
||||||
showInspectButton: boolean;
|
const state = useWiredToolsState();
|
||||||
showSystemNotifications: boolean;
|
const actions = useWiredToolsActions();
|
||||||
showToolbarButton: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredRoomSettings
|
return { ...state, ...actions };
|
||||||
{
|
|
||||||
canInspect: boolean;
|
|
||||||
canManageSettings: boolean;
|
|
||||||
canModify: boolean;
|
|
||||||
inspectMask: number;
|
|
||||||
isLoaded: boolean;
|
|
||||||
modifyMask: number;
|
|
||||||
roomId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredUserVariableDefinition
|
|
||||||
{
|
|
||||||
availability: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
isReadOnly?: boolean;
|
|
||||||
isTextConnected: boolean;
|
|
||||||
itemId: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredUserVariableAssignment
|
|
||||||
{
|
|
||||||
createdAt: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
updatedAt: number;
|
|
||||||
value: number | null;
|
|
||||||
variableItemId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredFurniVariableDefinition
|
|
||||||
{
|
|
||||||
availability: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
isReadOnly?: boolean;
|
|
||||||
isTextConnected: boolean;
|
|
||||||
itemId: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredFurniVariableAssignment
|
|
||||||
{
|
|
||||||
createdAt: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
updatedAt: number;
|
|
||||||
value: number | null;
|
|
||||||
variableItemId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredRoomVariableDefinition
|
|
||||||
{
|
|
||||||
availability: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
isReadOnly?: boolean;
|
|
||||||
isTextConnected: boolean;
|
|
||||||
itemId: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredRoomVariableAssignment
|
|
||||||
{
|
|
||||||
createdAt: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
updatedAt: number;
|
|
||||||
value: number | null;
|
|
||||||
variableItemId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWiredContextVariableDefinition
|
|
||||||
{
|
|
||||||
availability: number;
|
|
||||||
hasValue: boolean;
|
|
||||||
isReadOnly?: boolean;
|
|
||||||
isTextConnected: boolean;
|
|
||||||
itemId: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const WIRED_VARIABLE_TARGET_USER = 0;
|
|
||||||
const WIRED_VARIABLE_TARGET_FURNI = 1;
|
|
||||||
const WIRED_VARIABLE_TARGET_ROOM = 3;
|
|
||||||
const WIRED_VARIABLE_MANAGE_ACTION_ASSIGN = 0;
|
|
||||||
const WIRED_VARIABLE_MANAGE_ACTION_REMOVE = 1;
|
|
||||||
|
|
||||||
const WIRED_TOOLS_STORAGE_PREFIX = 'nitro.wired.tools.preferences';
|
|
||||||
const getCurrentUnixTime = () => Math.floor(Date.now() / 1000);
|
|
||||||
const DEFAULT_ACCOUNT_PREFERENCES: IWiredAccountPreferences = {
|
|
||||||
showToolbarButton: false,
|
|
||||||
showInspectButton: false,
|
|
||||||
showSystemNotifications: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_ROOM_SETTINGS: IWiredRoomSettings = {
|
|
||||||
roomId: 0,
|
|
||||||
inspectMask: 0,
|
|
||||||
modifyMask: 0,
|
|
||||||
canInspect: false,
|
|
||||||
canModify: false,
|
|
||||||
canManageSettings: false,
|
|
||||||
isLoaded: false
|
|
||||||
};
|
|
||||||
|
|
||||||
const useWiredToolsState = () =>
|
|
||||||
{
|
|
||||||
const { roomSession = null } = useRoom();
|
|
||||||
const { simpleAlert = null } = useNotification();
|
|
||||||
const [ accountPreferences, setAccountPreferences ] = useState<IWiredAccountPreferences>(DEFAULT_ACCOUNT_PREFERENCES);
|
|
||||||
const [ roomSettings, setRoomSettings ] = useState<IWiredRoomSettings>(DEFAULT_ROOM_SETTINGS);
|
|
||||||
const [ userVariableDefinitions, setUserVariableDefinitions ] = useState<IWiredUserVariableDefinition[]>([]);
|
|
||||||
const [ userVariableAssignments, setUserVariableAssignments ] = useState<Record<number, IWiredUserVariableAssignment[]>>({});
|
|
||||||
const [ furniVariableDefinitions, setFurniVariableDefinitions ] = useState<IWiredFurniVariableDefinition[]>([]);
|
|
||||||
const [ furniVariableAssignments, setFurniVariableAssignments ] = useState<Record<number, IWiredFurniVariableAssignment[]>>({});
|
|
||||||
const [ roomVariableDefinitions, setRoomVariableDefinitions ] = useState<IWiredRoomVariableDefinition[]>([]);
|
|
||||||
const [ roomVariableAssignments, setRoomVariableAssignments ] = useState<IWiredRoomVariableAssignment[]>([]);
|
|
||||||
const [ contextVariableDefinitions, setContextVariableDefinitions ] = useState<IWiredContextVariableDefinition[]>([]);
|
|
||||||
const [ areUserVariablesLoaded, setAreUserVariablesLoaded ] = useState(false);
|
|
||||||
|
|
||||||
const storageKey = useMemo(() =>
|
|
||||||
{
|
|
||||||
const userId = GetSessionDataManager().userId;
|
|
||||||
|
|
||||||
return `${ WIRED_TOOLS_STORAGE_PREFIX }.${ userId || 'guest' }`;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const rawValue = window.localStorage.getItem(storageKey);
|
|
||||||
|
|
||||||
if(!rawValue)
|
|
||||||
{
|
|
||||||
setAccountPreferences(DEFAULT_ACCOUNT_PREFERENCES);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedValue = JSON.parse(rawValue) as Partial<IWiredAccountPreferences>;
|
|
||||||
|
|
||||||
setAccountPreferences({
|
|
||||||
...DEFAULT_ACCOUNT_PREFERENCES,
|
|
||||||
...(parsedValue || {})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
setAccountPreferences(DEFAULT_ACCOUNT_PREFERENCES);
|
|
||||||
}
|
|
||||||
}, [ storageKey ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
window.localStorage.setItem(storageKey, JSON.stringify(accountPreferences));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}, [ accountPreferences, storageKey ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!roomSession?.roomId)
|
|
||||||
{
|
|
||||||
setRoomSettings(DEFAULT_ROOM_SETTINGS);
|
|
||||||
setUserVariableDefinitions([]);
|
|
||||||
setUserVariableAssignments({});
|
|
||||||
setFurniVariableDefinitions([]);
|
|
||||||
setFurniVariableAssignments({});
|
|
||||||
setRoomVariableDefinitions([]);
|
|
||||||
setRoomVariableAssignments([]);
|
|
||||||
setContextVariableDefinitions([]);
|
|
||||||
setAreUserVariablesLoaded(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRoomSettings(prevValue => ({
|
|
||||||
...DEFAULT_ROOM_SETTINGS,
|
|
||||||
roomId: roomSession.roomId,
|
|
||||||
canInspect: prevValue.roomId === roomSession.roomId ? prevValue.canInspect : false,
|
|
||||||
canModify: prevValue.roomId === roomSession.roomId ? prevValue.canModify : false,
|
|
||||||
canManageSettings: prevValue.roomId === roomSession.roomId ? prevValue.canManageSettings : false
|
|
||||||
}));
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredRoomSettingsRequestComposer());
|
|
||||||
}, [ roomSession?.roomId ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!roomSession?.roomId || !roomSettings.canInspect)
|
|
||||||
{
|
|
||||||
setUserVariableDefinitions([]);
|
|
||||||
setUserVariableAssignments({});
|
|
||||||
setFurniVariableDefinitions([]);
|
|
||||||
setFurniVariableAssignments({});
|
|
||||||
setRoomVariableDefinitions([]);
|
|
||||||
setRoomVariableAssignments([]);
|
|
||||||
setContextVariableDefinitions([]);
|
|
||||||
setAreUserVariablesLoaded(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariablesRequestComposer());
|
|
||||||
}, [ roomSession?.roomId, roomSettings.canInspect ]);
|
|
||||||
|
|
||||||
useMessageEvent<WiredRoomSettingsDataEvent>(WiredRoomSettingsDataEvent, event =>
|
|
||||||
{
|
|
||||||
const parser = event.getParser();
|
|
||||||
|
|
||||||
if(roomSession?.roomId && parser.roomId && (parser.roomId !== roomSession.roomId)) return;
|
|
||||||
|
|
||||||
setRoomSettings({
|
|
||||||
roomId: parser.roomId,
|
|
||||||
inspectMask: parser.inspectMask,
|
|
||||||
modifyMask: parser.modifyMask,
|
|
||||||
canInspect: parser.canInspect,
|
|
||||||
canModify: parser.canModify,
|
|
||||||
canManageSettings: parser.canManageSettings,
|
|
||||||
isLoaded: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
useMessageEvent<WiredUserVariablesDataEvent>(WiredUserVariablesDataEvent, event =>
|
|
||||||
{
|
|
||||||
const parser = event.getParser();
|
|
||||||
|
|
||||||
if(roomSession?.roomId && parser.roomId && (parser.roomId !== roomSession.roomId)) return;
|
|
||||||
|
|
||||||
const nextAssignments: Record<number, IWiredUserVariableAssignment[]> = {};
|
|
||||||
const nextFurniAssignments: Record<number, IWiredFurniVariableAssignment[]> = {};
|
|
||||||
|
|
||||||
for(const userEntry of (parser.users || []))
|
|
||||||
{
|
|
||||||
nextAssignments[userEntry.userId] = [ ...(userEntry.assignments || []) ];
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const furniEntry of (parser.furnis || []))
|
|
||||||
{
|
|
||||||
nextFurniAssignments[furniEntry.furniId] = [ ...(furniEntry.assignments || []) ];
|
|
||||||
}
|
|
||||||
|
|
||||||
setUserVariableDefinitions([ ...(parser.definitions || []) ]);
|
|
||||||
setUserVariableAssignments(nextAssignments);
|
|
||||||
setFurniVariableDefinitions([ ...(parser.furniDefinitions || []) ]);
|
|
||||||
setFurniVariableAssignments(nextFurniAssignments);
|
|
||||||
setRoomVariableDefinitions([ ...(parser.roomDefinitions || []) ]);
|
|
||||||
setRoomVariableAssignments([ ...(parser.roomAssignments || []) ]);
|
|
||||||
setContextVariableDefinitions([ ...(parser.contextDefinitions || []) ]);
|
|
||||||
setAreUserVariablesLoaded(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateAccountPreferences = useCallback((partialPreferences: Partial<IWiredAccountPreferences>) =>
|
|
||||||
{
|
|
||||||
setAccountPreferences(prevValue => ({
|
|
||||||
...prevValue,
|
|
||||||
...partialPreferences
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const saveRoomSettings = useCallback((inspectMask: number, modifyMask: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canManageSettings) return;
|
|
||||||
|
|
||||||
setRoomSettings(prevValue => ({
|
|
||||||
...prevValue,
|
|
||||||
inspectMask,
|
|
||||||
modifyMask
|
|
||||||
}));
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredRoomSettingsSaveComposer(inspectMask, modifyMask));
|
|
||||||
}, [ roomSettings.canManageSettings ]);
|
|
||||||
|
|
||||||
const requestUserVariables = useCallback(() =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canInspect) return;
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariablesRequestComposer());
|
|
||||||
}, [ roomSettings.canInspect ]);
|
|
||||||
|
|
||||||
const updateUserVariableValue = useCallback((userId: number, variableItemId: number, value: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
setUserVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const existingAssignments = prevValue[userId];
|
|
||||||
|
|
||||||
if(!existingAssignments?.length) return prevValue;
|
|
||||||
|
|
||||||
let didChange = false;
|
|
||||||
const nextAssignments = existingAssignments.map(assignment =>
|
|
||||||
{
|
|
||||||
if(assignment.variableItemId !== variableItemId) return assignment;
|
|
||||||
|
|
||||||
didChange = true;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...assignment,
|
|
||||||
hasValue: true,
|
|
||||||
value,
|
|
||||||
updatedAt: getCurrentUnixTime()
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!didChange) return prevValue;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prevValue,
|
|
||||||
[userId]: nextAssignments
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableUpdateComposer(WIRED_VARIABLE_TARGET_USER, userId, variableItemId, value));
|
|
||||||
}, [ roomSettings.canModify ]);
|
|
||||||
|
|
||||||
const updateFurniVariableValue = useCallback((furniId: number, variableItemId: number, value: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
setFurniVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const existingAssignments = prevValue[furniId];
|
|
||||||
|
|
||||||
if(!existingAssignments?.length) return prevValue;
|
|
||||||
|
|
||||||
let didChange = false;
|
|
||||||
const nextAssignments = existingAssignments.map(assignment =>
|
|
||||||
{
|
|
||||||
if(assignment.variableItemId !== variableItemId) return assignment;
|
|
||||||
|
|
||||||
didChange = true;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...assignment,
|
|
||||||
hasValue: true,
|
|
||||||
value,
|
|
||||||
updatedAt: getCurrentUnixTime()
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!didChange) return prevValue;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prevValue,
|
|
||||||
[furniId]: nextAssignments
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableUpdateComposer(WIRED_VARIABLE_TARGET_FURNI, furniId, variableItemId, value));
|
|
||||||
}, [ roomSettings.canModify ]);
|
|
||||||
|
|
||||||
const updateRoomVariableValue = useCallback((variableItemId: number, value: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
setRoomVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const now = getCurrentUnixTime();
|
|
||||||
let didChange = false;
|
|
||||||
const nextAssignments = prevValue.map(assignment =>
|
|
||||||
{
|
|
||||||
if(assignment.variableItemId !== variableItemId) return assignment;
|
|
||||||
|
|
||||||
didChange = true;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...assignment,
|
|
||||||
hasValue: true,
|
|
||||||
value,
|
|
||||||
updatedAt: now
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if(didChange) return nextAssignments;
|
|
||||||
|
|
||||||
return [
|
|
||||||
...prevValue,
|
|
||||||
{
|
|
||||||
variableItemId,
|
|
||||||
hasValue: true,
|
|
||||||
value,
|
|
||||||
createdAt: 0,
|
|
||||||
updatedAt: now
|
|
||||||
}
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableUpdateComposer(WIRED_VARIABLE_TARGET_ROOM, roomSettings.roomId, variableItemId, value));
|
|
||||||
}, [ roomSettings.canModify, roomSettings.roomId ]);
|
|
||||||
const assignUserVariable = useCallback((userId: number, variableItemId: number, value: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
const definition = userVariableDefinitions.find(entry => (entry.itemId === variableItemId));
|
|
||||||
|
|
||||||
if(!definition) return;
|
|
||||||
|
|
||||||
const now = getCurrentUnixTime();
|
|
||||||
const normalizedValue = (definition.hasValue ? value : null);
|
|
||||||
|
|
||||||
setUserVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const existingAssignments = [ ...(prevValue[userId] || []) ];
|
|
||||||
const existingIndex = existingAssignments.findIndex(assignment => (assignment.variableItemId === variableItemId));
|
|
||||||
|
|
||||||
if(existingIndex >= 0)
|
|
||||||
{
|
|
||||||
const existingAssignment = existingAssignments[existingIndex];
|
|
||||||
|
|
||||||
existingAssignments[existingIndex] = {
|
|
||||||
...existingAssignment,
|
|
||||||
hasValue: definition.hasValue,
|
|
||||||
value: normalizedValue,
|
|
||||||
updatedAt: now
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
existingAssignments.push({
|
|
||||||
variableItemId,
|
|
||||||
hasValue: definition.hasValue,
|
|
||||||
value: normalizedValue,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prevValue,
|
|
||||||
[userId]: existingAssignments
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_ASSIGN, WIRED_VARIABLE_TARGET_USER, userId, variableItemId, Number(normalizedValue ?? 0)));
|
|
||||||
}, [ roomSettings.canModify, userVariableDefinitions ]);
|
|
||||||
const removeUserVariable = useCallback((userId: number, variableItemId: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
setUserVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const existingAssignments = prevValue[userId];
|
|
||||||
|
|
||||||
if(!existingAssignments?.length) return prevValue;
|
|
||||||
|
|
||||||
const nextAssignments = existingAssignments.filter(assignment => (assignment.variableItemId !== variableItemId));
|
|
||||||
|
|
||||||
if(nextAssignments.length === existingAssignments.length) return prevValue;
|
|
||||||
|
|
||||||
const nextValue = { ...prevValue };
|
|
||||||
|
|
||||||
if(nextAssignments.length) nextValue[userId] = nextAssignments;
|
|
||||||
else delete nextValue[userId];
|
|
||||||
|
|
||||||
return nextValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_REMOVE, WIRED_VARIABLE_TARGET_USER, userId, variableItemId, 0));
|
|
||||||
}, [ roomSettings.canModify ]);
|
|
||||||
const assignFurniVariable = useCallback((furniId: number, variableItemId: number, value: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
const definition = furniVariableDefinitions.find(entry => (entry.itemId === variableItemId));
|
|
||||||
|
|
||||||
if(!definition) return;
|
|
||||||
|
|
||||||
const now = getCurrentUnixTime();
|
|
||||||
const normalizedValue = (definition.hasValue ? value : null);
|
|
||||||
|
|
||||||
setFurniVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const existingAssignments = [ ...(prevValue[furniId] || []) ];
|
|
||||||
const existingIndex = existingAssignments.findIndex(assignment => (assignment.variableItemId === variableItemId));
|
|
||||||
|
|
||||||
if(existingIndex >= 0)
|
|
||||||
{
|
|
||||||
const existingAssignment = existingAssignments[existingIndex];
|
|
||||||
|
|
||||||
existingAssignments[existingIndex] = {
|
|
||||||
...existingAssignment,
|
|
||||||
hasValue: definition.hasValue,
|
|
||||||
value: normalizedValue,
|
|
||||||
updatedAt: now
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
existingAssignments.push({
|
|
||||||
variableItemId,
|
|
||||||
hasValue: definition.hasValue,
|
|
||||||
value: normalizedValue,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prevValue,
|
|
||||||
[furniId]: existingAssignments
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_ASSIGN, WIRED_VARIABLE_TARGET_FURNI, furniId, variableItemId, Number(normalizedValue ?? 0)));
|
|
||||||
}, [ furniVariableDefinitions, roomSettings.canModify ]);
|
|
||||||
const removeFurniVariable = useCallback((furniId: number, variableItemId: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canModify) return;
|
|
||||||
|
|
||||||
setFurniVariableAssignments(prevValue =>
|
|
||||||
{
|
|
||||||
const existingAssignments = prevValue[furniId];
|
|
||||||
|
|
||||||
if(!existingAssignments?.length) return prevValue;
|
|
||||||
|
|
||||||
const nextAssignments = existingAssignments.filter(assignment => (assignment.variableItemId !== variableItemId));
|
|
||||||
|
|
||||||
if(nextAssignments.length === existingAssignments.length) return prevValue;
|
|
||||||
|
|
||||||
const nextValue = { ...prevValue };
|
|
||||||
|
|
||||||
if(nextAssignments.length) nextValue[furniId] = nextAssignments;
|
|
||||||
else delete nextValue[furniId];
|
|
||||||
|
|
||||||
return nextValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_REMOVE, WIRED_VARIABLE_TARGET_FURNI, furniId, variableItemId, 0));
|
|
||||||
}, [ roomSettings.canModify ]);
|
|
||||||
|
|
||||||
const showInvalidRoomAlert = useCallback(() =>
|
|
||||||
{
|
|
||||||
if(!simpleAlert) return;
|
|
||||||
|
|
||||||
simpleAlert(LocalizeText('wiredmenu.invalid_room.desc'), NotificationAlertType.ALERT, null, null, LocalizeText('generic.alert.title'));
|
|
||||||
}, [ simpleAlert ]);
|
|
||||||
|
|
||||||
const openMonitor = useCallback(() =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canInspect)
|
|
||||||
{
|
|
||||||
showInvalidRoomAlert();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateLinkEvent('wired-tools/show');
|
|
||||||
}, [ roomSettings.canInspect, showInvalidRoomAlert ]);
|
|
||||||
|
|
||||||
const openInspectionForFurni = useCallback((objectId: number, category: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canInspect)
|
|
||||||
{
|
|
||||||
showInvalidRoomAlert();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateLinkEvent(`wired-tools/inspection/furni/${ objectId }/${ category }`);
|
|
||||||
}, [ roomSettings.canInspect, showInvalidRoomAlert ]);
|
|
||||||
|
|
||||||
const openInspectionForUser = useCallback((roomIndex: number) =>
|
|
||||||
{
|
|
||||||
if(!roomSettings.canInspect)
|
|
||||||
{
|
|
||||||
showInvalidRoomAlert();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateLinkEvent(`wired-tools/inspection/user/${ roomIndex }`);
|
|
||||||
}, [ roomSettings.canInspect, showInvalidRoomAlert ]);
|
|
||||||
|
|
||||||
const showToolbarButton = !!roomSession?.roomId && roomSettings.canInspect && accountPreferences.showToolbarButton;
|
|
||||||
const showInspectButton = !!roomSession?.roomId && roomSettings.canInspect && accountPreferences.showInspectButton;
|
|
||||||
|
|
||||||
return {
|
|
||||||
accountPreferences,
|
|
||||||
roomSettings,
|
|
||||||
showInspectButton,
|
|
||||||
showToolbarButton,
|
|
||||||
userVariableDefinitions,
|
|
||||||
userVariableAssignments,
|
|
||||||
furniVariableDefinitions,
|
|
||||||
furniVariableAssignments,
|
|
||||||
roomVariableDefinitions,
|
|
||||||
roomVariableAssignments,
|
|
||||||
contextVariableDefinitions,
|
|
||||||
areUserVariablesLoaded,
|
|
||||||
updateAccountPreferences,
|
|
||||||
saveRoomSettings,
|
|
||||||
requestUserVariables,
|
|
||||||
assignUserVariable,
|
|
||||||
removeUserVariable,
|
|
||||||
updateUserVariableValue,
|
|
||||||
assignFurniVariable,
|
|
||||||
removeFurniVariable,
|
|
||||||
updateFurniVariableValue,
|
|
||||||
updateRoomVariableValue,
|
|
||||||
openMonitor,
|
|
||||||
openInspectionForFurni,
|
|
||||||
openInspectionForUser
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useWiredTools = () => useBetween(useWiredToolsState);
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { useBetween } from 'use-between';
|
||||||
|
import { useWiredToolsStore } from './useWiredToolsStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imperative slice of the Wired tools store: state-mutating actions
|
||||||
|
* (assignXxx / removeXxx / updateXxx) plus the link-event openers
|
||||||
|
* (openMonitor / openInspectionForFurni / openInspectionForUser) and
|
||||||
|
* the persistence helpers (saveRoomSettings, updateAccountPreferences,
|
||||||
|
* requestUserVariables).
|
||||||
|
*
|
||||||
|
* Stays separate from useWiredToolsState so components that only need
|
||||||
|
* to trigger Wired actions don't have to pull in the full state shape.
|
||||||
|
*/
|
||||||
|
export const useWiredToolsActions = () =>
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
updateAccountPreferences,
|
||||||
|
saveRoomSettings,
|
||||||
|
requestUserVariables,
|
||||||
|
assignUserVariable,
|
||||||
|
removeUserVariable,
|
||||||
|
updateUserVariableValue,
|
||||||
|
assignFurniVariable,
|
||||||
|
removeFurniVariable,
|
||||||
|
updateFurniVariableValue,
|
||||||
|
updateRoomVariableValue,
|
||||||
|
openMonitor,
|
||||||
|
openInspectionForFurni,
|
||||||
|
openInspectionForUser
|
||||||
|
} = useBetween(useWiredToolsStore);
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateAccountPreferences,
|
||||||
|
saveRoomSettings,
|
||||||
|
requestUserVariables,
|
||||||
|
assignUserVariable,
|
||||||
|
removeUserVariable,
|
||||||
|
updateUserVariableValue,
|
||||||
|
assignFurniVariable,
|
||||||
|
removeFurniVariable,
|
||||||
|
updateFurniVariableValue,
|
||||||
|
updateRoomVariableValue,
|
||||||
|
openMonitor,
|
||||||
|
openInspectionForFurni,
|
||||||
|
openInspectionForUser
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { useBetween } from 'use-between';
|
||||||
|
import { useWiredToolsStore } from './useWiredToolsStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-only slice of the Wired tools store: account preferences,
|
||||||
|
* room-settings flags, variable definitions / assignments, plus the
|
||||||
|
* two derived 'should show X button' booleans.
|
||||||
|
*
|
||||||
|
* Components that only need to render Wired state subscribe through
|
||||||
|
* this hook so it's easy to grep for read-only consumers vs. the
|
||||||
|
* imperative ones (which use useWiredToolsActions).
|
||||||
|
*/
|
||||||
|
export const useWiredToolsState = () =>
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
accountPreferences,
|
||||||
|
roomSettings,
|
||||||
|
showInspectButton,
|
||||||
|
showToolbarButton,
|
||||||
|
userVariableDefinitions,
|
||||||
|
userVariableAssignments,
|
||||||
|
furniVariableDefinitions,
|
||||||
|
furniVariableAssignments,
|
||||||
|
roomVariableDefinitions,
|
||||||
|
roomVariableAssignments,
|
||||||
|
contextVariableDefinitions,
|
||||||
|
areUserVariablesLoaded
|
||||||
|
} = useBetween(useWiredToolsStore);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accountPreferences,
|
||||||
|
roomSettings,
|
||||||
|
showInspectButton,
|
||||||
|
showToolbarButton,
|
||||||
|
userVariableDefinitions,
|
||||||
|
userVariableAssignments,
|
||||||
|
furniVariableDefinitions,
|
||||||
|
furniVariableAssignments,
|
||||||
|
roomVariableDefinitions,
|
||||||
|
roomVariableAssignments,
|
||||||
|
contextVariableDefinitions,
|
||||||
|
areUserVariablesLoaded
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,630 @@
|
|||||||
|
import { CreateLinkEvent, GetSessionDataManager, WiredRoomSettingsDataEvent, WiredRoomSettingsRequestComposer, WiredRoomSettingsSaveComposer, WiredUserVariableManageComposer, WiredUserVariableUpdateComposer, WiredUserVariablesDataEvent, WiredUserVariablesRequestComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { LocalizeText, NotificationAlertType, SendMessageComposer } from '../../api';
|
||||||
|
import { useMessageEvent } from '../events';
|
||||||
|
import { useNotification } from '../notification';
|
||||||
|
import { useRoom } from '../rooms';
|
||||||
|
|
||||||
|
export interface IWiredAccountPreferences
|
||||||
|
{
|
||||||
|
showInspectButton: boolean;
|
||||||
|
showSystemNotifications: boolean;
|
||||||
|
showToolbarButton: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredRoomSettings
|
||||||
|
{
|
||||||
|
canInspect: boolean;
|
||||||
|
canManageSettings: boolean;
|
||||||
|
canModify: boolean;
|
||||||
|
inspectMask: number;
|
||||||
|
isLoaded: boolean;
|
||||||
|
modifyMask: number;
|
||||||
|
roomId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredUserVariableDefinition
|
||||||
|
{
|
||||||
|
availability: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
isReadOnly?: boolean;
|
||||||
|
isTextConnected: boolean;
|
||||||
|
itemId: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredUserVariableAssignment
|
||||||
|
{
|
||||||
|
createdAt: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
updatedAt: number;
|
||||||
|
value: number | null;
|
||||||
|
variableItemId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredFurniVariableDefinition
|
||||||
|
{
|
||||||
|
availability: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
isReadOnly?: boolean;
|
||||||
|
isTextConnected: boolean;
|
||||||
|
itemId: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredFurniVariableAssignment
|
||||||
|
{
|
||||||
|
createdAt: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
updatedAt: number;
|
||||||
|
value: number | null;
|
||||||
|
variableItemId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredRoomVariableDefinition
|
||||||
|
{
|
||||||
|
availability: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
isReadOnly?: boolean;
|
||||||
|
isTextConnected: boolean;
|
||||||
|
itemId: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredRoomVariableAssignment
|
||||||
|
{
|
||||||
|
createdAt: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
updatedAt: number;
|
||||||
|
value: number | null;
|
||||||
|
variableItemId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWiredContextVariableDefinition
|
||||||
|
{
|
||||||
|
availability: number;
|
||||||
|
hasValue: boolean;
|
||||||
|
isReadOnly?: boolean;
|
||||||
|
isTextConnected: boolean;
|
||||||
|
itemId: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WIRED_VARIABLE_TARGET_USER = 0;
|
||||||
|
const WIRED_VARIABLE_TARGET_FURNI = 1;
|
||||||
|
const WIRED_VARIABLE_TARGET_ROOM = 3;
|
||||||
|
const WIRED_VARIABLE_MANAGE_ACTION_ASSIGN = 0;
|
||||||
|
const WIRED_VARIABLE_MANAGE_ACTION_REMOVE = 1;
|
||||||
|
|
||||||
|
const WIRED_TOOLS_STORAGE_PREFIX = 'nitro.wired.tools.preferences';
|
||||||
|
const getCurrentUnixTime = () => Math.floor(Date.now() / 1000);
|
||||||
|
const DEFAULT_ACCOUNT_PREFERENCES: IWiredAccountPreferences = {
|
||||||
|
showToolbarButton: false,
|
||||||
|
showInspectButton: false,
|
||||||
|
showSystemNotifications: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_ROOM_SETTINGS: IWiredRoomSettings = {
|
||||||
|
roomId: 0,
|
||||||
|
inspectMask: 0,
|
||||||
|
modifyMask: 0,
|
||||||
|
canInspect: false,
|
||||||
|
canModify: false,
|
||||||
|
canManageSettings: false,
|
||||||
|
isLoaded: false
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal singleton state-+-actions hook for the Wired tools subsystem.
|
||||||
|
* Public consumers should NOT call this directly — go through
|
||||||
|
* useWiredToolsState (read-only slice) or useWiredToolsActions
|
||||||
|
* (imperative slice). useWiredTools is the legacy shim that composes
|
||||||
|
* both into the original shape.
|
||||||
|
*
|
||||||
|
* Wrapped in useBetween at the public-hook layer so every consumer in
|
||||||
|
* the tree sees the same instance (matches the previous
|
||||||
|
* useBetween(useWiredToolsState) wiring).
|
||||||
|
*/
|
||||||
|
export const useWiredToolsStore = () =>
|
||||||
|
{
|
||||||
|
const { roomSession = null } = useRoom();
|
||||||
|
const { simpleAlert = null } = useNotification();
|
||||||
|
const [ accountPreferences, setAccountPreferences ] = useState<IWiredAccountPreferences>(DEFAULT_ACCOUNT_PREFERENCES);
|
||||||
|
const [ roomSettings, setRoomSettings ] = useState<IWiredRoomSettings>(DEFAULT_ROOM_SETTINGS);
|
||||||
|
const [ userVariableDefinitions, setUserVariableDefinitions ] = useState<IWiredUserVariableDefinition[]>([]);
|
||||||
|
const [ userVariableAssignments, setUserVariableAssignments ] = useState<Record<number, IWiredUserVariableAssignment[]>>({});
|
||||||
|
const [ furniVariableDefinitions, setFurniVariableDefinitions ] = useState<IWiredFurniVariableDefinition[]>([]);
|
||||||
|
const [ furniVariableAssignments, setFurniVariableAssignments ] = useState<Record<number, IWiredFurniVariableAssignment[]>>({});
|
||||||
|
const [ roomVariableDefinitions, setRoomVariableDefinitions ] = useState<IWiredRoomVariableDefinition[]>([]);
|
||||||
|
const [ roomVariableAssignments, setRoomVariableAssignments ] = useState<IWiredRoomVariableAssignment[]>([]);
|
||||||
|
const [ contextVariableDefinitions, setContextVariableDefinitions ] = useState<IWiredContextVariableDefinition[]>([]);
|
||||||
|
const [ areUserVariablesLoaded, setAreUserVariablesLoaded ] = useState(false);
|
||||||
|
|
||||||
|
const storageKey = useMemo(() =>
|
||||||
|
{
|
||||||
|
const userId = GetSessionDataManager().userId;
|
||||||
|
|
||||||
|
return `${ WIRED_TOOLS_STORAGE_PREFIX }.${ userId || 'guest' }`;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const rawValue = window.localStorage.getItem(storageKey);
|
||||||
|
|
||||||
|
if(!rawValue)
|
||||||
|
{
|
||||||
|
setAccountPreferences(DEFAULT_ACCOUNT_PREFERENCES);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedValue = JSON.parse(rawValue) as Partial<IWiredAccountPreferences>;
|
||||||
|
|
||||||
|
setAccountPreferences({
|
||||||
|
...DEFAULT_ACCOUNT_PREFERENCES,
|
||||||
|
...(parsedValue || {})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
setAccountPreferences(DEFAULT_ACCOUNT_PREFERENCES);
|
||||||
|
}
|
||||||
|
}, [ storageKey ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
window.localStorage.setItem(storageKey, JSON.stringify(accountPreferences));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}, [ accountPreferences, storageKey ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!roomSession?.roomId)
|
||||||
|
{
|
||||||
|
setRoomSettings(DEFAULT_ROOM_SETTINGS);
|
||||||
|
setUserVariableDefinitions([]);
|
||||||
|
setUserVariableAssignments({});
|
||||||
|
setFurniVariableDefinitions([]);
|
||||||
|
setFurniVariableAssignments({});
|
||||||
|
setRoomVariableDefinitions([]);
|
||||||
|
setRoomVariableAssignments([]);
|
||||||
|
setContextVariableDefinitions([]);
|
||||||
|
setAreUserVariablesLoaded(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRoomSettings(prevValue => ({
|
||||||
|
...DEFAULT_ROOM_SETTINGS,
|
||||||
|
roomId: roomSession.roomId,
|
||||||
|
canInspect: prevValue.roomId === roomSession.roomId ? prevValue.canInspect : false,
|
||||||
|
canModify: prevValue.roomId === roomSession.roomId ? prevValue.canModify : false,
|
||||||
|
canManageSettings: prevValue.roomId === roomSession.roomId ? prevValue.canManageSettings : false
|
||||||
|
}));
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredRoomSettingsRequestComposer());
|
||||||
|
}, [ roomSession?.roomId ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!roomSession?.roomId || !roomSettings.canInspect)
|
||||||
|
{
|
||||||
|
setUserVariableDefinitions([]);
|
||||||
|
setUserVariableAssignments({});
|
||||||
|
setFurniVariableDefinitions([]);
|
||||||
|
setFurniVariableAssignments({});
|
||||||
|
setRoomVariableDefinitions([]);
|
||||||
|
setRoomVariableAssignments([]);
|
||||||
|
setContextVariableDefinitions([]);
|
||||||
|
setAreUserVariablesLoaded(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariablesRequestComposer());
|
||||||
|
}, [ roomSession?.roomId, roomSettings.canInspect ]);
|
||||||
|
|
||||||
|
useMessageEvent<WiredRoomSettingsDataEvent>(WiredRoomSettingsDataEvent, event =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(roomSession?.roomId && parser.roomId && (parser.roomId !== roomSession.roomId)) return;
|
||||||
|
|
||||||
|
setRoomSettings({
|
||||||
|
roomId: parser.roomId,
|
||||||
|
inspectMask: parser.inspectMask,
|
||||||
|
modifyMask: parser.modifyMask,
|
||||||
|
canInspect: parser.canInspect,
|
||||||
|
canModify: parser.canModify,
|
||||||
|
canManageSettings: parser.canManageSettings,
|
||||||
|
isLoaded: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
useMessageEvent<WiredUserVariablesDataEvent>(WiredUserVariablesDataEvent, event =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(roomSession?.roomId && parser.roomId && (parser.roomId !== roomSession.roomId)) return;
|
||||||
|
|
||||||
|
const nextAssignments: Record<number, IWiredUserVariableAssignment[]> = {};
|
||||||
|
const nextFurniAssignments: Record<number, IWiredFurniVariableAssignment[]> = {};
|
||||||
|
|
||||||
|
for(const userEntry of (parser.users || []))
|
||||||
|
{
|
||||||
|
nextAssignments[userEntry.userId] = [ ...(userEntry.assignments || []) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const furniEntry of (parser.furnis || []))
|
||||||
|
{
|
||||||
|
nextFurniAssignments[furniEntry.furniId] = [ ...(furniEntry.assignments || []) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
setUserVariableDefinitions([ ...(parser.definitions || []) ]);
|
||||||
|
setUserVariableAssignments(nextAssignments);
|
||||||
|
setFurniVariableDefinitions([ ...(parser.furniDefinitions || []) ]);
|
||||||
|
setFurniVariableAssignments(nextFurniAssignments);
|
||||||
|
setRoomVariableDefinitions([ ...(parser.roomDefinitions || []) ]);
|
||||||
|
setRoomVariableAssignments([ ...(parser.roomAssignments || []) ]);
|
||||||
|
setContextVariableDefinitions([ ...(parser.contextDefinitions || []) ]);
|
||||||
|
setAreUserVariablesLoaded(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAccountPreferences = useCallback((partialPreferences: Partial<IWiredAccountPreferences>) =>
|
||||||
|
{
|
||||||
|
setAccountPreferences(prevValue => ({
|
||||||
|
...prevValue,
|
||||||
|
...partialPreferences
|
||||||
|
}));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const saveRoomSettings = useCallback((inspectMask: number, modifyMask: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canManageSettings) return;
|
||||||
|
|
||||||
|
setRoomSettings(prevValue => ({
|
||||||
|
...prevValue,
|
||||||
|
inspectMask,
|
||||||
|
modifyMask
|
||||||
|
}));
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredRoomSettingsSaveComposer(inspectMask, modifyMask));
|
||||||
|
}, [ roomSettings.canManageSettings ]);
|
||||||
|
|
||||||
|
const requestUserVariables = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canInspect) return;
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariablesRequestComposer());
|
||||||
|
}, [ roomSettings.canInspect ]);
|
||||||
|
|
||||||
|
const updateUserVariableValue = useCallback((userId: number, variableItemId: number, value: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
setUserVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const existingAssignments = prevValue[userId];
|
||||||
|
|
||||||
|
if(!existingAssignments?.length) return prevValue;
|
||||||
|
|
||||||
|
let didChange = false;
|
||||||
|
const nextAssignments = existingAssignments.map(assignment =>
|
||||||
|
{
|
||||||
|
if(assignment.variableItemId !== variableItemId) return assignment;
|
||||||
|
|
||||||
|
didChange = true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...assignment,
|
||||||
|
hasValue: true,
|
||||||
|
value,
|
||||||
|
updatedAt: getCurrentUnixTime()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!didChange) return prevValue;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevValue,
|
||||||
|
[userId]: nextAssignments
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableUpdateComposer(WIRED_VARIABLE_TARGET_USER, userId, variableItemId, value));
|
||||||
|
}, [ roomSettings.canModify ]);
|
||||||
|
|
||||||
|
const updateFurniVariableValue = useCallback((furniId: number, variableItemId: number, value: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
setFurniVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const existingAssignments = prevValue[furniId];
|
||||||
|
|
||||||
|
if(!existingAssignments?.length) return prevValue;
|
||||||
|
|
||||||
|
let didChange = false;
|
||||||
|
const nextAssignments = existingAssignments.map(assignment =>
|
||||||
|
{
|
||||||
|
if(assignment.variableItemId !== variableItemId) return assignment;
|
||||||
|
|
||||||
|
didChange = true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...assignment,
|
||||||
|
hasValue: true,
|
||||||
|
value,
|
||||||
|
updatedAt: getCurrentUnixTime()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!didChange) return prevValue;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevValue,
|
||||||
|
[furniId]: nextAssignments
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableUpdateComposer(WIRED_VARIABLE_TARGET_FURNI, furniId, variableItemId, value));
|
||||||
|
}, [ roomSettings.canModify ]);
|
||||||
|
|
||||||
|
const updateRoomVariableValue = useCallback((variableItemId: number, value: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
setRoomVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const now = getCurrentUnixTime();
|
||||||
|
let didChange = false;
|
||||||
|
const nextAssignments = prevValue.map(assignment =>
|
||||||
|
{
|
||||||
|
if(assignment.variableItemId !== variableItemId) return assignment;
|
||||||
|
|
||||||
|
didChange = true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...assignment,
|
||||||
|
hasValue: true,
|
||||||
|
value,
|
||||||
|
updatedAt: now
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if(didChange) return nextAssignments;
|
||||||
|
|
||||||
|
return [
|
||||||
|
...prevValue,
|
||||||
|
{
|
||||||
|
variableItemId,
|
||||||
|
hasValue: true,
|
||||||
|
value,
|
||||||
|
createdAt: 0,
|
||||||
|
updatedAt: now
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableUpdateComposer(WIRED_VARIABLE_TARGET_ROOM, roomSettings.roomId, variableItemId, value));
|
||||||
|
}, [ roomSettings.canModify, roomSettings.roomId ]);
|
||||||
|
|
||||||
|
const assignUserVariable = useCallback((userId: number, variableItemId: number, value: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
const definition = userVariableDefinitions.find(entry => (entry.itemId === variableItemId));
|
||||||
|
|
||||||
|
if(!definition) return;
|
||||||
|
|
||||||
|
const now = getCurrentUnixTime();
|
||||||
|
const normalizedValue = (definition.hasValue ? value : null);
|
||||||
|
|
||||||
|
setUserVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const existingAssignments = [ ...(prevValue[userId] || []) ];
|
||||||
|
const existingIndex = existingAssignments.findIndex(assignment => (assignment.variableItemId === variableItemId));
|
||||||
|
|
||||||
|
if(existingIndex >= 0)
|
||||||
|
{
|
||||||
|
const existingAssignment = existingAssignments[existingIndex];
|
||||||
|
|
||||||
|
existingAssignments[existingIndex] = {
|
||||||
|
...existingAssignment,
|
||||||
|
hasValue: definition.hasValue,
|
||||||
|
value: normalizedValue,
|
||||||
|
updatedAt: now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
existingAssignments.push({
|
||||||
|
variableItemId,
|
||||||
|
hasValue: definition.hasValue,
|
||||||
|
value: normalizedValue,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevValue,
|
||||||
|
[userId]: existingAssignments
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_ASSIGN, WIRED_VARIABLE_TARGET_USER, userId, variableItemId, Number(normalizedValue ?? 0)));
|
||||||
|
}, [ roomSettings.canModify, userVariableDefinitions ]);
|
||||||
|
|
||||||
|
const removeUserVariable = useCallback((userId: number, variableItemId: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
setUserVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const existingAssignments = prevValue[userId];
|
||||||
|
|
||||||
|
if(!existingAssignments?.length) return prevValue;
|
||||||
|
|
||||||
|
const nextAssignments = existingAssignments.filter(assignment => (assignment.variableItemId !== variableItemId));
|
||||||
|
|
||||||
|
if(nextAssignments.length === existingAssignments.length) return prevValue;
|
||||||
|
|
||||||
|
const nextValue = { ...prevValue };
|
||||||
|
|
||||||
|
if(nextAssignments.length) nextValue[userId] = nextAssignments;
|
||||||
|
else delete nextValue[userId];
|
||||||
|
|
||||||
|
return nextValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_REMOVE, WIRED_VARIABLE_TARGET_USER, userId, variableItemId, 0));
|
||||||
|
}, [ roomSettings.canModify ]);
|
||||||
|
|
||||||
|
const assignFurniVariable = useCallback((furniId: number, variableItemId: number, value: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
const definition = furniVariableDefinitions.find(entry => (entry.itemId === variableItemId));
|
||||||
|
|
||||||
|
if(!definition) return;
|
||||||
|
|
||||||
|
const now = getCurrentUnixTime();
|
||||||
|
const normalizedValue = (definition.hasValue ? value : null);
|
||||||
|
|
||||||
|
setFurniVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const existingAssignments = [ ...(prevValue[furniId] || []) ];
|
||||||
|
const existingIndex = existingAssignments.findIndex(assignment => (assignment.variableItemId === variableItemId));
|
||||||
|
|
||||||
|
if(existingIndex >= 0)
|
||||||
|
{
|
||||||
|
const existingAssignment = existingAssignments[existingIndex];
|
||||||
|
|
||||||
|
existingAssignments[existingIndex] = {
|
||||||
|
...existingAssignment,
|
||||||
|
hasValue: definition.hasValue,
|
||||||
|
value: normalizedValue,
|
||||||
|
updatedAt: now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
existingAssignments.push({
|
||||||
|
variableItemId,
|
||||||
|
hasValue: definition.hasValue,
|
||||||
|
value: normalizedValue,
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevValue,
|
||||||
|
[furniId]: existingAssignments
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_ASSIGN, WIRED_VARIABLE_TARGET_FURNI, furniId, variableItemId, Number(normalizedValue ?? 0)));
|
||||||
|
}, [ furniVariableDefinitions, roomSettings.canModify ]);
|
||||||
|
|
||||||
|
const removeFurniVariable = useCallback((furniId: number, variableItemId: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canModify) return;
|
||||||
|
|
||||||
|
setFurniVariableAssignments(prevValue =>
|
||||||
|
{
|
||||||
|
const existingAssignments = prevValue[furniId];
|
||||||
|
|
||||||
|
if(!existingAssignments?.length) return prevValue;
|
||||||
|
|
||||||
|
const nextAssignments = existingAssignments.filter(assignment => (assignment.variableItemId !== variableItemId));
|
||||||
|
|
||||||
|
if(nextAssignments.length === existingAssignments.length) return prevValue;
|
||||||
|
|
||||||
|
const nextValue = { ...prevValue };
|
||||||
|
|
||||||
|
if(nextAssignments.length) nextValue[furniId] = nextAssignments;
|
||||||
|
else delete nextValue[furniId];
|
||||||
|
|
||||||
|
return nextValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageComposer(new WiredUserVariableManageComposer(WIRED_VARIABLE_MANAGE_ACTION_REMOVE, WIRED_VARIABLE_TARGET_FURNI, furniId, variableItemId, 0));
|
||||||
|
}, [ roomSettings.canModify ]);
|
||||||
|
|
||||||
|
const showInvalidRoomAlert = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!simpleAlert) return;
|
||||||
|
|
||||||
|
simpleAlert(LocalizeText('wiredmenu.invalid_room.desc'), NotificationAlertType.ALERT, null, null, LocalizeText('generic.alert.title'));
|
||||||
|
}, [ simpleAlert ]);
|
||||||
|
|
||||||
|
const openMonitor = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canInspect)
|
||||||
|
{
|
||||||
|
showInvalidRoomAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateLinkEvent('wired-tools/show');
|
||||||
|
}, [ roomSettings.canInspect, showInvalidRoomAlert ]);
|
||||||
|
|
||||||
|
const openInspectionForFurni = useCallback((objectId: number, category: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canInspect)
|
||||||
|
{
|
||||||
|
showInvalidRoomAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateLinkEvent(`wired-tools/inspection/furni/${ objectId }/${ category }`);
|
||||||
|
}, [ roomSettings.canInspect, showInvalidRoomAlert ]);
|
||||||
|
|
||||||
|
const openInspectionForUser = useCallback((roomIndex: number) =>
|
||||||
|
{
|
||||||
|
if(!roomSettings.canInspect)
|
||||||
|
{
|
||||||
|
showInvalidRoomAlert();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateLinkEvent(`wired-tools/inspection/user/${ roomIndex }`);
|
||||||
|
}, [ roomSettings.canInspect, showInvalidRoomAlert ]);
|
||||||
|
|
||||||
|
const showToolbarButton = !!roomSession?.roomId && roomSettings.canInspect && accountPreferences.showToolbarButton;
|
||||||
|
const showInspectButton = !!roomSession?.roomId && roomSettings.canInspect && accountPreferences.showInspectButton;
|
||||||
|
|
||||||
|
return {
|
||||||
|
accountPreferences,
|
||||||
|
roomSettings,
|
||||||
|
showInspectButton,
|
||||||
|
showToolbarButton,
|
||||||
|
userVariableDefinitions,
|
||||||
|
userVariableAssignments,
|
||||||
|
furniVariableDefinitions,
|
||||||
|
furniVariableAssignments,
|
||||||
|
roomVariableDefinitions,
|
||||||
|
roomVariableAssignments,
|
||||||
|
contextVariableDefinitions,
|
||||||
|
areUserVariablesLoaded,
|
||||||
|
updateAccountPreferences,
|
||||||
|
saveRoomSettings,
|
||||||
|
requestUserVariables,
|
||||||
|
assignUserVariable,
|
||||||
|
removeUserVariable,
|
||||||
|
updateUserVariableValue,
|
||||||
|
assignFurniVariable,
|
||||||
|
removeFurniVariable,
|
||||||
|
updateFurniVariableValue,
|
||||||
|
updateRoomVariableValue,
|
||||||
|
openMonitor,
|
||||||
|
openInspectionForFurni,
|
||||||
|
openInspectionForUser
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user