pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
at main 124 lines 4.1 kB view raw
1import { useEffect, useState } from "react"; 2import { useNavigate } from "react-router-dom"; 3 4import { get } from "@/backend/metadata/tmdb"; 5import { Movie } from "@/pages/discover/common"; 6import { conf } from "@/setup/config"; 7import { useLanguageStore } from "@/stores/language"; 8import { getTmdbLanguageCode } from "@/utils/language"; 9 10interface TMDBMovieResponse { 11 results: Movie[]; 12} 13 14export function RandomMovieButton() { 15 const [randomMovie, setRandomMovie] = useState<Movie | null>(null); 16 const [countdown, setCountdown] = useState<number | null>(null); 17 const [countdownTimeout, setCountdownTimeout] = 18 useState<NodeJS.Timeout | null>(null); 19 const [movies, setMovies] = useState<Movie[]>([]); 20 const navigate = useNavigate(); 21 const userLanguage = useLanguageStore((s) => s.language); 22 const formattedLanguage = getTmdbLanguageCode(userLanguage); 23 24 // Fetch popular movies for random selection 25 useEffect(() => { 26 const fetchMovies = async () => { 27 try { 28 const data = await get<TMDBMovieResponse>("/movie/popular", { 29 api_key: conf().TMDB_READ_API_KEY, 30 language: formattedLanguage, 31 page: 2, 32 }); 33 setMovies(data.results); 34 } catch (error) { 35 console.error("Error fetching popular movies:", error); 36 } 37 }; 38 39 fetchMovies(); 40 }, [formattedLanguage]); 41 42 useEffect(() => { 43 let countdownInterval: NodeJS.Timeout; 44 if (countdown !== null && countdown > 0) { 45 countdownInterval = setInterval(() => { 46 setCountdown((prev) => (prev !== null ? prev - 1 : prev)); 47 }, 1000); 48 } 49 return () => clearInterval(countdownInterval); 50 }, [countdown]); 51 52 const handleRandomMovieClick = () => { 53 if (movies.length === 0) return; 54 55 const uniqueTitles = new Set(movies.map((movie) => movie.title)); 56 const uniqueTitlesArray = Array.from(uniqueTitles); 57 const randomIndex = Math.floor(Math.random() * uniqueTitlesArray.length); 58 const selectedMovie = movies.find( 59 (movie) => movie.title === uniqueTitlesArray[randomIndex], 60 ); 61 62 if (selectedMovie) { 63 if (countdown !== null && countdown > 0) { 64 setCountdown(null); 65 if (countdownTimeout) { 66 clearTimeout(countdownTimeout); 67 setCountdownTimeout(null); 68 setRandomMovie(null); 69 } 70 } else { 71 setRandomMovie(selectedMovie); 72 setCountdown(5); 73 const timeoutId = setTimeout(() => { 74 navigate(`/media/tmdb-movie-${selectedMovie.id}-random`); 75 }, 5000); 76 setCountdownTimeout(timeoutId); 77 } 78 } 79 }; 80 81 return ( 82 <div className="flex justify-center items-center"> 83 <button 84 type="button" 85 className={` 86 relative flex items-center overflow-hidden 87 rounded-full text-white h-10 88 bg-pill-background bg-opacity-50 hover:bg-pill-backgroundHover 89 transition-all duration-300 ease-in-out 90 ${countdown !== null && countdown > 0 ? "min-w-[10px] pl-3" : "w-10"} 91 `} 92 onClick={handleRandomMovieClick} 93 > 94 {/* Title container that slides in */} 95 <div 96 className={` 97 relative whitespace-nowrap 98 transition-all duration-300 ease-in-out 99 ${countdown !== null && countdown > 0 ? "opacity-100 translate-x-0" : "opacity-0 -translate-x-4"} 100 `} 101 > 102 {countdown !== null && countdown > 0 && ( 103 <span className="font-bold">{randomMovie?.title}</span> 104 )} 105 </div> 106 107 {/* Icon container that stays fixed on the right */} 108 <div className="ml-auto flex items-center justify-center w-10 h-10"> 109 {countdown !== null && countdown > 0 ? ( 110 <div className="animate-[pulse_1s_ease-in-out_infinite] text-lg font-bold"> 111 {countdown} 112 </div> 113 ) : ( 114 <img 115 src="/lightbar-images/dice.svg" 116 alt="Dice" 117 className="w-6 h-6" 118 /> 119 )} 120 </div> 121 </button> 122 </div> 123 ); 124}