Skip to content
Merged
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
54 changes: 17 additions & 37 deletions src/components/Chat/ChatContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { getProfileInfoSelector } from '../../../reducer/profile';
import ChatZone from '../ChatZone';
import Textarea from '../Textarea';
import useSocket from '../../../hooks/useSocket';
import { chatSection } from '../../../utils/chatSection';
import { getChatData, newChatData } from '../../../utils/ChatSocketData';
import { getAllMessages, getChatDataSelector, sendMessages } from '../../../reducer/chat';

import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useParams } from 'react-router';
import { Scrollbars } from 'react-custom-scrollbars';

import { AllMessagesDataType, ChatDataType, ChatIdType, ChatUpdateDataType } from '../../../types/types';

const WorkSpaceChat = (): JSX.Element => {
const profileInfo = useSelector(getProfileInfoSelector);
const chatData = useSelector(getChatDataSelector);

const { projectUrl, part: room } = useParams<{ projectUrl: string; part: string }>();
const history = useHistory();
const dispatch = useDispatch();
const currentAddress = history.location.pathname.split('/')[3];
const [socket, connectionSocket, disconnectSocket] = useSocket(projectUrl, currentAddress);
const scrollbarRef = useRef<Scrollbars>(null);
Expand Down Expand Up @@ -46,9 +49,9 @@ const WorkSpaceChat = (): JSX.Element => {
}

setIsEnd(isEnd);
setChatBucket([...chats, ...chatBucket]);
dispatch(getAllMessages(chats));
});
}, [chatBucket]);
}, [chatData]);

useEffect(() => {
// TODO: room이 바뀌면 room과 다시 연결한다.
Expand All @@ -67,7 +70,7 @@ const WorkSpaceChat = (): JSX.Element => {
// TODO: 메세지를 받으면 재렌더링 한다.
socket?.on('sendMessage', (chat: ChatDataType) => {
if (chat) {
setChatBucket([...chatBucket, chat]);
dispatch(sendMessages([chat]));
}
});

Expand Down Expand Up @@ -97,17 +100,17 @@ const WorkSpaceChat = (): JSX.Element => {
// TODO: 채팅 이미지 업로드
socket?.on('sendImage', ({ chat }: ChatDataType) => {
if (chat?.uploadImage) {
setChatBucket([...chatBucket, chat]);
dispatch(sendMessages([chat]));
}
});
}, [chatBucket]);
}, [chatData]);

// TODO: 스크롤바는 항상 맨 밑에 위치한다.
useEffect(() => {
if (chatBucket && order === 0) {
if (chatData && order === 0) {
scrollbarRef.current?.scrollToBottom();
}
}, [chatBucket]);
}, [chatData]);

// TODO: 채팅 입력
const onSubmitForm = useCallback(
Expand All @@ -117,54 +120,31 @@ const WorkSpaceChat = (): JSX.Element => {
return;
}

const data: ChatUpdateDataType = getChatData(room, profileInfo, chat, chatBucket.length);
const data: ChatUpdateDataType = getChatData(room, profileInfo, chat, chatData.length);
const newChat: ChatDataType = newChatData(-1, chat, '', room, profileInfo);

socket?.emit('sendMessage', data);

if (chat) {
setChatBucket([...chatBucket, newChat]);
dispatch(sendMessages([newChat]));
setChat('');
}

if (scrollbarRef.current) {
scrollbarRef.current.scrollToBottom();
}
},
[chat, chatBucket],
[chat, chatData],
);

const onChangeChatValue: React.ChangeEventHandler<HTMLTextAreaElement> = useCallback((e): void => {
setChat(e.target.value);
}, []);

const chatSections = chatSection(chatBucket ? [...chatBucket] : []);
const isEmpty = chatBucket.length === 0;
const isReachingEnd = isEmpty || (chatBucket && chatBucket.length < 30);

