From 2792f13c4852ed07b6dc971e973ef2d19ed05753 Mon Sep 17 00:00:00 2001 From: Ralph Date: Fri, 22 Jun 2018 20:23:44 +0100 Subject: [PATCH 01/10] Added search --- index.html | 23 +++-- src/App.js | 28 +++++- src/Header.js | 7 ++ src/Movie.js | 24 +++++ src/Movies.js | 30 ++++++ src/Search.js | 73 +++++++++++++++ src/css/style.css | 226 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 397 insertions(+), 14 deletions(-) create mode 100644 src/Header.js create mode 100644 src/Movie.js create mode 100644 src/Movies.js create mode 100644 src/Search.js create mode 100644 src/css/style.css diff --git a/index.html b/index.html index 44fcd2a..2c761f8 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,15 @@ - - - - Hello World - - -
- - - + + + + + OMDb Movie search + + + +
+ + + + \ No newline at end of file diff --git a/src/App.js b/src/App.js index 9520f77..eadbb8d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,14 +1,34 @@ import React from 'react'; +import Header from './Header'; +import Search from './Search'; +import Movies from './Movies'; +import Movie from './Movie'; + + class App extends React.Component { - constructor(){ + constructor() { super(); + this.state = { + search: [] + } + this.receiver = this.receiver.bind(this); + } + + receiver(movies) { + this.setState({ + search: movies + }) } - render(){ + render() { + // const Movies = this.state.search !== undefined ? : null; return ( -
- React cinema app +
+
+ + {/* {Movies} */} +
) } diff --git a/src/Header.js b/src/Header.js new file mode 100644 index 0000000..67a273f --- /dev/null +++ b/src/Header.js @@ -0,0 +1,7 @@ +import React from 'react'; +function Header({ title }) { + return ( +

{title}

+ ) +} +export default Header; \ No newline at end of file diff --git a/src/Movie.js b/src/Movie.js new file mode 100644 index 0000000..7ffc225 --- /dev/null +++ b/src/Movie.js @@ -0,0 +1,24 @@ +import React from 'react'; + +function Movie({ poster, title, year, imdbID }) { + console.log('asd'); + return ( +
+
+ +
+
+

{title}

+
Year: { + year + }
+ IMDb ↗ + +
+
+ ); +} + +export default Movie; \ No newline at end of file diff --git a/src/Movies.js b/src/Movies.js new file mode 100644 index 0000000..dd791a5 --- /dev/null +++ b/src/Movies.js @@ -0,0 +1,30 @@ +import React from 'react'; +import Movie from './Movie'; + +// Poster: "https://ia.media-imdb.com/images/M/MV5BMDdmZGU3NDQtY2E5My00ZTliLWIzOTUtMTY4ZGI1YjdiNjk3XkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg" +// ​​ +// Title: "Titanic" +// ​​ +// Type: "movie" +// ​​ +// Year: "1997" +// ​​ +// imdbID: "tt0120338" + +function Movies({ movies }) { + return ( + movies.length > 0 + ? +
+
+ {movies.map(movie => { + return ; + })} +
+
+ : + "" + ); +} + +export default Movies; \ No newline at end of file diff --git a/src/Search.js b/src/Search.js new file mode 100644 index 0000000..95efdac --- /dev/null +++ b/src/Search.js @@ -0,0 +1,73 @@ +import React from 'react'; + +class Search extends React.Component { + constructor() { + super(); + this.state = { + search: "", + // receiver: this.state + } + this.handleSubmit = this.handleSubmit.bind(this); + this.handleChange = this.handleChange.bind(this); + }; + + fetchMovies() { + const OMBbAPIKey = "aba7af8e"; + fetch(`http://www.omdbapi.com/?s=${this.state.search}&apikey=${OMBbAPIKey}`) + .then(response => response.json()) + .then(result => { + console.log(result.Search) + this.props.receiver(result.Search); + }); + } + + handleSubmit(event) { + event.preventDefault(); + this.fetchMovies(event.target.value); + } + + handleChange(event) { + event.preventDefault(); + + this.setState({ + search: event.target.value + }); + } + + render() { + return ( +
+
+ + {/*
    +
*/} + + {/* +
+ + + + + +
*/} +
+
+ ); + } +} + +export default Search; \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css new file mode 100644 index 0000000..b912b40 --- /dev/null +++ b/src/css/style.css @@ -0,0 +1,226 @@ +/* font-family: 'Source Sans Pro', sans-serif; */ +@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro"); + +:root { + --primary-color: #f1f1f1; + --secondary-color: #fdb813; +} + +body { + font-family: "Source Sans Pro", sans-serif; + margin: 0; + padding: 0; + font-size: 16px; +} +.container { + width: 100%; + max-width: 100%; +} + +h1.main-heading { + padding-left: 30px; +} + +@media (min-width: 768px) { + h1.main-heading { + padding-left: 0; + } +} + +/* Search form */ +.search-form { + /* margin-bottom: 60px; */ + background-color: var(--primary-color); + padding: 30px; + margin-bottom: 50px; +} + +@media (min-width: 768px) { + .container { + width: 80%; + max-width: 1200px; + margin: 0 auto; + } +} + +.movie-request__form { + display: block; + position: relative; +} +@media (min-width: 768px) { + .movie-request__form { + display: flex; + flex-wrap: wrap; + } +} + +.search-form input.movie-request__input { + width: calc(100% - 36px); + padding: 15px; + font-size: 1.1rem; + color: var(--secondary-color); +} + +.movie-request__submit { + width: 100%; + padding: 20px; + border: none; + background-color: var(--secondary-color); + color: #fff; + font-size: 0.875rem; +} + +@media (min-width: 768px) { + .movie-request__submit { + width: 100px; + } + .search-form input.movie-request__input { + width: calc(100% - 140px); + } +} + +/* Autocomplete */ +.autocomplete { + width: calc(100% - 25px); + position: absolute; + top: 58px; + background-color: #fff; + margin: 0; + left: 1px; + list-style-type: none; + padding-left: 20px; +} +.autocomplete li { + line-height: 30px; +} +.autocomplete li:hover { + color: #666; + cursor: pointer; +} + +/* Search filters */ +.movie-request__filters-list { + width: 100%; + margin-top: 10px; + padding-left: 9px; +} +.movie-request__filters-list.hidden { + display: none; +} +.movie-request__filters-list select { + margin-right: 10px; +} +.movie-request__filters-list label { + font-size: 0.875rem; +} + +.movie-request__filters-toggle { + border: none; + background-color: transparent; + margin-top: 10px; + font-size: 0.875rem; +} + +.movie-request__filters-toggle:hover { + cursor: pointer; + opacity: 0.7; +} + +/* Movies list */ +.movie-list-wrapper { + display: grid; + grid-template-columns: 100%; + padding: 30px; + margin-bottom: 30px; + background-color: var(--primary-color); +} + +.movie-details { + display: grid; + grid-template-columns: 100%; + + margin-bottom: 20px; + padding-bottom: 30px; + border-bottom: 1px solid rgb(0, 0, 0, 0.1); + animation: movie-fade-in 2s 1; +} + +@keyframes movie-fade-in { + 0% { opacity: 0; } + 100% { opacity: 1; } +} +@media (min-width: 480px) { + .movie-details { + grid-template-columns: 200px auto; + margin-bottom: 40px; + } +} + +.movie-info__moreinfo { + display: block; + margin-top: 10px; + margin-bottom: 10px; + width: 100px; + height: 40px; + border: none; + background-color: var(--secondary-color); + color: #fff; + font-size: 0.875rem; +} + +.movie-info__title { + border-bottom: 2px solid var(--secondary-color); +} +.movie-info__imdb-link { + display: block; + color: #000; +} + +.movie-poster img { + width: 100%; + max-width: 100%; +} + +@media (min-width: 480px) { + .movie-poster img { + width: 100%; + max-width: 150px; + } +} + +/* Movie more info details */ +ul.movie-info__moreinfo-details { + list-style-type: none; + padding-left: 0; +} + +/* Errors */ +.error-box { + display: none; + width: calc(100% - 44px); + color: #fff; + background-color: tomato; + padding: 10px 20px; +} + +/* Pagination */ +.pagination { + list-style-type: none; + visibility: hidden; + display: flex; + justify-content: flex-end; + width: 100%; + padding-left: 0; +} + +.pagination-index { + margin: 0 20px; +} + +/* .pagination li { +} */ + +.pagination li:hover { + cursor: pointer; + opacity: 0.7; +} From 6a6db11ac5bce3da259bb4226492fbc265527dd8 Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 12:21:41 +0100 Subject: [PATCH 02/10] Added infinite scroll --- src/App.js | 60 +++++++++++++++++++++++++++++++++++++++++++---- src/Movie.js | 1 - src/Movies.js | 10 +++----- src/Search.js | 24 ++++++++++++++----- src/css/style.css | 14 +++++++++-- 5 files changed, 89 insertions(+), 20 deletions(-) diff --git a/src/App.js b/src/App.js index eadbb8d..01dfe13 100644 --- a/src/App.js +++ b/src/App.js @@ -3,21 +3,71 @@ import React from 'react'; import Header from './Header'; import Search from './Search'; import Movies from './Movies'; -import Movie from './Movie'; class App extends React.Component { constructor() { super(); this.state = { - search: [] + search: [], + searchTerm: "", + page: 2 } this.receiver = this.receiver.bind(this); + this.handleInfiniteScroll = this.handleInfiniteScroll.bind(this); } - receiver(movies) { + handleInfiniteScroll() { + const windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight; + const body = document.body; + const html = document.documentElement; + const docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); + const windowBottom = windowHeight + window.pageYOffset; + + const loading = document.querySelector('#search-results__loading'); + const OMBbAPIKey = "aba7af8e"; + const query = 'http://www.omdbapi.com/?s=' + this.state.searchTerm + '&apikey=' + OMBbAPIKey + '&page=' + this.state.page; + + if (windowBottom >= docHeight) { + fetch(query) + .then(response => { + loading.textContent = "Loading..."; + loading.classList.add('show'); + return response.json(); + } + ) + .then(movies => { + if (movies.Search) { + const self = this; + setTimeout(function () { + self.setState({ + search: [...self.state.search, ...movies.Search], + page: self.state.page += 1 + }); + loading.classList.remove('show'); + }, 500) + } else { + loading.textContent = "No more results"; + loading.classList.add('show'); + } + }) + .catch(error => { + console.log(error); + loading.textContent = "There's been a problem fetching the results. Try again later.."; + loading.classList.add('show'); + }); + + } + } + + componentDidMount() { + window.addEventListener("scroll", this.handleInfiniteScroll); + } + + receiver(movies, searchTerm) { this.setState({ - search: movies + search: movies, + searchTerm: searchTerm }) } @@ -29,6 +79,8 @@ class App extends React.Component { {/* {Movies} */} + +
Loading...
) } diff --git a/src/Movie.js b/src/Movie.js index 7ffc225..88518aa 100644 --- a/src/Movie.js +++ b/src/Movie.js @@ -1,7 +1,6 @@ import React from 'react'; function Movie({ poster, title, year, imdbID }) { - console.log('asd'); return (
diff --git a/src/Movies.js b/src/Movies.js index dd791a5..f61a394 100644 --- a/src/Movies.js +++ b/src/Movies.js @@ -2,13 +2,9 @@ import React from 'react'; import Movie from './Movie'; // Poster: "https://ia.media-imdb.com/images/M/MV5BMDdmZGU3NDQtY2E5My00ZTliLWIzOTUtMTY4ZGI1YjdiNjk3XkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg" -// ​​ -// Title: "Titanic" -// ​​ +// ​​Title: "Titanic" // Type: "movie" -// ​​ // Year: "1997" -// ​​ // imdbID: "tt0120338" function Movies({ movies }) { @@ -17,8 +13,8 @@ function Movies({ movies }) { ?
- {movies.map(movie => { - return ; + {movies.map((movie, index) => { + return ; })}
diff --git a/src/Search.js b/src/Search.js index 95efdac..e172254 100644 --- a/src/Search.js +++ b/src/Search.js @@ -5,19 +5,30 @@ class Search extends React.Component { super(); this.state = { search: "", - // receiver: this.state + searchTerm: "" } this.handleSubmit = this.handleSubmit.bind(this); this.handleChange = this.handleChange.bind(this); + this.fetchMovies = this.fetchMovies.bind(this); }; fetchMovies() { const OMBbAPIKey = "aba7af8e"; - fetch(`http://www.omdbapi.com/?s=${this.state.search}&apikey=${OMBbAPIKey}`) - .then(response => response.json()) + const loading = document.querySelector('#search-results__loading'); + fetch('http://www.omdbapi.com/?s=' + this.state.search + '&apikey=' + OMBbAPIKey) + .then(response => { + loading.textContent = "Loading.."; + loading.classList.add('show'); + return response.json(); + }) .then(result => { - console.log(result.Search) - this.props.receiver(result.Search); + loading.classList.remove('show'); + this.props.receiver(result.Search, this.state.searchTerm); + }) + .catch(error => { + loading.textContent = "There's been a problem fetching the results. Try again later..."; + loading.classList.add('show'); + console.log(error); }); } @@ -30,7 +41,8 @@ class Search extends React.Component { event.preventDefault(); this.setState({ - search: event.target.value + search: event.target.value, + searchTerm: event.target.value }); } diff --git a/src/css/style.css b/src/css/style.css index b912b40..e4adec7 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -131,7 +131,7 @@ h1.main-heading { display: grid; grid-template-columns: 100%; padding: 30px; - margin-bottom: 30px; + margin-bottom: 50px; background-color: var(--primary-color); } @@ -142,7 +142,7 @@ h1.main-heading { margin-bottom: 20px; padding-bottom: 30px; border-bottom: 1px solid rgb(0, 0, 0, 0.1); - animation: movie-fade-in 2s 1; + animation: movie-fade-in 4s 1; } @keyframes movie-fade-in { @@ -194,6 +194,16 @@ ul.movie-info__moreinfo-details { padding-left: 0; } +/* Infnitie scroll loading */ +.search-results__loading { + display: none; + text-align: center; + margin-bottom: 50px; +} +.search-results__loading.show { + display: block; +} + /* Errors */ .error-box { display: none; From 4be366548f8905bf494d8b77d7f181818afda3e9 Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 12:24:23 +0100 Subject: [PATCH 03/10] Update style movies list to two columns --- src/css/style.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/css/style.css b/src/css/style.css index e4adec7..cfee8c5 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -155,6 +155,19 @@ h1.main-heading { margin-bottom: 40px; } } +@media (min-width: 768px) { + .movie-details { + padding-right: 35px; + } + .movie-list-wrapper { + grid-template-columns: 50% 50%; + } + + .movie-details { + grid-template-columns: 170px auto; + margin-bottom: 40px; + } +} .movie-info__moreinfo { display: block; From 01b1b7947d4dc21ac0c4a4ddd46c1c395fb97cdf Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 13:41:36 +0100 Subject: [PATCH 04/10] Added Back To Top button --- src/App.js | 24 +++++++++++++++++++--- src/css/style.css | 51 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/App.js b/src/App.js index 01dfe13..2f4bbe1 100644 --- a/src/App.js +++ b/src/App.js @@ -14,20 +14,29 @@ class App extends React.Component { page: 2 } this.receiver = this.receiver.bind(this); - this.handleInfiniteScroll = this.handleInfiniteScroll.bind(this); + this.handleScroll = this.handleScroll.bind(this); } - handleInfiniteScroll() { + handleScroll() { const windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight; const body = document.body; const html = document.documentElement; const docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); const windowBottom = windowHeight + window.pageYOffset; + const backToTop = body.querySelector("#back-to-top"); const loading = document.querySelector('#search-results__loading'); const OMBbAPIKey = "aba7af8e"; const query = 'http://www.omdbapi.com/?s=' + this.state.searchTerm + '&apikey=' + OMBbAPIKey + '&page=' + this.state.page; + // Back to top button + if (body.scrollTop > 300 || html.scrollTop > 300) { + backToTop.classList.add('visible'); + } else { + backToTop.classList.remove('visible'); + } + + // Infinite scroll if (windowBottom >= docHeight) { fetch(query) .then(response => { @@ -60,8 +69,12 @@ class App extends React.Component { } } + handleBackToTop() { + window.scrollTo(0, 0); + } + componentDidMount() { - window.addEventListener("scroll", this.handleInfiniteScroll); + window.addEventListener("scroll", this.handleScroll); } receiver(movies, searchTerm) { @@ -81,6 +94,11 @@ class App extends React.Component {
Loading...
+
) } diff --git a/src/css/style.css b/src/css/style.css index cfee8c5..c0d6794 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -21,7 +21,7 @@ h1.main-heading { padding-left: 30px; } -@media (min-width: 768px) { +@media (min-width: 960px) { h1.main-heading { padding-left: 0; } @@ -35,7 +35,7 @@ h1.main-heading { margin-bottom: 50px; } -@media (min-width: 768px) { +@media (min-width: 960px) { .container { width: 80%; max-width: 1200px; @@ -142,13 +142,9 @@ h1.main-heading { margin-bottom: 20px; padding-bottom: 30px; border-bottom: 1px solid rgb(0, 0, 0, 0.1); - animation: movie-fade-in 4s 1; + animation: fade-in 4s 1; } -@keyframes movie-fade-in { - 0% { opacity: 0; } - 100% { opacity: 1; } -} @media (min-width: 480px) { .movie-details { grid-template-columns: 200px auto; @@ -177,10 +173,15 @@ h1.main-heading { height: 40px; border: none; background-color: var(--secondary-color); - color: #fff; + /* color: #fff; */ font-size: 0.875rem; } +.movie-info__moreinfo:hover { + cursor: pointer; + opacity: .8; +} + .movie-info__title { border-bottom: 2px solid var(--secondary-color); } @@ -217,6 +218,32 @@ ul.movie-info__moreinfo-details { display: block; } +/* Back To Top */ +.back-to-top { + display: none; +} + +.back-to-top.visible { + display: flex; + align-items: center; + justify-content: space-around; + + background-color: var(--secondary-color); + border-radius: 50%; + border: none; + width: 40px; + height: 40px; + position: fixed; + bottom: 20px; + right: 20px; + animation: fade-in 1s 1; +} + +.back-to-top:hover { + cursor: pointer; + opacity: .8; +} + /* Errors */ .error-box { display: none; @@ -247,3 +274,11 @@ ul.movie-info__moreinfo-details { cursor: pointer; opacity: 0.7; } + + +/* ANIMATIONS */ + +@keyframes fade-in { + 0% { opacity: 0; } + 100% { opacity: 1; } +} \ No newline at end of file From 05becbbbd93515a4c8efacfb0897bb661341b6b2 Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 14:24:09 +0100 Subject: [PATCH 05/10] Refactoring Loading and BackToTop into components --- src/App.js | 15 ++++----------- src/elements/BackToTop.js | 16 ++++++++++++++++ src/elements/Loading.js | 9 +++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 src/elements/BackToTop.js create mode 100644 src/elements/Loading.js diff --git a/src/App.js b/src/App.js index 2f4bbe1..562619e 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,8 @@ import React from 'react'; import Header from './Header'; import Search from './Search'; import Movies from './Movies'; +import Loading from './elements/Loading'; +import BackToTop from './elements/BackToTop'; class App extends React.Component { @@ -69,10 +71,6 @@ class App extends React.Component { } } - handleBackToTop() { - window.scrollTo(0, 0); - } - componentDidMount() { window.addEventListener("scroll", this.handleScroll); } @@ -92,13 +90,8 @@ class App extends React.Component { {/* {Movies} */} - -
Loading...
- + +
) } diff --git a/src/elements/BackToTop.js b/src/elements/BackToTop.js new file mode 100644 index 0000000..fc9acf0 --- /dev/null +++ b/src/elements/BackToTop.js @@ -0,0 +1,16 @@ +import React from 'react'; + +function BackToTop() { + function handleBackToTop() { + window.scrollTo(0, 0); + }; + return ( + + ) +} + +export default BackToTop; \ No newline at end of file diff --git a/src/elements/Loading.js b/src/elements/Loading.js new file mode 100644 index 0000000..020f7de --- /dev/null +++ b/src/elements/Loading.js @@ -0,0 +1,9 @@ +import React from 'react'; + +function Loading() { + return ( +
Loading...
+ ) +} + +export default Loading; \ No newline at end of file From d7f45444f6d6da6c8897466c0a4687bd8801b51a Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 16:38:55 +0100 Subject: [PATCH 06/10] Added Info button --- src/Movie.js | 84 ++++++++++++++++++++++++++++++++++++++--------- src/css/style.css | 19 +++++++++-- 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/Movie.js b/src/Movie.js index 88518aa..f3c9123 100644 --- a/src/Movie.js +++ b/src/Movie.js @@ -1,23 +1,75 @@ import React from 'react'; -function Movie({ poster, title, year, imdbID }) { - return ( -
-
- +class Movie extends React.Component { + constructor(props) { + super(props); + this.movieFetch = this.movieFetch.bind(this); + }; + movieFetch(e) { + const evtTarget = e.target; + const movieParentNode = evtTarget.parentNode; + const OMBbAPIKey = "aba7af8e"; + fetch('http://www.omdbapi.com/?t=' + this.props.title + '&apikey=' + OMBbAPIKey) + .then(response => { + return response.json(); + }) + .then(data => { + // Show/hide the list + if (movieParentNode.parentNode.querySelector(".movie-info__moreinfo-details")) { + // Remove list + movieParentNode.parentNode.querySelector(".movie-info__moreinfo-details").remove(); + // Update button text + evtTarget.textContent = "Info"; + } else { + // Create list with desired movie details + const infoToDisplay = [ + "Genre", + "Plot", + "Runtime", + "Awards", + "Language" + ]; + const html = Object.keys(data) + .map(function(key) { + if (infoToDisplay.indexOf(key) !== -1) { + return `
  • ${key}: ${data[key]}
  • `; + } + }) + .join(""); + + // Update button text + evtTarget.textContent = "Close"; + + // Append list + const moreInfoList = document.createElement("ul"); + moreInfoList.setAttribute("class", "movie-info__moreinfo-details"); + moreInfoList.innerHTML = html; + movieParentNode.parentNode.append(moreInfoList); + } + }) + .catch(error => { + console.error(error); + }); + } + render(){ + return ( +
    +
    + {this.props.title} +
    +
    +

    {this.props.title}

    +
    Year: { + this.props.year + }
    + IMDb ↗ +
    -
    -

    {title}

    -
    Year: { - year - }
    - IMDb ↗ -
    -
    - ); + ); + }; } export default Movie; \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css index c0d6794..18cc525 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -66,10 +66,15 @@ h1.main-heading { padding: 20px; border: none; background-color: var(--secondary-color); - color: #fff; + /* color: #fff; */ font-size: 0.875rem; } +.movie-request__submit:hover { + cursor: pointer; + opacity: .8; +} + @media (min-width: 768px) { .movie-request__submit { width: 100px; @@ -145,11 +150,18 @@ h1.main-heading { animation: fade-in 4s 1; } +.movie-info { + grid-column: 1/4; +} + @media (min-width: 480px) { .movie-details { grid-template-columns: 200px auto; margin-bottom: 40px; } + .movie-info { + grid-column: 2/4; + } } @media (min-width: 768px) { .movie-details { @@ -169,8 +181,7 @@ h1.main-heading { display: block; margin-top: 10px; margin-bottom: 10px; - width: 100px; - height: 40px; + padding: 7px 16px; border: none; background-color: var(--secondary-color); /* color: #fff; */ @@ -204,6 +215,8 @@ h1.main-heading { /* Movie more info details */ ul.movie-info__moreinfo-details { + grid-column: 1/4; + list-style-type: none; padding-left: 0; } From f2f47ade8834518c89963e4dc1e0da506e61ae1e Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 16:43:29 +0100 Subject: [PATCH 07/10] Bug fix placeholder image --- src/Movie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Movie.js b/src/Movie.js index f3c9123..7b03a0c 100644 --- a/src/Movie.js +++ b/src/Movie.js @@ -55,7 +55,7 @@ class Movie extends React.Component { return (
    - {this.props.title} + {this.props.title}

    {this.props.title}

    From 5a4b3721ec58d9c4941276fc7e8c4d28d60da6ac Mon Sep 17 00:00:00 2001 From: Ralph Date: Sat, 23 Jun 2018 21:37:34 +0100 Subject: [PATCH 08/10] Added watch list --- src/App.js | 48 ++++++++++++++---- src/Header.js | 20 ++++++-- src/Movie.js | 107 ++++++++++++++++++++++------------------ src/Movies.js | 50 ++++++++++++------- src/css/style.css | 23 +++++++++ src/elements/Loading.js | 2 +- 6 files changed, 169 insertions(+), 81 deletions(-) diff --git a/src/App.js b/src/App.js index 562619e..7f56cda 100644 --- a/src/App.js +++ b/src/App.js @@ -13,9 +13,13 @@ class App extends React.Component { this.state = { search: [], searchTerm: "", - page: 2 + page: 2, + watchList: [], + watchListSection: false } - this.receiver = this.receiver.bind(this); + this.receiverSearch = this.receiverSearch.bind(this); + this.receiverWatchList = this.receiverWatchList.bind(this); + this.receiverDisplayWatchlist = this.receiverDisplayWatchlist.bind(this); this.handleScroll = this.handleScroll.bind(this); } @@ -75,21 +79,47 @@ class App extends React.Component { window.addEventListener("scroll", this.handleScroll); } - receiver(movies, searchTerm) { + receiverSearch(movies, searchTerm) { this.setState({ search: movies, - searchTerm: searchTerm + searchTerm: searchTerm, + watchListSection: false }) } + receiverDisplayWatchlist() { + this.setState({ + watchListSection: true + }) + } + + receiverWatchList(articleTitle) { + this.state.search.forEach(articleObj => { + return Object.keys(articleObj).find(articleKey => { + if (articleObj[articleKey] === articleTitle) { + this.setState({ + watchList: [...this.state.watchList, articleObj] + }) + } + }); + }); + + } + + render() { - // const Movies = this.state.search !== undefined ? : null; + const watchList = this.state.watchListSection; + let MoviesList; + if (watchList) { + MoviesList = ; + } else { + MoviesList = ; + } return (
    -
    - - {/* {Movies} */} - +
    + + {MoviesList}
    diff --git a/src/Header.js b/src/Header.js index 67a273f..ae29574 100644 --- a/src/Header.js +++ b/src/Header.js @@ -1,7 +1,19 @@ import React from 'react'; -function Header({ title }) { - return ( -

    {title}

    - ) +class Header extends React.Component { + constructor(props) { + super(props); + this.handleWatchList = this.handleWatchList.bind(this); + } + handleWatchList() { + this.props.receiver(); + } + render() { + return ( +
    +

    {this.props.title}

    + +
    + ) + } } export default Header; \ No newline at end of file diff --git a/src/Movie.js b/src/Movie.js index 7b03a0c..03146cd 100644 --- a/src/Movie.js +++ b/src/Movie.js @@ -4,68 +4,79 @@ class Movie extends React.Component { constructor(props) { super(props); this.movieFetch = this.movieFetch.bind(this); + this.handleWatchList = this.handleWatchList.bind(this); }; movieFetch(e) { const evtTarget = e.target; const movieParentNode = evtTarget.parentNode; const OMBbAPIKey = "aba7af8e"; fetch('http://www.omdbapi.com/?t=' + this.props.title + '&apikey=' + OMBbAPIKey) - .then(response => { - return response.json(); - }) - .then(data => { - // Show/hide the list - if (movieParentNode.parentNode.querySelector(".movie-info__moreinfo-details")) { - // Remove list - movieParentNode.parentNode.querySelector(".movie-info__moreinfo-details").remove(); - // Update button text - evtTarget.textContent = "Info"; - } else { - // Create list with desired movie details - const infoToDisplay = [ - "Genre", - "Plot", - "Runtime", - "Awards", - "Language" - ]; - const html = Object.keys(data) - .map(function(key) { - if (infoToDisplay.indexOf(key) !== -1) { - return `
  • ${key}: ${data[key]}
  • `; - } - }) - .join(""); - - // Update button text - evtTarget.textContent = "Close"; - - // Append list - const moreInfoList = document.createElement("ul"); - moreInfoList.setAttribute("class", "movie-info__moreinfo-details"); - moreInfoList.innerHTML = html; - movieParentNode.parentNode.append(moreInfoList); - } - }) - .catch(error => { - console.error(error); - }); - } - render(){ + .then(response => { + return response.json(); + }) + .then(data => { + // Show/hide the list + if (movieParentNode.parentNode.querySelector(".movie-info__moreinfo-details")) { + // Remove list + movieParentNode.parentNode.querySelector(".movie-info__moreinfo-details").remove(); + // Update button text + evtTarget.textContent = "Info"; + } else { + // Create list with desired movie details + const infoToDisplay = [ + "Genre", + "Plot", + "Runtime", + "Awards", + "Language" + ]; + const html = Object.keys(data) + .map(function (key) { + if (infoToDisplay.indexOf(key) !== -1) { + return `
  • ${key}: ${data[key]}
  • `; + } + }) + .join(""); + + // Update button text + evtTarget.textContent = "Close"; + + // Append list + const moreInfoList = document.createElement("ul"); + moreInfoList.setAttribute("class", "movie-info__moreinfo-details"); + moreInfoList.innerHTML = html; + movieParentNode.parentNode.append(moreInfoList); + } + }) + .catch(error => { + console.error(error); + }); + } + handleWatchList(e) { + const evtTarget = e.target; + if (!evtTarget.classList.contains('disabled')) { + evtTarget.classList.add("disabled"); + const articleTitle = evtTarget.getAttribute("data-title"); + this.props.receiver(articleTitle); + } + } + render() { + const watchlistbutton = this.props.watchlistbutton; return (
    - {this.props.title} -
    -
    + {this.props.title} +
    +

    {this.props.title}

    Year: { this.props.year }
    IMDb ↗ - + {watchlistbutton + ? + : ""} +
    ); diff --git a/src/Movies.js b/src/Movies.js index f61a394..8196211 100644 --- a/src/Movies.js +++ b/src/Movies.js @@ -1,26 +1,38 @@ import React from 'react'; import Movie from './Movie'; -// Poster: "https://ia.media-imdb.com/images/M/MV5BMDdmZGU3NDQtY2E5My00ZTliLWIzOTUtMTY4ZGI1YjdiNjk3XkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg" -// ​​Title: "Titanic" -// Type: "movie" -// Year: "1997" -// imdbID: "tt0120338" -function Movies({ movies }) { - return ( - movies.length > 0 - ? -
    -
    - {movies.map((movie, index) => { - return ; - })} -
    -
    - : - "" - ); +class Movies extends React.Component { + constructor() { + super(); + this.titleReceiver = this.titleReceiver.bind(this); + } + + titleReceiver(title) { + this.props.receiver(title); + } + + render() { + return ( + this.props.movies.length > 0 + ? +
    +
    + {this.props.movies.map((movie, index) => { + return ; + })} +
    +
    + : + "" + ); + } } export default Movies; \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css index 18cc525..e96ae3d 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -12,11 +12,29 @@ body { padding: 0; font-size: 16px; } + +input[type=text]:focus { + box-shadow: 0 0 5px rgba(253, 184, 19, 1); + border: 1px solid rgba(253, 184, 19, 1); +} + .container { width: 100%; max-width: 100%; } +header.main-header { + display: flex; + align-items: center; + justify-content: space-between; +} + +header.main-header .watch-list__button{ + max-height: 40px; + background-color: #000; + color: var(--secondary-color); +} + h1.main-heading { padding-left: 30px; } @@ -193,6 +211,11 @@ h1.main-heading { opacity: .8; } +.movie-info__moreinfo.disabled { + cursor: default; + opacity: .2; +} + .movie-info__title { border-bottom: 2px solid var(--secondary-color); } diff --git a/src/elements/Loading.js b/src/elements/Loading.js index 020f7de..c7f7f7d 100644 --- a/src/elements/Loading.js +++ b/src/elements/Loading.js @@ -1,6 +1,6 @@ import React from 'react'; -function Loading() { +function Loading(props) { return (
    Loading...
    ) From fe92891caa98150c78b1834151d4b21f0d731c41 Mon Sep 17 00:00:00 2001 From: Ralph Date: Sun, 24 Jun 2018 20:53:56 +0100 Subject: [PATCH 09/10] Added watchList to localStorage --- src/App.js | 6 +++++- src/Movie.js | 5 +++++ src/Movies.js | 5 +++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 7f56cda..1c50d0c 100644 --- a/src/App.js +++ b/src/App.js @@ -100,9 +100,12 @@ class App extends React.Component { this.setState({ watchList: [...this.state.watchList, articleObj] }) + localStorage.setItem("watchList", JSON.stringify([...this.state.watchList, articleObj])); } }); }); + // console.log("state", this.state.watchList); + // console.log("getItem", JSON.parse(localStorage.getItem("watchList"))); } @@ -110,8 +113,9 @@ class App extends React.Component { render() { const watchList = this.state.watchListSection; let MoviesList; + console.log(localStorage.getItem("watchList")) if (watchList) { - MoviesList = ; + MoviesList = ; } else { MoviesList = ; } diff --git a/src/Movie.js b/src/Movie.js index 03146cd..9e4e426 100644 --- a/src/Movie.js +++ b/src/Movie.js @@ -73,6 +73,11 @@ class Movie extends React.Component { this.props.year }
    IMDb ↗ + + Watch trailer ↗ + {watchlistbutton ? : ""} diff --git a/src/Movies.js b/src/Movies.js index 8196211..ce17bc8 100644 --- a/src/Movies.js +++ b/src/Movies.js @@ -18,10 +18,11 @@ class Movies extends React.Component { ?
    - {this.props.movies.map((movie, index) => { + {this.props.movies.map((movie) => { return Date: Wed, 27 Jun 2018 10:22:39 +0100 Subject: [PATCH 10/10] Working on localStorage and watchList --- src/App.js | 37 ++++++++++++++++++++++++------------- src/Header.js | 2 ++ src/Movie.js | 8 ++++---- src/Movies.js | 11 +++++++---- src/css/style.css | 7 +++++++ 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/App.js b/src/App.js index 1c50d0c..1a34df5 100644 --- a/src/App.js +++ b/src/App.js @@ -14,7 +14,7 @@ class App extends React.Component { search: [], searchTerm: "", page: 2, - watchList: [], + watchList: localStorage.getItem("watchList") ? localStorage.getItem("watchList") : [], watchListSection: false } this.receiverSearch = this.receiverSearch.bind(this); @@ -93,27 +93,38 @@ class App extends React.Component { }) } - receiverWatchList(articleTitle) { - this.state.search.forEach(articleObj => { - return Object.keys(articleObj).find(articleKey => { - if (articleObj[articleKey] === articleTitle) { + receiverWatchList(articleTitle, action) { + if (action === "addToWatchList") { + this.state.search.forEach(articleObj => { + return Object.keys(articleObj).find(articleKey => { + if (articleObj[articleKey] === articleTitle) { + this.setState({ + watchList: [...this.state.watchList, articleObj] + }) + + localStorage.getItem("watchList") + ? localStorage.setItem("watchList", JSON.stringify([articleObj, ...JSON.parse(localStorage.getItem("watchList"))])) + : localStorage.setItem("watchList", JSON.stringify([...this.state.watchList, articleObj])); + } + }); + }); + } else { + const index = JSON.parse(this.state.watchList).forEach((item, index) => { + if (item.Title === articleTitle) { + // console.log("Aquis", this.state.watchList); this.setState({ - watchList: [...this.state.watchList, articleObj] + watchList: JSON.parse(this.state.watchList).splice(index, 1) }) - localStorage.setItem("watchList", JSON.stringify([...this.state.watchList, articleObj])); + localStorage.setItem("watchList", this.state.watchList); + // console.log("Aquis", localStorage.getItem("watchList")); } }); - }); - // console.log("state", this.state.watchList); - // console.log("getItem", JSON.parse(localStorage.getItem("watchList"))); - + } } - render() { const watchList = this.state.watchListSection; let MoviesList; - console.log(localStorage.getItem("watchList")) if (watchList) { MoviesList = ; } else { diff --git a/src/Header.js b/src/Header.js index ae29574..d89bca8 100644 --- a/src/Header.js +++ b/src/Header.js @@ -5,10 +5,12 @@ class Header extends React.Component { this.handleWatchList = this.handleWatchList.bind(this); } handleWatchList() { + document.querySelectorAll('.movie-info__moreinfo').forEach(item => item.classList.remove("disabled")); this.props.receiver(); } render() { return ( +

    {this.props.title}

    diff --git a/src/Movie.js b/src/Movie.js index 9e4e426..1839d99 100644 --- a/src/Movie.js +++ b/src/Movie.js @@ -52,12 +52,12 @@ class Movie extends React.Component { console.error(error); }); } - handleWatchList(e) { + handleWatchList(e, action) { const evtTarget = e.target; if (!evtTarget.classList.contains('disabled')) { evtTarget.classList.add("disabled"); const articleTitle = evtTarget.getAttribute("data-title"); - this.props.receiver(articleTitle); + this.props.receiver(articleTitle, action); } } render() { @@ -79,8 +79,8 @@ class Movie extends React.Component { Watch trailer ↗ {watchlistbutton - ? - : ""} + ? + : }
    diff --git a/src/Movies.js b/src/Movies.js index ce17bc8..bf520b2 100644 --- a/src/Movies.js +++ b/src/Movies.js @@ -5,14 +5,17 @@ import Movie from './Movie'; class Movies extends React.Component { constructor() { super(); - this.titleReceiver = this.titleReceiver.bind(this); + this.watchListReceiver = this.watchListReceiver.bind(this); } - titleReceiver(title) { - this.props.receiver(title); + watchListReceiver(title, action) { + this.props.receiver(title, action); } render() { + + console.log(this.props.movies.length); + console.log(this.props.movies); return ( this.props.movies.length > 0 ? @@ -26,7 +29,7 @@ class Movies extends React.Component { title={movie.Title} year={movie.Year} imdbID={movie.imdbID} - receiver={this.titleReceiver} />; + receiver={this.watchListReceiver} />; })}
    diff --git a/src/css/style.css b/src/css/style.css index e96ae3d..48cff9d 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -33,6 +33,13 @@ header.main-header .watch-list__button{ max-height: 40px; background-color: #000; color: var(--secondary-color); + margin-right: 30px; +} + +@media(min-width: 768px){ + header.main-header .watch-list__button{ + margin-right: 0; + } } h1.main-heading {