Skip to content

Commit f3b797a

Browse files
authored
Merge pull request #163 from wafflestudio/158-bug-story
✨ feat: d
2 parents 673b2cd + de8aee6 commit f3b797a

File tree

3 files changed

+91
-72
lines changed

3 files changed

+91
-72
lines changed

src/features/story-viewer/model/useStoryViewer.ts

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useCallback, useMemo } from 'react'
1+
import { useState, useCallback, useMemo, useEffect } from 'react'
22
import { useNavigate } from '@tanstack/react-router'
33
import type { StoryFeedItem } from '@/entities/story/model/types'
44

@@ -16,27 +16,34 @@ export function useStoryViewer(
1616
)
1717
}, [storiesData, initialUserId])
1818

19-
const [currentStoryIndex, setCurrentStoryIndex] = useState(0)
20-
const [progress, setProgress] = useState(0)
21-
const [isPaused, setIsPaused] = useState(false)
19+
const [state, setState] = useState(() => ({
20+
userId: initialUserId,
21+
storyIndex: 0,
22+
progress: 0,
23+
isPaused: false,
24+
}))
2225

23-
const [prevUserId, setPrevUserId] = useState(initialUserId)
24-
25-
if (prevUserId !== initialUserId) {
26-
setPrevUserId(initialUserId)
27-
setCurrentStoryIndex(0)
28-
setProgress(0)
29-
setIsPaused(false)
26+
if (state.userId !== initialUserId) {
27+
setState({
28+
userId: initialUserId,
29+
storyIndex: 0,
30+
progress: 0,
31+
isPaused: false,
32+
})
3033
}
3134

3235
const currentUser = storiesData[currentUserIndex]
33-
const currentStory = currentUser?.stories?.[currentStoryIndex]
36+
const currentStory = currentUser?.stories?.[state.storyIndex]
3437

3538
const handleNext = useCallback(() => {
3639
if (!currentUser) return
37-
if (currentStoryIndex < currentUser.stories.length - 1) {
38-
setCurrentStoryIndex((prev) => prev + 1)
39-
setProgress(0)
40+
41+
if (state.storyIndex < currentUser.stories.length - 1) {
42+
setState((prev) => ({
43+
...prev,
44+
storyIndex: prev.storyIndex + 1,
45+
progress: 0,
46+
}))
4047
} else if (currentUserIndex < storiesData.length - 1) {
4148
const nextUser = storiesData[currentUserIndex + 1]
4249
navigate({
@@ -46,50 +53,60 @@ export function useStoryViewer(
4653
} else {
4754
navigate({ to: '/', search: { page: 1 } })
4855
}
49-
}, [currentUser, currentStoryIndex, currentUserIndex, storiesData, navigate])
56+
}, [currentUser, state.storyIndex, currentUserIndex, storiesData, navigate])
5057

5158
const handlePrev = useCallback(() => {
5259
if (!currentUser) return
53-
if (currentStoryIndex > 0) {
54-
setCurrentStoryIndex((prev) => prev - 1)
55-
setProgress(0)
60+
61+
if (state.storyIndex > 0) {
62+
setState((prev) => ({
63+
...prev,
64+
storyIndex: prev.storyIndex - 1,
65+
progress: 0,
66+
}))
5667
} else if (currentUserIndex > 0) {
5768
const prevUser = storiesData[currentUserIndex - 1]
5869
navigate({
5970
to: '/stories/$user_id',
6071
params: { user_id: String(prevUser.userId) },
6172
})
6273
}
63-
}, [currentUser, currentStoryIndex, currentUserIndex, storiesData, navigate])
74+
}, [currentUser, state.storyIndex, currentUserIndex, storiesData, navigate])
6475

6576
const togglePause = useCallback(() => {
66-
setIsPaused((prev) => !prev)
77+
setState((prev) => ({
78+
...prev,
79+
isPaused: !prev.isPaused,
80+
}))
6781
}, [])
6882

6983
useEffect(() => {
70-
if (isPaused || !currentStory) return
84+
if (state.isPaused || !currentStory) return
7185

7286
const step = (INTERVAL_MS / STORY_DURATION) * 100
7387
const timer = setInterval(() => {
74-
setProgress((prev) => {
75-
if (prev >= 100) {
88+
setState((prev) => {
89+
if (prev.progress >= 100) {
7690
handleNext()
77-
return 100
91+
return prev
92+
}
93+
return {
94+
...prev,
95+
progress: prev.progress + step,
7896
}
79-
return prev + step
8097
})
8198
}, INTERVAL_MS)
8299

83100
return () => clearInterval(timer)
84-
}, [isPaused, currentStoryIndex, handleNext, currentStory])
101+
}, [state.isPaused, currentStory, handleNext])
85102

86103
return {
87104
currentUser,
88105
currentStory,
89-
currentStoryIndex,
106+
currentStoryIndex: state.storyIndex,
90107
currentUserIndex,
91-
progress,
92-
isPaused,
108+
progress: state.progress,
109+
isPaused: state.isPaused,
93110
handleNext,
94111
handlePrev,
95112
togglePause,

src/features/story-viewer/ui/StoryViewer.tsx

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import instagramLogo from '@/assets/instagram-black-logo.png'
2222
interface StoryViewerProps {
2323
feed: StoryFeedItem[]
2424
userId: string
25+
detailUser?: StoryFeedItem
2526
isDetailLoading?: boolean
2627
}
2728

@@ -40,6 +41,7 @@ const formatRelativeTime = (createdAt: string) => {
4041
export function StoryViewer({
4142
feed,
4243
userId,
44+
detailUser,
4345
isDetailLoading,
4446
}: StoryViewerProps) {
4547
const navigate = useNavigate()
@@ -64,25 +66,30 @@ export function StoryViewer({
6466
togglePause,
6567
} = useStoryViewer(feed, userId)
6668

69+
const viewerUser =
70+
detailUser && String(detailUser.userId) === String(userId)
71+
? detailUser
72+
: currentUser
73+
6774
const isMine =
6875
!isMeLoading &&
6976
me?.userId !== undefined &&
70-
currentUser?.userId !== undefined &&
71-
String(me.userId) === String(currentUser.userId)
77+
viewerUser?.userId !== undefined &&
78+
String(me.userId) === String(viewerUser.userId)
7279

7380
useEffect(() => {
7481
if (imageError && !isPaused) {
7582
togglePause()
7683
}
7784
}, [imageError, isPaused, togglePause])
7885

79-
if (!currentUser) return null
86+
if (!viewerUser || !currentStory) return null
8087

8188
const isFirstStoryOfFirstUser =
8289
currentUserIndex === 0 && currentStoryIndex === 0
8390
const isLastStoryOfLastUser =
8491
currentUserIndex === feed.length - 1 &&
85-
currentStoryIndex === (currentUser?.stories?.length ?? 0) - 1
92+
currentStoryIndex === (viewerUser.stories.length ?? 0) - 1
8693

8794
const handleOpenOptions = (e: React.MouseEvent) => {
8895
e.stopPropagation()
@@ -113,7 +120,7 @@ export function StoryViewer({
113120
const handleDeleteStory = async () => {
114121
try {
115122
const response = await instance
116-
.delete(`api/v1/stories/${currentStory?.id}`)
123+
.delete(`api/v1/stories/${currentStory.id}`)
117124
.json<{ isSuccess: boolean; code: string; message: string }>()
118125
if (response.isSuccess) {
119126
queryClient.invalidateQueries({ queryKey: ['stories', 'feed'] })
@@ -158,15 +165,15 @@ export function StoryViewer({
158165
)}
159166

160167
<div className={STORY_VIEWER_UI.STYLES.VIEWER_CARD}>
161-
{isDetailLoading && !currentStory?.imageUrl && (
168+
{isDetailLoading && !currentStory.imageUrl && (
162169
<div className="absolute inset-0 z-10 flex items-center justify-center bg-black">
163170
<div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-600 border-t-white" />
164171
</div>
165172
)}
166173

167174
<div className={STORY_VIEWER_UI.STYLES.OVERLAY_TOP}>
168175
<div className={STORY_VIEWER_UI.STYLES.PROGRESS_CONTAINER}>
169-
{currentUser.stories.map((story: Story, i: number) => (
176+
{viewerUser.stories.map((story: Story, i: number) => (
170177
<div
171178
key={story.id}
172179
className={STORY_VIEWER_UI.STYLES.PROGRESS_BAR}
@@ -189,21 +196,19 @@ export function StoryViewer({
189196
<div className={STORY_VIEWER_UI.STYLES.HEADER}>
190197
<Link
191198
to="/$userId"
192-
params={{ userId: String(currentUser.userId) }}
199+
params={{ userId: String(viewerUser.userId) }}
193200
className={STORY_VIEWER_UI.STYLES.USER_SECTION}
194201
>
195202
<img
196-
src={currentUser.profileImageUrl ?? ''}
203+
src={viewerUser.profileImageUrl ?? ''}
197204
className={STORY_VIEWER_UI.STYLES.AVATAR}
198205
alt=""
199206
/>
200207
<div className={STORY_VIEWER_UI.STYLES.USER_INFO}>
201-
<span className="font-bold">{currentUser.nickname}</span>
202-
{currentStory && (
203-
<span className="text-[13px] font-normal opacity-60">
204-
{formatRelativeTime(currentStory.createdAt)}
205-
</span>
206-
)}
208+
<span className="font-bold">{viewerUser.nickname}</span>
209+
<span className="text-[13px] font-normal opacity-60">
210+
{formatRelativeTime(currentStory.createdAt)}
211+
</span>
207212
</div>
208213
</Link>
209214

@@ -234,37 +239,30 @@ export function StoryViewer({
234239

235240
<div
236241
className={STORY_VIEWER_UI.STYLES.CONTENT_AREA}
237-
onClick={(e: React.MouseEvent<HTMLDivElement>) => {
242+
onClick={(e) => {
238243
const rect = e.currentTarget.getBoundingClientRect()
239244
setImageError(false)
240245
if (e.clientX - rect.left < rect.width / 2) handlePrev()
241246
else handleNext()
242247
}}
243248
>
244249
{imageError ? (
245-
<div className="flex h-full w-full flex-col items-center justify-center bg-black px-10 text-center text-white">
246-
<p className="text-[14px] leading-relaxed font-medium">
247-
더 이상 이용할 수 없는 콘텐츠입니다
248-
</p>
250+
<div className="flex h-full w-full items-center justify-center bg-black text-white">
251+
더 이상 이용할 수 없는 콘텐츠입니다
249252
</div>
250253
) : (
251254
<>
252-
{currentStory?.imageUrl && (
253-
<img
254-
key={currentStory.imageUrl}
255-
src={currentStory.imageUrl}
256-
className="h-full w-full object-cover select-none"
257-
alt="story"
258-
onError={() => setImageError(true)}
259-
referrerPolicy="no-referrer"
260-
/>
261-
)}
255+
<img
256+
src={currentStory.imageUrl}
257+
className="h-full w-full object-cover select-none"
258+
alt="story"
259+
onError={() => setImageError(true)}
260+
referrerPolicy="no-referrer"
261+
/>
262262

263-
{isMine && currentStory?.viewCount !== undefined && (
264-
<div className="absolute bottom-4 left-4 z-50 flex flex-col items-start gap-1">
265-
<span className="text-[13px] font-semibold text-white drop-shadow-md">
266-
{currentStory.viewCount}명이 읽음
267-
</span>
263+
{isMine && currentStory.viewCount !== undefined && (
264+
<div className="absolute bottom-4 left-4 z-50 text-white">
265+
{currentStory.viewCount}명이 읽음
268266
</div>
269267
)}
270268
</>
@@ -289,7 +287,7 @@ export function StoryViewer({
289287
isOpen={isOptionsOpen}
290288
onClose={handleCloseOptions}
291289
isMine={isMine}
292-
userId={currentUser.userId}
290+
userId={viewerUser.userId}
293291
onReport={handleOpenReport}
294292
onAccountInfo={handleOpenAccountInfo}
295293
onDelete={handleOpenDeleteConfirm}
@@ -302,7 +300,7 @@ export function StoryViewer({
302300
if (isPaused && !imageError) togglePause()
303301
}}
304302
onHideComment={() => {}}
305-
nickname={currentUser.nickname}
303+
nickname={viewerUser.nickname}
306304
type="post"
307305
/>
308306
)}
@@ -313,8 +311,8 @@ export function StoryViewer({
313311
setIsAccountInfoOpen(false)
314312
if (isPaused && !imageError) togglePause()
315313
}}
316-
nickname={currentUser.nickname}
317-
profileImageUrl={currentUser.profileImageUrl}
314+
nickname={viewerUser.nickname}
315+
profileImageUrl={viewerUser.profileImageUrl}
318316
/>
319317
)}
320318

@@ -340,7 +338,7 @@ export function StoryViewer({
340338
</div>
341339
<button
342340
onClick={handleDeleteStory}
343-
className="w-full border-t border-gray-200 py-3 text-[14px] font-bold text-red-500 active:bg-gray-50"
341+
className="w-full border-t border-gray-200 py-3 text-[14px] font-bold text-red-500"
344342
>
345343
삭제
346344
</button>
@@ -349,7 +347,7 @@ export function StoryViewer({
349347
setIsDeleteConfirmOpen(false)
350348
if (isPaused && !imageError) togglePause()
351349
}}
352-
className="w-full border-t border-gray-200 py-3 text-[14px] active:bg-gray-50"
350+
className="w-full border-t border-gray-200 py-3 text-[14px]"
353351
>
354352
취소
355353
</button>

src/routes/stories/$user_id.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ function RouteComponent() {
2121

2222
const mergedFeed = useMemo(() => {
2323
if (!feedData) return []
24+
2425
if (!detailData) return feedData
2526

2627
return feedData.map((item) =>
27-
String(item.userId) === String(user_id) ? detailData : item
28+
String(item.userId) === String(user_id)
29+
? { ...item, stories: detailData.stories }
30+
: item
2831
)
2932
}, [feedData, detailData, user_id])
3033

@@ -41,6 +44,7 @@ function RouteComponent() {
4144
<StoryViewer
4245
feed={mergedFeed}
4346
userId={user_id}
47+
detailUser={detailData}
4448
isDetailLoading={isDetailLoading}
4549
/>
4650
</div>

0 commit comments

Comments
 (0)