Skip to content
2 changes: 1 addition & 1 deletion src/entities/playlist/model/usePlaylists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const usePlaylistDetail = (playlistId: number | null, options?: { enabled
return useQuery({
queryKey: ['playlistDetail', playlistId],
queryFn: () => getPlaylistDetail(playlistId as number),
enabled: !!playlistId || options?.enabled,
enabled: options?.enabled ?? !!playlistId,
})
}

Expand Down
5 changes: 1 addition & 4 deletions src/features/recommend/types/recommend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ export interface Playlist {
creatorId: string
creatorNickname: string
songs: Track[]

// TODO : 스티커 정보는 둘 중 하나로 올 수 있음 추후 통일 필요
onlyCdResponse?: {
cdResponse?: {
cdItems: CdCustomData[]
}
cdItems?: CdCustomData[]
}

export type RecommendationsResponse = Playlist[]
Expand Down
4 changes: 2 additions & 2 deletions src/pages/home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const HomePage = () => {
key={item.playlistId}
title={item.playlistName}
username={item.creatorNickname}
stickers={item.onlyCdResponse?.cdItems}
stickers={item.cdResponse?.cdItems}
/>
))}
</ScrollCarousel>
Expand All @@ -73,7 +73,7 @@ const HomePage = () => {
title={playlist.playlistName}
username={playlist.creatorNickname}
songs={playlist.songs}
stickers={playlist.onlyCdResponse?.cdItems}
stickers={playlist.cdResponse?.cdItems}
/>
))}
</ScrollCarousel>
Expand Down
71 changes: 62 additions & 9 deletions src/pages/mypage/ui/main/components/MyLikedCdList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'

import styled from 'styled-components'
Expand All @@ -7,18 +8,54 @@ import { useMyLikedCdList } from '@/entities/playlist/model/useMyCd'
import { CdNameInfo } from '@/pages/mypage/ui/main/components'
import { useSingleSelect } from '@/shared/lib/useSingleSelect'
import { flexColCenter } from '@/shared/styles/mixins'
import { Loading, Error, ContentHeader, Cd } from '@/shared/ui'
import { Loading, Error as ErrorUi, ContentHeader, Modal } from '@/shared/ui'
import type { SortType } from '@/shared/ui/ContentHeader'
import type { ModalProps } from '@/shared/ui/Modal'
import { Playlist } from '@/widgets/playlist'

