├── 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 |

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 |

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 |

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 |

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 |
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 |
19 |
28 |
35 |
36 |
40 |

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 |
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 |
47 | :
48 |
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 |
182 |
183 |
184 |
185 | )
186 | }
187 |
188 | }
189 | export default App;
190 |
--------------------------------------------------------------------------------
/src/components/elements/img/icon.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------