mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 13:38:51 +00:00
Update sample code
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React</title>
|
||||
<title>usePopcorn</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
+168
-38
@@ -1,5 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { useState } from "react";
|
||||
import StarRating from "./StarRating";
|
||||
|
||||
const average = (arr) =>
|
||||
arr.reduce((acc, cur, i, arr) => acc + cur / arr.length, 0);
|
||||
@@ -23,13 +24,25 @@ export default function App() {
|
||||
setSelectedId(null);
|
||||
};
|
||||
|
||||
const handleAddWatched = (movie) => {
|
||||
setWatched((watched) => [...watched, movie]);
|
||||
};
|
||||
|
||||
const handleDeleteWatched = (id) => {
|
||||
setWatched((watched) => watched.filter((movie) => movie.imdbID !== id));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
|
||||
const fetchMovies = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
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)
|
||||
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!");
|
||||
|
||||
setMovies(data.Search);
|
||||
setError("");
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
if (err.name !== "AbortError") setError(err.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -53,6 +67,10 @@ export default function App() {
|
||||
}
|
||||
|
||||
fetchMovies();
|
||||
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
@@ -77,11 +95,16 @@ export default function App() {
|
||||
<MovieDetails
|
||||
selectedId={selectedId}
|
||||
onCloseMovie={handleCloseMovie}
|
||||
onAddWatched={handleAddWatched}
|
||||
watched={watched}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<WatchedSummary watched={watched} />
|
||||
<WatchMovieList watched={watched} />
|
||||
<WatchMovieList
|
||||
watched={watched}
|
||||
onDeleteWatched={handleDeleteWatched}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</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 }) {
|
||||
return (
|
||||
<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 (
|
||||
<div className="details">
|
||||
<button className="btn-back" onClick={onCloseMovie}>
|
||||
←
|
||||
</button>
|
||||
{selectedId}
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<>
|
||||
<header>
|
||||
<button className="btn-back" onClick={onCloseMovie}>
|
||||
←
|
||||
</button>
|
||||
<img src={poster} alt={`Poster of ${movie}`} />
|
||||
<div className="details-overview">
|
||||
<h2>{title}</h2>
|
||||
<p>
|
||||
{released} • {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>
|
||||
);
|
||||
}
|
||||
@@ -226,36 +346,40 @@ function WatchedSummary({ watched }) {
|
||||
</p>
|
||||
<p>
|
||||
<span>⭐️</span>
|
||||
<span>{avgImdbRating}</span>
|
||||
<span>{avgImdbRating.toFixed(2)}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>🌟</span>
|
||||
<span>{avgUserRating}</span>
|
||||
<span>{avgUserRating.toFixed(2)}</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>⏳</span>
|
||||
<span>{avgRuntime} min</span>
|
||||
<span>{avgRuntime.toFixed(2)} min</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function WatchMovieList({ watched }) {
|
||||
function WatchMovieList({ watched, onDeleteWatched }) {
|
||||
return (
|
||||
<ul className="list">
|
||||
{watched.map((movie) => (
|
||||
<WatchedMovie movie={movie} key={movie.imdbID} />
|
||||
<WatchedMovie
|
||||
movie={movie}
|
||||
key={movie.imdbID}
|
||||
onDeleteWatched={onDeleteWatched}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
function WatchedMovie({ movie }) {
|
||||
function WatchedMovie({ movie, onDeleteWatched }) {
|
||||
return (
|
||||
<li key={movie.imdbID}>
|
||||
<img src={movie.Poster} alt={`${movie.Title} poster`} />
|
||||
<h3>{movie.Title}</h3>
|
||||
<img src={movie.poster} alt={`${movie.title} poster`} />
|
||||
<h3>{movie.title}</h3>
|
||||
<div>
|
||||
<p>
|
||||
<span>⭐️</span>
|
||||
@@ -269,6 +393,12 @@ function WatchedMovie({ movie }) {
|
||||
<span>⏳</span>
|
||||
<span>{movie.runtime} min</span>
|
||||
</p>
|
||||
<button
|
||||
className="btn-delete"
|
||||
onClick={() => onDeleteWatched(movie.imdbID)}
|
||||
>
|
||||
X
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function StarRating({
|
||||
const handleRating = (rating) => {
|
||||
setRating(rating);
|
||||
|
||||
if (onSetRating) setRating(rating);
|
||||
if (onSetRating) onSetRating(rating);
|
||||
};
|
||||
|
||||
const textStyle = {
|
||||
|
||||
Reference in New Issue
Block a user