Skip to content

Commit

Permalink
Merge pull request #204 from mbti-nf-team/feat/place-detail-api
Browse files Browse the repository at this point in the history
  • Loading branch information
saseungmin authored Oct 17, 2023
2 parents 62b2571 + ccf4082 commit 214c0df
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 123 deletions.
71 changes: 71 additions & 0 deletions src/app/api/google/search/detail/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { NextRequest, NextResponse } from 'next/server';

import { Client } from '@googlemaps/google-maps-services-js';

export const runtime = 'nodejs';

const TEN_MINUTES = 600;

export async function GET(request: NextRequest) {
const client = new Client({});

const { searchParams } = new URL(request.url);
const requestHeaders = new Headers(request.headers);

const placeId = searchParams.get('placeId');
const sessionToken = requestHeaders.get('session-token');

if (!placeId) {
return NextResponse.json(null, {
headers: requestHeaders,
status: 400,
statusText: 'not found place',
});
}

const response = await client.placeDetails({
params: {
key: process.env.NEXT_PUBLIC_GOOGLE_MAP_API_KEY,
place_id: placeId,
region: 'KR',
sessiontoken: sessionToken ?? undefined,
},
});

if (response.status !== 200) {
return NextResponse.json(null, {
status: response.status,
statusText: response.statusText,
headers: requestHeaders,
});
}

const thumbnailPhotoReference = response.data.result.photos?.[0].photo_reference;

if (!thumbnailPhotoReference) {
return NextResponse.json(response.data, {
status: response.status,
statusText: response.statusText,
headers: requestHeaders,
});
}

const thumbnailPhotoUrl = `https://maps.googleapis.com/maps/api/place/photo?maxwidth=800&photoreference=${thumbnailPhotoReference}&key=${process.env.NEXT_PUBLIC_GOOGLE_MAP_API_KEY}`;

return NextResponse.json({
...response.data,
result: {
...response.data.result,
thumbnail: thumbnailPhotoUrl,
},
}, {
status: response.status,
statusText: response.statusText,
headers: {
...requestHeaders,
'Cache-Control': 'public, s-maxage=1',
'CDN-Cache-Control': 'public, s-maxage=60',
'Vercel-CDN-Cache-Control': `public, s-maxage=${TEN_MINUTES}`,
},
});
}
8 changes: 4 additions & 4 deletions src/components/detail/PlaceDetailWindow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import StarRating from '@/components/common/StarRating';
import useRenderToast from '@/hooks/useRenderToast';
import { paramsSerializer } from '@/lib/apis';
import { CloseIcon, ShareIcon } from '@/lib/assets/icons';
import { PlacesWithDetailSearchResult } from '@/lib/types/search';
import { PlacesWithSearchResult } from '@/lib/types/search';

import styles from './index.module.scss';

Expand All @@ -47,7 +47,7 @@ type Props = {
isVisible: boolean;
isLoading: boolean;
onClose: () => void;
placeDetail: PlacesWithDetailSearchResult<true> | null;
placeDetail: PlacesWithSearchResult<true> | null;
};

