-
Notifications
You must be signed in to change notification settings - Fork 2
[feat] 피드 CD 재생 API 연결 #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0c606d3
affa3db
9569401
8877edc
3740494
e648663
3db2421
fd1116c
18dfa1e
4a8aa9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,16 @@ | ||||||||||||||||||||||||||||||
| import { Outlet } from 'react-router-dom' | ||||||||||||||||||||||||||||||
| import { Outlet, useParams } from 'react-router-dom' | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import { useOwnerStatus } from '@/features/auth' | ||||||||||||||||||||||||||||||
| import { Loading } from '@/shared/ui' | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const FeedLayout = () => { | ||||||||||||||||||||||||||||||
| return <Outlet /> | ||||||||||||||||||||||||||||||
| const { shareCode = '' } = useParams() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const { data, isLoading } = useOwnerStatus(shareCode || '') | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (isLoading) return <Loading isLoading /> | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return <Outlet context={{ isOwner: data?.isOwner }} /> | ||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오너 상태 조회 실패를 정상 렌더로 넘기지 마세요. 여기서는 🐛 수정 예시-import { Outlet, useParams } from 'react-router-dom'
+import { Navigate, Outlet, useParams } from 'react-router-dom'
...
- const { data, isLoading } = useOwnerStatus(shareCode || '')
+ const { data, isLoading, isError } = useOwnerStatus(shareCode)
...
- return <Outlet context={{ isOwner: data?.isOwner }} />
+ if (isError || !data) return <Navigate to="/error" replace />
+
+ return <Outlet context={data} />📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export default FeedLayout | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,87 +1,5 @@ | ||
| import { useEffect, useCallback, useState, useMemo } from 'react' | ||
| import { useNavigate, useParams } from 'react-router-dom' | ||
| import { FeedCarousel } from '@/pages/feed/ui' | ||
|
|
||
| import { useMyCdActions, useMyCdList } from '@/entities/playlist/model/useMyCd' | ||
| import { CdViewerLayout } from '@/pages/feed/ui' | ||
| import { Loading } from '@/shared/ui' | ||
|
|
||
| const Cds = () => { | ||
| const navigate = useNavigate() | ||
| const { id: routePlaylistId } = useParams<{ id?: string }>() | ||
|
|
||
| const myCdPlaylist = useMyCdList('RECENT') | ||
|
|
||
| const playlistData = useMemo(() => { | ||
| return myCdPlaylist.data ?? [] | ||
| }, [myCdPlaylist.data]) | ||
|
|
||
| const isLoading = myCdPlaylist.isLoading | ||
| const isError = myCdPlaylist.isError | ||
|
|
||
| const [centerItem, setCenterItem] = useState<{ | ||
| playlistId: number | null | ||
| playlistName: string | ||
| }>({ playlistId: null, playlistName: '' }) | ||
|
|
||
| useEffect(() => { | ||
| if (isLoading || !playlistData.length) return | ||
|
|
||
| const routeId = routePlaylistId ? Number(routePlaylistId) : null | ||
| const found = routeId ? playlistData.find((p) => p.playlistId === routeId) : null | ||
|
|
||
| if (found) { | ||
| setCenterItem({ | ||
| playlistId: found.playlistId, | ||
| playlistName: found.playlistName, | ||
| }) | ||
| } else { | ||
| const first = playlistData[0] | ||
|
|
||
| setCenterItem({ | ||
| playlistId: first.playlistId, | ||
| playlistName: first.playlistName, | ||
| }) | ||
|
|
||
| navigate(`./${first.playlistId}`, { replace: true }) | ||
| } | ||
| }, [playlistData, isLoading, routePlaylistId, navigate]) | ||
|
|
||
| const handleCenterChange = useCallback( | ||
| (playlist: { playlistId: number; playlistName: string }) => { | ||
| setCenterItem(playlist) | ||
|
|
||
| const path = `./${playlist.playlistId}` | ||
|
|
||
| navigate(path, { replace: true }) | ||
| }, | ||
| [navigate] | ||
| ) | ||
|
|
||
| const myCdDetail = useMyCdActions(Number(centerItem.playlistId), { | ||
| enabled: !!centerItem.playlistId, | ||
| }) | ||
|
|
||
| const playlistDetail = myCdDetail.tracklist | ||
|
|
||
| if (isLoading) return <Loading isLoading /> | ||
|
|
||
| if (isError) { | ||
| navigate('/error') | ||
| return null | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <CdViewerLayout | ||
| playlistData={playlistData} | ||
| playlistDetail={playlistDetail} | ||
| centerItem={centerItem} | ||
| onCenterChange={handleCenterChange} | ||
| pageType="MY" | ||
| isOwner // TODO: 실제 값으로 수정 필요 | ||
| /> | ||
| </> | ||
| ) | ||
| } | ||
| const Cds = () => <FeedCarousel type="cds" pageType="MY" /> | ||
|
|
||
| export default Cds |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,91 +1,5 @@ | ||
| import { useEffect, useCallback, useState, useMemo, useRef } from 'react' | ||
| import { useNavigate, useParams } from 'react-router-dom' | ||
| import { FeedCarousel } from '@/pages/feed/ui' | ||
|
|
||
| import { usePlaylistDetail } from '@/entities/playlist' | ||
| import { useMyLikedCdList } from '@/entities/playlist/model/useMyCd' | ||
| import { CdViewerLayout } from '@/pages/feed/ui' | ||
| import { Loading } from '@/shared/ui' | ||
|
|
||
| const Likes = () => { | ||
| const navigate = useNavigate() | ||
| const { id: routePlaylistId } = useParams<{ id?: string }>() | ||
|
|
||
| const likedCdPlaylist = useMyLikedCdList('RECENT') | ||
|
|
||
| const playlistData = useMemo(() => { | ||
| return likedCdPlaylist.data ?? [] | ||
| }, [likedCdPlaylist.data]) | ||
|
|
||
| const isLoading = likedCdPlaylist.isLoading | ||
| const isError = likedCdPlaylist.isError | ||
|
|
||
| const [centerItem, setCenterItem] = useState<{ | ||
| playlistId: number | null | ||
| playlistName: string | ||
| }>({ playlistId: null, playlistName: '' }) | ||
|
|
||
| const lastIndexRef = useRef<number>(0) | ||
| useEffect(() => { | ||
| if (likedCdPlaylist.isLoading || !playlistData.length) return | ||
|
|
||
| const routeId = routePlaylistId ? Number(routePlaylistId) : null | ||
| const currentIndex = playlistData.findIndex((p) => p.playlistId === routeId) | ||
|
|
||
| if (currentIndex !== -1) { | ||
| lastIndexRef.current = currentIndex | ||
| setCenterItem({ | ||
| playlistId: playlistData[currentIndex].playlistId, | ||
| playlistName: playlistData[currentIndex].playlistName, | ||
| }) | ||
| } else { | ||
| const nextItem = playlistData[lastIndexRef.current] || playlistData.at(-1) | ||
|
|
||
| if (nextItem) { | ||
| setCenterItem({ | ||
| playlistId: nextItem.playlistId, | ||
| playlistName: nextItem.playlistName, | ||
| }) | ||
|
|
||
| navigate(`../${nextItem.playlistId}`, { replace: true }) | ||
| } | ||
| } | ||
| }, [playlistData, likedCdPlaylist.isLoading, routePlaylistId, navigate]) | ||
|
|
||
| const handleCenterChange = useCallback( | ||
| (playlist: { playlistId: number; playlistName: string }) => { | ||
| setCenterItem(playlist) | ||
|
|
||
| const path = `../${playlist.playlistId}` | ||
|
|
||
| navigate(path, { replace: true }) | ||
| }, | ||
| [navigate] | ||
| ) | ||
|
|
||
| const likedCdDetail = usePlaylistDetail(centerItem.playlistId, { | ||
| enabled: !!centerItem.playlistId, | ||
| }) | ||
| const playlistDetail = likedCdDetail.data | ||
|
|
||
| if (isLoading) return <Loading isLoading /> | ||
|
|
||
| if (isError) { | ||
| navigate('/error') | ||
| return null | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <CdViewerLayout | ||
| playlistData={playlistData} | ||
| playlistDetail={playlistDetail} | ||
| centerItem={centerItem} | ||
| onCenterChange={handleCenterChange} | ||
| pageType="LIKE" | ||
| isOwner // TODO: 실제 값으로 수정 필요 | ||
| /> | ||
| </> | ||
| ) | ||
| } | ||
| const Likes = () => <FeedCarousel type="likes" pageType="LIKE" /> | ||
|
|
||
| export default Likes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
anchorId가 cache key에 들어가서 카드 이동마다 infinite query가 리셋됩니다.FeedCarousel은 중심 카드가 바뀔 때마다 routeid를 갱신합니다. 지금 키에params.anchorId가 포함돼 있어서 스와이프 한 번마다 새 쿼리가 생성되고, 이미 받아온pages를 버린 채 다시 로딩하게 됩니다. 이 훅의 양방향 프리패칭 이점이 거의 사라집니다.♻️ 수정 방향 예시
return useInfiniteQuery({ - queryKey: ['feedCdList', type, shareCode, params.sort, params.anchorId], + queryKey: ['feedCdList', type, shareCode, params.sort, params.limit],anchorId는 초기 진입 seed로만 사용하고, 실제 재-anchor가 필요할 때만 별도로 reset/refetch 하는 편이 안전합니다.🤖 Prompt for AI Agents