본문 바로가기
개발/React

리액트 - 채팅방 변경하기 (Change Chat Room)

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

리액트 전체 링크

 

참고

- 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로 중복 로그인 방지하기

 

채팅방 UI를 만들었으니, 채팅방을 변경하는 기능을 추가해 보자.


컴포넌트와 데이터 분리하기

 

메시지 컴포넌트에서 데이터를 분리한 것처럼, Conversation데이터를 분리한다.

  <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>

 

위에서 설정된 옵션을 아래와 같이 분리하였다.

const defaultConversation = [
  {
    info: "I'm fine, too. thank you, and you?",
    lastSenderName: "bloodstrawberry",
    name: "bloodstrawberry",
    src: AVATAR_IMAGE,
    status: "available",
  },
  {
    info: "Yes i can do it for you",
    lastSenderName: "Patrik",
    name: "Patrik",
    src: "https://chatscope.io/storybook/react/assets/patrik-yC7svbAR.svg",
    status: "invisible",
  },
];

 

이제 ConversationList 내부는 깔끔하게 변경된다.

  <Sidebar position="left">
    <ConversationList>
      {getConversationComponent(defaultConversation)}
    </ConversationList>
  </Sidebar>

 

getConversationComponent는 다음과 같다.

useState에서 activeID를 관리하고, 클릭한 Conversation의 index를 얻어 active를 활성화 하였다.

  const [activeID, setActiveID] = useState(0);

  const getConversationComponent = (data) => {
    return data.map((item, index) => {
      return (
        <Conversation
          key={index}
          active={index === activeID}
          info={item.info}
          lastSenderName={item.lastSenderName}
          name={item.name}
          onClick={() => setActiveID(index)}
        >
          <Avatar name={item.name} src={item.src} status={item.status} />
        </Conversation>
      );
    });
  };

 

이제 채팅방 탭을 클릭하면 활성화된 채팅방이 변하게 된다.


채팅방 내부 변경

 

활성화된 채팅방을 변경했으니, 내부도 변경해 보자.

Patric과 했던 대화는 다음과 같다.

const defaultMessage = [
  ...
];

const defaultMessagePatrik = [
  {
    model: {
      message: "Can you take care of this for me?",
      direction: "outgoing",
    },
  },
  {
    model: {
      message: "Yes i can do it for you",
      direction: "incoming",
    },
    avatar: {
      src: "https://chatscope.io/storybook/react/assets/patrik-yC7svbAR.svg",
      name: "Patrik",
    },
  },
];

 

그리고 전체 대화방의 메시지를 totalMessages가 가지고 있는다.

const totalMessages = [defaultMessage, defaultMessagePatrik];

 

getMessageComponent는 전체 메시지 중, active된 메시지만 보여주면 된다.

  const [messages, setMessages] = useState(totalMessages);

  const getMessageComponent = (totalMessages) => {
    let data = totalMessages[activeID];

    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>
      );
    });
  };

 

handleSend도 마찬가지로 active된 채팅방에만 신규 메시지를 추가한다.

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

    let temp = [...totalMessages];
    temp[activeID].push(newMessage);
    setMessages(temp);
  };

 

전체 코드는 다음과 같다.

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 defaultMessagePatrik = [
  {
    model: {
      message: "Can you take care of this for me?",
      direction: "outgoing",
    },
  },
  {
    model: {
      message: "Yes i can do it for you",
      direction: "incoming",
    },
    avatar: {
      src: "https://chatscope.io/storybook/react/assets/patrik-yC7svbAR.svg",
      name: "Patrik",
    },
  },
];

const totalMessages = [defaultMessage, defaultMessagePatrik];

const defaultConversation = [
  {
    info: "I'm fine, too. thank you, and you?",
    lastSenderName: "bloodstrawberry",
    name: "bloodstrawberry",
    src: AVATAR_IMAGE,
    status: "available",
  },
  {
    info: "Yes i can do it for you",
    lastSenderName: "Patrik",
    name: "Patrik",
    src: "https://chatscope.io/storybook/react/assets/patrik-yC7svbAR.svg",
    status: "invisible",
  },
];

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

  const [activeID, setActiveID] = useState(0);
  const [messages, setMessages] = useState(totalMessages);

  const getMessageComponent = (totalMessages) => {
    let data = totalMessages[activeID];

    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 getConversationComponent = (data) => {
    return data.map((item, index) => {
      return (
        <Conversation
          key={index}
          active={index === activeID}
          info={item.info}
          lastSenderName={item.lastSenderName}
          name={item.name}
          onClick={() => setActiveID(index)}
        >
          <Avatar name={item.name} src={item.src} status={item.status} />
        </Conversation>
      );
    });
  };

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

    let temp = [...totalMessages];
    temp[activeID].push(newMessage);
    setMessages(temp);
  };

  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>
            {getConversationComponent(defaultConversation)}
          </ConversationList>
        </Sidebar>
        <ChatContainer>
          <ConversationHeader>
            <ConversationHeader.Back />
            <Avatar
              name={defaultConversation[activeID].name}
              src={defaultConversation[activeID].src}
            />
            {activeID === 0 ? (
              <ConversationHeader.Content
                info="Active 10 mins ago"
                userName="bloodstrawberry"
              />
            ) : (
              <ConversationHeader.Content
                info="Active 7 hours ago"
                userName="Patrik"
              />
            )}
            <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;
반응형

댓글