pstream is dead; long live pstream
taciturnaxolotl.github.io/pstream-ng/
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}