const MyLikedCdList = () => {
const navigate = useNavigate()

const { selected: currentSort, onSelect: setCurrentSort } = useSingleSelect<SortType>('POPULAR')
const [modal, setModal] = useState<ModalProps>({
isOpen: false,
title: '',
description: '',
ctaType: 'single',
confirmText: '',
cancelText: '',
onClose: () => {
setModal((prev) => ({ ...prev, isOpen: false }))
},
onConfirm: () => {},
onCancel: () => {
setModal((prev) => ({ ...prev, isOpen: false }))
},
})

const { selected: currentSort, onSelect: setCurrentSort } = useSingleSelect<SortType>('POPULAR')
const { data: myLikedCdList, isLoading, isError, isSuccess } = useMyLikedCdList(currentSort)

const onLikedCdClick = useCallback(
(cdId: number, isPublic: boolean) => {
if (!cdId) return
if (!isPublic) {
setModal({
isOpen: true,
title: '비공개된 CD는 재생할 수 없어요.',
ctaType: 'single',
confirmText: '확인',
onClose: () => setModal((p) => ({ ...p, isOpen: false })),
onConfirm: () => setModal((p) => ({ ...p, isOpen: false })),
})
return
}
navigate(`/mypage/${cdId}/tracklist`)
},
[navigate]
)

if (isLoading) return <Loading isLoading={isLoading} />
if (isError || !isSuccess) return <Error />
if (isError || !isSuccess) return <ErrorUi />

return (
<>
Expand All @@ -40,20 +77,36 @@ const MyLikedCdList = () => {
) : (
myLikedCdList?.map((item) => (
<li key={item.playlistId}>
{/* TODO: 나의CD 이동 후 좋아요한 CD 재생은 각 브랜치 병합 이후 작업 */}
<CdButton type="button" onClick={() => navigate('/mycd')}>
{/* TODO: CD 컴포넌트 좋아요는 각 브랜치 병합 이후 작업 (10/20~) */}
<Cd
variant="responsive"
<CdButton
type="button"
onClick={() => onLikedCdClick(item?.playlistId, item?.isPublic)}
>
<Playlist
id={item.playlistId}
title={item.playlistName}
username={item?.creatorNickname || ''}
stickers={item?.cdResponse?.cdItems}
isPublic={item?.isPublic}
cdVariant="responsive"
isPublic={item.isPublic}
/>
</CdButton>
<CdNameInfo title={item?.playlistName || ''} creator={item?.creatorNickname || ''} />
</li>
))
)}
</CdListWrap>

<Modal
isOpen={modal.isOpen}
title={modal.title}
description={modal.description}
ctaType={modal.ctaType}
confirmText={modal.confirmText}
cancelText={modal.cancelText}
onClose={modal.onClose}
onConfirm={modal.onConfirm}
onCancel={modal.onCancel}
/>
</>
)
}
Expand Down
9 changes: 6 additions & 3 deletions src/pages/mypage/ui/tracklist/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const MypageTracklist = () => {

const onModalClose = () => setModal((prev) => ({ ...prev, isOpen: false }))

const onCdPlayClick = () => {
if (!cdMetadata?.playlistId) return
navigate(`/mycd?id=${cdMetadata?.playlistId}${isFromMyCdList ? '' : '&type=LIKE'}`)
}

const onCdDeleteClick = () => {
setModal({
isOpen: true,
Expand Down Expand Up @@ -125,8 +130,7 @@ const MypageTracklist = () => {
stickers={cdMetadata?.cdResponse?.cdItems ?? []}
/>
<ControlContainer>
{/* TODO: 모두 재생 브랜치 병합 후 작업 */}
<CdPlayButton type="button">
<CdPlayButton type="button" onClick={onCdPlayClick}>
<StartBlack width={20} height={20} />
모두 재생
</CdPlayButton>
Expand All @@ -142,7 +146,6 @@ const MypageTracklist = () => {
>
<Share width={20} height={20} />
</CdActionButton>
{/* TODO: 공개 토글 백엔드 API 답변 오면 추가 작업 */}
{isMyCd && (
<CdActionButton
type="button"
Expand Down
4 changes: 4 additions & 0 deletions src/shared/styles/globalStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const GlobalStyle = createGlobalStyle`
line-height: 1.5;
}

button, input, textarea, select, a {
font-family: inherit;
}

button {
background: none;
border: none;
Expand Down
2 changes: 1 addition & 1 deletion src/shared/ui/Cd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { THEME_IMAGES_MAP } from '@/pages/mypage/lib/customizeTheme'
import { THEME_PROP_ID_OFFSET } from '@/pages/mypage/types/mypage'
import { flexRowCenter, flexColCenter } from '@/shared/styles/mixins'

interface CdProps {
export interface CdProps {
variant:
| 'xxl'
| 'xl'
Expand Down
42 changes: 29 additions & 13 deletions src/widgets/playlist/Playlist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,66 @@ import styled from 'styled-components'
import type { CdCustomData } from '@/entities/playlist'
import { LikeButton } from '@/features/like'
import Cd from '@/shared/ui/Cd'
import type { CdProps } from '@/shared/ui/Cd'

interface PlaylistProps {
title: string
username: string
id: number
stickers?: CdCustomData[]
cdVariant?: CdProps['variant']
isPublic?: boolean
}

const Playlist = ({ id, title, username, stickers }: PlaylistProps) => {
const Playlist = ({
id,
title,
username,
stickers,
cdVariant = 'xl',
isPublic = true,
}: PlaylistProps) => {
const navigate = useNavigate()

// 마이페이지에서만 아래 옵션을 사용하고 있어 구분하기 위함
const isFromMypage = cdVariant === 'responsive'

const handleClick = () => {
if (isFromMypage) return
navigate(`/discover/${id}`)
}

return (
<Wrapper onClick={handleClick}>
<CdBox>
<Cd variant="xl" stickers={stickers} />
<Wrapper onClick={handleClick} $cdVariant={cdVariant}>
<CdBox $cdVariant={cdVariant}>
<Cd variant={cdVariant} stickers={stickers} isPublic={isPublic} />
<ButtonContainer>
<LikeButton playlistId={id} type="HOME" />
</ButtonContainer>
</CdBox>
<InfoBox>
<Title>{title}</Title>
<UserName>{username}</UserName>
</InfoBox>
{!isFromMypage && (
<InfoBox>
<Title>{title}</Title>
<UserName>{username}</UserName>
</InfoBox>
)}
</Wrapper>
)
}

export default Playlist

const Wrapper = styled.div`
const Wrapper = styled.div<{ $cdVariant: string }>`
display: flex;
flex-direction: column;
gap: 12px;
width: 140px;
width: ${({ $cdVariant }) => ($cdVariant === 'responsive' ? '100%' : '140px')};
`

const CdBox = styled.div`
const CdBox = styled.div<{ $cdVariant: string }>`
position: relative;
width: 140px;
height: 140px;
width: ${({ $cdVariant }) => ($cdVariant === 'responsive' ? '100%' : '140px')};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

CdBox 컴포넌트의 width 속성이 부모 컴포넌트인 Wrapper와 동일한 로직으로 중복 정의되어 있습니다. Wrapper가 이미 너비를 제어하고 있으므로, CdBoxwidth: 100%로 설정하여 부모의 너비를 따르게 하면 코드가 더 간결해집니다. 이 경우 CdBox에 전달되는 $cdVariant prop은 더 이상 필요하지 않게 됩니다.

Suggested change
width: ${({ $cdVariant }) => ($cdVariant === 'responsive' ? '100%' : '140px')};
width: 100%;

aspect-ratio: 1 / 1;
border-radius: 16px;

display: flex;
Expand Down
Loading