set env variables config in webpack file

This commit is contained in:
andres alcocer
2020-05-14 17:38:17 -04:00
parent 4d3655e576
commit 3faffa603a
41 changed files with 2774 additions and 364 deletions

2
.eslintignore Normal file
View File

@@ -0,0 +1,2 @@
client/dist/
node_modules/

25
.eslintrc.json Normal file
View File

@@ -0,0 +1,25 @@
{
"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"]
}
}

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
package-lock.json
.next
node_modules/

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"singleQuote": true
}

View File

@@ -5,7 +5,7 @@
This project is a simplified front end clone of Netflix. It was created with React and CSS (Grid and Flexbox). It uses [The MovieDB Api](https://www.themoviedb.org/documentation/api) to search for movies and display details. Feel free to contribute! This project is a simplified front end clone of Netflix. It was created with React and CSS (Grid and Flexbox). It uses [The MovieDB Api](https://www.themoviedb.org/documentation/api) to search for movies and display details. Feel free to contribute!
### Tools used: ### Tools used:
- Webpack - Webpack
- Axios - Axios
- Redux & React - Redux & React
- Sass (grid & flexbox) - Sass (grid & flexbox)

2389
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,14 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "webpack-dev-server --open --mode development", "dev": "webpack-dev-server --mode development --config webpack.config.js --open",
"build": "webpack -p" "build": "webpack -p",
"lint": "eslint --fix . && echo 'Lint complete.'"
}, },
"engines": { "engines": {
"node": "10.1.0", "node": "10.1.0",
"npm": "6.4.1" "npm": "6.4.1",
"watch": "watch 'clear && npm run -s test | tap-nirvana && npm run -s lint' src"
}, },
"author": "Andres Alcocer", "author": "Andres Alcocer",
"license": "ISC", "license": "ISC",
@@ -38,6 +40,14 @@
"copy-webpack-plugin": "^4.6.0", "copy-webpack-plugin": "^4.6.0",
"css-loader": "^1.0.1", "css-loader": "^1.0.1",
"dotenv": "^6.2.0", "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",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^2.0.0", "file-loader": "^2.0.0",
"html-loader": "^0.5.5", "html-loader": "^0.5.5",
@@ -46,13 +56,16 @@
"mini-css-extract-plugin": "^0.4.4", "mini-css-extract-plugin": "^0.4.4",
"node-sass": "^4.10.0", "node-sass": "^4.10.0",
"optimize-css-assets-webpack-plugin": "^5.0.1", "optimize-css-assets-webpack-plugin": "^5.0.1",
"prettier": "^2.0.5",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"svg-inline-loader": "^0.8.0", "svg-inline-loader": "^0.8.0",
"svg-react-loader": "^0.4.6", "svg-react-loader": "^0.4.6",
"tap-nirvana": "^1.1.0",
"uglifyjs-webpack-plugin": "^2.0.1", "uglifyjs-webpack-plugin": "^2.0.1",
"watch": "^1.0.2",
"webpack": "^4.25.1", "webpack": "^4.25.1",
"webpack-cli": "^3.1.2", "webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10" "webpack-dev-server": "^3.1.10"
} }
} }

View File

@@ -1,8 +1,8 @@
import axios from 'axios'; import axios from 'axios';
/** base url to make requests to the the movie database */ /** base url to make requests to the the movie database */
const instance = axios.create({ const instance = axios.create({
baseURL: "https://api.themoviedb.org/3" baseURL: 'https://api.themoviedb.org/3',
}); });
export default instance; export default instance;

View File

