From 05ff7df7d2797836cb92694c73f8273b4b9c813b Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Mon, 18 May 2026 21:48:17 +0200 Subject: [PATCH] refactor(useChatWidget,useAvatarInfoWidget): reactive ownUserId + typed avatar-click-control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small modernization wins on the previously skip-motivated god-hooks. Neither hook lends itself to the data/actions split, but both had concrete imperative-style residue worth tidying: == useChatWidget Replace `const ownUserId = GetSessionDataManager()?.userId || -1;` with `useUserDataSnapshot().userId`. The previous read happened at hook mount and stayed pinned to whatever userId the manager held at that point — a session change (re-login without page reload) would silently corrupt the outgoing-translation owner check below. With the snapshot hook, the value updates reactively via SESSION_DATA_UPDATED and the useNitroEvent re-registration picks up the fresh ownUserId for every incoming chat event. == useAvatarInfoWidget Two tidy points: - CLICK_USER_DEBOUNCE_MS (the 120ms window during which a directional click suppresses the context menu) lifted from inside the hook body to a module-level const. It's never going to change at runtime and doesn't depend on hook state — keeping it inside meant it was redeclared on every render. - The `(globalThis as any).__nitroAvatarClickControl` read replaced by a typed `getAvatarClickControl()` helper backed by a proper `NitroAvatarClickControl` interface. Same runtime behaviour; type channel no longer goes through `any`, and the symbol is documented in one place above the hook. Public APIs of both hooks unchanged. Suite: 207/207. --- src/hooks/rooms/widgets/useAvatarInfoWidget.ts | 16 ++++++++++++++-- src/hooks/rooms/widgets/useChatWidget.ts | 9 +++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/hooks/rooms/widgets/useAvatarInfoWidget.ts b/src/hooks/rooms/widgets/useAvatarInfoWidget.ts index 0c5196b..189f34a 100644 --- a/src/hooks/rooms/widgets/useAvatarInfoWidget.ts +++ b/src/hooks/rooms/widgets/useAvatarInfoWidget.ts @@ -8,9 +8,21 @@ import { useObjectDeselectedEvent, useObjectRollOutEvent, useObjectRollOverEvent import { useRoom } from '../useRoom'; import { applyFavouriteGroupUpdate, applyUserBadgesUpdate, applyUserFigureUpdate } from './avatarInfo.reducers'; +// Time window after a directional / move-state user click during +// which the context menu must NOT open. Set on `globalThis.__nitroAvatarClickControl` +// by the click-routing code in the room engine. +const CLICK_USER_DEBOUNCE_MS = 120; + +interface NitroAvatarClickControl +{ + suppressMenuUntil: number; +} + +const getAvatarClickControl = (): NitroAvatarClickControl | null => + (globalThis as unknown as { __nitroAvatarClickControl?: NitroAvatarClickControl }).__nitroAvatarClickControl ?? null; + const useAvatarInfoWidgetState = () => { - const CLICK_USER_DEBOUNCE_MS = 120; const [ avatarInfo, setAvatarInfo ] = useState(null); const [ activeNameBubble, setActiveNameBubble ] = useState(null); const [ nameBubbles, setNameBubbles ] = useState([]); @@ -33,7 +45,7 @@ const useAvatarInfoWidgetState = () => const isAvatarMenuBlocked = () => { - const control = (globalThis as any).__nitroAvatarClickControl; + const control = getAvatarClickControl(); return !!control && (control.suppressMenuUntil > Date.now()); }; diff --git a/src/hooks/rooms/widgets/useChatWidget.ts b/src/hooks/rooms/widgets/useChatWidget.ts index f278659..f81ffd2 100644 --- a/src/hooks/rooms/widgets/useChatWidget.ts +++ b/src/hooks/rooms/widgets/useChatWidget.ts @@ -1,7 +1,8 @@ -import { GetGuestRoomResultEvent, GetRoomEngine, GetSessionDataManager, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum } from '@nitrots/nitro-renderer'; +import { GetGuestRoomResultEvent, GetRoomEngine, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum } from '@nitrots/nitro-renderer'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ChatBubbleMessage, ChatBubbleUtilities, ChatEntryType, ChatHistoryCurrentDate, GetConfigurationValue, GetRoomObjectScreenLocation, IRoomChatSettings, LocalizeText, PlaySound, RoomChatFormatter } from '../../../api'; import { useMessageEvent, useNitroEvent } from '../../events'; +import { useUserDataSnapshot } from '../../session/useSessionSnapshots'; import { useTranslation } from '../../translation'; import { useRoom } from '../useRoom'; import { useChatHistory } from './../../chat-history'; @@ -22,7 +23,11 @@ const useChatWidgetState = () => const { addChatEntry, updateChatEntry } = useChatHistory(); const { settings, translateIncoming, consumeOutgoingTranslation } = useTranslation(); const isDisposed = useRef(false); - const ownUserId = (GetSessionDataManager()?.userId || -1); + // Reactive: a session change (re-login without reload) immediately + // updates the outgoing-translation owner check below. Old code read + // GetSessionDataManager()?.userId at hook mount and would have stayed + // stuck on the previous session's id. + const ownUserId = (useUserDataSnapshot().userId || -1); const applyTranslationToBubble = useCallback((chatMessage: ChatBubbleMessage, originalText: string, translatedText: string, detectedLanguage: string, targetLanguage: string) => {