├── .gitignore ├── LICENSE ├── README.md ├── docs ├── icon-gitpages.png ├── icon-reactjs.png ├── icon-tmdb.png ├── logo-cdr.cdr ├── logo-netflix-clone.png └── netflix-gif.gif ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── Tmdb.js ├── assets │ ├── icon-amazon.png │ └── icon-netflix.png ├── components │ ├── FeaturedMovie │ │ ├── index.js │ │ └── styles.css │ ├── Header │ │ ├── index.js │ │ └── styles.css │ └── MovieRow │ │ ├── index.js │ │ └── styles.css ├── index.js ├── pages │ ├── Details │ │ ├── index.js │ │ └── styles.css │ └── Home │ │ ├── index.js │ │ └── styles.css ├── routes.js └── screen │ └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | /confidencial -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marcio Costa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | Logo 4 |

5 |

6 | Banner 7 |

8 | 9 |

10 | Netflix homepage clone using React JS. 11 |
12 |
13 | React JS 14 |

15 | 16 | ## Technologies 17 | Basically, this project was developed based on the following technologies: 18 | 19 |

20 | React JS 21 |    22 | TMDB 23 |    24 |

25 | 26 | ## Do you too 27 | 28 |

The 🔥 NETFLIX Clone in REACTJS for Beginners video by the amazing Bonieky Lacerda was used as a basis for creating the project and made me go beyond what was taught.

29 | 30 |

31 | 🔥 NETFLIX Clone in REACTJS for Beginners 32 |

