Room.tsx 3.96 KB
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { useHistory, useLocation } from 'react-router';
import { Main } from '../components/common/Main';
import { MessageResponse, MessageType, RawMessage } from '../components/common/types';
import { Canvas } from '../components/room/Canvas';
import { Chat } from '../components/room/Chat';
import { GameBoard } from '../components/room/GameBoard';
import { Ready } from '../components/room/Ready';
import { RoomInfo } from '../components/room/RoomInfo';
import { RoomData, UpdateRoomUser } from '../components/room/types';
import { UserInfo } from '../components/room/UserInfo';
import { UserRole } from '../components/room/UserRole';
import { UserStatus } from '../components/room/UserStatus';
import SocketContext from '../contexts/SocketContext';

interface RoomLocation {
  state: { roomData: RoomData }
}

export const Room: React.FC = () => {
  const history = useHistory();
  const socket = useContext(SocketContext);
  const location: RoomLocation = useLocation();

  const [ roomData, setRoomData ] = useState<RoomData>({
    // 기본값
    uuid: '0',
    name: 'loading...',
    maxUsers: 9,
    users: []
  });
  const [ isInGame, setIsInGame ] = useState(false);

  const handleInGame = useCallback((rawMessage: RawMessage) => {
    if (rawMessage.type === MessageType.GAME_START) {
      setIsInGame(true);
    } else if (rawMessage.type === MessageType.GAME_FINISH_GAME) {
      setIsInGame(false);
    }
  }, []);

  const handleUpdateRoomUser = useCallback((rawMessage: RawMessage) => {
    if (rawMessage.type == MessageType.ROOM_USER_UPDATE) {
      const data = rawMessage.message as UpdateRoomUser;
      if (data.state == 'removed') {
        const newUsers = roomData.users;
        const index = newUsers.findIndex(x => x.username === data.user.username);
        if (index < 0) {
          console.log('존재하지 않는 유저를 제거 시도');
        } else {
          newUsers.splice(index, 1);
        }
        setRoomData({
          ...roomData,
          users: newUsers
        });
      } else if (data.state === 'updated') {
        const newUsers = roomData.users;
        const index = newUsers.findIndex(x => x.username === data.user.username);
        if (index < 0) {
          console.log('존재하지 않는 유저를 업데이트 시도');
        } else {
          newUsers[index] = data.user;
        }
        setRoomData({
          ...roomData,
          users: newUsers
        })
      } else {
        setRoomData({
          ...roomData,
          users: [data.user, ...roomData.users]
        });
      }
    }
  }, [roomData]);

  useEffect(() => {
    socket.on('msg', handleUpdateRoomUser);
    
    return () => {
      socket.off('msg', handleUpdateRoomUser);
    }
  }, [roomData]);

  useEffect(() => {
    // 비정상적인 루트로 방을 들어오면 로그인 화면으로 푸시
    if (location.state === undefined) {
      history.push('/');
      return;
    }

    setRoomData(location.state.roomData);
    socket.on('msg', handleInGame);

    return () => {
      socket.off('msg', handleInGame);

      const rawMessage: RawMessage = {
        type: MessageType.ROOM_LEAVE,
        message: ''
      }
      socket.emit('msg', rawMessage, (response : MessageResponse<undefined>) => {});
    }
  }, [])
  
  return (
    <Main>
      <RoomInfo roomData={roomData}/>
      <div className='w-full flex justify-center'>
        {/* 게임보드와 유저롤을 계속 살려둬서 리스너를 항상 열어놓도록 하자 */}
        <UserRole isInGame={isInGame} />
        <GameBoard isInGame={isInGame} />
        {
          isInGame ? (
              <Chat w='w-4/12' h='h-132' />  
          ) : (
            <div className='w-full flex flex-col justify-center items-center'>
              <UserInfo users={roomData.users}/>
              <Ready users={roomData.users} />
              <Chat w='w-7/12' h='h-96' />
            </div>
          )
        }
      </div>
    </Main>
  );
}