Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';

import ChatButton from '@/components/chat/ChatButton';
import ChatDetail from '@/components/chat/ChatDetail';
Expand Down Expand Up @@ -41,10 +41,11 @@ const Chat = ({
isUser,
shopImageUrl,
}: ChatProps) => {
// TODO: 커스텀 훅으로 빼기.
const seller: SellerInfo = { sellerId: sellerId, shopName: sellerName };
const product: ProductInfo = { productId: productId, productName: productName };
const [user, setUser] = useState<UserInfo>({ userId: userId, userName: userName });
const [isCustomRoomId, setIsCustomRoomId] = useState<boolean>(true);
const [isNowChatRoom, setIsNowChatRoom] = useState<boolean>(false);

const isSeller = isUser;
const role = isSeller ? 'seller' : 'user';
Expand All @@ -65,45 +66,43 @@ const Chat = ({
return customRoomId;
}

/** clickPrevButton() : 채팅방에서 뒤로가기 버튼 누르면
* 1. 분기처리를 위해 customRoomId값을 빈 스트링을 넣는다. */
/** clickPrevButton() : 채팅방에서 뒤로가기 버튼 */
const clickPrevButton = () => {
setCustomRoomId('');
setIsNowChatRoom((prev) => !prev);
};

/** clickListBox() : 채팅list에서 해당 채팅방으로 들어가기 위해
* 1. isCustomRoomId를 false,
* 1. isNowChatRoom로 분기처리,
* 2. ChatBody.tsx에서 custumroomId를 받아와서 ChatList.tsx로 넘겨줌.
* 3. seller일때 현재 디테일에서 넘어오는 userId, userName값은 판매자의 정보여서 ChatList.tsx에서
문의한 구매자의 userId값, userName값을 가져옴.
*/
const clickListBox = (customRoomId: string, userId: number, userName: string) => {
setIsCustomRoomId((prev) => !prev);
setIsNowChatRoom((prev) => !prev);
setCustomRoomId(customRoomId);
setUser({ ...user, userId, userName });
};

const handleOpen = () => {
setIsModalOpen((prev) => !prev);
setIsNowChatRoom(false);
};

useEffect(() => {
if (customRoomId.length === 0) {
setIsCustomRoomId((prev) => !prev);
}
}, [customRoomId]);
console.log('isNowChatRoom', isNowChatRoom);

return (
<>
<ChatButton handleOpen={handleOpen} />
{isModalOpen && isSeller && (
<S.Chat>
{isCustomRoomId ? (
{!isNowChatRoom ? (
<ChatList
handleOpen={handleOpen}
clickListBox={clickListBox}
seller={seller}
user={user}
isSeller={isSeller}
role={role}
product={product}
/>
) : (
Expand All @@ -122,12 +121,14 @@ const Chat = ({
)}
{isModalOpen && !isSeller && (
<S.Chat>
{!isCustomRoomId ? (
{isNowChatRoom ? (
<ChatList
handleOpen={handleOpen}
clickListBox={clickListBox}
seller={seller}
user={user}
isSeller={isSeller}
role={role}
product={product}
/>
) : (
Expand Down
32 changes: 23 additions & 9 deletions src/components/chat/ChatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useEffect, useState } from 'react';
import { sellerChatList, userChatList } from '@/apis/chat';
import ChatBody from '@/components/chat/chatList/ChatBody';
import ChatHeader from '@/components/chat/chatList/ChatHeader';
import { Message } from '@/models/chat';
import { useSSE } from '@/hooks/useSSE';
import { Message, UserInfo } from '@/models/chat';

export type Chat = {
[timestamp: string]: Message;
Expand All @@ -23,30 +24,42 @@ export type List = {
lastChat: Message;
};

type chatProps = {
type ChatProps = {
clickListBox: (customRoomId: string, userId: number, userName: string) => void;
handleOpen: () => void;
seller: {
sellerId: number;
shopName: string;
};
user: UserInfo;
product: {
productId: number;
productName: string;
};
isSeller: boolean;
role: string;
};

const ChatList = ({ handleOpen, clickListBox, seller, isSeller, product }: chatProps) => {
const ChatList = ({
handleOpen,
clickListBox,
seller,
user,
isSeller,
product,
role,
}: ChatProps) => {
const [list, setList] = useState<List[]>([]);

const [shopImg, setShopImg] = useState<string>('');

const message = useSSE(role, seller.sellerId, user.userId);

//TODO: 리액트쿼리로 변경하기
const loadUserChatList: () => Promise<void> = async () => {
const sellerId = seller.sellerId;
userChatList(sellerId)
userChatList(seller.sellerId)
.then((resData) => {
const data = resData.chatList;
console.log('list', data);
const img = resData.shopImage;
setList([...data]);
setShopImg(img);
Expand All @@ -62,6 +75,7 @@ const ChatList = ({ handleOpen, clickListBox, seller, isSeller, product }: chatP
sellerChatList(sellerId, productId)
.then((resData) => {
const data = resData.chatList;
console.log('list', data);
const img = resData.shopImage;
setList([...data]);
setShopImg(img);
Expand All @@ -77,10 +91,10 @@ const ChatList = ({ handleOpen, clickListBox, seller, isSeller, product }: chatP
}, [isSeller]);

return (
<div>
<>
<ChatHeader shopName={seller.shopName} shopImg={shopImg} handleOpen={handleOpen} />
<ChatBody chatList={list} clickListBox={clickListBox} />
</div>
<ChatBody chatList={list} clickListBox={clickListBox} newMessage={message} />
</>
);
};

Expand Down
10 changes: 8 additions & 2 deletions src/components/chat/chatList/ChatBody.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { List } from '@/components/chat/ChatList';
import ChatBox from '@/components/chat/chatList/ChatBox';
import { ChatAlarm } from '@/hooks/useSSE';
import * as S from '../Chat.styles';

type ChatBodyProps = {
chatList: List[];
clickListBox: (customRoomId: string, userId: number, userName: string) => void;
newMessage?: ChatAlarm;
};

const ChatBody = ({ chatList, clickListBox }: ChatBodyProps) => {
const ChatBody = ({ chatList, clickListBox, newMessage }: ChatBodyProps) => {
return (
<S.ChatBody>
{chatList?.map((item, idx) => {
Expand All @@ -17,7 +19,11 @@ const ChatBody = ({ chatList, clickListBox }: ChatBodyProps) => {
key={idx}
>
<ChatBox
lastChat={item.lastChat.content}
lastChat={
newMessage?.customRoomId === item.customRoomId
? newMessage?.content
: item.lastChat.content
}
sender={item?.lastChat.sender}
productName={item.productName}
image={item.imageUrl}
Expand Down
45 changes: 45 additions & 0 deletions src/hooks/useSSE.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useEffect, useState } from 'react';

export type ChatAlarm = {
content: string;
createAt: string;
customRoomId: string;
role: string;
sellerId: number;
sender: string;
};

//TODO: useSSE를 쓰는 컴포넌트에서 두번 렌더링 되는 이유 찾기.(최상단부터 뒤져보기)
export const useSSE = (role: string, sellerId: number, userId: number) => {
const [message, setMessage] = useState<ChatAlarm>();

const isUser =
role === 'user'
? `${import.meta.env.VITE_API_SSE_URL}/chat-alarm/${role}/${sellerId}/${userId}`
: `${import.meta.env.VITE_API_SSE_URL}/chat-alarm/${role}/${sellerId}`;

const [eventSource] = useState(() => new EventSource(isUser));

useEffect(() => {
console.log('되나', eventSource);
eventSource.addEventListener('sse', function (event) {
const message = JSON.parse(event.data);
console.log('새로운 채팅 알람: ', message);
setMessage({ ...message });
});

eventSource.onerror = function (error) {
console.error('EventSource failed:', error);
eventSource.close();
};

window.addEventListener('unload', function () {
if (eventSource) {
eventSource.close();
console.log('EventSource closed');
}
});
}, []);

return message;
};