본문 바로가기
개발/React

리액트 - 로그인 + 채팅방 UI 만들기 (React Chat Room UI)

by 피로물든딸기 2024. 3. 25.
반응형

리액트 전체 링크

 

참고

- https://mui.com/material-ui/react-select/

- useNavigate로 Toast UI Editor 이동하기

- https://chatscope.io/storybook/react/?path=/docs/components-maincontainer--docs

 

chat-ui-kit-react로 채팅창 UI 만들기
로그인 + 채팅방 UI 만들기
채팅방 변경하기
채팅방 들어오고 나갈 때 표시하기
Socket.IO로 채팅하기
Socket.IO Room으로 채팅방 관리하기
Socket.IO namespace로 채팅방 관리하기
Socket.IO로 중복 로그인 제한하기

- Socket.IO Middleware로 중복 로그인 방지하기

 

로그인 ID를 선택한 후, 채팅방에 들어가도록 구현해 보자.


Simple Login UI

 

Material UI의 Drop Down을 이용해 ID를 선택할 수 있도록 하자.

<FormControl sx={{ marginRight: 1, width: 300 }}>
    <InputLabel>ID</InputLabel>
    <Select
    labelId="demo-simple-select-label"
    value={id}
    label="ID"
    onChange={handleChange}
    >
    <MenuItem value="Lilly">Lilly</MenuItem>
    <MenuItem value="Joe">Joe</MenuItem>
    <MenuItem value="Emily">Emily</MenuItem>
    <MenuItem value="Akane">Akane</MenuItem>
    <MenuItem value="Eliot">Eliot</MenuItem>
    <MenuItem value="Zoe">Zoe</MenuItem>
    </Select>
</FormControl>

 

ID를 선택한 후, 로그인 버튼을 누르면 채팅방으로 이동할 수 있도록 useNavigate를 이용한다.

import { useNavigate } from "react-router-dom";

const ChatLogin = () => {
  const navigate = useNavigate();
  const [id, setID] = React.useState("");

  const handleChange = (event) => {
    setID(event.target.value);
  };

  const login = () => {
    if (id === "") return;
    console.log(id);
    navigate("/chat", { state: { loginID: id } });
  };
  
  ...

 

채팅방(ChatUI.js)은 각 사용자 별로 아바타 이미지 링크를 정의한다.

const AVATAR_MAP = {
  Lilly: "https://chatscope.io/storybook/react/assets/lilly-aj6lnGPk.svg",
  Joe: "https://chatscope.io/storybook/react/assets/joe-v8Vy3KOS.svg",
  Emily: "https://chatscope.io/storybook/react/assets/emily-xzL8sDL2.svg",
  Akane: "https://chatscope.io/storybook/react/assets/akane-MXhWvx63.svg",
  Eliot: "https://chatscope.io/storybook/react/assets/eliot-JNkqSAth.svg",
  Zoe: "https://chatscope.io/storybook/react/assets/zoe-E7ZdmXF0.svg",
};

 

로그인 한 사용자채팅방 위에 표시하도록 하자.

useState로 관리하는 loginID는 로그인 UI에서 useNavigate의 stateID를 넘겨 받게 된다.

  const [loginID, setLoginID] = useState("");
  
  ...

  <Conversation
    info="I'm fine, thank you, and you?"
    lastSenderName={loginID}
    name={loginID}
  >
    <Avatar name={loginID} src={AVATAR_MAP[loginID]} status="available" />
  </Conversation>

 

이제 navigate에서 얻은 state에서 ID를 설정한다.

  const init = () => {
    setLoginID(location.state.loginID);
  }

  useEffect(init, []);

 

채팅방 UI링크를 참고하였다.

 

전체 코드는 다음과 같다.

 

ChatLogin.js

import React from "react";

import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";
import { useNavigate } from "react-router-dom";

const ChatLogin = () => {
  const navigate = useNavigate();
  const [id, setID] = React.useState("");

  const handleChange = (event) => {
    setID(event.target.value);
  };

  const login = () => {
    if (id === "") return;
    console.log(id);
    navigate("/chat", { state: { loginID: id } });
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        height: "100vh",
      }}
    >
      <Box sx={{ display: "flex", alignItems: "center" }}>
        <FormControl sx={{ marginRight: 1, width: 300 }}>
          <InputLabel>ID</InputLabel>
          <Select
            labelId="demo-simple-select-label"
            value={id}
            label="ID"
            onChange={handleChange}
          >
            <MenuItem value="Lilly">Lilly</MenuItem>
            <MenuItem value="Joe">Joe</MenuItem>
            <MenuItem value="Emily">Emily</MenuItem>
            <MenuItem value="Akane">Akane</MenuItem>
            <MenuItem value="Eliot">Eliot</MenuItem>
            <MenuItem value="Zoe">Zoe</MenuItem>
          </Select>
        </FormControl>
        <Button variant="outlined" onClick={login}>
          Login
        </Button>
      </Box>
    </div>
  );
};

