From 42a835d7e73cd4ab2cd690efdef3a2ad4fd884f0 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:28:29 +0900 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20mostSearchedBooks=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EB=B0=80=EB=A6=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/search/MostSearchedBooks.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/search/MostSearchedBooks.tsx b/src/components/search/MostSearchedBooks.tsx index 2cb711cb..8f702499 100644 --- a/src/components/search/MostSearchedBooks.tsx +++ b/src/components/search/MostSearchedBooks.tsx @@ -143,7 +143,9 @@ const BookTitle = styled.span` overflow: hidden; text-overflow: ellipsis; margin-left: 8px; - flex-shrink: 1; + flex: 1 1 0%; + min-width: 0; + max-width: calc(100% - 77px); `; const EmptyMessage = styled.div` From a4a8fc1b383156bdeb8bd36394aa3573b7c52d87 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:47:45 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20filledSaveIcon=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/common/filledSaveIcon.svg | 4 ++-- src/pages/searchBook/SearchBook.styled.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/assets/common/filledSaveIcon.svg b/src/assets/common/filledSaveIcon.svg index 9fffb2da..5aee517d 100644 --- a/src/assets/common/filledSaveIcon.svg +++ b/src/assets/common/filledSaveIcon.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/pages/searchBook/SearchBook.styled.ts b/src/pages/searchBook/SearchBook.styled.ts index a9baa3d4..d84cf29d 100644 --- a/src/pages/searchBook/SearchBook.styled.ts +++ b/src/pages/searchBook/SearchBook.styled.ts @@ -117,7 +117,7 @@ export const ButtonSection = styled.div` export const RecruitingGroupButton = styled.button` width: 100%; - height: 48px; + height: 44px; border: 1px solid ${colors.grey[200]}; border-radius: 12px; background: transparent; @@ -140,7 +140,7 @@ export const RightArea = styled.div` export const WritePostButton = styled.button` flex: 1; - height: 48px; + height: 44px; background-color: ${colors.purple.main}; color: ${colors.white}; font-size: ${typography.fontSize.base}; From a041efc459949c578894079a01de5a4bb8f87279 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:55:34 +0900 Subject: [PATCH 3/7] =?UTF-8?q?design:=20searchBook=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/Filter.tsx | 1 - src/pages/searchBook/SearchBook.styled.ts | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/common/Filter.tsx b/src/components/common/Filter.tsx index e3023c45..1fffdd8b 100644 --- a/src/components/common/Filter.tsx +++ b/src/components/common/Filter.tsx @@ -49,7 +49,6 @@ const Container = styled.div` position: relative; justify-content: center; align-items: center; - width: 85px; `; const Text = styled.p` diff --git a/src/pages/searchBook/SearchBook.styled.ts b/src/pages/searchBook/SearchBook.styled.ts index d84cf29d..a0113940 100644 --- a/src/pages/searchBook/SearchBook.styled.ts +++ b/src/pages/searchBook/SearchBook.styled.ts @@ -157,8 +157,8 @@ export const WritePostButton = styled.button` `; export const SaveButton = styled.button` - width: 48px; - height: 48px; + width: 44px; + height: 44px; background: transparent; border: none; border-radius: 12px; @@ -170,7 +170,7 @@ export const SaveButton = styled.button` export const FeedSection = styled.section` display: flex; - width: 96%; + width: 100%; flex-direction: column; margin-top: 20px; `; @@ -179,7 +179,7 @@ export const FeedTitle = styled.h2` color: ${colors.white}; font-size: ${typography.fontSize.lg}; font-weight: ${typography.fontWeight.semibold}; - padding: 20px; + padding: 24px 20px 8px 20px; `; export const FilterContainer = styled.div` From db8ac2547a531bb6934f1374a88720515176be44 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:02:43 +0900 Subject: [PATCH 4/7] =?UTF-8?q?design:=20GroupDetail=20header=20=EB=A7=90?= =?UTF-8?q?=EC=A4=84=EC=9E=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/groupDetail/GroupDetail.styled.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/pages/groupDetail/GroupDetail.styled.ts b/src/pages/groupDetail/GroupDetail.styled.ts index f80a5cff..888119cd 100644 --- a/src/pages/groupDetail/GroupDetail.styled.ts +++ b/src/pages/groupDetail/GroupDetail.styled.ts @@ -152,11 +152,27 @@ export const BookSection = styled.section` export const BookHeader = styled.div` display: flex; - justify-content: space-between; + flex-direction: row; align-items: center; color: ${colors.white}; font-size: ${typography.fontSize['base']}; font-weight: ${typography.fontWeight.medium}; + width: 100%; + gap: 0; + + h3 { + flex: 1 1 0%; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: 0; + font-size: inherit; + font-weight: inherit; + } + img { + flex-shrink: 0; + } `; export const BookInfo = styled.div` From 88b3e2b19fcb19eaf98bb5d7faecd5486c5eb5cf Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 02:16:19 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20getMyRooms=20API=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/rooms/getMyRooms.ts | 1 + src/components/group/GroupCard.tsx | 3 +- src/components/group/MyGroupBox.tsx | 1 + src/components/group/MyGroupModal.tsx | 161 ++++++++++++++++++++++---- 4 files changed, 142 insertions(+), 24 deletions(-) diff --git a/src/api/rooms/getMyRooms.ts b/src/api/rooms/getMyRooms.ts index 445f0627..438ef95e 100644 --- a/src/api/rooms/getMyRooms.ts +++ b/src/api/rooms/getMyRooms.ts @@ -11,6 +11,7 @@ export interface Room { memberCount: number; endDate: string; type: string; + isPublic: boolean; } // 내 방 조회 응답 타입 diff --git a/src/components/group/GroupCard.tsx b/src/components/group/GroupCard.tsx index c77af0cc..e8d214dc 100644 --- a/src/components/group/GroupCard.tsx +++ b/src/components/group/GroupCard.tsx @@ -12,6 +12,7 @@ interface Props { isRecommend?: boolean; onClick?: () => void; isFirstCard?: boolean; + isPublic?: boolean; } export const GroupCard = forwardRef( @@ -20,7 +21,7 @@ export const GroupCard = forwardRef( - {group.isOnGoing === false && ( + {group.isPublic === false && ( locked diff --git a/src/components/group/MyGroupBox.tsx b/src/components/group/MyGroupBox.tsx index 0cf2fc7e..05328dc2 100644 --- a/src/components/group/MyGroupBox.tsx +++ b/src/components/group/MyGroupBox.tsx @@ -19,6 +19,7 @@ export interface Group { deadLine?: string; genre?: string; isOnGoing?: boolean; + isPublic?: boolean; } const convertJoinedRoomToGroup = (room: JoinedRoomItem): Group => ({ diff --git a/src/components/group/MyGroupModal.tsx b/src/components/group/MyGroupModal.tsx index ec522758..29f80f7e 100644 --- a/src/components/group/MyGroupModal.tsx +++ b/src/components/group/MyGroupModal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import styled from '@emotion/styled'; import TitleHeader from '../common/TitleHeader'; import leftArrow from '../../assets/common/leftArrow.svg'; @@ -14,11 +14,20 @@ interface MyGroupModalProps { } export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { + useEffect(() => { + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = ''; + }; + }, []); const navigate = useNavigate(); const [selected, setSelected] = useState<'진행중' | '모집중' | ''>(''); const [rooms, setRooms] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); + const [nextCursor, setNextCursor] = useState(null); + const [isLast, setIsLast] = useState(false); + const contentRef = useRef(null); const convertRoomToGroup = (room: Room): Group => { return { @@ -31,6 +40,7 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { deadLine: room.endDate || '', genre: '', isOnGoing: room.type === 'playing' || room.type === 'playingAndRecruiting', + isPublic: room.isPublic, }; }; @@ -39,6 +49,8 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { try { setIsLoading(true); setError(null); + setNextCursor(null); + setIsLast(false); const roomType: RoomType = selected === '진행중' @@ -51,6 +63,8 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { if (response.isSuccess) { setRooms(response.data.roomList); + setNextCursor(response.data.nextCursor); + setIsLast(response.data.isLast); } else { setError(response.message); } @@ -65,6 +79,104 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { fetchRooms(); }, [selected]); + const isFetchingRef = useRef(false); + + const loadMore = async () => { + if (isFetchingRef.current || isLast || !nextCursor) return; + + isFetchingRef.current = true; + setIsLoading(true); + try { + const roomType: RoomType = + selected === '진행중' + ? 'playing' + : selected === '모집중' + ? 'recruiting' + : 'playingAndRecruiting'; + + const res = await getMyRooms(roomType, nextCursor); + if (res.isSuccess) { + setRooms(prev => [...prev, ...res.data.roomList]); + setNextCursor(res.data.nextCursor); + setIsLast(res.data.isLast); + } else { + setError(res.message); + } + } catch (e) { + console.log(e); + setError('방 목록을 불러오는데 실패했습니다.'); + } finally { + setIsLoading(false); + isFetchingRef.current = false; + } + }; + + const handleScroll = async (e: React.UIEvent) => { + const el = e.currentTarget; + const { scrollTop, scrollHeight, clientHeight } = el; + if (scrollHeight - scrollTop - clientHeight < 100) { + await loadMore(); + } + }; + + useEffect(() => { + const fetchRooms = async () => { + try { + setIsLoading(true); + setError(null); + setNextCursor(null); + setIsLast(false); + + const roomType: RoomType = + selected === '진행중' + ? 'playing' + : selected === '모집중' + ? 'recruiting' + : 'playingAndRecruiting'; + + const res = await getMyRooms(roomType, null); + if (res.isSuccess) { + setRooms(res.data.roomList); + setNextCursor(res.data.nextCursor); + setIsLast(res.data.isLast); + } else { + setError(res.message); + } + } catch (e) { + console.log(e); + setError('방 목록을 불러오는데 실패했습니다.'); + } finally { + setIsLoading(false); + } + }; + + fetchRooms(); + }, [selected]); + + useEffect(() => { + const tryFill = async () => { + if (!contentRef.current || isLast) return; + let guard = 2; // 최대 3페이지까지 자동 프리로드(필요시 늘리기) + while ( + guard-- > 0 && + contentRef.current && + contentRef.current.scrollHeight <= contentRef.current.clientHeight && + !isLast && + nextCursor + ) { + await loadMore(); + await new Promise(requestAnimationFrame); + } + }; + tryFill(); + }, [rooms, nextCursor, isLast]); + + useEffect(() => { + if (contentRef.current) { + contentRef.current.scrollTo({ top: 0, behavior: 'smooth' }); + } + }, [selected]); + const convertedGroups = rooms.map(convertRoomToGroup); const handleGroupCardClick = (group: Group) => { @@ -101,22 +213,22 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => { ))} - - {isLoading ? ( - 로딩 중... - ) : error ? ( - {error} - ) : convertedGroups.length > 0 ? ( - convertedGroups.map(group => ( - handleGroupCardClick(group)} - /> - )) - ) : ( + + {error && {error}} + + {convertedGroups.map(group => ( + handleGroupCardClick(group)} + /> + ))} + + {isLoading && 불러오는 중…} + + {!isLoading && convertedGroups.length === 0 && ( {selected === '진행중' @@ -163,21 +275,24 @@ const Content = styled.div` gap: 20px; overflow-y: auto; padding: 0 20px 20px 20px; - grid-template-columns: 1fr; - + scrollbar-width: none; + -ms-overflow-style: none; + &::-webkit-scrollbar { + display: none; + } @media (min-width: 584px) { grid-template-columns: 1fr 1fr; } `; -const LoadingMessage = styled.div` +const BottomSpinner = styled.div` display: flex; justify-content: center; align-items: center; - padding: 40px 20px; - color: #fff; - font-size: ${typography.fontSize.base}; + padding: 16px 0 24px; + color: ${colors.grey[100]}; + font-size: ${typography.fontSize.sm}; `; const ErrorMessage = styled.div` From 17454a277df0c68dafdd28c8de4430b5653d1720 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 02:35:39 +0900 Subject: [PATCH 6/7] =?UTF-8?q?design:=20=EC=A0=84=EC=97=AD=20var=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feed/BookInfoCard.tsx | 15 +++++++------ src/components/group/MyGroupCard.tsx | 25 +++++++++++---------- src/components/group/RecruitingGroupBox.tsx | 8 +++---- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/feed/BookInfoCard.tsx b/src/components/feed/BookInfoCard.tsx index a4c9563f..2255ce3e 100644 --- a/src/components/feed/BookInfoCard.tsx +++ b/src/components/feed/BookInfoCard.tsx @@ -1,6 +1,7 @@ import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; import rightArrow from '../../assets/common/rightArrow.svg'; +import { colors, typography } from '@/styles/global/global'; interface BookInfoCardProps { bookTitle: string; @@ -37,17 +38,17 @@ const BookContainer = styled.div` align-items: center; justify-content: space-between; border-radius: 12px; - background: var(--color-darkgrey-main); + background: ${colors.darkgrey.main}; cursor: pointer; .left { overflow: hidden; width: 220px; white-space: nowrap; - color: var(--color-white); + color: ${colors.white}; text-overflow: ellipsis; - font-size: var(--font-size-base); - font-weight: var(--font-weight-semibold); + font-size: ${typography.fontSize.base}; + font-weight: ${typography.fontWeight.semibold}; line-height: 24px; } @@ -56,12 +57,12 @@ const BookContainer = styled.div` flex-direction: row; gap: 4px; overflow: hidden; - color: var(--color-grey-100); + color: ${colors.grey[100]}; text-align: right; text-overflow: ellipsis; - font-size: var(--font-size-xs); + font-size: ${typography.fontSize.xs}; font-style: normal; - font-weight: var(--font-weight-regular); + font-weight: ${typography.fontWeight.regular}; line-height: 24px; .name { diff --git a/src/components/group/MyGroupCard.tsx b/src/components/group/MyGroupCard.tsx index ebbb1921..2aece52d 100644 --- a/src/components/group/MyGroupCard.tsx +++ b/src/components/group/MyGroupCard.tsx @@ -2,6 +2,7 @@ import { forwardRef } from 'react'; import styled from '@emotion/styled'; import peopleImg from '../../assets/common/people.svg'; import type { Group } from './MyGroupBox'; +import { colors, typography } from '@/styles/global/global'; interface MyGroupCardProps { group: Group; @@ -66,8 +67,8 @@ const Info = styled.div` `; const CardTitle = styled.h2` - font-size: var(--font-size-large01); - font-weight: var(--font-weight-semibold); + font-size: ${typography.fontSize.lg}; + font-weight: ${typography.fontWeight.semibold}; color: #000; margin: 0; white-space: nowrap; @@ -79,9 +80,9 @@ const Participants = styled.p` display: flex; align-items: center; gap: 4px; - font-size: var(--font-size-small03); - font-weight: var(--font-weight-medium); - color: var(--color-grey-300); + font-size: ${typography.fontSize.xs}; + font-weight: ${typography.fontWeight.medium}; + color: ${colors.grey[300]}; margin: 8px 0; > span { line-height: 20px; @@ -89,21 +90,21 @@ const Participants = styled.p` `; const ProgressText = styled.p` - font-size: var(--font-size-medium01); - color: var(--color-grey-300); + font-size: ${typography.fontSize.sm}; + color: ${colors.grey[300]}; margin: 12px 0; `; const Percent = styled.span` - font-size: var(--font-size-medium02); - color: var(--color-purple-main); - font-weight: var(--font-weight-semibold); + font-size: ${typography.fontSize.base}; + color: ${colors.purple.main}; + font-weight: ${typography.fontWeight.semibold}; `; const Bar = styled.div` width: 100%; height: 6px; - background: var(--color-grey-300); + background: ${colors.grey[300]}; border-radius: 4px; margin-top: 4px; `; @@ -111,6 +112,6 @@ const Bar = styled.div` const Fill = styled.div<{ width: number }>` width: ${({ width }) => width}%; height: 100%; - background-color: var(--color-purple-main); + background-color: ${colors.purple.main}; border-radius: 4px; `; diff --git a/src/components/group/RecruitingGroupBox.tsx b/src/components/group/RecruitingGroupBox.tsx index cfa97b9a..ce08a528 100644 --- a/src/components/group/RecruitingGroupBox.tsx +++ b/src/components/group/RecruitingGroupBox.tsx @@ -88,8 +88,8 @@ const Container = styled.div` const Title = styled.h2` color: #fff; - font-size: var(--font-size-large02); - font-weight: var(--font-weight-bold); + font-size: ${typography.fontSize.lg}; + font-weight: ${typography.fontWeight.bold}; margin-bottom: 32px; text-align: center; `; @@ -105,8 +105,8 @@ const TabContainer = styled.div` const Tab = styled.button<{ selected?: boolean }>` white-space: nowrap; padding: 8px 12px; - font-size: var(--font-size-small03); - font-weight: var(--font-weight-regular); + font-size: ${typography.fontSize.xs}; + font-weight: ${typography.fontWeight.regular}; border: none; border-radius: 16px; background: ${({ selected }) => From 2cab5ef30f98c2153a9c691091f937b8dd1b9767 Mon Sep 17 00:00:00 2001 From: Ji Ho June <129824629+ho0010@users.noreply.github.com> Date: Thu, 21 Aug 2025 02:41:06 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20GroupDetail=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/groupDetail/GroupDetail.tsx | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/pages/groupDetail/GroupDetail.tsx b/src/pages/groupDetail/GroupDetail.tsx index 4e66d783..4b7a106b 100644 --- a/src/pages/groupDetail/GroupDetail.tsx +++ b/src/pages/groupDetail/GroupDetail.tsx @@ -328,21 +328,23 @@ const GroupDetail = () => { - - 이런 모임방은 어때요? - - {recommendRooms.map(room => ( - handleRecommendGroupCardClick(room.roomId)} - /> - ))} - - + {recommendRooms.length > 0 && ( + + 이런 모임방은 어때요? + + {recommendRooms.map(room => ( + handleRecommendGroupCardClick(room.roomId)} + /> + ))} + + + )} {roomData.isHost ? '모집 마감하기' : isJoining ? '참여 취소하기' : '참여하기'}