diff --git a/packages/react-native/src/apis/queries/detail/useAroundSpotQuery.ts b/packages/react-native/src/apis/queries/detail/useAroundSpotQuery.ts index eb414602..262f8a03 100644 --- a/packages/react-native/src/apis/queries/detail/useAroundSpotQuery.ts +++ b/packages/react-native/src/apis/queries/detail/useAroundSpotQuery.ts @@ -6,6 +6,7 @@ import QUERY_KEYS from '@/constants/QUERY_KEYS'; interface UseAroundSpotQueryParams { id: number; + workId: number; } interface AroundSpotResponse { @@ -14,12 +15,15 @@ interface AroundSpotResponse { accomodation: SpotResponse[]; } -export default function useAroundSpotQuery({ id }: UseAroundSpotQueryParams) { +export default function useAroundSpotQuery({ + id, + workId, +}: UseAroundSpotQueryParams) { const authAxios = useAuthAxios(); const getAroundSpot = async () => { const result = await authAxios.get>( - `/api/spot/${id}/arounds`, + `/api/spot/${id}/arounds?workId=${workId}`, ); return result.data.result; }; diff --git a/packages/react-native/src/apis/queries/detail/useDetailQuery.ts b/packages/react-native/src/apis/queries/detail/useDetailQuery.ts index 8b530d28..2bc7b280 100644 --- a/packages/react-native/src/apis/queries/detail/useDetailQuery.ts +++ b/packages/react-native/src/apis/queries/detail/useDetailQuery.ts @@ -20,11 +20,17 @@ interface DetailResponse { posterUrl: string; } -export default function useDetailQuery(id: number) { +export default function useDetailQuery({ + id, + workId, +}: { + id: number; + workId: number; +}) { const authAxios = useAuthAxios(); const getSpotAroundInfo = async () => { const result = await authAxios.get>( - `/api/spot/${id}`, + `/api/spot/${id}?workId=${workId}`, ); return result.data.result; }; diff --git a/packages/react-native/src/apis/queries/useSearchQuery.ts b/packages/react-native/src/apis/queries/useSearchQuery.ts new file mode 100644 index 00000000..9b64644c --- /dev/null +++ b/packages/react-native/src/apis/queries/useSearchQuery.ts @@ -0,0 +1,39 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { City, Region } from '@/constants/CITY'; +import useAuthAxios from '../useAuthAxios'; +import { ServerResponse } from '@/types/response'; +import QUERY_KEYS from '@/constants/QUERY_KEYS'; + +interface UseSearchQueryParams { + keyword: string; +} + +interface SearchResponse { + id: number; + contentId: number; + name: string; + region: Region; + city: City; + workId: number; + workName: string; + posterUrl: string; + quote: string; + isLiked: boolean; + likeCount: number; +} + +export default function useSearchQuery({ keyword }: UseSearchQueryParams) { + const authAxios = useAuthAxios(); + const search = async () => { + const result = await authAxios.get>( + `/api/spot/search?keyword=${keyword}`, + ); + + return result.data.result; + }; + + return useSuspenseQuery({ + queryKey: [QUERY_KEYS.SEARCH, keyword], + queryFn: search, + }); +} diff --git a/packages/react-native/src/components/common/Card.tsx b/packages/react-native/src/components/common/Card.tsx index a0298aeb..465dcb82 100644 --- a/packages/react-native/src/components/common/Card.tsx +++ b/packages/react-native/src/components/common/Card.tsx @@ -12,8 +12,17 @@ interface CardProps { } function Default({ data, size = 260 }: CardProps) { - const { isLiked, name, region, city, posterUrl, likeCount, contentId, id } = - data; + const { + isLiked, + name, + region, + city, + posterUrl, + likeCount, + contentId, + id, + workId, + } = data; const navigation = useNavigation>(); return ( @@ -24,7 +33,9 @@ function Default({ data, size = 260 }: CardProps) { > navigation.navigate('Home/Detail', { contentId, id })} + onPress={() => + navigation.navigate('Home/Detail', { contentId, id, workId }) + } activeOpacity={1} > @@ -61,7 +72,7 @@ function Default({ data, size = 260 }: CardProps) { } function Small({ data, size = 180 }: CardProps) { - const { name, region, city, posterUrl, contentId, id } = data; + const { name, region, city, posterUrl, contentId, id, workId } = data; const navigation = useNavigation>(); return ( @@ -72,7 +83,9 @@ function Small({ data, size = 180 }: CardProps) { > navigation.navigate('Home/Detail', { contentId, id })} + onPress={() => + navigation.navigate('Home/Detail', { contentId, id, workId }) + } > diff --git a/packages/react-native/src/constants/QUERY_KEYS.ts b/packages/react-native/src/constants/QUERY_KEYS.ts index 16e8da17..b2c165e2 100644 --- a/packages/react-native/src/constants/QUERY_KEYS.ts +++ b/packages/react-native/src/constants/QUERY_KEYS.ts @@ -12,6 +12,7 @@ const QUERY_KEYS = { TRIP_PLAN_DETAIL: 'tripPlanDetail', EDIT_PLAN: 'editPlan', SPOT_DETAIL: 'spotDetail', + SEARCH: 'search', }; export default QUERY_KEYS; diff --git a/packages/react-native/src/pages/Detail.tsx b/packages/react-native/src/pages/Detail.tsx index 32770600..72b8d36a 100644 --- a/packages/react-native/src/pages/Detail.tsx +++ b/packages/react-native/src/pages/Detail.tsx @@ -14,9 +14,9 @@ import { getDisplayRegion } from '@/utils/getDisplayRegionName'; const Detail = withSuspense(() => { const route = useRoute>(); const navigation = useNavigation>(); - const { id, contentId: paramsContentId } = route.params; + const { id, contentId: paramsContentId, workId } = route.params; - const { data } = useDetailQuery(paramsContentId); + const { data } = useDetailQuery({ id: paramsContentId, workId }); const { like, cancelLike, isLikePending, isCancelLikePending } = useSpotLikeMutation({ contentId: paramsContentId }); diff --git a/packages/react-native/src/pages/Detail/DetailInfo.tsx b/packages/react-native/src/pages/Detail/DetailInfo.tsx index 4d59b022..948abfc6 100644 --- a/packages/react-native/src/pages/Detail/DetailInfo.tsx +++ b/packages/react-native/src/pages/Detail/DetailInfo.tsx @@ -7,8 +7,8 @@ import { StackRouteProps } from '@/types/navigation'; export default function DetailInfo() { const route = useRoute>(); - const { contentId } = route.params; - const { data } = useDetailQuery(contentId); + const { contentId, workId } = route.params; + const { data } = useDetailQuery({ id: contentId, workId }); return ( (); const navigation = useNavigation>(); - const { contentId } = route.params; + const { contentId, workId } = route.params; - const { data } = useAroundSpotQuery({ id: contentId }); + const { data } = useAroundSpotQuery({ id: contentId, workId }); const startLongPress = (spot: SpotResponse) => { if (!longPressMode) { diff --git a/packages/react-native/src/pages/Search.tsx b/packages/react-native/src/pages/Search.tsx index e8c6d968..7a566fd9 100644 --- a/packages/react-native/src/pages/Search.tsx +++ b/packages/react-native/src/pages/Search.tsx @@ -1,70 +1,25 @@ import { useRef, useState } from 'react'; -import { useRoute } from '@react-navigation/native'; -import { View } from 'react-native'; +import { useNavigation, useRoute } from '@react-navigation/native'; +import { TouchableOpacity, View } from 'react-native'; import { Font } from 'design-system'; import Carousel, { ICarouselInstance } from 'react-native-reanimated-carousel'; import BackGroundGradient from '@/layouts/BackGroundGradient'; -import { SpotCardData } from '@/types/spot'; import Card from '@/components/common/Card'; import WordBreak from '@/components/common/WordBreak'; import Header from '@/components/common/Header'; -import { StackRouteProps } from '@/types/navigation'; +import { StackNavigation, StackRouteProps } from '@/types/navigation'; import { DEFAULT_COLOR } from '@/constants/DEFAULT_COLOR'; +import useSearchQuery from '@/apis/queries/useSearchQuery'; +import withSuspense from '@/components/HOC/withSuspense'; -// FIXME: 추후 제거 -const mockData: SpotCardData[] = [ - { - contentId: 10, - name: '주문진 방파제', - region: 1, - city: 20, - - posterUrl: - 'https://i.namu.wiki/i/oIbSW8G0ldkxIJGYLf9e9OzO1n2PkDJMu78IBDV1wh7v06rSdSvlzGUc4znFOc7EH0xi1OLJx3HqzfJgkb514Oj0BPqXU2m0dWl0wwY8coA1A7rQiOcKlLpUfEZ45Ee7bKskmI3RSb760_xrYzysTw.webp', - isLiked: true, - likeCount: 21, - quote: '', - workName: '도깨비', - workId: 1, - }, - { - contentId: 20, - name: '룰루랄라', - region: 1, - city: 20, - - posterUrl: - 'https://i.namu.wiki/i/5YW8uWfQIeY4JG56CkWX8tL8hzeQxsQ9o8aXoLkHiMxHkTJLyRzC0RHdd6zJHctNQYXfhHqddAg48LhqHIgdyTRRH-awxeCSoCSIT04XlPAx_ciulV9hfEPPdir_zbiT0mtWDNtr9a1hewqNhwpAbg.webp', - isLiked: false, - likeCount: 12, - quote: '', - workName: '도깨비', - workId: 1, - }, - { - contentId: 30, - name: '룰루랄라2', - region: 1, - city: 20, - - posterUrl: - 'https://i.namu.wiki/i/x9m5nUyjJvqSTSPX5EczIy02UrLrzZ7ndKHhsh2yl7SXKRQMGwHCtGM-vvfWI6ySufjf_2YXXoZezjSNgDSFRyvilXPootx2PKAitdNBvEizIkMBYCCnZxu98NMSudE83LkWHXhWIVNsCXBblDzctQ.webp', - isLiked: false, - likeCount: 12, - quote: '', - workName: '도깨비', - workId: 1, - }, -]; - -const mockDescription = - '"너와 함께한 시간 모두 눈부셨다. 날이 좋아서, 날이 좋지 않아서, 날이 적당해서 모든 날이 좋았다."'; +export default withSuspense(function Search() { + const route = useRoute>(); + const { title } = route.params; + const { data } = useSearchQuery({ keyword: title }); + const navigation = useNavigation>(); -export default function Search() { const [carouselIndex, setCarouselIndex] = useState(0); const carouselRef = useRef(null); - const route = useRoute>(); - const { title } = route.params; const onProgressChange = () => { if (!carouselRef.current) return; @@ -72,12 +27,36 @@ export default function Search() { setCarouselIndex(index); }; + if (data.length === 0) { + return ( + +
+ + + 검색결과가 없어요. + + + 다른 작품을 검색해볼까요? + + navigation.navigate('Home/Main')} + > + + 돌아가기 + + + + + ); + } + return (
- {title} + {data[carouselIndex].workName} @@ -85,7 +64,7 @@ export default function Search() { width={220} type="body1" color="white" - content={mockDescription} + content={data[carouselIndex].quote} /> @@ -101,13 +80,13 @@ export default function Search() { width={260} height={350} mode="horizontal-stack" - modeConfig={{ showLength: mockData.length }} + modeConfig={{ showLength: data.length }} loop={false} - data={mockData} + data={data} renderItem={({ item }) => } /> - {mockData.map((_, index) => ( + {data.map((_, index) => ( ); -} +}); diff --git a/packages/react-native/src/routes/DetailTabNavigator.tsx b/packages/react-native/src/routes/DetailTabNavigator.tsx index 1d7036a0..04dbdeee 100644 --- a/packages/react-native/src/routes/DetailTabNavigator.tsx +++ b/packages/react-native/src/routes/DetailTabNavigator.tsx @@ -8,7 +8,7 @@ const Tab = createMaterialTopTabNavigator(); export default function DetailTabNavigator() { const route = useRoute>(); - const { id, contentId } = route.params; + const { id, contentId, workId } = route.params; return ( ); diff --git a/packages/react-native/src/types/navigation.ts b/packages/react-native/src/types/navigation.ts index 51767a1b..ba9a6fa9 100644 --- a/packages/react-native/src/types/navigation.ts +++ b/packages/react-native/src/types/navigation.ts @@ -41,7 +41,7 @@ export type StackParamList = { 'Home/Main': undefined; 'Home/Search': { title: string }; - 'Home/Detail': { contentId: number; id: number }; + 'Home/Detail': { contentId: number; id: number; workId: number }; 'Home/AddSpot': { spots: SpotResponse[]; };