From 77097373a32ea0764904012eb1821a81c372767f Mon Sep 17 00:00:00 2001 From: young Date: Fri, 21 Feb 2025 20:27:15 +0900 Subject: [PATCH 01/13] =?UTF-8?q?Fix:=20=EB=A7=A4=EC=B9=AD=20=ED=83=AD?= =?UTF-8?q?=EC=9D=84=20=EC=B1=84=ED=8C=85=20UX=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/matching/dto.ts | 30 +++--- src/pages/Chats/MatchingRoom/index.tsx | 67 ++++++++++++ src/pages/Chats/MatchingRoom/styles.tsx | 47 ++++++++ src/pages/Chats/RecentChat/index.tsx | 19 ++-- src/pages/Chats/TabBar/index.tsx | 136 ------------------------ src/pages/Chats/TabBar/styles.tsx | 55 ---------- src/pages/Chats/index.tsx | 5 +- 7 files changed, 145 insertions(+), 214 deletions(-) create mode 100644 src/pages/Chats/MatchingRoom/index.tsx create mode 100644 src/pages/Chats/MatchingRoom/styles.tsx delete mode 100644 src/pages/Chats/TabBar/index.tsx delete mode 100644 src/pages/Chats/TabBar/styles.tsx diff --git a/src/apis/matching/dto.ts b/src/apis/matching/dto.ts index ccae2fc6..9b5ffb34 100644 --- a/src/apis/matching/dto.ts +++ b/src/apis/matching/dto.ts @@ -18,29 +18,35 @@ export interface CreateMatchingData { targetId: number; } +// 최근 매칭 조회 +export interface LatestMatchingData { + id: number; + requesterId: number; + targetId: number; + requestStatus: 'accepted' | 'rejected' | 'pending'; + createdAt: Date; +} + // 매칭 리스트 조회 // response -export type GetMatchingListResponse = BaseSuccessResponse; - -export interface GetMatchingListData { - hasMatching: boolean; - matchingsCount: number; - matching: MatchingDto[]; -} -export interface MatchingDto { - id: number; // matchingId +export interface MatchingInfoData { + id: number; + message: string; + createdAt: string; + chatRoomId: number; + targetId: number; requester: RequesterDto; } export interface RequesterDto { - id: number; // requesterId + id: number; nickname: string; profilePictureUrl: string; - representativePost: RepresentativePost; + representativePost: RepresentativePostDto; } -export interface RepresentativePost { +export interface RepresentativePostDto { postImages: PostImageDto[]; styleTags: string[]; } diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx new file mode 100644 index 00000000..77968660 --- /dev/null +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -0,0 +1,67 @@ +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import dayjs, { extend } from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +import theme from '@styles/theme'; + +import { LatestMatchingData } from '@apis/matching/dto'; + +import { StyledText } from '@components/Text/StyledText'; + +import { UserImage, MatchingRoomLayout, LeftBox, RightBox, LatestMessage } from './styles'; + +const MatchingRoom: React.FC> = ({ requestStatus, createdAt }) => { + const [timeAgo, setTimeAgo] = useState(null); + const nav = useNavigate(); + extend(relativeTime); + + const handleMatchingRoomClick = () => { + nav(`/matching`); + }; + + useEffect(() => { + if (createdAt) { + // 초기 시간 설정 + setTimeAgo(dayjs(createdAt).locale('ko').fromNow()); + + // 1초마다 `timeAgo`를 업데이트 + const interval = setInterval(() => { + setTimeAgo(dayjs(createdAt).locale('ko').fromNow()); + }, 1000); + + // 컴포넌트 언마운트 시 타이머 정리 + return () => clearInterval(interval); + } else { + setTimeAgo(null); + } + }, []); + + return ( + + + + + 오딩이 + + + {requestStatus === 'pending' + ? '얘가 너 소개해 달래' + : requestStatus === 'rejected' + ? 'ㅠㅠ 담에 더 좋은 애 소개해 줄게' + : requestStatus === 'accepted' + ? '한번 연락해 봐봐' + : ''} + + + + + {timeAgo} + + + + ); +}; + +export default MatchingRoom; diff --git a/src/pages/Chats/MatchingRoom/styles.tsx b/src/pages/Chats/MatchingRoom/styles.tsx new file mode 100644 index 00000000..a6dbdb5c --- /dev/null +++ b/src/pages/Chats/MatchingRoom/styles.tsx @@ -0,0 +1,47 @@ +import { styled } from 'styled-components'; + +import { StyledText } from '@components/Text/StyledText'; + +export const MatchingRoomLayout = styled.li` + width: 100%; + display: grid; + grid-template-columns: auto 1fr auto; + margin: 0 auto; + cursor: pointer; +`; + +export const UserImage = styled.img` + width: 3.25rem; + height: 3.25rem; + object-fit: cover; + border-radius: 50%; + box-shadow: + 0px 1px 2px 0px rgba(0, 0, 0, 0.12), + 0px 0px 1px 0px rgba(0, 0, 0, 0.08), + 0px 0px 1px 0px rgba(0, 0, 0, 0.08); +`; + +export const LeftBox = styled.div` + margin: 0.2rem 0.5rem; + display: flex; + flex-direction: column; + gap: 0.3rem; + overflow: hidden; +`; + +export const LatestMessage = styled(StyledText)` + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + overflow-wrap: break-word; + text-overflow: ellipsis; +`; + +export const RightBox = styled.div` + margin: 0.1rem 0 0.1rem auto; + text-align: right; + display: flex; + flex-direction: column; + justify-content: space-between; +`; diff --git a/src/pages/Chats/RecentChat/index.tsx b/src/pages/Chats/RecentChat/index.tsx index b0de9f2b..c281dfb8 100644 --- a/src/pages/Chats/RecentChat/index.tsx +++ b/src/pages/Chats/RecentChat/index.tsx @@ -1,9 +1,8 @@ import { useEffect, useState } from 'react'; -import SwiperCore from 'swiper'; - import theme from '@styles/theme'; +import { LatestMatchingData } from '@apis/matching/dto'; import { useSocket } from '@context/SocketProvider'; import { getCurrentUserId } from '@utils/getCurrentUserId'; @@ -13,16 +12,13 @@ import { StyledText } from '@components/Text/StyledText'; import type { ChatRoomData } from '@apis/chatting/dto'; import ChatRoomItem from '../ChatRoomItem/index'; +import MatchingRoom from '../MatchingRoom/index'; import { ChatRoomList, NoChatRoomWrapper, RecentChatInfo } from './styles'; -interface RecentChatProps { - matchingCount: number; - swiperRef: React.MutableRefObject; -} - -const RecentChat: React.FC = () => { +const RecentChat: React.FC = () => { const [chatRoomList, setChatRoomList] = useState([]); + const [latestMatching, setLatestMatching] = useState(); const [isLoading, setIsLoading] = useState(true); const socket = useSocket(); const currentUserId = getCurrentUserId(); @@ -34,8 +30,14 @@ const RecentChat: React.FC = () => { setIsLoading(false); }; + // 최근 매칭 조회 + const getLatestMatching = (data: LatestMatchingData) => { + setLatestMatching(data); + }; + if (socket) { socket.emit('getChatRooms', { userId: currentUserId }); + socket.on('matchingInfo', getLatestMatching); socket.on('chatRoomList', getChatRooms); } @@ -58,6 +60,7 @@ const RecentChat: React.FC = () => { 최근 채팅방 + {chatRoomList.map((chatRoom) => ( ))} diff --git a/src/pages/Chats/TabBar/index.tsx b/src/pages/Chats/TabBar/index.tsx deleted file mode 100644 index a48c9607..00000000 --- a/src/pages/Chats/TabBar/index.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { memo, useCallback, useEffect, useRef, useState } from 'react'; - -import SwiperCore from 'swiper'; -import { Swiper, SwiperSlide } from 'swiper/react'; - -import theme from '@styles/theme'; - -import { getMatchingListApi } from '@apis/matching'; - -import { StyledText } from '@components/Text/StyledText'; - -import Matching from '../Matching/index'; -import RecentChat from '../RecentChat/index'; - -import { TabBarLayout, TabBarContainer, TabBarWrapper, TabBarList, Tabs } from './styles'; - -import 'swiper/css'; - -const TabBar: React.FC = () => { - const [matchingCount, setMatchingCount] = useState(0); - const [hasMatchingRequest, setHasMatchingRequest] = useState(false); - - const [activeIndex, setActiveIndex] = useState(1); - const swiperRef = useRef(null); - const tabs = [`요청 ${activeIndex === 1 ? matchingCount : ''}`, '최근 채팅']; - - // request 컴포넌트에서 매칭 거절 시 matchingCount 감소 - const decreaseMatchingCount = useCallback(() => { - if (matchingCount !== 1) { - setMatchingCount((prev) => Math.max(0, prev - 1)); - } else { - setHasMatchingRequest(false); - swiperRef.current?.slideNext(); - } - }, [matchingCount]); - - // 매칭 요청이 있는 경우에만 '요청' 탭을 활성화 - const handleTabClick = useCallback( - (index: number) => { - if (index !== 0 || hasMatchingRequest) { - setActiveIndex(index); - if (swiperRef.current) { - swiperRef.current.slideTo(index); - } - } - }, - [hasMatchingRequest], - ); - - // 슬라이드가 변경될 때 호출 - const handleSlideChange = useCallback( - (swiper: SwiperCore) => { - // 매칭 요청이 없고 1번 index에 있을 때 0번 탭 비활성화 - if (!hasMatchingRequest && swiper.activeIndex > swiper.previousIndex) { - swiper.allowSlidePrev = false; - setActiveIndex(swiper.activeIndex); - } - // 매칭 요청이 있을 때 양쪽 스와이퍼 가능 - else { - swiper.allowSlidePrev = true; - setActiveIndex(swiper.activeIndex); - } - }, - [hasMatchingRequest], - ); - - // 매칭 리스트 조회 api - const getMatchingList = async () => { - const response = await getMatchingListApi(); - - if (response.isSuccess) { - setMatchingCount(response.data.matchingsCount); - setHasMatchingRequest(response.data.hasMatching); - } - }; - - useEffect(() => { - // 첫 탭을 최근 채팅으로 설정 - if (swiperRef.current) { - swiperRef.current.slideTo(1, 0); - } - - getMatchingList(); - }, []); - - return ( - - - - {tabs.map((tab, index) => ( - handleTabClick(index)} - > - - {tab} - - - ))} - - - - { - swiperRef.current = swiper; - }} - onSlideChange={handleSlideChange} - allowSlidePrev={hasMatchingRequest} - spaceBetween={0} - slidesPerView={1} - autoHeight={true} // 각 슬라이드 높이를 자동으로 조정 - > - - - - - - - - - - ); -}; - -export default memo(TabBar); diff --git a/src/pages/Chats/TabBar/styles.tsx b/src/pages/Chats/TabBar/styles.tsx deleted file mode 100644 index 05b2cae6..00000000 --- a/src/pages/Chats/TabBar/styles.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { styled } from 'styled-components'; - -export const TabBarLayout = styled.div` - display: flex; - flex-direction: column; - margin-top: 0; - flex: 1; -`; - -export const TabBarContainer = styled.div` - width: 100%; - margin: 0 auto; - display: flex; - justify-content: space-around; - align-items: flex-end; -`; - -export const TabBarList = styled.ul` - display: flex; - flex: 1; - justify-content: space-between; -`; - -export const TabBarWrapper = styled.li<{ $isSelected: boolean; $isPointer: boolean }>` - border-bottom: 0.13rem solid ${({ theme }) => theme.colors.border.divider}; - border-image: ${({ $isSelected, theme }) => ($isSelected ? `${theme.colors.brand.gradient} 0 0 1 0` : 'transparent')}; - text-align: center; - flex-grow: 1; - padding: 0.62rem; - padding-bottom: 0.4rem; - cursor: ${({ $isPointer }) => ($isPointer ? 'pointer' : '')}; -`; - -export const Tabs = styled.div` - width: 100%; - height: 100%; - - .swiper { - height: 100%; - } - - .swiper-wrapper { - height: 100%; - } - - .swiper-slider { - height: 100%; - overflow-y: scroll; - padding-bottom: 0.7rem; - - &::-webkit-scrollbar { - display: none; - } - } -`; diff --git a/src/pages/Chats/index.tsx b/src/pages/Chats/index.tsx index bf4ff29f..1b2e3f9a 100644 --- a/src/pages/Chats/index.tsx +++ b/src/pages/Chats/index.tsx @@ -3,8 +3,7 @@ import theme from '@styles/theme'; import { OODDFrame } from '@components/Frame/Frame'; import NavBar from '@components/NavBar'; -import TabBar from './TabBar/index'; - +import RecentChat from './RecentChat'; import { Header } from './styles'; const Chats: React.FC = () => { @@ -13,7 +12,7 @@ const Chats: React.FC = () => {
Chats
- + ); From c656100b9bcc7497d80788bbe95b6503266b4e25 Mon Sep 17 00:00:00 2001 From: young Date: Fri, 21 Feb 2025 20:31:24 +0900 Subject: [PATCH 02/13] =?UTF-8?q?Rename:=20=EB=A7=A4=EC=B9=AD=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=8F=B4=EB=8D=94=20=EB=B0=8F=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=AA=85=20=EC=88=98=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chats/Matching/index.tsx | 27 -------- src/pages/Chats/Matching/styles.tsx | 10 --- .../Cards/Card/dto.ts | 0 .../Cards/Card/index.tsx | 0 .../Cards/Card/styles.tsx | 0 .../{Matching => MatchingRoom}/Cards/dto.ts | 0 .../Cards/index.tsx | 0 .../Cards/styles.tsx | 0 .../Chats/{Matching => MatchingRoom}/dto.ts | 2 +- src/pages/Chats/MatchingRoom/index.tsx | 68 ++++--------------- src/pages/Chats/MatchingRoom/styles.tsx | 45 ++---------- src/pages/Chats/MatchingRoomItem/index.tsx | 67 ++++++++++++++++++ src/pages/Chats/MatchingRoomItem/styles.tsx | 47 +++++++++++++ src/pages/Chats/RecentChat/index.tsx | 4 +- 14 files changed, 135 insertions(+), 135 deletions(-) delete mode 100644 src/pages/Chats/Matching/index.tsx delete mode 100644 src/pages/Chats/Matching/styles.tsx rename src/pages/Chats/{Matching => MatchingRoom}/Cards/Card/dto.ts (100%) rename src/pages/Chats/{Matching => MatchingRoom}/Cards/Card/index.tsx (100%) rename src/pages/Chats/{Matching => MatchingRoom}/Cards/Card/styles.tsx (100%) rename src/pages/Chats/{Matching => MatchingRoom}/Cards/dto.ts (100%) rename src/pages/Chats/{Matching => MatchingRoom}/Cards/index.tsx (100%) rename src/pages/Chats/{Matching => MatchingRoom}/Cards/styles.tsx (100%) rename src/pages/Chats/{Matching => MatchingRoom}/dto.ts (62%) create mode 100644 src/pages/Chats/MatchingRoomItem/index.tsx create mode 100644 src/pages/Chats/MatchingRoomItem/styles.tsx diff --git a/src/pages/Chats/Matching/index.tsx b/src/pages/Chats/Matching/index.tsx deleted file mode 100644 index b3822165..00000000 --- a/src/pages/Chats/Matching/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { memo } from 'react'; - -import theme from '@styles/theme'; - -import { StyledText } from '@components/Text/StyledText'; - -import type { MatchingProps } from './dto'; - -import Cards from './Cards/index'; - -import { ReqeustInfo } from './styles'; - -const Matching: React.FC = ({ matchingCount, decreaseMatchingCount }) => { - return ( - <> - - Message  - - {matchingCount} - - - - - ); -}; - -export default memo(Matching); diff --git a/src/pages/Chats/Matching/styles.tsx b/src/pages/Chats/Matching/styles.tsx deleted file mode 100644 index 57375bbf..00000000 --- a/src/pages/Chats/Matching/styles.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { styled } from 'styled-components'; - -import { StyledText } from '@components/Text/StyledText'; - -export const ReqeustInfo = styled(StyledText)` - display: flex; - align-items: center; - padding: 0.5rem 1.25rem; - text-align: left; -`; diff --git a/src/pages/Chats/Matching/Cards/Card/dto.ts b/src/pages/Chats/MatchingRoom/Cards/Card/dto.ts similarity index 100% rename from src/pages/Chats/Matching/Cards/Card/dto.ts rename to src/pages/Chats/MatchingRoom/Cards/Card/dto.ts diff --git a/src/pages/Chats/Matching/Cards/Card/index.tsx b/src/pages/Chats/MatchingRoom/Cards/Card/index.tsx similarity index 100% rename from src/pages/Chats/Matching/Cards/Card/index.tsx rename to src/pages/Chats/MatchingRoom/Cards/Card/index.tsx diff --git a/src/pages/Chats/Matching/Cards/Card/styles.tsx b/src/pages/Chats/MatchingRoom/Cards/Card/styles.tsx similarity index 100% rename from src/pages/Chats/Matching/Cards/Card/styles.tsx rename to src/pages/Chats/MatchingRoom/Cards/Card/styles.tsx diff --git a/src/pages/Chats/Matching/Cards/dto.ts b/src/pages/Chats/MatchingRoom/Cards/dto.ts similarity index 100% rename from src/pages/Chats/Matching/Cards/dto.ts rename to src/pages/Chats/MatchingRoom/Cards/dto.ts diff --git a/src/pages/Chats/Matching/Cards/index.tsx b/src/pages/Chats/MatchingRoom/Cards/index.tsx similarity index 100% rename from src/pages/Chats/Matching/Cards/index.tsx rename to src/pages/Chats/MatchingRoom/Cards/index.tsx diff --git a/src/pages/Chats/Matching/Cards/styles.tsx b/src/pages/Chats/MatchingRoom/Cards/styles.tsx similarity index 100% rename from src/pages/Chats/Matching/Cards/styles.tsx rename to src/pages/Chats/MatchingRoom/Cards/styles.tsx diff --git a/src/pages/Chats/Matching/dto.ts b/src/pages/Chats/MatchingRoom/dto.ts similarity index 62% rename from src/pages/Chats/Matching/dto.ts rename to src/pages/Chats/MatchingRoom/dto.ts index 77f292c7..922acd65 100644 --- a/src/pages/Chats/Matching/dto.ts +++ b/src/pages/Chats/MatchingRoom/dto.ts @@ -1,4 +1,4 @@ -export interface MatchingProps { +export interface MatchingRoomProps { matchingCount: number; decreaseMatchingCount: () => void; } diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 77968660..23108196 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -1,67 +1,27 @@ -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import dayjs, { extend } from 'dayjs'; -import relativeTime from 'dayjs/plugin/relativeTime'; +import { memo } from 'react'; import theme from '@styles/theme'; -import { LatestMatchingData } from '@apis/matching/dto'; - import { StyledText } from '@components/Text/StyledText'; -import { UserImage, MatchingRoomLayout, LeftBox, RightBox, LatestMessage } from './styles'; - -const MatchingRoom: React.FC> = ({ requestStatus, createdAt }) => { - const [timeAgo, setTimeAgo] = useState(null); - const nav = useNavigate(); - extend(relativeTime); +import type { MatchingRoomProps } from './dto'; - const handleMatchingRoomClick = () => { - nav(`/matching`); - }; +import Cards from './Cards/index'; - useEffect(() => { - if (createdAt) { - // 초기 시간 설정 - setTimeAgo(dayjs(createdAt).locale('ko').fromNow()); - - // 1초마다 `timeAgo`를 업데이트 - const interval = setInterval(() => { - setTimeAgo(dayjs(createdAt).locale('ko').fromNow()); - }, 1000); - - // 컴포넌트 언마운트 시 타이머 정리 - return () => clearInterval(interval); - } else { - setTimeAgo(null); - } - }, []); +import { ReqeustInfo } from './styles'; +const MatchingRoom: React.FC = ({ matchingCount, decreaseMatchingCount }) => { return ( - - - - - 오딩이 - - - {requestStatus === 'pending' - ? '얘가 너 소개해 달래' - : requestStatus === 'rejected' - ? 'ㅠㅠ 담에 더 좋은 애 소개해 줄게' - : requestStatus === 'accepted' - ? '한번 연락해 봐봐' - : ''} - - - - - {timeAgo} + <> + + Message  + + {matchingCount} - - + + + ); }; -export default MatchingRoom; +export default memo(MatchingRoom); diff --git a/src/pages/Chats/MatchingRoom/styles.tsx b/src/pages/Chats/MatchingRoom/styles.tsx index a6dbdb5c..57375bbf 100644 --- a/src/pages/Chats/MatchingRoom/styles.tsx +++ b/src/pages/Chats/MatchingRoom/styles.tsx @@ -2,46 +2,9 @@ import { styled } from 'styled-components'; import { StyledText } from '@components/Text/StyledText'; -export const MatchingRoomLayout = styled.li` - width: 100%; - display: grid; - grid-template-columns: auto 1fr auto; - margin: 0 auto; - cursor: pointer; -`; - -export const UserImage = styled.img` - width: 3.25rem; - height: 3.25rem; - object-fit: cover; - border-radius: 50%; - box-shadow: - 0px 1px 2px 0px rgba(0, 0, 0, 0.12), - 0px 0px 1px 0px rgba(0, 0, 0, 0.08), - 0px 0px 1px 0px rgba(0, 0, 0, 0.08); -`; - -export const LeftBox = styled.div` - margin: 0.2rem 0.5rem; - display: flex; - flex-direction: column; - gap: 0.3rem; - overflow: hidden; -`; - -export const LatestMessage = styled(StyledText)` - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - overflow: hidden; - overflow-wrap: break-word; - text-overflow: ellipsis; -`; - -export const RightBox = styled.div` - margin: 0.1rem 0 0.1rem auto; - text-align: right; +export const ReqeustInfo = styled(StyledText)` display: flex; - flex-direction: column; - justify-content: space-between; + align-items: center; + padding: 0.5rem 1.25rem; + text-align: left; `; diff --git a/src/pages/Chats/MatchingRoomItem/index.tsx b/src/pages/Chats/MatchingRoomItem/index.tsx new file mode 100644 index 00000000..83e841c2 --- /dev/null +++ b/src/pages/Chats/MatchingRoomItem/index.tsx @@ -0,0 +1,67 @@ +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import dayjs, { extend } from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +import theme from '@styles/theme'; + +import { LatestMatchingData } from '@apis/matching/dto'; + +import { StyledText } from '@components/Text/StyledText'; + +import { UserImage, MatchingRoomLayout, LeftBox, RightBox, LatestMessage } from './styles'; + +const MatchingRoomItem: React.FC> = ({ requestStatus, createdAt }) => { + const [timeAgo, setTimeAgo] = useState(null); + const nav = useNavigate(); + extend(relativeTime); + + const handleMatchingRoomClick = () => { + nav(`/matching`); + }; + + useEffect(() => { + if (createdAt) { + // 초기 시간 설정 + setTimeAgo(dayjs(createdAt).locale('ko').fromNow()); + + // 1초마다 `timeAgo`를 업데이트 + const interval = setInterval(() => { + setTimeAgo(dayjs(createdAt).locale('ko').fromNow()); + }, 1000); + + // 컴포넌트 언마운트 시 타이머 정리 + return () => clearInterval(interval); + } else { + setTimeAgo(null); + } + }, []); + + return ( + + + + + 오딩이 + + + {requestStatus === 'pending' + ? '얘가 너 소개해 달래' + : requestStatus === 'rejected' + ? 'ㅠㅠ 담에 더 좋은 애 소개해 줄게' + : requestStatus === 'accepted' + ? '한번 연락해 봐봐' + : ''} + + + + + {timeAgo} + + + + ); +}; + +export default MatchingRoomItem; diff --git a/src/pages/Chats/MatchingRoomItem/styles.tsx b/src/pages/Chats/MatchingRoomItem/styles.tsx new file mode 100644 index 00000000..a6dbdb5c --- /dev/null +++ b/src/pages/Chats/MatchingRoomItem/styles.tsx @@ -0,0 +1,47 @@ +import { styled } from 'styled-components'; + +import { StyledText } from '@components/Text/StyledText'; + +export const MatchingRoomLayout = styled.li` + width: 100%; + display: grid; + grid-template-columns: auto 1fr auto; + margin: 0 auto; + cursor: pointer; +`; + +export const UserImage = styled.img` + width: 3.25rem; + height: 3.25rem; + object-fit: cover; + border-radius: 50%; + box-shadow: + 0px 1px 2px 0px rgba(0, 0, 0, 0.12), + 0px 0px 1px 0px rgba(0, 0, 0, 0.08), + 0px 0px 1px 0px rgba(0, 0, 0, 0.08); +`; + +export const LeftBox = styled.div` + margin: 0.2rem 0.5rem; + display: flex; + flex-direction: column; + gap: 0.3rem; + overflow: hidden; +`; + +export const LatestMessage = styled(StyledText)` + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + overflow-wrap: break-word; + text-overflow: ellipsis; +`; + +export const RightBox = styled.div` + margin: 0.1rem 0 0.1rem auto; + text-align: right; + display: flex; + flex-direction: column; + justify-content: space-between; +`; diff --git a/src/pages/Chats/RecentChat/index.tsx b/src/pages/Chats/RecentChat/index.tsx index c281dfb8..90cfe413 100644 --- a/src/pages/Chats/RecentChat/index.tsx +++ b/src/pages/Chats/RecentChat/index.tsx @@ -12,7 +12,7 @@ import { StyledText } from '@components/Text/StyledText'; import type { ChatRoomData } from '@apis/chatting/dto'; import ChatRoomItem from '../ChatRoomItem/index'; -import MatchingRoom from '../MatchingRoom/index'; +import MatchingRoomItem from '../MatchingRoomItem/index'; import { ChatRoomList, NoChatRoomWrapper, RecentChatInfo } from './styles'; @@ -60,7 +60,7 @@ const RecentChat: React.FC = () => { 최근 채팅방 - + {chatRoomList.map((chatRoom) => ( ))} From d312ff27333aeece0e81b49cdbb32384ab59bd21 Mon Sep 17 00:00:00 2001 From: young Date: Sat, 22 Feb 2025 18:25:51 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat:=20matching=20UX=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/chatting/dto.ts | 2 +- src/apis/matching/dto.ts | 13 +-- .../Chats/{ChatRoom => }/ChatBox/index.tsx | 0 .../Chats/{ChatRoom => }/ChatBox/styles.tsx | 0 .../Chats/ChatRoom/createExtendedMessages.ts | 11 ++- src/pages/Chats/ChatRoom/dto.ts | 24 +---- src/pages/Chats/ChatRoom/index.tsx | 14 +-- .../Chats/{ChatRoom => }/DateBar/index.tsx | 0 .../Chats/{ChatRoom => }/DateBar/styles.tsx | 0 src/pages/Chats/MatchingRoom/dto.ts | 11 +++ src/pages/Chats/MatchingRoom/index.tsx | 92 ++++++++++++++++--- src/pages/Chats/MatchingRoom/styles.tsx | 15 +-- src/pages/Chats/RcvdMessage/dto.ts | 9 ++ .../{ChatRoom => }/RcvdMessage/index.tsx | 2 +- .../{ChatRoom => }/RcvdMessage/styles.tsx | 0 src/pages/Chats/SentMessage/dto.ts | 6 ++ .../{ChatRoom => }/SentMessage/index.tsx | 2 +- .../{ChatRoom => }/SentMessage/styles.tsx | 0 18 files changed, 141 insertions(+), 60 deletions(-) rename src/pages/Chats/{ChatRoom => }/ChatBox/index.tsx (100%) rename src/pages/Chats/{ChatRoom => }/ChatBox/styles.tsx (100%) rename src/pages/Chats/{ChatRoom => }/DateBar/index.tsx (100%) rename src/pages/Chats/{ChatRoom => }/DateBar/styles.tsx (100%) create mode 100644 src/pages/Chats/RcvdMessage/dto.ts rename src/pages/Chats/{ChatRoom => }/RcvdMessage/index.tsx (96%) rename src/pages/Chats/{ChatRoom => }/RcvdMessage/styles.tsx (100%) create mode 100644 src/pages/Chats/SentMessage/dto.ts rename src/pages/Chats/{ChatRoom => }/SentMessage/index.tsx (91%) rename src/pages/Chats/{ChatRoom => }/SentMessage/styles.tsx (100%) diff --git a/src/apis/chatting/dto.ts b/src/apis/chatting/dto.ts index 2d317a07..12181c15 100644 --- a/src/apis/chatting/dto.ts +++ b/src/apis/chatting/dto.ts @@ -21,7 +21,7 @@ export interface LatestMessageDto { // 채팅방 전체 대화 내역 조회 // 최근 메시지 수신 // response -export interface chatRoomMessagesData { +export interface ChatRoomMessagesData { id: number; content: string; fromUser: FromUserDto; diff --git a/src/apis/matching/dto.ts b/src/apis/matching/dto.ts index 9b5ffb34..6b9e98ee 100644 --- a/src/apis/matching/dto.ts +++ b/src/apis/matching/dto.ts @@ -1,5 +1,7 @@ import type { BaseSuccessResponse } from '@apis/core/dto'; +type RequestStatusEnum = 'accepted' | 'rejected' | 'pending'; + // 매칭 요청 // request export interface CreateMatchingRequest { @@ -18,25 +20,24 @@ export interface CreateMatchingData { targetId: number; } -// 최근 매칭 조회 +// 최근 매칭 조회 (채팅방 리스트에서) export interface LatestMatchingData { id: number; requesterId: number; targetId: number; - requestStatus: 'accepted' | 'rejected' | 'pending'; + requestStatus: RequestStatusEnum; createdAt: Date; } -// 매칭 리스트 조회 -// response - -export interface MatchingInfoData { +// 전체 매칭 리스트 조회 +export interface MatchingData { id: number; message: string; createdAt: string; chatRoomId: number; targetId: number; requester: RequesterDto; + requestStatus: RequestStatusEnum; } export interface RequesterDto { diff --git a/src/pages/Chats/ChatRoom/ChatBox/index.tsx b/src/pages/Chats/ChatBox/index.tsx similarity index 100% rename from src/pages/Chats/ChatRoom/ChatBox/index.tsx rename to src/pages/Chats/ChatBox/index.tsx diff --git a/src/pages/Chats/ChatRoom/ChatBox/styles.tsx b/src/pages/Chats/ChatBox/styles.tsx similarity index 100% rename from src/pages/Chats/ChatRoom/ChatBox/styles.tsx rename to src/pages/Chats/ChatBox/styles.tsx diff --git a/src/pages/Chats/ChatRoom/createExtendedMessages.ts b/src/pages/Chats/ChatRoom/createExtendedMessages.ts index bf9747e5..0b01eccf 100644 --- a/src/pages/Chats/ChatRoom/createExtendedMessages.ts +++ b/src/pages/Chats/ChatRoom/createExtendedMessages.ts @@ -3,12 +3,15 @@ import 'dayjs/locale/ko'; import defaultProfile from '@assets/default/defaultProfile.svg'; -import type { OtherUserDto, chatRoomMessagesData } from '@apis/chatting/dto'; +import type { OtherUserDto, ChatRoomMessagesData } from '@apis/chatting/dto'; -import type { ExtendedMessageDto, RcvdMessageProps, SentMessageProps } from './dto'; +import { RcvdMessageProps } from '../RcvdMessage/dto'; +import { SentMessageProps } from '../SentMessage/dto'; + +import type { ExtendedMessageDto } from './dto'; export const createExtendedMessages = ( - allMessages: chatRoomMessagesData[], + allMessages: ChatRoomMessagesData[], userId: number, otherUser: OtherUserDto | null, ) => { @@ -21,7 +24,7 @@ export const createExtendedMessages = ( }; // 렌더링에 필요한 요소를 추가한 메시지 배열 - const temp: ExtendedMessageDto[] = allMessages.map((message: chatRoomMessagesData, index) => { + const temp: ExtendedMessageDto[] = allMessages.map((message: ChatRoomMessagesData, index) => { const prevMessage = index !== 0 ? allMessages[index - 1] : null; const nextMessage = index !== allMessages.length - 1 ? allMessages[index + 1] : null; const formattedTime = dayjs(message.createdAt).format('HH:mm'); diff --git a/src/pages/Chats/ChatRoom/dto.ts b/src/pages/Chats/ChatRoom/dto.ts index dd409693..6c821d6d 100644 --- a/src/pages/Chats/ChatRoom/dto.ts +++ b/src/pages/Chats/ChatRoom/dto.ts @@ -1,24 +1,10 @@ -import type { chatRoomMessagesData } from '@apis/chatting/dto'; +import type { ChatRoomMessagesData } from '@apis/chatting/dto'; -export interface ExtendedMessageDto extends chatRoomMessagesData { +import { RcvdMessageProps } from '../RcvdMessage/dto'; +import { SentMessageProps } from '../SentMessage/dto'; + +export interface ExtendedMessageDto extends ChatRoomMessagesData { isDateBarVisible: boolean; sentMessage?: SentMessageProps; rcvdMessage?: RcvdMessageProps; } - -export interface SentMessageProps { - content: string; - isSenderChanged: boolean; // 상단 마진 추가 여부 - isTimeVisible: boolean; // 메시지 옆 시간 표시 여부 - formattedTime: string; // 타임스탬프를 HH:MM 형태로 변환한 값 -} - -export interface RcvdMessageProps { - fromUserNickname: string; - profilePictureUrl: string; - content: string; - isSenderChanged: boolean; // 상단 마진 추가 여부 - isProfileImageVisible: boolean; // 사용자 프로필 표시 여부 - isTimeVisible: boolean; // 메시지 옆 시간 표시 여부 - formattedTime: string; // 타임스탬프를 HH:MM 형태로 변환한 값 -} diff --git a/src/pages/Chats/ChatRoom/index.tsx b/src/pages/Chats/ChatRoom/index.tsx index 21899ab3..844ef6d3 100644 --- a/src/pages/Chats/ChatRoom/index.tsx +++ b/src/pages/Chats/ChatRoom/index.tsx @@ -24,7 +24,7 @@ import Loading from '@components/Loading'; import Modal from '@components/Modal'; import TopBar from '@components/TopBar'; -import type { chatRoomMessagesData } from '@apis/chatting/dto'; +import type { ChatRoomMessagesData } from '@apis/chatting/dto'; import type { PostUserBlockRequest } from '@apis/user-block/dto'; import type { BottomSheetMenuProps } from '@components/BottomSheet/BottomSheetMenu/dto'; import type { BottomSheetProps } from '@components/BottomSheet/dto'; @@ -32,10 +32,10 @@ import type { ModalProps } from '@components/Modal/dto'; import type { ExtendedMessageDto } from './dto'; -import ChatBox from './ChatBox/index'; -import DateBar from './DateBar/index'; -import RcvdMessage from './RcvdMessage/index'; -import SentMessage from './SentMessage/index'; +import ChatBox from '../ChatBox/index'; +import DateBar from '../DateBar/index'; +import RcvdMessage from '../RcvdMessage/index'; +import SentMessage from '../SentMessage/index'; import { createExtendedMessages } from './createExtendedMessages'; import { MessagesContainer } from './styles'; @@ -114,7 +114,7 @@ const ChatRoom: React.FC = () => { }; // 전체 메시지 조회 socket api - const getChatRoomMessages = (data: chatRoomMessagesData[]) => { + const getChatRoomMessages = (data: ChatRoomMessagesData[]) => { setAllMessages(data); if (data.length > messageLengthRef.current) { setIsScroll((prev) => !prev); @@ -123,7 +123,7 @@ const ChatRoom: React.FC = () => { }; // 새 메시지 수신 socket api - const getNewMessage = (data: chatRoomMessagesData) => { + const getNewMessage = (data: ChatRoomMessagesData) => { setAllMessages((prevMessages) => [...prevMessages, data]); setIsScroll((prev) => !prev); }; diff --git a/src/pages/Chats/ChatRoom/DateBar/index.tsx b/src/pages/Chats/DateBar/index.tsx similarity index 100% rename from src/pages/Chats/ChatRoom/DateBar/index.tsx rename to src/pages/Chats/DateBar/index.tsx diff --git a/src/pages/Chats/ChatRoom/DateBar/styles.tsx b/src/pages/Chats/DateBar/styles.tsx similarity index 100% rename from src/pages/Chats/ChatRoom/DateBar/styles.tsx rename to src/pages/Chats/DateBar/styles.tsx diff --git a/src/pages/Chats/MatchingRoom/dto.ts b/src/pages/Chats/MatchingRoom/dto.ts index 922acd65..55f0d139 100644 --- a/src/pages/Chats/MatchingRoom/dto.ts +++ b/src/pages/Chats/MatchingRoom/dto.ts @@ -1,4 +1,15 @@ +import { MatchingData } from '@apis/matching/dto'; + +import { RcvdMessageProps } from '../RcvdMessage/dto'; +import { SentMessageProps } from '../SentMessage/dto'; + export interface MatchingRoomProps { matchingCount: number; decreaseMatchingCount: () => void; } + +export interface ExtendedMessageDto extends MatchingData { + isDateBarVisible: boolean; + sentMessage?: SentMessageProps; + rcvdMessage?: RcvdMessageProps; +} diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 23108196..fc228f32 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -1,26 +1,88 @@ -import { memo } from 'react'; +import { memo, useEffect, useRef, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; -import theme from '@styles/theme'; +import { MatchingData } from '@apis/matching/dto'; +import { useSocket } from '@context/SocketProvider'; -import { StyledText } from '@components/Text/StyledText'; +import Back from '@assets/arrow/left.svg'; + +import { OODDFrame } from '@components/Frame/Frame'; +import TopBar from '@components/TopBar'; import type { MatchingRoomProps } from './dto'; -import Cards from './Cards/index'; +import { MessagesContainer } from './styles'; + +const MatchingRoom: React.FC = () => { + const [allMatchings, setAllMatchings] = useState([]); + + const [isLoading, setIsLoading] = useState(true); + const [isScroll, setIsScroll] = useState(false); + const chatWindowRef = useRef(null); + + const nav = useNavigate(); + const socket = useSocket(); + + // 메시지 수신 시 아래로 스크롤 (스크롤 아래 고정) + const scrollToBottom = (ref: React.RefObject) => { + if (ref.current) ref.current.scrollIntoView(); + }; + + // 매칭 정보 불러오기 socket api + const getMatchingInfo = (data: MatchingData) => { + setAllMatchings([...allMatchings, data]); + setIsScroll(true); + setIsLoading(false); + }; + + // 채팅방 입장 시 스크롤 아래로 이동 + useEffect(() => { + const messagesContainer = chatWindowRef.current?.parentElement; + + if (messagesContainer) { + messagesContainer.style.scrollBehavior = 'auto'; + messagesContainer.scrollTop = messagesContainer.scrollHeight; + } + }, []); + + // 메시지 수신 시 + useEffect(() => { + // 스크롤 아래로 이동 + if (isScroll) { + scrollToBottom(chatWindowRef); + setIsScroll(false); + } + }, [allMatchings]); + + useEffect(() => { + if (socket) { + socket.on('getMatchingInfo', getMatchingInfo); + } -import { ReqeustInfo } from './styles'; + return () => { + if (socket) { + socket.off(); + } + }; + }, [socket]); -const MatchingRoom: React.FC = ({ matchingCount, decreaseMatchingCount }) => { return ( - <> - - Message  - - {matchingCount} - - - - + + { + nav(-1); + }} + $withBorder={true} + /> + + {allMatchings.map((matching: MatchingData) => { + return
; + })} +
+ + ); }; diff --git a/src/pages/Chats/MatchingRoom/styles.tsx b/src/pages/Chats/MatchingRoom/styles.tsx index 57375bbf..b8eec77c 100644 --- a/src/pages/Chats/MatchingRoom/styles.tsx +++ b/src/pages/Chats/MatchingRoom/styles.tsx @@ -1,10 +1,13 @@ import { styled } from 'styled-components'; -import { StyledText } from '@components/Text/StyledText'; - -export const ReqeustInfo = styled(StyledText)` +export const MessagesContainer = styled.div<{ $isLoading: boolean }>` + visibility: ${({ $isLoading }) => ($isLoading ? 'hidden' : 'visible')}; + width: 100%; + overflow-y: scroll; display: flex; - align-items: center; - padding: 0.5rem 1.25rem; - text-align: left; + flex: 1; + flex-direction: column; + padding: 1.25rem 1.25rem 0 1.25rem; + margin: 0 auto 3.2rem auto; + scroll-behavior: smooth; `; diff --git a/src/pages/Chats/RcvdMessage/dto.ts b/src/pages/Chats/RcvdMessage/dto.ts new file mode 100644 index 00000000..5563feef --- /dev/null +++ b/src/pages/Chats/RcvdMessage/dto.ts @@ -0,0 +1,9 @@ +export interface RcvdMessageProps { + fromUserNickname: string; + profilePictureUrl: string; + content: string; + isSenderChanged: boolean; // 상단 마진 추가 여부 + isProfileImageVisible: boolean; // 사용자 프로필 표시 여부 + isTimeVisible: boolean; // 메시지 옆 시간 표시 여부 + formattedTime: string; // 타임스탬프를 HH:MM 형태로 변환한 값 +} diff --git a/src/pages/Chats/ChatRoom/RcvdMessage/index.tsx b/src/pages/Chats/RcvdMessage/index.tsx similarity index 96% rename from src/pages/Chats/ChatRoom/RcvdMessage/index.tsx rename to src/pages/Chats/RcvdMessage/index.tsx index 8fcdfad2..ed7f9bcc 100644 --- a/src/pages/Chats/ChatRoom/RcvdMessage/index.tsx +++ b/src/pages/Chats/RcvdMessage/index.tsx @@ -2,7 +2,7 @@ import { memo } from 'react'; import theme from '@styles/theme'; -import type { RcvdMessageProps } from '../dto'; +import type { RcvdMessageProps } from './dto'; import { FirstMessageLayout, UserImage, UsernameText, MessageBox, Message, TimeWrapper, MessageLayout } from './styles'; diff --git a/src/pages/Chats/ChatRoom/RcvdMessage/styles.tsx b/src/pages/Chats/RcvdMessage/styles.tsx similarity index 100% rename from src/pages/Chats/ChatRoom/RcvdMessage/styles.tsx rename to src/pages/Chats/RcvdMessage/styles.tsx diff --git a/src/pages/Chats/SentMessage/dto.ts b/src/pages/Chats/SentMessage/dto.ts new file mode 100644 index 00000000..a4c97184 --- /dev/null +++ b/src/pages/Chats/SentMessage/dto.ts @@ -0,0 +1,6 @@ +export interface SentMessageProps { + content: string; + isSenderChanged: boolean; // 상단 마진 추가 여부 + isTimeVisible: boolean; // 메시지 옆 시간 표시 여부 + formattedTime: string; // 타임스탬프를 HH:MM 형태로 변환한 값 +} diff --git a/src/pages/Chats/ChatRoom/SentMessage/index.tsx b/src/pages/Chats/SentMessage/index.tsx similarity index 91% rename from src/pages/Chats/ChatRoom/SentMessage/index.tsx rename to src/pages/Chats/SentMessage/index.tsx index 6ad94e05..e6aabad1 100644 --- a/src/pages/Chats/ChatRoom/SentMessage/index.tsx +++ b/src/pages/Chats/SentMessage/index.tsx @@ -2,7 +2,7 @@ import { memo } from 'react'; import theme from '@styles/theme'; -import type { SentMessageProps } from '../dto'; +import type { SentMessageProps } from './dto'; import { Message, TimeWrapper, MessageLayout } from './styles'; diff --git a/src/pages/Chats/ChatRoom/SentMessage/styles.tsx b/src/pages/Chats/SentMessage/styles.tsx similarity index 100% rename from src/pages/Chats/ChatRoom/SentMessage/styles.tsx rename to src/pages/Chats/SentMessage/styles.tsx From 3d0fa61597725dca75984f322513406b53741323 Mon Sep 17 00:00:00 2001 From: young Date: Sat, 22 Feb 2025 23:24:03 +0900 Subject: [PATCH 04/13] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20UX=EC=9D=98?= =?UTF-8?q?=20=EB=A7=A4=EC=B9=AD=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 + src/apis/matching/dto.ts | 8 +-- src/pages/Chats/MatchingRoom/Card/dto.ts | 8 +++ .../MatchingRoom/{Cards => }/Card/index.tsx | 10 +-- .../MatchingRoom/{Cards => }/Card/styles.tsx | 0 .../Chats/MatchingRoom/Cards/Card/dto.ts | 6 -- src/pages/Chats/MatchingRoom/Cards/dto.ts | 4 -- src/pages/Chats/MatchingRoom/Cards/index.tsx | 68 ------------------- src/pages/Chats/MatchingRoom/Cards/styles.tsx | 29 -------- .../MatchingRoom/MatchingMessage/index.tsx | 51 ++++++++++++++ .../MatchingRoom/NoMatchingMessage/index.tsx | 23 +++++++ src/pages/Chats/MatchingRoom/dto.ts | 5 -- src/pages/Chats/MatchingRoom/index.tsx | 25 ++++--- src/pages/Chats/MatchingRoomItem/index.tsx | 14 ++-- src/pages/Chats/RcvdMessage/index.tsx | 7 +- src/pages/Chats/RecentChat/index.tsx | 10 ++- 16 files changed, 127 insertions(+), 143 deletions(-) create mode 100644 src/pages/Chats/MatchingRoom/Card/dto.ts rename src/pages/Chats/MatchingRoom/{Cards => }/Card/index.tsx (92%) rename src/pages/Chats/MatchingRoom/{Cards => }/Card/styles.tsx (100%) delete mode 100644 src/pages/Chats/MatchingRoom/Cards/Card/dto.ts delete mode 100644 src/pages/Chats/MatchingRoom/Cards/dto.ts delete mode 100644 src/pages/Chats/MatchingRoom/Cards/index.tsx delete mode 100644 src/pages/Chats/MatchingRoom/Cards/styles.tsx create mode 100644 src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx create mode 100644 src/pages/Chats/MatchingRoom/NoMatchingMessage/index.tsx diff --git a/src/App.tsx b/src/App.tsx index e2069bac..7360c66c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -24,6 +24,7 @@ import PostInstaFeedSelect from '@pages/Post/PostInstaFeedSelect'; import Chats from '@pages/Chats'; import ChatRoom from '@pages/Chats/ChatRoom'; +import MatchingRoom from '@pages/Chats/MatchingRoom'; import NotFound from '@pages/NotFound'; @@ -55,6 +56,7 @@ const protectedRoutes = [ // 메시지/채팅 { path: '/chats', element: }, { path: '/chats/:chatRoomId', element: }, + { path: '/matching', element: }, ]; // 인증이 필요 없는 페이지 배열 diff --git a/src/apis/matching/dto.ts b/src/apis/matching/dto.ts index 6b9e98ee..e88e4486 100644 --- a/src/apis/matching/dto.ts +++ b/src/apis/matching/dto.ts @@ -22,10 +22,10 @@ export interface CreateMatchingData { // 최근 매칭 조회 (채팅방 리스트에서) export interface LatestMatchingData { - id: number; - requesterId: number; - targetId: number; - requestStatus: RequestStatusEnum; + id?: number; + requesterId?: number; + targetId?: number; + requestStatus?: RequestStatusEnum; createdAt: Date; } diff --git a/src/pages/Chats/MatchingRoom/Card/dto.ts b/src/pages/Chats/MatchingRoom/Card/dto.ts new file mode 100644 index 00000000..a3ff3c62 --- /dev/null +++ b/src/pages/Chats/MatchingRoom/Card/dto.ts @@ -0,0 +1,8 @@ +import type { MatchingData } from '@apis/matching/dto'; + +// export interface CardProps { +// removeRejectedMatching: () => void; +// matching: MatchingData; +// } + +export type CardProps = Pick; diff --git a/src/pages/Chats/MatchingRoom/Cards/Card/index.tsx b/src/pages/Chats/MatchingRoom/Card/index.tsx similarity index 92% rename from src/pages/Chats/MatchingRoom/Cards/Card/index.tsx rename to src/pages/Chats/MatchingRoom/Card/index.tsx index c0ec4802..4415949d 100644 --- a/src/pages/Chats/MatchingRoom/Cards/Card/index.tsx +++ b/src/pages/Chats/MatchingRoom/Card/index.tsx @@ -37,12 +37,11 @@ import { SeeMore, } from './styles'; -const Card: React.FC = ({ removeRejectedMatching, matching }) => { +const Card: React.FC = ({ id, chatRoomId, requester }) => { const [isStatusModalOpen, setIsStatusModalOpen] = useState(false); const [modalContent, setModalContent] = useState('알 수 없는 오류가 발생했습니다.\n관리자에게 문의해 주세요.'); const [, setOtherUser] = useRecoilState(OtherUserAtom); const nav = useNavigate(); - const requester = matching.requester; const handleUserClick = () => { nav(`/profile/${requester.id}`); @@ -59,19 +58,16 @@ const Card: React.FC = ({ removeRejectedMatching, matching }) => { // 매칭 거절 및 수락 api const modifyMatchingStatus = async (status: 'accept' | 'reject') => { try { - console.log(matching); - const response = await modifyMatchingStatusApi(matching.id, { requestStatus: status }); + const response = await modifyMatchingStatusApi(id, { requestStatus: status }); if (response.isSuccess) { - removeRejectedMatching(); // 매칭 리스트에서 해당 매칭을 제거 - if (status === 'accept') { setOtherUser({ id: requester.id, nickname: requester.nickname, profilePictureUrl: requester.profilePictureUrl, }); - nav(`/chats/${response.data.chatRoomId}`); + nav(`/chats/${chatRoomId}`); } } } catch (error) { diff --git a/src/pages/Chats/MatchingRoom/Cards/Card/styles.tsx b/src/pages/Chats/MatchingRoom/Card/styles.tsx similarity index 100% rename from src/pages/Chats/MatchingRoom/Cards/Card/styles.tsx rename to src/pages/Chats/MatchingRoom/Card/styles.tsx diff --git a/src/pages/Chats/MatchingRoom/Cards/Card/dto.ts b/src/pages/Chats/MatchingRoom/Cards/Card/dto.ts deleted file mode 100644 index 3fda591d..00000000 --- a/src/pages/Chats/MatchingRoom/Cards/Card/dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { MatchingDto } from '@apis/matching/dto'; - -export interface CardProps { - removeRejectedMatching: () => void; - matching: MatchingDto; -} diff --git a/src/pages/Chats/MatchingRoom/Cards/dto.ts b/src/pages/Chats/MatchingRoom/Cards/dto.ts deleted file mode 100644 index d097ff89..00000000 --- a/src/pages/Chats/MatchingRoom/Cards/dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface CardsProps { - // 탭바의 matchingCount와 연동하여 매칭 요청이 없으면 요청 탭 비활성화 - decreaseMatchingCount: () => void; -} diff --git a/src/pages/Chats/MatchingRoom/Cards/index.tsx b/src/pages/Chats/MatchingRoom/Cards/index.tsx deleted file mode 100644 index a5648bec..00000000 --- a/src/pages/Chats/MatchingRoom/Cards/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; - -import { Pagination } from 'swiper/modules'; -import { Swiper, SwiperRef, SwiperSlide } from 'swiper/react'; - -import 'swiper/css'; -import 'swiper/css/pagination'; -import { getMatchingListApi } from '@apis/matching'; - -import type { MatchingDto } from '@apis/matching/dto'; - -import type { CardsProps } from './dto'; - -import Card from './Card/index'; - -import { CardsContainer } from './styles'; - -const Cards: React.FC = ({ decreaseMatchingCount }) => { - const [matchings, setMatchings] = useState([]); - const swiperRef = useRef(null); - - // 매칭 요청 거절 시 거절한 요청을 제거하는 함수 - const removeRejectedMatching = (index: number) => { - if (swiperRef.current && swiperRef.current.swiper) { - // 해당 요청을 리스트에서 제거 - const remainMatchings = matchings.filter((_, i) => i !== index); - setMatchings(remainMatchings); - decreaseMatchingCount(); - } else { - console.log('Swiper instance is not available'); - } - }; - - // 매칭 리스트 조회 api - const getMatchingList = async () => { - const response = await getMatchingListApi(); - - setMatchings(response.data.matching); - }; - - useEffect(() => { - getMatchingList(); - }, []); - - return ( - - - {matchings.map((matching, index) => ( - - removeRejectedMatching(index)} - /> - - ))} - - - ); -}; - -export default Cards; diff --git a/src/pages/Chats/MatchingRoom/Cards/styles.tsx b/src/pages/Chats/MatchingRoom/Cards/styles.tsx deleted file mode 100644 index 7b4fe6c7..00000000 --- a/src/pages/Chats/MatchingRoom/Cards/styles.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { styled } from 'styled-components'; - -export const CardsContainer = styled.div` - display: flex; - flex-direction: column; - - .parentSwiper { - width: 100%; - z-index: 10; - } - - .parentSwiper .swiper-slide { - transition: transform 0.3s; - transform: scale(0.95); - } - - .parentSwiper .swiper-slide-active { - transform: scale(1); - } - - .parentSwiper .swiper-slide-next, - .parentSwiper .swiper-slide-prev { - transform: scale(0.95); - } - - .parentSwiper.swiper-container { - margin-left: 0.9375rem; - } -`; diff --git a/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx b/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx new file mode 100644 index 00000000..fa144aec --- /dev/null +++ b/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx @@ -0,0 +1,51 @@ +import dayjs from 'dayjs'; + +import RcvdMessage from '@pages/Chats/RcvdMessage'; + +import type { MatchingData } from '@apis/matching/dto'; +import type { RcvdMessageProps } from '@pages/Chats/RcvdMessage/dto'; + +import type { CardProps } from '../Card/dto'; + +import Card from '../Card'; + +const MatchingMessage: React.FC = ({ id, message, createdAt, chatRoomId, requester }: MatchingData) => { + const formattedTime = dayjs(createdAt).format('HH:mm'); + + const firstMessageProps: RcvdMessageProps = { + fromUserNickname: '오딩이', + profilePictureUrl: '', + content: '얘가 너 소개받고 싶대', + isSenderChanged: true, + isProfileImageVisible: true, + isTimeVisible: false, + formattedTime, + }; + + const matchingMessageProps: RcvdMessageProps = { + fromUserNickname: '오딩이', + profilePictureUrl: '', + content: message, + isSenderChanged: false, + isProfileImageVisible: false, + isTimeVisible: true, + formattedTime, + }; + + const cardProps: CardProps = { + id, + chatRoomId, + requester, + }; + + return ( + <> + + + + + + ); +}; + +export default MatchingMessage; diff --git a/src/pages/Chats/MatchingRoom/NoMatchingMessage/index.tsx b/src/pages/Chats/MatchingRoom/NoMatchingMessage/index.tsx new file mode 100644 index 00000000..9385e7f9 --- /dev/null +++ b/src/pages/Chats/MatchingRoom/NoMatchingMessage/index.tsx @@ -0,0 +1,23 @@ +import dayjs from 'dayjs'; + +import RcvdMessage from '@pages/Chats/RcvdMessage'; + +import type { RcvdMessageProps } from '@pages/Chats/RcvdMessage/dto'; + +const NoMatchingMessage: React.FC = () => { + const formattedTime = dayjs(new Date()).format('HH:mm'); + + const messageProps: RcvdMessageProps = { + fromUserNickname: '오딩이', + profilePictureUrl: '', + content: '매칭이 들어오면 오딩이가 알려줄게!', + isSenderChanged: true, + isProfileImageVisible: true, + isTimeVisible: false, + formattedTime, + }; + + return ; +}; + +export default NoMatchingMessage; diff --git a/src/pages/Chats/MatchingRoom/dto.ts b/src/pages/Chats/MatchingRoom/dto.ts index 55f0d139..f45a874d 100644 --- a/src/pages/Chats/MatchingRoom/dto.ts +++ b/src/pages/Chats/MatchingRoom/dto.ts @@ -3,11 +3,6 @@ import { MatchingData } from '@apis/matching/dto'; import { RcvdMessageProps } from '../RcvdMessage/dto'; import { SentMessageProps } from '../SentMessage/dto'; -export interface MatchingRoomProps { - matchingCount: number; - decreaseMatchingCount: () => void; -} - export interface ExtendedMessageDto extends MatchingData { isDateBarVisible: boolean; sentMessage?: SentMessageProps; diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index fc228f32..3ce12535 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -9,11 +9,11 @@ import Back from '@assets/arrow/left.svg'; import { OODDFrame } from '@components/Frame/Frame'; import TopBar from '@components/TopBar'; -import type { MatchingRoomProps } from './dto'; - +import MatchingMessage from './MatchingMessage'; +import NoMatchingMessage from './NoMatchingMessage'; import { MessagesContainer } from './styles'; -const MatchingRoom: React.FC = () => { +const MatchingRoom: React.FC = () => { const [allMatchings, setAllMatchings] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -28,9 +28,9 @@ const MatchingRoom: React.FC = () => { if (ref.current) ref.current.scrollIntoView(); }; - // 매칭 정보 불러오기 socket api - const getMatchingInfo = (data: MatchingData) => { - setAllMatchings([...allMatchings, data]); + // 전체 매칭 불러오기 socket api + const getAllMatchings = (data: MatchingData[]) => { + setAllMatchings(data); setIsScroll(true); setIsLoading(false); }; @@ -56,7 +56,7 @@ const MatchingRoom: React.FC = () => { useEffect(() => { if (socket) { - socket.on('getMatchingInfo', getMatchingInfo); + socket.emit('getAllMatchings', getAllMatchings); } return () => { @@ -77,9 +77,14 @@ const MatchingRoom: React.FC = () => { $withBorder={true} /> - {allMatchings.map((matching: MatchingData) => { - return
; - })} + {allMatchings.length === 0 ? ( + + ) : ( + allMatchings.map((matching: MatchingData) => { + // TODO: 매칭 상태에 따라 응답 버튼 렌더링 + return ; + }) + )}
diff --git a/src/pages/Chats/MatchingRoomItem/index.tsx b/src/pages/Chats/MatchingRoomItem/index.tsx index 83e841c2..90f0b980 100644 --- a/src/pages/Chats/MatchingRoomItem/index.tsx +++ b/src/pages/Chats/MatchingRoomItem/index.tsx @@ -46,13 +46,13 @@ const MatchingRoomItem: React.FC> = ({ requestStatus 오딩이 - {requestStatus === 'pending' - ? '얘가 너 소개해 달래' - : requestStatus === 'rejected' - ? 'ㅠㅠ 담에 더 좋은 애 소개해 줄게' - : requestStatus === 'accepted' - ? '한번 연락해 봐봐' - : ''} + {!requestStatus + ? '매칭이 들어오면 오딩이가 알려줄게!' + : requestStatus === 'pending' + ? '얘가 너 소개해 달래' + : requestStatus === 'rejected' + ? 'ㅠㅠ 담에 더 좋은 애 소개해 줄게' + : '한번 연락해 봐봐'} diff --git a/src/pages/Chats/RcvdMessage/index.tsx b/src/pages/Chats/RcvdMessage/index.tsx index ed7f9bcc..01ac329d 100644 --- a/src/pages/Chats/RcvdMessage/index.tsx +++ b/src/pages/Chats/RcvdMessage/index.tsx @@ -1,4 +1,4 @@ -import { memo } from 'react'; +import { memo, ReactNode } from 'react'; import theme from '@styles/theme'; @@ -6,7 +6,7 @@ import type { RcvdMessageProps } from './dto'; import { FirstMessageLayout, UserImage, UsernameText, MessageBox, Message, TimeWrapper, MessageLayout } from './styles'; -const RcvdMessage: React.FC void }> = memo( +const RcvdMessage: React.FC void; children?: ReactNode }> = memo( ({ fromUserNickname, profilePictureUrl, @@ -16,6 +16,7 @@ const RcvdMessage: React.FC void }> = isTimeVisible, formattedTime, onClickProfile, + children, }) => { if (isProfileImageVisible) { return ( @@ -30,6 +31,7 @@ const RcvdMessage: React.FC void }> = {fromUserNickname} + {children} {content} @@ -40,6 +42,7 @@ const RcvdMessage: React.FC void }> = return ( + {children} {content} {isTimeVisible && {formattedTime}} diff --git a/src/pages/Chats/RecentChat/index.tsx b/src/pages/Chats/RecentChat/index.tsx index 90cfe413..ae344d39 100644 --- a/src/pages/Chats/RecentChat/index.tsx +++ b/src/pages/Chats/RecentChat/index.tsx @@ -33,11 +33,19 @@ const RecentChat: React.FC = () => { // 최근 매칭 조회 const getLatestMatching = (data: LatestMatchingData) => { setLatestMatching(data); + console.log('getLatestMatching'); + }; + + const matchingNotFound = (data: LatestMatchingData) => { + console.log(data); + console.log('matchingNotFound'); }; if (socket) { socket.emit('getChatRooms', { userId: currentUserId }); - socket.on('matchingInfo', getLatestMatching); + socket.emit('getLatestMatching', { userId: currentUserId }, getLatestMatching); + socket.on('getLatestMatching', getLatestMatching); + socket.on('matchingNotFound', matchingNotFound); socket.on('chatRoomList', getChatRooms); } From 49f3bc1f6456b8654b0f4d9343c61b9e252029e0 Mon Sep 17 00:00:00 2001 From: young Date: Sat, 22 Feb 2025 23:26:18 +0900 Subject: [PATCH 05/13] =?UTF-8?q?Refactor:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=9D=84=20RecentChat=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chats/{ => RecentChat}/ChatRoomItem/index.tsx | 0 src/pages/Chats/{ => RecentChat}/ChatRoomItem/styles.tsx | 0 src/pages/Chats/{ => RecentChat}/MatchingRoomItem/index.tsx | 0 src/pages/Chats/{ => RecentChat}/MatchingRoomItem/styles.tsx | 0 src/pages/Chats/RecentChat/index.tsx | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) rename src/pages/Chats/{ => RecentChat}/ChatRoomItem/index.tsx (100%) rename src/pages/Chats/{ => RecentChat}/ChatRoomItem/styles.tsx (100%) rename src/pages/Chats/{ => RecentChat}/MatchingRoomItem/index.tsx (100%) rename src/pages/Chats/{ => RecentChat}/MatchingRoomItem/styles.tsx (100%) diff --git a/src/pages/Chats/ChatRoomItem/index.tsx b/src/pages/Chats/RecentChat/ChatRoomItem/index.tsx similarity index 100% rename from src/pages/Chats/ChatRoomItem/index.tsx rename to src/pages/Chats/RecentChat/ChatRoomItem/index.tsx diff --git a/src/pages/Chats/ChatRoomItem/styles.tsx b/src/pages/Chats/RecentChat/ChatRoomItem/styles.tsx similarity index 100% rename from src/pages/Chats/ChatRoomItem/styles.tsx rename to src/pages/Chats/RecentChat/ChatRoomItem/styles.tsx diff --git a/src/pages/Chats/MatchingRoomItem/index.tsx b/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx similarity index 100% rename from src/pages/Chats/MatchingRoomItem/index.tsx rename to src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx diff --git a/src/pages/Chats/MatchingRoomItem/styles.tsx b/src/pages/Chats/RecentChat/MatchingRoomItem/styles.tsx similarity index 100% rename from src/pages/Chats/MatchingRoomItem/styles.tsx rename to src/pages/Chats/RecentChat/MatchingRoomItem/styles.tsx diff --git a/src/pages/Chats/RecentChat/index.tsx b/src/pages/Chats/RecentChat/index.tsx index ae344d39..81f546f1 100644 --- a/src/pages/Chats/RecentChat/index.tsx +++ b/src/pages/Chats/RecentChat/index.tsx @@ -11,8 +11,8 @@ import { StyledText } from '@components/Text/StyledText'; import type { ChatRoomData } from '@apis/chatting/dto'; -import ChatRoomItem from '../ChatRoomItem/index'; -import MatchingRoomItem from '../MatchingRoomItem/index'; +import ChatRoomItem from './ChatRoomItem/index'; +import MatchingRoomItem from './MatchingRoomItem/index'; import { ChatRoomList, NoChatRoomWrapper, RecentChatInfo } from './styles'; From 647d3ea781198272d02765585b3e07216ddf33ca Mon Sep 17 00:00:00 2001 From: young Date: Tue, 25 Feb 2025 20:30:22 +0900 Subject: [PATCH 06/13] =?UTF-8?q?fix:=20=EC=84=9C=EB=A1=9C=20=EB=8B=A4?= =?UTF-8?q?=EB=A5=B8=20=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20useSocket=20=ED=9B=85=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context/SocketProvider.tsx | 60 +++++++++++-------- src/pages/Chats/MatchingRoom/index.tsx | 30 ++++++---- src/pages/Chats/RcvdMessage/styles.tsx | 2 +- .../RecentChat/MatchingRoomItem/index.tsx | 10 +--- src/pages/Chats/RecentChat/index.tsx | 20 +++++-- 5 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/context/SocketProvider.tsx b/src/context/SocketProvider.tsx index a77d7108..e7b593f0 100644 --- a/src/context/SocketProvider.tsx +++ b/src/context/SocketProvider.tsx @@ -2,47 +2,55 @@ import { createContext, useContext, useEffect, useState } from 'react'; import { io, Socket } from 'socket.io-client'; -const SocketContext = createContext(null); +type SocketMap = { [endpoint: string]: Socket }; + +const SocketContext = createContext(null); export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [socket, setSocket] = useState(null); + const [socketMap, setSocketMap] = useState({}); useEffect(() => { - const newSocket = io(`${import.meta.env.VITE_NEW_API_URL}/socket/chatting`, { - transports: ['websocket'], - }); - setSocket(newSocket); - - newSocket.on('connect', () => { - console.log('connection is open'); + const endpoints = ['chatting', 'matching']; // 필요한 엔드포인트 추가 + const newSockets: SocketMap = {}; + + endpoints.forEach((endpoint) => { + const socket = io(`${import.meta.env.VITE_NEW_API_URL}/socket/${endpoint}`, { + transports: ['websocket'], + }); + newSockets[endpoint] = socket; + + socket.on('connect', () => { + console.log(`${endpoint} connection is open`); + }); + + socket.on('disconnect', (reason) => { + console.log(`${endpoint} Disconnected from server:`, reason); + }); + + socket.on('connect_error', (err) => { + console.log(`${endpoint} connect error:`, err.message); + }); }); - newSocket.on('disconnect', (reason) => { - console.log('Disconnected from server:', reason); - }); - - newSocket.on('connect_error', (err) => { - console.log(err.message); - }); + setSocketMap(newSockets); return () => { - newSocket.disconnect(); + Object.values(newSockets).forEach((socket) => socket.disconnect()); }; }, []); - // 소켓 설정이 완료되지 않은 경우 렌더링 방지 - // 채팅방에서 새로고침했을 때 오류 방지 - if (!socket) { + if (!Object.keys(socketMap).length) { return null; } - return {children}; + return {children}; }; -export const useSocket = () => { - const context = useContext(SocketContext); - if (context === null) { - throw new Error('useSocket must be used within a SocketProvider'); +// 엔드포인트를 인자로 받아 해당 소켓을 반환하는 훅 +export const useSocket = (endpoint = 'chatting') => { + const socketMap = useContext(SocketContext); + if (!socketMap || !socketMap[endpoint]) { + throw new Error(`useSocket must be used within a SocketProvider with a valid endpoint (${endpoint})`); } - return context; + return socketMap[endpoint]; }; diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 3ce12535..5240ab50 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { MatchingData } from '@apis/matching/dto'; import { useSocket } from '@context/SocketProvider'; +import { getCurrentUserId } from '@utils/getCurrentUserId'; import Back from '@assets/arrow/left.svg'; @@ -20,21 +21,15 @@ const MatchingRoom: React.FC = () => { const [isScroll, setIsScroll] = useState(false); const chatWindowRef = useRef(null); + const currentUserId = getCurrentUserId(); const nav = useNavigate(); - const socket = useSocket(); + const socket = useSocket('matching'); // 메시지 수신 시 아래로 스크롤 (스크롤 아래 고정) const scrollToBottom = (ref: React.RefObject) => { if (ref.current) ref.current.scrollIntoView(); }; - // 전체 매칭 불러오기 socket api - const getAllMatchings = (data: MatchingData[]) => { - setAllMatchings(data); - setIsScroll(true); - setIsLoading(false); - }; - // 채팅방 입장 시 스크롤 아래로 이동 useEffect(() => { const messagesContainer = chatWindowRef.current?.parentElement; @@ -55,8 +50,23 @@ const MatchingRoom: React.FC = () => { }, [allMatchings]); useEffect(() => { + // 전체 매칭 불러오기 socket api + const getAllMatchings = ({ matching }: { matching: MatchingData[] }) => { + console.log(allMatchings); + setAllMatchings(matching); + setIsScroll(true); + setIsLoading(false); + }; + + const getNewMatching = (data: MatchingData) => { + console.log(data); + }; + if (socket) { - socket.emit('getAllMatchings', getAllMatchings); + socket.emit('getAllMatchings', { userId: currentUserId }); + socket.emit('getMatching', { userId: currentUserId }); + socket.on('matchings', getAllMatchings); + socket.on('nextMatching', getNewMatching); } return () => { @@ -82,7 +92,7 @@ const MatchingRoom: React.FC = () => { ) : ( allMatchings.map((matching: MatchingData) => { // TODO: 매칭 상태에 따라 응답 버튼 렌더링 - return ; + return ; }) )}
diff --git a/src/pages/Chats/RcvdMessage/styles.tsx b/src/pages/Chats/RcvdMessage/styles.tsx index 1c659f2b..ee11e142 100644 --- a/src/pages/Chats/RcvdMessage/styles.tsx +++ b/src/pages/Chats/RcvdMessage/styles.tsx @@ -30,7 +30,7 @@ export const MessageBox = styled.div` display: flex; flex-direction: column; gap: 0.2rem; - max-width: 75%; + /* max-width: 75%; */ margin-right: 0.5rem; `; diff --git a/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx b/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx index 90f0b980..8c25445d 100644 --- a/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx +++ b/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx @@ -38,6 +38,8 @@ const MatchingRoomItem: React.FC> = ({ requestStatus } }, []); + console.log(requestStatus); + return ( @@ -46,13 +48,7 @@ const MatchingRoomItem: React.FC> = ({ requestStatus 오딩이 - {!requestStatus - ? '매칭이 들어오면 오딩이가 알려줄게!' - : requestStatus === 'pending' - ? '얘가 너 소개해 달래' - : requestStatus === 'rejected' - ? 'ㅠㅠ 담에 더 좋은 애 소개해 줄게' - : '한번 연락해 봐봐'} + {requestStatus === 'pending' ? '얘가 너 소개받고 싶대' : '매칭이 들어오면 오딩이가 알려줄게!'} diff --git a/src/pages/Chats/RecentChat/index.tsx b/src/pages/Chats/RecentChat/index.tsx index 81f546f1..c7d1200e 100644 --- a/src/pages/Chats/RecentChat/index.tsx +++ b/src/pages/Chats/RecentChat/index.tsx @@ -20,9 +20,11 @@ const RecentChat: React.FC = () => { const [chatRoomList, setChatRoomList] = useState([]); const [latestMatching, setLatestMatching] = useState(); const [isLoading, setIsLoading] = useState(true); - const socket = useSocket(); const currentUserId = getCurrentUserId(); + const socket = useSocket(); + const matchingSocket = useSocket('matching'); + useEffect(() => { // 채팅방 리스트 조회 const getChatRooms = (data: ChatRoomData[]) => { @@ -43,20 +45,28 @@ const RecentChat: React.FC = () => { if (socket) { socket.emit('getChatRooms', { userId: currentUserId }); - socket.emit('getLatestMatching', { userId: currentUserId }, getLatestMatching); - socket.on('getLatestMatching', getLatestMatching); - socket.on('matchingNotFound', matchingNotFound); socket.on('chatRoomList', getChatRooms); } + if (matchingSocket) { + matchingSocket.emit('getLatestMatching', { userId: currentUserId }); + matchingSocket.on('getLatestMatching', getLatestMatching); + matchingSocket.on('matchingNotFound', matchingNotFound); + } + // 이벤트 리스너 정리 // 컴포넌트가 언마운트되면 더 이상 이벤트를 수신하지 않음 return () => { if (socket) { socket.off('getChatRooms', getChatRooms); } + + if (matchingSocket) { + matchingSocket.off('getLatestMatching', getLatestMatching); + matchingSocket.off('matchingNotFound', matchingNotFound); + } }; - }, [socket]); + }, [socket, matchingSocket]); return ( <> From 74542401ee3675054aa7503ba46b6eb0786bbfb0 Mon Sep 17 00:00:00 2001 From: young Date: Tue, 25 Feb 2025 20:45:45 +0900 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=EB=A7=A4=EC=B9=AD=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chats/MatchingRoom/Card/index.tsx | 81 +------------------- src/pages/Chats/MatchingRoom/Card/styles.tsx | 53 ++----------- 2 files changed, 8 insertions(+), 126 deletions(-) diff --git a/src/pages/Chats/MatchingRoom/Card/index.tsx b/src/pages/Chats/MatchingRoom/Card/index.tsx index 4415949d..960c8704 100644 --- a/src/pages/Chats/MatchingRoom/Card/index.tsx +++ b/src/pages/Chats/MatchingRoom/Card/index.tsx @@ -1,7 +1,5 @@ -import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useRecoilState } from 'recoil'; import { Pagination } from 'swiper/modules'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; @@ -9,81 +7,21 @@ import 'swiper/css/pagination'; import theme from '@styles/theme'; -import { modifyMatchingStatusApi } from '@apis/matching'; -import { handleError } from '@apis/util/handleError'; -import { OtherUserAtom } from '@recoil/util/OtherUser'; - -import acceptButton from '@assets/default/accept.svg'; import defaultProfile from '@assets/default/defaultProfile.svg'; -import rejectButton from '@assets/default/reject.svg'; -import Modal from '@components/Modal'; import { StyledText } from '@components/Text/StyledText'; -import type { ModalProps } from '@components/Modal/dto'; - import type { CardProps } from './dto'; -import { - ArrowButton, - Btn, - CardLayout, - OOTDImgBackground, - OOTDImgBox, - ProfileContainer, - ProfileImgBox, - ProfileInfo, - Reaction, - SeeMore, -} from './styles'; +import { CardLayout, OOTDImgBackground, OOTDImgBox, ProfileContainer, ProfileImgBox, ProfileInfo } from './styles'; -const Card: React.FC = ({ id, chatRoomId, requester }) => { - const [isStatusModalOpen, setIsStatusModalOpen] = useState(false); - const [modalContent, setModalContent] = useState('알 수 없는 오류가 발생했습니다.\n관리자에게 문의해 주세요.'); - const [, setOtherUser] = useRecoilState(OtherUserAtom); +const Card: React.FC = ({ requester }) => { const nav = useNavigate(); const handleUserClick = () => { nav(`/profile/${requester.id}`); }; - const handleRejectButtonClick = () => { - modifyMatchingStatus('reject'); - }; - - const handleAcceptButtonClick = () => { - modifyMatchingStatus('accept'); - }; - - // 매칭 거절 및 수락 api - const modifyMatchingStatus = async (status: 'accept' | 'reject') => { - try { - const response = await modifyMatchingStatusApi(id, { requestStatus: status }); - - if (response.isSuccess) { - if (status === 'accept') { - setOtherUser({ - id: requester.id, - nickname: requester.nickname, - profilePictureUrl: requester.profilePictureUrl, - }); - nav(`/chats/${chatRoomId}`); - } - } - } catch (error) { - const errorMessage = handleError(error); - setModalContent(errorMessage); - setIsStatusModalOpen(true); - } - }; - - const statusModalProps: ModalProps = { - content: modalContent, - onClose: () => { - setIsStatusModalOpen(false); - }, - }; - return ( @@ -113,12 +51,6 @@ const Card: React.FC = ({ id, chatRoomId, requester }) => { ))}
- nav(`/profile/${requester.id}`)}> - - OOTD 더 보기 - - - = ({ id, chatRoomId, requester }) => { ))} - - - reject - - - accept - - - {isStatusModalOpen && } ); }; diff --git a/src/pages/Chats/MatchingRoom/Card/styles.tsx b/src/pages/Chats/MatchingRoom/Card/styles.tsx index 3d345859..73eb31a8 100644 --- a/src/pages/Chats/MatchingRoom/Card/styles.tsx +++ b/src/pages/Chats/MatchingRoom/Card/styles.tsx @@ -1,24 +1,20 @@ import { styled } from 'styled-components'; -import ArrowIcon from '@assets/arrow/min-right.svg'; - export const CardLayout = styled.div` - background-color: ${({ theme }) => theme.colors.background.divider}; border-radius: 0.5rem; position: relative; - height: 100%; `; export const ProfileContainer = styled.div` display: grid; grid-template-columns: auto 1fr auto; align-items: center; - padding: 1rem 0.5rem; + padding: 0.5rem 0; `; export const ProfileImgBox = styled.div` - width: 3.25rem; - height: 3.25rem; + width: 2.25rem; + height: 2.25rem; margin-right: 0.5rem; border-radius: 50%; cursor: pointer; @@ -45,35 +41,19 @@ export const ProfileInfo = styled.div` } `; -export const SeeMore = styled.div` - cursor: pointer; - display: flex; - align-items: center; - margin-bottom: 2.13rem; -`; - -export const ArrowButton = styled.button` - width: 1.125rem; - height: 1.125rem; - background-image: url(${ArrowIcon}); - background-repeat: no-repeat; - background-position: center; -`; - export const OOTDImgBox = styled.div` position: relative; width: 100%; - height: 100%; - border-radius: 0 0 0.5rem 0.5rem; + height: fit-content; + margin-bottom: 0.5rem; + border-radius: 0.5rem; overflow: hidden; display: flex; justify-content: center; align-items: center; - aspect-ratio: 1/1; .slide-image-small { width: 100%; - max-width: 640px; height: 100%; object-fit: contain; } @@ -136,24 +116,3 @@ export const OOTDImgBackground = styled.div<{ $src: string }>` background-repeat: no-repeat; background-size: cover; `; - -export const Reaction = styled.div` - position: absolute; - bottom: 0; - padding: 1rem 0rem; - display: flex; - align-items: center; - gap: 0.9375rem; - z-index: 100; -`; - -export const Btn = styled.button` - cursor: pointer; - width: 3.5rem; - height: 3.5rem; - background-color: transparent; - - display: flex; - justify-content: center; - align-items: center; -`; From 7efd26af1c63048c68abeb8f437a2bb77c173e20 Mon Sep 17 00:00:00 2001 From: young Date: Thu, 27 Feb 2025 17:09:42 +0900 Subject: [PATCH 08/13] =?UTF-8?q?feat:=20=EB=A7=A4=EC=B9=AD=20=EC=88=98?= =?UTF-8?q?=EB=9D=BD/=EA=B1=B0=EC=A0=88=20=EB=B2=84=ED=8A=BC=20UI=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Chats/MatchingRoom/MatchingMessage/index.tsx | 2 +- .../Chats/MatchingRoom/ResponseMessage/index.tsx | 16 ++++++++++++++++ .../MatchingRoom/ResponseMessage/styles.tsx | 15 +++++++++++++++ src/pages/Chats/MatchingRoom/index.tsx | 11 ++++++++++- 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx create mode 100644 src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx diff --git a/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx b/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx index fa144aec..f215eb76 100644 --- a/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx +++ b/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx @@ -16,7 +16,7 @@ const MatchingMessage: React.FC = ({ id, message, createdAt, chatR fromUserNickname: '오딩이', profilePictureUrl: '', content: '얘가 너 소개받고 싶대', - isSenderChanged: true, + isSenderChanged: false, isProfileImageVisible: true, isTimeVisible: false, formattedTime, diff --git a/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx b/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx new file mode 100644 index 00000000..34491201 --- /dev/null +++ b/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx @@ -0,0 +1,16 @@ +import { ResponseButton, ResponseContainer } from './styles'; + +export interface ResponseMessageProps { + requestStatus: 'accepted' | 'rejected' | 'pending'; +} + +const ResponseMessage: React.FC = ({ requestStatus }) => { + return ( + + {(requestStatus === 'pending' || 'accepted') && 거절} + {(requestStatus === 'pending' || 'rejected') && 수락} + + ); +}; + +export default ResponseMessage; diff --git a/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx b/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx new file mode 100644 index 00000000..dd3e25c6 --- /dev/null +++ b/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx @@ -0,0 +1,15 @@ +import { styled } from 'styled-components'; + +export const ResponseContainer = styled.div` + display: flex; + gap: 0.5rem; + justify-content: flex-end; +`; + +export const ResponseButton = styled.button` + padding: 0.4rem 0.8rem; + margin: 0.5rem 0; + background-color: #f2f2f2; + border-radius: 0.5rem; + overflow-wrap: break-word; +`; diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 5240ab50..1ab285b8 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -10,8 +10,11 @@ import Back from '@assets/arrow/left.svg'; import { OODDFrame } from '@components/Frame/Frame'; import TopBar from '@components/TopBar'; +import ChatBox from '../ChatBox'; + import MatchingMessage from './MatchingMessage'; import NoMatchingMessage from './NoMatchingMessage'; +import ResponseMessage from './ResponseMessage'; import { MessagesContainer } from './styles'; const MatchingRoom: React.FC = () => { @@ -92,11 +95,17 @@ const MatchingRoom: React.FC = () => { ) : ( allMatchings.map((matching: MatchingData) => { // TODO: 매칭 상태에 따라 응답 버튼 렌더링 - return ; + return ( +
+ + +
+ ); }) )}
+ ); }; From 6077165838e0a509950eed09e518f5084ecf1a2e Mon Sep 17 00:00:00 2001 From: young Date: Thu, 27 Feb 2025 17:12:22 +0900 Subject: [PATCH 09/13] =?UTF-8?q?feat:=20MatchingRoom=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=20=EC=B1=97=EB=B0=95=EC=8A=A4=20disabled=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chats/ChatBox/index.tsx | 6 +++--- src/pages/Chats/MatchingRoom/index.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/Chats/ChatBox/index.tsx b/src/pages/Chats/ChatBox/index.tsx index 8931cc03..1661a2eb 100644 --- a/src/pages/Chats/ChatBox/index.tsx +++ b/src/pages/Chats/ChatBox/index.tsx @@ -11,7 +11,7 @@ import SendIcon from '@assets/default/send-message.svg'; import { ChatBoxContainer, Textarea, SendButton } from './styles'; -const ChatBox: React.FC = () => { +const ChatBox: React.FC<{ disabled?: boolean }> = ({ disabled = false }) => { const [newMessage, setNewMessage] = useState(''); const textareaRef = useRef(null); const socket = useSocket(); @@ -22,9 +22,9 @@ const ChatBox: React.FC = () => { const isOtherUserValid = !!(otherUser && otherUser.id); useEffect(() => { - if (textareaRef.current && !isOtherUserValid) { + if (textareaRef.current && (!isOtherUserValid || disabled)) { textareaRef.current.disabled = true; - textareaRef.current.placeholder = '메시지를 보낼 수 없습니다.'; + textareaRef.current.placeholder = '메시지를 보낼 수 없는 채팅방입니다.'; } }, []); diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 1ab285b8..767fc2a2 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -105,7 +105,7 @@ const MatchingRoom: React.FC = () => { )}
- + ); }; From d41b30c0ee6087d3d7d5a96268ee29f0b4aa9f07 Mon Sep 17 00:00:00 2001 From: young Date: Thu, 27 Feb 2025 17:38:02 +0900 Subject: [PATCH 10/13] =?UTF-8?q?fix:=20=EB=A7=A4=EC=B9=AD=20=EA=B1=B0?= =?UTF-8?q?=EC=A0=88/=EC=88=98=EB=9D=BD=20UI=20=EB=B0=8F=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MatchingRoom/ResponseMessage/index.tsx | 26 ++++++++++++++++--- .../MatchingRoom/ResponseMessage/styles.tsx | 3 ++- src/pages/Chats/MatchingRoom/index.tsx | 5 ++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx b/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx index 34491201..59bbe7a0 100644 --- a/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx +++ b/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx @@ -1,14 +1,34 @@ +import { useSocket } from '@context/SocketProvider'; + import { ResponseButton, ResponseContainer } from './styles'; export interface ResponseMessageProps { + matchingId: number; requestStatus: 'accepted' | 'rejected' | 'pending'; } -const ResponseMessage: React.FC = ({ requestStatus }) => { +const ResponseMessage: React.FC = ({ matchingId, requestStatus }) => { + const socket = useSocket('matching'); + const isPending = requestStatus === 'pending'; + + const handlebuttonClick = (status: 'accept' | 'reject') => { + if (socket) { + socket.emit('patchMatching', { id: matchingId, requestStatus: status }); + } + }; + return ( - {(requestStatus === 'pending' || 'accepted') && 거절} - {(requestStatus === 'pending' || 'rejected') && 수락} + {(requestStatus === 'pending' || requestStatus === 'rejected') && ( + handlebuttonClick('reject')}> + 거절 + + )} + {(requestStatus === 'pending' || requestStatus === 'accepted') && ( + handlebuttonClick('accept')}> + 수락 + + )} ); }; diff --git a/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx b/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx index dd3e25c6..dab174ff 100644 --- a/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx +++ b/src/pages/Chats/MatchingRoom/ResponseMessage/styles.tsx @@ -6,7 +6,8 @@ export const ResponseContainer = styled.div` justify-content: flex-end; `; -export const ResponseButton = styled.button` +export const ResponseButton = styled.button<{ $isPending: boolean }>` + cursor: ${({ $isPending }) => `${$isPending ? 'pointer' : 'default'}`}; padding: 0.4rem 0.8rem; margin: 0.5rem 0; background-color: #f2f2f2; diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 767fc2a2..c6561b25 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -62,7 +62,8 @@ const MatchingRoom: React.FC = () => { }; const getNewMatching = (data: MatchingData) => { - console.log(data); + if (JSON.stringify(data) === '{}') return; + setAllMatchings([...allMatchings, data]); }; if (socket) { @@ -98,7 +99,7 @@ const MatchingRoom: React.FC = () => { return (
- +
); }) From 4acece69a2c7df8c0a5e779a15cbe553ea81ee4a Mon Sep 17 00:00:00 2001 From: young Date: Thu, 27 Feb 2025 19:00:39 +0900 Subject: [PATCH 11/13] =?UTF-8?q?fix:=20=EC=83=88=20=EB=A7=A4=EC=B9=AD?= =?UTF-8?q?=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EB=95=8C=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chats/MatchingRoom/Card/styles.tsx | 1 + .../Chats/MatchingRoom/MatchingMessage/index.tsx | 10 ++++++---- src/pages/Chats/MatchingRoom/index.tsx | 11 +++++++---- src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx | 2 -- src/pages/Chats/RecentChat/index.tsx | 8 ++++---- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/pages/Chats/MatchingRoom/Card/styles.tsx b/src/pages/Chats/MatchingRoom/Card/styles.tsx index 73eb31a8..57fd1b78 100644 --- a/src/pages/Chats/MatchingRoom/Card/styles.tsx +++ b/src/pages/Chats/MatchingRoom/Card/styles.tsx @@ -3,6 +3,7 @@ import { styled } from 'styled-components'; export const CardLayout = styled.div` border-radius: 0.5rem; position: relative; + height: fit-content; `; export const ProfileContainer = styled.div` diff --git a/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx b/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx index f215eb76..ee9ad9fc 100644 --- a/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx +++ b/src/pages/Chats/MatchingRoom/MatchingMessage/index.tsx @@ -28,7 +28,7 @@ const MatchingMessage: React.FC = ({ id, message, createdAt, chatR content: message, isSenderChanged: false, isProfileImageVisible: false, - isTimeVisible: true, + isTimeVisible: false, formattedTime, }; @@ -41,9 +41,11 @@ const MatchingMessage: React.FC = ({ id, message, createdAt, chatR return ( <> - - - +
+ + + +
); }; diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index c6561b25..98b2210a 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -19,6 +19,7 @@ import { MessagesContainer } from './styles'; const MatchingRoom: React.FC = () => { const [allMatchings, setAllMatchings] = useState([]); + const [hasNewMatching, setHasNewMatching] = useState(true); const [isLoading, setIsLoading] = useState(true); const [isScroll, setIsScroll] = useState(false); @@ -55,15 +56,17 @@ const MatchingRoom: React.FC = () => { useEffect(() => { // 전체 매칭 불러오기 socket api const getAllMatchings = ({ matching }: { matching: MatchingData[] }) => { - console.log(allMatchings); setAllMatchings(matching); setIsScroll(true); setIsLoading(false); }; const getNewMatching = (data: MatchingData) => { - if (JSON.stringify(data) === '{}') return; - setAllMatchings([...allMatchings, data]); + if (JSON.stringify(data) === '{}') { + setHasNewMatching(false); + } else { + setAllMatchings([...allMatchings, data]); + } }; if (socket) { @@ -95,7 +98,6 @@ const MatchingRoom: React.FC = () => { ) : ( allMatchings.map((matching: MatchingData) => { - // TODO: 매칭 상태에 따라 응답 버튼 렌더링 return (
@@ -104,6 +106,7 @@ const MatchingRoom: React.FC = () => { ); }) )} + {!hasNewMatching && }
diff --git a/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx b/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx index 8c25445d..3900110b 100644 --- a/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx +++ b/src/pages/Chats/RecentChat/MatchingRoomItem/index.tsx @@ -38,8 +38,6 @@ const MatchingRoomItem: React.FC> = ({ requestStatus } }, []); - console.log(requestStatus); - return ( diff --git a/src/pages/Chats/RecentChat/index.tsx b/src/pages/Chats/RecentChat/index.tsx index c7d1200e..97bd23bd 100644 --- a/src/pages/Chats/RecentChat/index.tsx +++ b/src/pages/Chats/RecentChat/index.tsx @@ -35,12 +35,12 @@ const RecentChat: React.FC = () => { // 최근 매칭 조회 const getLatestMatching = (data: LatestMatchingData) => { setLatestMatching(data); - console.log('getLatestMatching'); }; - const matchingNotFound = (data: LatestMatchingData) => { - console.log(data); - console.log('matchingNotFound'); + const matchingNotFound = (data: { joinedAt: Date }) => { + setLatestMatching({ + createdAt: data.joinedAt, + }); }; if (socket) { From a192cbe58f9c690c67bc6abe26f9c21c78800467 Mon Sep 17 00:00:00 2001 From: young Date: Thu, 27 Feb 2025 19:05:27 +0900 Subject: [PATCH 12/13] =?UTF-8?q?feat:=20=EB=A7=A4=EC=B9=AD=20=EC=88=98?= =?UTF-8?q?=EB=9D=BD=20=EC=8B=9C=20=EC=B1=84=ED=8C=85=EB=B0=A9=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx | 10 +++++++++- src/pages/Chats/MatchingRoom/index.tsx | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx b/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx index 59bbe7a0..755dc575 100644 --- a/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx +++ b/src/pages/Chats/MatchingRoom/ResponseMessage/index.tsx @@ -1,19 +1,27 @@ +import { useNavigate } from 'react-router-dom'; + import { useSocket } from '@context/SocketProvider'; import { ResponseButton, ResponseContainer } from './styles'; export interface ResponseMessageProps { matchingId: number; + chatRoomId: number; requestStatus: 'accepted' | 'rejected' | 'pending'; } -const ResponseMessage: React.FC = ({ matchingId, requestStatus }) => { +const ResponseMessage: React.FC = ({ matchingId, chatRoomId, requestStatus }) => { const socket = useSocket('matching'); const isPending = requestStatus === 'pending'; + const nav = useNavigate(); const handlebuttonClick = (status: 'accept' | 'reject') => { + if (requestStatus !== 'pending') return; if (socket) { socket.emit('patchMatching', { id: matchingId, requestStatus: status }); + if (status === 'accept') { + nav(`/chats/${chatRoomId}`); + } } }; diff --git a/src/pages/Chats/MatchingRoom/index.tsx b/src/pages/Chats/MatchingRoom/index.tsx index 98b2210a..7dc8eda3 100644 --- a/src/pages/Chats/MatchingRoom/index.tsx +++ b/src/pages/Chats/MatchingRoom/index.tsx @@ -98,10 +98,15 @@ const MatchingRoom: React.FC = () => { ) : ( allMatchings.map((matching: MatchingData) => { + console.log(matching); return (
- +
); }) From bf1116e3ae9382f2043b3947dfac1ba1e2c1e506 Mon Sep 17 00:00:00 2001 From: young Date: Thu, 27 Feb 2025 19:29:56 +0900 Subject: [PATCH 13/13] =?UTF-8?q?refactor:=20=EB=A7=A4=EC=B9=AD=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=EC=9D=84=20=EC=86=8C=EC=BC=93=20api=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/OOTD/Feed/index.tsx | 35 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/pages/Home/OOTD/Feed/index.tsx b/src/pages/Home/OOTD/Feed/index.tsx index 663e420a..a36383ce 100644 --- a/src/pages/Home/OOTD/Feed/index.tsx +++ b/src/pages/Home/OOTD/Feed/index.tsx @@ -10,10 +10,10 @@ import 'swiper/css/pagination'; import theme from '@styles/theme'; -import { createMatchingApi } from '@apis/matching'; import { togglePostLikeStatusApi } from '@apis/post-like'; import { postUserBlockApi } from '@apis/user-block'; import { handleError } from '@apis/util/handleError'; +import { useSocket } from '@context/SocketProvider'; import { getCurrentUserId } from '@utils/getCurrentUserId'; import defaultProfile from '@assets/default/defaultProfile.svg'; @@ -29,7 +29,6 @@ import OptionsBottomSheet from '@components/BottomSheet/OptionsBottomSheet'; import Modal from '@components/Modal'; import { StyledText } from '@components/Text/StyledText'; -import type { CreateMatchingRequest } from '@apis/matching/dto'; import type { PostUserBlockRequest } from '@apis/user-block/dto'; import type { CommentBottomSheetProps } from '@components/BottomSheet/CommentBottomSheet/dto'; import { OptionsBottomSheetProps } from '@components/BottomSheet/OptionsBottomSheet/dto'; @@ -65,6 +64,8 @@ const Feed: React.FC = ({ feed }) => { const currentUserId = getCurrentUserId(); const timeAgo = dayjs(feed.createdAt).locale('ko').fromNow(); + const socket = useSocket('matching'); + const handleMoreButtonClick = (e: React.MouseEvent) => { e.stopPropagation(); setIsOptionsBottomSheetOpen(true); @@ -141,26 +142,22 @@ const Feed: React.FC = ({ feed }) => { } }; - // 매칭 생성 api - const createMatching = async (comment: string) => { - try { - const matchingRequest: CreateMatchingRequest = { - requesterId: currentUserId || -1, - targetId: feed.user.id || -1, - message: comment, - }; - const response = await createMatchingApi(matchingRequest); + // 매칭 신청 socket api + const createMatching = (comment: string) => { + socket.emit('requestMatching', { + requesterId: currentUserId, + targetId: feed.user.id, + message: comment, + }); - if (response.isSuccess) { - setModalContent(`${feed.user.nickname} 님에게 대표 OOTD와\n한 줄 메세지를 보냈어요!`); - } - } catch (error) { - const errorMessage = handleError(error, 'user'); - setModalContent(errorMessage); - } finally { + socket.on('error', (data) => { + setModalContent(data); setIsMatchingCommentBottomSheetOpen(false); setIsStatusModalOpen(true); - } + + // 리스너가 중복 등록되지 않도록 바로 정리 + socket.off('error'); + }); }; // 게시글 옵션(더보기) 바텀시트