참고
- 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;
'개발 > React' 카테고리의 다른 글
리액트, Node JS - Socket.IO로 채팅하기 (Chatting with Socket.IO) (0) | 2024.03.31 |
---|---|
리액트 - 채팅방 들어오고 나갈 때 표시하기 (Message Separator) (0) | 2024.03.31 |
리액트, Node JS - Socket.IO로 로그인 유저 관리하기 (Managing Logged-in Users with Socket.IO) (0) | 2024.03.27 |
리액트, Node JS - webSocket으로 로그인 유저 관리하기 (Managing Logged-in Users with WebSocket) (0) | 2024.03.27 |
리액트 - 로그인 + 채팅방 UI 만들기 (React Chat Room UI) (0) | 2024.03.25 |
댓글