5180 fichiers 38.5 Mo 0 téléchargements 202 vues mis à jour il y a 5 heures ui
nitro-v36-with-renderer-v36 / src / hooks / wired-tools / useWiredToolsStore.ts
useWiredToolsStore.ts 31.1 Ko Télécharger le fichier
import {
    CreateLinkEvent,
    GetSessionDataManager,
    RoomRightsEvent,
    WiredMenuPermissionsSaveComposer,
    WiredRoomSettingsDataEvent,
    WiredRoomSettingsRequestComposer,
    WiredRoomStateActionComposer,
    WiredUserVariableManageComposer,
    WiredUserVariablesDataEvent,
    WiredUserVariablesRequestComposer,
    UserSettingsEvent,
    WiredMenuSettingsComposer,
    WiredUserVariableUpdateComposer
} from '@octane/renderer';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { registerSharedHook } from '@/state/useSharedHook';
import {
    createPacketCooldownGate,
    decodeWiredMenuPreferences,
    LocalizeText,
    localizeWithFallback,
    NotificationAlertType,
    NotificationBubbleType,
    normalizeWiredStyle,
    SendMessageComposer,
    WIRED_STYLE_DEFAULT,
    WiredStyleName,
    wiredPlayTestNotificationKey
} from '../../api';
import { useMessageEvent } from '../events';
import { useWiredPlayTest } from './useWiredPlayTest';
import { useNotification } from '../notification';
import { useRoom } from '../rooms';

export interface IWiredAccountPreferences {
    /**
     * Official playtest mode: kept on the server with the other wired menu preferences
     * (WiredMenuSettingsComposer playTestMode).
     */
    playTestMode: boolean;
    showInspectButton: boolean;
    showSystemNotifications: boolean;
    showToolbarButton: boolean;
    /** The look of the wired box windows: "" (default, illumina) or one of WIRED_STYLE_OPTIONS. */
    wiredStyle: WiredStyleName;
}

export interface IWiredRoomSettings {
    canInspect: boolean;
    canManageSettings: boolean;
    canModify: boolean;
    inspectMask: number;
    isLoaded: boolean;
    modifyMask: number;
    roomId: number;
    /** The room's wired timezone as the server keeps it; empty when the hotel's own applies. */
    timezone: string;
}

export interface IWiredUserVariableDefinition {
    availability: number;
    hasValue: boolean;
    isReadOnly?: boolean;
    isTextConnected: boolean;
    itemId: number;
    name: string;
    /** The value-to-text table of a text connected definition, when the server sent one. */
    textConnector?: Array<{ key: number; value: string }>;
    /** AIR WiredVariable.requiresExecutionContext: a pointer, which only exists while a stack runs. */
    requiresExecutionContext?: boolean;
}

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;
    /** The value-to-text table of a text connected definition, when the server sent one. */
    textConnector?: Array<{ key: number; value: string }>;
    /** AIR WiredVariable.requiresExecutionContext: a pointer, which only exists while a stack runs. */
    requiresExecutionContext?: boolean;
}

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;
    /** The value-to-text table of a text connected definition, when the server sent one. */
    textConnector?: Array<{ key: number; value: string }>;
    /** AIR WiredVariable.requiresExecutionContext: a pointer, which only exists while a stack runs. */
    requiresExecutionContext?: boolean;
}

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;
    /** The value-to-text table of a text connected definition, when the server sent one. */
    textConnector?: Array<{ key: number; value: string }>;
    /** AIR WiredVariable.requiresExecutionContext: a pointer, which only exists while a stack runs. */
    requiresExecutionContext?: boolean;
}

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_VARIABLE_MANAGE_ACTION_CLEAR_ALL = 2;

const WIRED_TOOLS_STORAGE_PREFIX = 'nitro.wired.tools.preferences';
const getCurrentUnixTime = () => Math.floor(Date.now() / 1000);
const DEFAULT_ACCOUNT_PREFERENCES: IWiredAccountPreferences = {
    playTestMode: false,
    showToolbarButton: false,
    showInspectButton: false,
    showSystemNotifications: false,
    wiredStyle: WIRED_STYLE_DEFAULT
};

const DEFAULT_ROOM_SETTINGS: IWiredRoomSettings = {
    roomId: 0,
    inspectMask: 0,
    modifyMask: 0,
    canInspect: false,
    canModify: false,
    canManageSettings: false,
    isLoaded: false,
    timezone: ''
};

/**
 * 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 by the Zustand bridge at the public-hook layer so every consumer in
 * the tree sees the same instance (matches the previous
 * shared-store wiring).
 */