function PlaceDetailWindow({
Expand Down Expand Up @@ -155,14 +155,14 @@ function PlaceDetailWindow({
{!isLoading && placeDetail && (
<>
<h1 className={styles.bodyHeader}>{placeDetail?.name}</h1>
{placeDetail?.photos?.[0] && (
{placeDetail?.thumbnail && (
<div className={styles.placeImageWrapper}>
<Image
width={382}
height={382}
quality={100}
className={styles.placeImage}
src={placeDetail.photos[0].getUrl()}
src={placeDetail.thumbnail}
alt={placeDetail?.name}
/>
</div>
Expand Down
23 changes: 6 additions & 17 deletions src/components/detail/PlaceDetailWindowContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,31 @@
import { useEffect } from 'react';

import useGetPlaceDetails from '@/hooks/maps/useGetPlaceDetails';
import useGetPlaceDetail from '@/hooks/queries/useGetPlaceDetail';
import useGetSearchBlog from '@/hooks/queries/useGetSearchBlog';
import { PlaceDetailResult } from '@/lib/types/google.maps';
import { PlacesWithDetailSearchResult } from '@/lib/types/search';
import { PlaceResult } from '@/lib/types/google.maps';
import usePlaceDetailWindowStore from '@/stores/placeDetailWindow';

import PlaceDetailWindow from '../PlaceDetailWindow';

function PlaceDetailWindowContainer() {
const [placeDetailsState, onGetPlaceDetails, resetPlaceDetails] = useGetPlaceDetails();

const {
isOpenPlaceDetailWindow, onClosePlaceDetailWindow, placeId,
} = usePlaceDetailWindowStore(['isOpenPlaceDetailWindow', 'onClosePlaceDetailWindow', 'placeId']);
const { data: placeDetail, isSuccess } = useGetPlaceDetail({ placeId });

const { data: placesWithSearchResult, isLoading } = useGetSearchBlog<true>({
placesResult: [placeDetailsState as PlaceDetailResult],
placesResult: [placeDetail?.result as PlaceResult],
includePost: true,
enabled: !!placeDetailsState,
enabled: isSuccess && !!placeDetail?.result,
});

const onCloseDetailWindow = () => {
resetPlaceDetails();
onClosePlaceDetailWindow();
};

useEffect(() => {
if (placeId) {
onGetPlaceDetails(placeId);
}
}, [placeId]);

return (
<PlaceDetailWindow
isLoading={isLoading}
isVisible={isOpenPlaceDetailWindow}
placeDetail={placesWithSearchResult?.[0] as PlacesWithDetailSearchResult<true> | null}
placeDetail={placesWithSearchResult?.[0]}
onClose={onCloseDetailWindow}
/>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/map/PlaceBottomSheet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Spinner from '@/components/common/Spinner';
import useGetSearchBlog from '@/hooks/queries/useGetSearchBlog';
import { InfiniteRefState } from '@/lib/types';
import { TextSearchPlace } from '@/lib/types/google.maps';
import { PlacesWithSearchResult, SelectedPlace } from '@/lib/types/search';
import { SelectedPlace } from '@/lib/types/search';
import usePlaceDetailWindowStore from '@/stores/placeDetailWindow';
import { targetFalseThenValue } from '@/utils';

Expand Down Expand Up @@ -76,7 +76,7 @@ function PlaceBottomSheet({ places, refState, isSuccess }: Props) {
!(placesWithSearchResult.length - 1 === index),
)(refState.lastItemRef)
}
place={place as PlacesWithSearchResult}
place={place}
onClick={onClickPlaceItem}
/>
))}
Expand Down
20 changes: 11 additions & 9 deletions src/components/map/SearchTermsList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
ForwardedRef, forwardRef, memo, RefObject, useCallback, useEffect, useState,
} from 'react';

import useGetPlaceDetails from '@/hooks/maps/useGetPlaceDetails';
import useGetPlaceDetail from '@/hooks/queries/useGetPlaceDetail';
import useRenderToast from '@/hooks/useRenderToast';
import useSearchActionKeyEvent from '@/hooks/useSearchActionKeyEvent';
import usePlaceStore from '@/stores/place';
Expand All @@ -27,14 +27,16 @@ function SearchTermsList({
estimatedSearchTerms, setEstimatedSearchTerms,
] = useState<google.maps.places.AutocompletePrediction[]>([]);
const renderToast = useRenderToast();
const [placeDetailsState, onGetPlaceDetails] = useGetPlaceDetails();
const [placeId, setPlaceId] = useState<string>();
const [isZeroResult, setIsZeroResult] = useState<boolean>();
const { setPlaces } = usePlaceStore(['setPlaces']);
const { addRecentSearch } = useRecentSearchStore(['addRecentSearch']);

const onActionTextSearch = (placeId: string, placeName: string) => {
onGetPlaceDetails(placeId);
addRecentSearch(placeName);
const { data: placeDetail, isSuccess } = useGetPlaceDetail({ placeId });

const onActionTextSearch = (targetPlaceId: string, targetPlaceName: string) => {
setPlaceId(targetPlaceId);
addRecentSearch(targetPlaceName);
};

const onKeyDown = useSearchActionKeyEvent<[string, string]>({
Expand Down Expand Up @@ -68,11 +70,11 @@ function SearchTermsList({
}, [keyword, displaySuggestions, service, sessionToken]);

useEffect(() => {
if (placeDetailsState) {
setPlaces([placeDetailsState]);
onInput(`${placeDetailsState?.name}`);
if (isSuccess && placeDetail?.result) {
setPlaces([placeDetail.result]);
onInput(`${placeDetail.result.name}`);
}
}, [placeDetailsState]);
}, [isSuccess, placeDetail]);

useEffect(() => {
if (isZeroResult) {
Expand Down
53 changes: 0 additions & 53 deletions src/hooks/maps/useGetPlaceDetails.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/hooks/queries/useGetGoogleSearch.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TextSearchResponseData } from '@googlemaps/google-maps-services-js';
import { checkEmpty } from '@nf-team/core';
import { useInfiniteQuery } from '@tanstack/react-query';

Expand All @@ -8,7 +9,7 @@ import { filteredPlaces } from '@/utils';
import useIntersectionObserver from '../useIntersectionObserver';

function useGetGoogleSearch({ keyword }: { keyword: string; }) {
const query = useInfiniteQuery<TextSearchPlace>(['googleSearch', keyword], ({ pageParam }) => fetchGoogleSearch({ keyword, nextCursor: pageParam }), {
const query = useInfiniteQuery<TextSearchResponseData, unknown, TextSearchPlace>(['googleSearch', keyword], ({ pageParam }) => fetchGoogleSearch({ keyword, nextCursor: pageParam }), {
enabled: !!keyword,
getNextPageParam: ({ next_page_token }) => next_page_token,
select: (response) => ({
Expand Down
39 changes: 39 additions & 0 deletions src/hooks/queries/useGetPlaceDetail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { PlaceDetailsResponseData } from '@googlemaps/google-maps-services-js';
import { useQuery } from '@tanstack/react-query';

import { fetchPlaceDetail } from '@/lib/apis/search';
import { PlaceDetail } from '@/lib/types/google.maps';

const hasPlaceId = (
placeDetail: PlaceDetailsResponseData,
): placeDetail is PlaceDetail => {
if (
!placeDetail?.result
|| !placeDetail.result?.geometry?.location
|| !placeDetail.result?.place_id
|| !placeDetail.result?.name
) {
return false;
}

return true;
};

function useGetPlaceDetail({
placeId, sessionToken,
}: { placeId?: string; sessionToken?: string; }) {
const query = useQuery<PlaceDetailsResponseData, undefined, PlaceDetail | undefined>(['placeDetail', placeId], () => fetchPlaceDetail({ placeId: placeId as string, sessionToken }), {
enabled: !!placeId,
select: (placeDetail) => {
if (hasPlaceId(placeDetail)) {
return placeDetail;
}

return undefined;
},
});

return query;
}

export default useGetPlaceDetail;
19 changes: 3 additions & 16 deletions src/hooks/queries/useGetSearchBlog.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
import { useMemo } from 'react';

import { checkEmpty } from '@nf-team/core';
import { useQuery } from '@tanstack/react-query';

import { fetchAllSettledSearchBlogs } from '@/lib/apis/search';
import { PlaceDetailResult, PlaceResult } from '@/lib/types/google.maps';
import { PlaceResult } from '@/lib/types/google.maps';

const TEN_MINUTES = 600000;

const isPlaceDetail = <T = boolean>(
placesResult: PlaceDetailResult[] | PlaceResult[],
includePost?: T,
): placesResult is PlaceDetailResult[] => Boolean(includePost) === true;

function useGetSearchBlog<T = boolean>({
placesResult, includePost, enabled,
}: {
placesResult: PlaceDetailResult[] | PlaceResult[];
placesResult: PlaceResult[];
includePost?: T;
enabled?: boolean; }) {
const placeName = useMemo(() => {
if (isPlaceDetail<T>(placesResult, includePost)) {
return placesResult.filter((place) => place).map((place) => place?.name);
}

return placesResult.filter((place) => place).map((place) => place?.name);
}, [placesResult, includePost]);
const placeName = placesResult.filter((place) => place).map((place) => place.name);

const query = useQuery(
[{ placeName, includePost }],
Expand Down
Loading

1 comment on commit 214c0df

@vercel
Copy link

@vercel vercel bot commented on 214c0df Oct 17, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.