-
Notifications
You must be signed in to change notification settings - Fork 38
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
Luke Sikuade Project Cinema #24
base: master
Are you sure you want to change the base?
Changes from all commits
c715d03
f4fdd53
dd920d2
39eaa1b
7d8f2be
d996639
6599874
88d2631
d8f58a9
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,48 +1,23 @@ | ||
# Project Cinema | ||
## Cinema App | ||
This is a simple paginated website, presenting information from two APIs: omdbapi, and themoviedb. | ||
Search for a movie or show and receive results based on that query. Selecting results will bring up more detailed | ||
information, including a sample review. | ||
|
||
We want to create a movie search engine. To power it we will use the [Open Movie Database](http://www.omdbapi.com) API. | ||
## Setting Up | ||
Clone the repo: | ||
``` | ||
git clone [repo path] | ||
``` | ||
Install dependencies | ||
``` | ||
npm i | ||
``` | ||
Copy and paste the path of the index.html document into your browser. | ||
|
||
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. | ||
## Built With | ||
Vanilla JS | ||
CSS | ||
HTML | ||
|
||
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 change events on 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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> | ||
<link rel="stylesheet" type="text/css" href="style.css"> | ||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"> | ||
<title>Cinema App</title> | ||
</head> | ||
|
||
<body> | ||
<div> | ||
<header class="nav"> | ||
<form> | ||
<input class="search__input" type="text" placeholder="Search..." name="search" /> <button class="search__button" | ||
type="submit"> </button> | ||
</form> | ||
</header> | ||
</div> | ||
|
||
<div class="display__film"> | ||
<ul class="films"> | ||
</ul> | ||
</div> | ||
|
||
<div> | ||
<footer> | ||
</footer> | ||
</div> | ||
<script src="index.js"></script> | ||
</body> | ||
|
||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
const baseUrlSearch = "http://www.omdbapi.com/?apikey=305417ec&s="; | ||
const baseUrlSpecific = "http://www.omdbapi.com/?apikey=305417ec&t="; | ||
const filmContainer = document.querySelector(".films"); | ||
|
||
const formInput = document.querySelector("form"); | ||
formInput.addEventListener("submit", function(searchEvent) { | ||
filmContainer.innerHTML = ""; | ||
searchEvent.preventDefault(); | ||
let searchInput = document.querySelector(".search__input"); | ||
refineUrl(searchInput.value); | ||
}); | ||
|
||
const refineUrl = search => { | ||
let pageNum = 1; | ||
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. Wouldn't pageNum be reset to 1 every time this function is called this making pageNum++ redundant |
||
fetchRefined(`${baseUrlSearch}${search}&page=${pageNum}`); | ||
pageNum++; | ||
nextpage(`${baseUrlSearch}${search}`, pageNum); | ||
}; | ||
|
||
function fetchRefined(url) { | ||
fetch(url) | ||
.then(response => { | ||
return response.json(); | ||
}) | ||
.then(body => bottleneckFetchedData(body)); | ||
} | ||
//prettier-ignore | ||
const bottleneckFetchedData = films => films.Search.forEach(film => createFilmElement(film)); | ||
|
||
function createFilmElement(singleFilmObject) { | ||
const movieListElement = document.createElement("li"); | ||
movieListElement.className = "movie__element"; | ||
|
||
//prettier-ignore | ||
movieListElement.innerHTML = `<img class="movie__img" src='${singleFilmObject.Poster}'> | ||
<h2 class="movie__title">${singleFilmObject.Title}</h2><h2 class="movie__date">(${singleFilmObject.Year})</h2> | ||
<div class="image__interior"> | ||
<img src="images/question.png" class="more__info"> | ||
</div>`; | ||
appendIntoDocument(movieListElement); | ||
} | ||
|
||
const appendIntoDocument = movie => { | ||
filmContainer.appendChild(movie); | ||
}; | ||
|
||
function nextpage(search, pageNum) { | ||
const next = document.createElement("button"); | ||
next.className = "next__page"; | ||
next.textContent = "Next Page"; | ||
|
||
const header = document.querySelector("header"); | ||
header.appendChild(next); | ||
next.addEventListener("click", function(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. It would better to implement the click handler using delegation to avoid having to reset it each time function is called |
||
const films = document.querySelector(".films"); | ||
films.innerHTML = ""; | ||
const nextUrl = `${search}&page=${pageNum++}`; | ||
fetchRefined(nextUrl); | ||
}); | ||
} | ||
|
||
/////////////////////////////////////////////////////// | ||
|
||
filmContainer.addEventListener("click", function(event) { | ||
//prettier-ignore | ||
if (event.target.className === "more__info") { | ||
/*We need the parent node to (more readably) access | ||
the H2 tag. */ | ||
const superParent = event.target.parentNode.parentNode; | ||
const filmName = pullFilmName(superParent.childNodes); | ||
typeSearchFilm(filmName); | ||
//For mobile. | ||
}else if(event.target.className === "movie__img"){ | ||
const mobileSuperParent = event.target.parentNode; | ||
const mobileFilmName = pullFilmName(mobileSuperParent.childNodes); | ||
typeSearchFilm(mobileFilmName); | ||
} | ||
}); | ||
|
||
const pullFilmName = childElementArray => { | ||
console.log(childElementArray.length); | ||
return childElementArray[2].textContent; | ||
}; | ||
|
||
function typeSearchFilm(filmName) { | ||
console.log(filmName); | ||
const url = `${baseUrlSpecific}${filmName}`; | ||
fetch(url) | ||
.then(response => { | ||
return response.json(); | ||
}) | ||
.then(body => createInfoPanel(body)); | ||
} | ||
|
||
async function createInfoPanel(typeSearchedObject) { | ||
//Specify that you will wait for the promise to resolve. | ||
const movieID = await getMovieIDForReview(typeSearchedObject); | ||
const review = await getReview(movieID); | ||
//Currently not operating as intended. | ||
let mobileClose = ""; | ||
screen.width < 700 ? (mobileClose = "Close") : (mobileClose = "x"); | ||
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. This could better written as |
||
|
||
const infoPanel = document.createElement("div"); | ||
infoPanel.className = "info__panel"; | ||
infoPanel.innerHTML = | ||
//prettier-ignore | ||
`<div class= "close"> | ||
<button class="info__panel__close" onclick ="closeInfoPanel(this)">${mobileClose}</button> | ||
</div> | ||
<div class="img__title__date__genre"> | ||
<img src="${typeSearchedObject.Poster}"> | ||
<h1 class="info__panel__title">${typeSearchedObject.Title}</h1> | ||
<h1 class="info__panel__date">${typeSearchedObject.Year}</h1> | ||
<p class="genre">${typeSearchedObject.Genre}</p> | ||
</div> | ||
<div class="desc"> | ||
<p class="info__panel__description">${typeSearchedObject.Plot}</p> | ||
<h2>Top Review:</h2> | ||
<p class="review">${review.content}</p> | ||
<p class="review__author">${review.author}</p> | ||
</div> | ||
<div class="director__starring"> | ||
<p class="info__panel__director">Directed by: ${typeSearchedObject.Director}</p> | ||
<p class="info__panel__starring">Starring: ${typeSearchedObject.Actors}</p> | ||
</div> | ||
|
||
<div class="rating"> | ||
<span class="stars"> | ||
<span class="fa fa-star checked"></span> | ||
<span class="fa fa-star checked"></span> | ||
<span class="fa fa-star checked"></span> | ||
<span class="fa fa-star"></span> | ||
<span class="fa fa-star"></span> | ||
</span> | ||
</div>`; | ||
//Will need to append it into view. | ||
const filmContainer = document.querySelector(".display__film"); | ||
filmContainer.appendChild(infoPanel); | ||
infoPanel.scrollIntoView(); | ||
} | ||
|
||
const closeInfoPanel = closeButton => { | ||
closeButton.parentNode.parentNode.remove(); | ||
}; | ||
|
||
async function getMovieIDForReview(filmObject) { | ||
let movieDBBaseURI = `https://api.themoviedb.org/3/search/movie?api_key=c1eda2d27f7a73d8ca633b6936e5b012&query=`; | ||
|
||
const response = await fetch(`${movieDBBaseURI}${filmObject.Title}`); | ||
const movieData = await response.json(); | ||
|
||
const movie = movieData.results.find(item => { | ||
if (item.title === filmObject.Title) { | ||
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. Since find callback expects a boolean, this could be simplified to |
||
//console.log(item.id); | ||
return item; | ||
} | ||
}); | ||
return movie.id; | ||
} | ||
async function getReview(id) { | ||
//prettier-ignore | ||
const response = await fetch(`https://api.themoviedb.org/3/movie/${id}/reviews?api_key=c1eda2d27f7a73d8ca633b6936e5b012`) | ||
const reviews = await response.json(); | ||
const firstReview = await reviews.results[0]; | ||
if (firstReview != undefined) { | ||
return firstReview; | ||
} else { | ||
return { | ||
content: "No review found for this production.", | ||
author: `See for yourself at themoviedb.org, ID: ${id}` | ||
}; | ||
} | ||
} | ||
|
||
// function youMayAlsoLike(genre, actors, director, writer){ | ||
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. Commented out code can removed to avoid clutter |
||
// //search for films/tv with the same genre, actors and director | ||
// //fail? search for films/tv with same genre and actors | ||
// //fail? search for films with same genre and writer | ||
// fetch(`${baseUrlSearch`){ | ||
|
||
// } | ||
// } |
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.
Nice