Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 into dev-fe
  • Loading branch information
spearStr committed Dec 2, 2024
2 parents 87093d9 + a58b3b5 commit 5aa53d5
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 112 deletions.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/user-block.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/src/components/chat/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SpeakerIcon from '@assets/icons/speaker.svg';
import SendIcon from '@assets/icons/send.svg';
import { useRef, useEffect, useState, ChangeEvent, KeyboardEvent, memo } from 'react';
import { CHATTING_SOCKET_SEND_EVENT, CHATTING_TYPES } from '@constants/chat';
import { ChattingTypes } from '@type/chat';
import { ChattingSendTypes } from '@type/chat';
import { getStoredId } from '@utils/id';
import { UserType } from '@type/user';

Expand All @@ -20,7 +20,7 @@ const INITIAL_TEXTAREA_HEIGHT = 20;
export const ChatInput = ({ worker, userType, roomId }: ChatInputProps) => {
const [hasInput, setHasInput] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const [msgType, setMsgType] = useState<ChattingTypes>(CHATTING_TYPES.NORMAL);
const [msgType, setMsgType] = useState<ChattingSendTypes>(CHATTING_TYPES.NORMAL);
const [message, setMessage] = useState('');
const textareaRef = useRef<HTMLTextAreaElement | null>(null);

Expand Down
120 changes: 66 additions & 54 deletions frontend/src/components/chat/ChatList.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,84 @@
import styled from 'styled-components';
import QuestionCard from './QuestionCard';
import { memo, useContext, useEffect, useRef, useState } from 'react';
import { MessageReceiveData } from '@type/chat';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { UserInfoData, MessageReceiveData } from '@type/chat';
import { CHATTING_TYPES } from '@constants/chat';
import { ChatContext } from 'src/contexts/chatContext';
import NoticeCard from './NoticeCard';
import ChatAutoScroll from './ChatAutoScroll';
import HostIconGreen from '@assets/icons/host_icon_green.svg';
import { useChat } from '@contexts/chatContext';

export interface ChatListProps {
messages: MessageReceiveData[];
}

const ChatItemWrapper = memo(({ chat }: { chat: MessageReceiveData }) => {
if (chat.msgType === CHATTING_TYPES.QUESTION) {
return (
<ChatItem>
<QuestionCard type="client" question={chat} />
</ChatItem>
);
} else if (chat.msgType === CHATTING_TYPES.NOTICE) {
return (
<ChatItem>
<NoticeChat>
<span>📢</span>
<span>{chat.msg}</span>
</NoticeChat>
</ChatItem>
);
} else {
return (
<ChatItem>
<NormalChat $isHost={chat.owner === 'host'} $pointColor={chat.owner === 'host' ? '#0ADD91' : chat.color}>
{chat.owner === 'me' ? (
<span className="text_point">🧀</span>
) : chat.owner === 'host' ? (
<StyledIcon as={HostIconGreen} />
) : null}
<span className="text_point">{chat.nickname}</span>
<span className="chat_message">{chat.msg}</span>
</NormalChat>
</ChatItem>
);
const ChatItemWrapper = memo(
({ chat, onNicknameClick }: { chat: MessageReceiveData; onNicknameClick: (data: UserInfoData) => void }) => {
const { nickname, socketId, entryTime, owner } = chat;
const handleNicknameClick = () => onNicknameClick({ nickname, socketId, entryTime, owner });
if (chat.msgType === CHATTING_TYPES.QUESTION) {
return (
<ChatItem>
<QuestionCard type="client" question={chat} />
</ChatItem>
);
} else if (chat.msgType === CHATTING_TYPES.NOTICE) {
return (
<ChatItem>
<NoticeChat>
<span>📢</span>
<span>{chat.msg}</span>
</NoticeChat>
</ChatItem>
);
} else if (chat.msgType === CHATTING_TYPES.EXCEPTION) {
return (
<ChatItem>
<NoticeChat>
<span>🚨</span>
<span>{chat.msg}</span>
</NoticeChat>
</ChatItem>
);
} else {
return (
<ChatItem>
<NormalChat $isHost={chat.owner === 'host'} $pointColor={chat.owner === 'host' ? '#0ADD91' : chat.color}>
{chat.owner === 'me' ? (
<span className="text_point">🧀</span>
) : chat.owner === 'host' ? (
<StyledIcon as={HostIconGreen} />
) : null}
<span className="text_point" onClick={handleNicknameClick}>
{chat.nickname}
</span>
<span className="chat_message">{chat.msg}</span>
</NormalChat>
</ChatItem>
);
}
}
});
);

ChatItemWrapper.displayName = 'ChatItemWrapper';

const ChatList = ({ messages }: ChatListProps) => {
const { state } = useContext(ChatContext);
const [isAtBottom, setIsAtBottom] = useState(true);
const [currentChat, setCurrentChat] = useState<MessageReceiveData | null>(null);

const chatListRef = useRef<HTMLDivElement | null>(null);

const { dispatch } = useChat();

const onNicknameClick = useCallback(
(data: UserInfoData) => {
dispatch({
type: 'SET_SELECTED_USER',
payload: data
});
},
[dispatch]
);

const checkIfAtBottom = () => {
if (!chatListRef.current) return;
const { scrollTop, scrollHeight, clientHeight } = chatListRef.current;
Expand Down Expand Up @@ -83,40 +108,34 @@ const ChatList = ({ messages }: ChatListProps) => {
<ChatListSection>
<ChatListWrapper ref={chatListRef} onScroll={checkIfAtBottom}>
{messages.map((chat, index) => (
<ChatItemWrapper chat={chat} key={index} />
<ChatItemWrapper chat={chat} key={index} onNicknameClick={onNicknameClick} />
))}
</ChatListWrapper>
<ChatAutoScroll currentChat={currentChat} isAtBottom={isAtBottom} scrollToBottom={scrollToBottom} />
{state.isNoticePopupOpen && (
<PopupWrapper>
<NoticeCard />
</PopupWrapper>
)}
</ChatListSection>
);
};

export default ChatList;

const ChatListSection = styled.div`
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-end;
position: relative;
height: 100%;
overflow-y: hidden;
`;

const ChatListWrapper = styled.div`
box-sizing: border-box;
position: absolute;
max-height: 100%;
width: 100%;
display: flex;
flex-direction: column;
padding: 50px 20px 0 20px;
overflow-y: auto;
padding: 50px 20px 0 20px;
scrollbar-width: none;
z-index: 100;
`;

const ChatItem = styled.div`
Expand Down Expand Up @@ -144,6 +163,7 @@ const NormalChat = styled.div<{ $isHost: boolean; $pointColor: string }>`
${({ theme }) => theme.tokenTypographys['display-bold14']};
color: ${({ $pointColor }) => $pointColor};
margin-right: 8px;
cursor: pointer;
}
.chat_message {
Expand All @@ -155,14 +175,6 @@ const NormalChat = styled.div<{ $isHost: boolean; $pointColor: string }>`
word-break: break-word;
`;

const PopupWrapper = styled.div`
position: absolute;
bottom: 0;
left: 5%;
right: 5%;
z-index: 1000;
`;

const StyledIcon = styled.svg`
width: 18px;
height: 18px;
Expand Down
28 changes: 27 additions & 1 deletion frontend/src/components/chat/ChatRoomLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useCallback, useState } from 'react';
import { memo, useCallback, useContext, useState } from 'react';
import styled from 'styled-components';

import ChatHeader from './ChatHeader';
Expand All @@ -9,6 +9,9 @@ import ChatIcon from '@assets/icons/chat_icon.svg';
import { useChatRoom } from '@hooks/useChatRoom';
import { UserType } from '@type/user';
import { getStoredId } from '@utils/id';
import NoticeCard from './NoticeCard';
import { ChatContext } from '@contexts/chatContext';
import UserInfoCard from './UserInfoCard';

interface ChatRoomLayoutProps {
userType: UserType;
Expand All @@ -21,6 +24,8 @@ const ChatRoomLayout = ({ userType, roomId }: ChatRoomLayoutProps) => {
const userId = getStoredId();
const { worker, messages, questions } = useChatRoom(roomId as string, userId);

const { state } = useContext(ChatContext);

const handleCloseChatRoom = useCallback(() => {
setIsChatRoomVisible(false);
}, []);
Expand All @@ -42,6 +47,18 @@ const ChatRoomLayout = ({ userType, roomId }: ChatRoomLayoutProps) => {

<ChatList messages={messages} />

{state.isNoticePopupOpen && (
<PopupWrapper>
<NoticeCard />
</PopupWrapper>
)}

{state.isUserInfoPopupOpen && (
<PopupWrapper>
<UserInfoCard worker={worker} roomId={roomId} userType={userType} />
</PopupWrapper>
)}

<ChatInputContainer>
<ChatInput worker={worker} userType={userType} roomId={roomId} />
</ChatInputContainer>
Expand All @@ -68,6 +85,7 @@ const StyledChatIcon = styled(ChatIcon)`
`;

const ChatRoomContainer = styled.aside<{ $isVisible: boolean }>`
position: relative;
display: ${({ $isVisible }) => ($isVisible ? 'flex' : 'none')};
flex-direction: column;
height: 100%;
Expand All @@ -80,3 +98,11 @@ const ChatRoomContainer = styled.aside<{ $isVisible: boolean }>`
const ChatInputContainer = styled.div`
padding: 10px 20px;
`;

const PopupWrapper = styled.div`
position: absolute;
bottom: 60px;
left: 5%;
right: 5%;
z-index: 1000;
`;
Loading

0 comments on commit 5aa53d5

Please sign in to comment.