Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/search/GroupSearchResult.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ export const Tab = styled.button<{ selected?: boolean }>`
cursor: pointer;
`;

export const Content = styled.div`
export const Content = styled.div<{ isRefetching?: boolean }>`
display: flex;
flex-direction: column;
overflow-y: auto;
padding: 0 20px 60px;
opacity: ${({ isRefetching }) => (isRefetching ? 0.45 : 1)};
transition: opacity 0.15s ease;
Comment on lines +29 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 opacity로 전환되는 패턴 너무 좋은 것 같아요! 👍🏻


&::-webkit-scrollbar {
display: none;
Expand Down
10 changes: 7 additions & 3 deletions src/components/search/GroupSearchResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ const GroupSearchResult = ({
onClickRoom,
}: Props) => {
const mapped = useMemo(() => rooms.map(mapToGroupCardModel), [rooms]);
const isEmpty = !isLoading && mapped.length === 0;
// searching 중에는 아직 debounce 대기 중일 수 있으므로 빈 결과 화면을 표시하지 않음
const isEmpty = !isLoading && mapped.length === 0 && type !== 'searching';
// 기존 결과를 유지한 채 재검색 중인 상태 (필터·카테고리 변경 시)
const isRefetching = isLoading && mapped.length > 0;

return (
<>
Expand All @@ -87,7 +90,8 @@ const GroupSearchResult = ({

{(showTabs || type === 'searched') && (
<GroupCardHeader>
<GroupNum>전체 {mapped.length}</GroupNum>
{/* 재검색 중엔 이전 카운트를 유지하고, 초기 검색 완료 시 카운트를 표시 */}
<GroupNum>{isLoading && !isRefetching ? '' : `전체 ${mapped.length}`}</GroupNum>
<Filter
filters={FILTER}
selectedFilter={selectedFilter}
Expand All @@ -96,7 +100,7 @@ const GroupSearchResult = ({
</GroupCardHeader>
)}

<Content>
<Content isRefetching={isRefetching}>
{error && <ErrorText>{error}</ErrorText>}

{isEmpty ? (
Expand Down
6 changes: 5 additions & 1 deletion src/components/search/RecentSearchTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ interface RecentSearchTabsProps {
recentSearches: string[];
handleDelete: (term: string) => void;
handleRecentSearchClick: (term: string) => void;
isLoading?: boolean;
}

const RecentSearchTabs = ({
recentSearches,
handleDelete,
handleRecentSearchClick,
isLoading = false,
}: RecentSearchTabsProps) => {
return (
<Container>
<Title>최근 검색어</Title>
<TabContainer>
{recentSearches.length === 0 ? (
{isLoading ? (
<Text>최근 검색어를 불러오고 있습니다.</Text>
) : recentSearches.length === 0 ? (
<Text>최근 검색어가 아직 없어요.</Text>
) : (
recentSearches.map(recentSearch => (
Expand Down
9 changes: 9 additions & 0 deletions src/pages/feed/MyFeedPage.styled.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import styled from '@emotion/styled';
import { colors } from '@/styles/global/global';

export const Container = styled.div`
min-width: 320px;
max-width: 767px;
margin: 0 auto;
`;

export const LoadingScreen = styled.div`
min-width: 320px;
max-width: 767px;
margin: 0 auto;
min-height: 100dvh;
background-color: ${colors.black.main};
`;
11 changes: 9 additions & 2 deletions src/pages/feed/MyFeedPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import OtherFeed from '@/components/feed/OtherFeed';
import { getOtherFeed, type OtherFeedItem } from '@/api/feeds/getOtherFeed';
import { getOtherProfile } from '@/api/users/getOtherProfile';
import type { OtherProfileData } from '@/types/profile';
import { Container } from './MyFeedPage.styled';
import { Container, LoadingScreen } from './MyFeedPage.styled';

const MyFeedPage = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -53,7 +53,14 @@ const MyFeedPage = () => {
}, [userId]);

if (loading) {
return <></>;
return (
<LoadingScreen>
<TitleHeader
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBackClick}
/>
</LoadingScreen>
);
}

if (error) {
Expand Down
11 changes: 9 additions & 2 deletions src/pages/feed/OtherFeedPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import OtherFeed from '@/components/feed/OtherFeed';
import { getOtherFeed, type OtherFeedItem } from '@/api/feeds/getOtherFeed';
import { getOtherProfile } from '@/api/users/getOtherProfile';
import type { OtherProfileData } from '@/types/profile';
import { Container } from './MyFeedPage.styled';
import { Container, LoadingScreen } from './MyFeedPage.styled';

const OtherFeedPage = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -53,7 +53,14 @@ const OtherFeedPage = () => {
}, [userId]);

if (loading) {
return <></>;
return (
<LoadingScreen>
<TitleHeader
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBackClick}
/>
</LoadingScreen>
);
}

if (error) {
Expand Down
9 changes: 9 additions & 0 deletions src/pages/feed/UserSearch.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ export const SearchBarContainer = styled.div`
export const Content = styled.div`
margin-top: 132px;
`;

export const LoadingMessage = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding: 40px 20px;
color: ${colors.white};
font-size: 16px;
`;
19 changes: 15 additions & 4 deletions src/pages/feed/UserSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useNavigate } from 'react-router-dom';
import { useUserSearch } from '@/hooks/useUserSearch';
import { getRecentSearch, type RecentSearchData } from '@/api/recentsearch/getRecentSearch';
import { deleteRecentSearch } from '@/api/recentsearch/deleteRecentSearch';
import { Content, SearchBarContainer, Wrapper } from './UserSearch.styled';
import { Content, SearchBarContainer, Wrapper, LoadingMessage } from './UserSearch.styled';

const UserSearch = () => {
const navigate = useNavigate();
Expand All @@ -25,10 +25,18 @@ const UserSearch = () => {
});

const [recentSearches, setRecentSearches] = useState<RecentSearchData[]>([]);
const [isRecentLoading, setIsRecentLoading] = useState(false);

const fetchRecentSearches = async () => {
const response = await getRecentSearch('USER');
setRecentSearches(response.data.recentSearchList);
setIsRecentLoading(true);
try {
const response = await getRecentSearch('USER');
setRecentSearches(response.isSuccess ? response.data.recentSearchList : []);
} catch {
setRecentSearches([]);
} finally {
setIsRecentLoading(false);
}
};

useEffect(() => {
Expand Down Expand Up @@ -100,7 +108,9 @@ const UserSearch = () => {
<Content>
{isSearching ? (
<>
{isSearched ? (
{userList.length === 0 && (loading || !isSearched) ? (
<LoadingMessage>검색 중...</LoadingMessage>
) : isSearched ? (
<UserSearchResult
type={'searched'}
searchedUserList={userList}
Expand All @@ -124,6 +134,7 @@ const UserSearch = () => {
recentSearches={recentSearches.map(item => item.searchTerm)}
handleDelete={handleDeleteWrapper}
handleRecentSearchClick={handleRecentSearchClick}
isLoading={isRecentLoading}
/>
</>
)}
Expand Down
9 changes: 3 additions & 6 deletions src/pages/feed/UserSearchResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ export function UserSearchResult({
hasMore,
onLoadMore,
}: UserSearchResultProps) {
const isEmptySearchedUserList = () => {
if (searchedUserList.length === 0) return true;
else return false;
};
const isEmpty = searchedUserList.length === 0 && type !== 'searching';

const observerRef = useRef<HTMLDivElement>(null);

Expand All @@ -47,8 +44,8 @@ export function UserSearchResult({
<List>
{type === 'searching' ? <></> : <ResultHeader>전체 {searchedUserList.length}</ResultHeader>}

{isEmptySearchedUserList() ? (
<EmptyWrapper>{loading ? '사용자 찾는 중...' : '찾는 사용자가 없어요.'}</EmptyWrapper>
{isEmpty ? (
<EmptyWrapper>찾는 사용자가 없어요.</EmptyWrapper>
) : (
<>
{searchedUserList.map((user, index) => (
Expand Down
48 changes: 39 additions & 9 deletions src/pages/groupSearch/GroupSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const GroupSearch = () => {
const [category, setCategory] = useState<string>('');

const [recentSearches, setRecentSearches] = useState<RecentSearchData[]>([]);
const [isRecentLoading, setIsRecentLoading] = useState(false);
const [searchTimeoutId, setSearchTimeoutId] = useState<NodeJS.Timeout | null>(null);

const [showTabs, setShowTabs] = useState(false);
Expand All @@ -46,11 +47,14 @@ const GroupSearch = () => {

useEffect(() => {
(async () => {
setIsRecentLoading(true);
try {
const response = await getRecentSearch('ROOM');
setRecentSearches(response.isSuccess ? response.data.recentSearchList : []);
} catch {
setRecentSearches([]);
} finally {
setIsRecentLoading(false);
}
})();
}, []);
Expand All @@ -62,11 +66,14 @@ const GroupSearch = () => {
}, [searchStatus]);

const fetchRecentSearches = async () => {
setIsRecentLoading(true);
try {
const response = await getRecentSearch('ROOM');
setRecentSearches(response.isSuccess ? response.data.recentSearchList : []);
} catch {
setRecentSearches([]);
} finally {
setIsRecentLoading(false);
}
};

Expand All @@ -77,12 +84,15 @@ const GroupSearch = () => {
status: 'searching' | 'searched',
categoryParam: string,
isAllCategory: boolean = false,
keepPrevious: boolean = false,
) => {
setIsLoading(true);
setError(null);
setRooms([]);
setNextCursor(null);
setIsLast(true);
if (!keepPrevious) {
setRooms([]);
setNextCursor(null);
setIsLast(true);
}

try {
const isFinalized = status === 'searched';
Expand Down Expand Up @@ -142,7 +152,7 @@ const GroupSearch = () => {
setSearchStatus('searching');
setShowTabs(false);
const id = setTimeout(() => {
searchFirstPage(trimmed, toSortKey(selectedFilter), 'searching', category);
searchFirstPage(trimmed, toSortKey(selectedFilter), 'searching', category, false, true);
}, 300);
setSearchTimeoutId(id);
};
Expand Down Expand Up @@ -195,12 +205,30 @@ const GroupSearch = () => {
useEffect(() => {
if (searchStatus !== 'searched') return;

const term = searchTerm.trim();
const term = searchTermRef.current.trim();
const currentCategory = categoryRef.current;
const isAllCategory = !term && currentCategory === '';

searchFirstPage(
term,
toSortKey(selectedFilterRef.current),
'searched',
currentCategory,
isAllCategory,
true,
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchStatus, searchTerm]);

useEffect(() => {
if (searchStatusRef.current !== 'searched') return;

const term = searchTermRef.current.trim();
const isAllCategory = !term && category === '';

searchFirstPage(term, toSortKey(selectedFilter), 'searched', category, isAllCategory);
searchFirstPage(term, toSortKey(selectedFilter), 'searched', category, isAllCategory, true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedFilter, category, searchStatus, searchTerm]);
}, [selectedFilter, category]);

useEffect(() => {
const term = searchTerm.trim();
Expand All @@ -213,7 +241,7 @@ const GroupSearch = () => {

const id = setTimeout(() => {
const currentCategory = categoryRef.current;
searchFirstPage(term, toSortKey(selectedFilter), 'searching', currentCategory);
searchFirstPage(term, toSortKey(selectedFilter), 'searching', currentCategory, false, true);
}, 300);
setSearchTimeoutId(id);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -320,7 +348,8 @@ const GroupSearch = () => {

{searchStatus !== 'idle' ? (
<>
{isLoading && rooms.length === 0 ? (
{(isLoading && rooms.length === 0) ||
(searchStatus === 'searching' && rooms.length === 0) ? (
<LoadingMessage>검색 중...</LoadingMessage>
) : (
<GroupSearchResult
Expand Down Expand Up @@ -353,6 +382,7 @@ const GroupSearch = () => {
}
}}
handleRecentSearchClick={handleRecentSearchClick}
isLoading={isRecentLoading}
/>
<AllRoomsButton onClick={handleAllRoomsClick}>
<p>전체 모임방 둘러보기</p>
Expand Down