From 236e364e993c833c40e43e407454ff1c8d08469d Mon Sep 17 00:00:00 2001 From: haavarhu <havard.hunshamar@ntnu.no> Date: Sun, 11 Nov 2018 12:42:52 +0100 Subject: [PATCH 1/4] made component for moveidetailscontent #38 --- .../src/components/MovieDetailsContent.js | 431 ++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 moviesearch/src/components/MovieDetailsContent.js diff --git a/moviesearch/src/components/MovieDetailsContent.js b/moviesearch/src/components/MovieDetailsContent.js new file mode 100644 index 0000000..2e37a3e --- /dev/null +++ b/moviesearch/src/components/MovieDetailsContent.js @@ -0,0 +1,431 @@ +import React, { Component } from "react"; +import Modal from "@material-ui/core/Modal"; +import { withStyles } from "@material-ui/core/styles"; +import PropTypes from "prop-types"; +import Grid from "@material-ui/core/Grid"; +import { connect } from "react-redux"; +import Button from "@material-ui/core/Button"; +import { updateMovie } from "../actions/updateMovieAction"; +import { nextMovie, prevMovie } from "../actions/browseMoviesAction"; +import MaterialIcon from "material-icons-react"; +import RatingComponent from "./RatingComponent"; +import ReviewsComponent from "./ReviewsComponent"; +import AddReviewComponent from "./AddReviewComponent"; +import StarRatings from "react-star-ratings"; +import { sortMovies } from "../actions/sortMoviesAction"; + +class MovieDetailsContent extends Component { + constructor(props) { + super(props); + this.state = { showMovieList: false }; + } + searchByYear = () => { + this.props.dispatch({ + type: "UPDATE_YEAR", + payload: this.props.selectedMovie.year + }); + + let sortBy = "vote_average"; + + this.props.dispatch( + sortMovies("None", this.props.selectedMovie.year, "None", sortBy) + ); + this.props.dispatch({ type: "RESET" }); + this.hideDetails(); + }; + + searchByGenre = () => { + this.props.dispatch({ type: "UPDATE_GENRE", payload: "Drama" }); + + let sortBy = "vote_average"; + + this.props.dispatch(sortMovies("None", "None", "Drama", sortBy)); + this.props.dispatch({ type: "RESET" }); + this.hideDetails(); + }; + + changeRating = rating => { + this.props.dispatch({ + type: "UPDATE_RATING", + rating: Number(rating) + }); + }; + + handleKeyPress = event => { + switch (event.key) { + case "ArrowRight": { + this.props.dispatch( + nextMovie(this.props.selectedMovie, this.props.movies) + ); + break; + } + case "ArrowLeft": { + this.props.dispatch( + prevMovie(this.props.selectedMovie, this.props.movies) + ); + break; + } + default: + } + }; + + hideDetails = () => { + this.props.dispatch({ type: "UNSELECT_MOVIE" }); + this.props.dispatch({ type: "RESET_REVIEW_FIELDS" }); + this.props.dispatch({ type: "SET_HOVER", payload: false }); + }; + + hover = () => { + this.props.dispatch({ type: "SET_HOVER", payload: true }); + }; + unHover = () => { + this.props.dispatch({ type: "SET_HOVER", payload: false }); + }; + + addRating = () => { + if (this.props.rating > 0) { + this.props.selectedMovie["feedbackList"].push({ + name: "", + rating: this.props.rating, + review: "" + }); + this.props.dispatch(updateMovie(this.props.selectedMovie)); + } else { + console.log("error: cant rate movie zero stars"); + } + }; + + addReview = () => { + if (this.props.openReview) { + if (this.props.rating > 0) { + this.props.selectedMovie["feedbackList"].push({ + name: this.props.name.trim(), + rating: this.props.rating, + review: this.props.review.trim() + }); + this.props.dispatch(updateMovie(this.props.selectedMovie)); + } else { + console.log("feltene er ikke fylt ut"); + } + } else { + this.props.dispatch({ type: "OPEN_REVIEW", payload: true }); + } + }; + + render() { + let movie = this.props.selectedMovie; + const { classes } = this.props; + + return ( + <div> + <div + className={classes.exitButton} + onClick={this.hideDetails} + onMouseEnter={this.hover} + onMouseLeave={this.unHover} + > + <MaterialIcon + key={this.props.hover ? 1 : 2} + icon="close" + color={this.props.hover ? "white" : "rgb(50,50,50"} + size={20} + /> + </div> + <img + src={"https://image.tmdb.org/t/p/original" + movie.backdrop_path} + width={"100%"} + alt={movie.title} + className={classes.backdrop} + /> + <div className={classes.movieCard}> + <Grid container spacing={0} style={{ margin: 0 }}> + <Grid item xs={5}> + <img + src={"https://image.tmdb.org/t/p/w500" + movie.poster_path} + alt={movie.title} + className={classes.poster} + /> + </Grid> + <Grid item xs style={{ padding: "24px" }}> + <span + className={classes.header} + style={{ display: "inline-block" }} + > + {String(movie.title).toUpperCase()} + </span> + <br /> + <span + onClick={this.searchByYear} + className={classes.link} + style={{ cursor: "pointer" }} + > + {movie.year} + </span> + {"Directed by "} + {movie.director.map((director, index) => ( + <span> + <span + className={classes.link} + style={{ cursor: "pointer" }} + onClick={() => + this.props.dispatch({ + type: "SHOW_MOVIE_LIST", + showMovies: true + }) + } + > + {director.name} + </span> + {movie.director.length !== index + 1 && ", "} + </span> + ))} + {averageRating(movie.feedbackList) > 0 && ( + <span> + <RatingComponent + rating={averageRating(movie.feedbackList)} + text1="User" + text2={movie.feedbackList.length} + /> + </span> + )} + <RatingComponent + text1={"IMDb"} + text2={movie.vote_count} + rating={movie.vote_average} + /> + <span className={classes.greenHeader}>{movie.tagline}</span> + <span> {movie.overview} </span> + <span className={classes.greenHeader}> + {printList(movie.genres)} + </span> + <span>{printList(movie.production_companies)}</span> + <span className={classes.greenHeader}> Cast </span>{" "} + {movie.cast.slice(0, 10).map((cast, index) => ( + <span> + <span className={classes.link}>{cast.name}</span> + {index !== 9 && ", "} + </span> + ))} + <br /> + <hr style={{ marginBottom: 3, marginTop: 30 }} size={1} /> + {this.props.openReview && ( + <AddReviewComponent classes={classes} /> + )} + {!this.props.openReview && ( + <span style={{ paddingRight: "5%" }}> + <br /> + Rate {movie.title} + <span> + + {"- User rating: "} + {averageRating(movie.feedbackList) > 0 ? ( + <span> + {averageRating(movie.feedbackList)} + <span style={{ fontSize: "0.65em" }}>/10</span> + <span + style={{ + fontSize: "0.7em", + color: "rgb(200,200,200)" + }} + > + (votes: {movie.feedbackList.length}) + </span> + </span> + ) : ( + "No users have voted" + )} + </span> + <br /> + <StarRatings + id="rating-input" + rating={this.props.rating} + starRatedColor="#C39400" + starHoverColor="#C39400" + changeRating={this.changeRating} + name="rating-input" + numberOfStars={10} + starDimension="25px" + starSpacing="1px" + /> + <br /> + <br /> + <Button onClick={this.addRating} variant="contained"> + rate + </Button> + </span> + )} + <span> + <Button onClick={this.addReview} variant="contained"> + {this.props.openReview ? "post review" : "add review"} + </Button> + </span> + <br /> + <br /> + <ReviewsComponent + feedbackList={movie.feedbackList.filter(fb => fb.name !== "")} + averageUserScore={averageRating( + movie.feedbackList.filter(fb => fb.name !== "") + )} + /> + </Grid> + </Grid> + </div> + </div> + ); + } +} + +function printList(list) { + let string = ""; + for (let i = 0; i < list.length; i++) { + string += list[i]; + if (i !== list.length - 1) { + string += ", "; + } + } + return string; +} + +function averageRating(feedbackList) { + let total = 0; + for (let i = 0; i < feedbackList.length; i++) { + total += feedbackList[i].rating; + } + return (total / feedbackList.length).toFixed(1); +} + +MovieDetailsContent.propTypes = { + classes: PropTypes.object.isRequired +}; + +const styles = theme => ({ + paper: { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%) !important", + width: "85%", + height: "auto", + minHeight: "75%", + backgroundColor: "black", + boxShadow: theme.shadows[5], + margin: "0 auto", + outline: "none" + }, + backdrop: { + opacity: "0.8", + filter: "blur(3px)" + }, + movieCard: { + color: "white", + fontFamily: "Oswald", + fontSize: "1em", + backgroundColor: "rgba(0,0,0,0.75)", + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%) !important", + width: "60%", + height: "66%", + overflowY: "scroll", + outline: "none" + }, + poster: { + width: "100%", + position: "-webkit-sticky", + position: "sticky", + top: 0 + }, + header: { + fontFamily: "Lato", + fontWeight: "bold", + fontSize: "2.2em", + margin: 0, + display: "inline" + }, + greenHeader: { + display: "block", + marginTop: "1em", + color: "#02FB87", + fontSize: "1.3em", + fontFamily: "Oswald", + fontWeight: "normal" + }, + textInput: { + color: "white" + }, + bootstrapRoot: { + "label + &": { + marginTop: theme.spacing.unit * 3 + } + }, + bootstrapInput: { + borderRadius: 4, + backgroundColor: "rgb(200,200,200)", + border: "1px solid #ced4da", + fontSize: 16, + padding: "10px 12px", + transition: theme.transitions.create(["border-color", "box-shadow"]) + }, + bootstrapFormLabel: { + fontSize: 18, + color: "white", + "&:focus": { + color: "white" + } + }, + margin: { + margin: theme.spacing.unit + }, + link: { + color: "white", + textDecoration: "underline" + }, + + exitButton: { + cursor: "pointer", + height: "20px", + width: "20px", + zIndex: 5, + position: "absolute", + right: "20%", + top: "14%", + borderRadius: "50%", + textAlign: "center", + transform: "translate(30%, 80%) !important", + backgroundColor: "#02FB87", + boxShadow: "-2px 2px 3px" + }, + exitForm: { + float: "right", + cursor: "pointer" + } +}); + +const mapStateToProps = state => ({ + movies: state.search.movies, + selectedMovie: state.selectMovie.selectedMovie, + cachedImages: state.selectMovie.cachedImages, + name: state.textField.name, + rating: state.textField.rating, + review: state.textField.review, + hover: state.closeButton.hover, + openReview: state.textField.openReview, + searchText: state.textField.text, + movies: state.search.movies, + selectedYear: state.yearField.selectedYear, + years: state.yearField.years, + selectedGenre: state.genreField.selectedGenre, + genres: state.genreField.genres, + sortedMovies: state.sort.sortedMovies, + page: state.pageReducer, + fetching: state.search.fetching, + returnedNull: state.search.returnedNull, + sortAlph: state.sort.sortAlph, + sortYear: state.sort.sortYear, + sortRating: state.sort.sortRating +}); + +export default connect(mapStateToProps)( + withStyles(styles)(MovieDetailsContent) +); -- GitLab From d2ca02223ab5779f5cb8f6ab2f0c8075647826d0 Mon Sep 17 00:00:00 2001 From: haavarhu <havard.hunshamar@ntnu.no> Date: Sun, 11 Nov 2018 12:45:33 +0100 Subject: [PATCH 2/4] Choose year to search from moviedetails #43 --- .../src/components/MovieDetailsComponent.js | 241 +----------------- moviesearch/src/reducers/yearReducer.js | 2 +- 2 files changed, 12 insertions(+), 231 deletions(-) diff --git a/moviesearch/src/components/MovieDetailsComponent.js b/moviesearch/src/components/MovieDetailsComponent.js index 8c91f2d..db669ce 100644 --- a/moviesearch/src/components/MovieDetailsComponent.js +++ b/moviesearch/src/components/MovieDetailsComponent.js @@ -2,15 +2,10 @@ import React, { Component } from "react"; import Modal from "@material-ui/core/Modal"; import { withStyles } from "@material-ui/core/styles"; import PropTypes from "prop-types"; -import Grid from "@material-ui/core/Grid"; import { connect } from "react-redux"; -import Button from "@material-ui/core/Button"; -import { updateMovie } from "../actions/updateMovieAction"; import { nextMovie, prevMovie } from "../actions/browseMoviesAction"; -import MaterialIcon from "material-icons-react"; -import RatingComponent from "./RatingComponent"; -import ReviewsComponent from "./ReviewsComponent"; -import AddReviewComponent from "./AddReviewComponent"; +import MovieDetailsContent from "./MovieDetailsContent"; +import MovieListComponent from "./MovieListComponent"; class MovieDetailsComponent extends Component { handleKeyPress = event => { @@ -35,30 +30,7 @@ class MovieDetailsComponent extends Component { this.props.dispatch({ type: "UNSELECT_MOVIE" }); this.props.dispatch({ type: "RESET_REVIEW_FIELDS" }); this.props.dispatch({ type: "SET_HOVER", payload: false }); - }; - - hover = () => { - this.props.dispatch({ type: "SET_HOVER", payload: true }); - }; - unHover = () => { - this.props.dispatch({ type: "SET_HOVER", payload: false }); - }; - - addReview = () => { - if (this.props.openReview) { - if (this.props.rating > 0) { - this.props.selectedMovie["feedbackList"].push({ - name: this.props.name.trim(), - rating: this.props.rating, - review: this.props.review.trim() - }); - this.props.dispatch(updateMovie(this.props.selectedMovie)); - } else { - console.log("feltene er ikke fylt ut"); - } - } else { - this.props.dispatch({ type: "OPEN_REVIEW", payload: true }); - } + this.props.dispatch({ type: "SHOW_MOVIE_LIST", showMovieList: false }); }; render() { @@ -71,116 +43,18 @@ class MovieDetailsComponent extends Component { onBackdropClick={this.hideDetails} onEscapeKeyDown={this.hideDetails} > - {movie != null && ( - <div className={classes.paper} onKeyDown={this.handleKeyPress}> - <div - className={classes.exitButton} - onClick={this.hideDetails} - onMouseEnter={this.hover} - onMouseLeave={this.unHover} - > - <MaterialIcon - key={this.props.hover ? 1 : 2} - icon="close" - color={this.props.hover ? "white" : "rgb(50,50,50"} - size={20} - /> - </div> - <img - src={"https://image.tmdb.org/t/p/original" + movie.backdrop_path} - width={"100%"} - alt={movie.title} - className={classes.backdrop} - /> - <div className={classes.movieCard}> - <Grid container spacing={0} style={{ margin: 0 }}> - <Grid item xs={5}> - <img - src={"https://image.tmdb.org/t/p/w500" + movie.poster_path} - alt={movie.title} - className={classes.poster} - /> - </Grid> - <Grid item xs style={{ padding: "24px" }}> - <span - className={classes.header} - style={{ display: "inline-block" }} - > - {String(movie.title).toUpperCase()} - </span> - <br /> - <span className={classes.link}>{movie.year}</span> - {"Directed by "} - {movie.director.map((director, index) => ( - <span> - <span className={classes.link}>{director.name}</span> - {movie.director.length !== index + 1 && ", "} - </span> - ))} - <RatingComponent rating={movie.vote_average} /> - {averageRating(movie.feedbackList) > 0 && ( - <RatingComponent - rating={averageRating(movie.feedbackList)} - /> - )} - <span className={classes.greenHeader}>{movie.tagline}</span> - <span> {movie.overview} </span> - <span className={classes.greenHeader}> - {printList(movie.genres)} - </span> - <span>{printList(movie.production_companies)}</span> - <span className={classes.greenHeader}> Cast </span>{" "} - {movie.cast.slice(0, 10).map((cast, index) => ( - <span> - <span className={classes.link}>{cast.name}</span> - {index !== 9 && ", "} - </span> - ))} - <br /> - {this.props.openReview && ( - <AddReviewComponent classes={classes} /> - )} - <br /> - <Button onClick={this.addReview} variant="contained"> - {this.props.openReview ? "post review" : "add review"} - </Button> - <br /> - <br /> - <ReviewsComponent - feedbackList={movie.feedbackList.filter( - fb => fb.name !== "" - )} - averageUserScore={averageRating(movie.feedbackList)} - /> - </Grid> - </Grid> - </div> - </div> - )} + <div className={classes.paper} onKeyDown={this.handleKeyPress}> + {movie != null && !this.props.showMovies ? ( + <MovieDetailsContent /> + ) : ( + <MovieListComponent /> + )} + </div> </Modal> ); } } -function printList(list) { - let string = ""; - for (let i = 0; i < list.length; i++) { - string += list[i]; - if (i !== list.length - 1) { - string += ", "; - } - } - return string; -} - -function averageRating(feedbackList) { - let total = 0; - for (let i = 0; i < feedbackList.length; i++) { - total += feedbackList[i].rating; - } - return (total / feedbackList.length).toFixed(1); -} - MovieDetailsComponent.propTypes = { classes: PropTypes.object.isRequired }; @@ -198,106 +72,13 @@ const styles = theme => ({ boxShadow: theme.shadows[5], margin: "0 auto", outline: "none" - }, - backdrop: { - opacity: "0.8", - filter: "blur(3px)" - }, - movieCard: { - color: "white", - fontFamily: "Oswald", - fontSize: "1em", - backgroundColor: "rgba(0,0,0,0.75)", - position: "absolute", - top: "50%", - left: "50%", - transform: "translate(-50%, -50%) !important", - width: "60%", - height: "66%", - overflowY: "scroll", - outline: "none" - }, - poster: { - width: "100%", - position: "-webkit-sticky", - position: "sticky", - top: 0 - }, - header: { - fontFamily: "Lato", - fontWeight: "bold", - fontSize: "2.2em", - margin: 0, - display: "inline" - }, - greenHeader: { - display: "block", - marginTop: "1em", - color: "#02FB87", - fontSize: "1.3em", - fontFamily: "Oswald", - fontWeight: "normal" - }, - textInput: { - color: "white" - }, - bootstrapRoot: { - "label + &": { - marginTop: theme.spacing.unit * 3 - } - }, - bootstrapInput: { - borderRadius: 4, - backgroundColor: "rgb(200,200,200)", - border: "1px solid #ced4da", - fontSize: 16, - padding: "10px 12px", - transition: theme.transitions.create(["border-color", "box-shadow"]) - }, - bootstrapFormLabel: { - fontSize: 18, - color: "white", - "&:focus": { - color: "white" - } - }, - margin: { - margin: theme.spacing.unit - }, - link: { - color: "white", - textDecoration: "underline" - }, - - exitButton: { - cursor: "pointer", - height: "20px", - width: "20px", - zIndex: 5, - position: "absolute", - right: "20%", - top: "14%", - borderRadius: "50%", - textAlign: "center", - transform: "translate(30%, 80%) !important", - backgroundColor: "#02FB87", - boxShadow: "-2px 2px 3px" - }, - exitForm: { - float: "right", - cursor: "pointer" } }); const mapStateToProps = state => ({ movies: state.search.movies, selectedMovie: state.selectMovie.selectedMovie, - cachedImages: state.selectMovie.cachedImages, - name: state.textField.name, - rating: state.textField.rating, - review: state.textField.review, - hover: state.closeButton.hover, - openReview: state.textField.openReview + showMovies: state.showMovies.showMovies }); export default connect(mapStateToProps)( diff --git a/moviesearch/src/reducers/yearReducer.js b/moviesearch/src/reducers/yearReducer.js index 80c769a..59f8d7c 100644 --- a/moviesearch/src/reducers/yearReducer.js +++ b/moviesearch/src/reducers/yearReducer.js @@ -3,7 +3,7 @@ const initialState = { selectedYear: "None" }; -for (let i = 1980; i < 2019; i++) { +for (let i = 2018; i > 1918; i--) { initialState.years.push(String(i)); } -- GitLab From 141f812fbe124f9e98d2c889a04cbc9bcfefdb44 Mon Sep 17 00:00:00 2001 From: haavarhu <havard.hunshamar@ntnu.no> Date: Sun, 11 Nov 2018 12:46:35 +0100 Subject: [PATCH 3/4] improved rating/reviewing #19 --- .../src/components/AddReviewComponent.js | 3 +- moviesearch/src/components/RatingComponent.js | 58 ++++++++++++++----- .../src/components/ReviewsComponent.js | 31 ++++++---- 3 files changed, 64 insertions(+), 28 deletions(-) diff --git a/moviesearch/src/components/AddReviewComponent.js b/moviesearch/src/components/AddReviewComponent.js index 5dd850a..8789891 100644 --- a/moviesearch/src/components/AddReviewComponent.js +++ b/moviesearch/src/components/AddReviewComponent.js @@ -14,7 +14,7 @@ class AddReviewComponent extends Component { updateName = event => { this.props.dispatch({ type: "UPDATE_NAME", - name: event.target.value + name: event.target.value.trim() }); }; @@ -32,7 +32,6 @@ class AddReviewComponent extends Component { render() { return ( <div> - <hr style={{ marginBottom: 3, marginTop: 30 }} size={1} /> <div className={this.props.classes.exitForm} onClick={this.hideReview}> <MaterialIcon key={1} icon="close" color={"white"} size={20} /> </div> diff --git a/moviesearch/src/components/RatingComponent.js b/moviesearch/src/components/RatingComponent.js index 9046708..28d9412 100644 --- a/moviesearch/src/components/RatingComponent.js +++ b/moviesearch/src/components/RatingComponent.js @@ -7,22 +7,52 @@ class RatingComponent extends Component { <span style={{ float: "right", - display: "inline-block" + display: "inline-block", + margin: "2%" }} > - <StarRatings - rating={1} - numberOfStars={1} - starDimension="1.2em" - starRatedColor="#C39400" - />{" "} - <span> - <span style={{ fontSize: "1.2em" }}>{this.props.rating}</span> - <span style={{ fontSize: "0.8em", color: "rgb(200,200,200)" }}> - /10 - </span> - <br /> - </span> + <div style={{ display: "flex" }}> + <div style={{ lineHeight: "1.8em" }}> + <StarRatings + rating={1} + numberOfStars={1} + starDimension="1.2em" + starRatedColor="#C39400" + /> + {this.props.text1 && ( + <div + style={{ + fontSize: "0.7em", + textAlign: "center", + position: "relative", + top: "-1.2em" + }} + > + {this.props.text1} + </div> + )} + </div> + <div style={{ paddingLeft: "3px" }}> + <span> + <span style={{ fontSize: "1.2em" }}>{this.props.rating}</span> + <span style={{ fontSize: "0.8em", color: "rgb(200,200,200)" }}> + /10 + </span> + <br /> + </span> + <div + style={{ + fontWeight: 50, + fontSize: "0.7em", + position: "relative", + top: "-0.5em", + color: "rgb(200,200,200)" + }} + > + <span>{this.props.text2}</span> + </div> + </div> + </div> </span> ); } diff --git a/moviesearch/src/components/ReviewsComponent.js b/moviesearch/src/components/ReviewsComponent.js index 5938e15..eed1812 100644 --- a/moviesearch/src/components/ReviewsComponent.js +++ b/moviesearch/src/components/ReviewsComponent.js @@ -6,18 +6,25 @@ class ReviewsComponent extends Component { return ( <div> <span>REVIEWS</span> - {this.props.feedbackList.length > 1 && ( - <span style={{ float: "right" }}> - <span style={{ fontSize: "0.8em" }}>Score:</span> - - <StarRatings - rating={1} - numberOfStars={1} - starDimension="20px" - starRatedColor="#C39400" - /> - {this.props.averageUserScore} - <span style={{ fontSize: "0.65em" }}>/10</span> + {this.props.feedbackList.length > 0 && ( + <span> + <span style={{ color: "rgb(200,200,200)" }}> + {" (" + this.props.feedbackList.length}) + </span> + <span style={{ float: "right" }}> + <span style={{ fontSize: "0.8em" }}> + Average reviewer rating: + </span> + + <StarRatings + rating={1} + numberOfStars={1} + starDimension="20px" + starRatedColor="#C39400" + /> + {this.props.averageUserScore} + <span style={{ fontSize: "0.65em" }}>/10</span> + </span> </span> )} <hr style={{ marginTop: 3 }} size={2} /> -- GitLab From 90d04b09544df12eb6fb92827a719d5b8d5ab781 Mon Sep 17 00:00:00 2001 From: haavarhu <havard.hunshamar@ntnu.no> Date: Sun, 11 Nov 2018 12:48:59 +0100 Subject: [PATCH 4/4] Added list of movies by director in moviedetails #44 --- controllers/movie.sort.controller.js | 6 +- models/movie.server.model.js | 13 ++- moviesearch/public/index.html | 40 +++++--- .../src/actions/getDirectorMoviesAction.js | 20 ++++ moviesearch/src/actions/sortMoviesAction.js | 3 +- .../src/components/MovieListComponent.js | 99 +++++++++++++++++++ moviesearch/src/reducers/index.js | 4 +- moviesearch/src/reducers/searchReducer.js | 9 +- .../src/reducers/selectMovieReducer.js | 73 +++++++------- routes/movie.search.route.js | 4 +- routes/movie.sort.route.js | 6 +- 11 files changed, 218 insertions(+), 59 deletions(-) create mode 100644 moviesearch/src/actions/getDirectorMoviesAction.js create mode 100644 moviesearch/src/components/MovieListComponent.js diff --git a/controllers/movie.sort.controller.js b/controllers/movie.sort.controller.js index cf126ad..8d7bea1 100644 --- a/controllers/movie.sort.controller.js +++ b/controllers/movie.sort.controller.js @@ -6,7 +6,11 @@ export const sortMovies = (req, res) => { if (req.params.text !== "None") { console.log(req.params.text); let regex = new RegExp(req.params.text, "i"); - request["original_title"] = regex; + request["title"] = regex; + } + if (req.params.director !== "None") { + let regex = new RegExp(req.params.director, "i"); + request["director.name"] = regex; } if (req.params.year !== "None") { request["year"] = req.params.year; diff --git a/models/movie.server.model.js b/models/movie.server.model.js index df4c056..9935d2a 100644 --- a/models/movie.server.model.js +++ b/models/movie.server.model.js @@ -5,12 +5,15 @@ var movieSchema = mongoose.Schema({ _id: Number, original_title: String, year: String, + director: [{ name: String, id: Number }], genres: [String], - feedbackList: [{ - name: String, - rating: Number, - review: String, - }] + feedbackList: [ + { + name: String, + rating: Number, + review: String + } + ] }); var Movie = mongoose.model("movies", movieSchema); diff --git a/moviesearch/public/index.html b/moviesearch/public/index.html index 6edba60..9b59a77 100644 --- a/moviesearch/public/index.html +++ b/moviesearch/public/index.html @@ -1,22 +1,36 @@ <!DOCTYPE html> <html lang="en"> <head> - <meta charset="utf-8"> - <link rel="shortcut icon" href="%PUBLIC_URL%/movie.png"> - <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet"> - <link href="https://fonts.googleapis.com/css?family=Lato:700" rel="stylesheet"> - <link href="https://fonts.googleapis.com/css?family=Oswald:300" rel="stylesheet"> - <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <meta name="theme-color" content="#000000"> - <link rel="manifest" href="%PUBLIC_URL%/manifest.json"> + <meta charset="utf-8" /> + <link rel="shortcut icon" href="%PUBLIC_URL%/movie.png" /> + <link + href="https://fonts.googleapis.com/css?family=Raleway" + rel="stylesheet" + /> + <link + href="https://fonts.googleapis.com/css?family=Lato:700" + rel="stylesheet" + /> + <link + href="https://fonts.googleapis.com/css?family=Oswald:300" + rel="stylesheet" + /> + <link + rel="stylesheet" + href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" + integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" + crossorigin="anonymous" + /> + <meta + name="viewport" + content="width=device-width, initial-scale=1, shrink-to-fit=no" + /> + <meta name="theme-color" content="#000000" /> + <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>MovieSearch</title> </head> <body> - <noscript> - You need to enable JavaScript to run this app. - </noscript> + <noscript> You need to enable JavaScript to run this app. </noscript> <div id="root"></div> - </body> </html> diff --git a/moviesearch/src/actions/getDirectorMoviesAction.js b/moviesearch/src/actions/getDirectorMoviesAction.js new file mode 100644 index 0000000..eb2cdcc --- /dev/null +++ b/moviesearch/src/actions/getDirectorMoviesAction.js @@ -0,0 +1,20 @@ +export function getDirectorMovies(director) { + return function(dispatch) { + fetch("http://localhost:3001/sort/vote_average/None/None/None/" + director) + .then(response => response.json()) + .then(data => { + /*if (sortBy === "vote_average") { + data.movie = data.movie.filter(function(n) { + return n.vote_count > 1000; + }); + }*/ + dispatch({ + type: "RECEIVE_DIRECTOR_MOVIES", + payload: data.movie + }); + }) + .catch(err => { + dispatch({ type: "FAILED_SORT", payload: err }); + }); + }; +} diff --git a/moviesearch/src/actions/sortMoviesAction.js b/moviesearch/src/actions/sortMoviesAction.js index 133bbc5..6ea370b 100644 --- a/moviesearch/src/actions/sortMoviesAction.js +++ b/moviesearch/src/actions/sortMoviesAction.js @@ -8,7 +8,8 @@ export function sortMovies(text, year, genre, sortBy) { "/" + year + "/" + - genre + genre + + "/None" ) .then(response => response.json()) .then(data => { diff --git a/moviesearch/src/components/MovieListComponent.js b/moviesearch/src/components/MovieListComponent.js new file mode 100644 index 0000000..3061083 --- /dev/null +++ b/moviesearch/src/components/MovieListComponent.js @@ -0,0 +1,99 @@ +import React, { Component } from "react"; +import { withStyles } from "@material-ui/core/styles"; +import { connect } from "react-redux"; +import MovieCardComponent from "./MovieCardComponent"; +import MaterialIcon from "material-icons-react"; +import { getDirectorMovies } from "../actions/getDirectorMoviesAction"; + +class MovieListComponent extends Component { + constructor(props) { + super(props); + this.state = {}; + } + + componentDidMount() { + this.props.dispatch( + getDirectorMovies(this.props.selectedMovie.director[0].name) + ); + } + + render() { + const { classes } = this.props; + return ( + <div> + <div + className={classes.exitButton} + onClick={this.hideDetails} + onMouseEnter={this.hover} + onMouseLeave={this.unHover} + > + <MaterialIcon + key={this.props.hover ? 1 : 2} + icon="close" + color={this.props.hover ? "white" : "rgb(50,50,50"} + size={20} + /> + </div> + <img + src={ + "https://image.tmdb.org/t/p/original" + + this.props.selectedMovie.backdrop_path + } + width={"100%"} + alt={this.props.selectedMovie.title} + className={classes.backdrop} + /> + <div className={classes.movieCard}> + {this.props.directorMovies.slice(0, 10).map(movie => ( + <MovieCardComponent movie={movie} key={movie._id} /> + ))} + </div> + </div> + ); + } +} + +MovieListComponent.propTypes = {}; + +const styles = theme => ({ + movieCard: { + color: "white", + fontFamily: "Oswald", + fontSize: "1em", + backgroundColor: "rgba(0,0,0,0.75)", + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%) !important", + width: "60%", + height: "66%", + overflowY: "scroll", + outline: "none" + }, + backdrop: { + opacity: "0.8", + filter: "blur(3px)" + }, + exitButton: { + cursor: "pointer", + height: "20px", + width: "20px", + zIndex: 5, + position: "absolute", + right: "20%", + top: "14%", + borderRadius: "50%", + textAlign: "center", + transform: "translate(30%, 80%) !important", + backgroundColor: "#02FB87", + boxShadow: "-2px 2px 3px" + } +}); + +const mapStateToProps = state => ({ + movies: state.search.movies, + selectedMovie: state.selectMovie.selectedMovie, + directorMovies: state.search.directorMovies +}); + +export default connect(mapStateToProps)(withStyles(styles)(MovieListComponent)); diff --git a/moviesearch/src/reducers/index.js b/moviesearch/src/reducers/index.js index 58fee7a..b61b2e5 100644 --- a/moviesearch/src/reducers/index.js +++ b/moviesearch/src/reducers/index.js @@ -7,7 +7,8 @@ import genreField from "./genreReducer"; import selectMovie from "./selectMovieReducer"; import cacheImages from "./selectMovieReducer"; import pageReducer from "./pageReducer"; -import closeButton from "./closeButtonReducer" +import closeButton from "./closeButtonReducer"; +import showMovies from "./selectMovieReducer"; export default combineReducers({ search, @@ -19,4 +20,5 @@ export default combineReducers({ genreField, pageReducer, closeButton, + showMovies }); diff --git a/moviesearch/src/reducers/searchReducer.js b/moviesearch/src/reducers/searchReducer.js index 80384b4..643341d 100644 --- a/moviesearch/src/reducers/searchReducer.js +++ b/moviesearch/src/reducers/searchReducer.js @@ -3,7 +3,8 @@ const initialState = { fetched: false, movies: [], returnedNull: false, - error: null + error: null, + directorMovies: [] }; export default function reducer(state = initialState, action) { @@ -33,6 +34,12 @@ export default function reducer(state = initialState, action) { }; } } + case "RECEIVE_DIRECTOR_MOVIES": { + return { + ...state, + directorMovies: action.payload + }; + } case "FAILED_FETCH": { return { ...state, diff --git a/moviesearch/src/reducers/selectMovieReducer.js b/moviesearch/src/reducers/selectMovieReducer.js index 2bcd018..a5677b6 100644 --- a/moviesearch/src/reducers/selectMovieReducer.js +++ b/moviesearch/src/reducers/selectMovieReducer.js @@ -1,37 +1,42 @@ const initialState = { - selectedMovie : null, - cachedImages: null - }; - - export default function reducer(state = initialState, action) { - switch (action.type) { - case "SELECT_MOVIE": { - return { - ...state, - selectedMovie: action.selectedMovie - } - } - case "UNSELECT_MOVIE": { - return { - ...state, - selectedMovie: null - } - } - case "ADD_FEEDBACK": { - - return { - ...state, - selectedMovie: action.updatedMovie, - } - } - case "CACHE_IMAGES": { - return { - ...state, - cachedImages: action.cachedImages - } - } - default: - return state + selectedMovie: null, + cachedImages: null, + showMovies: false +}; + +export default function reducer(state = initialState, action) { + switch (action.type) { + case "SELECT_MOVIE": { + return { + ...state, + selectedMovie: action.selectedMovie + }; } + case "UNSELECT_MOVIE": { + return { + ...state, + selectedMovie: null + }; + } + case "SHOW_MOVIE_LIST": { + return { + ...state, + showMovies: action.showMovies + }; + } + case "ADD_FEEDBACK": { + return { + ...state, + selectedMovie: action.updatedMovie + }; + } + case "CACHE_IMAGES": { + return { + ...state, + cachedImages: action.cachedImages + }; + } + default: + return state; } - \ No newline at end of file +} diff --git a/routes/movie.search.route.js b/routes/movie.search.route.js index a42ab36..be7d2ee 100644 --- a/routes/movie.search.route.js +++ b/routes/movie.search.route.js @@ -3,6 +3,8 @@ import * as movieSearchController from "../controllers/movie.search.controller"; // get an instance of express router and send request to movie controller functions const router = express.Router(); -router.route("/:text/:year/:genre").get(movieSearchController.getMovie); +router + .route("/:text/:year/:genre/director") + .get(movieSearchController.getMovie); export default router; diff --git a/routes/movie.sort.route.js b/routes/movie.sort.route.js index a08f0dc..9a18c62 100644 --- a/routes/movie.sort.route.js +++ b/routes/movie.sort.route.js @@ -3,6 +3,8 @@ import * as movieSortController from "../controllers/movie.sort.controller"; // get an instance of express router and send request to movie controller functions const router = express.Router(); -router.route("/:sortBy/:text/:year/:genre").get(movieSortController.sortMovies); +router + .route("/:sortBy/:text/:year/:genre/:director") + .get(movieSortController.sortMovies); -export default router; \ No newline at end of file +export default router; -- GitLab