From c66526241c38eb1791420ae3a6148ffde3032e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CHenry-Hiles=E2=80=9D?= <“henry@henryhiles.com”> Date: Thu, 27 Oct 2022 13:48:50 -0400 Subject: [PATCH] Lots of changes --- jsconfig.json | 1 - src/App.css | 1 - src/components/Card.jsx | 5 +- src/components/TopBar.jsx | 9 ++- src/constants.js | 21 ++++++ src/contexts/MoviesContext.jsx | 71 ------------------- src/hooks/useDebounce.jsx | 14 ++++ src/index.css | 16 +++-- src/main.jsx | 9 +-- src/pages/Home.jsx | 43 +++++++++++- src/pages/Movie.jsx | 120 ++++++++++++++++++++++++++------- src/styles/Card.module.css | 9 +++ src/styles/Home.module.css | 1 + src/styles/Movie.module.css | 58 ++++++++++++++++ src/styles/TopBar.module.css | 21 ++++++ vite.config.js | 1 - 16 files changed, 286 insertions(+), 114 deletions(-) create mode 100644 src/constants.js delete mode 100644 src/contexts/MoviesContext.jsx create mode 100644 src/hooks/useDebounce.jsx create mode 100644 src/styles/Movie.module.css create mode 100644 src/styles/TopBar.module.css diff --git a/jsconfig.json b/jsconfig.json index c927f2a..c598157 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -5,7 +5,6 @@ "styles/*": ["styles/*"], "components/*": ["components/*"], "hooks/*": ["hooks/*"], - "contexts/*": ["contexts/*"], "pages/*": ["pages/*"] } } diff --git a/src/App.css b/src/App.css index 2e75c1f..1cfffd2 100644 --- a/src/App.css +++ b/src/App.css @@ -1,4 +1,3 @@ -#root, .App { width: 100%; height: 100%; diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 1a9f6d1..f8c9549 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -10,7 +10,9 @@ const Card = ({ movie }) => ( className={styles.Image} />
-

{movie.title}

+

+ {movie.title} - {movie.year} +

