Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/MOVIEJOJO7/cat-talk into fe…
Browse files Browse the repository at this point in the history
…ature/#48
  • Loading branch information
LikeFireAndSky committed Nov 15, 2023
2 parents e0bfd53 + bd5a8cd commit f8a2257
Show file tree
Hide file tree
Showing 11 changed files with 541 additions and 169 deletions.
51 changes: 51 additions & 0 deletions Components/Chat/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { getCookie } from '@/Components/Login/Cookie';
import { useRouter } from 'next/navigation';
import { Socket } from 'socket.io-client';
import { DefaultEventsMap } from 'socket.io/dist/typed-events';

const ChatHeader = ({
socket,
chatId,
chatName,
chatUsers,
}: {
socket: Socket<DefaultEventsMap, DefaultEventsMap>;
chatId: string;
chatName: string;
chatUsers: number;
}) => {
const router = useRouter();

const accessToken = getCookie('accessToken');

const handleBackChat = () => {
socket.disconnect();
router.back();
};

const handleLeaveChat = async () => {
await fetch('https://fastcampus-chat.net/chat/leave', {
method: 'PATCH',
headers: {
'content-type': 'application/json',
serverId: process.env.NEXT_PUBLIC_SERVER_ID as string,
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ chatId }),
});

socket.disconnect();
router.back();
};

return (
<>
<button onClick={handleBackChat}>뒤로 가기</button>
<span>{chatName}</span>
<span>{chatUsers}</span>
<button onClick={handleLeaveChat}>채팅방 나가기</button>
</>
);
};

export default ChatHeader;
147 changes: 147 additions & 0 deletions Components/Chat/ChatRoom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
'use client';

import React, { useEffect, useState } from 'react';
import { Socket } from 'socket.io-client';
import { Chat, Message, chatUsersObject } from '@/types';
import { DefaultEventsMap } from 'socket.io/dist/typed-events';
import ChatHeader from '@/Components/Chat/ChatHeader';
import RenderChats from '@/Components/Chat/RenderChats';

const ChatRoom = ({
socket,
chatId,
privateValue,
accessToken,
}: {
socket: Socket<DefaultEventsMap, DefaultEventsMap>;
chatId: string;
privateValue: string;
accessToken: string;
}) => {
const initChatusers = {
id: '',
name: '',
users: [], // 속한 유저 정보
isPrivate: JSON.parse(privateValue),
latestMessage: null,
updatedAt: new Date(),
};

const [messages, setMessages] = useState<Message[]>([]);
const [newMessage, setNewMessage] = useState('');
const [chatUsers, setChatUsers] = useState<Chat>(initChatusers);

// 채팅 참여자 fetch, socket 이벤트 등록 (1회 동작)
useEffect(() => {
const fetchChatUsers = async () => {
const res = await fetch(
`https://fastcampus-chat.net/chat/only?chatId=${chatId}`,
{
method: 'GET',
headers: {
'content-type': 'application/json',
serverId: process.env.NEXT_PUBLIC_SERVER_ID as string,
Authorization: `Bearer ${accessToken}`,
},
cache: 'no-cache',
},
);
const chatUsersObject: chatUsersObject = await res.json();
const chatUsers: Chat = chatUsersObject.chat;

setChatUsers(chatUsers);
};

fetchChatUsers();

socket.on('connect', () => {
socket.on('messages-to-client', (responseData) => {
setMessages(responseData.messages);
});

socket.on('message-to-client', (messageObject) => {
setMessages((prevMessages) => [...prevMessages, messageObject]);
});

socket.emit('fetch-messages');
});

socket.on('connect_error', (error) => {
throw error;
});

return () => {
socket.off('message-to-client');
socket.off('messages-to-client');
};
}, [accessToken, chatId, socket]);

// 서버로 메시지 전송
const sendMessage = () => {
if (newMessage.trim() !== '') {
socket.emit('message-to-server', newMessage);
setNewMessage('');
}
};

// enter 입력 시 메시지 전송
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
sendMessage();
}
};

