Last active
June 12, 2023 15:31
-
-
Save stephyswe/424dd1514434c88bdd5f9db8d47f1604 to your computer and use it in GitHub Desktop.
track length and spotify design
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { useEffect, useRef, useState } from "react"; | |
import { AiFillStepBackward, AiFillStepForward } from "react-icons/ai"; | |
import { BsPauseFill, BsPlayFill } from "react-icons/bs"; | |
import { HiSpeakerWave, HiSpeakerXMark } from "react-icons/hi2"; | |
import useSound from "use-sound"; | |
import Slider from "@/components/Slider"; | |
import SliderTrack from "@/components/SliderTrack"; | |
import usePlayer from "@/hooks/usePlayer"; | |
import { Song } from "@/types"; | |
import LikeButton from "./LikeButton"; | |
import MediaItem from "./MediaItem"; | |
interface PlayerContentProps { | |
song: Song | undefined; | |
songUrl: string; | |
} | |
function formatTime(seconds: number) { | |
const mins = Math.floor(seconds / 60); | |
const secs = Math.floor(seconds % 60); | |
return `${mins}:${secs < 10 ? "0" : ""}${secs}`; | |
} | |
const PlayerContent: React.FC<PlayerContentProps> = ({ song, songUrl }) => { | |
const player = usePlayer(); | |
const [volume, setVolume] = useState(1); | |
const [isPlaying, setIsPlaying] = useState(false); | |
const [progress, setProgress] = useState(0); | |
const Icon = isPlaying ? BsPauseFill : BsPlayFill; | |
const VolumeIcon = volume === 0 ? HiSpeakerXMark : HiSpeakerWave; | |
const onPlayNext = () => { | |
if (player.ids.length === 0) { | |
return; | |
} | |
const currentIndex = player.ids.findIndex((id) => id === player.activeId); | |
const nextSong = player.ids[currentIndex + 1]; | |
if (!nextSong) { | |
return player.setId(player.ids[0]); | |
} | |
player.setId(nextSong); | |
}; | |
const onPlayPrevious = () => { | |
if (player.ids.length === 0) { | |
return; | |
} | |
const currentIndex = player.ids.findIndex((id) => id === player.activeId); | |
const previousSong = player.ids[currentIndex - 1]; | |
if (!previousSong) { | |
return player.setId(player.ids[player.ids.length - 1]); | |
} | |
player.setId(previousSong); | |
}; | |
const [play, { pause, sound }] = useSound(songUrl, { | |
volume: volume, | |
onplay: () => { | |
setIsPlaying(true); | |
// setInterval(() => setProgress(sound.seek()), 1000); | |
}, | |
onend: () => { | |
setIsPlaying(false); | |
onPlayNext(); | |
}, | |
onpause: () => setIsPlaying(false), | |
format: ["mp3"], | |
}); | |
useEffect(() => { | |
sound?.play(); | |
// const clearId = setInterval(() => setProgress(sound.seek()), 1000); | |
// return () => clearInterval(clearId); | |
return () => { | |
sound?.unload(); | |
}; | |
}, [sound]); | |
// Create a reference for the interval id | |
const intervalId = useRef<NodeJS.Timeout | null>(null); | |
useEffect(() => { | |
// Only start the interval if the sound is loaded and is playing | |
if (sound && isPlaying) { | |
intervalId.current = setInterval(() => { | |
setProgress(sound.seek() as number); | |
}, 1000); | |
} else { | |
// If the sound is not playing, clear the interval | |
if (intervalId.current) { | |
clearInterval(intervalId.current); | |
} | |
} | |
// Clean up function to clear the interval when the component unmounts | |
return () => { | |
if (intervalId.current) { | |
clearInterval(intervalId.current); | |
} | |
}; | |
}, [sound, isPlaying]); | |
const handlePlay = () => { | |
if (!isPlaying) { | |
play(); | |
} else { | |
pause(); | |
} | |
}; | |
const toggleMute = () => { | |
if (volume === 0) { | |
setVolume(1); | |
} else { | |
setVolume(0); | |
} | |
}; | |
const handleSliderChange = (value: any) => { | |
if (!sound) { | |
return; | |
} | |
sound.seek(value); | |
setProgress(value); | |
}; | |
return ( | |
<div className="grid grid-cols-2 md:grid-cols-3 h-full"> | |
<div className="flex w-full justify-start"> | |
{song ? ( | |
<div className="flex items-center gap-x-4"> | |
<MediaItem data={song} /> | |
<LikeButton songId={song.id} /> | |
</div> | |
) : null} | |
</div> | |
<div | |
className=" | |
flex | |
md:hidden | |
col-auto | |
w-full | |
justify-end | |
items-center | |
" | |
> | |
<div | |
onClick={handlePlay} | |
className=" | |
h-10 | |
w-10 | |
flex | |
items-center | |
justify-center | |
rounded-full | |
bg-white | |
p-1 | |
cursor-pointer | |
" | |
> | |
<Icon size={30} className="text-black" /> | |
</div> | |
</div> | |
<div | |
className=" | |
hidden | |
h-full | |
md:flex | |
flex-col | |
justify-center | |
items-center | |
w-full | |
max-w-[722px] | |
gap-x-4 | |
" | |
> | |
<div | |
className=" | |
h-full | |
md:flex | |
justify-center | |
items-center | |
w-full | |
max-w-[722px] | |
gap-x-6 | |
" | |
> | |
<AiFillStepBackward | |
onClick={onPlayPrevious} | |
size={30} | |
className=" | |
text-neutral-400 | |
cursor-pointer | |
hover:text-white | |
transition | |
" | |
/> | |
<div | |
onClick={handlePlay} | |
className=" | |
flex | |
items-center | |
justify-center | |
h-10 | |
w-10 | |
rounded-full | |
bg-white | |
p-1 | |
cursor-pointer | |
" | |
> | |
<Icon size={30} className="text-black" /> | |
</div> | |
<AiFillStepForward | |
onClick={onPlayNext} | |
size={30} | |
className=" | |
text-neutral-400 | |
cursor-pointer | |
hover:text-white | |
transition | |
" | |
/> | |
</div> | |
<div className="w-full md:flex justify-center items-center gap-x-2"> | |
<span className="text-white text-xs">{formatTime(progress)}</span> | |
<SliderTrack | |
value={progress} | |
onChange={(value) => handleSliderChange(value)} | |
max={sound ? Math.floor(sound.duration()) : 0} | |
/> | |
<span className="text-white text-xs"> | |
{sound ? formatTime(Math.floor(sound.duration())) : "0:00"} | |
</span> | |
</div> | |
</div> | |
<div className="hidden md:flex w-full justify-end pr-2"> | |
<div className="flex items-center gap-x-2 w-[120px]"> | |
<VolumeIcon | |
onClick={toggleMute} | |
className="cursor-pointer" | |
size={34} | |
/> | |
<Slider value={volume} onChange={(value) => setVolume(value)} /> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default PlayerContent; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment