mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
a4c9dd87db
Continues the proposal #4 split pattern (doorbell, poll, furni-chooser, user-chooser, friend-request) for the chat-input widget. Splits the 334-line useChatInputWidget along the natural seam: - useChatInputState — selectedUsername / floodBlocked / floodBlockedSeconds / isTyping / isIdle state plus the three event listeners (FLOOD_EVENT, ObjectSelected, ObjectDeselected) and the three lifecycle effects (flood-countdown, idle-auto-clear, typing-indicator sync). - useChatInputActions — sendChat(text, chatType, recipientName, styleId). Carries the slash-command handler (":shake", ":rotate", ":zoom", ":screenshot", ":pickall", etc.) and the chat-vs-shout-vs-whisper dispatch path, with the optional outgoing-translation hook. - useChatInputWidget — deprecated shim that composes both into the historical { selectedUsername, floodBlocked, floodBlockedSeconds, setIsTyping, setIsIdle, sendChat } shape so ChatInputView keeps working unchanged. Bonus while in here: - Guarded all roomSession reads in actions with optional chaining (the hook can be called during the brief no-room window between enter and leave). - Dropped the useless 'if(isIdle)' inside the idle effect body — the early return guard above it already covers that branch.
114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
import { RoomEngineObjectEvent, RoomObjectCategory, RoomSessionChatEvent } from '@nitrots/nitro-renderer';
|
|
import { useEffect, useState } from 'react';
|
|
import { useNitroEvent } from '../../events';
|
|
import { useObjectSelectedEvent } from '../engine';
|
|
import { useRoom } from '../useRoom';
|
|
|
|
/**
|
|
* State + event subscriptions for the chat-input widget. Pure
|
|
* imperative dispatch (sendChat) lives in useChatInputActions.
|
|
*
|
|
* - selectedUsername → tracks the last avatar the user clicked,
|
|
* used by `/whisper` shortcuts.
|
|
* - floodBlocked / → flood-throttle banner state, driven by the
|
|
* floodBlockedSeconds renderer's FLOOD_EVENT plus a 1s tick.
|
|
* - isTyping / → typing indicator + 10s idle auto-clear, with
|
|
* isIdle an internal `typingStartedSent` ref so the
|
|
* outgoing sendChatTypingMessage only fires on
|
|
* state edges (start / stop), not every render.
|
|
*/
|
|
export const useChatInputState = () =>
|
|
{
|
|
const [ selectedUsername, setSelectedUsername ] = useState('');
|
|
const [ isTyping, setIsTyping ] = useState<boolean>(false);
|
|
const [ typingStartedSent, setTypingStartedSent ] = useState(false);
|
|
const [ isIdle, setIsIdle ] = useState(false);
|
|
const [ floodBlocked, setFloodBlocked ] = useState(false);
|
|
const [ floodBlockedSeconds, setFloodBlockedSeconds ] = useState(0);
|
|
const { roomSession = null } = useRoom();
|
|
|
|
useNitroEvent<RoomSessionChatEvent>(RoomSessionChatEvent.FLOOD_EVENT, event =>
|
|
{
|
|
setFloodBlocked(true);
|
|
setFloodBlockedSeconds(parseFloat(event.message));
|
|
});
|
|
|
|
useObjectSelectedEvent(event =>
|
|
{
|
|
if(event.category !== RoomObjectCategory.UNIT) return;
|
|
|
|
const userData = roomSession?.userDataManager?.getUserDataByIndex(event.id);
|
|
|
|
if(!userData) return;
|
|
|
|
setSelectedUsername(userData.name);
|
|
});
|
|
|
|
useNitroEvent<RoomEngineObjectEvent>(RoomEngineObjectEvent.DESELECTED, () => setSelectedUsername(''));
|
|
|
|
useEffect(() =>
|
|
{
|
|
if(!floodBlocked) return;
|
|
|
|
let seconds = 0;
|
|
|
|
const interval = setInterval(() =>
|
|
{
|
|
setFloodBlockedSeconds(prevValue =>
|
|
{
|
|
seconds = ((prevValue || 0) - 1);
|
|
|
|
return seconds;
|
|
});
|
|
|
|
if(seconds < 0)
|
|
{
|
|
clearInterval(interval);
|
|
|
|
setFloodBlocked(false);
|
|
}
|
|
}, 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [ floodBlocked ]);
|
|
|
|
useEffect(() =>
|
|
{
|
|
if(!isIdle) return;
|
|
|
|
const timeout = setTimeout(() =>
|
|
{
|
|
setIsIdle(false);
|
|
setIsTyping(false);
|
|
}, 10000);
|
|
|
|
return () => clearTimeout(timeout);
|
|
}, [ isIdle ]);
|
|
|
|
useEffect(() =>
|
|
{
|
|
if(!roomSession) return;
|
|
|
|
if(isTyping)
|
|
{
|
|
if(!typingStartedSent)
|
|
{
|
|
setTypingStartedSent(true);
|
|
|
|
roomSession.sendChatTypingMessage(isTyping);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(typingStartedSent)
|
|
{
|
|
setTypingStartedSent(false);
|
|
|
|
roomSession.sendChatTypingMessage(isTyping);
|
|
}
|
|
}
|
|
}, [ roomSession, isTyping, typingStartedSent ]);
|
|
|
|
return { selectedUsername, floodBlocked, floodBlockedSeconds, setIsTyping, setIsIdle };
|
|
};
|