@@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
const dropdownContent = () => ( const dropdownContent = () => (
<div className="dropdownContainer"> <div className="dropdownContainer">
<div className="navigation__container--userLogo"> <div className="navigation__container--userLogo">
<div className="dropdownContent"> <div className="dropdownContent">
@@ -19,7 +17,6 @@ const dropdownContent = () => (
<p className="dropdownContent--user-text">Luis</p> <p className="dropdownContent--user-text">Luis</p>
</div> </div>
<p className="dropdownContent-text">Manage Profiles</p> <p className="dropdownContent-text">Manage Profiles</p>
</div> </div>
<div className="dropdownContent dropdownContent--2"> <div className="dropdownContent dropdownContent--2">
<p className="dropdownContent-textOutside">Account</p> <p className="dropdownContent-textOutside">Account</p>
@@ -31,4 +28,3 @@ const dropdownContent = () => (
); );
export default dropdownContent; export default dropdownContent;

View File

@@ -1,11 +1,15 @@
import React from 'react'; import React from 'react';
const footer = () => ( const footer = () => (
<footer className="footer">
<footer className="footer"> <div className="footer__copyright">
<div className="footer__copyright"> &copy; 2018 Made with by{' '}
&copy; 2018 Made with by <a className="footer__copyright--link" href="http://andresio.com"> Andres Alcocer</a></div> <a className="footer__copyright--link" href="http://andresio.com">
</footer> {' '}
Andres Alcocer
</a>
</div>
</footer>
); );
export default footer; export default footer;

View File

@@ -3,19 +3,21 @@ import React from 'react';
import PlayLogo from '../static/images/play-button.svg'; import PlayLogo from '../static/images/play-button.svg';
import AddLogo from '../static/images/add.svg'; import AddLogo from '../static/images/add.svg';
export default function Header(props) { export default function Header(props) {
const backgroundStyle = { const backgroundStyle = {
backgroundSize: "cover", backgroundSize: 'cover',
backgroundImage: `url(https://image.tmdb.org/t/p/original/${props.movie.backdrop_path})`, backgroundImage: `url(https://image.tmdb.org/t/p/original/${props.movie.backdrop_path})`,
backgroundPosition: "center", backgroundPosition: 'center',
} };
return ( return (
<header style={backgroundStyle} className="header"> <header style={backgroundStyle} className="header">
<div className="header__container"> <div className="header__container">
<h1 className="header__container-heading">{props.movie.name}</h1> <h1 className="header__container-heading">{props.movie.name}</h1>
<button onClick={() => alert("not a movie!")} className="header__container-btnPlay"> <button
onClick={() => alert('not a movie!')}
className="header__container-btnPlay"
>
<PlayLogo className="header__container-btnMyList-play" /> <PlayLogo className="header__container-btnMyList-play" />
Play Play
</button> </button>
@@ -28,5 +30,5 @@ export default function Header(props) {
</div> </div>
<div className="header--fadeBottom"></div> <div className="header--fadeBottom"></div>
</header> </header>
) );
} }

View File

@@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
const Movie = (props) => ( const Movie = (props) => (
<div className="movie"> <div className="movie">
<div onClick={props.movieDetails} className="movie__column-poster"> <div onClick={props.movieDetails} className="movie__column-poster">
<img src={props.movieImage} alt="" className="movie__poster" /> <img src={props.movieImage} alt="" className="movie__poster" />
</div> </div>
</div> </div>
); );
export default Movie; export default Movie;

View File

@@ -13,13 +13,18 @@ export default function MovieDetails(props) {
</h1> </h1>
<p className="modal__info"> <p className="modal__info">
<span className="modal__rating"> <span className="modal__rating">
Rating: {props.movie.vote_average * 10}%{" "} Rating: {props.movie.vote_average * 10}%{' '}
</span> </span>
Release date: {props.movie.release_date || props.movie.first_air_date} Runtime: {props.movie.runtime || props.movie.episode_run_time}m Release date: {props.movie.release_date || props.movie.first_air_date}{' '}
Runtime: {props.movie.runtime || props.movie.episode_run_time}m
</p> </p>
<p className="modal__episode"> <p className="modal__episode">
{props.movie.number_of_episodes ? " Episodes: " + props.movie.number_of_episodes : ""} {props.movie.number_of_episodes
{props.movie.number_of_seasons ? " Seasons: " + props.movie.number_of_seasons : ""} ? ' Episodes: ' + props.movie.number_of_episodes
: ''}
{props.movie.number_of_seasons
? ' Seasons: ' + props.movie.number_of_seasons
: ''}
</p> </p>
<p className="modal__overview">{props.movie.overview}</p> <p className="modal__overview">{props.movie.overview}</p>
<button className="modal__btn modal__btn--red"> <button className="modal__btn modal__btn--red">
@@ -33,4 +38,4 @@ export default function MovieDetails(props) {
</div> </div>
</Aux> </Aux>
); );
} }

View File

@@ -1,16 +1,26 @@
import React from 'react' import React from 'react';
export default function MovieGenreImage(props) { export default function MovieGenreImage(props) {
let netflixUrl = false; let netflixUrl = false;
if (props.url === "https://api.themoviedb.org/3/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_networks=213") { if (
netflixUrl = true; props.url ===
} 'https://api.themoviedb.org/3/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_networks=213'
) {
netflixUrl = true;
}
return ( return (
<div onClick={props.movieDetailsModal} <div
className={"movieShowcase__container--movie" + (netflixUrl ? "__netflix" : "")}> onClick={props.movieDetailsModal}
<img src={props.posterUrl} className="movieShowcase__container--movie-image" /> className={
</div> 'movieShowcase__container--movie' + (netflixUrl ? '__netflix' : '')
); }
} >
<img
src={props.posterUrl}
className="movieShowcase__container--movie-image"
/>
</div>
);
}

View File

@@ -2,8 +2,13 @@ import React from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
const navigationItem = (props) => ( const navigationItem = (props) => (
<NavLink className="navigation__container-link" exact={props.exact} <NavLink
to={props.link}>{props.children}</NavLink> className="navigation__container-link"
) exact={props.exact}
to={props.link}
>
{props.children}
</NavLink>
);
export default navigationItem; export default navigationItem;

View File

@@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
const backdrop = (props) => ( const backdrop = (props) =>
props.show ? <div props.show ? (
onClick={props.toggleBackdrop} <div onClick={props.toggleBackdrop} className="backdrop"></div>
className="backdrop"></div> : null ) : null;
);
export default backdrop; export default backdrop;

View File

@@ -1,23 +1,25 @@
import React from 'react' import React from 'react';
import Aux from '../../hoc/Aux'; import Aux from '../../hoc/Aux';
import Backdrop from './Backdrop' import Backdrop from './Backdrop';
export default function Modal(props) { export default function Modal(props) {
const backgroundStyle = { const backgroundStyle = {
backgroundSize: "cover", 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/${
} props.movie.backdrop_path || props.movie.poster_path
})`,
};
return ( return (
<Aux> <Aux>
<Backdrop show={props.show} toggleBackdrop={props.modalClosed} /> <Backdrop show={props.show} toggleBackdrop={props.modalClosed} />
<div <div
style={backgroundStyle} style={backgroundStyle}
className={(props.show ? "modal show" : "modal hide")}> className={props.show ? 'modal show' : 'modal hide'}
{props.children} >
</div> {props.children}
</Aux> </div>
) </Aux>
);
} }

View File

@@ -1,41 +1,39 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchActionMovies } from '../store/actions/index'; import { fetchActionMovies } from '../store/actions/index';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
class ActionMovies extends Component { class ActionMovies extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchActionMovies(); this.props.fetchActionMovies();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.actionMovies.data) { if (this.props.actionMovies.data) {
const url = "/discover/movie?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=28"; const url =
'/discover/movie?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=28';
movies = getMovieRows(this.props.actionMovies.data, url); movies = getMovieRows(this.props.actionMovies.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Action Movies</h1> <h1 className="movieShowcase__heading">Action Movies</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { actionMovies: state.action } return { actionMovies: state.action };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchActionMovies }, dispatch) return bindActionCreators({ fetchActionMovies }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(ActionMovies); export default connect(mapStateToProps, mapDispatchToProps)(ActionMovies);

View File

@@ -1,14 +1,10 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import Layout from './Layout'; import Layout from './Layout';
class App extends Component { class App extends Component {
render() { render() {
return ( return <Layout />;
<Layout />
)
} }
} }
export default App; export default App;

View File

@@ -1,42 +1,39 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
import { fetchComedyMovies } from '../store/actions/index'; import { fetchComedyMovies } from '../store/actions/index';
class ComedyMovies extends Component { class ComedyMovies extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchComedyMovies(); this.props.fetchComedyMovies();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.movies.data) { if (this.props.movies.data) {
const url = '/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=35'; const url =
'/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=35';
movies = getMovieRows(this.props.movies.data, url); movies = getMovieRows(this.props.movies.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Comedy Movies</h1> <h1 className="movieShowcase__heading">Comedy Movies</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { movies: state.comedy } return { movies: state.comedy };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchComedyMovies }, dispatch) return bindActionCreators({ fetchComedyMovies }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(ComedyMovies); export default connect(mapStateToProps, mapDispatchToProps)(ComedyMovies);

View File

@@ -1,42 +1,39 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
import { fetchDocumentaries } from '../store/actions/index'; import { fetchDocumentaries } from '../store/actions/index';
class Documentaries extends Component { class Documentaries extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchDocumentaries(); this.props.fetchDocumentaries();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.movies.data) { if (this.props.movies.data) {
const url = '/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=99'; const url =
'/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=99';
movies = getMovieRows(this.props.movies.data, url); movies = getMovieRows(this.props.movies.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Documentaries</h1> <h1 className="movieShowcase__heading">Documentaries</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { movies: state.documentary } return { movies: state.documentary };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchDocumentaries }, dispatch) return bindActionCreators({ fetchDocumentaries }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(Documentaries); export default connect(mapStateToProps, mapDispatchToProps)(Documentaries);

View File

@@ -1,42 +1,39 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
import { fetchHorrorMovies } from '../store/actions/index'; import { fetchHorrorMovies } from '../store/actions/index';
class HorrorMovies extends Component { class HorrorMovies extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchHorrorMovies(); this.props.fetchHorrorMovies();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.movies.data) { if (this.props.movies.data) {
const url = '/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=27'; const url =
'/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=27';
movies = getMovieRows(this.props.movies.data, url); movies = getMovieRows(this.props.movies.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Horror Movies</h1> <h1 className="movieShowcase__heading">Horror Movies</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { movies: state.horror } return { movies: state.horror };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchHorrorMovies }, dispatch) return bindActionCreators({ fetchHorrorMovies }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(HorrorMovies); export default connect(mapStateToProps, mapDispatchToProps)(HorrorMovies);

View File

@@ -113,7 +113,6 @@ class Layout extends Component {
this.state.toggleMovieList ? <MainContent /> : <div this.state.toggleMovieList ? <MainContent /> : <div
className="search-container">{this.state.MovieList}</div> className="search-container">{this.state.MovieList}</div>
} }
<Modal show={this.state.toggleModal} <Modal show={this.state.toggleModal}
modalClosed={this.closeModal} modalClosed={this.closeModal}
movie={this.state.movieOverview}> movie={this.state.movieOverview}>

View File

@@ -1,42 +1,39 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
import { fetchNetflixOriginals } from '../store/actions/index'; import { fetchNetflixOriginals } from '../store/actions/index';
class NetflixOriginals extends Component { class NetflixOriginals extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchNetflixOriginals(); this.props.fetchNetflixOriginals();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.movies.data) { if (this.props.movies.data) {
const url = '/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_networks=213'; const url =
'/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_networks=213';
movies = getMovieRows(this.props.movies.data, url); movies = getMovieRows(this.props.movies.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">NETFLIX ORIGINALS</h1> <h1 className="movieShowcase__heading">NETFLIX ORIGINALS</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { movies: state.netflixOriginals } return { movies: state.netflixOriginals };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchNetflixOriginals }, dispatch) return bindActionCreators({ fetchNetflixOriginals }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(NetflixOriginals); export default connect(mapStateToProps, mapDispatchToProps)(NetflixOriginals);

View File

@@ -1,42 +1,39 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
import { fetchRomanceMovies } from '../store/actions/index'; import { fetchRomanceMovies } from '../store/actions/index';
class RomanceMovies extends Component { class RomanceMovies extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchRomanceMovies(); this.props.fetchRomanceMovies();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.movies.data) { if (this.props.movies.data) {
const url = '/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=10749'; const url =
'/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_genres=10749';
movies = getMovieRows(this.props.movies.data, url); movies = getMovieRows(this.props.movies.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Romance Movies</h1> <h1 className="movieShowcase__heading">Romance Movies</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { movies: state.romance } return { movies: state.romance };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchRomanceMovies }, dispatch) return bindActionCreators({ fetchRomanceMovies }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(RomanceMovies); export default connect(mapStateToProps, mapDispatchToProps)(RomanceMovies);

View File

@@ -1,41 +1,39 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchTopRated } from '../store/actions/index'; import { fetchTopRated } from '../store/actions/index';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
class TopRated extends Component { class TopRated extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchTopRated(); this.props.fetchTopRated();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.topRated.data) { if (this.props.topRated.data) {
const url = "/movie/top_rated?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&language=en-US"; const url =
'/movie/top_rated?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&language=en-US';
movies = getMovieRows(this.props.topRated.data, url); movies = getMovieRows(this.props.topRated.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Top Rated</h1> <h1 className="movieShowcase__heading">Top Rated</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { topRated: state.topRated } return { topRated: state.topRated };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchTopRated }, dispatch) return bindActionCreators({ fetchTopRated }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(TopRated); export default connect(mapStateToProps, mapDispatchToProps)(TopRated);

View File

@@ -1,41 +1,39 @@
import React, { Component } from 'react' import React, { Component } from 'react';
import { bindActionCreators } from "redux"; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchTrending } from '../store/actions/index'; import { fetchTrending } from '../store/actions/index';
import { getMovieRows } from '../getMovie'; import { getMovieRows } from '../getMovie';
class TrendingMovies extends Component { class TrendingMovies extends Component {
componentWillMount() { componentWillMount() {
this.props.fetchTrending(); this.props.fetchTrending();
} }
render() { render() {
let movies let movies;
// Call getMoviesRows function only when we get the data back // Call getMoviesRows function only when we get the data back
// from the API through redux // from the API through redux
if (this.props.trending.data) { if (this.props.trending.data) {
const url = '/trending/all/week?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&language=en-US'; const url =
'/trending/all/week?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&language=en-US';
movies = getMovieRows(this.props.trending.data, url); movies = getMovieRows(this.props.trending.data, url);
} }
return ( return (
<> <>
<h1 className="movieShowcase__heading">Trending Now</h1> <h1 className="movieShowcase__heading">Trending Now</h1>
<div className="movieShowcase__container"> <div className="movieShowcase__container">{movies}</div>
{movies}
</div>
</> </>
) );
} }
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { trending: state.trending } return { trending: state.trending };
} };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchTrending }, dispatch) return bindActionCreators({ fetchTrending }, dispatch);
} };
export default connect(mapStateToProps, mapDispatchToProps)(TrendingMovies); export default connect(mapStateToProps, mapDispatchToProps)(TrendingMovies);

View File

@@ -3,21 +3,29 @@ import React from 'react';
export function getMovieRows(movies, url) { export function getMovieRows(movies, url) {
const movieRow = movies.map((movie) => { const movieRow = movies.map((movie) => {
let movieImageUrl = "https://image.tmdb.org/t/p/w500/" + movie.backdrop_path; let movieImageUrl =
if (url === "/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_networks=213") { 'https://image.tmdb.org/t/p/w500/' + movie.backdrop_path;
movieImageUrl = "https://image.tmdb.org/t/p/original/" + movie.poster_path; if (
url ===
'/discover/tv?api_key=224ce27b38a3805ecf6f6c36eb3ba9d0&with_networks=213'
) {
movieImageUrl =
'https://image.tmdb.org/t/p/original/' + movie.poster_path;
} }
if (movie.poster_path && movie.backdrop_path !== null) { if (movie.poster_path && movie.backdrop_path !== null) {
const movieComponent = <MovieGenre const movieComponent = (
key={movie.id} <MovieGenre
url={url} key={movie.id}
posterUrl={movieImageUrl} url={url}
movie={movie} /> posterUrl={movieImageUrl}
movie={movie}
/>
);
return movieComponent; return movieComponent;
} }
}); });
return movieRow return movieRow;
} }

View File

@@ -1,3 +1,3 @@
const aux = (props) => props.children; const aux = (props) => props.children;
export default aux; export default aux;

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux'; import { createStore, applyMiddleware } from 'redux';
import reducers from './store/reducers'; import reducers from './store/reducers';
@@ -13,11 +13,11 @@ import './static/sass/style.scss';
const createStoreWithMiddleware = applyMiddleware(promise)(createStore); const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
const app = ( const app = (
<Provider store={createStoreWithMiddleware(reducers)}> <Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter> <BrowserRouter>
<App /> <App />
</BrowserRouter> </BrowserRouter>
</Provider> </Provider>
); );
ReactDOM.render(app, document.getElementById('app')); ReactDOM.render(app, document.getElementById('app'));

View File

@@ -12,73 +12,89 @@ export const FETCH_DOCUMENTARIES = 'FETCH_DOCUMENTARIES';
const API_KEY = '224ce27b38a3805ecf6f6c36eb3ba9d0'; const API_KEY = '224ce27b38a3805ecf6f6c36eb3ba9d0';
export function fetchTrending() { export function fetchTrending() {
const request = axios.get(`/trending/all/week?api_key=${API_KEY}&language=en-US`); const request = axios.get(
`/trending/all/week?api_key=${API_KEY}&language=en-US`
);
return { return {
type: FETCH_TRENDING, type: FETCH_TRENDING,
payload: request payload: request,
} };
} }
export function fetchNetflixOriginals() { export function fetchNetflixOriginals() {
const request = axios.get(`/discover/tv?api_key=${API_KEY}&with_networks=213`); const request = axios.get(
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
);
return { return {
type: FETCH_NETFLIX_ORIGINALS, type: FETCH_NETFLIX_ORIGINALS,
payload: request payload: request,
} };
} }
export function fetchTopRated() { export function fetchTopRated() {
const request = axios.get(`/movie/top_rated?api_key=${API_KEY}&language=en-US`) const request = axios.get(
`/movie/top_rated?api_key=${API_KEY}&language=en-US`
);
return { return {
type: FETCH_TOP_RATED, type: FETCH_TOP_RATED,
payload: request payload: request,
} };
} }
export function fetchActionMovies() { export function fetchActionMovies() {
const request = axios.get(`/discover/movie?api_key=${API_KEY}&with_genres=28`) const request = axios.get(
`/discover/movie?api_key=${API_KEY}&with_genres=28`
);
return { return {
type: FETCH_ACTION_MOVIES, type: FETCH_ACTION_MOVIES,
payload: request payload: request,
} };
} }
export function fetchComedyMovies() { export function fetchComedyMovies() {
const request = axios.get(`/discover/movie?api_key=${API_KEY}&with_genres=35`) const request = axios.get(
`/discover/movie?api_key=${API_KEY}&with_genres=35`
);
return { return {
type: FETCH_COMEDY_MOVIES, type: FETCH_COMEDY_MOVIES,
payload: request payload: request,
} };
} }
export function fetchHorrorMovies() { export function fetchHorrorMovies() {
const request = axios.get(`/discover/movie?api_key=${API_KEY}&with_genres=27`) const request = axios.get(
`/discover/movie?api_key=${API_KEY}&with_genres=27`
);
return { return {
type: FETCH_HORROR_MOVIES, type: FETCH_HORROR_MOVIES,
payload: request payload: request,
} };
} }
export function fetchRomanceMovies() { export function fetchRomanceMovies() {
const request = axios.get(`/discover/movie?api_key=${API_KEY}&with_genres=10749`) const request = axios.get(
`/discover/movie?api_key=${API_KEY}&with_genres=10749`
);
return { return {
type: FETCH_ROMANCE_MOVIES, type: FETCH_ROMANCE_MOVIES,
payload: request payload: request,
} };
} }
export function fetchDocumentaries() { export function fetchDocumentaries() {
const request = axios.get(`/discover/movie?api_key=${API_KEY}&with_genres=99`) const request = axios.get(
`/discover/movie?api_key=${API_KEY}&with_genres=99`
);
return { return {
type: FETCH_DOCUMENTARIES, type: FETCH_DOCUMENTARIES,
payload: request payload: request,
} };
} }

View File

@@ -8,7 +8,6 @@ import HorrorMoviesReducer from './reducerHorrorMovies';
import RomanceMoviesReducer from './reducerRomanceMovies'; import RomanceMoviesReducer from './reducerRomanceMovies';
import DocumentaryReducer from './reducerDocumentary'; import DocumentaryReducer from './reducerDocumentary';
const rootReducer = combineReducers({ const rootReducer = combineReducers({
trending: TrendingReducer, trending: TrendingReducer,
netflixOriginals: NetflixOriginalsReducer, netflixOriginals: NetflixOriginalsReducer,
@@ -17,7 +16,7 @@ const rootReducer = combineReducers({
comedy: ComedyMoviesReducer, comedy: ComedyMoviesReducer,
horror: HorrorMoviesReducer, horror: HorrorMoviesReducer,
romance: RomanceMoviesReducer, romance: RomanceMoviesReducer,
documentary: DocumentaryReducer documentary: DocumentaryReducer,
}); });
export default rootReducer; export default rootReducer;

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_ACTION_MOVIES: case FETCH_ACTION_MOVIES:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_COMEDY_MOVIES: case FETCH_COMEDY_MOVIES:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_DOCUMENTARIES: case FETCH_DOCUMENTARIES:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_HORROR_MOVIES: case FETCH_HORROR_MOVIES:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_NETFLIX_ORIGINALS: case FETCH_NETFLIX_ORIGINALS:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_ROMANCE_MOVIES: case FETCH_ROMANCE_MOVIES:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_TOP_RATED: case FETCH_TOP_RATED:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -4,8 +4,8 @@ export default function (state = {}, action) {
switch (action.type) { switch (action.type) {
case FETCH_TRENDING: case FETCH_TRENDING:
const data = action.payload.data.results; const data = action.payload.data.results;
return { ...state, data } return { ...state, data };
default: default:
return state; return state;
} }
} }

View File

@@ -1,74 +1,89 @@
const HtmlWebPackPlugin = require("html-webpack-plugin"); const HtmlWebPackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require("copy-webpack-plugin"); const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin');
const dotenv = require('dotenv');
const webpack = require('webpack');
module.exports = () => {
// call dotenv and it will return an Object with a parsed key
const env = dotenv.config().parsed;
module.exports = { // reduce env variables to an oject
const envKeys = Object.keys(env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(env[next]);
return prev;
}, {});
entry: "./src/index.js", console.log('keys', envKeys);
output: {
filename: "bundle.js", return {
}, entry: './src/index.js',
module: { output: {
filename: 'bundle.js',
},
module: {
rules: [ rules: [
{ {
test: /\.js$/, test: /\.js$/,
exclude: /node_modules/, exclude: /node_modules/,
use: { use: {
loader: "babel-loader" loader: 'babel-loader',
} },
}, },
{ {
test: /\.svg$/, test: /\.svg$/,
exclude: /node_modules/, exclude: /node_modules/,
use: { use: {
loader: 'svg-react-loader' loader: 'svg-react-loader',
} },
},
}, {
{ test: /\.scss$/,
test: /\.scss$/, use: [
use: [ {
{ loader: MiniCssExtractPlugin.loader,
loader: MiniCssExtractPlugin.loader, options: {
options: { publicPath: '../',
publicPath: '../' },
} },
}, 'css-loader',
'css-loader', 'sass-loader',
'sass-loader' ],
] },
}, {
{ test: /\.(gif|png|jpe?g)$/i,
test: /\.(gif|png|jpe?g)$/i, use: [
use: [ 'file-loader',
'file-loader', {
{ loader: 'image-webpack-loader',
loader: 'image-webpack-loader', options: {
options: { bypassOnDebug: true, // webpack@1.x
bypassOnDebug: true, // webpack@1.x disable: true, // webpack@2.x and newer
disable: true, // webpack@2.x and newer },
}, },
}, ],
], },
} ],
] },
}, node: {
node: { fs: 'empty',
fs: "empty" },
}, plugins: [
plugins: [ new webpack.DefinePlugin(envKeys),
new HtmlWebPackPlugin({ new HtmlWebPackPlugin({
template: "./src/index.html", template: './src/index.html',
filename: "./index.html" filename: './index.html',
}), }),
new CopyWebpackPlugin([{ from: 'src/static/images', to: 'static/images' }]), new CopyWebpackPlugin([
{ from: 'src/static/images', to: 'static/images' },
]),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output // Options similar to the same options in webpackOptions.output
// both options are optional // both options are optional
filename: "main.css" filename: 'main.css',
}), }),
new CleanWebpackPlugin(['dist']), new CleanWebpackPlugin(['dist']),
] ],
}; };
};