export default ChatLogin;

 

ChatUI.js

import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";

import styles from "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";

import {
  MainContainer,
  ChatContainer,
  MessageList,
  Message,
  MessageInput,
  Avatar,
  Sidebar,
  ConversationList,
  Conversation,
  ConversationHeader,
  VoiceCallButton,
  VideoCallButton,
  InfoButton,
  MessageSeparator,
} from "@chatscope/chat-ui-kit-react";

const AVATAR_IMAGE =
  "https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory3.daumcdn.net%2Ftistory%2F4431109%2Fattach%2F3af65be1d8b64ece859b8f6d07fafadc";

const AVATAR_MAP = {
  Lilly: "https://chatscope.io/storybook/react/assets/lilly-aj6lnGPk.svg",
  Joe: "https://chatscope.io/storybook/react/assets/joe-v8Vy3KOS.svg",
  Emily: "https://chatscope.io/storybook/react/assets/emily-xzL8sDL2.svg",
  Akane: "https://chatscope.io/storybook/react/assets/akane-MXhWvx63.svg",
  Eliot: "https://chatscope.io/storybook/react/assets/eliot-JNkqSAth.svg",
  Zoe: "https://chatscope.io/storybook/react/assets/zoe-E7ZdmXF0.svg",
};

const defaultMessage = [
  {
    model: {
      message: "How are you?",
      direction: "incoming",
    },
    avatar: {
      src: AVATAR_IMAGE,
      name: "bloodstrawberry",
    },
  },
  {
    model: {
      message: "I'm fine, thank you, and you?",
      direction: "outgoing",
    },
  },
  {
    model: {
      message: "I'm fine, too. thank you, and you?",
      direction: "incoming",
    },
    avatar: {
      src: AVATAR_IMAGE,
      name: "bloodstrawberry",
    },
  },
];

const getMessageComponent = (data) => {
  return data.map((item, index) => {
    return (
      <Message key={index} model={item.model}>
        {item.avatar ? (
          <Avatar src={item.avatar.src} name={item.avatar.name} />
        ) : null}
      </Message>
    );
  });
};

const ChatUI = () => {
  const location = useLocation();
  const [loginID, setLoginID] = useState("");
  const [messages, setMessages] = useState(defaultMessage);

  const handleSend = (input) => {
    let newMessage = {
      model: {
        message: input,
        direction: "outgoing",
      },
    };

    setMessages([...messages, newMessage]);
  };

  const init = () => {
    setLoginID(location.state.loginID);
  };

  useEffect(init, []);

  return (
    <div>
      <Conversation
        info="I'm fine, thank you, and you?"
        lastSenderName={loginID}
        name={loginID}
      >
        <Avatar name={loginID} src={AVATAR_MAP[loginID]} status="available" />
      </Conversation>
      <MessageSeparator style={{ marginTop: 5, marginBottom: 5 }} />
      <MainContainer
        responsive
        style={{
          height: "600px",
        }}
      >
        <Sidebar position="left">
          <ConversationList>
            <Conversation
              active
              info="Yes i can do it for you"
              lastSenderName="bloodstrawberry"
              name="bloodstrawberry"
            >
              <Avatar
                name="bloodstrawberry"
                src={AVATAR_IMAGE}
                status="available"
              />
            </Conversation>
            <Conversation
              info="Yes i can do it for you"
              lastSenderName="Patrik"
              name="Patrik"
            >
              <Avatar
                name="Patrik"
                src="https://chatscope.io/storybook/react/assets/patrik-yC7svbAR.svg"
                status="invisible"
              />
            </Conversation>
          </ConversationList>
        </Sidebar>
        <ChatContainer>
          <ConversationHeader>
            <ConversationHeader.Back />
            <Avatar name="bloodstrawberry" src={AVATAR_IMAGE} />
            <ConversationHeader.Content
              info="Active 10 mins ago"
              userName="bloodstrawberry"
            />
            <ConversationHeader.Actions>
              <VoiceCallButton />
              <VideoCallButton />
              <InfoButton />
            </ConversationHeader.Actions>
          </ConversationHeader>
          <MessageList>{getMessageComponent(messages)}</MessageList>
          <MessageInput placeholder="Type message here" onSend={handleSend} />
        </ChatContainer>
      </MainContainer>
    </div>
  );
};

export default ChatUI;

 

 

 

 

 

 

반응형

댓글