Update sample code

This commit is contained in:
2023-10-12 17:36:19 +07:00
parent db56b62030
commit 9dba4f5646
3 changed files with 171 additions and 41 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title> <title>usePopcorn</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+168 -38
View File
@@ -1,5 +1,6 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { useState } from "react"; import { useState } from "react";
import StarRating from "./StarRating";
const average = (arr) => const average = (arr) =>
arr.reduce((acc, cur, i, arr) => acc + cur / arr.length, 0); arr.reduce((acc, cur, i, arr) => acc + cur / arr.length, 0);
@@ -23,13 +24,25 @@ export default function App() {
setSelectedId(null); setSelectedId(null);
}; };
const handleAddWatched = (movie) => {
setWatched((watched) => [...watched, movie]);
};
const handleDeleteWatched = (id) => {
setWatched((watched) => watched.filter((movie) => movie.imdbID !== id));
};
useEffect(() => { useEffect(() => {
const controller = new AbortController();
const fetchMovies = async () => { const fetchMovies = async () => {
try { try {
setIsLoading(true); setIsLoading(true);
setError(""); setError("");
const res = await fetch(BASE_URL + `?apikey=${KEY}&s=${query}`); const res = await fetch(BASE_URL + `?apikey=${KEY}&s=${query}`, {
signal: controller.signal,
});
if (!res.ok) if (!res.ok)
throw new Error("Something went wrong with fetching movies"); throw new Error("Something went wrong with fetching movies");
@@ -39,8 +52,9 @@ export default function App() {
if (data.Response === "False") throw new Error("Movie not found!"); if (data.Response === "False") throw new Error("Movie not found!");
setMovies(data.Search); setMovies(data.Search);
setError("");
} catch (err) { } catch (err) {
setError(err.message); if (err.name !== "AbortError") setError(err.message);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@@ -53,6 +67,10 @@ export default function App() {
} }
fetchMovies(); fetchMovies();
return () => {
controller.abort();
};
}, [query]); }, [query]);
return ( return (
@@ -77,11 +95,16 @@ export default function App() {
<MovieDetails <MovieDetails
selectedId={selectedId} selectedId={selectedId}
onCloseMovie={handleCloseMovie} onCloseMovie={handleCloseMovie}
onAddWatched={handleAddWatched}
watched={watched}
/> />
) : ( ) : (
<> <>
<WatchedSummary watched={watched} /> <WatchedSummary watched={watched} />
<WatchMovieList watched={watched} /> <WatchMovieList
watched={watched}
onDeleteWatched={handleDeleteWatched}
/>
</> </>
)} )}
</Box> </Box>
@@ -153,28 +176,6 @@ function Box({ children }) {
); );
} }
// function WatchedBox() {
// const [watched, setWatched] = useState(tempWatchedData);
// const [isOpen2, setIsOpen2] = useState(true);
// return (
// <div className="box">
// <button
// className="btn-toggle"
// onClick={() => setIsOpen2((open) => !open)}
// >
// {isOpen2 ? "" : "+"}
// </button>
// {isOpen2 && (
// <>
// <WatchedSummary watched={watched} />
// <WatchMovieList watched={watched} />
// </>
// )}
// </div>
// );
// }
function MovieList({ movies, onSelectMovie }) { function MovieList({ movies, onSelectMovie }) {
return ( return (
<ul className="list list-movies"> <ul className="list list-movies">
@@ -200,13 +201,132 @@ function Movie({ movie, onSelectMovie }) {
); );
} }
function MovieDetails({ selectedId, onCloseMovie }) { function MovieDetails({ selectedId, onCloseMovie, onAddWatched, watched }) {
const [movie, setMovie] = useState({});
const [isLoading, setIsLoading] = useState(false);
const [userRating, setUserRating] = useState(0);
const isWatched = watched.map((movie) => movie.imdbID).includes(selectedId);
const watchedUserRating = watched.find(
(movie) => movie.imdbID === selectedId
)?.userRating;
const {
Title: title,
Year: year,
Poster: poster,
Runtime: runtime,
imdbRating,
Plot: plot,
Released: released,
Actors: actors,
Director: director,
Genre: genre,
} = movie;
const handleAdd = () => {
const newWatchedMovie = {
imdbID: selectedId,
title,
year,
poster,
imdbRating: Number(imdbRating),
runtime: Number(runtime.split(" ").at(0)),
userRating,
};
onAddWatched(newWatchedMovie);
onCloseMovie();
};
useEffect(() => {
if (!title) return;
document.title = `Movie | ${title}`;
return () => {
document.title = "usePopcorn";
};
}, [title]);
useEffect(() => {
async function getMovieDetails() {
setIsLoading(true);
const res = await fetch(BASE_URL + `?apikey=${KEY}&i=${selectedId}`);
const data = await res.json();
setMovie(data);
setIsLoading(false);
}
getMovieDetails();
}, [selectedId]);
useEffect(() => {
const callback = (e) => {
if (e.code === "Escape") {
onCloseMovie();
}
};
document.addEventListener("keydown", callback);
return () => {
document.removeEventListener("keydown", callback);
};
}, [onCloseMovie]);
return ( return (
<div className="details"> <div className="details">
<button className="btn-back" onClick={onCloseMovie}> {isLoading ? (
&larr; <Loader />
</button> ) : (
{selectedId} <>
<header>
<button className="btn-back" onClick={onCloseMovie}>
&larr;
</button>
<img src={poster} alt={`Poster of ${movie}`} />
<div className="details-overview">
<h2>{title}</h2>
<p>
{released} &bull; {runtime}
</p>
<p>{genre}</p>
<p>
<span></span>
{imdbRating} IMD rating
</p>
</div>
</header>
<section>
<div className="rating">
{!isWatched ? (
<>
<StarRating
maxRating={10}
size={24}
onSetRating={setUserRating}
/>
{userRating > 0 && (
<button className="btn-add" onClick={handleAdd}>
+ Add to list
</button>
)}
</>
) : (
<p>You rated this movie {watchedUserRating} </p>
)}
</div>
<p>
<em>{plot}</em>
</p>
<p>Starring {actors}</p>
<p>Directed by {director}</p>
</section>
</>
)}
</div> </div>
); );
} }
@@ -226,36 +346,40 @@ function WatchedSummary({ watched }) {
</p> </p>
<p> <p>
<span></span> <span></span>
<span>{avgImdbRating}</span> <span>{avgImdbRating.toFixed(2)}</span>
</p> </p>
<p> <p>
<span>🌟</span> <span>🌟</span>
<span>{avgUserRating}</span> <span>{avgUserRating.toFixed(2)}</span>
</p> </p>
<p> <p>
<span></span> <span></span>
<span>{avgRuntime} min</span> <span>{avgRuntime.toFixed(2)} min</span>
</p> </p>
</div> </div>
</div> </div>
); );
} }
function WatchMovieList({ watched }) { function WatchMovieList({ watched, onDeleteWatched }) {
return ( return (
<ul className="list"> <ul className="list">
{watched.map((movie) => ( {watched.map((movie) => (
<WatchedMovie movie={movie} key={movie.imdbID} /> <WatchedMovie
movie={movie}
key={movie.imdbID}
onDeleteWatched={onDeleteWatched}
/>
))} ))}
</ul> </ul>
); );
} }
function WatchedMovie({ movie }) { function WatchedMovie({ movie, onDeleteWatched }) {
return ( return (
<li key={movie.imdbID}> <li key={movie.imdbID}>
<img src={movie.Poster} alt={`${movie.Title} poster`} /> <img src={movie.poster} alt={`${movie.title} poster`} />
<h3>{movie.Title}</h3> <h3>{movie.title}</h3>
<div> <div>
<p> <p>
<span></span> <span></span>
@@ -269,6 +393,12 @@ function WatchedMovie({ movie }) {
<span></span> <span></span>
<span>{movie.runtime} min</span> <span>{movie.runtime} min</span>
</p> </p>
<button
className="btn-delete"
onClick={() => onDeleteWatched(movie.imdbID)}
>
X
</button>
</div> </div>
</li> </li>
); );
+1 -1
View File
@@ -36,7 +36,7 @@ export default function StarRating({
const handleRating = (rating) => { const handleRating = (rating) => {
setRating(rating); setRating(rating);
if (onSetRating) setRating(rating); if (onSetRating) onSetRating(rating);
}; };
const textStyle = { const textStyle = {