├── src ├── components │ ├── NotFound │ │ ├── NotFound.css │ │ └── NotFound.js │ ├── elements │ │ ├── img │ │ │ ├── bookmark.png │ │ │ ├── loading.gif │ │ │ ├── no_image.jpg │ │ │ ├── bg-not-found.jpg │ │ │ ├── favourite-img.png │ │ │ └── icon.svg │ │ ├── HaveNotFavouriteMovies │ │ │ ├── HaveNotFavouriteMovies.css │ │ │ └── HaveNotFavouriteMovies.js │ │ ├── HaveNotWatchedMovies │ │ │ ├── HaveNotWatchedMovies.css │ │ │ └── HaveNotWatchedMovies.js │ │ ├── Spinner │ │ │ ├── Loading.css │ │ │ ├── Spinner.js │ │ │ └── Spinner.css │ │ ├── ImageFrame │ │ │ ├── ImageFrame.css │ │ │ └── ImageFrame.js │ │ ├── PageTitle │ │ │ └── PageTitle.js │ │ ├── ScrollTop │ │ │ └── ScrollTop.js │ │ ├── Actors │ │ │ ├── Actors.css │ │ │ └── Actors.js │ │ ├── MovieInfo │ │ │ ├── MovieInfo.css │ │ │ └── MovieInfo.js │ │ ├── LoadMore │ │ │ └── LoadMoreBtn.js │ │ ├── BreadCrumb │ │ │ └── BreadCrumbs.js │ │ └── SearchBar │ │ │ ├── SearchBar.js │ │ │ └── SearchBar.css │ ├── Footer │ │ ├── Footer.css │ │ └── Footer.js │ ├── SimilarMoviesContainer.js │ ├── WatchedMovies │ │ └── WatchedMovies.js │ ├── FavouriteMovies │ │ └── FavouriteMovies.js │ ├── SimilarMovies.js │ ├── Navbar │ │ ├── Navbar.js │ │ └── Navbar.css │ ├── Movie │ │ └── Movie.js │ ├── Home │ │ └── Home.js │ └── App │ │ └── App.js ├── Functions │ ├── CommonFunctions │ │ └── commonFunctions.js │ └── StorageFunctions │ │ └── storageFunctions.js ├── context.js ├── config.js ├── index.js ├── index.css └── serviceWorker.js ├── public ├── robots.txt ├── favicon.ico ├── manifest.json └── index.html ├── .gitignore ├── package.json └── README.md /src/components/NotFound/NotFound.css: -------------------------------------------------------------------------------- 1 | .not-found-img { 2 | object-fit: cover; 3 | } 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nasideyildirim/Capstone-Movie-App-upschool/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/components/elements/img/bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nasideyildirim/Capstone-Movie-App-upschool/HEAD/src/components/elements/img/bookmark.png -------------------------------------------------------------------------------- /src/components/elements/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nasideyildirim/Capstone-Movie-App-upschool/HEAD/src/components/elements/img/loading.gif -------------------------------------------------------------------------------- /src/components/elements/img/no_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nasideyildirim/Capstone-Movie-App-upschool/HEAD/src/components/elements/img/no_image.jpg -------------------------------------------------------------------------------- /src/components/elements/img/bg-not-found.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nasideyildirim/Capstone-Movie-App-upschool/HEAD/src/components/elements/img/bg-not-found.jpg -------------------------------------------------------------------------------- /src/components/elements/img/favourite-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nasideyildirim/Capstone-Movie-App-upschool/HEAD/src/components/elements/img/favourite-img.png -------------------------------------------------------------------------------- /src/Functions/CommonFunctions/commonFunctions.js: -------------------------------------------------------------------------------- 1 | export const editReleaseDate = date => { 2 | 3 | if (date !== null && date !== "") { 4 | return date.split("-").reverse().join("/") 5 | } 6 | else { 7 | return "No Information" 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.css: -------------------------------------------------------------------------------- 1 | .fa-linkedin { 2 | background: #007bb5; 3 | color: white; 4 | 5 | } 6 | 7 | .fa-github { 8 | background: #007bb5; 9 | color: white; 10 | } 11 | 12 | .fa-medium { 13 | background: #007bb5; 14 | color: white; 15 | font-size:48px; 16 | } -------------------------------------------------------------------------------- /src/components/elements/HaveNotFavouriteMovies/HaveNotFavouriteMovies.css: -------------------------------------------------------------------------------- 1 | .text-container { 2 | height: calc(100vh - 74px); 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | flex-direction: column; 7 | } 8 | 9 | .no-movie-img { 10 | width: 150px; 11 | margin-bottom: 20px; 12 | } -------------------------------------------------------------------------------- /src/components/elements/HaveNotWatchedMovies/HaveNotWatchedMovies.css: -------------------------------------------------------------------------------- 1 | .text-container { 2 | height: calc(100vh - 74px); 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | flex-direction: column; 7 | } 8 | 9 | .no-movie-img { 10 | width: 150px; 11 | margin-bottom: 20px; 12 | } -------------------------------------------------------------------------------- /src/context.js: -------------------------------------------------------------------------------- 1 | import React, { useState, createContext } from 'react'; 2 | 3 | export const Context = createContext(); 4 | 5 | const UserProvider = ({ children }) => { 6 | const [state, setState] = useState(undefined); 7 | 8 | return ( 9 | {children} 10 | ); 11 | }; 12 | 13 | export default UserProvider; -------------------------------------------------------------------------------- /src/components/elements/Spinner/Loading.css: -------------------------------------------------------------------------------- 1 | .loading { 2 | position: fixed; 3 | top: 65px; 4 | left: 0; 5 | height: 100vh; 6 | width: 100vw; 7 | background-color: #000; 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | z-index: 99999999999; 12 | } 13 | 14 | .loading img { 15 | width: 150px; 16 | height: 150px; 17 | } -------------------------------------------------------------------------------- /src/components/elements/Spinner/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Spinner.css'; 3 | import './Loading.css'; 4 | import load from '../img/loading.gif' 5 | 6 | const Spinner = () => { 7 | return ( 8 |
9 | Loading content of movies gif 10 |
11 | ) 12 | } 13 | 14 | export default Spinner; -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/components/elements/ImageFrame/ImageFrame.css: -------------------------------------------------------------------------------- 1 | .image-frame { 2 | transition: .5s ease; 3 | } 4 | 5 | .image-frame:hover { 6 | transform: scale(1.07); 7 | border: none !important; 8 | } 9 | 10 | .card-box { 11 | -webkit-box-shadow: 3px 2px 20px 1px rgba(0,0,0,0.89); 12 | box-shadow: 3px 2px 20px 1px rgba(0,0,0,0.89); 13 | } 14 | 15 | a { 16 | text-decoration: none !important; 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const BASE_URL = "https://api.themoviedb.org/3"; 2 | const BASE_IMG = "https://image.tmdb.org/t/p/w500"; 3 | const API_KEY = "bc035e2281adfb65b8f61bddf7d55f66"; 4 | const IMAGE_BASE_URL ='http://image.tmdb.org/t/p/'; 5 | const BACKDROP_SIZE = 'w1280'; 6 | 7 | 8 | export { 9 | BASE_URL, 10 | BASE_IMG, 11 | API_KEY, 12 | IMAGE_BASE_URL, 13 | BACKDROP_SIZE, 14 | 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/elements/PageTitle/PageTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Proptypes from 'prop-types'; 3 | 4 | const PageTitle = ({title}) => { 5 | return ( 6 | 7 |

{title}

8 |
9 | ) 10 | } 11 | 12 | PageTitle.propTypes = { 13 | title : Proptypes.string.isRequired 14 | } 15 | 16 | export default PageTitle; 17 | 18 | -------------------------------------------------------------------------------- /src/components/NotFound/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './NotFound.css'; 3 | 4 | const NotFound = () => { 5 | return ( 6 |
7 | 404 img 11 | 12 |
13 | ) 14 | } 15 | 16 | export default NotFound; 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/elements/ScrollTop/ScrollTop.js: -------------------------------------------------------------------------------- 1 | import { Component } from "react"; 2 | import { withRouter } from "react-router-dom"; 3 | 4 | class ScrollTop extends Component { // This component will enable scroll to start from above on each page 5 | 6 | componentDidUpdate(prevProps) { 7 | if (this.props.location !== prevProps.location) { 8 | window.scrollTo(0, 0); 9 | } 10 | } 11 | 12 | render() { 13 | return this.props.children; 14 | } 15 | } 16 | 17 | export default withRouter(ScrollTop); -------------------------------------------------------------------------------- /src/components/elements/HaveNotWatchedMovies/HaveNotWatchedMovies.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./HaveNotWatchedMovies.css"; 3 | import bookmark from '../img/bookmark.png'; 4 | 5 | const HaveNotWatchedMovies = () => { 6 | return ( 7 |
8 | bookmark-img 9 |

Click X Icon to Add Movies to Watched.

10 |
11 | ); 12 | }; 13 | 14 | export default HaveNotWatchedMovies; 15 | -------------------------------------------------------------------------------- /src/components/elements/HaveNotFavouriteMovies/HaveNotFavouriteMovies.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./HaveNotFavouriteMovies.css"; 3 | import hearth from '../img/favourite-img.png'; 4 | 5 | const HaveNotFavouriteMovies = () => { 6 | return ( 7 |
8 | hearth-img 9 |

Click ♥ Icon to Add Movies to Favorites.

10 |
11 | ); 12 | }; 13 | 14 | export default HaveNotFavouriteMovies; 15 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './components/App/App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | import 'bootstrap/dist/css/bootstrap.min.css'; 7 | import 'alertifyjs/build/css/alertify.min.css'; 8 | 9 | ReactDOM.render( 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /src/components/SimilarMoviesContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SimilarMovies from './SimilarMovies'; 3 | export default function SimilarMoviesContainer(props) { 4 | let similar_movie = props.similar_movies_array.map((similar_item, index) => 5 | 11 | ) 12 | return ( 13 | 14 |
15 | {similar_movie} 16 |
17 |
18 | ) 19 | } -------------------------------------------------------------------------------- /src/components/elements/Actors/Actors.css: -------------------------------------------------------------------------------- 1 | .actors-card { 2 | background-color: rgba(0, 0, 0, 0.925) !important; 3 | height: 380px; 4 | border-radius: 5px !important; 5 | text-align: center; 6 | border: 5px solid black !important; 7 | border-radius: 10px; 8 | transition: .5s ease !important; 9 | } 10 | 11 | .actors-card:hover { 12 | border: 5px solid #0275d8 !important 13 | } 14 | 15 | .actors-card .actors-card-body p{ 16 | transition: .5s ease ; 17 | 18 | } 19 | 20 | .actors-card:hover .actors-card-body p{ 21 | color: #f0ad4e; 22 | } 23 | 24 | .actor-name { 25 | font-size: 16px; 26 | letter-spacing: .3px; 27 | color: #fff; 28 | } 29 | 30 | .actor-character { 31 | font-size: 13px; 32 | letter-spacing: .3px; 33 | color: #fff; 34 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap'); 2 | 3 | *{ 4 | box-sizing: border-box; 5 | margin: 0; 6 | padding: 0; 7 | font-family: 'Roboto', 'sans-serif'; 8 | font-weight: 400; 9 | } 10 | 11 | a { 12 | text-decoration: none !important; 13 | } 14 | 15 | li { 16 | list-style: none !important; 17 | } 18 | 19 | body { 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | } 23 | 24 | code { 25 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 26 | monospace; 27 | } 28 | 29 | 30 | .fadeEffect { 31 | animation: fade 1s; 32 | 33 | } 34 | 35 | @keyframes fade { 36 | from { 37 | opacity:0; 38 | } 39 | to { 40 | opacity:1; 41 | } 42 | } -------------------------------------------------------------------------------- /src/Functions/StorageFunctions/storageFunctions.js: -------------------------------------------------------------------------------- 1 | export const addLocalStorage = movie => { 2 | let movies = getMovieFromStorage(); 3 | 4 | movies.push(movie) 5 | 6 | localStorage.setItem('favouriteMovies', 'watchedMovies', JSON.stringify(movies)) 7 | 8 | } 9 | 10 | export const getMovieFromStorage = () => { 11 | let movies; 12 | if (localStorage.getItem('favouriteMovies' , 'watchedMovies') === null ) { 13 | movies =[] 14 | } 15 | else { 16 | movies = JSON.parse(localStorage.getItem('favouriteMovies', 'watchedMovies')) 17 | } 18 | 19 | return movies; 20 | } 21 | 22 | 23 | export const removeMovieFromStorage = id => { 24 | let movies = getMovieFromStorage(); 25 | movies.forEach(function(movie, index) { 26 | if (movie.id === id) { 27 | movies.splice(index, 1) 28 | } 29 | }) 30 | localStorage.setItem("favouriteMovies", "watchedMovies", JSON.stringify(movies)) 31 | } 32 | -------------------------------------------------------------------------------- /src/components/elements/MovieInfo/MovieInfo.css: -------------------------------------------------------------------------------- 1 | 2 | .movieInfo { 3 | background-color: rgba(0, 0, 0, 0.644); 4 | position: relative; 5 | } 6 | 7 | .add-to-favourites { 8 | position: absolute; 9 | top: 10px; 10 | right: 20px; 11 | transition: ease .3s all; 12 | cursor: pointer; 13 | color: rgb(250, 213, 7); 14 | } 15 | 16 | .add-to-watched { 17 | position: absolute; 18 | top: 80px; 19 | right: 20px; 20 | transition: ease .3s all; 21 | cursor: pointer; 22 | color: rgb(250, 213, 7); 23 | } 24 | 25 | /* .add-to-favourites:hover { 26 | color: red; 27 | } */ 28 | 29 | .movie-title { 30 | font-size: 40px; 31 | font-weight: 700 !important; 32 | color: #0275d8; 33 | } 34 | .breadcrumb { 35 | margin-bottom: 0 !important; 36 | background-color: #fff !important; 37 | } 38 | 39 | .breadcrumb a { 40 | text-decoration: none !important; 41 | } 42 | 43 | .movieInfoContainer { 44 | background-size: cover !important; 45 | background-repeat: no-repeat !important; 46 | min-height: 100vh; 47 | } 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movieapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "alertifyjs": "^1.13.1", 10 | "animate.css": "^3.7.2", 11 | "bootstrap": "^4.4.1", 12 | "react": "^16.13.1", 13 | "react-bootstrap": "^1.0.0", 14 | "react-dom": "^16.13.1", 15 | "react-iframe": "^1.8.0", 16 | "react-router-dom": "^5.3.0", 17 | "react-scripts": "3.4.1", 18 | "styled-components": "^5.3.3" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": "react-app" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/elements/LoadMore/LoadMoreBtn.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Row, Col, Badge, Container } from 'react-bootstrap'; 3 | import Proptypes from 'prop-types'; 4 | 5 | const LoadMoreBtn = ({ loadMoreMovies, currentPage, text }) => { 6 | return ( 7 |
8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 |
22 | ) 23 | } 24 | 25 | LoadMoreBtn.propTypes = { 26 | loadMoreMovies : Proptypes.func, 27 | text : Proptypes.string, 28 | currentPage : Proptypes.number 29 | } 30 | 31 | export default LoadMoreBtn; 32 | -------------------------------------------------------------------------------- /src/components/elements/BreadCrumb/BreadCrumbs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Breadcrumb } from 'react-bootstrap'; 3 | import { Link } from 'react-router-dom'; 4 | import Proptypes from 'prop-types'; 5 | 6 | 7 | const BreadCrumbs = ({ searchWord, clickable, title }) => { 8 | 9 | return ( 10 | 11 | 12 | Home / 13 | 14 | { 15 | searchWord ? 16 | 17 | {searchWord} 18 | {/* This props comes = Home ----> ImageFrame ----> Movie (with Link url way) ----> MovieInfo ----> Here */} 19 | : null 20 | } 21 | 22 | {title} 23 | 24 | 25 | ) 26 | } 27 | 28 | BreadCrumbs.propTypes = { 29 | title : Proptypes.string, 30 | searchWord : Proptypes.string, 31 | clickable : Proptypes.bool 32 | } 33 | 34 | BreadCrumbs.defaultProps = { 35 | title: 'Failed to Submit Actor Name' 36 | }; 37 | 38 | export default BreadCrumbs; 39 | -------------------------------------------------------------------------------- /src/components/elements/Actors/Actors.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Col, Card} from 'react-bootstrap'; 3 | import './Actors.css'; 4 | import { Link } from 'react-router-dom'; 5 | import PropTypes from 'prop-types'; 6 | 7 | const Actors = ({ personId, name, image, character}) => { 8 | 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 |

{name}

16 |

{character}

17 |
18 |
19 | 20 | 21 | ) 22 | } 23 | 24 | Actors.propTypes = { 25 | personId : PropTypes.number, 26 | name : PropTypes.string, 27 | image : PropTypes.string, 28 | character : PropTypes.string 29 | } 30 | 31 | 32 | export default Actors; -------------------------------------------------------------------------------- /src/components/elements/SearchBar/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Form, Container } from 'react-bootstrap'; 3 | import './SearchBar.css'; 4 | import Proptypes from 'prop-types'; 5 | 6 | class SearchBar extends Component { 7 | 8 | state = { 9 | value: "" 10 | } 11 | 12 | timeout = null; 13 | 14 | getValue = event => { 15 | let value = event.target.value; 16 | this.setState({ 17 | value 18 | }); 19 | 20 | clearTimeout(this.timeout); 21 | 22 | this.timeout = setTimeout(() => { 23 | this.props.callback(this.state.value); 24 | }, 500); 25 | } 26 | 27 | render() { 28 | 29 | const { placeHolder } = this.props 30 | 31 | return ( 32 |
33 | 34 | 35 | 41 | 42 | 43 | 44 |
45 | ) 46 | } 47 | } 48 | 49 | SearchBar.propTypes = { 50 | placeholder : Proptypes.string, 51 | callback : Proptypes.func 52 | } 53 | 54 | export default SearchBar; -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Footer() { 4 | return ( 5 |
6 |
7 |
8 |
9 | 10 |
    11 | 12 |
  • 13 | 14 | LinkedIn : 15 |

    16 |
  • 17 |
  • 18 | GitHub : 19 |

    20 |
  • 21 |
  • 22 | Medium : 23 |

    24 |
  • 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
© 2022 Copyright Naşide Yıldırım
34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 | ) 42 | } -------------------------------------------------------------------------------- /src/components/elements/Spinner/Spinner.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | font-size: 10px; 3 | margin: 50px auto; 4 | text-indent: -9999em; 5 | width: 11em; 6 | height: 11em; 7 | border-radius: 50%; 8 | background: #0026ff; 9 | background: -moz-linear-gradient(left, #0026ff 10%, rgba(0,38,255, 0) 42%); 10 | background: -webkit-linear-gradient(left, #0026ff 10%, rgba(0,38,255, 0) 42%); 11 | background: -o-linear-gradient(left, #0026ff 10%, rgba(0,38,255, 0) 42%); 12 | background: -ms-linear-gradient(left, #0026ff 10%, rgba(0,38,255, 0) 42%); 13 | background: linear-gradient(to right, #0026ff 10%, rgba(0,38,255, 0) 42%); 14 | position: relative; 15 | -webkit-animation: load3 1.4s infinite linear; 16 | animation: load3 1.4s infinite linear; 17 | -webkit-transform: translateZ(0); 18 | -ms-transform: translateZ(0); 19 | transform: translateZ(0); 20 | } 21 | .loader:before { 22 | width: 50%; 23 | height: 50%; 24 | background: #0026ff; 25 | border-radius: 100% 0 0 0; 26 | position: absolute; 27 | top: 0; 28 | left: 0; 29 | content: ''; 30 | } 31 | .loader:after { 32 | background: #f8f0ff; 33 | width: 75%; 34 | height: 75%; 35 | border-radius: 50%; 36 | content: ''; 37 | margin: auto; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | bottom: 0; 42 | right: 0; 43 | } 44 | @-webkit-keyframes load3 { 45 | 0% { 46 | -webkit-transform: rotate(0deg); 47 | transform: rotate(0deg); 48 | } 49 | 100% { 50 | -webkit-transform: rotate(360deg); 51 | transform: rotate(360deg); 52 | } 53 | } 54 | @keyframes load3 { 55 | 0% { 56 | -webkit-transform: rotate(0deg); 57 | transform: rotate(0deg); 58 | } 59 | 100% { 60 | -webkit-transform: rotate(360deg); 61 | transform: rotate(360deg); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/WatchedMovies/WatchedMovies.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ImageFrame from '../elements/ImageFrame/ImageFrame'; 3 | import { BASE_IMG } from '../../config'; 4 | import no_img from '../elements/img/no_image.jpg'; 5 | import { Container, Row, Col } from 'react-bootstrap'; 6 | import HaveNotWatchedMovies from '../elements/HaveNotWatchedMovies/HaveNotWatchedMovies'; 7 | import PageTitle from '../elements/PageTitle/PageTitle'; 8 | 9 | class WatchedMovies extends Component { 10 | 11 | render() { 12 | const { watchedMovies, clearAllWatchedMovies, clearWatchedMovie } = this.props; 13 | 14 | return ( 15 | 16 | { 17 | watchedMovies.length ? 18 | 19 | 20 | 21 | { 22 | watchedMovies.map((movie) => { 23 | return ( 24 | 31 | ) 32 | }) 33 | } 34 | 35 | {watchedMovies.length > 1 && 36 | 37 | 38 | 43 | 44 | 45 | } 46 | 47 | 48 | : 49 | 50 | 51 | } 52 | 53 | 54 | ) 55 | } 56 | } 57 | 58 | export default WatchedMovies; -------------------------------------------------------------------------------- /src/components/elements/ImageFrame/ImageFrame.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Col } from 'react-bootstrap'; 3 | import { Link } from 'react-router-dom'; 4 | import PropTypes from 'prop-types'; 5 | import './ImageFrame.css'; 6 | 7 | 8 | const ImageFrame = ({ movieId, movieName, searchWord, image, personMovieId, clickable, clearFavouriteMovie }) => { 9 | 10 | return ( 11 | 12 | { clickable ? /* if clickable props is true --> go movie, else go movie again but with personal movie id ! */ 13 | 14 | 15 | 16 | 17 | 18 | 19 | { 20 | clearFavouriteMovie && 24 | 25 | } 26 | 27 | : 28 | {/* Person Known For Movies*/} 29 | 30 | 31 | 32 | 33 | } 34 | 35 | ) 36 | } 37 | 38 | ImageFrame.propTypes = { 39 | image : PropTypes.string, 40 | movieId : PropTypes.number, 41 | movieName : PropTypes.string, 42 | searchWord : PropTypes.string, 43 | id : PropTypes.number, 44 | clickable: PropTypes.bool 45 | } 46 | 47 | export default ImageFrame; 48 | -------------------------------------------------------------------------------- /src/components/FavouriteMovies/FavouriteMovies.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ImageFrame from '../elements/ImageFrame/ImageFrame'; 3 | import { BASE_IMG } from '../../config'; 4 | import no_img from '../elements/img/no_image.jpg'; 5 | import { Container, Row, Col } from 'react-bootstrap'; 6 | import HaveNotFavouriteMovies from '../elements/HaveNotFavouriteMovies/HaveNotFavouriteMovies'; 7 | import PageTitle from '../elements/PageTitle/PageTitle'; 8 | 9 | class FavouriteMovies extends Component { 10 | 11 | render() { 12 | const { favouriteMovies, clearAllFavouriteMovies, clearFavouriteMovie } = this.props; 13 | 14 | return ( 15 | 16 | { 17 | favouriteMovies.length ? 18 | 19 | 20 | 21 | { 22 | favouriteMovies.map((movie) => { 23 | return ( 24 | 31 | ) 32 | }) 33 | } 34 | 35 | {favouriteMovies.length > 1 && 36 | 37 | 38 | 43 | 44 | 45 | } 46 | 47 | 48 | : 49 | 50 | 51 | } 52 | 53 | 54 | ) 55 | } 56 | } 57 | 58 | export default FavouriteMovies; 59 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | 28 | 29 | 30 | 31 | 32 | Movie App 33 | 34 | 35 | 36 |
37 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/components/SimilarMovies.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | export default function SimilarMovies(props) { 5 | return ( 6 | 7 |
8 | {props.vote} 9 |
10 |
11 |
12 | 17 | 18 | 19 | 26 | 27 | 28 | 33 | 34 | 35 |
36 | 40 | {`${props.title}`} 44 | 45 |
46 |
47 |
{props.title}
48 |
49 |
50 | ); 51 | } -------------------------------------------------------------------------------- /src/components/elements/SearchBar/SearchBar.css: -------------------------------------------------------------------------------- 1 | .form-control { 2 | border: 4px inset rgba(248, 192, 7, 0.973) !important; 3 | border-radius: 25px ; 4 | position: center; 5 | 6 | 7 | } 8 | 9 | 10 | 11 | ::placeholder { 12 | text-align: left; 13 | } 14 | 15 | .gener{ 16 | position: right; 17 | right: 20px; 18 | cursor: pointer; 19 | color: blue; 20 | outline: none; 21 | border: none; 22 | font-weight: 800; 23 | border-radius: 0.2vw; 24 | padding-left: 2rem; 25 | padding-right: 2rem; 26 | margin-right: 1rem; 27 | padding-top: 0.5rem; 28 | 29 | padding-bottom: 0.5rem; 30 | } 31 | 32 | .gener:hover{ 33 | color: black; 34 | background-color: blue; 35 | transition: all 0.52s; 36 | 37 | 38 | } 39 | .forme{ 40 | min-height: 400px; 41 | position: center; 42 | display: none; 43 | font-family: 'Poppins', sans-serif; 44 | margin: auto; 45 | text-align: center; 46 | width: 350px; 47 | padding: 20px; 48 | border-radius: 10px; 49 | background: rgba(255, 255, 255, 0.2); 50 | border: 1px solid blue; 51 | border-right: 1px solid blue; 52 | border-bottom: 1px solid blue; 53 | backdrop-filter: blur(5px); 54 | box-shadow: 0 25px 45px rgba(202, 17, 17, 0.1); 55 | } 56 | .forme .close-btn{ 57 | color: blue; 58 | width: 20px; 59 | margin-top: -10px; 60 | display: flex; 61 | font-size: 25px; 62 | cursor: pointer; 63 | } 64 | .close-btn:hover{ 65 | color: blue; 66 | } 67 | .forme h2{ 68 | text-align: center; 69 | color: blue; 70 | text-transform: uppercase; 71 | } 72 | form{ 73 | margin: 20px; 74 | } 75 | label{ 76 | display: block; 77 | color: blue; 78 | font-size: 18px; 79 | margin-top: 10px; 80 | } 81 | input{ 82 | display: block; 83 | width: 90%; 84 | background: transparent; 85 | border: none; 86 | outline: none; 87 | border-bottom: 1px solid blue; 88 | padding: 10px; 89 | color: blue; 90 | } 91 | .forme button{ 92 | display: block; 93 | width: 95%; 94 | padding: 8px; 95 | border: none; 96 | outline: none; 97 | background: linear-gradient(to right, blue, 98 | #095a6e); 99 | color: white; 100 | font-size: 18px; 101 | cursor: pointer; 102 | margin-top: 20px; 103 | 104 | 105 | } 106 | .forme button:hover{ 107 | background: linear-gradient(to right, blue, 108 | #023e46); 109 | 110 | } 111 | .forme a{ 112 | text-decoration: none; 113 | color: brown; 114 | 115 | } 116 | 117 | .link{ 118 | margin-top: 30px; 119 | text-align: center; 120 | color: brown; 121 | } 122 | 123 | .forme a:hover{ 124 | text-decoration: underline; 125 | } 126 | 127 | @media only screen and (max-width: 530px){ 128 | .navbar img{ 129 | width: 100px; 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /src/components/Navbar/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Navbar, Nav } from 'react-bootstrap'; 3 | import { Link } from 'react-router-dom'; 4 | import logo from '../elements/img/icon.svg'; 5 | import "./Navbar.css"; 6 | 7 | const Navi = () => { 8 | const [show, handleShow] = useState(false); 9 | 10 | useEffect(() => { 11 | window.addEventListener("scroll", () => { 12 | if (window.scrollY > 100) { 13 | handleShow(true); 14 | } else handleShow(false); 15 | }); 16 | return () => { 17 | window.removeEventListener("scroll"); 18 | }; 19 | }, []); 20 | const openForm = () => { 21 | document.getElementById("fom").style.display = "block"; 22 | } 23 | 24 | const closeForm = () => { 25 | document.getElementById("fom").style.display = "none"; 26 | } 27 | return ( 28 | 29 | 30 | logo 31 | 32 | 33 | 63 | 64 | 65 | ) 66 | } 67 | 68 | export default Navi; 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/components/Navbar/Navbar.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap'); 2 | 3 | .navbar{ 4 | position: fixed; 5 | padding: 10px; 6 | width: 100%; 7 | height: 80px; 8 | top:0; 9 | display: flex; 10 | justify-content: space-between; 11 | z-index: 1; 12 | transition: all 0.6s; 13 | transition-timing-function: ease-in; 14 | } 15 | 16 | 17 | 18 | .gener{ 19 | position: right; 20 | right: 20px; 21 | cursor: pointer; 22 | color: blue; 23 | outline: none; 24 | border: none; 25 | font-weight: 800; 26 | border-radius: 0.2vw; 27 | padding-left: 2rem; 28 | padding-right: 2rem; 29 | margin-right: 1rem; 30 | padding-top: 0.5rem; 31 | 32 | padding-bottom: 0.5rem; 33 | } 34 | 35 | .gener:hover{ 36 | color: black; 37 | background-color: blue; 38 | transition: all 0.52s; 39 | 40 | 41 | } 42 | .forme{ 43 | min-height: 400px; 44 | position: center; 45 | display: none; 46 | font-family: 'Poppins', sans-serif; 47 | margin: auto; 48 | text-align: center; 49 | width: 350px; 50 | padding: 20px; 51 | border-radius: 10px; 52 | background: rgba(255, 255, 255, 0.2); 53 | border: 1px solid blue; 54 | border-right: 1px solid blue; 55 | border-bottom: 1px solid blue; 56 | backdrop-filter: blur(5px); 57 | box-shadow: 0 25px 45px rgba(202, 17, 17, 0.1); 58 | } 59 | .forme .close-btn{ 60 | color: blue; 61 | width: 20px; 62 | margin-top: -10px; 63 | display: flex; 64 | font-size: 25px; 65 | cursor: pointer; 66 | } 67 | .close-btn:hover{ 68 | color: blue; 69 | } 70 | .forme h2{ 71 | text-align: center; 72 | color: blue; 73 | text-transform: uppercase; 74 | } 75 | form{ 76 | margin: 20px; 77 | } 78 | label{ 79 | display: block; 80 | color: blue; 81 | font-size: 18px; 82 | margin-top: 10px; 83 | } 84 | input{ 85 | display: block; 86 | width: 90%; 87 | background: transparent; 88 | border: none; 89 | outline: none; 90 | border-bottom: 1px solid blue; 91 | padding: 10px; 92 | color: blue; 93 | } 94 | .forme button{ 95 | display: block; 96 | width: 95%; 97 | padding: 8px; 98 | border: none; 99 | outline: none; 100 | background: linear-gradient(to right, blue, 101 | #095a6e); 102 | color: white; 103 | font-size: 18px; 104 | cursor: pointer; 105 | margin-top: 20px; 106 | 107 | 108 | } 109 | .forme button:hover{ 110 | background: linear-gradient(to right, blue, 111 | #023e46); 112 | 113 | } 114 | .forme a{ 115 | text-decoration: none; 116 | color: brown; 117 | 118 | } 119 | 120 | .link{ 121 | margin-top: 30px; 122 | text-align: center; 123 | color: brown; 124 | } 125 | 126 | .forme a:hover{ 127 | text-decoration: underline; 128 | } 129 | 130 | @media only screen and (max-width: 530px){ 131 | .navbar img{ 132 | width: 100px; 133 | } 134 | 135 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://user-images.githubusercontent.com/26977710/159775622-b87d4e6f-6af2-4e5e-86f4-852004f7e54f.mp4 4 | 5 | 6 | 7 | React TMDB API Movie Project 8 | ------------------------------------------------------------ 9 | 10 | Getting Started with Create React App 11 | -------------------------------------------------------- 12 | This project was bootstrapped with Create React App. 13 | 14 | Available Scripts 15 | -------------------------------------------------- 16 | In the project directory, you can run: 17 | 18 | npm start 19 | --------------------- 20 | Runs the app in the development mode. 21 | Open http://localhost:3000 to view it in your browser. 22 | 23 | The page will reload when you make changes. 24 | You may also see any lint errors in the console. 25 | 26 | npm test 27 | -------------------- 28 | Launches the test runner in the interactive watch mode. 29 | See the section about running tests for more information. 30 | 31 | npm run build 32 | --------------------------- 33 | Builds the app for production to the build folder. 34 | It correctly bundles React in production mode and optimizes the build for the best performance. 35 | 36 | The build is minified and the filenames include the hashes. 37 | Your app is ready to be deployed! 38 | 39 | See the section about deployment for more information. 40 | 41 | npm run eject 42 | -------------------------------- 43 | Note: this is a one-way operation. Once you eject, you can't go back! 44 | 45 | If you aren't satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project. 46 | 47 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 48 | 49 | You don't have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 50 | 51 | Learn More 52 | ---------------------------------------------------------------------------------------- 53 | You can learn more in the Create React App documentation. 54 | 55 | To learn React, check out the React documentation. 56 | 57 | Code Splitting 58 | ------------------------------------- 59 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 60 | 61 | Analyzing the Bundle Size 62 | --------------------------------------- 63 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 64 | 65 | Making a Progressive Web App 66 | ----------------------------------------- 67 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 68 | 69 | Advanced Configuration 70 | ------------------------------------------- 71 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 72 | 73 | Deployment 74 | ---------------------------------------------- 75 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 76 | 77 | npm run build fails to minify 78 | ----------------------------------------------- 79 | 80 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 81 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | const isLocalhost = Boolean( 2 | window.location.hostname === 'localhost' || 3 | 4 | window.location.hostname === '[::1]' || 5 | 6 | window.location.hostname.match( 7 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 8 | ) 9 | ); 10 | 11 | export function register(config) { 12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 13 | 14 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 15 | if (publicUrl.origin !== window.location.origin) { 16 | 17 | return; 18 | } 19 | 20 | window.addEventListener('load', () => { 21 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 22 | 23 | if (isLocalhost) { 24 | 25 | navigator.serviceWorker.ready.then(() => { 26 | console.log( 27 | 'This web app is being served cache-first by a service ' + 28 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 29 | ); 30 | }); 31 | } else { 32 | 33 | registerValidSW(swUrl, config); 34 | } 35 | }); 36 | } 37 | } 38 | 39 | function registerValidSW(swUrl, config) { 40 | navigator.serviceWorker 41 | .register(swUrl) 42 | .then(registration => { 43 | registration.onupdatefound = () => { 44 | const installingWorker = registration.installing; 45 | if (installingWorker == null) { 46 | return; 47 | } 48 | installingWorker.onstatechange = () => { 49 | if (installingWorker.state === 'installed') { 50 | if (navigator.serviceWorker.controller) { 51 | 52 | console.log( 53 | 'New content is available and will be used when all ' + 54 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 55 | ); 56 | 57 | 58 | if (config && config.onUpdate) { 59 | config.onUpdate(registration); 60 | } 61 | } else { 62 | 63 | console.log('Content is cached for offline use.'); 64 | 65 | if (config && config.onSuccess) { 66 | config.onSuccess(registration); 67 | } 68 | } 69 | } 70 | }; 71 | }; 72 | }) 73 | .catch(error => { 74 | console.error('Error during service worker registration:', error); 75 | }); 76 | } 77 | 78 | function checkValidServiceWorker(swUrl, config) { 79 | 80 | fetch(swUrl, { 81 | headers: { 'Service-Worker': 'script' }, 82 | }) 83 | .then(response => { 84 | 85 | const contentType = response.headers.get('content-type'); 86 | if ( 87 | response.status === 404 || 88 | (contentType != null && contentType.indexOf('javascript') === -1) 89 | ) { 90 | 91 | navigator.serviceWorker.ready.then(registration => { 92 | registration.unregister().then(() => { 93 | window.location.reload(); 94 | }); 95 | }); 96 | } else { 97 | 98 | registerValidSW(swUrl, config); 99 | } 100 | }) 101 | .catch(() => { 102 | console.log( 103 | 'No internet connection found. App is running in offline mode.' 104 | ); 105 | }); 106 | } 107 | 108 | export function unregister() { 109 | if ('serviceWorker' in navigator) { 110 | navigator.serviceWorker.ready 111 | .then(registration => { 112 | registration.unregister(); 113 | }) 114 | .catch(error => { 115 | console.error(error.message); 116 | }); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/components/Movie/Movie.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BASE_URL, API_KEY } from '../../config'; 3 | import Spinner from '../elements/Spinner/Spinner'; 4 | import MovieInfo from '../elements/MovieInfo/MovieInfo'; 5 | 6 | 7 | class Movie extends Component { 8 | 9 | state = { 10 | movie: [], 11 | loadingMovies: false, 12 | loadingActors: false, 13 | actors: [], 14 | similar_movies_array: [], 15 | directors: [], 16 | visible: 6, // This state is for how many actors rendered. 17 | 18 | } 19 | 20 | componentDidMount() { 21 | const { match } = this.props; 22 | 23 | this.setState({ 24 | ...this.state, 25 | loadingMovies: true, 26 | loadingActors: true, 27 | 28 | }) 29 | 30 | let moviesEndPoint = `${BASE_URL}/movie/${match.params.movieId}?api_key=${API_KEY}&language=en-US` 31 | let creditsEndPoint = `${BASE_URL}/movie/${match.params.movieId}/credits?api_key=${API_KEY}`; 32 | this.getMovieWithId(moviesEndPoint); 33 | this.getDirectorsAndActors(creditsEndPoint); 34 | 35 | 36 | } 37 | 38 | getMovieWithId = moviesEndPoint => { 39 | const { match } = this.props; 40 | 41 | fetch(moviesEndPoint) 42 | .then(response => response.json()) 43 | .then((movie) => { 44 | // console.log(movie); 45 | 46 | if (movie.overview !== "") { 47 | this.setState({ 48 | movie, 49 | loadingMovies: false 50 | }) 51 | } 52 | else { // if have not turkish overview fetch this 53 | let engEndPoint = `${BASE_URL}/movie/${match.params.movieId}?api_key=${API_KEY}` 54 | fetch(engEndPoint) 55 | .then(response => response.json()) 56 | .then((movie) => { 57 | this.setState({ 58 | movie, 59 | loadingMovies: false 60 | }) 61 | }) 62 | } 63 | }) 64 | } 65 | 66 | getDirectorsAndActors = creditsEndPoint => { 67 | fetch(creditsEndPoint) 68 | .then(response => response.json()) 69 | .then((credits) => { 70 | // console.log(credits) 71 | const filterDirector = credits.crew.filter(person => person.job === "Director"); // filter directors from all employees 72 | // console.log(credits.crew) 73 | if (filterDirector.length) { 74 | this.setState({ 75 | actors: credits.cast, 76 | directors: filterDirector[0].name, 77 | loadingActors: false })} 78 | else { 79 | this.setState({ 80 | actors: credits.cast, 81 | directors: "No Information", 82 | loadingActors: false 83 | })} 84 | }) 85 | } 86 | 87 | loadMore = () => { 88 | this.setState({ 89 | visible: this.state.visible + 6, 90 | }) 91 | } 92 | 93 | render() { 94 | const { movie, loadingActors, loadingMovies, actors, directors, visible } = this.state 95 | const { location, getFavouriteMovies, getWatchedMovies } = this.props 96 | return ( 97 | 98 | { loadingActors || loadingMovies ? : 99 | (movie && actors.length ) ? 100 | : null 112 | } 113 | 114 | { 115 | !actors.length && !loadingActors ?

No Movie Found!

: null 116 | } 117 |
118 | ) 119 | } 120 | } 121 | 122 | export default Movie; 123 | 124 | -------------------------------------------------------------------------------- /src/components/Home/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BASE_URL, API_KEY, BASE_IMG } from '../../config' 3 | import ImageFrame from '../elements/ImageFrame/ImageFrame'; 4 | import Spinner from '../elements/Spinner/Spinner'; 5 | import LoadMoreBtn from '../elements/LoadMore/LoadMoreBtn' 6 | import SearchBar from '../elements/SearchBar/SearchBar'; 7 | import { Row, Col, Container } from 'react-bootstrap'; 8 | import no_img from '../elements/img/no_image.jpg'; 9 | 10 | 11 | class Home extends Component { 12 | 13 | state = { 14 | movies: [], 15 | currentPage: 0, 16 | totalPage: 0, 17 | loading: false, // Loading Effect 18 | searchWord: "", 19 | } 20 | 21 | componentDidMount() { 22 | const endPoint = `${BASE_URL}/movie/popular?api_key=${API_KEY}&page=1`; 23 | this.getRequest(endPoint); 24 | 25 | this.setState({ 26 | loading: true 27 | }) 28 | }; 29 | 30 | searchMovies = searchWord => { // This function trigger the Get Request Function 31 | 32 | let endPoint = ""; 33 | this.setState({ 34 | movies: [], 35 | searchWord, 36 | loading: true 37 | }); 38 | 39 | if (this.state.searchWord === "") { 40 | endPoint = `${BASE_URL}/movie/popular?api_key=${API_KEY}&page=1`; 41 | } 42 | else { 43 | endPoint = `${BASE_URL}/search/movie?api_key=${API_KEY}&query=${this.state.searchWord}` 44 | } 45 | 46 | this.getRequest(endPoint) 47 | } 48 | 49 | loadMoreMovies = () => { 50 | let endPoint = ""; 51 | this.setState({ 52 | loading: true 53 | }); 54 | 55 | if (this.state.searchWord === "") { 56 | endPoint = `${BASE_URL}/movie/popular?api_key=${API_KEY}&page=${this.state.currentPage + 1}`; 57 | } 58 | else { 59 | endPoint = `${BASE_URL}/search/movie?api_key=${API_KEY}&query=${this.state.searchWord}&page=${this.state.currentPage + 1}` 60 | } 61 | 62 | this.getRequest(endPoint) 63 | } 64 | 65 | getRequest = (endPoint) => { 66 | fetch(endPoint) 67 | .then(response => response.json()) 68 | .then(data => { 69 | // console.log(data); 70 | 71 | this.setState({ 72 | movies: [...this.state.movies, ...data.results], 73 | currentPage: data.page, 74 | totalPage: data.total_pages, 75 | loading: false 76 | }) 77 | }) 78 | }; 79 | 80 | render() { 81 | const { searchWord, movies, loading, currentPage, totalPage } = this.state 82 | 83 | return ( 84 | <> 85 | 89 | 90 | 91 | 92 | {searchWord ?

Search Results {searchWord}

: 93 |
94 |

Discover / Trending

95 |
96 | } 97 | 98 |
99 | 100 | { 101 | movies.map((movie) => { 102 | return 114 | }) 115 | } 116 | 117 |
118 | {loading ? : null} 119 | {(currentPage <= totalPage && !loading ) ? 120 | : null 121 | } 122 | 123 | ) 124 | } 125 | } 126 | 127 | export default Home; -------------------------------------------------------------------------------- /src/components/elements/MovieInfo/MovieInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Actors from '../Actors/Actors'; 3 | import { Row, Col, Container, ProgressBar, Badge } from "react-bootstrap"; 4 | import BreadCrumbs from "../BreadCrumb/BreadCrumbs"; 5 | import { BASE_IMG, IMAGE_BASE_URL, BACKDROP_SIZE } from "../../../config"; 6 | import './MovieInfo.css'; 7 | import no_img from '../img/no_image.jpg'; 8 | import no_img_bg from '../img/bg-not-found.jpg'; 9 | import Proptypes from 'prop-types'; 10 | import LoadMoreBtn from "../LoadMore/LoadMoreBtn"; 11 | import Spinner from "../Spinner/Spinner"; 12 | import { editReleaseDate } from '../../../Functions/CommonFunctions/commonFunctions'; 13 | 14 | import PageTitle from "../PageTitle/PageTitle"; 15 | 16 | 17 | const MovieInfo = ({ movieInfo, searchWord, directors, actors, visible, loadMore, loading, getFavouriteMovies, getWatchedMovies }) => { 18 | 19 | 20 | return ( 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 40 | 41 | { 42 | movieInfo.poster_path ? 43 | movieImg 47 | : 48 | movieImg 53 | } 54 | 55 | 56 |
57 | getFavouriteMovies(movieInfo)} 59 | className = "add-to-favourites"> 60 | 61 | 62 |
63 |
64 | getWatchedMovies(movieInfo)} 66 | className = "add-to-watched"> 67 | 68 | 69 |
70 |

