@@ -8,28 +8,38 @@ import { useRouter } from "next/navigation";
88import { useState } from "react" ;
99
1010import { Avatar } from "@/app/member/_components/Avatar" ;
11+ import { storiesQueryOptions } from "@/app/story/_api" ;
1112import CancelIcon from "@/assets/cancel.svg" ;
1213import LocationIcon from "@/assets/location.svg" ;
1314import MarketFillIcon from "@/assets/market-fill.svg" ;
1415import { GNB } from "@/components/ui/GNB" ;
1516import { Text } from "@/components/ui/Text" ;
1617
1718import { storyDetailQueryOptions } from "../../_api" ;
19+ import { KAKAO_PLACE_URL , STORIES_LIMIT } from "../../_constants" ;
1820import * as styles from "./StoryDetailContent.css" ;
1921
2022type StoryDetailContentProps = {
2123 storyId : string ;
2224} ;
2325
24- const KAKAO_PLACE_URL = "https://place.map.kakao.com" ;
25-
2626export const StoryDetailContent = ( { storyId } : StoryDetailContentProps ) => {
2727 const router = useRouter ( ) ;
2828
2929 const { data : story } = useSuspenseQuery ( storyDetailQueryOptions ( storyId ) ) ;
30+ const { data : storiesData } = useSuspenseQuery (
31+ storiesQueryOptions ( STORIES_LIMIT )
32+ ) ;
3033
3134 const [ isDescriptionExpanded , setIsDescriptionExpanded ] = useState ( false ) ;
3235
36+ const currentStory = storiesData . stories . findIndex (
37+ s => s . storyId . toString ( ) === storyId
38+ ) ;
39+
40+ const hasPrevious = currentStory > 0 ;
41+ const hasNext = currentStory < storiesData . stories . length - 1 ;
42+
3343 const handleCancelClick = ( ) => {
3444 router . push ( "/" ) ;
3545 } ;
@@ -38,6 +48,20 @@ export const StoryDetailContent = ({ storyId }: StoryDetailContentProps) => {
3848 setIsDescriptionExpanded ( ! isDescriptionExpanded ) ;
3949 } ;
4050
51+ const handlePrevStory = ( ) => {
52+ if ( hasPrevious ) {
53+ const prevStory = storiesData . stories [ currentStory - 1 ] ;
54+ router . push ( `/story/${ prevStory ?. storyId } ` ) ;
55+ }
56+ } ;
57+
58+ const handleNextStory = ( ) => {
59+ if ( hasNext ) {
60+ const nextStory = storiesData . stories [ currentStory + 1 ] ;
61+ router . push ( `/story/${ nextStory ?. storyId } ` ) ;
62+ }
63+ } ;
64+
4165 return (
4266 < div className = { styles . container } >
4367 < div className = { styles . gnbOverlay } >
@@ -56,7 +80,15 @@ export const StoryDetailContent = ({ storyId }: StoryDetailContentProps) => {
5680 />
5781 </ div >
5882
59- < div className = { styles . imageCard } >
83+ < div className = { styles . storyImageArea } >
84+ < button
85+ className = { styles . zone ( { side : "left" } ) }
86+ onClick = { handlePrevStory }
87+ />
88+ < button
89+ className = { styles . zone ( { side : "right" } ) }
90+ onClick = { handleNextStory }
91+ />
6092 < Image
6193 src = { story . imageUrl }
6294 alt = { `${ story . storeName } 스토리` }
@@ -66,79 +98,80 @@ export const StoryDetailContent = ({ storyId }: StoryDetailContentProps) => {
6698 // TODO: 추후 제거
6799 unoptimized
68100 />
69- < div className = { styles . imageContent } >
70- < div className = { styles . userWrapper } >
71- < Avatar memberId = { story . memberId } />
72- < Text typo = 'body1Sb' color = 'common.100' >
73- { story . memberNickname }
74- </ Text >
75- </ div >
101+ </ div >
76102
77- { story . description && (
78- < div className = { styles . descriptionContainer } >
79- < motion . div
80- className = { `${ styles . descriptionText } ${
81- isDescriptionExpanded ? styles . expanded : styles . collapsed
82- } `}
83- onClick = { handleToggle }
84- animate = { {
85- height : isDescriptionExpanded ? "auto" : "2.2rem" ,
86- } }
87- transition = { {
88- duration : 0.3 ,
89- ease : [ 0.4 , 0.0 , 0.2 , 1 ] ,
90- } }
91- style = { {
92- overflow : "hidden" ,
93- } }
94- >
95- < Text typo = 'body1Sb' color = 'common.100' >
96- { story . description }
97- </ Text >
98- </ motion . div >
99- </ div >
100- ) }
103+ < div className = { styles . informationContent } >
104+ < div className = { styles . userWrapper } >
105+ < Avatar memberId = { story . memberId } />
106+ < Text typo = 'body1Sb' color = 'common.100' >
107+ { story . memberNickname }
108+ </ Text >
109+ </ div >
101110
102- < div className = { styles . tagContainer } >
103- < div className = { styles . tag } >
104- < MarketFillIcon className = { styles . tagIcon } />
105- { story . storeId ? (
106- < Link href = { `/stores/${ story . storeId } ` } >
107- < Text typo = 'label1Sb' color = 'common.100' >
108- { story . storeName }
109- </ Text >
110- </ Link >
111- ) : (
112- < Text typo = 'label1Sb' color = 'common.100' >
113- { story . storeName }
114- </ Text >
115- ) }
116- </ div >
117- < a
118- href = { `${ KAKAO_PLACE_URL } /${ story . storeKakaoId } ` }
119- target = '_blank'
120- rel = 'noopener noreferrer'
111+ { story . description && (
112+ < div className = { styles . descriptionContainer } >
113+ < motion . div
114+ className = { `${ styles . descriptionText } ${
115+ isDescriptionExpanded ? styles . expanded : styles . collapsed
116+ } `}
117+ onClick = { handleToggle }
118+ animate = { {
119+ height : isDescriptionExpanded ? "auto" : "2.2rem" ,
120+ } }
121+ transition = { {
122+ duration : 0.3 ,
123+ ease : [ 0.4 , 0.0 , 0.2 , 1 ] ,
124+ } }
125+ style = { {
126+ overflow : "hidden" ,
127+ } }
121128 >
122- < div className = { styles . tag } >
123- < LocationIcon className = { styles . tagIcon } />
129+ < Text typo = 'body1Sb' color = 'common.100' >
130+ { story . description }
131+ </ Text >
132+ </ motion . div >
133+ </ div >
134+ ) }
135+
136+ < div className = { styles . tagContainer } >
137+ < div className = { styles . tag } >
138+ < MarketFillIcon className = { styles . tagIcon } />
139+ { story . storeId ? (
140+ < Link href = { `/stores/${ story . storeId } ` } >
124141 < Text typo = 'label1Sb' color = 'common.100' >
125- { story . storeDistrict } { story . storeNeighborhood }
142+ { story . storeName }
126143 </ Text >
127- </ div >
128- </ a >
144+ </ Link >
145+ ) : (
146+ < Text typo = 'label1Sb' color = 'common.100' >
147+ { story . storeName }
148+ </ Text >
149+ ) }
129150 </ div >
151+ < a
152+ href = { `${ KAKAO_PLACE_URL } /${ story . storeKakaoId } ` }
153+ target = '_blank'
154+ rel = 'noopener noreferrer'
155+ >
156+ < div className = { styles . tag } >
157+ < LocationIcon className = { styles . tagIcon } />
158+ < Text typo = 'label1Sb' color = 'common.100' >
159+ { story . storeDistrict } { story . storeNeighborhood }
160+ </ Text >
161+ </ div >
162+ </ a >
130163 </ div >
131-
132- < AnimatePresence >
133- < motion . div
134- className = { styles . descriptionOverlay }
135- initial = { { opacity : 0 } }
136- animate = { { opacity : 1 } }
137- exit = { { opacity : 0 } }
138- transition = { { duration : 0.3 } }
139- />
140- </ AnimatePresence >
141164 </ div >
165+
166+ < AnimatePresence >
167+ < motion . div
168+ className = { styles . descriptionOverlay }
169+ initial = { { opacity : 0 } }
170+ animate = { { opacity : 1 } }
171+ exit = { { opacity : 0 } }
172+ transition = { { duration : 0.3 } }
173+ />
174+ </ AnimatePresence >
142175 </ div >
143176 ) ;
144177} ;
0 commit comments