return (
<>
<ChatZone
socket={socket}
scrollbarRef={scrollbarRef}
chatSections={chatSections}
chatBucket={chatBucket}
setChatBucket={setChatBucket}
order={order}
setOrder={setOrder}
isEnd={isEnd}
isEmpty={isEmpty}
isReachingEnd={isReachingEnd}
/>
<Textarea
socket={socket}
onSubmitForm={onSubmitForm}
onChangeChat={onChangeChatValue}
chat={chat}
chatBucket={chatBucket}
setChatBucket={setChatBucket}
placeholder={`${room}에게 메세지 보내기`}
/>
<ChatZone socket={socket} scrollbarRef={scrollbarRef} order={order} setOrder={setOrder} isEnd={isEnd} />
<Textarea socket={socket} onSubmitForm={onSubmitForm} onChangeChat={onChangeChatValue} chat={chat} />
</>
);
};
Expand Down
47 changes: 24 additions & 23 deletions src/components/Chat/ChatItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { Dispatch, memo, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import ProfileImage from '../../Common/ProfileImage';
import ChatProfileModal from '../ChatProfileModal';
import DeleteAlert from '../DeleteAlert';
import useInput from '../../../hooks/useInput';
import { getProfileInfoSelector } from '../../../reducer/profile';
import { getChatDeleteData, getChatEditData, newChatData } from '../../../utils/ChatSocketData';
import { getChatDataSelector } from '../../../reducer/chat';

import { Socket } from 'socket.io-client';
import dayjs from 'dayjs';
Expand Down Expand Up @@ -35,14 +36,14 @@ import { ChatDataType, ChatUpdateDataType } from '../../../types/types';
interface Props {
socket: Socket<DefaultEventsMap, DefaultEventsMap> | undefined;
data: ChatDataType;
chatBucket: ChatDataType[];
setChatBucket: Dispatch<SetStateAction<ChatDataType[]>>;
isSameSender: boolean;
index: number;
}

const ChatItem = ({ socket, data, chatBucket, setChatBucket, isSameSender, index }: Props): JSX.Element => {
const ChatItem = ({ socket, data, isSameSender, index }: Props): JSX.Element => {
const profileInfo = useSelector(getProfileInfoSelector);
const chatData = useSelector(getChatDataSelector);

const { part: room } = useParams<{ projectUrl: string; part: string }>();
const textareaRef = useRef<HTMLTextAreaElement>(null);
const modalRef = useRef<HTMLDivElement>(null);
Expand All @@ -55,20 +56,23 @@ const ChatItem = ({ socket, data, chatBucket, setChatBucket, isSameSender, index

const { uploadImage, profileColor, name, email } = data.writer;

useEffect(() => {
if (textareaRef.current) {
autosize(textareaRef.current);
const onClickChatItem = useCallback(() => {
if (modalRef.current) {
const location = modalRef.current.getBoundingClientRect();
if (location.y > 520) {
setChatLocation(`-${String(location.y - 520)}px`);
}
}
}, [textareaRef.current]);
}, []);

// TODO: 채팅 수정 엔터
const onChatEditEnter = useCallback((): void => {
const getChatEdit: ChatUpdateDataType = getChatEditData(room, index, data.id, editChat);
const newChat: ChatDataType = newChatData(data.id, editChat, '', room, profileInfo);

const copyChatBucket = [...chatBucket];
const copyChatBucket = [...chatData];
copyChatBucket.splice(index, 1, newChat);
setChatBucket([...copyChatBucket]);
// setChatBucket([...copyChatBucket]);

socket?.emit('editMessage', getChatEdit);
setShowChatEditForm(false);
Expand All @@ -82,9 +86,9 @@ const ChatItem = ({ socket, data, chatBucket, setChatBucket, isSameSender, index
const getChatEdit: ChatUpdateDataType = getChatEditData(room, index, data.id, editChat);
const newChat: ChatDataType = newChatData(data.id, editChat, '', room, profileInfo);

const copyChatBucket = [...chatBucket];
copyChatBucket.splice(index, 1, newChat);
setChatBucket([...copyChatBucket]);
const copyChatBucket = [...chatData];
copyChatBucket[index] = newChat;
// setChatBucket([...copyChatBucket]);

socket?.emit('editMessage', getChatEdit);
setShowChatEditForm(false);
Expand All @@ -109,12 +113,12 @@ const ChatItem = ({ socket, data, chatBucket, setChatBucket, isSameSender, index
setShowChatDeleteAlert(false);

const getChatDelete = getChatDeleteData(room, index, data.id);
const copyChatBucket = [...chatBucket];
const copyChatBucket = [...chatData];
copyChatBucket.splice(index, 1);
setChatBucket([...copyChatBucket]);
// setChatBucket([...copyChatBucket]);
socket?.emit('deleteMessage', getChatDelete);
},
[chatBucket],
[chatData],
);

// TODO: 채팅 프로필 모달 실행
Expand All @@ -136,14 +140,11 @@ const ChatItem = ({ socket, data, chatBucket, setChatBucket, isSameSender, index
};
}, []);

const onClickChatItem = useCallback(() => {
if (modalRef.current) {
const location = modalRef.current.getBoundingClientRect();
if (location.y > 520) {
setChatLocation(`-${String(location.y - 520)}px`);
}
useEffect(() => {
if (textareaRef.current) {
autosize(textareaRef.current);
}
}, []);
}, [textareaRef.current]);

return (
<ChatWrapper isSameSender={isSameSender} ref={modalRef} onClick={onClickChatItem}>
Expand Down
51 changes: 17 additions & 34 deletions src/components/Chat/ChatZone/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { onDragUploadImage } from '../../../utils/imageUpload';
import { getProfileInfoSelector } from '../../../reducer/profile';
import { getChatUploadImageData, newChatData } from '../../../utils/ChatSocketData';
import DragUploadModal from '../DragUploadModal';
import { chatSection } from '../../../utils/chatSection';
import { getChatDataSelector, sendMessages } from '../../../reducer/chat';

import { Socket } from 'socket.io-client';
import { Scrollbars } from 'react-custom-scrollbars';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router';

import { ChatList, ChatZoneContainer, ChatDateHeader, DragOverZone } from './styles';
Expand All @@ -18,34 +20,25 @@ import { ChatDataType, ChatSectionType, ChatUpdateDataType } from '../../../type
export interface Props {
socket: Socket<DefaultEventsMap, DefaultEventsMap> | undefined;
scrollbarRef: RefObject<Scrollbars>;
chatSections: ChatSectionType;
chatBucket: ChatDataType[];
setChatBucket: Dispatch<SetStateAction<ChatDataType[]>>;
order: number;
setOrder: Dispatch<SetStateAction<number>>;
isEnd: boolean;
isEmpty: boolean;
isReachingEnd: boolean;
}

const ChatZone = ({
socket,
scrollbarRef,
chatSections,
chatBucket,
setChatBucket,
order,
setOrder,
isEnd,
isEmpty,
isReachingEnd,
}: Props): JSX.Element => {
const ChatZone = ({ socket, scrollbarRef, order, setOrder, isEnd }: Props): JSX.Element => {
const profileInfo = useSelector(getProfileInfoSelector);
const chatData = useSelector(getChatDataSelector);

const dispatch = useDispatch();
const { part: room } = useParams<{ part: string }>();

const [dragOver, setDragOver] = useState<boolean>(false);
const [chatUploadImage, setChatUploadImage] = useState<string>('');

const chatSections: ChatSectionType = chatSection(chatData ? [...chatData] : []);
const isEmpty = chatData.length === 0;
const isReachingEnd = isEmpty || (chatData && chatData.length < 30);

// TODO: 채팅 인피니티 스크롤
const onScrollFrame = useCallback(
values => {
Expand All @@ -68,7 +61,7 @@ const ChatZone = ({
const newChat: ChatDataType = newChatData(-1, '', chatUploadImage, room, profileInfo);
const getImageData: ChatUpdateDataType = getChatUploadImageData(room, profileInfo, chatUploadImage);

setChatBucket([...chatBucket, newChat]);
dispatch(sendMessages([newChat]));
socket?.emit('sendImage', getImageData);
}
}, [chatUploadImage]);
Expand Down Expand Up @@ -97,29 +90,19 @@ const ChatZone = ({
return (
<ChatZoneContainer onDrop={onDrop} onDragOver={onDragOver} onDragLeave={onDragLeave}>
<Scrollbars autoHide ref={scrollbarRef} onScrollFrame={onScrollFrame}>
{Object.entries(chatSections).map(([date, chatsBucket]) => {
{Object.entries(chatSections).map(([date, chatData]) => {
return (
<ChatList key={date}>
<ChatDateHeader>
<button>{date}</button>
</ChatDateHeader>
{chatsBucket.length > 0 &&
chatsBucket.map((chat: ChatDataType, index: number) => {
{chatData.length > 0 &&
chatData.map((chat: ChatDataType, index: number) => {
let isSameSender = false;
if (index > 0) {
isSameSender = chat.writer.email === chatsBucket[index - 1].writer.email;
isSameSender = chat.writer.email === chatData[index - 1].writer.email;
}
return (
<ChatItem
socket={socket}
key={index}
data={chat}
chatBucket={chatBucket}
setChatBucket={setChatBucket}
index={index}
isSameSender={isSameSender}
/>
);
return <ChatItem socket={socket} key={index} data={chat} index={index} isSameSender={isSameSender} />;
})}
</ChatList>
);
Expand Down
Loading