Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
64 changes: 51 additions & 13 deletions apps/client/src/pages/home/components/suggest-music-section.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';

import { Box } from '@confeti/design-system';
Expand All @@ -6,25 +7,55 @@ import { HOME_QUERY_OPTIONS } from '@shared/apis/home/home-queries';
import { MusicList } from '@shared/components';
import MusicInfo from '@shared/components/music-list/music-info';
import { useMusicPlayer } from '@shared/hooks/use-music-player';
import { SuggestMusicPerformanceResponse } from '@shared/types/home-response';
import { RecommendPerformances } from '@shared/types/home-response';

const SuggestMusicSection = ({
data,
}: {
data: SuggestMusicPerformanceResponse;
}) => {
const musicIdList: string[] | undefined = undefined;
import { PERFORMANCE_SIZE, SONG_SIZE } from '../constants/recommend';

const { data: suggestMusic, isPending } = useQuery({
...HOME_QUERY_OPTIONS.SUGGEST_MUSIC(data.performanceId, musicIdList),
interface SuggestMusicSectionProps {
onClickDetail: (type: string, typeId: number) => void;
}

const SuggestMusicSection = ({ onClickDetail }: SuggestMusicSectionProps) => {
const { data, isPending } = useQuery({
...HOME_QUERY_OPTIONS.RECOMMEND_PERFORMANCES(PERFORMANCE_SIZE, SONG_SIZE),
});

if (!isPending && !suggestMusic?.musics) {
const performances: RecommendPerformances[] = data?.performances ?? [];
const [currentIndex, setCurrentIndex] = useState(0);
const currentPerformance = performances[currentIndex];

const musicData = useMemo(
() =>
currentPerformance?.songs.map((song) => ({
musicId: song.songId,
trackName: song.songName,
artistName: song.artistName,
artworkUrl: song.artworkUrl,
previewUrl: song.previewUrl,
})) ?? [],
[currentPerformance],
);

const { musicList, onClickPlayToggle, audioRef, audioEvents, stopAudio } =
useMusicPlayer(musicData);

if (!isPending && !performances) {
return null;
}
Comment on lines +42 to 44
Copy link
Member

Choose a reason for hiding this comment

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

요렇게 하면 ux가 자연스러운가요ㅕ ??


const { musicList, onClickPlayToggle, audioRef, audioEvents } =
useMusicPlayer(suggestMusic?.musics ?? []);
const handleDotClick = (index: number) => {
if (!performances.length) return;
if (index === currentIndex) return;
if (index < 0 || index >= performances.length) return;

stopAudio();
setCurrentIndex(index);
};

const handleClickDetail = () => {
if (!currentPerformance) return;
onClickDetail(currentPerformance.type, currentPerformance.typeId);
};

return (
<Box
Expand All @@ -33,7 +64,14 @@ const SuggestMusicSection = ({
subtitle="예상 셋리스트, 미리 한번 들어볼까요?"
>
<div>
<MusicInfo title={data.title} />
<MusicInfo
title={currentPerformance?.title ?? ''}
posterUrl={currentPerformance?.posterUrl ?? ''}
total={performances.length}
current={currentIndex}
onDotClick={handleDotClick}
onClickDetail={handleClickDetail}
/>
<MusicList
appearance="home"
musics={musicList}
Expand Down
3 changes: 3 additions & 0 deletions apps/client/src/pages/home/constants/recommend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PERFORMANCE_SIZE = 3;

export const SONG_SIZE = 3;
3 changes: 0 additions & 3 deletions apps/client/src/pages/home/hooks/use-home-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ export const useHomeQueries = () => {
ticketingResult,
latestPerformancesResult,
suggestPerformanceResult,
suggestMusicPerformanceResult,
] = useSuspenseQueries({
queries: [
USER_QUERY_OPTIONS.PROFILE(),
HOME_QUERY_OPTIONS.TICKETING(),
HOME_QUERY_OPTIONS.LATEST_PERFORMANCES(),
HOME_QUERY_OPTIONS.SUGGEST_PERFORMANCE(),
HOME_QUERY_OPTIONS.SUGGEST_MUSIC_PERFORMANCE(),
],
});

Expand All @@ -25,6 +23,5 @@ export const useHomeQueries = () => {
ticketing: ticketingResult.data,
latestPerformances: latestPerformancesResult.data,
suggestPerformance: suggestPerformanceResult.data,
suggestMusicPerformance: suggestMusicPerformanceResult.data,
};
};
13 changes: 3 additions & 10 deletions apps/client/src/pages/home/page/home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,8 @@ import * as styles from './home-page.css';
const HomePage = () => {
const navigateToDetail = useNavigateToDetail();

const {
userName,
ticketing,
latestPerformances,
suggestPerformance,
suggestMusicPerformance,
} = useHomeQueries();
const { userName, ticketing, latestPerformances, suggestPerformance } =
useHomeQueries();

const formattedCarouselData = latestPerformances.performances.map(
(performance) => ({
Expand Down Expand Up @@ -66,9 +61,7 @@ const HomePage = () => {
<SuggestPerformanceSection data={suggestPerformance.performances} />
<Spacing size="lg" color="white" />

{suggestMusicPerformance && (
<SuggestMusicSection data={suggestMusicPerformance} />
)}
<SuggestMusicSection onClickDetail={navigateToDetail} />
<Spacing size="xl" color="white" />

<FloatingButtonContainer />
Expand Down
61 changes: 15 additions & 46 deletions apps/client/src/shared/apis/home/home-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { END_POINT } from '@shared/constants/api';
import { HOME_QUERY_KEY } from '@shared/constants/query-key';
import {
CarouselPerformancesResponse,
SuggestMusicPerformanceResponse,
SuggestMusicResponse,
RecommendPerformancesResponse,
SuggestPerformanceResponse,
TicketingPerformancesResponse,
} from '@shared/types/home-response';
Expand All @@ -31,15 +30,13 @@ export const HOME_QUERY_OPTIONS = {
queryKey: HOME_QUERY_KEY.SUGGEST_PERFORMANCE(),
queryFn: getSuggestPerformance,
}),
SUGGEST_MUSIC_PERFORMANCE: () =>
RECOMMEND_PERFORMANCES: (performanceSize: number, songSize: number) =>
queryOptions({
queryKey: HOME_QUERY_KEY.SUGGEST_MUSIC_PERFORMANCE(),
queryFn: getSuggestMusicPerformance,
}),
SUGGEST_MUSIC: (performanceId: number, musicIds?: string[]) =>
queryOptions({
queryKey: HOME_QUERY_KEY.SUGGEST_MUSIC(performanceId),
queryFn: () => getSuggestMusic(performanceId, musicIds),
queryKey: HOME_QUERY_KEY.RECOMMEND_PERFORMANCES(
performanceSize,
songSize,
),
queryFn: () => getRecommendPerformances(performanceSize, songSize),
}),
};

Expand Down Expand Up @@ -67,41 +64,13 @@ export const getSuggestPerformance =
return response.data;
};

export const getSuggestMusicPerformance =
async (): Promise<SuggestMusicPerformanceResponse | null> => {
try {
const response = await get<BaseResponse<SuggestMusicPerformanceResponse>>(
END_POINT.GET_SUGGEST_MUSIC_PERFORMANCE,
);

if (!response.data) {
return null;
}

return response.data;
} catch (error) {
console.error(error);
return null;
}
};

export const getSuggestMusic = async (
performanceId: number,
musicIds?: string[],
): Promise<SuggestMusicResponse> => {
try {
const query = new URLSearchParams();

query.append('performanceId', String(performanceId));
musicIds?.forEach((id) => query.append('musicId', id));

const url = `performances/recommend/musics?${query.toString()}`;

const response = await get<BaseResponse<SuggestMusicResponse>>(url);
export const getRecommendPerformances = async (
performanceSize: number,
songSize: number,
): Promise<RecommendPerformancesResponse> => {
const response = await get<BaseResponse<RecommendPerformancesResponse>>(
END_POINT.GET_SUGGEST_MUSIC_PERFORMANCE(performanceSize, songSize),
);

return response.data || { musics: [] };
} catch (error) {
console.error(error);
return { musics: [] };
}
return response.data ?? { performances: [] };
};
20 changes: 14 additions & 6 deletions apps/client/src/shared/components/music-list/music-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,35 @@ import * as styles from './music-info.css';

interface MusicInfoProps {
title: string;
total?: number;
current?: number;
posterUrl: string;
total: number;
current: number;
onDotClick?: (index: number) => void;
onClickDetail?: () => void;
}

const MusicInfo = ({
title,
total = 2,
current = 0,
posterUrl,
total,
current,
onDotClick,
onClickDetail,
}: MusicInfoProps) => {
const showDots = total > 1;

return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.poster}></div>
<div>
<img src={posterUrl} className={styles.poster} />
</div>
<div className={styles.textSection}>
<p className={styles.title}>{title}</p>
<div className={styles.buttonSection}>
<p className={styles.buttonText}>공연 상세정보 확인하기</p>
<p className={styles.buttonText} onClick={onClickDetail}>
공연 상세정보 확인하기
</p>
<Icon name="arrow-horizontal" size={12} color="white" />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import * as styles from './music-list.css';

interface Music {
musicId: string;
artworkUrl: string;
trackName: string;
artworkUrl: string;
artistName: string;
isPlaying?: boolean;
progress?: number;
Expand Down
3 changes: 2 additions & 1 deletion apps/client/src/shared/constants/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export const END_POINT = {
GET_TICKETING: '/performances/reservation',
GET_LATEST_PERFORMANCES: '/performances/info',
GET_SUGGEST_PERFORMANCE: '/performances/recommend',
GET_SUGGEST_MUSIC_PERFORMANCE: '/performances/recommend/performance',
GET_SUGGEST_MUSIC_PERFORMANCE: (performanceSize: number, songSize: number) =>
`performances/v4/song/recommend?performanceSize=${performanceSize}&songSize=${songSize}`,

//타임 테이블
GET_AVAILABLE_FESTIVALS: '/user/timetables/festivals',
Expand Down
7 changes: 4 additions & 3 deletions apps/client/src/shared/constants/query-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ export const HOME_QUERY_KEY = {
LATEST_PERFORMANCES: () => [...HOME_QUERY_KEY.ALL, 'latest-performances'],
TICKETING: () => [...HOME_QUERY_KEY.ALL, 'ticketing'],
SUGGEST_PERFORMANCE: () => [...HOME_QUERY_KEY.ALL, 'suggest-performance'],
SUGGEST_MUSIC_PERFORMANCE: () => [...HOME_QUERY_KEY.ALL, 'suggest-music'],
SUGGEST_MUSIC: (performanceId: number) => [
RECOMMEND_PERFORMANCES: (performanceSize: number, songSize: number) => [
...HOME_QUERY_KEY.ALL,
performanceId,
'recommend-performances',
performanceSize,
songSize,
],
} as const;

Expand Down
35 changes: 17 additions & 18 deletions apps/client/src/shared/hooks/use-music-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ export const useMusicPlayer = (data: musics[]) => {
const [duration, setDuration] = useState(30);

const stopAudio = () => {
const audio = audioRef.current;
if (!audio) return;
audio.pause();
audio.currentTime = 0;
if (!audioRef.current) return;

audioRef.current.pause();
audioRef.current.currentTime = 0;

setCurrentPlayingId(null);
setIsAudioPlaying(false);
setCurrentTime(0);
Expand All @@ -24,35 +25,33 @@ export const useMusicPlayer = (data: musics[]) => {
if (!selectedMusic || !selectedMusic.previewUrl || !audioRef.current)
return;

const audio = audioRef.current;

if (currentPlayingId === musicId && !audio.paused) {
if (currentPlayingId === musicId && !audioRef.current.paused) {
stopAudio();
return;
}

audio.src = selectedMusic.previewUrl;
audio.play().catch((e) => console.error('재생 오류', e));
audioRef.current.src = selectedMusic.previewUrl;
audioRef.current.play().catch((e) => console.error('재생 오류', e));
setCurrentPlayingId(musicId);
};

const audioEvents = useMemo(
() => ({
onLoadedMetadata: () => {
const a = audioRef.current;
if (!a) return;
const d = Number.isFinite(a.duration) ? a.duration : 30;
if (!audioRef.current) return;

const d = Number.isFinite(audioRef.current.duration)
? audioRef.current.duration
: 30;
setDuration(d);
},
onTimeUpdate: () => {
const a = audioRef.current;
if (!a) return;
setCurrentTime(a.currentTime);
if (!audioRef.current) return;
setCurrentTime(audioRef.current.currentTime);
},
onSeeked: () => {
const a = audioRef.current;
if (!a) return;
setCurrentTime(a.currentTime);
if (!audioRef.current) return;
setCurrentTime(audioRef.current.currentTime);
},
onPlay: () => {
setIsAudioPlaying(true);
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/shared/pages/error/error.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { themeVars } from '@confeti/design-system/styles';
export const container = style({
...themeVars.display.flexJustifyAlignCenter,
flexDirection: 'column',
height: 'calc(100dvh - 14rem)',
flex: 1,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

홈으로 돌아가기 버튼이 하단에 안 붙어있어서 추가했어요

gap: '2rem',
});

Expand Down
20 changes: 20 additions & 0 deletions apps/client/src/shared/types/home-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,23 @@ export interface SuggestMusicPerformanceResponse {
export interface SuggestMusicResponse {
musics: musics[];
}

export interface RecommendSong {
songId: string;
songName: string;
artistName: string;
artworkUrl: string;
previewUrl: string;
}

export interface RecommendPerformances {
typeId: number;
type: 'concert' | 'festival';
title: string;
posterUrl: string;
songs: RecommendSong[];
}

export interface RecommendPerformancesResponse {
performances: RecommendPerformances[];
}
Loading
Loading