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
7 changes: 3 additions & 4 deletions src/app/router/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
AroundSearch,
TourList,
TourSearch,
TourSingleInfo,
TourShare,
} from '@/app/router';
import { Home } from '@/pages/home';
import { GeoTrip } from '@/pages/tour/geotrip';
Expand All @@ -28,9 +28,8 @@ export default function Router() {
<Route path="around-search" element={<AroundSearch />} />
</Route>
<Route path="/tour" element={<Tour />}>
<Route path=":contentId" element={<GeoTrip />} />
<Route path="geo-trip" element={<GeoTrip />} />
<Route path="single/:contentId" element={<TourSingleInfo />} />
<Route path="geo" element={<GeoTrip />} />
<Route path="share/:contentId" element={<TourShare />} />
<Route path="list" element={<TourList />} />
<Route path="search" element={<TourSearch />} />
<Route path="bookmark" element={<Bookmark />} />
Expand Down
4 changes: 1 addition & 3 deletions src/app/router/lazyRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@ export const AroundSearch = lazy(
);
export const TourList = lazy(() => import('@/pages/tour/tourList/TourList'));
export const TourSearch = lazy(() => import('@/pages/tour/search/TourSearch'));
export const TourSingleInfo = lazy(
() => import('@/pages/tour/geotrip/SingleTrip'),
);
export const TourShare = lazy(() => import('@/pages/tour/geotrip/Share'));
2 changes: 1 addition & 1 deletion src/features/auth/ui/AuthButtonContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function AuthButtonContainer() {
mutation.mutate();
};
const handleGuestLogin = () => {
navigate('/tour/geo-trip?distance=20000&tour-type=12');
navigate('/tour/geo');
};

return (
Expand Down
2 changes: 2 additions & 0 deletions src/features/shared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './lib';
export * from './ui';
1 change: 1 addition & 0 deletions src/features/shared/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useKakaoShare } from './useKakaoShare';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { tourQueries, type TourItem } from '@/entities/tour';
import { LoadingSpinner } from '@/shared/ui';
import { TourSlide } from '@/features/tourShort';

export default function Single() {
export default function ShareTrip() {
const { contentId } = useParams();
const navigate = useNavigate();

Expand Down
2 changes: 1 addition & 1 deletion src/features/shared/ui/SharedButtonContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface SharedButtonContainerProps {
export default function SharedButtonContainer({
contentId,
}: SharedButtonContainerProps) {
const link = `${window.location.origin}/tour/single/${contentId}`;
const link = `${window.location.origin}/tour/share/${contentId}`;

const { shareKakao } = useKakaoShare({ contentId, link });
return (
Expand Down
2 changes: 2 additions & 0 deletions src/features/shared/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as ShareTrip } from './ShareTrip';
export { default as SharedButtonContainer } from './SharedButtonContainer';
11 changes: 4 additions & 7 deletions src/features/tourDetail/ui/TourOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useSuspenseQuery } from '@tanstack/react-query';

import { commonSVG } from '@/assets';

import { BookmarkButtonContainer } from '@/features/bookmark';
import { tourQueries } from '@/entities/tour';
import { TourTypeBadge, DistanceTimeInfo, getCopyClipBoard } from '@/shared';
import { TourTypeBadge, DistanceTimeInfo } from '@/shared';
import { SharedButtonContainer } from '@/features/shared';

interface TourCardProps {
distance: string;
Expand Down Expand Up @@ -37,10 +36,8 @@ export default function TourOverview({
</div>
<div className="flex items-center gap-4 mt-4 bg-white">
<BookmarkButtonContainer contentId={tourContentId} />
<commonSVG.ShareIcon
className="cursor-pointer"
onClick={() => getCopyClipBoard(window.location.href)}
/>

<SharedButtonContainer contentId={tourContentId} />
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/features/tourShort/hook/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './useInfiniteSwiperControl';
export * from './usePersistSlideUrl';
export * from './usePersistSlideStorage';
43 changes: 43 additions & 0 deletions src/features/tourShort/hook/usePersistSlideStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useSessionStorage } from '@/shared';
import { useCallback } from 'react';

interface SlideState {
pageParam: number;
index: number;
}

export const usePersistSlideStorage = () => {
const [slideState, setSlideState] = useSessionStorage<SlideState>(
'slideState',
{
index: 0,
pageParam: 1,
},
);

const setSlideParams = useCallback(
({ index, pageParam }: SlideState) => {
setSlideState({ index: index % 10, pageParam });
},
[setSlideState],
);

const getSlideIndex = () => {
return slideState.index;
};

const getPageParam = () => {
return slideState.pageParam;
};

const slideReset = () => {
setSlideState({ index: 0, pageParam: 1 });
};

return {
setSlideParams,
getSlideIndex,
getPageParam,
slideReset,
};
};
50 changes: 0 additions & 50 deletions src/features/tourShort/hook/usePersistSlideUrl.ts

This file was deleted.

5 changes: 3 additions & 2 deletions src/features/tourShort/ui/TourSwiperContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import {
TourSwiperView,
useTourSwiperInfiniteQuery,
useInfiniteSwiperControl,
usePersistSlideUrl,
usePersistSlideStorage,
} from '@/features/tourShort';
import { getSuspenseLocation } from '@/shared';

import type { Swiper as SwiperType } from 'swiper/types';
import type { TourInjected } from '@/features/tour';

function TourSwiperContainer({ distance, contentTypeId }: TourInjected) {
const { setSlideParams, getSlideIndex, getPageParam } = usePersistSlideUrl();
const { setSlideParams, getSlideIndex, getPageParam } =
usePersistSlideStorage();
const geoLocation = getSuspenseLocation();

const { slideEntries, fetchAppend, fetchPrepend } =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import SingleTrip from '@/features/tour/ui/single/Single';
import { LoadingSpinner, QueryErrorBoundary } from '@/shared';
import { BottomNavigationBar, Seo } from '@/widgets';
import { Suspense } from 'react';
import { useParams } from 'react-router-dom';

export default function SingleTripPage() {
import { BottomNavigationBar, Seo } from '@/widgets';

import { ShareTrip } from '@/features/shared';

import { LoadingSpinner, QueryErrorBoundary } from '@/shared';

export default function ShareTripPage() {
const { contentId } = useParams();
return (
<>
<Seo
title="관광지 조회"
description="관광지를 단일 조회하는 페이지입니다."
canonicalUrl={`https://p-pick.com/tour/${contentId}`}
canonicalUrl={`https://p-pick.com/tour/share/${contentId}`}
/>
<section className="flex flex-col h-full w-full">
<div className="h-full w-full relative">
<div className="absolute h-full w-full flex items-center justify-center">
<QueryErrorBoundary>
<Suspense fallback={<LoadingSpinner />}>
<SingleTrip />
<ShareTrip />
</Suspense>
</QueryErrorBoundary>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/tour/geotrip/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default as GeoTrip } from './GeoTrip';
export { default as SingleTrip } from './SingleTrip';
export { default as SingleTrip } from './Share';
1 change: 1 addition & 0 deletions src/shared/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './useSyncedState';
export * from './useDebouncedCallback';
export * from './useLocalStorage';
export * from './useSessionStorage';
export * from './useFunnel';
export * from './useToggleState';
56 changes: 56 additions & 0 deletions src/shared/hooks/useSessionStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useCallback, useEffect, useRef, useState } from 'react';

/**
* sessionStorage 값을 저장하고 불러오는 커스텀 훅입니다.
* dispatchEvent를 사용하여 다른 컴포넌트에서 sessionStorage의 변경을 감지할 수 있습니다.
*
* @param {string} key - sessionStorage에서 사용할 키
* @param {T} initialValue - 초기값
* @returns {[T, (value: T | ((val: T) => T)) => void]} - 저장된 값과 업데이트 함수를 반환합니다.
*
* @example
* //초기값에 의해, 스토리지에 저장된 정보가 없다면 자동으로 값을 저장합니다.
* const [value, setValue] = useLocalStorage('myKey', 'defaultValue');
*/
export const useSessionStorage = <T>(key: string, initialValue: T) => {
const readValue = useCallback((): T => {
try {
const item = window.sessionStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
throw new Error(`스토리지를 불러오는데 실패했습니다: ${error}`);
}
}, [key, initialValue]);

const [storedValue, setStoredValue] = useState<T>(readValue);
const sessionStorageEventRef = useRef(new Event('session-storage'));

const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));

window.dispatchEvent(sessionStorageEventRef.current);
} catch (error) {
console.error(`Error setting sessionStorage key "${key}":`, error);
}
};

useEffect(() => {
const handleStorageChange = () => {
setStoredValue(readValue());
};

window.addEventListener('session-storage', handleStorageChange);
window.addEventListener('storage', handleStorageChange);

return () => {
window.removeEventListener('session-storage', handleStorageChange);
window.removeEventListener('storage', handleStorageChange);
};
}, [readValue]);

return [storedValue, setValue] as const;
};
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('BottomNavigationBar 컴포넌트', () => {
await user.click(homeButton);

expect(mockNavigate).toHaveBeenCalledWith(
expect.stringContaining('/tour/geo-trip'),
expect.stringContaining('/tour/geo'),
{ replace: true },
);
});
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/bottomNavigationBar/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export const createNavItems = ({ navigate }: createNavItemsParams) => {
{
id: 'home-navigation-tutorial',
icon: HomeIcon,
path: '/tour/geo-trip',
path: '/tour/geo',
label: 'home Icon',
onClick: () => navigateTo('/tour/geo-trip'),
onClick: () => navigateTo('/tour/geo'),
},
{
id: 'list-navigation-tutorial',
Expand Down