diff --git a/frontend/src/apis/fetchChatRule.ts b/frontend/src/apis/fetchChatRule.ts new file mode 100644 index 00000000..38205857 --- /dev/null +++ b/frontend/src/apis/fetchChatRule.ts @@ -0,0 +1,17 @@ +import { AxiosResponse } from 'axios'; +import { fetchInstance } from '.'; + +export type ChatRuleResponse = { + notice: string; + channelName: string; +}; + +export const fetchChatRule = async ({ sessionKey }: { sessionKey: string }): Promise => { + const response: AxiosResponse = await fetchInstance().get('/streams/notice', { + params: { + sessionKey + } + }); + + return response.data; +}; diff --git a/frontend/src/apis/queries/chat/useFetchChatRule.ts b/frontend/src/apis/queries/chat/useFetchChatRule.ts new file mode 100644 index 00000000..1ebf19c1 --- /dev/null +++ b/frontend/src/apis/queries/chat/useFetchChatRule.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; +import { ChatRuleResponse, fetchChatRule } from '@apis/fetchChatRule'; + +export const useFetchChatRule = ({ sessionKey }: { sessionKey: string }) => { + return useQuery({ + queryKey: ['chatRule'], + queryFn: () => fetchChatRule({ sessionKey }), + refetchOnWindowFocus: false + }); +}; diff --git a/frontend/src/components/chat/ChatList.tsx b/frontend/src/components/chat/ChatList.tsx index 01af0262..8418551d 100644 --- a/frontend/src/components/chat/ChatList.tsx +++ b/frontend/src/components/chat/ChatList.tsx @@ -18,7 +18,7 @@ const ChatItemWrapper = memo( if (chat.msgType === CHATTING_TYPES.QUESTION) { return ( - + ); } else if (chat.msgType === CHATTING_TYPES.NOTICE) { @@ -43,12 +43,8 @@ const ChatItemWrapper = memo( return ( - {chat.owner === 'me' ? ( - ๐Ÿง€ - ) : chat.owner === 'host' ? ( - - ) : null} - + + {chat.owner === 'me' ? '๐Ÿง€' : chat.owner === 'host' ? : null} {chat.nickname} {chat.msg} @@ -171,6 +167,15 @@ const NormalChat = styled.div<{ $isHost: boolean; $pointColor: string }>` line-height: 1.5; } + .user_name { + cursor: pointer; + padding: 2px; + border-radius: 5px; + &:hover { + background-color: #393939; + } + } + overflow-wrap: break-word; word-break: break-word; `; diff --git a/frontend/src/components/chat/ChatQuestionSection.tsx b/frontend/src/components/chat/ChatQuestionSection.tsx index e3e3c839..e4ac2ee5 100644 --- a/frontend/src/components/chat/ChatQuestionSection.tsx +++ b/frontend/src/components/chat/ChatQuestionSection.tsx @@ -1,10 +1,11 @@ import { memo, useState, useCallback } from 'react'; import styled from 'styled-components'; import QuestionCard from './QuestionCard'; -import { MessageReceiveData, MessageSendData } from '@type/chat'; +import { MessageReceiveData, MessageSendData, UserInfoData } from '@type/chat'; import { CHATTING_SOCKET_SEND_EVENT } from '@constants/chat'; import { getStoredId } from '@utils/id'; import { UserType } from '@type/user'; +import { useChat } from '@contexts/chatContext'; export interface ChatQuestionSectionProps { questions: MessageReceiveData[]; @@ -38,6 +39,18 @@ const ChatQuestionSection = ({ questions, worker, userType, roomId }: ChatQuesti [worker, roomId, userId] ); + const { dispatch } = useChat(); + + const onNicknameClick = useCallback( + (data: UserInfoData) => { + dispatch({ + type: 'SET_SELECTED_USER', + payload: data + }); + }, + [dispatch] + ); + return ( @@ -51,18 +64,32 @@ const ChatQuestionSection = ({ questions, worker, userType, roomId }: ChatQuesti question={questions[0]} handleQuestionDone={handleQuestionDone} ellipsis={!expanded} + onNicknameClick={() => + onNicknameClick({ + nickname: questions[0].nickname, + socketId: questions[0].socketId, + entryTime: questions[0].entryTime, + owner: questions[0].owner + }) + } /> {expanded && - questions - .slice(1) - .map((question) => ( - - ))} + questions.slice(1).map((question) => ( + + onNicknameClick({ + nickname: question.nickname, + socketId: question.socketId, + entryTime: question.entryTime, + owner: question.owner + }) + } + /> + ))} )} diff --git a/frontend/src/components/chat/ChatRoomLayout.tsx b/frontend/src/components/chat/ChatRoomLayout.tsx index 7ff41ee2..cc4091b9 100644 --- a/frontend/src/components/chat/ChatRoomLayout.tsx +++ b/frontend/src/components/chat/ChatRoomLayout.tsx @@ -49,7 +49,7 @@ const ChatRoomLayout = ({ userType, roomId }: ChatRoomLayoutProps) => { {state.isNoticePopupOpen && ( - + )} diff --git a/frontend/src/components/chat/NoticeCard.tsx b/frontend/src/components/chat/NoticeCard.tsx index 4ff86478..eefdd598 100644 --- a/frontend/src/components/chat/NoticeCard.tsx +++ b/frontend/src/components/chat/NoticeCard.tsx @@ -1,14 +1,17 @@ import styled from 'styled-components'; import CloseIcon from '@assets/icons/close.svg'; -import { useContext } from 'react'; +import { memo, useCallback, useContext } from 'react'; import { ChatContext } from 'src/contexts/chatContext'; +import { useFetchChatRule } from '@apis/queries/chat/useFetchChatRule'; -export const NoticeCard = () => { +export const NoticeCard = ({ sessionKey }: { sessionKey: string }) => { const { dispatch } = useContext(ChatContext); - const toggleSettings = () => { + const toggleSettings = useCallback(() => { dispatch({ type: 'TOGGLE_ANNOUNCEMENT_POPUP' }); - }; + }, [dispatch]); + + const { data: noticeInfo } = useFetchChatRule({ sessionKey }); return ( @@ -17,10 +20,10 @@ export const NoticeCard = () => {
- ๋„ค์ด๋ฒ„ ๋ถ€์ŠคํŠธ ์บ ํ”„ + {noticeInfo?.channelName} ๋‹˜์˜
-
์ปจํผ๋Ÿฐ์Šค ๊ณต์ง€ ๐Ÿ“ข
+
์ฑ„ํŒ… ๊ทœ์น™ ๐Ÿ“ข
@@ -28,17 +31,11 @@ export const NoticeCard = () => { - - - ์งˆ๋ฌธ์€ ์งˆ๋ฌธ ์ฑ„ํŒ…์œผ๋กœ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค -
- ์ปจํผ๋Ÿฐ์Šค ๋ณด๋Ÿฌ์™€์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค -
- ์ปจํผ๋Ÿฐ์Šค ๋ณด๋Ÿฌ์™€์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค -
- ์ปจํผ๋Ÿฐ์Šค ๋ณด๋Ÿฌ์™€์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค -
- ์ปจํผ๋Ÿฐ์Šค ๋ณด๋Ÿฌ์™€์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค -
+ {noticeInfo?.notice}
); }; -export default NoticeCard; +export default memo(NoticeCard); const NoticeCardContainer = styled.div` display: flex; @@ -113,5 +110,7 @@ const NoticeMessage = styled.p` margin-top: 10px; max-height: 170px; overflow-y: auto; - ${({ theme }) => theme.tokenTypographys['display-bold14']} + ${({ theme }) => theme.tokenTypographys['display-bold14']}; + white-space: pre-line; + word-break: keep-all; `; diff --git a/frontend/src/components/chat/QuestionCard.tsx b/frontend/src/components/chat/QuestionCard.tsx index cd150b36..006e5039 100644 --- a/frontend/src/components/chat/QuestionCard.tsx +++ b/frontend/src/components/chat/QuestionCard.tsx @@ -1,4 +1,4 @@ -import { memo, useEffect, useMemo, useRef } from 'react'; +import { memo, useEffect, useMemo, useRef, useCallback } from 'react'; import styled from 'styled-components'; import CheckIcon from '@assets/icons/check.svg'; import { MessageReceiveData } from '@type/chat'; @@ -10,9 +10,10 @@ interface QuestionCardProps { question: MessageReceiveData; handleQuestionDone?: (questionId: number) => void; ellipsis?: boolean; + onNicknameClick: () => void; } -export const QuestionCard = ({ type, question, handleQuestionDone, ellipsis = false }: QuestionCardProps) => { +const QuestionCard = ({ type, question, handleQuestionDone, onNicknameClick, ellipsis = false }: QuestionCardProps) => { const startDateFormat = useMemo(() => new Date(question.msgTime), [question.msgTime]); const nowRef = useRef(new Date()); @@ -36,11 +37,21 @@ export const QuestionCard = ({ type, question, handleQuestionDone, ellipsis = fa const timeElement = useRef(null); + const handleQuestionDoneMemoized = useCallback(() => { + if (handleQuestionDone) { + handleQuestionDone(question.questionId as number); + } + }, [handleQuestionDone, question.questionId]); + + const onNicknameClickMemoized = useCallback(() => { + onNicknameClick(); + }, [onNicknameClick]); + return ( - + {question.nickname} @@ -48,7 +59,7 @@ export const QuestionCard = ({ type, question, handleQuestionDone, ellipsis = fa {type === 'host' && handleQuestionDone && ( - handleQuestionDone(question.questionId as number)}> + )} @@ -59,7 +70,14 @@ export const QuestionCard = ({ type, question, handleQuestionDone, ellipsis = fa ); }; -export default memo(QuestionCard); +// shouldComponentUpdate๋ฅผ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก memo ์‚ฌ์šฉ +export default memo(QuestionCard, (prevProps, nextProps) => { + return ( + prevProps.question.questionId === nextProps.question.questionId && + prevProps.type === nextProps.type && + prevProps.ellipsis === nextProps.ellipsis + ); +}); const QuestionCardContainer = styled.div` display: flex; @@ -86,7 +104,12 @@ const QuestionInfo = styled.div` align-items: end; gap: 12px; .name_info { - ${({ theme }) => theme.tokenTypographys['display-bold12']} + border-radius: 7px; + ${({ theme }) => theme.tokenTypographys['display-bold12']}; + cursor: pointer; + &:hover { + color: #bbbbbb; + } } .time_info { ${({ theme }) => theme.tokenTypographys['display-medium12']} diff --git a/frontend/src/contexts/chatContext.tsx b/frontend/src/contexts/chatContext.tsx index b568b37d..e024b86a 100644 --- a/frontend/src/contexts/chatContext.tsx +++ b/frontend/src/contexts/chatContext.tsx @@ -70,7 +70,7 @@ const chatReducer = (state: ChatState, action: Action): ChatState => { const initialState: ChatState = { isSettingsOpen: false, settingOption: null, - isNoticePopupOpen: false, + isNoticePopupOpen: true, isUserInfoPopupOpen: false, selectedUser: null };