Chat.tsx 2.4 KB
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import SocketContext from '../../contexts/SocketContext';
import { MessageType, RawMessage } from '../common/types';
import { ChatLine } from './ChatLine';
import { ChatData } from './types';

interface ChatProps {
  w: string;
  h: string;
}

export const Chat: React.FC<ChatProps> = (props) => {
  const socket = useContext(SocketContext);
  const [ input, setInput ] = useState('');
  const [ chatLines, setChatLines ] = useState<ChatData[]>([]);
  const messageEndRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const handleChatData = (message: RawMessage) => {
      if (message.type === MessageType.ROOM_CHAT) {
        setChatLines(oldChatLines => [...oldChatLines, message.message as ChatData]);
      }
    }

    socket.on('msg', handleChatData);
    socket.on('msg', handleAcceptMessage);

    return () => {
      socket.off('msg', handleChatData);
      socket.off('msg', handleAcceptMessage);
    }
  }, []);

  const handleAutoScroll = useCallback(() => {
    messageEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, []);
  
  useEffect(handleAutoScroll, [chatLines])

  const handleEnter = useCallback((e: React.KeyboardEvent) => {
    if (e.key === 'Enter') {
      const rawMessage: RawMessage = {
        type: MessageType.ROOM_CHAT,
        message: { message: input }
      }
      socket.emit('msg', rawMessage, () => {});

      setInput('');
    }
  }, [input]);

  const handleAcceptMessage = useCallback((rawMessage: RawMessage) => {
    if (rawMessage.type === MessageType.GAME_ACCEPT) {
      const message: ChatData = {
        sender: 'SYSTEM',
        message: 'That\'s correct!'
      };
      setChatLines(oldChatLines => [...oldChatLines, message]);
    }
  }, []);

  return (
    <div className={props.w}>
      <div className={`${props.h} w-full py-2 rounded shadow flex flex-col overflow-y-scroll`}>
        {chatLines.map((line, i) => (<ChatLine key={16383+i} chatData={line}/>))}
        <div ref={messageEndRef} />
      </div>
      <input className='w-full px-3 py-2 bg-white
                      placeholder-gray-400 text-gray-700 text-sm
                      rounded shadow outline-none focus:outline-none'
            placeholder='Enter the answer'
            onChange={e => setInput(e.target.value)}
            value={input}
            onKeyPress={handleEnter}></input>
    </div>
  );
}