export const useWiredToolsStore = () => {
    const { roomSession = null } = useRoom();
    const { simpleAlert = null, showSingleBubble = null } = useNotification();
    const [accountPreferences, setAccountPreferences] = useState<IWiredAccountPreferences>(DEFAULT_ACCOUNT_PREFERENCES);
    const accountPreferencesRef = useRef<IWiredAccountPreferences>(DEFAULT_ACCOUNT_PREFERENCES);
    // Once the server sent stored preferences, the local copy no longer overrides them.
    const serverPreferencesAppliedRef = useRef(false);

    useEffect(() => {
        accountPreferencesRef.current = accountPreferences;
    }, [accountPreferences]);
    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 userVariablesRequestGateRef = useRef<ReturnType<typeof createPacketCooldownGate> | null>(null);

    if (!userVariablesRequestGateRef.current) {
        userVariablesRequestGateRef.current = createPacketCooldownGate();
    }

    const requestUserVariables = useCallback(() => {
        if (!roomSettings.canInspect) return;

        userVariablesRequestGateRef.current?.request(() => {
            SendMessageComposer(new WiredUserVariablesRequestComposer());
        });
    }, [roomSettings.canInspect]);

    const storageKey = useMemo(() => {
        const userId = GetSessionDataManager().userId;

        return `${WIRED_TOOLS_STORAGE_PREFIX}.${userId || 'guest'}`;
    }, []);

    useEffect(() => {
        if (serverPreferencesAppliedRef.current) return;

        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 || {}),
                wiredStyle: normalizeWiredStyle(parsedValue?.wiredStyle)
            });
        } 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;
        }

        requestUserVariables();
    }, [roomSession?.roomId, roomSettings.canInspect, requestUserVariables]);

    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,
            timezone: parser.timezone ?? ''
        });
    });

    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);
    });

    // The official client keeps the wired menu preferences on the server; the whisper switch rides
    // along in the same packet, so the last value the server sent is kept here.
    const wiredWhisperDisabledRef = useRef(false);

    // Official onControllerMessageEvent: while playtest mode is on, every rights update in a room
    // reminds the user, linked to the wired menu settings.
    useMessageEvent<RoomRightsEvent>(RoomRightsEvent, () => {
        if (!accountPreferencesRef.current.playTestMode) return;

        showSingleBubble?.(
            localizeWithFallback(
                'wiredmenu.settings.preferences.notification.playtest',
                'Info: you are currently in playtest mode. Disable in the Wired Menu (click)'
            ),
            NotificationBubbleType.INFO,
            null,
            'wiredmenu/open/settings'
        );
    });

    useMessageEvent<UserSettingsEvent>(UserSettingsEvent, (event) => {
        const parser = event.getParser();

        wiredWhisperDisabledRef.current = !!parser.wiredWhisperDisabled;

        const stored = decodeWiredMenuPreferences(parser.wiredMenuPreferences ?? '');

        if (!stored) return;

        serverPreferencesAppliedRef.current = true;
        setAccountPreferences((prevValue) => ({
            ...prevValue,
            playTestMode: stored.playTestMode,
            showToolbarButton: stored.showToolbarButton,
            showInspectButton: stored.showInspectButton,
            showSystemNotifications: stored.showSystemNotifications,
            wiredStyle: stored.wiredStyle
        }));
    });

    const sendWiredMenuSettings = useCallback((preferences: IWiredAccountPreferences) => {
        SendMessageComposer(
            new WiredMenuSettingsComposer(
                preferences.showToolbarButton,
                preferences.showInspectButton,
                preferences.playTestMode,
                wiredWhisperDisabledRef.current,
                preferences.showSystemNotifications,
                preferences.wiredStyle
            )
        );
    }, []);

    const updateAccountPreferences = useCallback(
        (partialPreferences: Partial<IWiredAccountPreferences>) => {
            const next = { ...accountPreferencesRef.current, ...partialPreferences };

            accountPreferencesRef.current = next;
            setAccountPreferences(next);
            sendWiredMenuSettings(next);
        },
        [sendWiredMenuSettings]
    );

    // Official setPlayTestMode(on, notify): the switch is saved with the other preferences and a
    // notification, linked to the wired menu settings, says which way it went.
    const setPlayTestMode = useCallback(
        (playTestMode: boolean) => {
            if (playTestMode === accountPreferencesRef.current.playTestMode) return;

            updateAccountPreferences({ playTestMode });
            showSingleBubble?.(
                localizeWithFallback(wiredPlayTestNotificationKey(playTestMode), playTestMode ? 'Playtest mode enabled' : 'Playtest mode disabled'),
                NotificationBubbleType.INFO,
                null,
                'wiredmenu/open/settings'
            );
        },
        [updateAccountPreferences, showSingleBubble]
    );

    const togglePlayTestMode = useCallback(() => setPlayTestMode(!accountPreferencesRef.current.playTestMode), [setPlayTestMode]);

    useWiredPlayTest(accountPreferences.playTestMode, togglePlayTestMode);

    /**
     * The whisper switch of the user settings travels in the same packet as the wired menu
     * preferences, so it is saved here with the stored ones and is not lost by the next save.
     */
    const updateWiredWhisperDisabled = useCallback(
        (disabled: boolean) => {
            wiredWhisperDisabledRef.current = disabled;
            sendWiredMenuSettings(accountPreferencesRef.current);
        },
        [sendWiredMenuSettings]
    );

    // The official permissions packet carries the timezone too, so every save sends all three and
    // the server answers with the settings it kept.
    const saveRoomSettings = useCallback(
        (inspectMask: number, modifyMask: number, timezone: string = roomSettings.timezone) => {
            if (!roomSettings.canManageSettings) return;

            setRoomSettings((prevValue) => ({
                ...prevValue,
                inspectMask,
                modifyMask,
                timezone
            }));

            SendMessageComposer(new WiredMenuPermissionsSaveComposer(modifyMask, inspectMask, timezone));
        },
        [roomSettings.canManageSettings, roomSettings.timezone]
    );

    const saveRoomTimezone = useCallback(
        (timezone: string) => saveRoomSettings(roomSettings.inspectMask, roomSettings.modifyMask, timezone),
        [saveRoomSettings, roomSettings.inspectMask, roomSettings.modifyMask]
    );

    /** Drops the room's cached wired stacks so its boxes are wired up again from the furniture as it stands. */
    const reloadRoomWired = useCallback(() => {
        if (!roomSettings.canModify) return;

        SendMessageComposer(new WiredRoomStateActionComposer(false));
    }, [roomSettings.canModify]);

    /** Re-reads every box from storage, discarding edits that were never saved, then rebuilds the stacks. */
    const rollbackRoomWired = useCallback(() => {
        if (!roomSettings.canModify) return;

        SendMessageComposer(new WiredRoomStateActionComposer(true));
    }, [roomSettings.canModify]);

    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]
    );

    /**
     * Takes a variable away from every holder at once, the official "clear this variable". The
     * server decides who may (the owner of the definition box); the room is told through the next
     * snapshot, but the local copy drops the holders straight away so the tab does not lag behind.
     */
    const clearVariableForAllHolders = useCallback(
        (scope: 'user' | 'furni', variableItemId: number) => {
            if (!roomSettings.canModify || !variableItemId) return;

            const strip = (prevValue: Record<number, { variableItemId: number }[]>) => {
                const nextValue: typeof prevValue = {};

                for (const [holderId, assignments] of Object.entries(prevValue)) {
                    const remaining = assignments.filter((assignment) => assignment.variableItemId !== variableItemId);

                    if (remaining.length) nextValue[Number(holderId)] = remaining;
                }

                return nextValue;
            };

            if (scope === 'furni') setFurniVariableAssignments((prevValue) => strip(prevValue) as typeof prevValue);
            else setUserVariableAssignments((prevValue) => strip(prevValue) as typeof prevValue);

            SendMessageComposer(
                new WiredUserVariableManageComposer(
                    WIRED_VARIABLE_MANAGE_ACTION_CLEAR_ALL,
                    scope === 'furni' ? WIRED_VARIABLE_TARGET_FURNI : WIRED_VARIABLE_TARGET_USER,
                    0,
                    variableItemId,
                    0
                )
            );
        },
        [roomSettings.canModify]
    );

    const showInvalidRoomAlert = useCallback(() => {
        if (!simpleAlert) return;

        simpleAlert(LocalizeText('wiredmenu.invalid_room.desc'), NotificationAlertType.ALERT, null, null, LocalizeText('wiredmenu.invalid_room.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,
        setPlayTestMode,
        togglePlayTestMode,
        updateWiredWhisperDisabled,
        saveRoomSettings,
        saveRoomTimezone,
        reloadRoomWired,
        rollbackRoomWired,
        requestUserVariables,
        assignUserVariable,
        removeUserVariable,
        updateUserVariableValue,
        assignFurniVariable,
        removeFurniVariable,
        clearVariableForAllHolders,
        updateFurniVariableValue,
        updateRoomVariableValue,
        openMonitor,
        openInspectionForFurni,
        openInspectionForUser
    };
};

registerSharedHook(useWiredToolsStore);
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser