import { FC, FormEvent, useEffect, useRef, useState } from 'react' interface Props { x?: number y?: number color: string hue: string message: string isTyping: boolean isCancelled?: boolean isLocalClient?: boolean onUpdateMessage?: (message: string) => void } const MAX_MESSAGE_LENGTH = 70 const MAX_DURATION = 4000 const MAX_BUBBLE_WIDTH_THRESHOLD = 280 + 50 const MAX_BUBBLE_HEIGHT_THRESHOLD = 40 + 50 const Cursor: FC = ({ x, y, color, hue, message, isTyping, isCancelled, isLocalClient, onUpdateMessage = () => {}, }) => { // Don't show cursor for the local client const _isLocalClient = !x || !y || isLocalClient const inputRef = useRef() as any const timeoutRef = useRef() as any const chatBubbleRef = useRef() as any const [flipX, setFlipX] = useState(false) const [flipY, setFlipY] = useState(false) const [hideInput, setHideInput] = useState(false) const [showMessageBubble, setShowMessageBubble] = useState(false) useEffect(() => { if (isTyping) { setShowMessageBubble(true) if (timeoutRef.current) clearTimeout(timeoutRef.current) if (isLocalClient) { if (inputRef.current) inputRef.current.focus() setHideInput(false) } } else { if (!message || isCancelled) { setShowMessageBubble(false) } else { if (timeoutRef.current) clearTimeout(timeoutRef.current) if (isLocalClient) setHideInput(true) const timeoutId = setTimeout(() => { setShowMessageBubble(false) }, MAX_DURATION) timeoutRef.current = timeoutId } } }, [isLocalClient, isTyping, isCancelled, message, inputRef]) useEffect(() => { // [Joshen] Experimental: dynamic flipping to ensure that chat // bubble always stays within the viewport, comment this block // out if the effect seems weird. setFlipX((x || 0) + MAX_BUBBLE_WIDTH_THRESHOLD >= window.innerWidth) setFlipY((y || 0) + MAX_BUBBLE_HEIGHT_THRESHOLD >= window.innerHeight) }, [x, y, isTyping, chatBubbleRef]) return ( <> {!_isLocalClient && ( )}
{_isLocalClient && !hideInput ? ( <> ) => { const text = e.currentTarget.value if (text.length <= MAX_MESSAGE_LENGTH) onUpdateMessage(e.currentTarget.value) }} />

{message.length}/{MAX_MESSAGE_LENGTH}

) : message.length ? (
{message}
) : (
)}
) } export default Cursor