-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
weekend react cinema #19
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,13 @@ | ||
# React Cinema | ||
React Cinema | ||
|
||
Let's revisit our first project where we built a movie search engine using the Open Movie Database. This time we want to implement it using React. It should be a Single Page App, that is all the functionality should be on a single page, rather switch between multiple pages. | ||
Functionality | ||
-This is a movie title search engine. | ||
-Simply enter your query into the search bar. | ||
-20 movie results will be displayed on search and you can view further movies by clicking on the view next 20 results button as many times as you wish. | ||
-If you want further information on a particular movie, just click on the movie tile. This will display further information on the movie in a pop up. | ||
|
||
Before starting to code produce a diagram using pen and paper of your application which shows both layout and a tree diagram of the components. | ||
|
||
What are some of the components you are going to need? Which components will fetch data and how will that data be displayed? Which components will store state and how will they pass data to other components? Which components should be re-used? Rather than re-implementing your previous solution again have a think about what you have learned in the past week and how you can apply it here. | ||
|
||
You can start coding once your plan has been checked by a TA or instructor. | ||
|
||
## The brief | ||
|
||
We want to create a movie search engine. To power it we will use the [Open Movie Database](http://www.omdbapi.com) API. | ||
|
||
To start using the OMDB API you will first need to sign up with them to receive and API key. The key issued to you will allow you 1000 requests per day and you will need to include this key as part of every request. | ||
|
||
To get started, fork and clone this repo. Please submit a pull request after your first commit and push commits regularly. | ||
|
||
You should complete as many of the following tasks as you can. | ||
|
||
- [ ] Work using mobile first, that is create the mobile version first and add tablet and desktop versions after. | ||
- [ ] Create an HTML page which should have a `form` at the top which contains a text input and a submit button. Below it should have a placeholder element for the returned results. | ||
- [ ] Use JavaScript to capture the `submit` event in your search form, extract the query string from the text input and use that to make an API call to the Open Movie Database API to search for films which match the query string using `fetch`. `console.log` the results | ||
- [ ] Display the data returned by the API including title, year and poster picture | ||
|
||
**Movie details** | ||
|
||
- [ ] Adjust your layout to create room for a detailed view of movie information | ||
- [ ] Capture clicks on your movie results items and use that information to make another request to the API for detailed movie information. Using event delegation will help you here. `console.log` the returned result | ||
- [ ] Display the detailed movie result in the in the details view you created earlier | ||
- [ ] Make your design responsive and ensure it looks great at different screen widths | ||
|
||
**Your own feature** | ||
|
||
- [ ] Implement any feature you would find useful or interesting | ||
|
||
**Stretch goals** | ||
|
||
- [ ] Implement pagination so that users can navigate between all movies in search results rather than just the first ten | ||
- [ ] Create a favourites list. It's up to you how you would add items to favourites. You could add a button or otherwise. Display a list of favourites somewhere on your page. | ||
- [ ] Make the favourites list sortable. Add `up` and `down` buttons to your favourites which on click will move the result in relevant direction | ||
- [ ] Save favourites locally using `localStorage` so that favourites persist in browser after refresh | ||
- [ ] Let's create a search preview. It should listen for input events and submit a search request with current query string. Display the search preview results in an absolute positioned container just below the search box. | ||
Hint: You may want to kick of the searching after at least 3 characters have been typed. | ||
|
||
## Objectives | ||
|
||
* We want to see great looking webpages that work well at all screen widths | ||
* Your code should have consistent indentation and sensible naming | ||
* Use lots of concise, reusable functions with a clear purpose | ||
* Add code comments where it is not immediately obvious what your code does | ||
* Your code should not throw errors and handle edge cases gracefully. For example not break if server fails to return expected results | ||
* Use BEM methodology to style your page | ||
* Try to use pure functions as much as possible, but keep in mind it will not be possible to make all functions pure. | ||
|
||
## README.md | ||
|
||
When finished, include a README.md in your repo. Someone who is not familiar with the project should be able to look at it and understand what it is and what to do with it. Explain functionality created, mention any outstanding issues and possible features you would include if you had more time. List technologies used to create the app. Include a screenshot of your app in the README. | ||
Technical information | ||
-React components are used (Search, Movies, Movie) | ||
-The additional information is shown in a modal. | ||
The OMDB API is used for the movie search. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,74 @@ | ||
import React from 'react'; | ||
import Search from './Search.js' | ||
import Movies from './Movies.js' | ||
|
||
class App extends React.Component { | ||
constructor(){ | ||
super(); | ||
|
||
this.state ={ | ||
pageNumber: 1, | ||
searchQuery: "", | ||
movies: [], | ||
videoClass: "video" | ||
} | ||
|
||
this.apiCall = this.apiCall.bind(this); | ||
this.submittedSearchQuery = this.submittedSearchQuery.bind(this); | ||
this.receivePaginationQuery = this.receivePaginationQuery.bind(this); | ||
this.receiveVideoBlock = this.receiveVideoBlock.bind(this); | ||
} | ||
|
||
componentDidMount(){ | ||
|
||
} | ||
|
||
apiCall(){ | ||
var url = `http://www.omdbapi.com/?apikey=77164d83&s=${this.state.searchQuery}&page=${[this.state.pageNumber]}` | ||
fetch(url) | ||
.then(function(response) { | ||
return response.json(); | ||
}) | ||
.then(body => { | ||
console.log(body.Search) | ||
this.setState({ | ||
movies: body.Search | ||
}) | ||
}) | ||
} | ||
|
||
submittedSearchQuery(search){ | ||
console.log(search) | ||
this.setState({ | ||
pageNumber: 1, | ||
searchQuery: search | ||
},() => this.apiCall() | ||
) | ||
} | ||
|
||
receivePaginationQuery(){ | ||
this.setState({ | ||
pageNumber: ++this.state.pageNumber | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling ++ mutates state. It would be better to use this.state.pageNumber + 1 to increment value |
||
},() => this.apiCall() | ||
) | ||
} | ||
|
||
receiveVideoBlock(){ | ||
this.setState({ | ||
videoClass: "video2" | ||
}) | ||
} | ||
|
||
|
||
render(){ | ||
return ( | ||
<div> | ||
React cinema app | ||
<header><h1>Screen Search</h1></header> | ||
<Search submittedSearchQuery={this.submittedSearchQuery} receivePaginationQuery={this.receivePaginationQuery} receiveVideoBlock={this.receiveVideoBlock}/> | ||
<Movies movies={this.state.movies} /> | ||
<div className="main"> | ||
<video className={this.state.videoClass} src="007.mp4" autoPlay loop muted></video> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import React from 'react'; | ||
|
||
class Modal extends React.Component{ | ||
constructor(){ | ||
super(); | ||
|
||
|
||
this.handleClick = this.handleClick.bind(this); | ||
|
||
} | ||
|
||
handleClick(event){ | ||
this.props.receiveModalCloseBtn() | ||
} | ||
|
||
|
||
render(){ | ||
|
||
|
||
return( | ||
<div id="simpleModal" className={this.props.className}> | ||
<div className="modal-content"> | ||
<div className="modal-header"> | ||
<span className="closeBtn" onClick={this.handleClick}>×</span> | ||
<h2>Details</h2> | ||
</div> | ||
<div className="modal-body"> | ||
<ul> | ||
<li>Director: {this.props.modalDetails.Director}</li> | ||
<li>Runtime: {this.props.modalDetails.Runtime}</li> | ||
<li>IMDB Rating: {this.props.modalDetails.imdbRating}</li> | ||
<li>IMDB Votes: {this.props.modalDetails.imdbVotes}</li> | ||
<li>Rating: {this.props.modalDetails.Rated}</li> | ||
<li>Awards: {this.props.modalDetails.Awards}</li> | ||
<li>Actors: {this.props.modalDetails.Actors}</li> | ||
</ul> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
} | ||
|
||
export default Modal |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from 'react'; | ||
import Modal from './Modal.js' | ||
|
||
class Movie extends React.Component{ | ||
constructor(){ | ||
super(); | ||
|
||
this.state = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it looks like this is not in use and can be removed |
||
details: null | ||
} | ||
|
||
this.handleClick = this.handleClick.bind(this); | ||
} | ||
|
||
handleClick(event) { | ||
this.props.receiveModal(this.props.movie.imdbID); | ||
} | ||
|
||
|
||
|
||
|
||
render(){ | ||
return( | ||
<div onClick={this.handleClick}> | ||
<img id="posterImage" src={this.props.movie.Poster} onError={(e)=>{e.target.onerror = null; e.target.src="imagefound.png"}}/> | ||
<h2>{this.props.movie.Title} ({this.props.movie.Year})</h2> | ||
</div> | ||
) | ||
} | ||
|
||
} | ||
|
||
|
||
export default Movie |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import React from 'react'; | ||
import Movie from './Movie.js'; | ||
import Modal from './Modal.js' | ||
|
||
class Movies extends React.Component { | ||
constructor(){ | ||
super(); | ||
|
||
this.state = { | ||
modalDetails: "", | ||
modalClass: "modal" | ||
} | ||
|
||
//binding here | ||
this.receiveModal = this.receiveModal.bind(this); | ||
this.receiveModalCloseBtn = this.receiveModalCloseBtn.bind(this); | ||
this.modalOutsideClick = this.modalOutsideClick.bind(this); | ||
} | ||
|
||
receiveModal(modal){ | ||
console.log(modal); | ||
var url = `http://www.omdbapi.com/?apikey=77164d83&i=${modal}` | ||
fetch(url) | ||
.then(function(response) { | ||
return response.json(); | ||
}) | ||
.then(body => { | ||
console.log(body) | ||
this.setState({ | ||
modalDetails: body, | ||
modalClass: "modal2" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be better to make this property a boolean and set the actual class name in render method. the reason for that is that if you wanted to change the class name in future, you would need to do it in mutiple places. if you set the class name in render depending on the state of the boolean, you would only need to change it in one place |
||
}) | ||
}) | ||
console.log(this.modalDetails) | ||
} | ||
|
||
receiveModalCloseBtn(){ | ||
this.setState({ | ||
modalClass: "modal" | ||
}) | ||
} | ||
|
||
modalOutsideClick(event){ | ||
if(event.target.className == "modal2"){ | ||
this.setState({ | ||
modalClass: "modal" | ||
}) | ||
} | ||
} | ||
|
||
|
||
|
||
|
||
|
||
render(){ | ||
return( | ||
<div onClick={this.modalOutsideClick}> | ||
<div> | ||
<Modal receiveModalCloseBtn={this.receiveModalCloseBtn} modalDetails={this.state.modalDetails} className={this.state.modalClass}/> | ||
</div> | ||
<div className="main"> | ||
{this.props.movies.map( movie => { | ||
return ( | ||
<div className="movieItem" key={movie.imdbID}> | ||
<Movie key={movie.imdbID} movie={movie} receiveModal={this.receiveModal}/> | ||
</div> | ||
) | ||
})} | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
} | ||
|
||
export default Movies |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from 'react'; | ||
|
||
class Search extends React.Component { | ||
constructor(){ | ||
super(); | ||
|
||
this.state = { | ||
searchQuery: "" | ||
} | ||
|
||
this.handleChange = this.handleChange.bind(this); | ||
this.handleSubmit = this.handleSubmit.bind(this); | ||
this.receivePaginationQuery = this.receivePaginationQuery.bind(this); | ||
|
||
} | ||
|
||
handleChange(event){ | ||
this.setState({ | ||
searchQuery: event.target.value | ||
}) | ||
let searchEntry = event.target.value | ||
if (searchEntry.length > 2){ | ||
handleSubmit(event) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are passing a change event into a submit event handler which might make things go funny. It would be better to split out functionality you want to share between two event handlers into own function and call that from both event handlers |
||
} | ||
} | ||
|
||
handleSubmit(event){ | ||
event.preventDefault(); | ||
this.props.receiveVideoBlock(); | ||
this.props.submittedSearchQuery(this.state.searchQuery); | ||
} | ||
//could directly call apiCall | ||
|
||
receivePaginationQuery(){ | ||
this.props.receivePaginationQuery(); | ||
} | ||
|
||
render(){ | ||
return( | ||
<form className="searchForm" onSubmit={this.handleSubmit}> | ||
<input placeholder="Enter your search terms here" onChange={this.handleChange}/> | ||
<button>Search</button><input onClick={this.receivePaginationQuery} className="next" type="button" value="Next 20 Results"/ > | ||
</form> | ||
) | ||
} | ||
|
||
} | ||
|
||
export default Search; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
method is not in use and can be removed