updated branch
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
client/dist/
|
|
||||||
node_modules/
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"commonjs": true,
|
|
||||||
"es6": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"react",
|
|
||||||
"react-hooks"
|
|
||||||
],
|
|
||||||
"extends": ["eslint:recommended", "plugin:react/recommended", "plugin:prettier/recommended"],
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module",
|
|
||||||
"ecmaVersion": 2018
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"react": {
|
|
||||||
"version": "detect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"linebreak-style": ["error", "unix"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
index.html,1596823315374,294686b5824777966e74b788a74f613225bd2a74f6e7eca9b7b98b3bd6e4ae60
|
bundle.js.LICENSE.txt,1633809037651,e6867f9fa01d54ddc1cf8eac25d56d3ae909da438be77aa5454f85532e879796
|
||||||
main.css,1596823315373,1fe11d275cdaeefa9ac00b547cc17a7559f641d94302b061941c58441778dc90
|
index.html,1633809037654,b96adf5936968f349bea11263a42d495b541d0bdac04424944a25ffd5253c4c4
|
||||||
static/images/add.svg,1596823315374,ef7cf8e9df96200815d767e6b3b767ef092dd338b4851d2f35966911dab09e4c
|
static/images/add.svg,1633809037652,ef7cf8e9df96200815d767e6b3b767ef092dd338b4851d2f35966911dab09e4c
|
||||||
static/images/bell-logo.svg,1596823315374,5ef07d3b72a99ccebfff2fb9bc1a80513d59376038055b606deff8133c3e3b56
|
static/images/bell.svg,1633809037652,6766dfa1ef7da3acfab70cdb2bbee5e6861fc0cdf8afbaf1cf7c387e4df415a4
|
||||||
static/images/bell.svg,1596823315375,6766dfa1ef7da3acfab70cdb2bbee5e6861fc0cdf8afbaf1cf7c387e4df415a4
|
static/images/bell-logo.svg,1633809037652,5ef07d3b72a99ccebfff2fb9bc1a80513d59376038055b606deff8133c3e3b56
|
||||||
static/images/cancel-music.svg,1596823315375,1bbd7eef0b56c5d4cec9993f3762a384447ca99392778a87292333b82f8a2f39
|
static/images/cancel-music.svg,1633809037652,1bbd7eef0b56c5d4cec9993f3762a384447ca99392778a87292333b82f8a2f39
|
||||||
static/images/drop-down-arrow.svg,1596823315374,7ebe79b34f9fa2590b4beb29e6e6e8d0b0fff722ff0e3071ad36d3467c045743
|
main.css,1633809037650,d5d7ec1073fd2f17b3fed17b0deaa04cb7678f4b8ea9b089a7cc336886753973
|
||||||
static/images/play-button.svg,1596823315375,d43cf31799ae2e480227ae79d858b997dfb0b54f67b1d3a3a1c18ca1dd927251
|
static/images/mute.svg,1633809037652,dbf382f4fa81759d125e5a526fab4464ba293c4a0dedfba395bcb64e5191ffbb
|
||||||
static/images/search-icon.svg,1596823315375,fcad2b6afe31083f5b452f601b1ca8669f6f768deecadf07e9d647269296012a
|
static/images/play-button.svg,1633809037652,d43cf31799ae2e480227ae79d858b997dfb0b54f67b1d3a3a1c18ca1dd927251
|
||||||
8562b6565f5ae1db5e4af40d85b4ed2d.png,1596823315373,ddacf16aa0704b6ff5ed27a0ff5416eff337a05915beb9c10504639f2f105ec1
|
static/images/search-icon.svg,1633809037652,fcad2b6afe31083f5b452f601b1ca8669f6f768deecadf07e9d647269296012a
|
||||||
static/images/Netflix_Logo_RGB.png,1596823315375,ddacf16aa0704b6ff5ed27a0ff5416eff337a05915beb9c10504639f2f105ec1
|
static/images/unmute.svg,1633809037652,f571ee9569a235ed5102856c1a1485ce0d1f22a739f291fe02f5d5c893ddd840
|
||||||
bundle.js,1596823315373,033e3afff30e48c0cc6b812ab7b48ad066cbc65e6442c1f10109e6a55a0e103b
|
static/images/drop-down-arrow.svg,1633809037652,7ebe79b34f9fa2590b4beb29e6e6e8d0b0fff722ff0e3071ad36d3467c045743
|
||||||
static/images/header-bg.jpg,1596823315376,f4cfcb4caa304a777e03d623c68bf00d22f9c21a55d0a6fb088889e3c2867d40
|
f4070143e1f521da82a119eb78b434d0.png,1633809037650,ddacf16aa0704b6ff5ed27a0ff5416eff337a05915beb9c10504639f2f105ec1
|
||||||
|
static/images/Netflix_Logo_RGB.png,1633809037654,ddacf16aa0704b6ff5ed27a0ff5416eff337a05915beb9c10504639f2f105ec1
|
||||||
|
bundle.js,1633809037651,1cbe274a30fafac6296c7ee44e3807e810c050dbd67f8a35da802bde26f80c4e
|
||||||
|
static/images/header-bg.jpg,1633809037653,f4cfcb4caa304a777e03d623c68bf00d22f9c21a55d0a6fb088889e3c2867d40
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -12,4 +12,7 @@ logs
|
|||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
|
||||||
.firebase
|
# firebase
|
||||||
|
.firebase
|
||||||
|
.firebaserc
|
||||||
|
.firebase.json
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
package-lock.json
|
|
||||||
.next
|
|
||||||
node_modules/
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
||||||
23
README.md
23
README.md
@@ -1,11 +1,12 @@
|
|||||||
# Netflix Clone
|
# Netflix Clone
|
||||||
|
|
||||||
- Demo: http://netflix-clone-react.surge.sh/
|
- Demo: <https://netflix-clone-9a0b9.firebaseapp.com/>
|
||||||
|
|
||||||
This project is a simplified front end clone of Netflix. It was created with React and CSS (Grid and Flexbox). It uses [The MovieDB Api](https://www.themoviedb.org/documentation/api) to search for movies and display details. Feel free to contribute!
|
This project is a simplified front end clone of Netflix. It was created with React and CSS (Grid and Flexbox). It uses [The MovieDB Api](https://www.themoviedb.org/documentation/api) to search for movies and display details. Feel free to contribute!
|
||||||
|
|
||||||
### Tools used:
|
### Tools used
|
||||||
- Webpack
|
|
||||||
|
- Webpack
|
||||||
- Axios
|
- Axios
|
||||||
- Redux & React
|
- Redux & React
|
||||||
- Sass (grid & flexbox)
|
- Sass (grid & flexbox)
|
||||||
@@ -13,24 +14,22 @@ This project is a simplified front end clone of Netflix. It was created with Rea
|
|||||||
- Swiper JS
|
- Swiper JS
|
||||||
|
|
||||||
### Runing Project Locally
|
### Runing Project Locally
|
||||||
|
|
||||||
- Install dependencies: run `npm install` in root project
|
- Install dependencies: run `npm install` in root project
|
||||||
- Get API key from [here](https://www.themoviedb.org/documentation/api)
|
- Get API key from [here](https://www.themoviedb.org/documentation/api)
|
||||||
- Create .env file in root project and add: `API_KEY=YOUR_API_KEY_HERE`
|
- Create .env file in root project and add: `API_KEY=YOUR_API_KEY_HERE`
|
||||||
- Run project: `npm run dev`
|
- Run project: `npm run dev`
|
||||||
|
|
||||||
### User Stories:
|
### User Stories
|
||||||
|
|
||||||
- User can search for movies and TV shows on TMDb
|
- User can search for movies and TV shows on TMDb
|
||||||
- User can the see upcoming and trending movies. Data updates weekly
|
- User can the see upcoming and trending movies. Data updates weekly
|
||||||
- User can click on a movie and a modal should pop up. It should display the title, release date, overview, and runtime.
|
- User can click on a movie and a modal should pop up. It should display the title, release date, overview, and runtime.
|
||||||
- The webpage adapts to any screen size.
|
- The webpage adapts to any screen size.
|
||||||
|
|
||||||
|
### Video Walktrough
|
||||||
|
|
||||||
|
|
||||||
### Video Walktrough
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Please feel free to create a pull request and submit any issues!
|
Please feel free to create a pull request and submit any issues!
|
||||||
Currently looking for backend developers. If you would to contribute to support a backend, reach out, all ideas are welcomed!
|
Currently looking for backend developers. If you would to contribute to support a backend, reach out, all ideas are welcomed!
|
||||||
|
|||||||
34501
package-lock.json
generated
34501
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
95
package.json
95
package.json
@@ -5,70 +5,71 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack-dev-server --mode development --config webpack.config.js --open",
|
"dev": "webpack-dev-server --mode development --config webpack.config.js --open",
|
||||||
"build": "webpack -p",
|
"build": "webpack --mode production",
|
||||||
"lint": "eslint --fix . && echo 'Lint complete.'"
|
"lint": "eslint --fix . && echo 'Lint complete.'"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "10.1.0",
|
"node": "v14.15.4",
|
||||||
"npm": "6.4.1",
|
"npm": "7.24.0",
|
||||||
"watch": "watch 'clear && npm run -s test | tap-nirvana && npm run -s lint' src"
|
"watch": "watch 'clear && npm run -s test | tap-nirvana && npm run -s lint' src"
|
||||||
},
|
},
|
||||||
"author": "Andres Alcocer",
|
"author": "Andres Alcocer",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.18.1",
|
"axios": "^0.22.0",
|
||||||
"lodash": "^4.17.19",
|
"firebase": "^9.1.2",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^16.13.1",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^17.0.2",
|
||||||
"react-redux": "^6.0.1",
|
"react-player": "^2.9.0",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-redux": "^7.2.5",
|
||||||
"redux": "^4.0.1",
|
"react-router-dom": "^5.3.0",
|
||||||
|
"redux": "^4.1.1",
|
||||||
"redux-promise": "^0.6.0",
|
"redux-promise": "^0.6.0",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"swiper": "^6.1.1"
|
"swiper": "^7.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.1.5",
|
"@babel/core": "^7.15.5",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
"@babel/plugin-proposal-export-namespace-from": "^7.14.5",
|
||||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
"@babel/plugin-proposal-throw-expressions": "^7.14.5",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/polyfill": "^7.0.0-beta.51",
|
"@babel/polyfill": "^7.0.0-beta.51",
|
||||||
"@babel/preset-env": "^7.1.5",
|
"@babel/preset-env": "^7.15.6",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.14.5",
|
||||||
"babel-loader": "^8.0.4",
|
"babel-loader": "^8.2.2",
|
||||||
"clean-webpack-plugin": "^1.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^4.6.0",
|
"copy-webpack-plugin": "^9.0.1",
|
||||||
"css-loader": "^1.0.1",
|
"css-loader": "^6.3.0",
|
||||||
"dotenv": "^6.2.0",
|
"dotenv": "^10.0.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-airbnb": "^18.1.0",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.24.2",
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-react": "^7.20.0",
|
"eslint-plugin-react": "^7.26.1",
|
||||||
"eslint-plugin-react-hooks": "^2.5.1",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^2.0.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-loader": "^0.5.5",
|
"html-loader": "^2.1.2",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"image-webpack-loader": "^4.5.0",
|
"image-webpack-loader": "^8.0.1",
|
||||||
"mini-css-extract-plugin": "^0.4.4",
|
"mini-css-extract-plugin": "^2.4.1",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^6.0.1",
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
"optimize-css-assets-webpack-plugin": "^6.0.1",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.4.1",
|
||||||
"react-owl-carousel2": "^0.3.0",
|
"react-owl-carousel2": "^0.3.0",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^12.1.0",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^3.3.0",
|
||||||
"svg-inline-loader": "^0.8.0",
|
"svg-inline-loader": "^0.8.2",
|
||||||
"svg-react-loader": "^0.4.6",
|
"svg-react-loader": "^0.4.6",
|
||||||
"tap-nirvana": "^1.1.0",
|
"tap-nirvana": "^1.1.0",
|
||||||
"uglifyjs-webpack-plugin": "^2.0.1",
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||||
"watch": "^1.0.2",
|
"watch": "^1.0.2",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^5.57.1",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^4.8.0",
|
||||||
"webpack-dev-server": "^3.11.0"
|
"webpack-dev-server": "^4.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/AppRouter.js
Normal file
23
src/AppRouter.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
|
||||||
|
|
||||||
|
import Home from './pages/Home'
|
||||||
|
import NotFound from './pages/NotFound'
|
||||||
|
import Search from './pages/Search'
|
||||||
|
import Navbar from './components/Navbar'
|
||||||
|
import Footer from './components/Footer'
|
||||||
|
|
||||||
|
const AppRouter = () => (
|
||||||
|
<BrowserRouter>
|
||||||
|
<Navbar />
|
||||||
|
<Switch>
|
||||||
|
<Route path='/' exact render={() => <Redirect to='/browse' />} />
|
||||||
|
<Route path='/browse' component={Home} />
|
||||||
|
<Route path='/search' component={Search} />
|
||||||
|
<Route component={NotFound} />
|
||||||
|
</Switch>
|
||||||
|
<Footer />
|
||||||
|
</BrowserRouter>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default AppRouter
|
||||||
79
src/components/DisplayMovieRow.js
Normal file
79
src/components/DisplayMovieRow.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||||
|
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper'
|
||||||
|
|
||||||
|
import { useViewport } from '../hooks/useViewport'
|
||||||
|
|
||||||
|
// install Swiper components
|
||||||
|
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y])
|
||||||
|
|
||||||
|
const DisplayMovieRow = ({ title, isNetflixMovies, movies, selectMovieHandler }) => {
|
||||||
|
const [windowDimensions] = useViewport()
|
||||||
|
const { width } = windowDimensions
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className='movieShowcase__heading'>{title}</h1>
|
||||||
|
<Swiper
|
||||||
|
className='movieShowcase__container'
|
||||||
|
navigation={true}
|
||||||
|
grabCursor={false}
|
||||||
|
draggable={false}
|
||||||
|
loop={true}
|
||||||
|
loopAdditionalSlides={
|
||||||
|
width >= 1378 ? 4 : width >= 998 ? 3 : width >= 625 ? 2 : 2
|
||||||
|
}
|
||||||
|
breakpoints={{
|
||||||
|
1378: {
|
||||||
|
slidesPerView: 5,
|
||||||
|
slidesPerGroup: 5,
|
||||||
|
},
|
||||||
|
998: {
|
||||||
|
slidesPerView: 4,
|
||||||
|
slidesPerGroup: 4,
|
||||||
|
},
|
||||||
|
625: {
|
||||||
|
slidesPerView: 3,
|
||||||
|
slidesPerGroup: 3,
|
||||||
|
},
|
||||||
|
0: {
|
||||||
|
slidesPerView: 2,
|
||||||
|
slidesPerGroup: 2,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
preventClicksPropagation={true}
|
||||||
|
preventClicks={true}
|
||||||
|
scrollbar={{ draggable: false, hide: true }}
|
||||||
|
slideToClickedSlide={false}
|
||||||
|
pagination={{ clickable: true }}
|
||||||
|
>
|
||||||
|
{movies &&
|
||||||
|
movies.map((movie, idx) => {
|
||||||
|
let movieImageUrl = isNetflixMovies
|
||||||
|
? `https://image.tmdb.org/t/p/original/${movie.poster_path}`
|
||||||
|
: `https://image.tmdb.org/t/p/w500/${movie.backdrop_path}`
|
||||||
|
|
||||||
|
if (movie.poster_path && movie.backdrop_path !== null) {
|
||||||
|
return (
|
||||||
|
<SwiperSlide
|
||||||
|
onClick={() => selectMovieHandler(movie)}
|
||||||
|
key={idx}
|
||||||
|
className={
|
||||||
|
'movieShowcase__container--movie' +
|
||||||
|
(isNetflixMovies ? '__netflix' : '')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={movieImageUrl}
|
||||||
|
className='movieShowcase__container--movie-image'
|
||||||
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Swiper>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DisplayMovieRow
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
|
|
||||||
const footer = () => (
|
const footer = () => (
|
||||||
<footer className="footer">
|
<footer className='footer'>
|
||||||
<div className="footer__copyright">
|
<div className='footer__copyright'>
|
||||||
© 2018 Made with ❤️ by{' '}
|
© 2021 Made with ❤️ by{' '}
|
||||||
<a className="footer__copyright--link" href="http://andresio.com">
|
<a className='footer__copyright--link' href='https://github.com/AndresXI'>
|
||||||
{' '}
|
{' '}
|
||||||
Andres Alcocer
|
Andres Alcocer
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
)
|
||||||
|
|
||||||
export default footer;
|
export default footer
|
||||||
|
|||||||
@@ -1,34 +1,54 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import PlayLogo from '../static/images/play-button.svg';
|
import PlayLogo from '../static/images/play-button.svg'
|
||||||
import AddLogo from '../static/images/add.svg';
|
import AddLogo from '../static/images/add.svg'
|
||||||
|
import MuteIcon from '../static/images/mute.svg'
|
||||||
|
import UnmuteIcon from '../static/images/unmute.svg'
|
||||||
|
import ReactPlayer from 'react-player'
|
||||||
|
|
||||||
export default function Header(props) {
|
const Header = ({ movie: { name, overview } }) => {
|
||||||
const backgroundStyle = {
|
const [isMuted, setIsMuted] = useState(true)
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${props.movie.backdrop_path})`,
|
|
||||||
backgroundPosition: 'center',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header style={backgroundStyle} className="header">
|
<header className='header'>
|
||||||
<div className="header__container">
|
<ReactPlayer
|
||||||
<h1 className="header__container-heading">{props.movie.name}</h1>
|
playing={true}
|
||||||
<button
|
loop={true}
|
||||||
onClick={() => alert('not a movie!')}
|
width='100%'
|
||||||
className="header__container-btnPlay"
|
height='100%'
|
||||||
>
|
volume={1}
|
||||||
<PlayLogo className="header__container-btnMyList-play" />
|
muted={isMuted}
|
||||||
Play
|
className='header__video'
|
||||||
</button>
|
url='https://vimeo.com/384025132'
|
||||||
|
/>
|
||||||
|
<h1 className='header__container-heading'>{name}</h1>
|
||||||
|
<button
|
||||||
|
onClick={() => alert('not a movie!')}
|
||||||
|
className='header__container-btnPlay'
|
||||||
|
>
|
||||||
|
<PlayLogo className='header__container-btnMyList-play' />
|
||||||
|
Play
|
||||||
|
</button>
|
||||||
|
<button className='header__container-btnMyList'>
|
||||||
|
<AddLogo className='header__container-btnMyList-add' />
|
||||||
|
My List
|
||||||
|
</button>
|
||||||
|
|
||||||
<button className="header__container-btnMyList">
|
{isMuted ? (
|
||||||
<AddLogo className="header__container-btnMyList-add" />
|
<MuteIcon
|
||||||
My List
|
onClick={() => setIsMuted(false)}
|
||||||
</button>
|
className='header__container-btnVolume'
|
||||||
<p className="header__container-overview">{props.movie.overview}</p>
|
/>
|
||||||
</div>
|
) : (
|
||||||
<div className="header--fadeBottom"></div>
|
<UnmuteIcon
|
||||||
|
onClick={() => setIsMuted(true)}
|
||||||
|
className='header__container-btnVolume'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<p className='header__container-overview'>{overview}</p>
|
||||||
|
<div className='header__container--fadeBottom'></div>
|
||||||
</header>
|
</header>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
|
|||||||
83
src/components/MainContent.js
Normal file
83
src/components/MainContent.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
import * as movieActions from '../store/actions'
|
||||||
|
|
||||||
|
import Header from './Header'
|
||||||
|
import DisplayMovieRow from './DisplayMovieRow'
|
||||||
|
|
||||||
|
const MainContent = ({ selectMovieHandler }) => {
|
||||||
|
const { movieDetails } = useSelector((state) => state.movieDetails)
|
||||||
|
const netflixOriginals = useSelector((state) => state.netflixOriginals)
|
||||||
|
const trending = useSelector((state) => state.trending)
|
||||||
|
const topRated = useSelector((state) => state.topRated)
|
||||||
|
const actionMovies = useSelector((state) => state.action)
|
||||||
|
const comedyMovies = useSelector((state) => state.comedy)
|
||||||
|
const horrorMovies = useSelector((state) => state.horror)
|
||||||
|
const romanceMovies = useSelector((state) => state.romance)
|
||||||
|
const documentaries = useSelector((state) => state.documentary)
|
||||||
|
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(movieActions.fetchMovieDetails('tv', '63351'))
|
||||||
|
dispatch(movieActions.fetchNetflixOriginals())
|
||||||
|
dispatch(movieActions.fetchTrending())
|
||||||
|
dispatch(movieActions.fetchTopRated())
|
||||||
|
dispatch(movieActions.fetchActionMovies())
|
||||||
|
dispatch(movieActions.fetchComedyMovies())
|
||||||
|
dispatch(movieActions.fetchHorrorMovies())
|
||||||
|
dispatch(movieActions.fetchRomanceMovies())
|
||||||
|
dispatch(movieActions.fetchDocumentaries())
|
||||||
|
}, [dispatch])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='container'>
|
||||||
|
<Header movie={movieDetails} />
|
||||||
|
<div className='movieShowcase'>
|
||||||
|
<DisplayMovieRow
|
||||||
|
isNetflixMovies={true}
|
||||||
|
title='Netflix Originals'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={netflixOriginals.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Trending'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={trending.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Top Rated'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={topRated.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Action Movies'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={actionMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Comedy'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={comedyMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Horror Movies'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={horrorMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Romance'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={romanceMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Documentaries'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={documentaries.data}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainContent
|
||||||
@@ -1,37 +1,45 @@
|
|||||||
import React from 'react';
|
|
||||||
import Aux from '../hoc/Aux';
|
|
||||||
import AddIcon from '../static/images/add.svg';
|
|
||||||
import PlayIcon from '../static/images/play-button.svg';
|
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
export default function ModalMovieDetails(props) {
|
import AddIcon from '../static/images/add.svg'
|
||||||
|
import PlayIcon from '../static/images/play-button.svg'
|
||||||
|
|
||||||
|
const MovieDetails = ({
|
||||||
|
movie: {
|
||||||
|
title,
|
||||||
|
name,
|
||||||
|
vote_average,
|
||||||
|
release_date,
|
||||||
|
first_air_date,
|
||||||
|
runtime,
|
||||||
|
episode_run_time,
|
||||||
|
number_of_episodes,
|
||||||
|
number_of_seasons,
|
||||||
|
overview,
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Aux>
|
<div className='modal__container'>
|
||||||
<div className="modal__container">
|
<h1 className='modal__title'>{title || name}</h1>
|
||||||
<h1 className="modal__title">
|
<p className='modal__info'>
|
||||||
{props.movie.title || props.movie.name}
|
<span className='modal__rating'>Rating: {vote_average * 10}% </span>
|
||||||
</h1>
|
Release date: {release_date || first_air_date} Runtime:{' '}
|
||||||
<p className="modal__info">
|
{runtime || episode_run_time}m
|
||||||
<span className="modal__rating">
|
</p>
|
||||||
Rating: {props.movie.vote_average * 10}%{" "}
|
<p className='modal__episode'>
|
||||||
</span>
|
{number_of_episodes ? ' Episodes: ' + number_of_episodes : ''}
|
||||||
Release date: {props.movie.release_date || props.movie.first_air_date} Runtime: {props.movie.runtime || props.movie.episode_run_time}m
|
{number_of_seasons ? ' Seasons: ' + number_of_seasons : ''}
|
||||||
</p>
|
</p>
|
||||||
<p className="modal__episode">
|
<p className='modal__overview'>{overview}</p>
|
||||||
{props.movie.number_of_episodes ? " Episodes: " + props.movie.number_of_episodes : ""}
|
<button className='modal__btn modal__btn--red'>
|
||||||
{props.movie.number_of_seasons ? " Seasons: " + props.movie.number_of_seasons : ""}
|
<PlayIcon className='modal__btn--icon' />
|
||||||
</p>
|
Play
|
||||||
<p className="modal__overview">{props.movie.overview}</p>
|
</button>
|
||||||
<button className="modal__btn modal__btn--red">
|
<button className='modal__btn'>
|
||||||
<PlayIcon className="modal__btn--icon" />
|
<AddIcon className='modal__btn--icon' />
|
||||||
Play
|
My List
|
||||||
</button>
|
</button>
|
||||||
<button className="modal__btn">
|
</div>
|
||||||
<AddIcon className="modal__btn--icon" />
|
)
|
||||||
My List
|
}
|
||||||
</button>
|
|
||||||
</div>
|
export default MovieDetails
|
||||||
</Aux>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
const Movie = (props) => (
|
|
||||||
<div className="movie">
|
|
||||||
<div onClick={props.movieDetails} className="movie__column-poster">
|
|
||||||
<img src={props.movieImage} alt="" className="movie__poster" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Movie;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Aux from '../../hoc/Aux';
|
|
||||||
import AddIcon from '../../static/images/add.svg';
|
|
||||||
import PlayIcon from '../../static/images/play-button.svg';
|
|
||||||
|
|
||||||
export default function MovieDetails(props) {
|
|
||||||
return (
|
|
||||||
<Aux>
|
|
||||||
<div className="modal__container">
|
|
||||||
<h1 className="modal__title">
|
|
||||||
{props.movie.title || props.movie.name}
|
|
||||||
</h1>
|
|
||||||
<p className="modal__info">
|
|
||||||
<span className="modal__rating">
|
|
||||||
Rating: {props.movie.vote_average * 10}%{' '}
|
|
||||||
</span>
|
|
||||||
Release date: {props.movie.release_date || props.movie.first_air_date}{' '}
|
|
||||||
Runtime: {props.movie.runtime || props.movie.episode_run_time}m
|
|
||||||
</p>
|
|
||||||
<p className="modal__episode">
|
|
||||||
{props.movie.number_of_episodes
|
|
||||||
? ' Episodes: ' + props.movie.number_of_episodes
|
|
||||||
: ''}
|
|
||||||
{props.movie.number_of_seasons
|
|
||||||
? ' Seasons: ' + props.movie.number_of_seasons
|
|
||||||
: ''}
|
|
||||||
</p>
|
|
||||||
<p className="modal__overview">{props.movie.overview}</p>
|
|
||||||
<button className="modal__btn modal__btn--red">
|
|
||||||
<PlayIcon className="modal__btn--icon" />
|
|
||||||
Play
|
|
||||||
</button>
|
|
||||||
<button className="modal__btn">
|
|
||||||
<AddIcon className="modal__btn--icon" />
|
|
||||||
My List
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Aux>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
78
src/components/Navbar.js
Normal file
78
src/components/Navbar.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react'
|
||||||
|
import { NavLink } from 'react-router-dom'
|
||||||
|
import { withRouter } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { useScroll } from '../hooks/useScroll'
|
||||||
|
import SearchLogo from '../static/images/search-icon.svg'
|
||||||
|
import NetflixLogo from '../static/images/Netflix_Logo_RGB.png'
|
||||||
|
import BellLogo from '../static/images/bell-logo.svg'
|
||||||
|
import DropdownArrow from '../static/images/drop-down-arrow.svg'
|
||||||
|
import DropdownContent from '../components/DropdownContent'
|
||||||
|
|
||||||
|
const Navbar = ({ history }) => {
|
||||||
|
const searchInput = React.useRef(null)
|
||||||
|
const [userInput, setUserInput] = useState('')
|
||||||
|
const [scrollDimensions] = useScroll()
|
||||||
|
const { scrollY } = scrollDimensions
|
||||||
|
|
||||||
|
const onChange = async (event) => {
|
||||||
|
setUserInput(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
document.activeElement === searchInput.current &&
|
||||||
|
userInput.length === 0
|
||||||
|
) {
|
||||||
|
history.push('/browse')
|
||||||
|
}
|
||||||
|
if (userInput.length > 0) history.push(`/search?q=${userInput}`)
|
||||||
|
}, [userInput, searchInput])
|
||||||
|
|
||||||
|
const onLogoClick = () => {
|
||||||
|
setUserInput('')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className={'navigation ' + (scrollY > 50 ? 'black' : '')}>
|
||||||
|
<ul className='navigation__container'>
|
||||||
|
<NavLink to='/' onClick={() => onLogoClick()}>
|
||||||
|
<img
|
||||||
|
className='navigation__container--logo'
|
||||||
|
src={NetflixLogo}
|
||||||
|
alt=''
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
<DropdownArrow className='navigation__container--downArrow-2'></DropdownArrow>
|
||||||
|
<div className='navigation__container-link pseudo-link'>Home</div>
|
||||||
|
<div className='navigation__container-link pseudo-link'>TV Shows</div>
|
||||||
|
<div className='navigation__container-link pseudo-link'>Movies</div>
|
||||||
|
<div className='navigation__container-link pseudo-link'>
|
||||||
|
Recently Added
|
||||||
|
</div>
|
||||||
|
<div className='navigation__container-link pseudo-link'>My List</div>
|
||||||
|
|
||||||
|
<div className='navigation__container--left'>
|
||||||
|
<SearchLogo className='logo' />
|
||||||
|
<input
|
||||||
|
ref={searchInput}
|
||||||
|
value={userInput}
|
||||||
|
onChange={(event) => onChange(event)}
|
||||||
|
className='navigation__container--left__input'
|
||||||
|
type='text'
|
||||||
|
placeholder='Title, genres, people'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='navigation__container-link pseudo-link'>KIDS</div>
|
||||||
|
<div className='navigation__container-link pseudo-link'>DVD</div>
|
||||||
|
<BellLogo className='navigation__container--bellLogo' />
|
||||||
|
|
||||||
|
<DropdownContent />
|
||||||
|
<DropdownArrow className='navigation__container--downArrow' />
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(Navbar)
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
|
|
||||||
const backdrop = (props) =>
|
const backdrop = ({ toggleBackdrop, show }) =>
|
||||||
props.show ? (
|
show ? (
|
||||||
<div onClick={props.toggleBackdrop} className="backdrop"></div>
|
<div
|
||||||
) : null;
|
onClick={toggleBackdrop}
|
||||||
|
className={`backdrop ${show ? 'hideB' : ''}`}
|
||||||
|
></div>
|
||||||
|
) : null
|
||||||
|
|
||||||
export default backdrop;
|
export default backdrop
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
|
|
||||||
import Aux from '../../hoc/Aux';
|
import Backdrop from './Backdrop'
|
||||||
import Backdrop from './Backdrop';
|
|
||||||
|
|
||||||
export default function Modal(props) {
|
const Modal = ({ show, modalClosed, children, backgroundImage }) => {
|
||||||
const backgroundStyle = {
|
const backgroundStyle = {
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${
|
backgroundImage: `url(https://image.tmdb.org/t/p/original/${backgroundImage})`,
|
||||||
props.movie.backdrop_path || props.movie.poster_path
|
}
|
||||||
})`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Aux>
|
<div>
|
||||||
<Backdrop show={props.show} toggleBackdrop={props.modalClosed} />
|
<Backdrop show={show} toggleBackdrop={modalClosed} />
|
||||||
<div
|
<div
|
||||||
style={backgroundStyle}
|
style={backgroundStyle}
|
||||||
className={props.show ? 'modal show' : 'modal hide'}
|
className={show ? 'modal show' : 'modal hide'}
|
||||||
>
|
>
|
||||||
{props.children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</Aux>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Modal
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import AppRouter from './AppRouter';
|
|
||||||
|
|
||||||
const App = () => <AppRouter />;
|
|
||||||
export default App;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { BrowserRouter, Route, Switch } from 'react-router-dom';
|
|
||||||
|
|
||||||
import Home from './Home';
|
|
||||||
import NotFound from './NotFound';
|
|
||||||
import Search from './Search';
|
|
||||||
import Navbar from './Navbar';
|
|
||||||
|
|
||||||
const AppRouter = () => (
|
|
||||||
<BrowserRouter>
|
|
||||||
<>
|
|
||||||
<Navbar />
|
|
||||||
<Switch>
|
|
||||||
<Route path="/" exact component={Home} />
|
|
||||||
<Route path="/search" component={Search} />
|
|
||||||
<Route component={NotFound} />
|
|
||||||
</Switch>
|
|
||||||
</>
|
|
||||||
</BrowserRouter>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default AppRouter;
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
|
||||||
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper';
|
|
||||||
// install Swiper components
|
|
||||||
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);
|
|
||||||
|
|
||||||
export default class DisplayMovieRow extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
width: window.innerWidth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.addEventListener("resize", this.handleResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnMount() {
|
|
||||||
window.addEventListener("resize", this.handleResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleResize = (e) => {
|
|
||||||
this.setState({ width: window.innerWidth });
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { width } = this.state;
|
|
||||||
let netflixUrl = false;
|
|
||||||
if (
|
|
||||||
this.props.url ===
|
|
||||||
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
|
||||||
) {
|
|
||||||
netflixUrl = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1 className="movieShowcase__heading">{this.props.title}</h1>
|
|
||||||
<Swiper
|
|
||||||
className="movieShowcase__container"
|
|
||||||
navigation={true}
|
|
||||||
grabCursor={false}
|
|
||||||
draggable={false}
|
|
||||||
loop={true}
|
|
||||||
loopAdditionalSlides={
|
|
||||||
width >= 1378 ? 4 :
|
|
||||||
width >= 998 ? 3 :
|
|
||||||
width >= 625 ? 2 : 2
|
|
||||||
}
|
|
||||||
breakpoints={{
|
|
||||||
1378: {
|
|
||||||
slidesPerView: 5,
|
|
||||||
slidesPerGroup: 5,
|
|
||||||
},
|
|
||||||
998: {
|
|
||||||
slidesPerView: 4,
|
|
||||||
slidesPerGroup: 4,
|
|
||||||
},
|
|
||||||
625: {
|
|
||||||
slidesPerView: 3,
|
|
||||||
slidesPerGroup: 3,
|
|
||||||
},
|
|
||||||
0: {
|
|
||||||
slidesPerView: 2,
|
|
||||||
slidesPerGroup: 2,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
preventClicksPropagation={true}
|
|
||||||
preventClicks={true}
|
|
||||||
scrollbar={{ draggable: false, hide: true }}
|
|
||||||
slideToClickedSlide={false}
|
|
||||||
pagination={{ clickable: true }}
|
|
||||||
>
|
|
||||||
{this.props.movies.map((movie, idx) => {
|
|
||||||
let movieImageUrl =
|
|
||||||
'https://image.tmdb.org/t/p/w500/' + movie.backdrop_path;
|
|
||||||
if (
|
|
||||||
this.props.url ===
|
|
||||||
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
|
||||||
) {
|
|
||||||
movieImageUrl =
|
|
||||||
'https://image.tmdb.org/t/p/original/' + movie.poster_path;
|
|
||||||
}
|
|
||||||
if (movie.poster_path && movie.backdrop_path !== null) {
|
|
||||||
return (
|
|
||||||
<SwiperSlide
|
|
||||||
onClick={() => this.props.selectMovieHandler(movie)}
|
|
||||||
key={idx}
|
|
||||||
className={
|
|
||||||
'movieShowcase__container--movie' +
|
|
||||||
(netflixUrl ? '__netflix' : '')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={movieImageUrl}
|
|
||||||
className="movieShowcase__container--movie-image"
|
|
||||||
/>
|
|
||||||
</SwiperSlide>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</Swiper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
import MainContent from './MainContent';
|
|
||||||
import Modal from '../components/UI/Modal';
|
|
||||||
import MovieDetails from '../components/Movie/MovieDetails';
|
|
||||||
|
|
||||||
class Home extends Component {
|
|
||||||
state = {
|
|
||||||
/** Toggles the modal when a movie is clicked. */
|
|
||||||
toggleModal: false,
|
|
||||||
/** Holds the movie information for a single movie. */
|
|
||||||
movieOverview: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get the appropriate details for a specific movie that was clicked */
|
|
||||||
selectMovieHandler = async (movie) => {
|
|
||||||
this.setState({ toggleModal: true });
|
|
||||||
await this.setState({ movieOverview: movie });
|
|
||||||
}
|
|
||||||
|
|
||||||
closeModal = () => {
|
|
||||||
this.setState({ toggleModal: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="main-content">
|
|
||||||
<MainContent selectMovieHandler={this.selectMovieHandler} />
|
|
||||||
</div>
|
|
||||||
<Modal show={this.state.toggleModal}
|
|
||||||
modalClosed={this.closeModal}
|
|
||||||
movie={this.state.movieOverview}>
|
|
||||||
<MovieDetails movie={this.state.movieOverview} />
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Home;
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import Header from '../components/Header';
|
|
||||||
import Footer from '../components/Footer';
|
|
||||||
import {
|
|
||||||
fetchNetflixOriginals,
|
|
||||||
fetchTrending,
|
|
||||||
fetchTopRated,
|
|
||||||
fetchActionMovies,
|
|
||||||
fetchComedyMovies,
|
|
||||||
fetchDocumentaries,
|
|
||||||
fetchHorrorMovies
|
|
||||||
} from '../store/actions/index';
|
|
||||||
import DisplayMovieRow from './DisplayMovieRow';
|
|
||||||
|
|
||||||
|
|
||||||
class MainContent extends Component {
|
|
||||||
|
|
||||||
state = {
|
|
||||||
/** Will hold our chosen movie to display on the header */
|
|
||||||
selectedMovie: {},
|
|
||||||
movieInfo: [
|
|
||||||
{
|
|
||||||
title: 'Netflix Originals',
|
|
||||||
url: `/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Trending Now',
|
|
||||||
url: `/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Top Rated',
|
|
||||||
url: `/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Action Movies',
|
|
||||||
url: `/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Comedy Movies',
|
|
||||||
url: `/discover/tv?api_key=${process.env.API_KEY}&with_genres=35`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Horror Movies',
|
|
||||||
url: `/discover/tv?api_key=${process.env.API_KEY}&with_genres=27`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Documentaries',
|
|
||||||
url: `/discover/tv?api_key=${process.env.API_KEY}&with_genres=99`,
|
|
||||||
movies: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount = async () => {
|
|
||||||
await this.getMovie();
|
|
||||||
await this.props.fetchNetflixOriginals();
|
|
||||||
await this.props.fetchTrending();
|
|
||||||
await this.props.fetchTopRated();
|
|
||||||
await this.props.fetchActionMovies();
|
|
||||||
await this.props.fetchComedyMovies();
|
|
||||||
await this.props.fetchDocumentaries();
|
|
||||||
await this.props.fetchHorrorMovies();
|
|
||||||
|
|
||||||
const newMoviesArray = this.state.movieInfo.map((movie) => {
|
|
||||||
if (movie.title === 'Netflix Originals') {
|
|
||||||
movie.movies.push(...this.props.netflixOriginals.data)
|
|
||||||
}
|
|
||||||
if (movie.title === 'Trending Now') {
|
|
||||||
movie.movies.push(...this.props.trending.data)
|
|
||||||
}
|
|
||||||
if (movie.title === 'Top Rated') {
|
|
||||||
movie.movies.push(...this.props.topRated.data)
|
|
||||||
}
|
|
||||||
if (movie.title === 'Action Movies') {
|
|
||||||
movie.movies.push(...this.props.actionMovies.data)
|
|
||||||
}
|
|
||||||
if (movie.title === 'Comedy Movies') {
|
|
||||||
movie.movies.push(...this.props.comedyMovies.data)
|
|
||||||
}
|
|
||||||
if (movie.title === 'Documentaries') {
|
|
||||||
movie.movies.push(...this.props.documentaries.data)
|
|
||||||
}
|
|
||||||
if (movie.title === 'Horror Movies') {
|
|
||||||
movie.movies.push(...this.props.horrorMovies.data)
|
|
||||||
}
|
|
||||||
return movie
|
|
||||||
})
|
|
||||||
await this.setState({ movieInfo: newMoviesArray })
|
|
||||||
};
|
|
||||||
|
|
||||||
getMovie = () => {
|
|
||||||
/** Movie Id for the Narcos series */
|
|
||||||
const movieId = 63351;
|
|
||||||
/** Make Api call to retrieve the details for a single movie */
|
|
||||||
const url = `https://api.themoviedb.org/3/tv/${movieId}?api_key=${process.env.API_KEY}`;
|
|
||||||
axios
|
|
||||||
.get(url)
|
|
||||||
.then(res => {
|
|
||||||
const movieData = res.data;
|
|
||||||
this.setState({ selectedMovie: movieData });
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="container">
|
|
||||||
<Header movie={this.state.selectedMovie} />
|
|
||||||
<div className="movieShowcase">
|
|
||||||
{
|
|
||||||
this.state.movieInfo.map((info) => {
|
|
||||||
if (info.movies.length > 0) {
|
|
||||||
return (
|
|
||||||
<DisplayMovieRow
|
|
||||||
selectMovieHandler={this.props.selectMovieHandler}
|
|
||||||
key={info.title}
|
|
||||||
title={info.title}
|
|
||||||
url={info.url}
|
|
||||||
movies={info.movies}
|
|
||||||
/>)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
netflixOriginals: state.netflixOriginals,
|
|
||||||
trending: state.trending,
|
|
||||||
topRated: state.topRated,
|
|
||||||
actionMovies: state.action,
|
|
||||||
comedyMovies: state.comedy,
|
|
||||||
documentaries: state.documentary,
|
|
||||||
horrorMovies: state.horror
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => {
|
|
||||||
return bindActionCreators({
|
|
||||||
fetchNetflixOriginals,
|
|
||||||
fetchTrending,
|
|
||||||
fetchTopRated,
|
|
||||||
fetchActionMovies,
|
|
||||||
fetchComedyMovies,
|
|
||||||
fetchDocumentaries,
|
|
||||||
fetchHorrorMovies
|
|
||||||
}, dispatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(MainContent);
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import { NavLink } from 'react-router-dom';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import { withRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
import axios from '../axios-movies';
|
|
||||||
import SearchLogo from '../static/images/search-icon.svg';
|
|
||||||
import NetflixLogo from '../static/images/Netflix_Logo_RGB.png';
|
|
||||||
import BellLogo from '../static/images/bell-logo.svg';
|
|
||||||
import DropdownArrow from '../static/images/drop-down-arrow.svg';
|
|
||||||
import DropdownContent from "../components/DropdownContent";
|
|
||||||
|
|
||||||
class Navbar extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.state = {
|
|
||||||
scrolling: false,
|
|
||||||
userInput: ''
|
|
||||||
}
|
|
||||||
// use to debounce api call
|
|
||||||
this.makeAipCall = _.debounce(this.makeAipCall, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
window.addEventListener('scroll', this.handleScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
window.removeEventListener('scroll', this.handleScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** changes the scrolling state depending on the Y-position */
|
|
||||||
handleScroll = () => {
|
|
||||||
if (window.scrollY === 0) {
|
|
||||||
this.setState({ scrolling: false });
|
|
||||||
}
|
|
||||||
else if (window.scrollY > 50) {
|
|
||||||
this.setState({ scrolling: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = async (event) => {
|
|
||||||
await this.setState({ userInput: event.target.value })
|
|
||||||
const { userInput } = this.state
|
|
||||||
|
|
||||||
await this.makeAipCall(userInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Make API call as soon as the user starts typing. */
|
|
||||||
makeAipCall = async (searchItem) => {
|
|
||||||
if (searchItem.length === 0) {
|
|
||||||
this.props.history.push('/')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const url = `/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchItem}`;
|
|
||||||
const response = await axios.get(url);
|
|
||||||
const results = response.data.results;
|
|
||||||
this.props.history.push({
|
|
||||||
pathname: '/search',
|
|
||||||
movieRows: results,
|
|
||||||
userInput: searchItem
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onLogoClick = () => {
|
|
||||||
// reset input state
|
|
||||||
this.setState({ userInput: '' })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { scrolling } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<nav className={"navigation " + (scrolling ? "black" : "")} >
|
|
||||||
<ul className="navigation__container">
|
|
||||||
<NavLink to="/" onClick={() => this.onLogoClick()}>
|
|
||||||
<img className="navigation__container--logo" src={NetflixLogo} alt="" />
|
|
||||||
</NavLink>
|
|
||||||
<DropdownArrow className="navigation__container--downArrow-2"></DropdownArrow>
|
|
||||||
<div className="navigation__container-link pseudo-link">Home</div>
|
|
||||||
<div className="navigation__container-link pseudo-link">TV Shows</div>
|
|
||||||
<div className="navigation__container-link pseudo-link">Movies</div>
|
|
||||||
<div className="navigation__container-link pseudo-link">Recently Added</div>
|
|
||||||
<div className="navigation__container-link pseudo-link">My List</div>
|
|
||||||
|
|
||||||
<div className="navigation__container--left">
|
|
||||||
<SearchLogo className="logo" />
|
|
||||||
<input
|
|
||||||
value={this.state.userInput}
|
|
||||||
onChange={() => this.onChange(event)}
|
|
||||||
className="navigation__container--left__input"
|
|
||||||
type="text"
|
|
||||||
placeholder="Title, genres, people" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="navigation__container-link pseudo-link">KIDS</div>
|
|
||||||
<div className="navigation__container-link pseudo-link">DVD</div>
|
|
||||||
<BellLogo className="navigation__container--bellLogo" />
|
|
||||||
|
|
||||||
<DropdownContent />
|
|
||||||
<DropdownArrow className="navigation__container--downArrow" />
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(Navbar);
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
import Modal from '../components/UI/Modal';
|
|
||||||
import MovieDetails from '../components/Movie/MovieDetails';
|
|
||||||
import Movie from '../components/Movie/Movie';
|
|
||||||
import axios from '../axios-movies';
|
|
||||||
|
|
||||||
export default class Search extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.state = {
|
|
||||||
movies: [],
|
|
||||||
toggleModal: false,
|
|
||||||
/** Holds the movie information for a single movie. */
|
|
||||||
movieOverview: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount = async () => {
|
|
||||||
const { movieRows } = this.props.history.location;
|
|
||||||
if (movieRows)
|
|
||||||
await this.setState({ movies: movieRows });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate = async (prevProps) => {
|
|
||||||
if (
|
|
||||||
prevProps.location.movieRows.length !==
|
|
||||||
this.props.location.movieRows.length
|
|
||||||
) {
|
|
||||||
await this.setState({ movies: this.props.location.movieRows });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeModal = () => {
|
|
||||||
this.setState({ toggleModal: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the appropriate details for a specific movie that was clicked */
|
|
||||||
selectMovieHandler = (movie) => {
|
|
||||||
this.setState({ toggleModal: true });
|
|
||||||
|
|
||||||
let url;
|
|
||||||
/** Make the appropriate API call to get the details for a single movie or tv show. */
|
|
||||||
if (movie.media_type === "movie") {
|
|
||||||
const movieId = movie.id;
|
|
||||||
url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${process.env.API_KEY}`;
|
|
||||||
|
|
||||||
} else if (movie.media_type === "tv") {
|
|
||||||
const tvId = movie.id
|
|
||||||
url = `https://api.themoviedb.org/3/tv/${tvId}?api_key=${process.env.API_KEY}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
axios.get(url)
|
|
||||||
.then(res => {
|
|
||||||
const movieData = res.data;
|
|
||||||
this.setState({ movieOverview: movieData });
|
|
||||||
}).catch(error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { movies } = this.state
|
|
||||||
const { userInput } = this.props.location
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{
|
|
||||||
movies.length > 0 ? (
|
|
||||||
<div className="search-container">
|
|
||||||
{
|
|
||||||
movies.map((movie) => {
|
|
||||||
let movieRows = []
|
|
||||||
let movieImageUrl;
|
|
||||||
if (movie.poster_path !== null && movie.media_type !== "person") {
|
|
||||||
movieImageUrl = "https://image.tmdb.org/t/p/w500" + movie.poster_path;
|
|
||||||
|
|
||||||
/** Set the movie object to our Movie component */
|
|
||||||
const movieComponent = <Movie
|
|
||||||
movieDetails={() => this.selectMovieHandler(movie)}
|
|
||||||
key={movie.id}
|
|
||||||
movieImage={movieImageUrl}
|
|
||||||
movie={movie} />
|
|
||||||
|
|
||||||
/** Push our movie component to our movieRows array */
|
|
||||||
movieRows.push(movieComponent);
|
|
||||||
}
|
|
||||||
return movieRows
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="no-results">
|
|
||||||
<div className="no-results__text">
|
|
||||||
<p>Your search for "{userInput}" did not have any matches.</p>
|
|
||||||
<p>Suggestions:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Try different keywords</li>
|
|
||||||
<li>Looking for a movie or TV show?</li>
|
|
||||||
<li>Try using a movie, TV show title, an actor or director</li>
|
|
||||||
<li>Try a genre, like comedy, romance, sports, or drama</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<Modal Modal show={this.state.toggleModal}
|
|
||||||
modalClosed={this.closeModal}
|
|
||||||
movie={this.state.movieOverview} >
|
|
||||||
<MovieDetails movie={this.state.movieOverview} />
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
const aux = (props) => props.children;
|
|
||||||
|
|
||||||
export default aux;
|
|
||||||
25
src/hooks/useDebounce.js
Normal file
25
src/hooks/useDebounce.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
export const useDebounce = (value, delay) => {
|
||||||
|
// State and setters for debounced value
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState(value)
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
// Update debounced value after delay
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedValue(value)
|
||||||
|
}, delay)
|
||||||
|
|
||||||
|
// Cancel the timeout if value changes (also on delay change or unmount)
|
||||||
|
// This is how we prevent debounced value from updating if value is changed ...
|
||||||
|
// .. within the delay period. Timeout gets cleared and restarted.
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[value, delay] // Only re-call effect if value or delay changes
|
||||||
|
)
|
||||||
|
|
||||||
|
return debouncedValue
|
||||||
|
}
|
||||||
26
src/hooks/useScroll.js
Normal file
26
src/hooks/useScroll.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
const scrollY = window.scrollY || document.documentElement.scrollTop
|
||||||
|
const scrollX = window.scrollY || document.documentElement.scrollWidth
|
||||||
|
|
||||||
|
export const useScroll = () => {
|
||||||
|
const [scrollDimensions, setScrollDimensions] = useState({ scrollY, scrollX })
|
||||||
|
|
||||||
|
const deriveScrollDimensions = () => {
|
||||||
|
const scrollY = window.scrollY || document.documentElement.scrollTop
|
||||||
|
const scrollX = window.scrollY || document.documentElement.scrollWidth
|
||||||
|
|
||||||
|
setScrollDimensions({ scrollY, scrollX })
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
deriveScrollDimensions()
|
||||||
|
window.addEventListener('scroll', deriveScrollDimensions)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', deriveScrollDimensions)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return [scrollDimensions]
|
||||||
|
}
|
||||||
39
src/hooks/useViewport.js
Normal file
39
src/hooks/useViewport.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
const height =
|
||||||
|
window.innerHeight ||
|
||||||
|
document.documentElement.clientHeight ||
|
||||||
|
document.body.clientHeight
|
||||||
|
const width =
|
||||||
|
window.innerWidth ||
|
||||||
|
document.documentElement.clientWidth ||
|
||||||
|
document.body.clientWidth
|
||||||
|
|
||||||
|
export const useViewport = () => {
|
||||||
|
const [windowDimensions, setWindowDimensions] = useState({ height, width })
|
||||||
|
|
||||||
|
const deriveWindowDimensions = () => {
|
||||||
|
const height =
|
||||||
|
window.innerHeight ||
|
||||||
|
document.documentElement.clientHeight ||
|
||||||
|
document.body.clientHeight
|
||||||
|
|
||||||
|
const width =
|
||||||
|
window.innerWidth ||
|
||||||
|
document.documentElement.clientWidth ||
|
||||||
|
document.body.clientWidth
|
||||||
|
|
||||||
|
setWindowDimensions({ height, width })
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
deriveWindowDimensions()
|
||||||
|
window.addEventListener('resize', deriveWindowDimensions)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', deriveWindowDimensions)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return [windowDimensions]
|
||||||
|
}
|
||||||
37
src/index.js
37
src/index.js
@@ -1,25 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom'
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux'
|
||||||
import { createStore, applyMiddleware } from 'redux';
|
import { createStore, applyMiddleware } from 'redux'
|
||||||
import reducers from './store/reducers';
|
import ReduxThunk from 'redux-thunk'
|
||||||
import promise from 'redux-promise';
|
import '@babel/polyfill'
|
||||||
import '@babel/polyfill';
|
|
||||||
|
|
||||||
import App from './containers/App';
|
import reducers from './store/reducers'
|
||||||
import 'swiper/swiper-bundle.min.css';
|
import AppRouter from './AppRouter'
|
||||||
// import 'swiper/components/navigation/navigation.scss';
|
|
||||||
// import 'swiper/components/pagination/pagination.scss';
|
// Import Swiper styles
|
||||||
// import 'swiper/components/scrollbar/scrollbar.scss';
|
import 'swiper/css'
|
||||||
|
import 'swiper/css/navigation'
|
||||||
|
import 'swiper/css/pagination'
|
||||||
// Import main sass file to apply global styles
|
// Import main sass file to apply global styles
|
||||||
import './static/sass/style.scss';
|
import './static/sass/style.scss'
|
||||||
|
|
||||||
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
|
const store = createStore(reducers, applyMiddleware(ReduxThunk))
|
||||||
|
|
||||||
const app = (
|
const app = (
|
||||||
<Provider store={createStoreWithMiddleware(reducers)}>
|
<Provider store={store}>
|
||||||
<App />
|
<AppRouter />
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
)
|
||||||
|
|
||||||
ReactDOM.render(app, document.getElementById('app'));
|
ReactDOM.render(app, document.getElementById('app'))
|
||||||
|
|||||||
36
src/pages/Home.js
Normal file
36
src/pages/Home.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import MainContent from '../components/MainContent'
|
||||||
|
import Modal from '../components/UI/Modal'
|
||||||
|
import ModalMovieDetails from '../components/ModalMovieDetails'
|
||||||
|
|
||||||
|
const Home = () => {
|
||||||
|
const [toggleModal, setToggleModal] = useState(false)
|
||||||
|
const [movieDetails, setMovieDetails] = useState({})
|
||||||
|
|
||||||
|
const selectMovieHandler = async (movie) => {
|
||||||
|
setToggleModal(true)
|
||||||
|
setMovieDetails(movie)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
setToggleModal(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='main-content'>
|
||||||
|
<MainContent selectMovieHandler={selectMovieHandler} />
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
show={toggleModal}
|
||||||
|
modalClosed={closeModal}
|
||||||
|
backgroundImage={movieDetails.backdrop_path || movieDetails.poster_path}
|
||||||
|
>
|
||||||
|
<ModalMovieDetails movie={movieDetails} />
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function NotFound() {
|
const NotFound = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>NOT FOUND</h1>
|
<h1>NOT FOUND</h1>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NotFound
|
||||||
91
src/pages/Search.js
Normal file
91
src/pages/Search.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
import { useDebounce } from '../hooks/useDebounce'
|
||||||
|
import * as movieActions from '../store/actions'
|
||||||
|
import Modal from '../components/UI/Modal'
|
||||||
|
import ModalMovieDetails from '../components/ModalMovieDetails'
|
||||||
|
|
||||||
|
// A custom hook that builds on useLocation to parse
|
||||||
|
// the query string for you.
|
||||||
|
const useQuery = () => {
|
||||||
|
return new URLSearchParams(useLocation().search)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Search = () => {
|
||||||
|
let query = useQuery()
|
||||||
|
const debouncedSearchTerm = useDebounce(query.get('q'), 500)
|
||||||
|
const [isToggleModal, setIsToggleModal] = useState(false)
|
||||||
|
const { searchResults, isLoading } = useSelector((state) => state.searchMovie)
|
||||||
|
const { movieDetails } = useSelector((state) => state.movieDetails)
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (debouncedSearchTerm) {
|
||||||
|
dispatch(movieActions.fetchSearchMovie(debouncedSearchTerm))
|
||||||
|
}
|
||||||
|
}, [debouncedSearchTerm])
|
||||||
|
|
||||||
|
const onCloseModalHandler = () => {
|
||||||
|
setIsToggleModal(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelectMovieHandler = (movie) => {
|
||||||
|
dispatch(movieActions.fetchMovieDetails(movie.media_type, movie.id))
|
||||||
|
setIsToggleModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderSearchResults = () => {
|
||||||
|
return searchResults.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<div className='search-container'>
|
||||||
|
{searchResults.map((movie) => {
|
||||||
|
if (movie.backdrop_path !== null && movie.media_type !== 'person') {
|
||||||
|
const movieImageUrl =
|
||||||
|
'https://image.tmdb.org/t/p/w500' + movie.backdrop_path
|
||||||
|
return (
|
||||||
|
<div className='movie'>
|
||||||
|
<div
|
||||||
|
onClick={() => onSelectMovieHandler(movie)}
|
||||||
|
className='movie__column-poster'
|
||||||
|
>
|
||||||
|
<img src={movieImageUrl} alt='' className='movie__poster' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
show={isToggleModal}
|
||||||
|
modalClosed={onCloseModalHandler}
|
||||||
|
backgroundImage={
|
||||||
|
movieDetails.backdrop_path || movieDetails.poster_path
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ModalMovieDetails movie={movieDetails} />
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className='no-results'>
|
||||||
|
<div className='no-results__text'>
|
||||||
|
<p>
|
||||||
|
Your search for "{debouncedSearchTerm}" did not have any matches.
|
||||||
|
</p>
|
||||||
|
<p>Suggestions:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Try different keywords</li>
|
||||||
|
<li>Looking for a movie or TV show?</li>
|
||||||
|
<li>Try using a movie, TV show title, an actor or director</li>
|
||||||
|
<li>Try a genre, like comedy, romance, sports, or drama</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isLoading ? renderSearchResults() : <h1>Loading...</h1>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Search
|
||||||
3
src/static/images/mute.svg
Normal file
3
src/static/images/mute.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="none" stroke="#000" stroke-width="2" d="M1,8 L1,16 L6.09901951,16 L12,21 L12,3 L6,8 L1,8 Z M15,9 L21,15 M21,9 L15,15"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 229 B |
3
src/static/images/unmute.svg
Normal file
3
src/static/images/unmute.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="none" stroke="#000" stroke-width="2" d="M15,16 C17.209,16 19,14.209 19,12 C19,9.791 17.209,8 15,8 M15,20 C20,20 23,16.411 23,12 C23,7.589 19.411,4 15,4 M1,12 L1,8 L6,8 L12,3 L12,21 L6,16 L1,16 L1,12"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 311 B |
@@ -1,67 +1,147 @@
|
|||||||
/* GLOBAL RESET */
|
/* GLOBAL RESET */
|
||||||
:global(html), :global(body), :global(div), :global(span), :global(applet), :global(object), :global(iframe),
|
:global(html),
|
||||||
:global(h1), :global(h2), :global(h3), :global(h4), :global(h5), :global(h6), :global(p), :global(blockquote), :global(pre),
|
:global(body),
|
||||||
:global(a), :global(abbr), :global(acronym), :global(address), :global(big), :global(cite), :global(code),
|
:global(div),
|
||||||
:global(del), :global(dfn), :global(em), :global(img), :global(ins), :global(kbd), :global(q), :global(s), :global(samp),
|
:global(span),
|
||||||
:global(small), :global(strike), :global(strong), :global(sub), :global(sup), :global(tt), :global(var),
|
:global(applet),
|
||||||
:global(b), :global(u), :global(i), :global(center),
|
:global(object),
|
||||||
:global(dl), :global(dt), :global(dd), :global(ol), :global(ul), :global(li),
|
:global(iframe),
|
||||||
:global(fieldset), :global(form), :global(label), :global(legend),
|
:global(h1),
|
||||||
:global(table), :global(caption), :global(tbody), :global(tfoot), :global(thead), :global(tr), :global(th), :global(td),
|
:global(h2),
|
||||||
:global(article), :global(aside), :global(canvas), :global(details), :global(embed),
|
:global(h3),
|
||||||
:global(figure), :global(figcaption), :global(footer), :global(header), :global(hgroup),
|
:global(h4),
|
||||||
:global(menu), :global(nav), :global(output), :global(ruby), :global(section), :global(summary),
|
:global(h5),
|
||||||
:global(time), :global(mark), :global(audio), :global(video) {
|
:global(h6),
|
||||||
margin: 0;
|
:global(p),
|
||||||
padding: 0;
|
:global(blockquote),
|
||||||
border: 0;
|
:global(pre),
|
||||||
vertical-align: baseline;
|
:global(a),
|
||||||
box-sizing: inherit;
|
:global(abbr),
|
||||||
|
:global(acronym),
|
||||||
|
:global(address),
|
||||||
|
:global(big),
|
||||||
|
:global(cite),
|
||||||
|
:global(code),
|
||||||
|
:global(del),
|
||||||
|
:global(dfn),
|
||||||
|
:global(em),
|
||||||
|
:global(img),
|
||||||
|
:global(ins),
|
||||||
|
:global(kbd),
|
||||||
|
:global(q),
|
||||||
|
:global(s),
|
||||||
|
:global(samp),
|
||||||
|
:global(small),
|
||||||
|
:global(strike),
|
||||||
|
:global(strong),
|
||||||
|
:global(sub),
|
||||||
|
:global(sup),
|
||||||
|
:global(tt),
|
||||||
|
:global(var),
|
||||||
|
:global(b),
|
||||||
|
:global(u),
|
||||||
|
:global(i),
|
||||||
|
:global(center),
|
||||||
|
:global(dl),
|
||||||
|
:global(dt),
|
||||||
|
:global(dd),
|
||||||
|
:global(ol),
|
||||||
|
:global(ul),
|
||||||
|
:global(li),
|
||||||
|
:global(fieldset),
|
||||||
|
:global(form),
|
||||||
|
:global(label),
|
||||||
|
:global(legend),
|
||||||
|
:global(table),
|
||||||
|
:global(caption),
|
||||||
|
:global(tbody),
|
||||||
|
:global(tfoot),
|
||||||
|
:global(thead),
|
||||||
|
:global(tr),
|
||||||
|
:global(th),
|
||||||
|
:global(td),
|
||||||
|
:global(article),
|
||||||
|
:global(aside),
|
||||||
|
:global(canvas),
|
||||||
|
:global(details),
|
||||||
|
:global(embed),
|
||||||
|
:global(figure),
|
||||||
|
:global(figcaption),
|
||||||
|
:global(footer),
|
||||||
|
:global(header),
|
||||||
|
:global(hgroup),
|
||||||
|
:global(menu),
|
||||||
|
:global(nav),
|
||||||
|
:global(output),
|
||||||
|
:global(ruby),
|
||||||
|
:global(section),
|
||||||
|
:global(summary),
|
||||||
|
:global(time),
|
||||||
|
:global(mark),
|
||||||
|
:global(audio),
|
||||||
|
:global(video) {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
/* HTML5 display-role reset for older browsers */
|
||||||
:global(article), :global(aside), :global(details), :global(figcaption), :global(figure),
|
:global(article),
|
||||||
:global(footer), :global(header), :global(hgroup), :global(menu), :global(nav), :global(section) {
|
:global(aside),
|
||||||
display: block;
|
:global(details),
|
||||||
|
:global(figcaption),
|
||||||
|
:global(figure),
|
||||||
|
:global(footer),
|
||||||
|
:global(header),
|
||||||
|
:global(hgroup),
|
||||||
|
:global(menu),
|
||||||
|
:global(nav),
|
||||||
|
:global(section) {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
:global(body) {
|
:global(body) {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
:global(ol), :global(ul) {
|
:global(ol),
|
||||||
list-style: none;
|
:global(ul) {
|
||||||
|
list-style: none;
|
||||||
}
|
}
|
||||||
:global(blockquote), :global(q) {
|
:global(blockquote),
|
||||||
quotes: none;
|
:global(q) {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
:global(blockquote:before),
|
||||||
|
:global(blockquote:after),
|
||||||
|
:global(q:before),
|
||||||
|
:global(q:after) {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
}
|
}
|
||||||
:global(blockquote:before), :global(blockquote:after),
|
|
||||||
:global(q:before), :global(q:after) {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
:global(table) {
|
:global(table) {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Hind', sans-serif;
|
font-family: 'Hind', sans-serif;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: $color-background;
|
background-color: $color-background;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
// this defines what 1rem is --> font root size
|
// this defines what 1rem is --> font root size
|
||||||
font-size: 62.5%; // 10/16, 1rem = 10px;
|
font-size: 62.5%; // 10/16, 1rem = 10px;
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_port) {
|
||||||
font-size: 50%;
|
font-size: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GRID AREA
|
// GRID AREA
|
||||||
.container {
|
.container {
|
||||||
|
// display: grid;
|
||||||
display: grid;
|
// grid-template-rows: min-content min-content min-content min-content;
|
||||||
grid-template-rows: min-content min-content min-content min-content;
|
// grid-template-columns: 0 repeat(10, 1fr);
|
||||||
grid-template-columns: 4% repeat(10, 1fr) 4%;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
.backdrop {
|
.backdrop {
|
||||||
width: 120%;
|
width: 120%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
background-color: rgba($color-background, .7);
|
animation: fadeIn 1s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
|
||||||
transition: all .3s;
|
background-color: rgba($color-background, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,142 +1,147 @@
|
|||||||
.modal {
|
.modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 500;
|
z-index: 500;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
height: 52rem;
|
height: 52rem;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
box-shadow: 0 1.5rem 4rem rgba($color-dark, .15);
|
box-shadow: 0 1.5rem 4rem rgba($color-dark, 0.15);
|
||||||
transition: all .3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_port) {
|
||||||
height: 38rem;
|
height: 38rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include responsive(phone) {
|
@include responsive(phone) {
|
||||||
height: 50rem;
|
height: 50rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
background: linear-gradient(90deg, #000 50%, transparent);
|
||||||
|
width: 70%;
|
||||||
|
padding-top: 3rem;
|
||||||
|
height: 100%;
|
||||||
|
padding-left: 5rem;
|
||||||
|
|
||||||
&__container {
|
@include responsive(tab_port) {
|
||||||
background: linear-gradient(90deg, #000 50%, transparent);
|
background: linear-gradient(
|
||||||
width: 70%;
|
90deg,
|
||||||
padding-top: 3rem;
|
rgb(0, 0, 0) 55%,
|
||||||
height: 100%;
|
rgba(0, 0, 0, 0.733),
|
||||||
padding-left: 5rem;
|
transparent
|
||||||
|
);
|
||||||
|
width: 88%;
|
||||||
|
}
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_medium) {
|
||||||
background: linear-gradient(90deg, rgb(0, 0, 0) 55%, rgba(0, 0, 0, 0.733), transparent);
|
background: linear-gradient(90deg, rgba(0, 0, 0, 0.966) 65%, transparent);
|
||||||
width: 88%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include responsive(tab_medium) {
|
@include responsive(phone) {
|
||||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.966) 65%, transparent);
|
padding-top: 1rem;
|
||||||
width: 100%;
|
padding-left: 1rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include responsive(phone) {
|
&__title {
|
||||||
padding-top: 1rem;
|
font-size: 4rem;
|
||||||
padding-left: 1rem;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
&__rating {
|
||||||
font-size: 4rem;
|
font-size: 2rem;
|
||||||
}
|
color: $color-green-modal;
|
||||||
|
}
|
||||||
|
|
||||||
&__rating {
|
&__info {
|
||||||
font-size: 2rem;
|
padding-top: 1.6rem;
|
||||||
color: $color-green-modal;
|
font-size: 2rem;
|
||||||
}
|
|
||||||
|
|
||||||
&__info {
|
@include responsive(phone) {
|
||||||
padding-top: 1.6rem;
|
padding-top: 1.2rem;
|
||||||
font-size: 2rem;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include responsive(phone) {
|
&__episode {
|
||||||
padding-top: 1.2rem;
|
padding-top: 0.5rem;
|
||||||
}
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__episode {
|
&__overview {
|
||||||
padding-top: .5rem;
|
color: $color-modal-grey-2;
|
||||||
font-size: 2rem;
|
padding-top: 2rem;
|
||||||
}
|
font-size: 2rem;
|
||||||
|
hyphens: auto;
|
||||||
|
width: 60%;
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
&__overview {
|
@include responsive(lg_desktop) {
|
||||||
color: $color-modal-grey-2;
|
|
||||||
padding-top: 2rem;
|
|
||||||
font-size: 2rem;
|
|
||||||
hyphens: auto;
|
|
||||||
width: 60%;
|
width: 60%;
|
||||||
line-height: 1.2;
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
@include responsive(lg_desktop) {
|
@include responsive(tab_port) {
|
||||||
width: 60%;
|
width: 80%;
|
||||||
font-size: 1.8rem;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_medium) {
|
||||||
width: 80%;
|
width: 95%;
|
||||||
}
|
color: rgba(255, 255, 255, 0.877);
|
||||||
|
}
|
||||||
|
|
||||||
@include responsive(tab_medium) {
|
@include responsive(phone) {
|
||||||
width: 95%;
|
padding-top: 1rem;
|
||||||
color: rgba(255, 255, 255, 0.877);
|
font-size: 1.7rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include responsive(phone) {
|
&__btn {
|
||||||
padding-top: 1rem;
|
text-transform: uppercase;
|
||||||
font-size: 1.7rem;
|
font-weight: 500;
|
||||||
}
|
cursor: pointer;
|
||||||
}
|
background-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
border: 0.5px solid $color-modal-grey-2;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
padding: 1rem 2rem 1rem 2rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
&__btn {
|
@include responsive(phone) {
|
||||||
text-transform: uppercase;
|
margin-top: 1rem;
|
||||||
font-weight: 500;
|
}
|
||||||
cursor: pointer;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #fff;
|
|
||||||
border: .5px solid $color-modal-grey-2;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
padding: 1rem 2rem 1rem 2rem;
|
|
||||||
transition: all .2s;
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
&:hover {
|
||||||
margin-top: 1rem;
|
transform: scale(1.09);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&--red {
|
||||||
transform: scale(1.09);
|
background-color: $color-red-2;
|
||||||
}
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
&--red {
|
&--icon {
|
||||||
background-color: $color-red-2;
|
fill: #fff;
|
||||||
border: none;
|
padding-right: 1rem;
|
||||||
}
|
height: 1.4rem;
|
||||||
|
width: 1.4rem;
|
||||||
&--icon {
|
}
|
||||||
fill: #fff;
|
}
|
||||||
padding-right: 1rem;
|
|
||||||
height: 1.4rem;
|
|
||||||
width: 1.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.show {
|
.show {
|
||||||
transition: .5s .3s ease-out;
|
transition: 0.3s 0.3s ease-out;
|
||||||
top: 25%;
|
top: 25%;
|
||||||
left: 0;
|
left: 0;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide {
|
.hide {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
transition: 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,38 +1,34 @@
|
|||||||
.movie {
|
.movie {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
padding-top: 6rem;
|
display: inline-block;
|
||||||
display: inline-block;
|
padding-right: 0.5rem;
|
||||||
|
padding-bottom: 7rem;
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(phone) {
|
||||||
padding-top: 4rem;
|
padding-bottom: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__column-poster {
|
&__column-poster {
|
||||||
height: 25rem;
|
height: 15rem;
|
||||||
width: 17.5rem;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
transition: transform 0.3s;
|
||||||
transition: transform .3s;
|
-webkit-transition: transform 0.3s;
|
||||||
-webkit-transition: transform .3s;
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.25);
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_port) {
|
||||||
height: 20rem;
|
transform: scale(1.15);
|
||||||
width: 14rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.25);
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
|
||||||
transform: scale(1.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__poster {
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__poster {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.movie:first-child {
|
div.movie:first-child {
|
||||||
padding-top: 15rem;
|
padding-top: 15rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +1,135 @@
|
|||||||
// Override swiper styles
|
// Override swiper styles
|
||||||
.swiper-pagination {
|
.swiper-pagination {
|
||||||
top: 0 !important;
|
top: 0 !important;
|
||||||
height: 2rem !important;
|
height: 1rem !important;
|
||||||
text-align: right !important;
|
text-align: right !important;
|
||||||
padding-right: 4rem !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-pagination-bullet {
|
.swiper-pagination-bullet {
|
||||||
background-color: rgb(255, 255, 255) !important;
|
background-color: rgb(255, 255, 255) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-container-horizontal>.swiper-pagination-bullets {
|
.swiper-horizontal > .swiper-pagination-bullets,
|
||||||
bottom: 0 !important;
|
.swiper-pagination-bullets.swiper-pagination-horizontal {
|
||||||
left: 0 !important;
|
width: 97.5% !important;
|
||||||
width: 100% !important;
|
height: 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-container-horizontal > .swiper-pagination-bullets {
|
||||||
|
bottom: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.swiper-button-prev,
|
div.swiper-button-prev,
|
||||||
div.swiper-button-next {
|
div.swiper-button-next {
|
||||||
transition: all 450ms !important;
|
transition: all 450ms !important;
|
||||||
color: rgb(255, 255, 255);
|
color: rgb(255, 255, 255);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.2) !important;
|
transform: scale(1.2) !important;
|
||||||
transition: all 450ms !important;
|
transition: all 450ms !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-slide {
|
.swiper-slide {
|
||||||
transition: all 450ms !important;
|
transition: all 450ms !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-wrapper:hover .swiper-slide {
|
.swiper-wrapper:hover .swiper-slide {
|
||||||
opacity: .3 !important;
|
z-index: 10000;
|
||||||
|
opacity: 0.3 !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.3) !important;
|
transform: scale(1.3) !important;
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_port) {
|
||||||
transform: scale(1.2) !important;
|
transform: scale(1.2) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.movieShowcase {
|
.movieShowcase {
|
||||||
background-color: $color-background;
|
background-color: transparent;
|
||||||
grid-column: 2 / 13;
|
grid-column: 2 / 13;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&__heading {
|
&__heading {
|
||||||
padding-top: 2rem;
|
position: relative;
|
||||||
}
|
top: 3rem;
|
||||||
|
// margin: 0;
|
||||||
|
padding-left: 4rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
&__container::-webkit-scrollbar {
|
&__container::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
@include responsive(phone) {
|
||||||
|
width: 98vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--movie:hover ~ &--movie {
|
||||||
|
transform: translate3d(6.5rem, 0, 0);
|
||||||
|
|
||||||
|
@include responsive(tab_port) {
|
||||||
|
transform: translate3d(3rem, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
&__container {
|
|
||||||
@include responsive(phone) {
|
@include responsive(phone) {
|
||||||
width: 98vw;
|
transform: translate3d(2.8rem, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--movie__netflix:hover ~ &--movie__netflix {
|
||||||
|
transform: translate3d(1rem, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover &--movie__netflix {
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1) !important;
|
||||||
|
|
||||||
|
@include responsive(tab_port) {
|
||||||
|
transform: scale(1.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
transform: scale(1.05) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--movie {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 450ms;
|
||||||
|
transform: center left;
|
||||||
|
padding-top: 3.5rem;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
min-height: 0;
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
padding-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--movie:hover~&--movie {
|
&-image {
|
||||||
transform: translate3d(5rem, 0, 0);
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
@include responsive(tab_port) {
|
object-fit: contain;
|
||||||
transform: translate3d(3rem, 0, 0);
|
border-radius: 5px;
|
||||||
}
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
transform: translate3d(2.8rem, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--movie__netflix:hover~&--movie__netflix {
|
&__netflix {
|
||||||
transform: translate3d(1rem, 0, 0);
|
min-height: 0;
|
||||||
|
padding-top: 3rem;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 450ms;
|
||||||
|
transform: center left;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
&:hover &--movie__netflix {
|
}
|
||||||
&:hover {
|
}
|
||||||
transform: scale(1.1) !important;
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
|
||||||
transform: scale(1.05) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
transform: scale(1.05) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--movie {
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 450ms;
|
|
||||||
transform: center left;
|
|
||||||
padding-top: 3.5rem;
|
|
||||||
padding-bottom: 4rem;
|
|
||||||
min-height: 0;
|
|
||||||
object-fit: contain;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
padding-right: .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-image {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__netflix {
|
|
||||||
min-height: 0;
|
|
||||||
padding-top: 3rem;
|
|
||||||
padding-bottom: 4rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 450ms;
|
|
||||||
transform: center left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,85 +1,165 @@
|
|||||||
.header {
|
.header {
|
||||||
background-color: $color-background;
|
background-color: $color-background;
|
||||||
grid-column: 1 / 13;
|
// grid-column: 1 / 13;
|
||||||
height: 65rem;
|
// height: 82vh;
|
||||||
display: inline-block;
|
// display: grid;
|
||||||
|
position: relative;
|
||||||
|
padding-top: 56.25%;
|
||||||
|
|
||||||
@include responsive(phone) {
|
@include responsive(phone) {
|
||||||
height: 50rem;
|
padding-top: 0;
|
||||||
}
|
height: 32rem;
|
||||||
|
}
|
||||||
|
|
||||||
&__container {
|
&__video {
|
||||||
padding-top: 20rem;
|
position: absolute;
|
||||||
padding-left: 4.5rem;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-heading {
|
||||||
|
font-size: 6rem;
|
||||||
|
position: absolute;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
top: 12rem;
|
||||||
|
left: 3.5rem;
|
||||||
|
|
||||||
@include responsive(phone) {
|
@include responsive(phone) {
|
||||||
padding-left: 1rem;
|
top: 7rem;
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-btnPlay,
|
||||||
|
&-btnMyList {
|
||||||
|
top: 40rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
color: #fff;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-left: 3.5rem;
|
||||||
|
padding-right: 3.5rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
background-color: rgba(112, 111, 111, 0.5);
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
top: 24rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-heading {
|
&-add {
|
||||||
font-size: 6rem;
|
width: 15px;
|
||||||
padding-bottom: 1rem;
|
height: 15px;
|
||||||
|
fill: #fff;
|
||||||
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-btnPlay,
|
&-play {
|
||||||
&-btnMyList {
|
width: 15px;
|
||||||
cursor: pointer;
|
height: 15px;
|
||||||
font-size: 1.6rem;
|
fill: #fff;
|
||||||
color: #fff;
|
margin-right: 1rem;
|
||||||
outline: none;
|
}
|
||||||
border: none;
|
}
|
||||||
font-weight: 700;
|
|
||||||
border-radius: .2vw;
|
|
||||||
padding-left: 3.5rem;
|
|
||||||
padding-right: 3.5rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
padding-top: 1rem;
|
|
||||||
background-color: rgba(51,51,51,.5);
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
|
|
||||||
&-add {
|
&-btnPlay {
|
||||||
width: 15px;
|
color: #000;
|
||||||
height: 15px;
|
background-color: #e6e6e6;
|
||||||
fill: #fff;
|
transition: all 0.2s;
|
||||||
margin-right: 1rem;
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
}
|
|
||||||
|
|
||||||
&-play {
|
& > * {
|
||||||
width: 15px;
|
fill: #000;
|
||||||
height: 15px;
|
}
|
||||||
fill: #fff;
|
left: 4rem;
|
||||||
margin-right: 1rem;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #000;
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
transition: all .2s;
|
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
|
|
||||||
|
|
||||||
& > * {
|
&-btnMyList {
|
||||||
fill: #000;
|
left: 18.5rem;
|
||||||
}
|
position: absolute;
|
||||||
}
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(37, 37, 37);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-btnVolume {
|
||||||
|
position: absolute;
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
top: 45rem;
|
||||||
|
right: 14rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 1rem;
|
||||||
|
border: #fff solid 1px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
@include responsive(tab_medium) {
|
||||||
|
top: 30rem;
|
||||||
|
}
|
||||||
|
@include responsive(phone) {
|
||||||
|
top: 20rem;
|
||||||
|
right: 8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-overview {
|
& > * {
|
||||||
width: 45rem;
|
stroke: #fff;
|
||||||
line-height: 1.3;
|
stroke-width: 1;
|
||||||
padding-top: 2.5rem;
|
|
||||||
font-size: 1.8rem;
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
width: 36rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--fadeBottom {
|
&:hover {
|
||||||
height: 28.5rem;
|
background-color: rgba(211, 211, 211, 0.178);
|
||||||
background-image: linear-gradient(180deg, transparent,rgba(37, 37, 37, 0.61), rgb(17, 17, 17));
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-overview {
|
||||||
|
top: 28rem;
|
||||||
|
left: 4rem;
|
||||||
|
position: absolute;
|
||||||
|
color: #fff;
|
||||||
|
width: 55rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
padding-top: 2.5rem;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
top: 10rem;
|
||||||
|
width: 20rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--fadeBottom {
|
||||||
|
// z-index: 100000000000;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
height: 10rem;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-image: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
transparent,
|
||||||
|
rgba(15, 15, 15, 0.61),
|
||||||
|
rgb(17, 17, 17),
|
||||||
|
rgb(17, 17, 17)
|
||||||
|
);
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,264 +1,252 @@
|
|||||||
|
|
||||||
.navigation {
|
.navigation {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
grid-column: 1 / 13;
|
grid-column: 1 / 13;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 6.7rem;
|
height: 6.7rem;
|
||||||
transition-timing-function: ease-in;
|
transition-timing-function: ease-in;
|
||||||
transition: all 1s;
|
transition: all 1s;
|
||||||
|
top: 0;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
|
||||||
|
&.black {
|
||||||
|
background-color: $color-background;
|
||||||
|
}
|
||||||
|
|
||||||
&.black {
|
&__container {
|
||||||
background-color: $color-background;
|
background-color: transparent;
|
||||||
}
|
display: flex;
|
||||||
|
height: 6.7rem;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 2rem;
|
||||||
|
|
||||||
&__container {
|
@include responsive(phone) {
|
||||||
background-color: transparent;
|
margin-left: 1rem;
|
||||||
margin-left: 3.8rem;
|
}
|
||||||
|
|
||||||
|
a:first-child {
|
||||||
|
padding-left: 0.2%;
|
||||||
|
color: $color-red;
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--bellLogo {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 2.2rem;
|
||||||
|
fill: red;
|
||||||
|
width: 2.2rem;
|
||||||
|
padding-right: 2.5rem;
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--userLogo {
|
||||||
|
margin-top: 2rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgb(201, 199, 78);
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
|
||||||
|
@include responsive(tab_port) {
|
||||||
|
margin-right: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover > .dropdownContent {
|
||||||
|
transition: all 0.4s;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--downArrow {
|
||||||
|
cursor: pointer;
|
||||||
|
fill: #fff;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
margin-right: 5.8rem;
|
||||||
|
|
||||||
|
@include responsive(tab_port) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--downArrow-2 {
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
fill: #fff;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
|
||||||
|
@include responsive(tab_port) {
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 1.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--logo {
|
||||||
|
margin-top: 2rem;
|
||||||
|
height: 6rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
|
||||||
|
@include responsive(tab_port) {
|
||||||
|
padding-right: 0.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-link {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: rgb(221, 221, 221);
|
||||||
|
text-decoration: none;
|
||||||
|
margin-right: 2rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 2.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgb(167, 167, 167);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--left {
|
||||||
|
flex-grow: 1;
|
||||||
|
color: #fff;
|
||||||
|
padding-right: 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 6.7rem;
|
justify-content: flex-end;
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
&__input {
|
||||||
margin-left: 1rem;
|
font-size: 1.4rem;
|
||||||
}
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
outline: none;
|
||||||
|
width: 0px;
|
||||||
|
padding: 10px;
|
||||||
|
padding-right: 2rem;
|
||||||
|
background: $color-background;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
transition: width 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
a:first-child {
|
&:focus {
|
||||||
padding-left: .2%;
|
padding-left: 4rem;
|
||||||
color: $color-red;
|
width: 22rem;
|
||||||
font-size: 4rem;
|
cursor: text;
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
@include responsive(phone) {
|
||||||
|
width: 16rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
&--bellLogo {
|
}
|
||||||
cursor: pointer;
|
|
||||||
height: 2.2rem;
|
|
||||||
fill: red;
|
|
||||||
width: 2.2rem;
|
|
||||||
padding-right: 2.5rem;
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&--userLogo {
|
|
||||||
margin-top: 2rem;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgb(201, 199, 78);
|
|
||||||
height: 3rem;
|
|
||||||
width: 3rem;
|
|
||||||
margin-right: .5rem;
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
|
||||||
margin-right: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover > .dropdownContent {
|
|
||||||
|
|
||||||
transition: all .4s;
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--downArrow {
|
|
||||||
cursor: pointer;
|
|
||||||
fill: #fff;
|
|
||||||
height: 1rem;
|
|
||||||
width: 1rem;
|
|
||||||
margin-right: 5.8rem;
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&--downArrow-2 {
|
|
||||||
display: none;
|
|
||||||
cursor: pointer;
|
|
||||||
fill: #fff;
|
|
||||||
height: 1rem;
|
|
||||||
width: 1rem;
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
|
||||||
height: 1.5rem;
|
|
||||||
width: 1.5rem;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&--logo {
|
|
||||||
margin-top: 1rem;
|
|
||||||
height: 5rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
|
|
||||||
@include responsive(tab_port) {
|
|
||||||
padding-right: .1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&-link {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: rgb(221, 221, 221);
|
|
||||||
text-decoration: none;
|
|
||||||
margin-right: 2rem;
|
|
||||||
transition: all .2s;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-right: 2.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: rgb(167, 167, 167);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&--left {
|
|
||||||
flex-grow: 1;
|
|
||||||
color: #fff;
|
|
||||||
padding-right: 2rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
&__input {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
border: none;
|
|
||||||
color: #fff;
|
|
||||||
outline: none;
|
|
||||||
width: 0px;
|
|
||||||
padding: 10px;
|
|
||||||
padding-right: 2rem;
|
|
||||||
background: $color-background;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
transition: width .5s;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
padding-left: 4rem;
|
|
||||||
width: 22rem;
|
|
||||||
cursor: text;
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
@include responsive(phone) {
|
|
||||||
width: 16rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 1.8rem;
|
width: 1.8rem;
|
||||||
height: 1.8rem;
|
height: 1.8rem;
|
||||||
transform: translateX(2.4rem) translateY(1rem);
|
transform: translateX(2.4rem) translateY(1rem);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.dropdownContent {
|
.dropdownContent {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// align-items: center;
|
// align-items: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
color: #FFF;
|
|
||||||
transition-delay: .5s;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-top: 1.8rem;
|
|
||||||
visibility: hidden;
|
|
||||||
height: 19rem;
|
|
||||||
border: 1px solid rgb(73, 73, 73);
|
|
||||||
width: 15rem;
|
|
||||||
background-color: rgba(#000, .9);
|
|
||||||
transform: translateY(4.6rem) translateX(-11rem);
|
|
||||||
|
|
||||||
&--2 {
|
color: #fff;
|
||||||
height: 10rem;
|
transition-delay: 0.5s;
|
||||||
}
|
padding-left: 1rem;
|
||||||
|
padding-top: 1.8rem;
|
||||||
|
visibility: hidden;
|
||||||
|
height: 19rem;
|
||||||
|
border: 1px solid rgb(73, 73, 73);
|
||||||
|
width: 15rem;
|
||||||
|
background-color: rgba(#000, 0.9);
|
||||||
|
transform: translateY(4.6rem) translateX(-11rem);
|
||||||
|
|
||||||
&-text {
|
&--2 {
|
||||||
width: 60%;
|
height: 10rem;
|
||||||
padding-top: 1.2rem;
|
}
|
||||||
font-size: 1.2rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-bottom: 1px solid #fff;
|
|
||||||
// border-width: 2%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-textOutside {
|
&-text {
|
||||||
font-size: 1.2rem;
|
width: 60%;
|
||||||
padding-bottom: 1rem;
|
padding-top: 1.2rem;
|
||||||
}
|
font-size: 1.2rem;
|
||||||
|
|
||||||
&--user {
|
&:hover {
|
||||||
height: 3rem;
|
border-bottom: 1px solid #fff;
|
||||||
|
// border-width: 2%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-textOutside {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--user {
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: rgb(64, 168, 228);
|
||||||
|
|
||||||
|
&-2 {
|
||||||
|
background-color: rgb(230, 145, 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-3 {
|
||||||
|
background-color: rgb(123, 230, 96);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
border-radius: 5px;
|
font-size: 1.2rem;
|
||||||
background-color: rgb(64, 168, 228);
|
transform: translateX(4rem) translateY(-2rem);
|
||||||
|
|
||||||
&-2 {
|
&:hover {
|
||||||
background-color: rgb(230, 145, 48);
|
border-bottom: 1px solid #fff;
|
||||||
|
border-width: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
&-3 {
|
}
|
||||||
background-color: rgb(123, 230, 96);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-text {
|
|
||||||
width: 3rem;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
transform: translateX(4rem) translateY(-2rem);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-bottom: 1px solid #fff;
|
|
||||||
border-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdownContainer {
|
.dropdownContainer {
|
||||||
|
height: 100%;
|
||||||
height: 100%;
|
justify-content: center;
|
||||||
justify-content: center;
|
align-content: center;
|
||||||
align-content: center;
|
|
||||||
|
|
||||||
&:hover .dropdownContent {
|
&:hover .dropdownContent {
|
||||||
transition: all .4s;
|
transition: all 0.4s;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pseudo-link {
|
.pseudo-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@include responsive(tab_port) {
|
@include responsive(tab_port) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,98 +1,150 @@
|
|||||||
import axios from '../../axios-movies';
|
import axios from '../../axios-movies'
|
||||||
|
|
||||||
export const FETCH_TRENDING = 'FETCH_TRENDING';
|
export const FETCH_TRENDING = 'FETCH_TRENDING'
|
||||||
export const FETCH_NETFLIX_ORIGINALS = 'FETCH_NETFLIX_ORIGINALS';
|
export const FETCH_NETFLIX_ORIGINALS = 'FETCH_NETFLIX_ORIGINALS'
|
||||||
export const FETCH_TOP_RATED = 'FETCH_TOP_RATED';
|
export const FETCH_TOP_RATED = 'FETCH_TOP_RATED'
|
||||||
export const FETCH_ACTION_MOVIES = 'FETCH_ACTION_MOVIES';
|
export const FETCH_ACTION_MOVIES = 'FETCH_ACTION_MOVIES'
|
||||||
export const FETCH_COMEDY_MOVIES = 'FETCH_COMEDY_MOVIES';
|
export const FETCH_COMEDY_MOVIES = 'FETCH_COMEDY_MOVIES'
|
||||||
export const FETCH_HORROR_MOVIES = 'FETCH_HORROR_MOVIES';
|
export const FETCH_HORROR_MOVIES = 'FETCH_HORROR_MOVIES'
|
||||||
export const FETCH_ROMANCE_MOVIES = 'FETCH_ROMANCE_MOVIES';
|
export const FETCH_ROMANCE_MOVIES = 'FETCH_ROMANCE_MOVIES'
|
||||||
export const FETCH_DOCUMENTARIES = 'FETCH_DOCUMENTARIES';
|
export const FETCH_DOCUMENTARIES = 'FETCH_DOCUMENTARIES'
|
||||||
|
// movie details
|
||||||
|
export const FETCH_MOVIE_DETAILS = 'FETCH_MOVIE_DETAILS'
|
||||||
|
export const FETCH_MOVIE_DETAILS_SUCCESS = 'FETCH_MOVIE_DETAILS_SUCCESS'
|
||||||
|
export const FETCH_MOVIE_DETAILS_FAIL = 'FETCH_MOVIE_DETAILS_FAIL'
|
||||||
|
// search
|
||||||
|
export const FETCH_SEARCH_MOVIE = 'FETCH_SEARCH_MOVIE'
|
||||||
|
export const FETCH_SEARCH_MOVIE_FAIL = 'FETCH_SEARCH_MOVIE_FAIL'
|
||||||
|
export const FETCH_SEARCH_MOVIE_SUCCESS = 'FETCH_SEARCH_MOVIE_SUCCESS'
|
||||||
|
|
||||||
export function fetchTrending() {
|
const media_type = {
|
||||||
const request = axios.get(
|
tv: 'tv',
|
||||||
`/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`
|
movie: 'movie',
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: FETCH_TRENDING,
|
|
||||||
payload: request,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchNetflixOriginals() {
|
export const fetchMovieDetails = (mediaType, mediaId) => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
try {
|
||||||
);
|
dispatch({ type: FETCH_MOVIE_DETAILS })
|
||||||
|
let urlPath
|
||||||
|
if (mediaType === media_type.movie)
|
||||||
|
urlPath = `/movie/${mediaId}?api_key=${process.env.API_KEY}`
|
||||||
|
if (mediaType === media_type.tv)
|
||||||
|
urlPath = `/tv/${mediaId}?api_key=${process.env.API_KEY}`
|
||||||
|
|
||||||
return {
|
const request = await axios.get(urlPath)
|
||||||
type: FETCH_NETFLIX_ORIGINALS,
|
dispatch({ type: FETCH_MOVIE_DETAILS_SUCCESS, payload: request })
|
||||||
payload: request,
|
} catch (error) {
|
||||||
};
|
console.log('error', error)
|
||||||
|
dispatch({ type: FETCH_MOVIE_DETAILS_FAIL })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchTopRated() {
|
export const fetchSearchMovie = (searchTerm) => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`
|
try {
|
||||||
);
|
dispatch({ type: FETCH_SEARCH_MOVIE })
|
||||||
|
const request = await axios.get(
|
||||||
return {
|
`/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchTerm}`
|
||||||
type: FETCH_TOP_RATED,
|
)
|
||||||
payload: request,
|
dispatch({ type: FETCH_SEARCH_MOVIE_SUCCESS, payload: request })
|
||||||
};
|
} catch (error) {
|
||||||
|
dispatch({ type: FETCH_SEARCH_MOVIE_FAIL })
|
||||||
|
console.log('error', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchActionMovies() {
|
export const fetchNetflixOriginals = () => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
try {
|
||||||
);
|
const request = await axios.get(
|
||||||
|
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
dispatch({ type: FETCH_NETFLIX_ORIGINALS, payload: request })
|
||||||
type: FETCH_ACTION_MOVIES,
|
} catch (error) {
|
||||||
payload: request,
|
console.log('error', error)
|
||||||
};
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchComedyMovies() {
|
export const fetchTrending = () => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=35`
|
try {
|
||||||
);
|
const request = await axios.get(
|
||||||
|
`/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`
|
||||||
return {
|
)
|
||||||
type: FETCH_COMEDY_MOVIES,
|
dispatch({ type: FETCH_TRENDING, payload: request })
|
||||||
payload: request,
|
} catch (error) {}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchHorrorMovies() {
|
export const fetchTopRated = () => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=27`
|
try {
|
||||||
);
|
const request = await axios.get(
|
||||||
|
`/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`
|
||||||
return {
|
)
|
||||||
type: FETCH_HORROR_MOVIES,
|
dispatch({ type: FETCH_TOP_RATED, payload: request })
|
||||||
payload: request,
|
} catch (error) {}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchRomanceMovies() {
|
export const fetchActionMovies = () => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=10749`
|
try {
|
||||||
);
|
const request = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
dispatch({ type: FETCH_ACTION_MOVIES, payload: request })
|
||||||
type: FETCH_ROMANCE_MOVIES,
|
} catch (error) {}
|
||||||
payload: request,
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchDocumentaries() {
|
export const fetchComedyMovies = () => {
|
||||||
const request = axios.get(
|
return async (dispatch) => {
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=99`
|
try {
|
||||||
);
|
const request = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=35`
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
dispatch({ type: FETCH_COMEDY_MOVIES, payload: request })
|
||||||
type: FETCH_DOCUMENTARIES,
|
} catch (error) {}
|
||||||
payload: request,
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export const fetchHorrorMovies = () => {
|
||||||
|
return async (dispatch) => {
|
||||||
|
try {
|
||||||
|
const request = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=27`
|
||||||
|
)
|
||||||
|
dispatch({ type: FETCH_HORROR_MOVIES, payload: request })
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchRomanceMovies = () => {
|
||||||
|
return async (dispatch) => {
|
||||||
|
try {
|
||||||
|
const request = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=10749`
|
||||||
|
)
|
||||||
|
dispatch({ type: FETCH_ROMANCE_MOVIES, payload: request })
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchDocumentaries = () => {
|
||||||
|
return async (dispatch) => {
|
||||||
|
try {
|
||||||
|
const request = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=99`
|
||||||
|
)
|
||||||
|
dispatch({ type: FETCH_DOCUMENTARIES, payload: request })
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux'
|
||||||
import TrendingReducer from './reducerTrending';
|
import TrendingReducer from './reducerTrending'
|
||||||
import NetflixOriginalsReducer from './reducerNetflixOriginals';
|
import NetflixOriginalsReducer from './reducerNetflixOriginals'
|
||||||
import TopRatedReducer from './reducerTopRated';
|
import TopRatedReducer from './reducerTopRated'
|
||||||
import ActionMoviesReducer from './reducerActionMovies';
|
import ActionMoviesReducer from './reducerActionMovies'
|
||||||
import ComedyMoviesReducer from './reducerComedyMovies';
|
import ComedyMoviesReducer from './reducerComedyMovies'
|
||||||
import HorrorMoviesReducer from './reducerHorrorMovies';
|
import HorrorMoviesReducer from './reducerHorrorMovies'
|
||||||
import RomanceMoviesReducer from './reducerRomanceMovies';
|
import RomanceMoviesReducer from './reducerRomanceMovies'
|
||||||
import DocumentaryReducer from './reducerDocumentary';
|
import DocumentaryReducer from './reducerDocumentary'
|
||||||
|
import SearchMovieReducer from './reducerSearchMovie'
|
||||||
|
import MovieDetailsReducer from './reducerMovieDetails'
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
trending: TrendingReducer,
|
trending: TrendingReducer,
|
||||||
@@ -17,6 +19,8 @@ const rootReducer = combineReducers({
|
|||||||
horror: HorrorMoviesReducer,
|
horror: HorrorMoviesReducer,
|
||||||
romance: RomanceMoviesReducer,
|
romance: RomanceMoviesReducer,
|
||||||
documentary: DocumentaryReducer,
|
documentary: DocumentaryReducer,
|
||||||
});
|
searchMovie: SearchMovieReducer,
|
||||||
|
movieDetails: MovieDetailsReducer,
|
||||||
|
})
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer
|
||||||
|
|||||||
24
src/store/reducers/reducerMovieDetails.js
Normal file
24
src/store/reducers/reducerMovieDetails.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
FETCH_MOVIE_DETAILS,
|
||||||
|
FETCH_MOVIE_DETAILS_SUCCESS,
|
||||||
|
FETCH_MOVIE_DETAILS_FAIL,
|
||||||
|
} from '../actions/index'
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
isLoading: false,
|
||||||
|
movieDetails: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case FETCH_MOVIE_DETAILS:
|
||||||
|
return { ...state, isLoading: true }
|
||||||
|
case FETCH_MOVIE_DETAILS_FAIL:
|
||||||
|
return { ...state, isLoading: false }
|
||||||
|
case FETCH_MOVIE_DETAILS_SUCCESS:
|
||||||
|
const movieDetails = action.payload.data
|
||||||
|
return { ...state, movieDetails, isLoading: false }
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { FETCH_NETFLIX_ORIGINALS } from '../actions/index';
|
import { FETCH_NETFLIX_ORIGINALS } from '../actions/index'
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
export default function (state = {}, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case FETCH_NETFLIX_ORIGINALS:
|
case FETCH_NETFLIX_ORIGINALS:
|
||||||
const data = action.payload.data.results;
|
const data = action.payload.data.results
|
||||||
return { ...state, data };
|
return { ...state, data }
|
||||||
default:
|
default:
|
||||||
return state;
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/store/reducers/reducerSearchMovie.js
Normal file
24
src/store/reducers/reducerSearchMovie.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
FETCH_SEARCH_MOVIE,
|
||||||
|
FETCH_SEARCH_MOVIE_FAIL,
|
||||||
|
FETCH_SEARCH_MOVIE_SUCCESS,
|
||||||
|
} from '../actions/index'
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
isLoading: false,
|
||||||
|
searchResults: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case FETCH_SEARCH_MOVIE:
|
||||||
|
return { ...state, isLoading: true }
|
||||||
|
case FETCH_SEARCH_MOVIE_FAIL:
|
||||||
|
return { ...state, isLoading: false }
|
||||||
|
case FETCH_SEARCH_MOVIE_SUCCESS:
|
||||||
|
const searchResults = action.payload.data.results
|
||||||
|
return { ...state, searchResults, isLoading: false }
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
const HtmlWebPackPlugin = require('html-webpack-plugin');
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
@@ -42,7 +42,7 @@ module.exports = () => {
|
|||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
include: /node_modules/,
|
include: /node_modules/,
|
||||||
loaders: ['style-loader', 'css-loader'],
|
use: ['style-loader', 'css-loader'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
@@ -76,7 +76,7 @@ module.exports = () => {
|
|||||||
historyApiFallback: true,
|
historyApiFallback: true,
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
fs: 'empty',
|
global: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin(envKeys),
|
new webpack.DefinePlugin(envKeys),
|
||||||
@@ -84,15 +84,23 @@ module.exports = () => {
|
|||||||
template: './src/index.html',
|
template: './src/index.html',
|
||||||
filename: './index.html',
|
filename: './index.html',
|
||||||
}),
|
}),
|
||||||
new CopyWebpackPlugin([
|
// new CopyWebpackPlugin({
|
||||||
{ from: 'src/static/images', to: 'static/images' },
|
// patterns: [[ { from: 'src/static/images', to: 'static/images' }],
|
||||||
]),
|
// }),
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: 'src/static/images',
|
||||||
|
to: 'static/images',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
// Options similar to the same options in webpackOptions.output
|
// Options similar to the same options in webpackOptions.output
|
||||||
// both options are optional
|
// both options are optional
|
||||||
filename: 'main.css',
|
filename: 'main.css',
|
||||||
}),
|
}),
|
||||||
new CleanWebpackPlugin(['dist']),
|
new CleanWebpackPlugin(),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user