Skip to content

Commit

Permalink
[#54] 채팅 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Dormarble committed Apr 29, 2021
1 parent 2daf876 commit f90ff23
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 27 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"sass-loader": "^11.0.1",
"socket.io-client": "2.3.1",
"style-loader": "^2.0.0",
"uuid": "3.4.0",
"web-vitals": "^1.1.0",
"webpack": "^5.24.2",
"webpack-cli": "^4.5.0",
Expand Down
2 changes: 2 additions & 0 deletions src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ export const API_GET_ALL_NOTICES = makeAPI(GET, '/notice/all');
export const API_CREATE_NOTICE = makeAPI(POST, '/notice');
export const API_UPDATE_NOTICE = makeAPI(PATCH, '/notice');
export const API_DELETE_NOTICE = makeAPI(DELETE, '/notice');
export const API_FIND_CHATROOM = makeAPI(GET, '/chatRoom');
export const API_FIND_CHATROOMS = makeAPI(GET, '/chatRoom');
export const API_GET_MESSAGES = makeAPI(GET, '/chatRoom/messages');
export const API_GET_POINTS = makeAPI(GET, '/chatRoom/points');
export const API_GET_ALL_LECTURES = makeAPI(GET, '/lecture');
export const API_UPDATE_LECTURES = makeAPI(PATCH, '/lecture');
export const API_GET_HISTORIES = makeAPI(GET, '/history');
Expand Down
6 changes: 4 additions & 2 deletions src/views/chat/chatMessage.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Container } from '@material-ui/core';
import dayjs from 'dayjs';
import React from 'react';

export default function ChatMessage({from, message, readCnt}) {
export default function ChatMessage({idx, from, message, readCnt}) {
const time = dayjs(message.date).format('HH:mm')
return (
<Container>
{from}
{message.content}
{message.date}
{time}
{readCnt > 0 ? readCnt : null }
</Container>
);
Expand Down
136 changes: 111 additions & 25 deletions src/views/chat/index.jsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,139 @@
import React, { useEffect, useRef, useState } from 'react';
import { Button, Container, Typography } from '@material-ui/core';
import queryString from 'query-string';
import { useRecoilState } from 'recoil';
import { chatroomState } from '../../states/Chatroom';
import { v4 as uuidv4 } from 'uuid';
import { Redirect } from 'react-router';
import { API_GET_MESSAGES, requestAPI } from '../../utils/api';
import { API_FIND_CHATROOM, API_GET_MESSAGES, API_GET_POINTS, requestAPI } from '../../utils/api';
import { StatusCodes } from 'http-status-codes';
import ChatMessage from './chatMessage';
import { getSocket } from '../../utils/socket';

export default function Chatroom({location}) {
const [chatroomList, setChatroomList] = useRecoilState(chatroomState);
const [chatRoom, setChatRoom] = useState({});
const [input, setInput] = useState('');
const [messages, setMessages] = useState([]);
const [readPoints, setReadPoints] = useState([]);
const [emptyMsg, setEmptyMsg] = useState('');

const query = queryString.parse(location.search)
const chatRoom = chatroomList.find(chatroom => chatroom._id === query.id);
const chatRoomId = queryString.parse(location.search).id;

if(!chatRoom) {
return <Redirect to='/chatrooms' />
if(chatRoom === null) {
return <Redirect to='/chatrooms' />;
}

const length = useRef(chatRoom.length);
const range = useRef({start: 0, end: 0});
const messageRef = useRef([]);
const readPointRef = useRef([]);
const chatRoomRef = useRef({});

const userId = window.localStorage.getItem('userID');
const socket = getSocket();

const onMessageEvent = (message) => {
length.current++;
const newChatRoom = {
...chatRoomRef.current,
length: length+1
};
setChatRoom(newChatRoom);
chatRoomRef.current = newChatRoom;
range.current.end++;

setMessages(messageRef.current.concat(message));
messageRef.current = messageRef.current.concat(message);
}
let newMessages;
if(message.from === userId) {
newMessages = messageRef.current.map(m => {
if(m.id === message.id) {
return message;
}
return m;
})
} else {
newMessages = messageRef.current.concat(message);
}
console.log(newMessages)
setMessages(newMessages);
messageRef.current = newMessages;

socket.emit('read', {
chatRoomId: chatRoomId,
userId: userId,
messageIdx: range.current.end
});
};

const onReadEvent = (read) => {
const updatedReadPoints = readPointRef.current.map(point => {
if(point.id === read.userId) {
return {
...point,
read: read.messageIdx
};
}
return point;
});

setReadPoints(updatedReadPoints);
readPointRef.current = updatedReadPoints;
};

useEffect(() => {
socket.on('message', onMessageEvent);
socket.on('read', onReadEvent);

const getChatRoom = async (chatRoomId) => {
const response = await requestAPI(API_FIND_CHATROOM().setPathParam(chatRoomId));

if(response.status !== StatusCodes.OK) {
setEmptyMsg('데이터를 불러오는데 실패했습니다.');
setChatRoom(null);
chatRoomRef.current = null;
return;
}

setChatRoom(response.data);
chatRoomRef.current = response.data;

return response.data;
}

const getMessages = async (chatRoomId, start, end) => {
if(end < 0) return;

const response = await requestAPI(API_GET_MESSAGES().setQuery({chatRoomId, start, end}));

if(response.status !== StatusCodes.OK) {
setEmptyMsg('메시지를 불러오는데 실패했습니다.');
setEmptyMsg('채팅을 불러오는데 실패했습니다.');
return;
}
setMessages(response.data);
messageRef.current = response.data
messageRef.current = response.data;
};

const start = length.current > 50 ? length.current-50 : 0;
const end = length.current > 0 ? length.current-1 : 0;
const getReadPoints = async (chatRoomId) => {
const response = await requestAPI(API_GET_POINTS().setQuery({chatRoomId}));

range.current = {start, end};
length.current = end - start + 1;
getMessages(chatRoom._id, start, end);
if(response.status !== StatusCodes.OK) {
setEmptyMsg('데이터를 불러오는데 실패했습니다.');
return;
}
setReadPoints(response.data);
readPointRef.current = response.data;
}

getChatRoom(chatRoomId).then((room) => {
const start = room.length > 50 ? room.length-50 : 0;
const end = room.length > 0 ? room.length-1 : 0;

range.current = {start, end};

getMessages(chatRoomId, start, end);
getReadPoints(chatRoomId);

socket.emit('read', {
chatRoomId: chatRoomId,
userId: userId,
messageIdx: range.current.end
});
});
}, []);


Expand All @@ -73,24 +149,34 @@ export default function Chatroom({location}) {

const send = () => {
if(input.length > 0) {
const id = uuidv4();

const messageEvent = {
id,
chatRoomId: chatRoom._id,
from: window.localStorage.getItem('userID'),
type: 'normal',
content: input
}

socket.emit('message', messageEvent);

const newMessages = messageRef.current.concat(messageEvent);
setMessages(newMessages);
messageRef.current = newMessages;

setInput('');
}

}

const messageList = messages.map((m, idx) => {
const index = range.current.start + idx
const index = range.current.start + idx;
const from = chatRoom.participants.find(p => p._id == m.from).name;
const readCnt = readPoints.filter(point => point.id !== userId)
.filter(point => point.idx < index)
const readCnt = readPoints.filter(point => point.id != m.from)
.filter(point => point.read < index)
.length;
return <ChatMessage key={index} from={from} message={m} readCnt={readCnt} />;
return <ChatMessage key={index} from={from} message={m} readCnt={readCnt} idx={index} />;
});

return (
Expand All @@ -100,7 +186,7 @@ export default function Chatroom({location}) {
{ messageList }
</Container>
<Container>
<input type='text' onChange={onChange} onKeyPress={onEnterPress} />
<input type='text' value={input} onChange={onChange} onKeyPress={onEnterPress} />
<Button onClick={send}>전송</Button>
</Container>
</Container>
Expand Down

0 comments on commit f90ff23

Please sign in to comment.