{movie.averageVote}{" "} (

+

{movie.overview}

diff --git a/src/components/TopBar.jsx b/src/components/TopBar.jsx index 17e85f0..40a14ee 100644 --- a/src/components/TopBar.jsx +++ b/src/components/TopBar.jsx @@ -1,9 +1,14 @@ +import styles from "styles/TopBar.module.css" + const TopBar = ({ search, setSearch }) => ( -
-

React Movie Finder

+
+

React Movie Finder

setSearch(target.value)} />
diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..c5bb65e --- /dev/null +++ b/src/constants.js @@ -0,0 +1,21 @@ +export const GENRES = [ + { id: 28, name: "Action" }, + { id: 12, name: "Adventure" }, + { id: 16, name: "Animation" }, + { id: 35, name: "Comedy" }, + { id: 80, name: "Crime" }, + { id: 99, name: "Documentary" }, + { id: 18, name: "Drama" }, + { id: 10751, name: "Family" }, + { id: 14, name: "Fantasy" }, + { id: 36, name: "History" }, + { id: 27, name: "Horror" }, + { id: 10402, name: "Music" }, + { id: 9648, name: "Mystery" }, + { id: 10749, name: "Romance" }, + { id: 878, name: "Science Fiction" }, + { id: 10770, name: "TV Movie" }, + { id: 53, name: "Thriller" }, + { id: 10752, name: "War" }, + { id: 37, name: "Western" }, +] diff --git a/src/contexts/MoviesContext.jsx b/src/contexts/MoviesContext.jsx deleted file mode 100644 index 12e2e50..0000000 --- a/src/contexts/MoviesContext.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import { createContext, useContext, useEffect, useState } from "react" -import config from "config" - -const MoviesContext = createContext() - -export const useMovies = () => useContext(MoviesContext) - -export const MoviesProvider = ({ children }) => { - const [movies, setMovies] = useState([]) - const [genres, setGenres] = useState({}) - const [page, setPage] = useState(1) - const [search, setSearch] = useState("") - - useEffect(() => { - const run = async () => { - const genresResponse = await fetch( - `https://api.themoviedb.org/3/genre/movie/list?api_key=${config.apiKey}` - ) - const genresData = await genresResponse.json() - genresData.genres.forEach((genre) => - setGenres((current) => { - const copy = {} - Object.assign(copy, current) - copy[genre.id] = genre.name - return copy - }) - ) - } - run() - }) - - useEffect(() => { - const run = async () => { - const response = await fetch( - `https://api.themoviedb.org/3/${ - search ? "search" : "discover" - }/movie?api_key=${ - config.apiKey - }&page=${page}&query=${encodeURIComponent(search)}` - ) - const data = await response.json() - setMovies( - data.results.map((movie) => ({ - id: movie.id, - overview: movie.overview, - adult: movie.adult, - posterUrl: `https://image.tmdb.org/t/p/w342/${movie.poster_path}`, - backdropUrl: `https://image.tmdb.org/t/p/original/${movie.backdrop_path}`, - genres: movie.genre_ids.map((genreId) => genres[genreId]), - title: movie.title, - releaseDate: movie.release_date, - averageVote: movie.vote_average, - voteCount: movie.vote_count, - popularity: movie.popularity, - })) - ) - } - - run() - }, [page, genres]) - - const nextPage = () => setPage((page) => page + 1) - - return ( - - {children} - - ) -} diff --git a/src/hooks/useDebounce.jsx b/src/hooks/useDebounce.jsx new file mode 100644 index 0000000..c172c28 --- /dev/null +++ b/src/hooks/useDebounce.jsx @@ -0,0 +1,14 @@ +import { useState, useEffect } from "react" + +export default function useDebounce(value, delay = 500) { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const timeout = setTimeout(() => { + setDebouncedValue(value) + }, delay) + return () => clearTimeout(timeout) + }, [value, delay]) + + return debouncedValue +} diff --git a/src/index.css b/src/index.css index 0c99d4c..b2f5e28 100644 --- a/src/index.css +++ b/src/index.css @@ -10,14 +10,12 @@ } body { - background-color: var(--secondary); margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - height: 100%; + margin: 0; + background-color: var(--secondary); } code { @@ -28,3 +26,13 @@ code { button { cursor: pointer; } + +hr { + width: 97%; + color: white; +} + +#root, +body { + height: 100%; +} diff --git a/src/main.jsx b/src/main.jsx index 76e4499..b3eda51 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -2,15 +2,12 @@ import React from "react" import ReactDOM from "react-dom/client" import App from "./App" import "./index.css" -import { MoviesProvider } from "contexts/MoviesContext" import { BrowserRouter } from "react-router-dom" ReactDOM.createRoot(document.getElementById("root")).render( - - - - - + + + ) diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 3730213..acf5f89 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,10 +1,49 @@ -import { useMovies } from "contexts/MoviesContext" import CardList from "components/CardList" import TopBar from "components/TopBar" +import useDebounce from "hooks/useDebounce" +import { useEffect, useState } from "react" import styles from "styles/Home.module.css" +import config from "config" +import { GENRES } from "../constants" const Home = () => { - const [movies, page, search, { nextPage, setSearch }] = useMovies() + const [movies, setMovies] = useState([]) + const [page, setPage] = useState(1) + const [search, setSearch] = useState("") + const debouncedSearch = useDebounce(search) + + useEffect(() => { + const run = async () => { + const response = await fetch( + `https://api.themoviedb.org/3/${ + debouncedSearch ? "search" : "discover" + }/movie?api_key=${ + config.apiKey + }&page=${page}&query=${encodeURIComponent(debouncedSearch)}` + ) + const data = await response.json() + setMovies( + data.results.map((movie) => ({ + id: movie.id, + overview: movie.overview, + adult: movie.adult, + posterUrl: `https://image.tmdb.org/t/p/w342${movie.poster_path}`, + backdropUrl: `https://image.tmdb.org/t/p/original${movie.backdrop_path}`, + genres: movie.genre_ids.map((genreId) => + GENRES.find((genre) => genre.id == genreId) + ), + title: movie.title, + releaseDate: movie.release_date, + year: movie.release_date.split("-")[0], + averageVote: movie.vote_average, + voteCount: movie.vote_count, + popularity: movie.popularity, + })) + ) + } + + run() + }, [page, debouncedSearch]) return (
diff --git a/src/pages/Movie.jsx b/src/pages/Movie.jsx index 34fd4d1..ec3de73 100644 --- a/src/pages/Movie.jsx +++ b/src/pages/Movie.jsx @@ -1,37 +1,107 @@ import { useEffect, useState } from "react" import { useParams } from "react-router-dom" -import { useMovies } from "contexts/MoviesContext" +import styles from "styles/Movie.module.css" +import config from "config" const Movie = () => { - const [movie, setMovie] = useState() - const [movies] = useMovies() const { movieId } = useParams() + const [movie, setMovie] = useState() + const [cast, setCast] = useState() - useEffect( - () => setMovie(movies.find((movie) => movie.id === parseInt(movieId))), - [movieId, movies] - ) + useEffect(() => { + const run = async () => { + const response = await fetch( + `https://api.themoviedb.org/3/movie/${movieId}?api_key=${config.apiKey}` + ) + const data = await response.json() + setMovie({ + id: data.id, + overview: data.overview, + adult: data.adult, + posterUrl: `https://image.tmdb.org/t/p/w342${data.poster_path}`, + backdropUrl: `https://image.tmdb.org/t/p/original${data.backdrop_path}`, + genres: data.genres.map((genre) => genre.name), + title: data.title, + releaseDate: data.release_date, + year: data.release_date.split("-")[0], + averageVote: data.vote_average, + voteCount: data.vote_count, + popularity: data.popularity, + tagline: data.tagline, + }) + } + run() + }, [movieId]) - return ( -
- {movie?.title} -
-

{movie?.title}

-

- {movie?.averageVote}{" "} - - - -

+ useEffect(() => { + const run = async () => { + const response = await fetch( + `https://api.themoviedb.org/3/movie/${movieId}/credits?api_key=${config.apiKey}` + ) + const data = await response.json() + setCast(data.cast) + } + run() + }, [movieId]) + + return movie ? ( +
+
+
+

+ {movie.title} - {movie.year} +

+

{movie.tagline}

+

{movie.overview}

+
+
+
+
+ {movie.genres.map((genre) => ( +

{genre}

+ ))} +

+ {movie.averageVote} + + + +

+
+
+
+

Actors

+
+ {cast?.map((actor) => ( +
+ {actor.profile_path ? ( + {actor.name} + ) : ( +

Image Unavailable

+ )} + +

{actor.name}

+
+ ))} +
+
+ ) : ( +
) } diff --git a/src/styles/Card.module.css b/src/styles/Card.module.css index 19b888c..5175c7d 100644 --- a/src/styles/Card.module.css +++ b/src/styles/Card.module.css @@ -21,6 +21,15 @@ flex-direction: column; } +.Overview { + text-overflow: ellipsis; + overflow: hidden; + display: -webkit-box !important; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; +} + .Title, .Average { margin: 0; diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css index 75f6177..5c9bdc6 100644 --- a/src/styles/Home.module.css +++ b/src/styles/Home.module.css @@ -2,4 +2,5 @@ display: flex; align-items: center; flex-direction: column; + gap: 1.5em; } diff --git a/src/styles/Movie.module.css b/src/styles/Movie.module.css new file mode 100644 index 0000000..32c3e37 --- /dev/null +++ b/src/styles/Movie.module.css @@ -0,0 +1,58 @@ +.Container { + color: white; +} + +.Top { + height: 100vh; + display: flex; + + background-size: cover; + background-position: center; + background-repeat: no-repeat; + flex-direction: column; + justify-content: flex-end; +} + +.Summary { + background: linear-gradient(#00000035, black); + height: 15%; + text-align: center; +} + +.Bottom { + background-color: black; + height: 100vh; + display: flex; + padding: 20px 40px; + flex-direction: column; +} + +.Section div { + margin-left: 10px; +} + +.Actors { + display: flex; + overflow: scroll; + align-items: start; +} + +.Actors > * { + text-align: center; +} + +.Tags { + display: flex; + gap: 10px; +} + +.Tags > * { + background-color: var(--primary); + padding: 5px 7px; + border-radius: 10px; +} + +.Average { + display: flex; + gap: 5px; +} diff --git a/src/styles/TopBar.module.css b/src/styles/TopBar.module.css new file mode 100644 index 0000000..57dd76d --- /dev/null +++ b/src/styles/TopBar.module.css @@ -0,0 +1,21 @@ +.Container { + display: flex; + width: 100%; + flex-direction: column; + align-items: center; +} + +.Search { + padding: 0.5em; + border: 2px solid white; + color: white; + font: inherit; + width: 20em; + height: 3em; + border-radius: 10px; + background-color: var(--secondary); +} + +.Title { + color: white; +} diff --git a/vite.config.js b/vite.config.js index 707b980..a3b705d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -10,7 +10,6 @@ export default defineConfig({ styles: path.resolve(__dirname, "/src/styles"), components: path.resolve(__dirname, "/src/components"), hooks: path.resolve(__dirname, "/src/hooks"), - contexts: path.resolve(__dirname, "/src/contexts"), pages: path.resolve(__dirname, "/src/pages"), }, },