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
|
||||
main.css,1596823315373,1fe11d275cdaeefa9ac00b547cc17a7559f641d94302b061941c58441778dc90
|
||||
static/images/add.svg,1596823315374,ef7cf8e9df96200815d767e6b3b767ef092dd338b4851d2f35966911dab09e4c
|
||||
static/images/bell-logo.svg,1596823315374,5ef07d3b72a99ccebfff2fb9bc1a80513d59376038055b606deff8133c3e3b56
|
||||
static/images/bell.svg,1596823315375,6766dfa1ef7da3acfab70cdb2bbee5e6861fc0cdf8afbaf1cf7c387e4df415a4
|
||||
static/images/cancel-music.svg,1596823315375,1bbd7eef0b56c5d4cec9993f3762a384447ca99392778a87292333b82f8a2f39
|
||||
static/images/drop-down-arrow.svg,1596823315374,7ebe79b34f9fa2590b4beb29e6e6e8d0b0fff722ff0e3071ad36d3467c045743
|
||||
static/images/play-button.svg,1596823315375,d43cf31799ae2e480227ae79d858b997dfb0b54f67b1d3a3a1c18ca1dd927251
|
||||
static/images/search-icon.svg,1596823315375,fcad2b6afe31083f5b452f601b1ca8669f6f768deecadf07e9d647269296012a
|
||||
8562b6565f5ae1db5e4af40d85b4ed2d.png,1596823315373,ddacf16aa0704b6ff5ed27a0ff5416eff337a05915beb9c10504639f2f105ec1
|
||||
static/images/Netflix_Logo_RGB.png,1596823315375,ddacf16aa0704b6ff5ed27a0ff5416eff337a05915beb9c10504639f2f105ec1
|
||||
bundle.js,1596823315373,033e3afff30e48c0cc6b812ab7b48ad066cbc65e6442c1f10109e6a55a0e103b
|
||||
static/images/header-bg.jpg,1596823315376,f4cfcb4caa304a777e03d623c68bf00d22f9c21a55d0a6fb088889e3c2867d40
|
||||
bundle.js.LICENSE.txt,1633809037651,e6867f9fa01d54ddc1cf8eac25d56d3ae909da438be77aa5454f85532e879796
|
||||
index.html,1633809037654,b96adf5936968f349bea11263a42d495b541d0bdac04424944a25ffd5253c4c4
|
||||
static/images/add.svg,1633809037652,ef7cf8e9df96200815d767e6b3b767ef092dd338b4851d2f35966911dab09e4c
|
||||
static/images/bell.svg,1633809037652,6766dfa1ef7da3acfab70cdb2bbee5e6861fc0cdf8afbaf1cf7c387e4df415a4
|
||||
static/images/bell-logo.svg,1633809037652,5ef07d3b72a99ccebfff2fb9bc1a80513d59376038055b606deff8133c3e3b56
|
||||
static/images/cancel-music.svg,1633809037652,1bbd7eef0b56c5d4cec9993f3762a384447ca99392778a87292333b82f8a2f39
|
||||
main.css,1633809037650,d5d7ec1073fd2f17b3fed17b0deaa04cb7678f4b8ea9b089a7cc336886753973
|
||||
static/images/mute.svg,1633809037652,dbf382f4fa81759d125e5a526fab4464ba293c4a0dedfba395bcb64e5191ffbb
|
||||
static/images/play-button.svg,1633809037652,d43cf31799ae2e480227ae79d858b997dfb0b54f67b1d3a3a1c18ca1dd927251
|
||||
static/images/search-icon.svg,1633809037652,fcad2b6afe31083f5b452f601b1ca8669f6f768deecadf07e9d647269296012a
|
||||
static/images/unmute.svg,1633809037652,f571ee9569a235ed5102856c1a1485ce0d1f22a739f291fe02f5d5c893ddd840
|
||||
static/images/drop-down-arrow.svg,1633809037652,7ebe79b34f9fa2590b4beb29e6e6e8d0b0fff722ff0e3071ad36d3467c045743
|
||||
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
|
||||
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
|
||||
|
||||
- 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!
|
||||
|
||||
### Tools used:
|
||||
- Webpack
|
||||
### Tools used
|
||||
|
||||
- Webpack
|
||||
- Axios
|
||||
- Redux & React
|
||||
- Sass (grid & flexbox)
|
||||
@@ -13,24 +14,22 @@ This project is a simplified front end clone of Netflix. It was created with Rea
|
||||
- Swiper JS
|
||||
|
||||
### Runing Project Locally
|
||||
|
||||
- Install dependencies: run `npm install` in root project
|
||||
- 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`
|
||||
- Run project: `npm run dev`
|
||||
|
||||
### User Stories:
|
||||
### User Stories
|
||||
|
||||
- User can search for movies and TV shows on TMDb
|
||||
- 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.
|
||||
- The webpage adapts to any screen size.
|
||||
- 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.
|
||||
- The webpage adapts to any screen size.
|
||||
|
||||
### Video Walktrough
|
||||
|
||||
|
||||
### Video Walktrough
|
||||

|
||||
|
||||
|
||||
|
||||
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",
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server --mode development --config webpack.config.js --open",
|
||||
"build": "webpack -p",
|
||||
"build": "webpack --mode production",
|
||||
"lint": "eslint --fix . && echo 'Lint complete.'"
|
||||
},
|
||||
"engines": {
|
||||
"node": "10.1.0",
|
||||
"npm": "6.4.1",
|
||||
"node": "v14.15.4",
|
||||
"npm": "7.24.0",
|
||||
"watch": "watch 'clear && npm run -s test | tap-nirvana && npm run -s lint' src"
|
||||
},
|
||||
"author": "Andres Alcocer",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.18.1",
|
||||
"lodash": "^4.17.19",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-redux": "^6.0.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"redux": "^4.0.1",
|
||||
"axios": "^0.22.0",
|
||||
"firebase": "^9.1.2",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-player": "^2.9.0",
|
||||
"react-redux": "^7.2.5",
|
||||
"react-router-dom": "^5.3.0",
|
||||
"redux": "^4.1.1",
|
||||
"redux-promise": "^0.6.0",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"swiper": "^6.1.1"
|
||||
"swiper": "^7.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.1.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.14.5",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.14.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/polyfill": "^7.0.0-beta.51",
|
||||
"@babel/preset-env": "^7.1.5",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-loader": "^8.0.4",
|
||||
"clean-webpack-plugin": "^1.0.0",
|
||||
"copy-webpack-plugin": "^4.6.0",
|
||||
"css-loader": "^1.0.1",
|
||||
"dotenv": "^6.2.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb": "^18.1.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.20.0",
|
||||
"eslint-plugin-react-hooks": "^2.5.1",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"babel-loader": "^8.2.2",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"css-loader": "^6.3.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb": "^18.2.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^2.0.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"image-webpack-loader": "^4.5.0",
|
||||
"mini-css-extract-plugin": "^0.4.4",
|
||||
"node-sass": "^4.14.1",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
"prettier": "^2.0.5",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-loader": "^2.1.2",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"image-webpack-loader": "^8.0.1",
|
||||
"mini-css-extract-plugin": "^2.4.1",
|
||||
"node-sass": "^6.0.1",
|
||||
"optimize-css-assets-webpack-plugin": "^6.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"react-owl-carousel2": "^0.3.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"svg-inline-loader": "^0.8.0",
|
||||
"sass-loader": "^12.1.0",
|
||||
"style-loader": "^3.3.0",
|
||||
"svg-inline-loader": "^0.8.2",
|
||||
"svg-react-loader": "^0.4.6",
|
||||
"tap-nirvana": "^1.1.0",
|
||||
"uglifyjs-webpack-plugin": "^2.0.1",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"watch": "^1.0.2",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
"webpack": "^5.57.1",
|
||||
"webpack-cli": "^4.8.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 = () => (
|
||||
<footer className="footer">
|
||||
<div className="footer__copyright">
|
||||
© 2018 Made with ❤️ by{' '}
|
||||
<a className="footer__copyright--link" href="http://andresio.com">
|
||||
<footer className='footer'>
|
||||
<div className='footer__copyright'>
|
||||
© 2021 Made with ❤️ by{' '}
|
||||
<a className='footer__copyright--link' href='https://github.com/AndresXI'>
|
||||
{' '}
|
||||
Andres Alcocer
|
||||
</a>
|
||||
</div>
|
||||
</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 AddLogo from '../static/images/add.svg';
|
||||
import PlayLogo from '../static/images/play-button.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 backgroundStyle = {
|
||||
backgroundSize: 'cover',
|
||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${props.movie.backdrop_path})`,
|
||||
backgroundPosition: 'center',
|
||||
};
|
||||
const Header = ({ movie: { name, overview } }) => {
|
||||
const [isMuted, setIsMuted] = useState(true)
|
||||
|
||||
return (
|
||||
<header style={backgroundStyle} className="header">
|
||||
<div className="header__container">
|
||||
<h1 className="header__container-heading">{props.movie.name}</h1>
|
||||
<button
|
||||
onClick={() => alert('not a movie!')}
|
||||
className="header__container-btnPlay"
|
||||
>
|
||||
<PlayLogo className="header__container-btnMyList-play" />
|
||||
Play
|
||||
</button>
|
||||
<header className='header'>
|
||||
<ReactPlayer
|
||||
playing={true}
|
||||
loop={true}
|
||||
width='100%'
|
||||
height='100%'
|
||||
volume={1}
|
||||
muted={isMuted}
|
||||
className='header__video'
|
||||
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">
|
||||
<AddLogo className="header__container-btnMyList-add" />
|
||||
My List
|
||||
</button>
|
||||
<p className="header__container-overview">{props.movie.overview}</p>
|
||||
</div>
|
||||
<div className="header--fadeBottom"></div>
|
||||
{isMuted ? (
|
||||
<MuteIcon
|
||||
onClick={() => setIsMuted(false)}
|
||||
className='header__container-btnVolume'
|
||||
/>
|
||||
) : (
|
||||
<UnmuteIcon
|
||||
onClick={() => setIsMuted(true)}
|
||||
className='header__container-btnVolume'
|
||||
/>
|
||||
)}
|
||||
<p className='header__container-overview'>{overview}</p>
|
||||
<div className='header__container--fadeBottom'></div>
|
||||
</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'
|
||||
|
||||
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 (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
<div className='modal__container'>
|
||||
<h1 className='modal__title'>{title || name}</h1>
|
||||
<p className='modal__info'>
|
||||
<span className='modal__rating'>Rating: {vote_average * 10}% </span>
|
||||
Release date: {release_date || first_air_date} Runtime:{' '}
|
||||
{runtime || episode_run_time}m
|
||||
</p>
|
||||
<p className='modal__episode'>
|
||||
{number_of_episodes ? ' Episodes: ' + number_of_episodes : ''}
|
||||
{number_of_seasons ? ' Seasons: ' + number_of_seasons : ''}
|
||||
</p>
|
||||
<p className='modal__overview'>{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>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieDetails
|
||||
|
||||
@@ -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) =>
|
||||
props.show ? (
|
||||
<div onClick={props.toggleBackdrop} className="backdrop"></div>
|
||||
) : null;
|
||||
const backdrop = ({ toggleBackdrop, show }) =>
|
||||
show ? (
|
||||
<div
|
||||
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 = {
|
||||
backgroundSize: 'cover',
|
||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${
|
||||
props.movie.backdrop_path || props.movie.poster_path
|
||||
})`,
|
||||
};
|
||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${backgroundImage})`,
|
||||
}
|
||||
|
||||
return (
|
||||
<Aux>
|
||||
<Backdrop show={props.show} toggleBackdrop={props.modalClosed} />
|
||||
<div>
|
||||
<Backdrop show={show} toggleBackdrop={modalClosed} />
|
||||
<div
|
||||
style={backgroundStyle}
|
||||
className={props.show ? 'modal show' : 'modal hide'}
|
||||
className={show ? 'modal show' : 'modal hide'}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</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 ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import reducers from './store/reducers';
|
||||
import promise from 'redux-promise';
|
||||
import '@babel/polyfill';
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Provider } from 'react-redux'
|
||||
import { createStore, applyMiddleware } from 'redux'
|
||||
import ReduxThunk from 'redux-thunk'
|
||||
import '@babel/polyfill'
|
||||
|
||||
import App from './containers/App';
|
||||
import 'swiper/swiper-bundle.min.css';
|
||||
// import 'swiper/components/navigation/navigation.scss';
|
||||
// import 'swiper/components/pagination/pagination.scss';
|
||||
// import 'swiper/components/scrollbar/scrollbar.scss';
|
||||
import reducers from './store/reducers'
|
||||
import AppRouter from './AppRouter'
|
||||
|
||||
// Import Swiper styles
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/navigation'
|
||||
import 'swiper/css/pagination'
|
||||
// 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 = (
|
||||
<Provider store={createStoreWithMiddleware(reducers)}>
|
||||
<App />
|
||||
<Provider store={store}>
|
||||
<AppRouter />
|
||||
</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';
|
||||
|
||||
export default function NotFound() {
|
||||
const NotFound = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>NOT FOUND</h1>
|
||||
</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(html), :global(body), :global(div), :global(span), :global(applet), :global(object), :global(iframe),
|
||||
:global(h1), :global(h2), :global(h3), :global(h4), :global(h5), :global(h6), :global(p), :global(blockquote), :global(pre),
|
||||
:global(a), :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;
|
||||
:global(html),
|
||||
:global(body),
|
||||
:global(div),
|
||||
:global(span),
|
||||
:global(applet),
|
||||
:global(object),
|
||||
:global(iframe),
|
||||
:global(h1),
|
||||
:global(h2),
|
||||
:global(h3),
|
||||
:global(h4),
|
||||
:global(h5),
|
||||
:global(h6),
|
||||
:global(p),
|
||||
:global(blockquote),
|
||||
:global(pre),
|
||||
:global(a),
|
||||
: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 */
|
||||
:global(article), :global(aside), :global(details), :global(figcaption), :global(figure),
|
||||
:global(footer), :global(header), :global(hgroup), :global(menu), :global(nav), :global(section) {
|
||||
display: block;
|
||||
:global(article),
|
||||
:global(aside),
|
||||
:global(details),
|
||||
:global(figcaption),
|
||||
:global(figure),
|
||||
:global(footer),
|
||||
:global(header),
|
||||
:global(hgroup),
|
||||
:global(menu),
|
||||
:global(nav),
|
||||
:global(section) {
|
||||
display: block;
|
||||
}
|
||||
:global(body) {
|
||||
line-height: 1;
|
||||
line-height: 1;
|
||||
}
|
||||
:global(ol), :global(ul) {
|
||||
list-style: none;
|
||||
:global(ol),
|
||||
:global(ul) {
|
||||
list-style: none;
|
||||
}
|
||||
:global(blockquote), :global(q) {
|
||||
quotes: none;
|
||||
:global(blockquote),
|
||||
: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) {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Hind', sans-serif;
|
||||
box-sizing: border-box;
|
||||
background-color: $color-background;
|
||||
font-family: 'Hind', sans-serif;
|
||||
box-sizing: border-box;
|
||||
background-color: $color-background;
|
||||
}
|
||||
|
||||
html {
|
||||
// this defines what 1rem is --> font root size
|
||||
font-size: 62.5%; // 10/16, 1rem = 10px;
|
||||
|
||||
@include responsive(tab_port) {
|
||||
font-size: 50%;
|
||||
}
|
||||
// this defines what 1rem is --> font root size
|
||||
font-size: 62.5%; // 10/16, 1rem = 10px;
|
||||
|
||||
@include responsive(tab_port) {
|
||||
font-size: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
// GRID AREA
|
||||
// GRID AREA
|
||||
.container {
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: min-content min-content min-content min-content;
|
||||
grid-template-columns: 4% repeat(10, 1fr) 4%;
|
||||
// display: grid;
|
||||
// grid-template-rows: min-content min-content min-content min-content;
|
||||
// grid-template-columns: 0 repeat(10, 1fr);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
.backdrop {
|
||||
width: 120%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: rgba($color-background, .7);
|
||||
transition: all .3s;
|
||||
}
|
||||
width: 120%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
left: 0;
|
||||
top: 0;
|
||||
animation: fadeIn 1s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
|
||||
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 {
|
||||
position: fixed;
|
||||
z-index: 500;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: #fff;
|
||||
height: 52rem;
|
||||
opacity: 0;
|
||||
box-shadow: 0 1.5rem 4rem rgba($color-dark, .15);
|
||||
transition: all .3s;
|
||||
position: fixed;
|
||||
z-index: 500;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: #fff;
|
||||
height: 52rem;
|
||||
opacity: 0;
|
||||
box-shadow: 0 1.5rem 4rem rgba($color-dark, 0.15);
|
||||
transition: all 0.3s;
|
||||
|
||||
@include responsive(tab_port) {
|
||||
height: 38rem;
|
||||
}
|
||||
@include responsive(tab_port) {
|
||||
height: 38rem;
|
||||
}
|
||||
|
||||
@include responsive(phone) {
|
||||
height: 50rem;
|
||||
}
|
||||
@include responsive(phone) {
|
||||
height: 50rem;
|
||||
}
|
||||
|
||||
&__container {
|
||||
background: linear-gradient(90deg, #000 50%, transparent);
|
||||
width: 70%;
|
||||
padding-top: 3rem;
|
||||
height: 100%;
|
||||
padding-left: 5rem;
|
||||
|
||||
&__container {
|
||||
background: linear-gradient(90deg, #000 50%, transparent);
|
||||
width: 70%;
|
||||
padding-top: 3rem;
|
||||
height: 100%;
|
||||
padding-left: 5rem;
|
||||
@include responsive(tab_port) {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgb(0, 0, 0) 55%,
|
||||
rgba(0, 0, 0, 0.733),
|
||||
transparent
|
||||
);
|
||||
width: 88%;
|
||||
}
|
||||
|
||||
@include responsive(tab_port) {
|
||||
background: linear-gradient(90deg, rgb(0, 0, 0) 55%, rgba(0, 0, 0, 0.733), transparent);
|
||||
width: 88%;
|
||||
}
|
||||
@include responsive(tab_medium) {
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.966) 65%, transparent);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include responsive(tab_medium) {
|
||||
background: linear-gradient(90deg, rgba(0, 0, 0, 0.966) 65%, transparent);
|
||||
width: 100%;
|
||||
}
|
||||
@include responsive(phone) {
|
||||
padding-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive(phone) {
|
||||
padding-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
&__title {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 4rem;
|
||||
}
|
||||
&__rating {
|
||||
font-size: 2rem;
|
||||
color: $color-green-modal;
|
||||
}
|
||||
|
||||
&__rating {
|
||||
font-size: 2rem;
|
||||
color: $color-green-modal;
|
||||
}
|
||||
&__info {
|
||||
padding-top: 1.6rem;
|
||||
font-size: 2rem;
|
||||
|
||||
&__info {
|
||||
padding-top: 1.6rem;
|
||||
font-size: 2rem;
|
||||
@include responsive(phone) {
|
||||
padding-top: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive(phone) {
|
||||
padding-top: 1.2rem;
|
||||
}
|
||||
}
|
||||
&__episode {
|
||||
padding-top: 0.5rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
&__episode {
|
||||
padding-top: .5rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
&__overview {
|
||||
color: $color-modal-grey-2;
|
||||
padding-top: 2rem;
|
||||
font-size: 2rem;
|
||||
hyphens: auto;
|
||||
width: 60%;
|
||||
line-height: 1.2;
|
||||
|
||||
&__overview {
|
||||
color: $color-modal-grey-2;
|
||||
padding-top: 2rem;
|
||||
font-size: 2rem;
|
||||
hyphens: auto;
|
||||
@include responsive(lg_desktop) {
|
||||
width: 60%;
|
||||
line-height: 1.2;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
@include responsive(lg_desktop) {
|
||||
width: 60%;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
@include responsive(tab_port) {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
@include responsive(tab_port) {
|
||||
width: 80%;
|
||||
}
|
||||
@include responsive(tab_medium) {
|
||||
width: 95%;
|
||||
color: rgba(255, 255, 255, 0.877);
|
||||
}
|
||||
|
||||
@include responsive(tab_medium) {
|
||||
width: 95%;
|
||||
color: rgba(255, 255, 255, 0.877);
|
||||
}
|
||||
@include responsive(phone) {
|
||||
padding-top: 1rem;
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive(phone) {
|
||||
padding-top: 1rem;
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
}
|
||||
&__btn {
|
||||
text-transform: uppercase;
|
||||
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 {
|
||||
text-transform: uppercase;
|
||||
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) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@include responsive(phone) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
&:hover {
|
||||
transform: scale(1.09);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.09);
|
||||
}
|
||||
&--red {
|
||||
background-color: $color-red-2;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&--red {
|
||||
background-color: $color-red-2;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&--icon {
|
||||
fill: #fff;
|
||||
padding-right: 1rem;
|
||||
height: 1.4rem;
|
||||
width: 1.4rem;
|
||||
}
|
||||
}
|
||||
&--icon {
|
||||
fill: #fff;
|
||||
padding-right: 1rem;
|
||||
height: 1.4rem;
|
||||
width: 1.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show {
|
||||
transition: .5s .3s ease-out;
|
||||
top: 25%;
|
||||
left: 0;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: 0.3s 0.3s ease-out;
|
||||
top: 25%;
|
||||
left: 0;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
visibility: hidden;
|
||||
transition: 0.3s ease-out;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
.movie {
|
||||
flex: 1 1 auto;
|
||||
padding-top: 6rem;
|
||||
display: inline-block;
|
||||
flex: 1 1 auto;
|
||||
display: inline-block;
|
||||
padding-right: 0.5rem;
|
||||
padding-bottom: 7rem;
|
||||
|
||||
@include responsive(tab_port) {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
@include responsive(phone) {
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
|
||||
&__column-poster {
|
||||
height: 25rem;
|
||||
width: 17.5rem;
|
||||
cursor: pointer;
|
||||
transition: transform .3s;
|
||||
-webkit-transition: transform .3s;
|
||||
&__column-poster {
|
||||
height: 15rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
-webkit-transition: transform 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.25);
|
||||
|
||||
@include responsive(tab_port) {
|
||||
height: 20rem;
|
||||
width: 14rem;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
&: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 {
|
||||
padding-top: 15rem;
|
||||
padding-top: 15rem;
|
||||
}
|
||||
|
||||
@@ -1,125 +1,135 @@
|
||||
// Override swiper styles
|
||||
.swiper-pagination {
|
||||
top: 0 !important;
|
||||
height: 2rem !important;
|
||||
text-align: right !important;
|
||||
padding-right: 4rem !important;
|
||||
top: 0 !important;
|
||||
height: 1rem !important;
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet {
|
||||
background-color: rgb(255, 255, 255) !important;
|
||||
background-color: rgb(255, 255, 255) !important;
|
||||
}
|
||||
|
||||
.swiper-container-horizontal>.swiper-pagination-bullets {
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
.swiper-horizontal > .swiper-pagination-bullets,
|
||||
.swiper-pagination-bullets.swiper-pagination-horizontal {
|
||||
width: 97.5% !important;
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.swiper-container-horizontal > .swiper-pagination-bullets {
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
div.swiper-button-prev,
|
||||
div.swiper-button-next {
|
||||
transition: all 450ms !important;
|
||||
color: rgb(255, 255, 255);
|
||||
transition: all 450ms !important;
|
||||
color: rgb(255, 255, 255);
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2) !important;
|
||||
transition: all 450ms !important;
|
||||
}
|
||||
&:hover {
|
||||
transform: scale(1.2) !important;
|
||||
transition: all 450ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
.swiper-slide {
|
||||
transition: all 450ms !important;
|
||||
transition: all 450ms !important;
|
||||
}
|
||||
|
||||
.swiper-wrapper:hover .swiper-slide {
|
||||
opacity: .3 !important;
|
||||
z-index: 10000;
|
||||
opacity: 0.3 !important;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.3) !important;
|
||||
opacity: 1 !important;
|
||||
&:hover {
|
||||
transform: scale(1.3) !important;
|
||||
opacity: 1 !important;
|
||||
|
||||
@include responsive(tab_port) {
|
||||
transform: scale(1.2) !important;
|
||||
}
|
||||
}
|
||||
@include responsive(tab_port) {
|
||||
transform: scale(1.2) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.movieShowcase {
|
||||
background-color: $color-background;
|
||||
grid-column: 2 / 13;
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
grid-column: 2 / 13;
|
||||
color: #fff;
|
||||
|
||||
&__heading {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
&__heading {
|
||||
position: relative;
|
||||
top: 3rem;
|
||||
// margin: 0;
|
||||
padding-left: 4rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
&__container::-webkit-scrollbar {
|
||||
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) {
|
||||
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 {
|
||||
transform: translate3d(5rem, 0, 0);
|
||||
|
||||
@include responsive(tab_port) {
|
||||
transform: translate3d(3rem, 0, 0);
|
||||
}
|
||||
|
||||
@include responsive(phone) {
|
||||
transform: translate3d(2.8rem, 0, 0);
|
||||
}
|
||||
&-image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&--movie__netflix:hover~&--movie__netflix {
|
||||
transform: translate3d(1rem, 0, 0);
|
||||
&__netflix {
|
||||
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 {
|
||||
background-color: $color-background;
|
||||
grid-column: 1 / 13;
|
||||
height: 65rem;
|
||||
display: inline-block;
|
||||
background-color: $color-background;
|
||||
// grid-column: 1 / 13;
|
||||
// height: 82vh;
|
||||
// display: grid;
|
||||
position: relative;
|
||||
padding-top: 56.25%;
|
||||
|
||||
@include responsive(phone) {
|
||||
height: 50rem;
|
||||
}
|
||||
|
||||
|
||||
&__container {
|
||||
padding-top: 20rem;
|
||||
padding-left: 4.5rem;
|
||||
@include responsive(phone) {
|
||||
padding-top: 0;
|
||||
height: 32rem;
|
||||
}
|
||||
|
||||
&__video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&__container {
|
||||
color: #fff;
|
||||
|
||||
@include responsive(phone) {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
&-heading {
|
||||
font-size: 6rem;
|
||||
position: absolute;
|
||||
color: #fff;
|
||||
top: 12rem;
|
||||
left: 3.5rem;
|
||||
|
||||
@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 {
|
||||
font-size: 6rem;
|
||||
padding-bottom: 1rem;
|
||||
&-add {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
fill: #fff;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
&-btnPlay,
|
||||
&-btnMyList {
|
||||
cursor: pointer;
|
||||
font-size: 1.6rem;
|
||||
color: #fff;
|
||||
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;
|
||||
&-play {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
fill: #fff;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-add {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
fill: #fff;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
&-btnPlay {
|
||||
color: #000;
|
||||
background-color: #e6e6e6;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&-play {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
fill: #fff;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #000;
|
||||
background-color: #e6e6e6;
|
||||
transition: all .2s;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
|
||||
& > * {
|
||||
fill: #000;
|
||||
}
|
||||
left: 4rem;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
& > * {
|
||||
fill: #000;
|
||||
}
|
||||
}
|
||||
&-btnMyList {
|
||||
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;
|
||||
line-height: 1.3;
|
||||
padding-top: 2.5rem;
|
||||
font-size: 1.8rem;
|
||||
|
||||
@include responsive(phone) {
|
||||
width: 36rem;
|
||||
}
|
||||
& > * {
|
||||
stroke: #fff;
|
||||
stroke-width: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&--fadeBottom {
|
||||
height: 28.5rem;
|
||||
background-image: linear-gradient(180deg, transparent,rgba(37, 37, 37, 0.61), rgb(17, 17, 17));
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgba(211, 211, 211, 0.178);
|
||||
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 {
|
||||
background-color: transparent;
|
||||
z-index: 100;
|
||||
grid-column: 1 / 13;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 6.7rem;
|
||||
transition-timing-function: ease-in;
|
||||
transition: all 1s;
|
||||
background-color: transparent;
|
||||
z-index: 100;
|
||||
grid-column: 1 / 13;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 6.7rem;
|
||||
transition-timing-function: ease-in;
|
||||
transition: all 1s;
|
||||
top: 0;
|
||||
padding-bottom: 1.5rem;
|
||||
|
||||
&.black {
|
||||
background-color: $color-background;
|
||||
}
|
||||
|
||||
&.black {
|
||||
background-color: $color-background;
|
||||
}
|
||||
&__container {
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
height: 6.7rem;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-left: 2rem;
|
||||
|
||||
&__container {
|
||||
background-color: transparent;
|
||||
margin-left: 3.8rem;
|
||||
@include responsive(phone) {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
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;
|
||||
height: 6.7rem;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
@include responsive(phone) {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
&__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 0.5s;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
|
||||
a:first-child {
|
||||
padding-left: .2%;
|
||||
color: $color-red;
|
||||
font-size: 4rem;
|
||||
&:focus {
|
||||
padding-left: 4rem;
|
||||
width: 22rem;
|
||||
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 {
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
transform: translateX(2.4rem) translateY(1rem);
|
||||
cursor: pointer;
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
transform: translateX(2.4rem) translateY(1rem);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dropdownContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// align-items: center;
|
||||
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);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// align-items: center;
|
||||
opacity: 0;
|
||||
|
||||
&--2 {
|
||||
height: 10rem;
|
||||
}
|
||||
color: #fff;
|
||||
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 {
|
||||
width: 60%;
|
||||
padding-top: 1.2rem;
|
||||
font-size: 1.2rem;
|
||||
|
||||
&:hover {
|
||||
border-bottom: 1px solid #fff;
|
||||
// border-width: 2%;
|
||||
}
|
||||
}
|
||||
&--2 {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
&-textOutside {
|
||||
font-size: 1.2rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
&-text {
|
||||
width: 60%;
|
||||
padding-top: 1.2rem;
|
||||
font-size: 1.2rem;
|
||||
|
||||
&--user {
|
||||
height: 3rem;
|
||||
&:hover {
|
||||
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;
|
||||
border-radius: 5px;
|
||||
background-color: rgb(64, 168, 228);
|
||||
font-size: 1.2rem;
|
||||
transform: translateX(4rem) translateY(-2rem);
|
||||
|
||||
&-2 {
|
||||
background-color: rgb(230, 145, 48);
|
||||
&:hover {
|
||||
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 {
|
||||
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
|
||||
&:hover .dropdownContent {
|
||||
transition: all .4s;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
&:hover .dropdownContent {
|
||||
transition: all 0.4s;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.pseudo-link {
|
||||
cursor: pointer;
|
||||
@include responsive(tab_port) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
cursor: pointer;
|
||||
@include responsive(tab_port) {
|
||||
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_NETFLIX_ORIGINALS = 'FETCH_NETFLIX_ORIGINALS';
|
||||
export const FETCH_TOP_RATED = 'FETCH_TOP_RATED';
|
||||
export const FETCH_ACTION_MOVIES = 'FETCH_ACTION_MOVIES';
|
||||
export const FETCH_COMEDY_MOVIES = 'FETCH_COMEDY_MOVIES';
|
||||
export const FETCH_HORROR_MOVIES = 'FETCH_HORROR_MOVIES';
|
||||
export const FETCH_ROMANCE_MOVIES = 'FETCH_ROMANCE_MOVIES';
|
||||
export const FETCH_DOCUMENTARIES = 'FETCH_DOCUMENTARIES';
|
||||
export const FETCH_TRENDING = 'FETCH_TRENDING'
|
||||
export const FETCH_NETFLIX_ORIGINALS = 'FETCH_NETFLIX_ORIGINALS'
|
||||
export const FETCH_TOP_RATED = 'FETCH_TOP_RATED'
|
||||
export const FETCH_ACTION_MOVIES = 'FETCH_ACTION_MOVIES'
|
||||
export const FETCH_COMEDY_MOVIES = 'FETCH_COMEDY_MOVIES'
|
||||
export const FETCH_HORROR_MOVIES = 'FETCH_HORROR_MOVIES'
|
||||
export const FETCH_ROMANCE_MOVIES = 'FETCH_ROMANCE_MOVIES'
|
||||
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 request = axios.get(
|
||||
`/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`
|
||||
);
|
||||
|
||||
return {
|
||||
type: FETCH_TRENDING,
|
||||
payload: request,
|
||||
};
|
||||
const media_type = {
|
||||
tv: 'tv',
|
||||
movie: 'movie',
|
||||
}
|
||||
|
||||
export function fetchNetflixOriginals() {
|
||||
const request = axios.get(
|
||||
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
||||
);
|
||||
export const fetchMovieDetails = (mediaType, mediaId) => {
|
||||
return async (dispatch) => {
|
||||
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 {
|
||||
type: FETCH_NETFLIX_ORIGINALS,
|
||||
payload: request,
|
||||
};
|
||||
const request = await axios.get(urlPath)
|
||||
dispatch({ type: FETCH_MOVIE_DETAILS_SUCCESS, payload: request })
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
dispatch({ type: FETCH_MOVIE_DETAILS_FAIL })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchTopRated() {
|
||||
const request = axios.get(
|
||||
`/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`
|
||||
);
|
||||
|
||||
return {
|
||||
type: FETCH_TOP_RATED,
|
||||
payload: request,
|
||||
};
|
||||
export const fetchSearchMovie = (searchTerm) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch({ type: FETCH_SEARCH_MOVIE })
|
||||
const request = await axios.get(
|
||||
`/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchTerm}`
|
||||
)
|
||||
dispatch({ type: FETCH_SEARCH_MOVIE_SUCCESS, payload: request })
|
||||
} catch (error) {
|
||||
dispatch({ type: FETCH_SEARCH_MOVIE_FAIL })
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchActionMovies() {
|
||||
const request = axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
||||
);
|
||||
export const fetchNetflixOriginals = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const request = await axios.get(
|
||||
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
||||
)
|
||||
|
||||
return {
|
||||
type: FETCH_ACTION_MOVIES,
|
||||
payload: request,
|
||||
};
|
||||
dispatch({ type: FETCH_NETFLIX_ORIGINALS, payload: request })
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchComedyMovies() {
|
||||
const request = axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=35`
|
||||
);
|
||||
|
||||
return {
|
||||
type: FETCH_COMEDY_MOVIES,
|
||||
payload: request,
|
||||
};
|
||||
export const fetchTrending = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const request = await axios.get(
|
||||
`/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`
|
||||
)
|
||||
dispatch({ type: FETCH_TRENDING, payload: request })
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchHorrorMovies() {
|
||||
const request = axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=27`
|
||||
);
|
||||
|
||||
return {
|
||||
type: FETCH_HORROR_MOVIES,
|
||||
payload: request,
|
||||
};
|
||||
export const fetchTopRated = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const request = await axios.get(
|
||||
`/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`
|
||||
)
|
||||
dispatch({ type: FETCH_TOP_RATED, payload: request })
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchRomanceMovies() {
|
||||
const request = axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=10749`
|
||||
);
|
||||
export const fetchActionMovies = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const request = await axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
||||
)
|
||||
|
||||
return {
|
||||
type: FETCH_ROMANCE_MOVIES,
|
||||
payload: request,
|
||||
};
|
||||
dispatch({ type: FETCH_ACTION_MOVIES, payload: request })
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchDocumentaries() {
|
||||
const request = axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=99`
|
||||
);
|
||||
export const fetchComedyMovies = () => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const request = await axios.get(
|
||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=35`
|
||||
)
|
||||
|
||||
return {
|
||||
type: FETCH_DOCUMENTARIES,
|
||||
payload: request,
|
||||
};
|
||||
dispatch({ type: FETCH_COMEDY_MOVIES, payload: request })
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
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 TrendingReducer from './reducerTrending';
|
||||
import NetflixOriginalsReducer from './reducerNetflixOriginals';
|
||||
import TopRatedReducer from './reducerTopRated';
|
||||
import ActionMoviesReducer from './reducerActionMovies';
|
||||
import ComedyMoviesReducer from './reducerComedyMovies';
|
||||
import HorrorMoviesReducer from './reducerHorrorMovies';
|
||||
import RomanceMoviesReducer from './reducerRomanceMovies';
|
||||
import DocumentaryReducer from './reducerDocumentary';
|
||||
import { combineReducers } from 'redux'
|
||||
import TrendingReducer from './reducerTrending'
|
||||
import NetflixOriginalsReducer from './reducerNetflixOriginals'
|
||||
import TopRatedReducer from './reducerTopRated'
|
||||
import ActionMoviesReducer from './reducerActionMovies'
|
||||
import ComedyMoviesReducer from './reducerComedyMovies'
|
||||
import HorrorMoviesReducer from './reducerHorrorMovies'
|
||||
import RomanceMoviesReducer from './reducerRomanceMovies'
|
||||
import DocumentaryReducer from './reducerDocumentary'
|
||||
import SearchMovieReducer from './reducerSearchMovie'
|
||||
import MovieDetailsReducer from './reducerMovieDetails'
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
trending: TrendingReducer,
|
||||
@@ -17,6 +19,8 @@ const rootReducer = combineReducers({
|
||||
horror: HorrorMoviesReducer,
|
||||
romance: RomanceMoviesReducer,
|
||||
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) {
|
||||
switch (action.type) {
|
||||
case FETCH_NETFLIX_ORIGINALS:
|
||||
const data = action.payload.data.results;
|
||||
return { ...state, data };
|
||||
const data = action.payload.data.results
|
||||
return { ...state, data }
|
||||
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 CopyWebpackPlugin = require('copy-webpack-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 webpack = require('webpack');
|
||||
var path = require('path');
|
||||
@@ -42,7 +42,7 @@ module.exports = () => {
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: /node_modules/,
|
||||
loaders: ['style-loader', 'css-loader'],
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
@@ -76,7 +76,7 @@ module.exports = () => {
|
||||
historyApiFallback: true,
|
||||
},
|
||||
node: {
|
||||
fs: 'empty',
|
||||
global: true,
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(envKeys),
|
||||
@@ -84,15 +84,23 @@ module.exports = () => {
|
||||
template: './src/index.html',
|
||||
filename: './index.html',
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{ from: 'src/static/images', to: 'static/images' },
|
||||
]),
|
||||
// new CopyWebpackPlugin({
|
||||
// patterns: [[ { from: 'src/static/images', to: 'static/images' }],
|
||||
// }),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: 'src/static/images',
|
||||
to: 'static/images',
|
||||
},
|
||||
],
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'main.css',
|
||||
}),
|
||||
new CleanWebpackPlugin(['dist']),
|
||||
new CleanWebpackPlugin(),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user