{movieInfo.title}

71 |
Release Date of the Movie: {editReleaseDate(movieInfo.release_date)}
72 |
Explanation
73 |

{movieInfo.overview}

74 | 75 |
Type of the Movie: 76 | 77 | 78 | { 79 | movieInfo.genres.map((genre) => { 80 | return 85 | {genre.name} 86 | 87 | }) 88 | } 89 | 90 | 91 |
92 | 93 |
Director: {directors}
94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | { 103 | actors.slice(0, `${visible}`).map((actor, i) => { 104 | return 112 | }) 113 | } 114 | 115 | { 116 | loading ? : 117 | ( 118 | visible < actors.length ? : null 122 | ) 123 | } 124 | 125 |
126 | ); 127 | }; 128 | 129 | MovieInfo.propTypes = { 130 | searchWord : Proptypes.string 131 | } 132 | 133 | export default MovieInfo; 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/components/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useContext } from 'react'; 2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 3 | import Home from '../Home/Home'; 4 | import FavouriteMovies from '../FavouriteMovies/FavouriteMovies'; 5 | import WatchedMovies from '../WatchedMovies/WatchedMovies'; 6 | import Navbar from '../Navbar/Navbar'; 7 | import Footer from '../Footer/Footer'; 8 | import Movie from '../Movie/Movie'; 9 | import NotFound from '../NotFound/NotFound'; 10 | import ScrollTop from '../elements/ScrollTop/ScrollTop'; 11 | import alertify from 'alertifyjs'; 12 | import { addLocalStorage, removeMovieFromStorage } from '../../Functions/StorageFunctions/storageFunctions'; 13 | import UserProvider from '../../context'; 14 | 15 | class App extends Component { 16 | 17 | state = { 18 | favouriteMovies: [], 19 | watchedMovies: [], 20 | } 21 | 22 | componentDidMount() { 23 | if (localStorage.getItem("favouriteMovies")) { // First Contact this App 24 | let movies = JSON.parse(localStorage.getItem('favouriteMovies')); 25 | this.setState({ 26 | ...this.state, 27 | favouriteMovies : movies, 28 | }) 29 | } 30 | } 31 | 32 | getFavouriteMovies = favouritesMovie => { 33 | const stateMovies = this.state.favouriteMovies; 34 | const addedMovie = stateMovies.find(movie => movie.id === favouritesMovie.id); 35 | if (!addedMovie) { 36 | this.setState({ 37 | ...this.state, 38 | favouriteMovies: [...stateMovies, favouritesMovie], 39 | }) 40 | addLocalStorage(favouritesMovie) 41 | alertify.success("Movie Added Successfully" , 2) 42 | } 43 | else { 44 | alertify.error("This movie is already on your list", 2) 45 | 46 | } 47 | } 48 | 49 | clearFavouriteMovie = id => { 50 | const filterMovie = this.state.favouriteMovies.filter(movie => movie.id !== id); 51 | this.setState({ 52 | favouriteMovies: filterMovie 53 | }) 54 | alertify.success(" This Movie Has Been Successfully Deleted", 2) 55 | removeMovieFromStorage(id) 56 | } 57 | 58 | clearAllFavouriteMovies = () => { 59 | this.setState({ 60 | favouriteMovies: [] 61 | }) 62 | if (this.state.favouriteMovies.length) { 63 | alertify.success(" All Movies Deleted Successfully", 2) 64 | } 65 | 66 | localStorage.removeItem("favouriteMovies"); 67 | } 68 | 69 | 70 | ////////// 71 | 72 | 73 | componentDidMount() { 74 | if (localStorage.getItem("watchedMovies")) { // First Contact this App 75 | let movies = JSON.parse(localStorage.getItem('watchedMovies')); 76 | this.setState({ 77 | ...this.state, 78 | watchedMovies : movies, 79 | }) 80 | } 81 | } 82 | 83 | getWatchedMovies = watchedMovie => { 84 | const stateMovies = this.state.watchedMovies; 85 | const addedMovie = stateMovies.find(movie => movie.id === watchedMovie.id); 86 | if (!addedMovie) { 87 | this.setState({ 88 | ...this.state, 89 | watchedMovies: [...stateMovies, watchedMovie], 90 | }) 91 | addLocalStorage(watchedMovie) 92 | alertify.success("Movie Added Successfully", 2) 93 | } 94 | else { 95 | alertify.error("This movie is already on your list", 2) 96 | 97 | } 98 | } 99 | 100 | clearWatchedMovie = id => { 101 | const filterMovie = this.state.watchedMovies.filter(movie => movie.id !== id); 102 | this.setState({ 103 | watchedMovies: filterMovie 104 | }) 105 | alertify.success(" This Movie Has Been Successfully Deleted", 2) 106 | removeMovieFromStorage(id) 107 | } 108 | 109 | clearAllWatchedMovies = () => { 110 | this.setState({ 111 | watchedMovies: [] 112 | }) 113 | if (this.state.watchedMovies.length) { 114 | alertify.success(" All Movies Deleted Successfully", 2) 115 | } 116 | 117 | localStorage.removeItem("watchedMovies"); 118 | } 119 | 120 | 121 | render() { 122 | 123 | const { favouriteMovies , watchedMovies } = this.state; 124 | 125 | return ( 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | ( 136 | 142 | ) 143 | } 144 | /> 145 | ( 148 | 152 | ) 153 | } 154 | /> 155 | 156 | ( 159 | 165 | ) 166 | } 167 | /> 168 | ( 171 | 175 | ) 176 | } 177 | /> 178 | 179 | 180 | 181 |