react-icons/ri#RiFolderMusicLine JavaScript Examples
The following examples show how to use
react-icons/ri#RiFolderMusicLine.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Player.jsx From xetera.dev with MIT License | 4 votes |
PlayerControls = ({
token,
play,
authorized,
login,
logout,
trackList,
volume,
setVolume,
refreshToken,
}) => {
const inside = useRef()
const mainPlayer = useRef()
const [closed, setClosed] = useLocalStorage("playerClosed", true)
const [trackListOpen, { toggle: toggleTrackList, off: closeTrackList }] =
useBoolean(false)
const [volumeOpen, { toggle: toggleVolume, off: closeVolume }] =
useBoolean(false)
const [timedOut, setTimedOut] = useState(false)
const timeoutTimer = useRef()
const playbackState = usePlaybackState(true, 100)
const player = useSpotifyPlayer()
const playerDevice = usePlayerDevice()
const scrollerColor = "bgSecondary"
useOutsideClick({
ref: inside,
handler: () => {
closeTrackList()
closeVolume()
},
})
function takeControl() {
if (playerDevice?.device_id === undefined) return console.log("no device")
if (token.current) {
// https://developer.spotify.com/documentation/web-api/reference/#endpoint-transfer-a-users-playback
fetch(`https://api.spotify.com/v1/me/player`, {
method: "PUT",
body: JSON.stringify({
device_ids: [playerDevice.device_id],
play: false,
}),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token.current}`,
},
})
}
}
async function playSong(offset, isRetry = false) {
const res = await play(playerDevice.device_id, trackListUris, offset)
if (!res.ok && res.status === 401) {
if (isRetry) {
console.log(
`Attempted to retry playing a song after a token refresh and it failed again`
)
return
}
refreshToken().then(() => {
playSong(offset, true)
})
}
}
function handleClick(e) {
e.preventDefault()
if (!authorized || timedOut) {
login()
return
}
// we don't want to handle clicks from the surrounding components
if (e.target !== mainPlayer.current) {
return
}
if (player) {
player.togglePlay()
}
}
useEffect(() => {
if (!playbackState && !timeoutTimer.current) {
timeoutTimer.current = setTimeout(() => {
setTimedOut(true)
}, 6000)
return
}
if (playbackState && timeoutTimer.current) {
clearTimeout(timeoutTimer.current)
}
if (playbackState && timedOut) {
setTimedOut(false)
}
}, [Boolean(playbackState)])
useEffect(takeControl, [playerDevice?.device_id, authorized])
const trackListUris = trackList?.tracks.items.map(item => item.uri)
const trackName = playbackState?.track_window.current_track.name
const albumName = playbackState?.track_window.current_track.artists[0].name
const albumArt = playbackState?.track_window.current_track.album.images[0].url
const thisMonth = months[new Date().getMonth()]
return (
<>
<AnimatePresence>
{closed && (
<MotionBox
position="fixed"
bottom={[2, null, 4, 8]}
left={[2, null, 4, 8]}
cursor="pointer"
onClick={() => setClosed(false)}
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 1 }}
zIndex={2}
>
<Box
background="bgSecondary"
borderRadius="lg"
p={2}
h={10}
w={10}
className="spotify-button"
>
<StaticImage
quality="100"
alt="Spotify logo"
src="./spotify.png"
placeholder="none"
style={{
filter: playbackState?.paused
? "grayscale(1)"
: "grayscale(0)",
}}
/>
</Box>
</MotionBox>
)}
</AnimatePresence>
<MotionFlex
ref={inside}
position="fixed"
bottom={[2, 4, 8]}
left={[2, 4, 8]}
alignItems="center"
zIndex={8}
variants={{
closed: { y: 200 },
open: { y: 0 },
}}
transition={{ stiffness: 50 }}
initial="closed"
animate={closed ? "closed" : "open"}
exit="open"
cursor="pointer"
>
<Flex
alignItems="center"
flexFlow="column"
ref={mainPlayer}
onClick={handleClick}
>
<Flex position="relative" width="300px" background="bgSecondary">
<Flex
position="absolute"
bottom="-40px"
top="-30px"
borderRadius="sm"
overflow="hidden"
left="100%"
pointerEvents={volumeOpen ? "auto" : "none"}
>
<MotionBox
ml={1}
height="100%"
overflow="hidden"
css={{
"&::-webkit-scrollbar": {
width: "8px",
},
"&::-webkit-scrollbar-track": {
width: "8px",
},
}}
display="flex"
justifyContent="center"
flexDirection="column"
width="30px"
background="bgSecondary"
transition={{ type: "tween", duration: 0.3 }}
variants={{
open: { x: 0, opacity: 1 },
closed: { x: -40, opacity: 0 },
}}
initial="closed"
animate={volumeOpen ? "open" : "closed"}
>
<Text fontSize="xs" color="text.400" textAlign="center" pt={2}>
{Math.floor(volume * 100)}
</Text>
<Box p={2} h="full">
<Slider
height="100%"
orientation="vertical"
value={volume * 100}
onChange={e => setVolume(e / 100)}
>
<SliderTrack background="bg.100">
<SliderFilledTrack bg="brandBackground.200" />
</SliderTrack>
<SliderThumb />
</Slider>
</Box>
</MotionBox>
</Flex>
<Box
width="100%"
position="absolute"
bottom="100%"
overflow="hidden"
right={0}
pointerEvents={trackListOpen ? "auto" : "none"}
>
<MotionBox
background="bg.100"
transition={{ type: "tween" }}
variants={{
open: { y: 0 },
closed: { y: 700 },
}}
initial="closed"
exit="closed"
animate={trackListOpen ? "open" : "closed"}
>
<Flex
className="themed-scrollable"
maxHeight="600px"
overflowY="auto"
borderColor="borderSubtle"
borderWidth="1px"
spacing={4}
alignItems="flex-start"
overflowX="hidden"
flexFlow="column"
>
{trackList.tracks.items.map((r, i) => {
// album images are sorted from biggest to smallest
const { images } = r.album
const albumArt = images[images.length - 1]
return (
<Flex
py={2}
px={3}
w="full"
key={r.uri}
_hover={{ background: scrollerColor }}
onClick={() => {
if (!authorized) {
return login()
}
if (timedOut || !playerDevice) {
return
}
playSong(i)
}}
alignItems="center"
background={
playbackState?.track_window.current_track.uri ===
r.uri
? "bgSecondary"
: ""
}
filter={
timedOut || !authorized
? "grayscale(1)"
: "grayscale(0)"
}
>
<Box
width={7}
whiteSpace="nowrap"
textAlign="right"
color="text.400"
fontSize="xs"
pr={3}
>
{i + 1}
</Box>
<Image
src={albumArt?.url}
alt={`Song: ${r.name}`}
h={8}
w={8}
marginInlineEnd={2}
loading="lazy"
// spotify CDN doesn't support HTTP2 and needs to be
// marked as low priority to prevent it from hogging
// precious bandwidth
fetchpriorit="low"
/>
<VStack alignItems="flex-start" spacing={0}>
<Text
fontWeight="bold"
fontSize="sm"
lineHeight="1.2"
>
{r.name}
</Text>
<Text fontSize="xs" lineHeight="1.2">
{r.artists[0]?.name ?? "Unknown artist"}
</Text>
</VStack>
</Flex>
)
})}
</Flex>
<Text
as="h2"
textAlign="center"
width="65%"
ml={6}
mt={1}
py={2}
lineHeight="1.2"
fontSize="xs"
fontWeight="medium"
color="text.100"
letterSpacing="1.1px"
textTransform="uppercase"
>
{thisMonth} favorites
</Text>
</MotionBox>
</Box>
<Flex
position="absolute"
bottom="100%"
right={0}
left={0}
justifyContent="space-between"
>
{authorized ? (
<Tooltip label="Logout">
<Flex
background="bgSecondary"
p={1}
mb={1}
borderRadius="sm"
onClick={logout}
>
<RiLogoutBoxLine size={BUTTON_SIZE} />
</Flex>
</Tooltip>
) : (
<Box />
)}
<HStack spacing={1} mb={1} justifyContent="flex-end">
<AnimatePresence>
{!closed && (
<Tooltip
label={
trackListOpen ? "Hide tracklist" : "Show tracklist"
}
>
<MotionFlex
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -20, opacity: 0 }}
transition={{ delay: 0.15, stiffness: 20 }}
background="bgSecondary"
p={1}
borderRadius="sm"
onClick={e => {
e.stopPropagation()
toggleTrackList()
}}
>
<RiFolderMusicLine size={BUTTON_SIZE} />
</MotionFlex>
</Tooltip>
)}
</AnimatePresence>
{authorized && (
<Tooltip label="Volume">
<MotionFlex
background="bgSecondary"
p={1}
borderRadius="sm"
onClick={e => {
e.stopPropagation()
toggleVolume()
}}
>
<RiVolumeUpLine size={BUTTON_SIZE} />
</MotionFlex>
</Tooltip>
)}
<AnimatePresence>
{!closed && (
<Tooltip label="Minimize">
<MotionFlex
background="bgSecondary"
p={1}
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -20, opacity: 0 }}
transition={{ delay: 0.3, stiffness: 20 }}
borderRadius="sm"
onClick={e => {
e.stopPropagation()
closeTrackList()
closeVolume()
setClosed(true)
}}
>
<RiCloseLine size={BUTTON_SIZE} />
</MotionFlex>
</Tooltip>
)}
</AnimatePresence>
</HStack>
</Flex>
<Flex
w="70px"
h="70px"
overflow="hidden"
maxWidth="100%"
alignItems="center"
justifyContent="center"
>
<AlbumCover
state={
playbackState
? { type: "ready", src: albumArt }
: !authorized
? { type: "notAuthorized" }
: timedOut
? { type: "timedOut" }
: { type: "waiting" }
}
/>
</Flex>
<HStack marginInlineStart={1} p={2}>
<VStack spacing={2} alignItems="flex-start" color="text.300">
{trackName ? (
<Text
fontSize="sm"
fontWeight="bold"
lineHeight="1.2"
color="text.100"
>
{trackName}
</Text>
) : !authorized ? (
<Text fontSize="sm" fontWeight="bold" lineHeight="1.2">
Got Spotify premium?
</Text>
) : timedOut ? (
<Text fontSize="sm" fontWeight="bold" lineHeight="1">
Couldn't connect to Spotify
</Text>
) : (
<Skeleton height="15px" width="80px" />
)}
{albumName ? (
<Text fontSize="xs" lineHeight="1.2">
{albumName}
</Text>
) : !authorized ? (
<Text fontSize="xs" lineHeight="1">
Click to vibe to my playlists
</Text>
) : timedOut ? (
<Text fontSize="xs" lineHeight="1">
Click to re-authorize
</Text>
) : (
<Skeleton height="8px" width="30px" />
)}
</VStack>
</HStack>
{playbackState && (
<Tooltip label="Track's Spotify page">
<Link
p={1}
borderColor="borderSubtle"
right={2}
bottom={2}
w={7}
h={7}
target="_blank"
rel="noopener noreferrer"
href={`https://open.spotify.com/track/${playbackState.track_window.current_track.id}`}
position="absolute"
borderWidth="1px"
borderRadius="lg"
overflow="hidden"
>
<StaticImage
quality="100"
alt="Spotify logo"
src="./spotify.png"
placeholder="none"
/>
</Link>
</Tooltip>
)}
</Flex>
<Flex
background="bgSecondary"
mt={1}
w="full"
h="35px"
position="relative"
borderTopRadius="sm"
alignItems="center"
justifyContent="center"
>
{!authorized ? (
<Text fontSize="xs" color="text.400">
This widget connects to your Spotify account
</Text>
) : timedOut ? (
<Text fontSize="xs" color="text.300">
Maybe change devices in your Spotify app?
</Text>
) : playbackState ? (
<HStack py={1}>
<Box
p={1}
borderColor="borderSubtle"
borderWidth="1px"
borderRadius="lg"
overflow="hidden"
onClick={() => player?.previousTrack()}
>
{!playbackState?.disallows.skipping_prev && (
<RiSkipBackLine size={BUTTON_SIZE} />
)}
</Box>
<Box
p={1}
borderColor="borderSubtle"
borderWidth="1px"
borderRadius="lg"
overflow="hidden"
onClick={() => player?.togglePlay()}
>
{!playbackState && (
<Skeleton
height={BUTTON_SIZE}
width={BUTTON_SIZE}
borderRadius="lg"
/>
)}
{playbackState?.disallows.resuming && (
<RiPauseLine size={BUTTON_SIZE} />
)}
{playbackState?.disallows.pausing && (
<RiPlayLine size={BUTTON_SIZE} />
)}
</Box>
<Box
p={1}
borderColor="borderSubtle"
borderWidth="1px"
borderRadius="lg"
overflow="hidden"
onClick={() => player?.nextTrack()}
>
{!playbackState?.disallows.skipping_next && (
<RiSkipForwardLine size={BUTTON_SIZE} />
)}
</Box>
</HStack>
) : (
<Skeleton w="full" h="35px" />
)}
{playbackState && (
<Seeker
position={playbackState.position}
duration={playbackState.duration}
seek={e => player.seek(e)}
/>
)}
</Flex>
</Flex>
</MotionFlex>
</>
)
}