return (
<>
<ChatHeader
socket={socket}
chatId={chatId}
chatName={chatUsers.name}
chatUsers={chatUsers.users.length}
/>

{privateValue === 'true' ? (
<>
{chatUsers.users.length === 2 || chatUsers.users.length === 1 ? (
<>
<p>1대1 채팅방 입니다.</p>
<RenderChats
messages={messages}
chatUsers={chatUsers}
useModal={false}
/>
</>
) : (
<>
<p> true 그룹 채팅방 입니다.</p>
<RenderChats
messages={messages}
chatUsers={chatUsers}
useModal={true}
/>
</>
)}
</>
) : (
<>
<p> false 오픈 채팅방 입니다. </p>
<RenderChats
messages={messages}
chatUsers={chatUsers}
useModal={false}
/>
</>
)}

<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={handleKeyPress}
/>
<button onClick={sendMessage}>전송</button>
</>
);
};

export default ChatRoom;
50 changes: 50 additions & 0 deletions Components/Chat/Chats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useRouter } from 'next/navigation';
import Image from 'next/image';
import { Message, User } from '@/types';

const Chats = ({
message,
user,
myId,
useModal,
}: {
message: Message;
user: User;
myId: string;
useModal: boolean;
}) => {
const router = useRouter();

const openProfile = () => {
router.push(`/profile/${user.id}?isMyProfile=false`);
};

return (
<>
{user.id === myId ? null : (
<>
{useModal ? (
<Image
src={user.picture}
width={50}
height={50}
alt="User Picture"
onClick={openProfile}
/>
) : (
<Image
src={user.picture}
width={50}
height={50}
alt="User Picture"
/>
)}
</>
)}
<p>{user.username}</p>
<p>{message.text}</p>
</>
);
};

export default Chats;
72 changes: 72 additions & 0 deletions Components/Chat/RenderChats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { getCookie } from '@/Components/Login/Cookie';
import React, { useEffect, useRef } from 'react';
import { Chat, Message } from '@/types';
import Chats from '@/Components/Chat/Chats';

const RenderChats = ({
messages,
chatUsers,
useModal,
}: {
messages: Message[];
chatUsers: Chat;
useModal: boolean;
}) => {
const myId = getCookie('userId');
const messageEndRef = useRef<HTMLDivElement>(null);

// 새로운 메세지 전송시 하단 스크롤
useEffect(() => {
const setTimeoutId = setTimeout(() => {
if (messageEndRef.current) {
messageEndRef.current.scrollIntoView({ behavior: 'smooth' });
}
}, 500);

return () => {
clearTimeout(setTimeoutId);
};
}, [messages.length]);

return (
<>
<ul>
{messages.map((message, index) => {
const myUser = chatUsers.users.find(
(user) => user.id === message.userId,
);

if (myUser) {
return (
<li
key={message.id}
className={
message.userId === myId ? 'bg-pink-500' : 'bg-pink-100'
}
>
{index === 0 ||
new Date(message.createdAt).toDateString() !==
new Date(messages[index - 1].createdAt).toDateString() ? (
<div className="h-[25px] bg-blue-300">
{new Date(message.createdAt).toDateString()}
</div>
) : null}

<Chats
key={message.id}
message={message}
user={myUser}
myId={myId}
useModal={useModal}
/>
</li>
);
}
})}
<div ref={messageEndRef}></div>
</ul>
</>
);
};

export default RenderChats;
13 changes: 3 additions & 10 deletions Components/Search/OpenChatModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ const OpenChatModal = ({ modalChat }: { modalChat: Chat }) => {
},
body: JSON.stringify({ chatId: process.env.NEXT_PUBLIC_CHAT_ID }),
});
// http://localhost:3000/chat/id 로 이동하기
/// 이동 후 id에서 채팅 데이터 다시 fetch 하기
// 에러 처리 필요
router.push(`chat/${modalChat.id}?isPrivate=false`);

router.push(`/chat/${modalChat.id}?isPrivate=false`);
};

return (
Expand Down Expand Up @@ -67,12 +65,7 @@ const OpenChatModal = ({ modalChat }: { modalChat: Chat }) => {
<div className="h-4/6 ml-5 text-white">
<OpenChatText openChat={modalChat} textSize={TEXT_SIZE} />
</div>
<button
className="h-1/6 bg-yellow-500 font-medium"
onClick={() => {
router.push(`/chat/${modalChat.id}?isPrivate=false`);
}}
>
<button className="h-1/6 bg-yellow-500 font-medium" onClick={joinChat}>
오픈 채팅방 참여하기
</button>

Expand Down
Loading

0 comments on commit f8a2257

Please sign in to comment.