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 vote down vote up
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>
    </>
  )
}