Skip to content

Commit

Permalink
Intercalate API and mock data, implement genre filtering, and improve…
Browse files Browse the repository at this point in the history
… image handling in Player component #27
  • Loading branch information
Jhonatanjacome07 committed Dec 5, 2024
1 parent 15d2cfd commit 112c460
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 93 deletions.
6 changes: 5 additions & 1 deletion frontend/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
images: {
domains: ["cdn-images.dzcdn.net"], // Dominio para poder trabajar con las imagenes
},
};

export default nextConfig;
14 changes: 8 additions & 6 deletions frontend/src/app/(page)/explore/components/SongItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ interface SongItemProps {
preview: string;
md5_image: string;
artist: string;
genre: string;
artistImage: string;
genres: string;

isSelected: boolean;
isFavorite: boolean;
Expand All @@ -21,7 +22,8 @@ interface SongItemProps {

const SongItem: React.FC<SongItemProps> = ({
title,
genre,
genres,
artistImage,
duration,
isSelected,
isFavorite,
Expand All @@ -41,9 +43,9 @@ const SongItem: React.FC<SongItemProps> = ({
<div className="grid grid-cols-[48px_40px_1fr_auto] sm:grid-cols-[78px_100px_2fr_180px_100px] items-center gap-2">
<div className="h-12 w-12 rounded overflow-hidden relative">
<Image
src="/bg-4.jpg"
src={artistImage}
alt="Album art"
fill
layout="fill"
className="object-cover"
/>
</div>
Expand All @@ -69,12 +71,12 @@ const SongItem: React.FC<SongItemProps> = ({
{title}
</div>
<div className="block sm:hidden text-white text-xs font-medium truncate">
{genre}
{genres}
</div>
</div>

<div className="hidden sm:block text-white font-bold text-lg truncate">
{genre}
{genres}
</div>

<div className="text-white font-bold text-right text-sm sm:text-lg">
Expand Down
72 changes: 51 additions & 21 deletions frontend/src/app/(page)/explore/components/SongList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState, useCallback } from "react";
import React, { useState, useEffect, useCallback } from "react";
import SongItem from "./SongItem";
import Player from "@/components/ui/Player";
import { fetchSongs } from "@/utils/fetchSongs";
import { useFilterSongs } from "@/hooks/useFilterSongs";
import { mockSongs } from "@/data/mockSongs";
import { Song } from "@/types/ui/Song";
import { formatDuration } from "@/app/utils/formatDuration";
import { formatDuration } from "@/utils/formatDuration";
import { mockSongs } from "@/data/mockSongs"; // Importa los datos hardcodeados

interface SongListProps {
searchTerm?: string;
Expand All @@ -17,8 +18,35 @@ export default function SongList({
}: SongListProps) {
const [selectedSong, setSelectedSong] = useState<Song | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [songs, setSongs] = useState<Song[]>(mockSongs);
const filteredSongs = useFilterSongs(songs, searchTerm, selectedGenre);
const [songs, setSongs] = useState<Song[]>([]); // Inicializar vacío

useEffect(() => {
const loadSongs = async () => {
try {
const fetchedSongs = await fetchSongs(searchTerm);
console.log("Fetched Songs:", fetchedSongs); // Debug log

// Intercalar canciones de la API y mockSongs
const combinedSongs: Song[] = [];
const maxLength = Math.max(fetchedSongs.length, mockSongs.length);
for (let i = 0; i < maxLength; i++) {
if (i < fetchedSongs.length) {
combinedSongs.push(fetchedSongs[i]);
}
if (i < mockSongs.length) {
combinedSongs.push(mockSongs[i]);
}
}

// Limitar a las primeras 15 canciones
setSongs(combinedSongs.slice(0, 15));
} catch (error) {
console.error("Error fetching songs:", error);
}
};

loadSongs();
}, [searchTerm]);

const handlePlay = useCallback(
(song: Song) => {
Expand All @@ -38,34 +66,33 @@ export default function SongList({

const handleNext = useCallback(() => {
if (!selectedSong) return;
const currentIndex = filteredSongs.findIndex(
(song) => song.id === selectedSong.id
);
if (currentIndex < filteredSongs.length - 1) {
setSelectedSong(filteredSongs[currentIndex + 1]);
const currentIndex = songs.findIndex((song) => song.id === selectedSong.id);
if (currentIndex < songs.length - 1) {
setSelectedSong(songs[currentIndex + 1]);
setIsPlaying(true);
}
}, [selectedSong, filteredSongs]);
}, [selectedSong, songs]);

const handlePrevious = useCallback(() => {
if (!selectedSong) return;
const currentIndex = filteredSongs.findIndex(
(song) => song.id === selectedSong.id
);
const currentIndex = songs.findIndex((song) => song.id === selectedSong.id);
if (currentIndex > 0) {
setSelectedSong(filteredSongs[currentIndex - 1]);
setSelectedSong(songs[currentIndex - 1]);
setIsPlaying(true);
}
}, [selectedSong, filteredSongs]);
}, [selectedSong, songs]);

const toggleFavorite = useCallback((songId: string) => {
const toggleFavoriteHandler = useCallback((songId: string) => {
setSongs((prevSongs) =>
prevSongs.map((song) =>
song.id === songId ? { ...song, isFavorite: !song.isFavorite } : song
)
);
}, []);

const filteredSongs = useFilterSongs(songs, searchTerm, selectedGenre);
console.log("Filtered Songs:", filteredSongs); // Debug log

return (
<div className="w-full">
<div className="mb-24">
Expand All @@ -75,31 +102,34 @@ export default function SongList({
id={song.id}
title={song.title}
title_short={song.title_short}
genre={song.genre}
genres={song.album.genres || ""}
duration={formatDuration(song.duration)}
md5_image={song.md5_image}
preview={song.preview}
artist={song.artist.name}
artistImage={song.artist.picture_medium}
isFavorite={song.isFavorite}
isSelected={selectedSong?.id === song.id}
onPlay={() => handlePlay(song)}
onFavoriteToggle={() => toggleFavorite(song.id)}
onFavoriteToggle={() => toggleFavoriteHandler(song.id)}
/>
))}
</div>
{selectedSong && (
<Player
currentSong={{
...selectedSong,
artist: selectedSong.artist.name, // Asegúrate de usar el nombre
artist: selectedSong.artist.name,
}}
genres={selectedSong.album.genres || ""}
artistImage={selectedSong.artist.picture_medium}
isFavorite={selectedSong.isFavorite}
isPlaying={isPlaying}
onPlayPause={handlePlayPause}
onNext={handleNext}
onPrevious={handlePrevious}
onFavoriteToggle={() =>
selectedSong && toggleFavorite(selectedSong.id)
selectedSong && toggleFavoriteHandler(selectedSong.id)
}
/>
)}
Expand Down
42 changes: 28 additions & 14 deletions frontend/src/components/ui/Player.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import Image from "next/image";
import { formatDuration } from "@/app/utils/formatDuration";
import { formatDuration } from "@/utils/formatDuration";
import {
SkipBack,
Rewind,
Expand All @@ -18,10 +18,11 @@ interface PlayerProps {
title: string;
artist: string;
duration: string;
genre: string;
preview: string;
isFavorite: boolean;
};
genres: string;
artistImage: string;
isPlaying: boolean;
onPlayPause: () => void;
onNext?: () => void;
Expand All @@ -32,6 +33,8 @@ interface PlayerProps {

const Player: React.FC<PlayerProps> = ({
currentSong,
genres,
artistImage,
isPlaying,
onPlayPause,
onNext,
Expand Down Expand Up @@ -90,14 +93,6 @@ const Player: React.FC<PlayerProps> = ({
}
}, [isPlaying, currentSong]);

const updateProgress = () => {
if (!audioRef.current) return;
const duration = audioRef.current.duration;
const currentTime = audioRef.current.currentTime;
const progressPercent = (currentTime / duration) * 100;
setProgress(progressPercent);
setCurrentTime(formatTime(currentTime));
};
//Para que el player se mueva con el scroll y no pase a cubrir el footer
useEffect(() => {
const handleScroll = () => {
Expand All @@ -118,6 +113,15 @@ const Player: React.FC<PlayerProps> = ({
return () => window.removeEventListener("scroll", handleScroll);
}, []);

const updateProgress = () => {
if (!audioRef.current) return;
const duration = audioRef.current.duration;
const currentTime = audioRef.current.currentTime;
const progressPercent = (currentTime / duration) * 100;
setProgress(progressPercent);
setCurrentTime(formatTime(currentTime));
};

const handleSongEnd = () => {
setProgress(0);
setCurrentTime("00:00");
Expand Down Expand Up @@ -161,6 +165,15 @@ const Player: React.FC<PlayerProps> = ({
);
};

const handleStop = () => {
if (audioRef.current) {
audioRef.current.pause();
audioRef.current.currentTime = 0;
setProgress(0);
setCurrentTime("00:00");
}
};

const formatTime = (time: number): string => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
Expand All @@ -178,10 +191,10 @@ const Player: React.FC<PlayerProps> = ({
>
<div className="w-1/8 mx-auto flex justify-center items-center">
<Image
src="/bg-4.jpg"
src={artistImage}
alt="Album art"
width={200}
height={250}
width={150}
height={150}
className="object-cover rounded-md"
/>
</div>
Expand All @@ -191,6 +204,7 @@ const Player: React.FC<PlayerProps> = ({
<div>
<h3 className="text-primary font-medium">{currentSong.title}</h3>
<p className="text-primary/60 text-sm">{currentSong.artist}</p>
<p className="text-primary/60 text-sm">{genres}</p>
</div>
<Heart
className={`w-6 h-6 cursor-pointer transition-colors
Expand Down Expand Up @@ -238,7 +252,7 @@ const Player: React.FC<PlayerProps> = ({
className="text-primary cursor-pointer"
size={47}
style={{ strokeWidth: 2, fill: "currentColor" }}
onClick={onPlayPause}
onClick={handleStop} // Cambia a handleStop
/>
<FastForward
className="text-primary cursor-pointer"
Expand Down
Loading

0 comments on commit 112c460

Please sign in to comment.