33 | 34 | ## Start project 35 | 36 | With the terminal available, follow the instructions below: 37 | 38 | ```bash 39 | # Clone the repository 40 | $ git clone https://github.com/mcosta21/netflix-clone-reactjs 41 | 42 | # Access the netflix-clone-reactjs folder 43 | $ cd netflix-clone-reactjs 44 | 45 | # Install dependencies 46 | $ npm install 47 | 48 | # Start application 49 | $ npm start 50 | 51 | # Application running on port 3000 52 | ``` 53 | ### Observation 54 | 55 | To use themoviedb.org API, you must register and obtain an access key, which is necessarily used in the application. 56 | 57 | > Folder **src** > **Tmdbb.js** > **API_KEY** 58 | 59 | ## Contact 60 | 61 | Marcio Costa - [marcioc424@gmail.com](mailto:marcioc424@gmail.com) 62 | 63 | | | | | 64 | | --- | --- | --- | 65 | 66 | ## License 67 | 68 | This project is under the MIT license. 69 | 70 | > Developed by [Marcio Costa](https://www.linkedin.com/in/marcio-costa-03131a149/). 71 |
72 | -------------------------------------------------------------------------------- /docs/icon-gitpages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/docs/icon-gitpages.png -------------------------------------------------------------------------------- /docs/icon-reactjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/docs/icon-reactjs.png -------------------------------------------------------------------------------- /docs/icon-tmdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/docs/icon-tmdb.png -------------------------------------------------------------------------------- /docs/logo-cdr.cdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/docs/logo-cdr.cdr -------------------------------------------------------------------------------- /docs/logo-netflix-clone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/docs/logo-netflix-clone.png -------------------------------------------------------------------------------- /docs/netflix-gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/docs/netflix-gif.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "netflix-clone-reactjs", 3 | "homepage": "https://mcosta21.github.io/netflix-clone-reactjs", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@material-ui/core": "^4.11.0", 8 | "@material-ui/icons": "^4.9.1", 9 | "@testing-library/jest-dom": "^4.2.4", 10 | "@testing-library/react": "^9.3.2", 11 | "@testing-library/user-event": "^7.1.2", 12 | "react": "^16.13.1", 13 | "react-dom": "^16.13.1", 14 | "react-router-dom": "^5.2.0", 15 | "react-scripts": "3.4.3" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject", 22 | "predeploy": "npm run build", 23 | "deploy": "gh-pages -d build" 24 | }, 25 | "eslintConfig": { 26 | "extends": "react-app" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "devDependencies": { 41 | "gh-pages": "^2.0.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | Netflix Clone 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/Tmdb.js: -------------------------------------------------------------------------------- 1 | const API_KEY = 'COLOQUE SUA KEY AQUI'; 2 | const API_BASE = 'https://api.themoviedb.org/3'; 3 | 4 | /* 5 | - Originais da Netflix 6 | - Recomendados (Trending) 7 | - Em alta (Top rated) 8 | - Ação 9 | - Comédia 10 | - Terror 11 | - Romance 12 | - Documentários 13 | */ 14 | 15 | const basicFetch = async (endpoint) => { 16 | const req = await fetch(`${API_BASE}${endpoint}`); 17 | const json = await req.json(); 18 | return json; 19 | } 20 | 21 | export default { 22 | getHomeList: async () => { 23 | return [ 24 | { 25 | slug: 'originals', 26 | title: 'Originais do Netflix', 27 | type: 'tv', 28 | items: await basicFetch(`/discover/tv?with_network=213&language=pt-BR&api_key=${API_KEY}`) 29 | }, 30 | { 31 | slug: 'trending', 32 | title: 'Recomendados para você', 33 | type: 'tv', 34 | items: await basicFetch(`/trending/all/week?language=pt-BR&api_key=${API_KEY}`) 35 | }, 36 | { 37 | slug: 'toprated', 38 | title: 'Em alta', 39 | type: 'movie', 40 | items: await basicFetch(`/movie/top_rated?language=pt-BR&api_key=${API_KEY}`) 41 | }, 42 | { 43 | slug: 'action', 44 | title: 'Ação', 45 | type: 'movie', 46 | items: await basicFetch(`/discover/movie?with_genres=28&language=pt-BR&api_key=${API_KEY}`) 47 | }, 48 | { 49 | slug: 'comedy', 50 | title: 'Comédia', 51 | type: 'movie', 52 | items: await basicFetch(`/discover/movie?with_genres=35&language=pt-BR&api_key=${API_KEY}`) 53 | }, 54 | { 55 | slug: 'horror', 56 | title: 'Terror', 57 | type: 'movie', 58 | items: await basicFetch(`/discover/movie?with_genres=27&language=pt-BR&api_key=${API_KEY}`) 59 | }, 60 | { 61 | slug: 'romance', 62 | title: 'Romance', 63 | type: 'movie', 64 | items: await basicFetch(`/discover/movie?with_genres=10749&language=pt-BR&api_key=${API_KEY}`) 65 | }, 66 | { 67 | slug: 'documentary', 68 | title: 'Documentários', 69 | type: 'movie', 70 | items: await basicFetch(`/discover/movie?with_genres=99&language=pt-BR&api_key=${API_KEY}`) 71 | } 72 | ] 73 | }, 74 | getMovieInfo: async (movieId, type) => { 75 | let info = {}; 76 | 77 | if(movieId){ 78 | switch(type){ 79 | case 'movie': 80 | info = await basicFetch(`/movie/${movieId}?language=pt-BR&api_key=${API_KEY}`); 81 | break; 82 | case 'tv': 83 | info = await basicFetch(`/tv/${movieId}?language=pt-BR&api_key=${API_KEY}`); 84 | break; 85 | default: 86 | info = null; 87 | break; 88 | } 89 | } 90 | return info; 91 | }, 92 | getTrailerVideo: async (movieId, type) => { 93 | let trailer = {}; 94 | 95 | if(movieId){ 96 | switch(type){ 97 | case 'movie': 98 | trailer = await basicFetch(`/movie/${movieId}/videos?language=pt-BR&api_key=${API_KEY}`); 99 | break; 100 | case 'tv': 101 | trailer = await basicFetch(`/tv/${movieId}/videos?language=pt-BR&api_key=${API_KEY}`); 102 | break; 103 | default: 104 | trailer = null; 105 | break; 106 | } 107 | } 108 | return trailer; 109 | }, 110 | } -------------------------------------------------------------------------------- /src/assets/icon-amazon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/src/assets/icon-amazon.png -------------------------------------------------------------------------------- /src/assets/icon-netflix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcosta21/netflix-clone-reactjs/9968b59c1f943aa4bb95381128322fce446f0de1/src/assets/icon-netflix.png -------------------------------------------------------------------------------- /src/components/FeaturedMovie/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './styles.css'; 3 | import PlayArrowIcon from '@material-ui/icons/PlayArrow'; 4 | import AddIcon from '@material-ui/icons/Add'; 5 | 6 | function FeaturedMovie( { item } ) { 7 | 8 | let firstDate = new Date(item.first_air_date); 9 | let genres = []; 10 | for(let i in item.genres){ 11 | genres.push(item.genres[i].name); 12 | } 13 | let description = item.overview.length > 200 ? item.overview.substring(0, 200) + '...' : item.overview; 14 | 15 | return ( 16 |
24 |
25 |
26 |
{item.original_name}
27 | 28 |
29 |
{item.vote_average} pontos
30 |
{firstDate.getFullYear()}
31 |
{item.number_of_seasons} temperatura{item.number_of_seasons !== 1 ? 's' : ''}
32 |
33 | 34 |
{description}
35 |
36 |
Assitir
37 |
Minha Lista
38 |
39 |
Gêneros: {genres.join(', ')}
40 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default FeaturedMovie; 47 | -------------------------------------------------------------------------------- /src/components/FeaturedMovie/styles.css: -------------------------------------------------------------------------------- 1 | .featured { 2 | height: 100vh; 3 | } 4 | 5 | .featured--vertical { 6 | width: inherit; 7 | height: inherit; 8 | background: linear-gradient(to top, #111 10%, transparent 90%); 9 | } 10 | 11 | .featured--horizontal{ 12 | width: inherit; 13 | height: inherit; 14 | background: linear-gradient(to right, #111 30%, transparent 70%); 15 | display: flex; 16 | flex-direction: column; 17 | justify-content: center; 18 | padding-left: 40px; 19 | padding-bottom: 100px; 20 | padding-top: 70px; 21 | } 22 | 23 | .featured--name { 24 | font-size: 60px; 25 | font-weight: bold; 26 | } 27 | 28 | .featured--info { 29 | font-size: 18px; 30 | font-weight: bold; 31 | margin-top: 15px; 32 | } 33 | 34 | .featured--points { 35 | color: #46d369; 36 | } 37 | 38 | .featured--points, .featured--year, .featured--seasons { 39 | display: inline-block; 40 | margin-right: 15px; 41 | } 42 | 43 | .featured--description { 44 | margin-top: 15px; 45 | font-size: 20px; 46 | color: #999; 47 | max-width: 40%; 48 | } 49 | 50 | .featured--buttons { 51 | margin-top: 15px; 52 | } 53 | 54 | .featured--watchbutton, .featured--mylistbutton { 55 | display: inline-block; 56 | font-size: 20px; 57 | font-weight: bold; 58 | padding: 12px 25px; 59 | border-radius: 5px; 60 | text-decoration: none; 61 | margin-right: 10px; 62 | opacity: 1; 63 | transition: all ease 0.2s; 64 | cursor: pointer; 65 | } 66 | 67 | .featured--watchbutton:hover, .featured--mylistbutton:hover { 68 | opacity: 0.7; 69 | } 70 | 71 | .featured--watchbutton svg, .featured--mylistbutton svg { 72 | margin-right: 8px; 73 | } 74 | 75 | .featured--watchbutton div, .featured--mylistbutton div { 76 | display: flex; 77 | align-items: center; 78 | justify-content: center; 79 | } 80 | 81 | .featured--watchbutton{ 82 | background-color: #FFF; 83 | color: #000; 84 | } 85 | 86 | .featured--mylistbutton { 87 | background-color: #333; 88 | color: #FFF; 89 | } 90 | 91 | .featured--genres { 92 | margin-top: 15px; 93 | font-size: 18px; 94 | color: #999; 95 | } 96 | 97 | @media (max-width: 760px){ 98 | .featured { 99 | height: 90vh; 100 | } 101 | 102 | .featured--name { 103 | font-size: 40px; 104 | } 105 | 106 | .featured--info { 107 | font-size: 16px; 108 | } 109 | 110 | .featured--description { 111 | font-size: 14px; 112 | max-width: 100%; 113 | margin-right: 40px; 114 | } 115 | 116 | .featured--watchbutton, .featured--mylistbutton { 117 | font-size: 16px; 118 | } 119 | 120 | .featured--genres { 121 | font-size: 14px; 122 | } 123 | } -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './styles.css'; 3 | 4 | function Header( { black } ) { 5 | return ( 6 |
7 |
8 | 9 | Netflix 10 | 11 |
12 | Clone 13 |
14 |
15 |
16 | 17 | Usuário 18 | 19 |
20 |
21 | ); 22 | } 23 | 24 | export default Header; 25 | -------------------------------------------------------------------------------- /src/components/Header/styles.css: -------------------------------------------------------------------------------- 1 | 2 | header { 3 | position: fixed; 4 | z-index: 999; 5 | top: 0; 6 | left: 0; 7 | right: 0; 8 | height: 70px; 9 | display: flex; 10 | justify-content: space-between; 11 | align-items: center; 12 | padding: 0 40px; 13 | background: transparent; 14 | transition: all ease 0.5s; 15 | } 16 | 17 | header.black { 18 | background: #141414; 19 | } 20 | 21 | .header--logo { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | .header--logo div { 28 | margin-left: 10px; 29 | background: #FFF; 30 | color: #333; 31 | text-transform: uppercase; 32 | font-weight: 900; 33 | height: 25px; 34 | padding: 0 12px; 35 | display: flex; 36 | align-items: center; 37 | border-radius: 3px; 38 | margin-top: -3px; 39 | } 40 | 41 | .header--logo img { 42 | height: 25px; 43 | } 44 | 45 | .header--user { 46 | height: 35px; 47 | } 48 | 49 | .header--user img { 50 | height: 100%; 51 | border-radius: 3px; 52 | } -------------------------------------------------------------------------------- /src/components/MovieRow/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import './styles.css'; 3 | import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore'; 4 | import NavigateNextIcon from '@material-ui/icons/NavigateNext'; 5 | import { Link } from 'react-router-dom'; 6 | 7 | function MovieRow( { title, items, type } ) { 8 | 9 | const [scrollX, setScrollX] = useState(-400); 10 | 11 | const handleLeftArrow = () => { 12 | let x = scrollX + Math.round(window.innerWidth / 2); 13 | if(x > 0){ 14 | x = 0; 15 | } 16 | setScrollX(x); 17 | } 18 | 19 | const handleRightArrow = () => { 20 | let x = scrollX - Math.round(window.innerWidth / 2); 21 | let listW = items.results.length * 200; 22 | if((window.innerWidth - listW) > x){ 23 | x = (window.innerWidth - listW) - 80; 24 | } 25 | setScrollX(x); 26 | } 27 | 28 | return ( 29 |
30 |

{title}

31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 | 40 |
41 |
48 | { 49 | items.results.length > 0 && 50 | items.results.map((item, key) => ( 51 | 52 | item.poster_path !== null && 53 |
54 | 55 | {item.original_title} 56 | 57 |
58 | )) 59 | } 60 |
61 |
62 |
63 | ); 64 | } 65 | 66 | export default MovieRow; 67 | -------------------------------------------------------------------------------- /src/components/MovieRow/styles.css: -------------------------------------------------------------------------------- 1 | 2 | .movieRow { 3 | margin-bottom: 40px; 4 | } 5 | 6 | .movieRow h2 { 7 | margin: 0 0 10px 50px; 8 | } 9 | 10 | .movieRow--listarea { 11 | overflow-x: hidden; 12 | margin-left: 40px; 13 | } 14 | 15 | .movieRow--list { 16 | transition: all ease 0.5s; 17 | } 18 | 19 | .movieRow--item { 20 | display: inline-block; 21 | width: 200px; 22 | cursor: pointer; 23 | } 24 | 25 | .movieRow--item img { 26 | width: 100%; 27 | transform: scale(0.9); 28 | border-radius: 4px; 29 | transition: all ease 0.2s; 30 | } 31 | 32 | .movieRow--item img:hover { 33 | transform: scale(1); 34 | } 35 | 36 | .movieRow--left, .movieRow--right { 37 | position: absolute; 38 | width: 40px; 39 | height: 300px; 40 | background-color: rgba(0, 0, 0, 0.6); 41 | z-index: 99; 42 | display: flex; 43 | align-items: center; 44 | justify-content: center; 45 | overflow: hidden; 46 | cursor: pointer; 47 | opacity: 0; 48 | transition: all ease .5s; 49 | } 50 | 51 | .movieRow--left { 52 | left: 0; 53 | } 54 | 55 | .movieRow--right { 56 | right: 0; 57 | } 58 | 59 | .movieRow:hover .movieRow--left, .movieRow:hover .movieRow--right { 60 | opacity: 1; 61 | } 62 | 63 | @media (max-width: 760px){ 64 | .movieRow--left, .movieRow--right { 65 | opacity: 1; 66 | } 67 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Screen from './screen';; 4 | 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /src/pages/Details/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import Tmdb from '../../Tmdb'; 3 | import { useParams } from 'react-router'; 4 | import TheatersIcon from '@material-ui/icons/Theaters'; 5 | import LanguageIcon from '@material-ui/icons/Language'; 6 | import AspectRatioIcon from '@material-ui/icons/AspectRatio'; 7 | import iconAmazon from '../../assets/icon-amazon.png'; 8 | import iconNetflix from '../../assets/icon-netflix.png'; 9 | import './styles.css'; 10 | import { Link } from 'react-router-dom'; 11 | 12 | function Details(){ 13 | const { id, type } = useParams(); 14 | 15 | const [movieDetails, setMovieDetails] = useState({}); 16 | const [trailerVideo, setTrailerVideo] = useState([]); 17 | const [urlVideo, setUrlVideo] = useState(); 18 | const [videoFullScreen, setVideoFullScreen] = useState(false); 19 | const [descriptionVideo, setDescriptionVideo] = useState(); 20 | 21 | useEffect(() => { 22 | const loadAll = async () => { 23 | let movie = await Tmdb.getMovieInfo(id, type); 24 | let trailer = await Tmdb.getTrailerVideo(id, type) 25 | setMovieDetails(movie); 26 | setTrailerVideo(trailer); 27 | setDescriptionVideo(movie.overview.length > 120 ? movie.overview.substring(0, 120) + '...' : movie.overview); 28 | //console.log(movie) 29 | } 30 | loadAll(); 31 | }, [id, type]) 32 | 33 | function handleShowTrailer(){ 34 | const trailer = trailerVideo.results; 35 | if(trailer !== undefined && trailer.length > 0){ 36 | const url = `https://youtube.com/embed/${trailer[0].key}?autoplay=1&controls=0&showinfo=0&autohide=1`; 37 | setUrlVideo(url); 38 | } 39 | } 40 | 41 | function handleVideoFullScreen(){ 42 | setVideoFullScreen(!videoFullScreen); 43 | } 44 | 45 | 46 | return ( 47 |
55 | Voltar 56 |
57 |
58 |
59 |

5 ? 'positive' : 'negative'}>{movieDetails.vote_average * 10 + '%'}

60 |
61 | 62 |

{movieDetails.original_title || movieDetails.original_name}

63 | 64 |

{descriptionVideo}

65 | 66 | { 67 | (trailerVideo.results !== undefined && trailerVideo.results.length !== 0) 68 | && 69 | handleShowTrailer()} className="details--viewtrailer">
Assistir trailer
70 | } 71 | { 72 | (movieDetails.homepage !== undefined && movieDetails.homepage !== '') && 73 | 74 |
75 | { 76 | movieDetails.homepage.includes('netflix') ? 77 | Netflix : 78 | movieDetails.homepage.includes('amazon') ? 79 | Amazon : 80 | 81 | } 82 | 83 |
84 |
85 | } 86 |
87 |
88 | { 89 | urlVideo !== undefined 90 | && 91 | 99 | } 100 |
101 | ) 102 | } 103 | 104 | export default Details; -------------------------------------------------------------------------------- /src/pages/Details/styles.css: -------------------------------------------------------------------------------- 1 | .details { 2 | height: 100vh; 3 | width: 100vw; 4 | } 5 | 6 | .details section { 7 | width: inherit; 8 | height: inherit; 9 | background: linear-gradient(to top, #111, transparent ); 10 | display: flex; 11 | align-items: flex-end; 12 | justify-content: flex-start; 13 | padding: 90px; 14 | } 15 | 16 | .details section div { 17 | text-align: left; 18 | } 19 | 20 | .details section div h1 { 21 | font-size: 42pt; 22 | } 23 | 24 | .details section div h4 { 25 | max-width: 50%; 26 | line-height: 28px; 27 | font-weight: 500; 28 | } 29 | 30 | .details--info { 31 | margin-bottom: 30px; 32 | } 33 | 34 | .details--info h3 { 35 | width: 50px; 36 | padding: 10px; 37 | border-radius: 4px; 38 | } 39 | 40 | .details--info h3.positive{ 41 | background: #46d369; 42 | } 43 | 44 | .details--info h3.negative{ 45 | background: #d91921; 46 | } 47 | 48 | .details a { 49 | padding: 12px 20px; 50 | display: inline-block; 51 | font-size: 20px; 52 | font-weight: bold; 53 | border-radius: 5px; 54 | text-decoration: none; 55 | margin-right: 10px; 56 | opacity: 1; 57 | transition: all ease 0.2s; 58 | cursor: pointer; 59 | } 60 | 61 | .details a div { 62 | display: flex; 63 | align-items: center; 64 | justify-content: center; 65 | } 66 | 67 | .details a:hover { 68 | opacity: 0.7; 69 | } 70 | 71 | .details--viewtrailer { 72 | background-color: #FFF; 73 | color: #000; 74 | } 75 | 76 | .details--viewtrailer svg { 77 | margin-right: 8px; 78 | } 79 | 80 | .details--officialsite { 81 | background-color: #FFF; 82 | color: #000; 83 | } 84 | 85 | .details aside{ 86 | width: 500px; 87 | height: 300px; 88 | position: fixed; 89 | bottom: 15%; 90 | right: 5%; 91 | } 92 | 93 | .details aside div { 94 | display: flex; 95 | justify-content: flex-end; 96 | align-items: center; 97 | padding: 10px 6px; 98 | } 99 | 100 | button { 101 | background: transparent; 102 | border: none; 103 | color: #FFF; 104 | cursor: pointer; 105 | } 106 | 107 | .video--fullscreen iframe{ 108 | width: 100vw; 109 | height: 100vh; 110 | left: 0; 111 | position: fixed; 112 | top: 0; 113 | z-index: 0 114 | } 115 | 116 | .video--fullscreen div { 117 | justify-content: center; 118 | z-index: 1; 119 | position: fixed; 120 | left: 0; 121 | border-radius: 0px 4px 4px 0px; 122 | background: #FFF; 123 | 124 | } 125 | 126 | .video--fullscreen div svg { 127 | color: #000; 128 | margin-top: 5px; 129 | } 130 | 131 | .details--backbutton { 132 | color: #FFF; 133 | position: fixed; 134 | top: 20px; 135 | left: 20px; 136 | font-size: 16px; 137 | text-shadow: 1px 1px 5px #999; 138 | } -------------------------------------------------------------------------------- /src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import Tmdb from '../../Tmdb'; 3 | import MovieRow from '../../components/MovieRow'; 4 | import FeaturedMovie from '../../components/FeaturedMovie'; 5 | import Header from '../../components/Header'; 6 | 7 | import './styles.css'; 8 | 9 | function Home() { 10 | 11 | const [featuredData, setFeaturedData] = useState(null); 12 | const [movieList, setMovieList] = useState([]); 13 | const [blackHeader, setBlackHeader] = useState(false); 14 | 15 | useEffect(() => { 16 | const loadAll = async () => { 17 | let list = await Tmdb.getHomeList(); 18 | setMovieList(list); 19 | 20 | let originals = list.filter(i => i.slug === 'originals'); 21 | let randomChosen = Math.floor(Math.random() * (originals[0].items.results.length - 1)); 22 | let movieChosen = originals[0].items.results[randomChosen]; 23 | 24 | let movieChosenData = await Tmdb.getMovieInfo(movieChosen.id, 'tv'); 25 | setFeaturedData(movieChosenData); 26 | } 27 | 28 | loadAll(); 29 | }, []); 30 | 31 | useEffect(() => { 32 | const scrollListener = () => { 33 | if(window.scrollY > 10) { 34 | setBlackHeader(true); 35 | } 36 | else { 37 | setBlackHeader(false); 38 | } 39 | } 40 | 41 | window.addEventListener('scroll', scrollListener); 42 | 43 | return () => { 44 | window.removeEventListener('scroll', scrollListener); 45 | } 46 | 47 | }, []); 48 | 49 | return ( 50 |
51 | 52 |
53 | 54 | { 55 | featuredData && 56 | 57 | } 58 | 59 | 60 |
61 | { 62 | movieList.map((item, key) => ( 63 | 64 | )) 65 | } 66 |
67 | 68 | 79 | 80 | { 81 | movieList.length <= 0 && 82 |
83 | Carregando 84 |
85 | } 86 | 87 |
88 | ); 89 | } 90 | 91 | export default Home; 92 | -------------------------------------------------------------------------------- /src/pages/Home/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;900&display=swap'); 2 | * { 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | background-color: #111; 8 | color: #FFF; 9 | margin: 0; 10 | font-family: 'Roboto', sans-serif; 11 | } 12 | 13 | .lists { 14 | margin-top: -100px; 15 | } 16 | 17 | footer { 18 | margin: 50px 0; 19 | text-align: center; 20 | } 21 | 22 | footer div { 23 | margin-bottom: 10px; 24 | } 25 | 26 | footer div img { 27 | margin: 10px; 28 | } 29 | 30 | footer a { 31 | text-decoration: none; 32 | color: #FFF; 33 | font-weight: bold; 34 | } 35 | 36 | .loading{ 37 | position: fixed; 38 | left: 0; 39 | right: 0; 40 | top: 0; 41 | bottom: 0; 42 | z-index: 99; 43 | background-color: #000; 44 | display: flex; 45 | justify-content: center; 46 | align-items: center; 47 | } 48 | 49 | .loading img { 50 | width: 700px 51 | } -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {BrowserRouter, Switch, Route} from 'react-router-dom'; 3 | 4 | import Home from './pages/Home'; 5 | import Details from './pages/Details'; 6 | 7 | function Routes(){ 8 | return( 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | export default Routes; -------------------------------------------------------------------------------- /src/screen/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Routes from '../routes'; 3 | 4 | function Screen() { 5 | return ( 6 | 7 | ); 8 | } 9 | 10 | export default Screen; 11 | --------------------------------------------------------------------------------