Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d5da0e1
chore: /mycd 경로 삭제 (#217)
maylh Mar 20, 2026
d2cf9bb
chore: 필터 버튼 폰트 크기 수정 (#217)
maylh Mar 20, 2026
bf89c6c
feat: 큐레이션 트랙리스트 상세페이지 경로추가 (#217)
maylh Mar 20, 2026
6600466
feat: useCarouselCdLisk queryKey에 anchorId 추가 (#217)
maylh Mar 21, 2026
f4d66d3
fix: CD 삭제 시 삭제된 anchorId로 carousel 요청 발생하던 이슈 해결 (#217)
maylh Mar 21, 2026
139e41f
fix: 캐러셀 빠르게 넘길 때 YouTube 플레이어 미준비 에러 수정 (#217)
maylh Mar 24, 2026
52d7467
refactor: 플레이어 레이아웃 통합 및 widgets 이동 (#217)
maylh Mar 24, 2026
74e49f7
refactor: PlaylistCarousel 위젯 분리 (#217)
maylh Mar 24, 2026
a80e7fe
feat: 타이틀 marquee 추가 (#217)
maylh Mar 25, 2026
97933b2
feat: 비로그인 좋아요 클릭 시 모달 추가 (#217)
maylh Mar 25, 2026
3be69bc
fix: 홈 컨텐츠 limit 수정사항 반영 (#217)
maylh Mar 25, 2026
c5212a5
refactor: 검색 결과 창에 useInView 훅 적용 (#217)
maylh Mar 25, 2026
49455db
fix: 트랙리스트 상세페이지 스크롤 되게 수정 (#217)
maylh Mar 25, 2026
11c0345
fix: 방금 삭제된 CD ID로 중복 요청이 발생하는 현상 수정 (#217)
maylh Mar 25, 2026
b95c5c3
chore: 콘솔 삭제 (#217)
maylh Mar 25, 2026
cf184bd
Merge branch 'develop' into fix/#217/qa-feed-curation
maylh Mar 26, 2026
2d75668
fix: 타이틀 line height 삭제 (#217)
maylh Mar 26, 2026
b575f14
feat: auto marquee 1s 딜레이 추가 (#217)
maylh Mar 26, 2026
246444f
fix: 커스터마이징 완료 페이지 쿼리 키 수정 (#217)
maylh Mar 26, 2026
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
13 changes: 8 additions & 5 deletions src/app/providers/PlayerProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@
if (time !== undefined) setCurrentTime(time)

const isSamePlaylist = currentPlaylist?.playlistId === playlist.playlistId

setIsPlaying((prev) => (isSamePlaylist ? prev : autoPlay))

if (playerRef.current) {
if (time !== undefined) playerRef.current.seekTo(time, true)

if (!isSamePlaylist && autoPlay) {
playerRef.current.playVideo()
try {
const state = playerRef.current.getPlayerState()
if (state === -1 || state === undefined) return

if (time !== undefined) playerRef.current.seekTo(time, true)
if (!isSamePlaylist && autoPlay) playerRef.current.playVideo()
} catch {
// 플레이어 미준비 상태는 onReady에서 처리
}
}
}
Expand Down Expand Up @@ -88,7 +91,7 @@
playerRef.current.seekTo(0, true)
}
}
}, [currentPlaylist, currentTrackIndex, isMuted])

Check warning on line 94 in src/app/providers/PlayerProvider.tsx

View workflow job for this annotation

GitHub Actions / Build and Lint

React Hook useCallback has an unnecessary dependency: 'isMuted'. Either exclude it or remove the dependency array

const prevTrack = useCallback(() => {
if (currentTrackIndex > 0) {
Expand Down Expand Up @@ -144,7 +147,7 @@

export default PlaylistProvider

export const usePlaylist = () => {

Check warning on line 150 in src/app/providers/PlayerProvider.tsx

View workflow job for this annotation

GitHub Actions / Build and Lint

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
const context = useContext(PlaylistContext)
if (!context) throw new Error('usePlaylist must be used within a PlaylistProvider')
return context
Expand Down
12 changes: 8 additions & 4 deletions src/entities/playlist/model/usePlaylists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export const useShufflePlaylists = (size: number = 5) => {
})
}

export const usePlaylistDetail = (playlistId: number | null, options?: { enabled?: boolean }) => {
export const usePlaylistDetail = (
playlistId: number | null | undefined, // TODO: mycd 레거시 정리 시 null 타입도 삭제
options?: { enabled?: boolean }
) => {
return useQuery({
queryKey: ['playlistDetail', playlistId],
queryFn: () => getPlaylistDetail(playlistId as number),
Expand Down Expand Up @@ -157,10 +160,11 @@ type PageParam = { cursor: number; direction: CarouselDirection } | undefined
export const useCarouselCdList = (
type: FEED_CD_LIST_TAB_TYPE, // cds or likes
shareCode: string,
params: CarouselParams
params: CarouselParams,
options?: { enabled?: boolean }
) => {
return useInfiniteQuery({
queryKey: ['feedCdList', type, shareCode, params.sort],
queryKey: ['feedCdList', type, shareCode, params.sort, params.anchorId],

queryFn: ({ pageParam }: { pageParam: PageParam }) => {
const fetchFn = type === 'cds' ? getCdCarousel : getLikedCdCarousel
Expand Down Expand Up @@ -195,6 +199,6 @@ export const useCarouselCdList = (
return { cursor: firstPage.prevCursor, direction: 'PREV' }
},

enabled: !!shareCode,
enabled: options?.enabled !== false && !!shareCode,
})
}
3 changes: 2 additions & 1 deletion src/features/like/model/useLike.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const useLikeStatus = (playlistId: number, options?: { enabled?: boolean
interface UseLikeOptions {
shouldNavigate?: boolean
getNextId?: () => number | undefined
openLoginModal?: () => void
}

const useLike = (playlistId: number, options?: UseLikeOptions) => {
Expand Down Expand Up @@ -89,7 +90,7 @@ const useLike = (playlistId: number, options?: UseLikeOptions) => {

const toggleLike = () => {
if (!isLogin) {
navigate('/login')
options?.openLoginModal?.()
return
}

Expand Down
65 changes: 45 additions & 20 deletions src/features/like/ui/LikeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react'
import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom'

import styled, { useTheme } from 'styled-components'

Expand All @@ -7,6 +8,7 @@ import type { CdMetaResponse } from '@/entities/playlist'
import { useLike } from '@/features/like'
import { getNextId } from '@/shared/lib'
import { myCdButton } from '@/shared/styles/mixins'
import { Modal } from '@/shared/ui'
import SvgButton from '@/shared/ui/SvgButton'

interface LikeButtonProps {
Expand All @@ -23,13 +25,16 @@ const ICON_STYLE = {
} as const

const LikeButton = ({ playlistId, type = 'HOME', playlistData, activeIndex }: LikeButtonProps) => {
const [isModalOpen, setIsModalOpen] = useState(false)
const theme = useTheme()
const navigate = useNavigate()
const { liked, toggleLike } = useLike(playlistId, {
shouldNavigate: type === 'MY',
getNextId: () => {
if (!playlistData || activeIndex === undefined) return undefined
return getNextId(activeIndex, playlistData)
},
openLoginModal: () => setIsModalOpen(true),
})

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
Expand All @@ -41,25 +46,45 @@ const LikeButton = ({ playlistId, type = 'HOME', playlistData, activeIndex }: Li
const opacity = type === 'HOME' ? (liked ? 1 : 0.2) : 1

return (
<Wrapper $opacity={opacity} $isMy={type === 'MY'}>
<SvgButton
icon={Icon}
onClick={handleClick}
width={size}
height={size}
fill={
type === 'HOME'
? liked
? theme.COLOR['primary-normal']
: theme.COLOR['gray-200']
: liked
? theme.COLOR['primary-normal']
: 'none'
}
stroke={liked ? theme.COLOR['primary-normal'] : theme.COLOR['gray-200']}
/>
{type === 'MY' && <p>좋아요</p>}
</Wrapper>
<>
<Wrapper $opacity={opacity} $isMy={type === 'MY'}>
<SvgButton
icon={Icon}
onClick={handleClick}
width={size}
height={size}
fill={
type === 'HOME'
? liked
? theme.COLOR['primary-normal']
: theme.COLOR['gray-200']
: liked
? theme.COLOR['primary-normal']
: 'none'
}
stroke={liked ? theme.COLOR['primary-normal'] : theme.COLOR['gray-200']}
/>
{type === 'MY' && <p>좋아요</p>}
</Wrapper>

{isModalOpen && (
<Modal
isOpen={isModalOpen}
title="로그인 후 이용할 수 있어요"
ctaType="double"
onConfirm={() => {
navigate('/login')
setIsModalOpen(false)
}}
onCancel={() => {
setIsModalOpen(false)
}}
onClose={() => setIsModalOpen(false)}
confirmText="로그인하기"
cancelText="다음에 하기"
/>
)}
</>
)
}

Expand Down
24 changes: 14 additions & 10 deletions src/pages/curation/CurationLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo } from 'react'
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'
import { Outlet, useLocation, useMatch, useNavigate, useParams } from 'react-router-dom'

import { LeftArrow } from '@/assets/icons'
import { useBundlePlaylist } from '@/entities/bundle'
Expand All @@ -12,6 +12,8 @@ const CurationLayout = () => {
const location = useLocation()
const { bundleId, id: playlistId } = useParams()

const isTracklistPage = useMatch('/curation/:bundleId/play/:id/tracklist')

const sectionTitle = useMemo(() => {
return location.state?.sectionTitle || getRandomItem(HOME_SECTION_TITLES.TIME)
}, [location.state?.sectionTitle])
Expand All @@ -26,15 +28,17 @@ const CurationLayout = () => {

return (
<div>
<Header
left={
<SvgButton
icon={LeftArrow}
onClick={() => navigate(playlistId ? `/curation/${bundleId}` : '/')}
/>
}
center={<span>{playlistId ? data?.title : sectionTitle}</span>}
/>
{!isTracklistPage && (
<Header
left={
<SvgButton
icon={LeftArrow}
onClick={() => navigate(playlistId ? `/curation/${bundleId}` : '/')}
/>
}
center={<span>{playlistId ? data?.title : sectionTitle}</span>}
/>
)}

<Outlet context={data} />
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/curation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const TotalCount = styled.p`
`

const ContentSection = styled.section`
padding: 24px 0 0 0;
padding: 24px 0 60px 0;
`

const PlaylistItems = styled.div`
Expand Down
8 changes: 5 additions & 3 deletions src/pages/curation/play/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { useNavigate, useOutletContext, useParams } from 'react-router-dom'

import type { BundleInfo } from '@/entities/bundle'
import { usePlaylistDetail, usePlaylistDetails } from '@/entities/playlist'
import { CurationCarousel } from '@/pages/curation/ui'
import { PlaylistCarousel } from '@/widgets/playlist'

const CurationPlayer = () => {
const bundle = useOutletContext<BundleInfo>()

const navigate = useNavigate()
const { id: routePlaylistId } = useParams()

const ids = useMemo(() => bundle.playlists.map((p) => p.playlistId), [bundle])
const { data: playlistData } = usePlaylistDetails(ids)

const routeId = routePlaylistId ? Number(routePlaylistId) : null
const routeId = Number(routePlaylistId)

useEffect(() => {
if (!playlistData || routeId) return
Expand All @@ -40,10 +41,11 @@ const CurationPlayer = () => {
if (!playlistDetail || !playlistData) return null

return (
<CurationCarousel
<PlaylistCarousel
playlistData={playlistData}
playlistDetail={playlistDetail}
onCenterChange={handleCenterChange}
basePath={`/curation/${bundle.bundleId}/play`}
/>
)
}
Expand Down
Loading
Loading