├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Netflix homepage clone using React JS.
11 |
12 |
13 |
14 |
15 |
16 | ## Technologies
17 | Basically, this project was developed based on the following technologies:
18 |
19 |
20 |
21 |   
22 |
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 |
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 |
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 |
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 |
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 |
88 | {
89 | urlVideo !== undefined
90 | &&
91 |
92 |
93 |
handleVideoFullScreen()}>
94 |
95 |
98 |
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 |
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 |
--------------------------------------------------------------------------------