├── .gitignore
├── README.md
├── README.old.md
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo.png
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.js
├── App.scss
├── App.test.js
├── Assets
│ ├── Footer1.png
│ ├── Footer2.png
│ ├── default.png
│ ├── guyPearce.png
│ ├── morganFreeman.png
│ └── tmdb.svg
├── Components
│ ├── Firebase
│ │ └── firebase.utils.js
│ ├── Footer
│ │ ├── Footer.jsx
│ │ └── Footer.scss
│ ├── Hero
│ │ ├── Hero.jsx
│ │ └── Hero.scss
│ ├── Home
│ │ ├── Home.jsx
│ │ └── Home.scss
│ ├── LoadMore
│ │ └── LoadMore.jsx
│ ├── MidMenu
│ │ ├── MidMenu.jsx
│ │ └── MidMenu.scss
│ ├── Modal
│ │ ├── Modal.jsx
│ │ └── Modal.scss
│ ├── MovieDetails
│ │ ├── MovieDetails.jsx
│ │ └── MovieDetails.scss
│ ├── MoviesComing
│ │ └── MoviesComing.jsx
│ ├── MoviesNow
│ │ └── MoviesNow.jsx
│ ├── MoviesPopular
│ │ └── MoviesPopular.jsx
│ ├── MoviesTop
│ │ └── MoviesTop.jsx
│ ├── Nav
│ │ ├── Nav.scss
│ │ └── NavTop.jsx
│ ├── Search
│ │ ├── Search.jsx
│ │ └── Search.scss
│ ├── SignIn
│ │ ├── SignIn.jsx
│ │ ├── SignIn.scss
│ │ ├── SignInComponent
│ │ │ ├── SignInComponent.jsx
│ │ │ └── SignInComponent.scss
│ │ └── SignUpComponent
│ │ │ ├── SignUpComponent.jsx
│ │ │ └── SignUpComponent.scss
│ ├── User
│ │ ├── User.jsx
│ │ └── User.scss
│ └── defaultMovieComponent
│ │ └── DefaultMovieComponent.jsx
├── Context.jsx
├── ScrollToTop.jsx
├── config.js
├── favicon.png
├── index.js
├── index.scss
├── logo.png
├── logo.svg
├── logo_transparent.png
├── serviceWorker.js
└── setupTests.js
└── yarn.lock
/.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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scriptbase :movie_camera:
2 |
3 | # A Movie Database made with React and TMDB API
4 |
5 | ## Using Router, Firebase, Context API and styled with Bootstrap
6 |
7 | ## Hero
8 |
9 | 
10 |
11 | ## Popular Movies/Movie Menus
12 |
13 | 
14 |
15 | ## Movie Details
16 |
17 | 
18 |
19 | 
20 |
21 | ## User Page
22 |
23 | 
24 |
25 | ## Sign In
26 |
27 | 
28 |
29 | ## Search Modal
30 |
31 | 
32 |
33 |
--------------------------------------------------------------------------------
/README.old.md:
--------------------------------------------------------------------------------
1 | # react-scriptbase
2 | A movie database made with React and TMDB API
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-scriptbase",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.3.2",
8 | "@testing-library/user-event": "^7.1.2",
9 | "axios": "^0.21.1",
10 | "bootstrap": "^4.4.1",
11 | "firebase": "^7.7.0",
12 | "node-sass": "^4.13.1",
13 | "react": "^16.12.0",
14 | "react-bootstrap": "^1.0.0-beta.16",
15 | "react-dom": "^16.12.0",
16 | "react-id-swiper": "^2.4.0",
17 | "react-lazyload": "^2.6.5",
18 | "react-persist": "^1.0.2",
19 | "react-router-dom": "^5.1.2",
20 | "react-scripts": "3.3.0",
21 | "swiper": "^5.3.0"
22 | },
23 | "scripts": {
24 | "start": "react-scripts start",
25 | "build": "react-scripts build",
26 | "test": "react-scripts test",
27 | "eject": "react-scripts eject"
28 | },
29 | "eslintConfig": {
30 | "extends": "react-app"
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
30 | Scriptbase - Movie DataBase
31 |
32 |
33 |
34 | You need to enable JavaScript to run this app.
35 |
36 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/public/logo.png
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/public/logo512.png
--------------------------------------------------------------------------------
/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 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import NavTop from "./components/nav/NavTop";
4 | import Home from "./components/home/Home";
5 | import MovieDetails from "./components/movieDetails/MovieDetails";
6 | import Footer from "./components/footer/Footer";
7 | import Modal from "./components/modal/Modal";
8 | import SignIn from "./components/signIn/SignIn";
9 | import User from "./components/user/User";
10 |
11 | import MovieContextProvider from "./Context";
12 |
13 | import { Switch, Route } from "react-router-dom";
14 |
15 | import "./App.scss";
16 |
17 | const App = () => {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/src/App.scss:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | position: relative;
4 | min-height: 100vh;
5 | transition: all 0.5 linear;
6 | // height: 250vh;
7 | }
8 |
9 |
10 | /* font-family: 'PT Sans', sans-serif;
11 | font-family: 'Lato', sans-serif;
12 | font-family: 'Metrophobic', sans-serif; */
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render( );
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/Assets/Footer1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/Assets/Footer1.png
--------------------------------------------------------------------------------
/src/Assets/Footer2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/Assets/Footer2.png
--------------------------------------------------------------------------------
/src/Assets/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/Assets/default.png
--------------------------------------------------------------------------------
/src/Assets/guyPearce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/Assets/guyPearce.png
--------------------------------------------------------------------------------
/src/Assets/morganFreeman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/Assets/morganFreeman.png
--------------------------------------------------------------------------------
/src/Assets/tmdb.svg:
--------------------------------------------------------------------------------
1 | PrimaryLogo_Green
--------------------------------------------------------------------------------
/src/Components/Firebase/firebase.utils.js:
--------------------------------------------------------------------------------
1 | import firebase from "firebase/app";
2 | import 'firebase/firestore';
3 | import 'firebase/auth';
4 |
5 | const config = {
6 | apiKey: "AIzaSyDIAO8bU1tlWLx9IPOeWVzGaIyPyRdKHts",
7 | authDomain: "urldev-scriptbase.firebaseapp.com",
8 | databaseURL: "https://urldev-scriptbase.firebaseio.com",
9 | projectId: "urldev-scriptbase",
10 | storageBucket: "urldev-scriptbase.appspot.com",
11 | messagingSenderId: "976679482375",
12 | appId: "1:976679482375:web:1dc8c1b43bbbed38b366a2"
13 | };
14 |
15 | firebase.initializeApp(config);
16 |
17 | //userAuth is coming from data that firebase stores.
18 | //additionalData is for other data we would need
19 | export const createUserProfileDocument = async(userAuth, additionalData) => {
20 | //if theres no userAuth, exit from statement
21 | if (!userAuth) return;
22 |
23 | const userRef = firestore.doc(`users/${userAuth.uid}`);
24 |
25 | //snapshot will check if user exists
26 | const snapShot = await userRef.get();
27 |
28 | //this will create a new user in the database
29 | if (!snapShot.exists) {
30 | const { displayName, email } = userAuth;
31 | const createdAt = new Date();
32 | try {
33 | await userRef.set({
34 | displayName,
35 | email,
36 | createdAt,
37 | ...additionalData
38 | });
39 | } catch (error) {
40 | console.log('error creating user', error.message);
41 | }
42 | }
43 |
44 | return userRef;
45 | };
46 |
47 |
48 | export const auth = firebase.auth();
49 | export const firestore = firebase.firestore();
50 |
51 | const provider = new firebase.auth.GoogleAuthProvider();
52 | provider.setCustomParameters({ prompt: 'select_account' });
53 | export const signInWithGoogle = () => auth.signInWithPopup(provider);
54 |
55 | export default firebase;
--------------------------------------------------------------------------------
/src/Components/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Container, Row, Col } from "react-bootstrap";
3 | import { Link } from "react-router-dom";
4 | import { MovieContext } from "../../Context";
5 |
6 | import "./Footer.scss";
7 |
8 | const Footer = () => {
9 | const { clearVisible, getPopular, refreshPage, pageRefreshed } = useContext(
10 | MovieContext
11 | );
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | {
24 | clearVisible();
25 | getPopular();
26 | refreshPage();
27 | }}
28 | />
29 |
30 |
31 |
32 |
41 | {pageRefreshed ? (
42 |
43 |
44 | "Hope is a good thing, maybe the best of the good things and
45 | no good thing ever dies"
46 |
47 |
48 | ) : (
49 |
50 | "We all need memories to remind ourselves who we are"
51 |
52 | )}
53 |
54 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | export default React.memo(Footer);
71 |
--------------------------------------------------------------------------------
/src/Components/Footer/Footer.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .Footer {
3 | position: absolute;
4 | bottom: 0;
5 | width: 100%;
6 | background: $blue;
7 | // height: 22em;
8 | // background: url("../../Assets/Footer2.png");
9 | // background-repeat: no-repeat;
10 | // background-size: cover;
11 | .container {
12 | .row {
13 | .col {
14 | .footerBrand {
15 | width: 13em;
16 | margin-left: -4em;
17 | }
18 | h1 {
19 | color: $white;
20 | font-family: $secondary-font;
21 | }
22 | }
23 | .col {
24 | .morganFreeman {
25 | width: 29em;
26 | margin-top: 2em;
27 | float: right;
28 | }
29 | .guyPearce {
30 | width: 25em;
31 | margin-top: 2em;
32 | float: right;
33 | }
34 | .footerText {
35 | font-family: $secondary-font;
36 | color: $white;
37 | font-size: 2em;
38 | margin-top: 3em;
39 | // margin-bottom: 2em;
40 | // margin-left: 4.5em;
41 | animation: fadeIn 2s linear;
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
48 | @media (min-width: 300px) and (max-width:750px) {
49 | .Footer {
50 | .container {
51 | padding: 5em;
52 | .row {
53 | .col {
54 | .footerBrand {
55 | width: 10em;
56 | }
57 | }
58 | .col {
59 | .morganFreeman {
60 | width: 15em;
61 | margin-top: 2em;
62 | float: right;
63 | }
64 | .guyPearce {
65 | width: 18em;
66 | margin-top: 2em;
67 | float: right;
68 | }
69 | .footerText {
70 | font-family: $secondary-font;
71 | color: $white;
72 | font-size: 2em;
73 | margin-top: 3em;
74 | // margin-bottom: 2em;
75 | // margin-left: 4.5em;
76 | animation: fadeIn 2s linear;
77 | }
78 | }
79 | }
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/src/Components/Hero/Hero.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 |
3 | import { MovieContext} from "../../Context";
4 | import Swiper from "react-id-swiper";
5 | import { Link } from "react-router-dom";
6 | import LazyLoad from "react-lazyload";
7 |
8 | import "./Hero.scss";
9 | import "swiper/swiper.scss";
10 |
11 | const params = {
12 | spaceBetween: 30,
13 | centeredSlides: true,
14 | rebuildOnUpdate: true,
15 | autoplay: {
16 | delay: 2500,
17 | disableOnInteraction: false
18 | },
19 | pagination: {
20 | el: ".swiper-pagination",
21 | clickable: true
22 | }
23 | };
24 |
25 | const Hero = () => {
26 | const { trending, handleClick, refreshPage } = useContext(MovieContext);
27 | return (
28 |
29 |
30 | {trending.map(movie => {
31 | return (
32 | {
36 | handleClick(movie.id);
37 | refreshPage();
38 | }}
39 | >
40 |
41 |
46 |
47 |
48 | {movie.title}
49 |
50 | );
51 | })}
52 |
53 |
54 | );
55 | };
56 |
57 | export default React.memo(Hero);
58 |
--------------------------------------------------------------------------------
/src/Components/Hero/Hero.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .Hero {
3 | position: relative;
4 | .swiper-container {
5 | z-index: 0;
6 | .swiper-wrapper {
7 | .swiper-slide {
8 | cursor: pointer;
9 | // position: absolute;
10 | img {
11 | height: 80vh;
12 | width: 100%;
13 | }
14 | .carousel-caption {
15 | z-index: 3;
16 | text-align: left;
17 | margin-bottom: 1.5em;
18 | // margin-left: -9%;
19 | position: absolute;
20 | // top: 85%;
21 | left: 16.4%;
22 | // transform: translate(-45%, -20%);
23 | // width: 20em;
24 | // height: 4em;
25 | // background: $blue;
26 | font-family: $primary-font;
27 | // font-size: 5em;
28 | text-shadow: 3px 3px $blue;
29 | font-weight: bold;
30 | letter-spacing: 0.1em;
31 | text-transform: uppercase;
32 | color: $white;
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
39 | @media (min-width: 300px) and (max-width:750px) {
40 | .Hero {
41 | .swiper-container {
42 | .swiper-wrapper {
43 | .swiper-slide {
44 | img {
45 | height: 30vh;
46 | width: 100%;
47 | }
48 | .carousel-caption {
49 | position: absolute;
50 | left: 8%;
51 | font-size: 2em;
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
59 | @media (min-width: 750px) and (max-width:1100px) {
60 | .Hero {
61 | position: relative;
62 | .swiper-container {
63 | .swiper-wrapper {
64 | .swiper-slide {
65 | img {
66 | height: 50vh;
67 | width: 100%;
68 | }
69 | .carousel-caption {
70 | position: absolute;
71 | left: 6%;
72 | font-size: 3em;
73 | }
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/src/Components/Home/Home.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Hero from "../hero/Hero";
4 | import MidMenu from "../midMenu/MidMenu";
5 | import MoviesPopular from "../moviesPopular/MoviesPopular";
6 | import MoviesNow from "../moviesNow/MoviesNow";
7 | import MoviesComing from "../moviesComing/MoviesComing";
8 | import MoviesTop from "../moviesTop/MoviesTop";
9 |
10 | import "./Home.scss";
11 |
12 | const Home = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export default Home;
26 |
--------------------------------------------------------------------------------
/src/Components/Home/Home.scss:
--------------------------------------------------------------------------------
1 | .Home {
2 | padding-bottom: 30em;
3 | }
--------------------------------------------------------------------------------
/src/Components/LoadMore/LoadMore.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { MovieContext } from "../../Context";
3 |
4 | const LoadMore = () => {
5 | const { visible, coming, loadMore } = useContext(MovieContext);
6 | return (
7 |
8 | {/* if value visible is smaller than popular.length then add button */}
9 | {visible < coming.length && (
10 |
11 | Load more
12 |
13 | )}
14 |
15 | );
16 | };
17 |
18 | export default LoadMore;
19 |
--------------------------------------------------------------------------------
/src/Components/MidMenu/MidMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Nav, Container } from "react-bootstrap";
3 | import { Link } from "react-router-dom";
4 | import "./MidMenu.scss";
5 | import { MovieContext } from "../../Context";
6 |
7 | const MidMenu = () => {
8 | const {
9 | popular,
10 | getPopular,
11 | clearVisible,
12 | now,
13 | getNow,
14 | coming,
15 | getComing,
16 | top,
17 | getTop
18 | } = useContext(MovieContext);
19 |
20 | return (
21 |
22 |
23 |
24 | 0 ? "mr-5 nav-link clicked" : "mr-5 nav-link"
28 | }
29 | onClick={() => {
30 | getPopular();
31 | clearVisible();
32 | // popularSelected();
33 | }}
34 | >
35 | POPULAR
36 |
37 |
38 |
39 | 0
43 | ? "ml-5 mr-5 nav-link clicked"
44 | : "ml-5 mr-5 nav-link"
45 | }
46 | onClick={() => {
47 | getNow();
48 | clearVisible();
49 | // nowSelected();
50 | }}
51 | >
52 | NOW PLAYING
53 |
54 |
55 |
56 | 0
60 | ? "ml-5 mr-5 nav-link clicked"
61 | : "ml-5 mr-5 nav-link"
62 | }
63 | onClick={() => {
64 | getComing();
65 | clearVisible();
66 | // comingSelected();
67 | }}
68 | >
69 | COMING SOON
70 |
71 |
72 |
73 | 0 ? "ml-5 nav-link clicked" : "ml-5 nav-link"
77 | }
78 | onClick={() => {
79 | getTop();
80 | clearVisible();
81 | // topSelected();
82 | }}
83 | >
84 | TOP RATED
85 |
86 |
87 |
88 |
89 | );
90 | };
91 |
92 | export default MidMenu;
93 |
--------------------------------------------------------------------------------
/src/Components/MidMenu/MidMenu.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .MidMenu {
3 | height: 5em;
4 | background: $white;
5 | color: $blue;
6 | margin-top: -5em;
7 | z-index: 1000;
8 | position: absolute;
9 | left: 50%;
10 | transform: translate(-50%);
11 | .nav {
12 | &-item {
13 | a {
14 | // padding: 0 2em;
15 | color: $blue;
16 | text-decoration: none;
17 | font-size: 2em;
18 | font-family: $primary-font;
19 | margin-top: 0.7em;
20 | transition: all 0.5s ease-in-out;
21 | &:hover {
22 | color: $yellow;
23 | font-weight: bold;
24 | text-shadow: 1px 1px $blue;
25 | }
26 | }
27 | }
28 | }
29 | .clicked {
30 | color: $yellow !important;
31 | font-weight: bold;
32 | text-shadow: 1px 1px $blue;
33 | }
34 | }
35 |
36 | @media (min-width: 300px) and (max-width:750px) {
37 | .MidMenu {
38 | padding: 0;
39 | // margin: 0;
40 | .nav {
41 | &-item {
42 | a {
43 | font-size: 1.2em;
44 | padding: 0;
45 | margin: 1em 0 !important;
46 | width: 7em;
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
53 | @media (min-width: 751px) and (max-width:999.98px) {
54 | .MidMenu {
55 | padding: 0;
56 | .nav {
57 | &-item {
58 | a {
59 | font-size: 2em;
60 | padding: 0;
61 | // width: 7em;
62 | }
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/Components/Modal/Modal.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { MovieContext } from "../../Context";
3 | import { Container, Row, Col } from "react-bootstrap";
4 |
5 | import { Link } from "react-router-dom";
6 | import "./Modal.scss";
7 |
8 | const Modal = () => {
9 | const {
10 | modalOpen,
11 | closeModal,
12 | moviesResult,
13 | handleClick,
14 | clearSearch
15 | } = useContext(MovieContext);
16 |
17 | //if modal open is false, dont return anything
18 | if (!modalOpen) {
19 | return null;
20 | } else {
21 | //if there is a modal, then return this
22 | return (
23 |
24 |
25 | {/* */}
26 | {moviesResult.map(movie => {
27 | const { id, poster_path, title, vote_average } = movie;
28 | return (
29 | {
34 | handleClick(id);
35 | clearSearch();
36 | }}
37 | >
38 |
39 |
40 |
45 |
46 |
47 | {title} {" "}
48 |
49 | {/* {release_date
50 | ? `${release_date
51 | .split("-")
52 | .reverse()
53 | .join("-")}`
54 | : "Release Date Unknown"} */}
55 |
56 |
57 | {vote_average} {" "}
58 |
59 |
60 |
61 | );
62 | })}
63 | {/* */}
64 |
65 |
66 | );
67 | }
68 | };
69 |
70 | export default Modal;
71 |
--------------------------------------------------------------------------------
/src/Components/Modal/Modal.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .Modal {
3 | position: absolute;
4 | top: 0em;
5 | left: 0;
6 | right: 0;
7 | bottom: 0;
8 | // background: rgba(0, 0, 0, 0.3);
9 | z-index: 3000;
10 | margin-left: -2em;
11 | .container {
12 | max-height: 50vh;
13 | margin-top: 8em;
14 | padding: 0;
15 | background: $white;
16 | overflow-y: scroll;
17 | overflow-x: hidden;
18 | .swiper-container {
19 | max-height: 50vh;
20 | }
21 | .row {
22 | padding-top: 1em;
23 | padding-bottom: 1em;
24 | &:hover,
25 | &:hover .modalTitle {
26 | background: $blue;
27 | color: $white !important;
28 | }
29 | padding-bottom: 1em;
30 | .col {
31 | img {
32 | width: 8em;
33 | border: 1px solid $yellow;
34 | }
35 | .modalTitle {
36 | color: $blue;
37 | position: absolute;
38 | // top: 50%;
39 | // left: 10%;
40 | // transform: translate(-50%, -50%);
41 | margin-top: 1.5em;
42 | text-decoration: none;
43 | font-size: 2.5em;
44 | font-weight: bold;
45 | font-family: $primary-font;
46 | &:hover {
47 | text-decoration: none;
48 | }
49 | }
50 | .average {
51 | font-family: $primary-font;
52 | margin-top: 2em;
53 | font-size: 1.5em;
54 | color: $white;
55 | background: $yellow;
56 | width: 3em;
57 | height: 3em;
58 | border-radius: 50%;
59 | line-height: 3em;
60 | text-shadow: 2px 2px $blue;
61 | font-weight: bold;
62 | &:hover {
63 | text-decoration: none !important;
64 | }
65 | }
66 | // .release {
67 | // color: $blue;
68 | // text-decoration: none;
69 | // font-size: 1.5em;
70 | // font-weight: bold;
71 | // font-family: $primary-font;
72 | // }
73 | }
74 | }
75 | }
76 | }
77 |
78 | @media (min-width: 300px) and (max-width:779px) {
79 | .Modal {
80 | width: 80%;
81 | position: absolute;
82 | top: 11em;
83 | left: 10%;
84 | right: 0;
85 | bottom: 0;
86 | // background: rgba(0, 0, 0, 0.3);
87 | z-index: 3000;
88 | margin-left: -2em;
89 | .container {
90 | max-height: 50vh;
91 | margin-top: 8em;
92 | padding: 0;
93 | background: $white;
94 | overflow-y: scroll;
95 | overflow-x: hidden;
96 | .swiper-container {
97 | max-height: 50vh;
98 | }
99 | .row {
100 | .col {
101 | padding-right: 0;
102 | img {
103 | width: 7em;
104 | border: 1px solid $yellow;
105 | }
106 | .modalTitle {
107 | color: $blue;
108 | position: absolute;
109 | text-decoration: none;
110 | font-size: 2em;
111 | font-weight: bold;
112 | font-family: $primary-font;
113 | &:hover {
114 | text-decoration: none;
115 | }
116 | }
117 | }
118 | }
119 | }
120 | }
121 | }
122 |
123 | @media (min-width: 780px) and (max-width:991.99px) {
124 | .Modal {
125 | width: 80%;
126 | position: absolute;
127 | top: 5em;
128 | left: 10%;
129 | right: 0;
130 | bottom: 0;
131 | // background: rgba(0, 0, 0, 0.3);
132 | z-index: 3000;
133 | margin-left: -2em;
134 | .container {
135 | max-height: 50vh;
136 | margin-top: 8em;
137 | padding: 0;
138 | background: $white;
139 | overflow-y: scroll;
140 | overflow-x: hidden;
141 | .swiper-container {
142 | max-height: 50vh;
143 | }
144 | .row {
145 | .col {
146 | padding-right: 0;
147 | img {
148 | width: 7em;
149 | border: 1px solid $yellow;
150 | }
151 | .modalTitle {
152 | color: $blue;
153 | position: absolute;
154 | text-decoration: none;
155 | font-size: 2em;
156 | font-weight: bold;
157 | font-family: $primary-font;
158 | &:hover {
159 | text-decoration: none;
160 | }
161 | }
162 | }
163 | }
164 | }
165 | }
166 | }
167 |
168 | ::-webkit-scrollbar {
169 | width: 1.5em;
170 | }
171 |
172 | ::-webkit-scrollbar-thumb {
173 | background: $red;
174 | border-radius: 0.5em;
175 | }
--------------------------------------------------------------------------------
/src/Components/MovieDetails/MovieDetails.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Container, Col, Row, Card } from "react-bootstrap";
3 | import { MovieContext } from "../../Context";
4 | import Swiper from "react-id-swiper";
5 | import { Link } from "react-router-dom";
6 | import LazyLoad from "react-lazyload";
7 |
8 | import "swiper/swiper.scss";
9 | import "./MovieDetails.scss";
10 |
11 | const params = {
12 | slidesPerView: 3,
13 | spaceBetween: 30,
14 | // centeredSlides: true,
15 | pagination: {
16 | el: ".swiper-pagination",
17 | clickable: true
18 | }
19 | };
20 |
21 | const MovieDetails = () => {
22 | const {
23 | details,
24 | currentUser,
25 | favorite,
26 | addFavorite,
27 | genres,
28 | companies,
29 | countries,
30 | cast,
31 | similar,
32 | handleClick,
33 | refreshPage,
34 | videos
35 | } = useContext(MovieContext);
36 |
37 | const {
38 | poster_path,
39 | overview,
40 | title,
41 | vote_average,
42 | vote_count,
43 | release_date,
44 | revenue,
45 | runtime,
46 | backdrop_path,
47 | budget,
48 | tagline
49 | } = details;
50 |
51 | return (
52 |
53 |
58 |
59 |
60 |
61 |
62 |
63 |
68 |
69 |
70 |
addFavorite(poster_path)}
73 | >
74 | {currentUser ? (
75 | favorite.includes(poster_path) ? (
76 |
81 | ) : (
82 |
83 | )
84 | ) : (
85 |
86 | {" "}
87 | {" "}
88 |
89 | )}
90 |
91 |
92 |
93 |
94 |
95 |
96 | {/* ({release_date}) */}
97 | {title}
98 |
99 |
100 | {overview}
101 |
102 | Average Note:
103 |
104 |
105 | Vote Count:
106 |
107 |
108 |
109 |
110 | {vote_average}
111 |
112 |
113 | {vote_count}
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | Genre:
123 | {genres.map(genre => genre.name).join(", ")}
124 |
125 |
126 | {/* using array and string methods like this, inside a ternary operator and string interpolator */}
127 | Release Date:
128 | {release_date
129 | ? `${release_date
130 | .split("-")
131 | .reverse()
132 | .join("-")}`
133 | : "Release Date Unknown"}
134 |
135 |
136 | Budget:
137 | {budget > 0 ? `${budget.toLocaleString()}$` : "Budget Unknown"}
138 |
139 |
140 | Revenue:
141 | {revenue > 0 ? `${revenue.toLocaleString()}$` : "Not Estimated"}
142 |
143 |
144 |
145 |
146 | Production Companies:
147 | {/* couldnt use array methods here so i made them as states and then used array methods */}
148 | {companies.map(company => company.name).join(", ")}
149 |
150 |
151 | Production Countries:
152 | {countries.map(country => country.name).join(", ")}
153 |
154 |
155 | Tagline:
156 | {tagline ? `${tagline}` : `No Tagline Found`}
157 |
158 |
159 | Runtime: {runtime}
160 | minutes{" "}
161 |
162 |
163 |
164 |
165 | Cast
166 |
167 | {cast.slice(0, 12).map(i => {
168 | return (
169 |
170 | {i.profile_path ? (
171 |
172 |
176 |
177 | ) : (
178 |
179 |
183 |
184 | )}
185 |
186 |
187 | {i.name}
188 | as {i.character}
189 |
190 |
191 | );
192 | })}
193 |
194 |
195 |
196 |
197 |
198 | Similar Movies
199 |
200 |
201 |
202 |
203 | {similar.slice(0, 10).map(movie => {
204 | return (
205 | {
211 | handleClick(movie.id);
212 | refreshPage();
213 | }}
214 | >
215 |
216 |
220 |
221 |
222 | );
223 | })}
224 |
225 |
226 |
227 | {videos.slice(0, 1).map(video => {
228 | return (
229 |
230 |
231 | Trailer
232 |
233 |
242 | VIDEO
254 |
255 |
256 | );
257 | })}
258 |
259 |
260 | );
261 | };
262 |
263 | export default MovieDetails;
264 |
--------------------------------------------------------------------------------
/src/Components/MovieDetails/MovieDetails.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .Details {
3 | padding-bottom: 30em;
4 | .firstImg {
5 | height: 80vh;
6 | width: 100%;
7 | }
8 | .row {
9 | .col {
10 | position: relative;
11 | img {
12 | width: 30em;
13 | margin-bottom: 2em;
14 | }
15 | .hearts {
16 | position: absolute;
17 | top: 78%;
18 | left: 60%;
19 | background: $yellow;
20 | color: $white;
21 | font-size: 3em;
22 | width: 2em;
23 | height: 2em;
24 | border-radius: 50%;
25 | text-align: center;
26 | line-height: 2em;
27 | transition: all 0.3s linear;
28 | &:hover {
29 | transform: scale(1.2);
30 | }
31 | }
32 | }
33 | .col {
34 | .row {
35 | h1 {
36 | letter-spacing: 0.2em;
37 | font-family: $primary-font;
38 | font-size: 5em;
39 | color: $yellow;
40 | text-transform: uppercase;
41 | text-shadow: 2px 2px $blue;
42 | font-weight: bold;
43 | margin: 0 auto;
44 | span {
45 | font-size: 0.5em;
46 | }
47 | }
48 | }
49 | .row {
50 | p {
51 | font-size: 2em;
52 | font-family: $third-font;
53 | text-align: left;
54 | color: $blue;
55 | }
56 | .col {
57 | font-family: $secondary-font;
58 | h2 {
59 | font-weight: bold;
60 | color: $blue;
61 | }
62 | }
63 | }
64 | .row {
65 | .col {
66 | h1 {
67 | margin: 0 auto;
68 | font-size: 1.5em;
69 | color: $white;
70 | background: $yellow;
71 | width: 3em;
72 | height: 3em;
73 | border-radius: 50%;
74 | line-height: 3em;
75 | }
76 | }
77 | .col {
78 | h3 {
79 | font-family: $primary-font;
80 | margin: 0 auto;
81 | font-size: 1.5em;
82 | color: $white;
83 | background: $yellow;
84 | width: 3em;
85 | height: 3em;
86 | border-radius: 50%;
87 | line-height: 3em;
88 | text-shadow: 2px 2px $blue;
89 | font-weight: bold;
90 | }
91 | }
92 | }
93 | }
94 | }
95 | .row {
96 | font-family: $secondary-font;
97 | color: $blue;
98 | .col {
99 | p {
100 | font-size: 2em;
101 | }
102 | }
103 | .col {}
104 | }
105 | .row {
106 | font-family: $secondary-font;
107 | .castTitle {
108 | color: $yellow;
109 | font-weight: bold;
110 | display: block;
111 | width: 100%;
112 | text-align: left;
113 | // text-shadow: 1px 1px $blue;
114 | }
115 | .card {
116 | border: 1px solid $yellow;
117 | // width: 15em;
118 | &-img-top {
119 | // // display: inline-block;
120 | // width: 10em;
121 | // height: 10em;
122 | // border-radius: 10em;
123 | filter: grayscale(100%);
124 | }
125 | &-body {
126 | background: $white;
127 | padding: 0;
128 | .card-title {
129 | margin-top: 0.5em;
130 | font-family: $secondary-font;
131 | font-size: 2em;
132 | color: $red;
133 | text-shadow: 1px 1px $blue;
134 | }
135 | .card-text {
136 | font-family: $third-font;
137 | font-size: 1.5em;
138 | color: $blue;
139 | margin-bottom: 0.5em;
140 | }
141 | }
142 | transition: all 0.3s linear;
143 | // border: 1px solid $yellow;
144 | // border-radius: 1em;
145 | &:hover {
146 | box-shadow: 0 0.2px 0.9px rgba(0, 0, 0, 0.055), 0 0.6px 2.2px rgba(0, 0, 0, 0.112), 0 1.1px 4.1px rgba(0, 0, 0, 0.173), 0 2px 7.4px rgba(0, 0, 0, 0.245), 0 3.8px 13.8px rgba(0, 0, 0, 0.35), 0 9px 33px rgba(0, 0, 0, 0.54);
147 | }
148 | }
149 | }
150 | .container {
151 | margin-top: 5em;
152 | .row {
153 | .similarTitle {
154 | color: $yellow;
155 | font-weight: bold;
156 | display: block;
157 | width: 100%;
158 | text-align: left;
159 | // text-shadow: 1px 1px $blue;
160 | }
161 | }
162 | }
163 | .similar {
164 | background: $red;
165 | padding-top: 5em;
166 | padding-bottom: 5em;
167 | .card {
168 | display: inline-block;
169 | // border: 1px solid $white;
170 | max-width: 15em;
171 | border: none;
172 | &-img-top {
173 | // // display: inline-block;
174 | // width: 10em;
175 | // height: 10em;
176 | // border-radius: 10em;
177 | // filter: grayscale(100%);
178 | // width: 15em;
179 | }
180 | &-body {
181 | // background: $white;
182 | padding: 0;
183 | .card-title {
184 | margin-top: 0.5em;
185 | font-family: $secondary-font;
186 | font-size: 2em;
187 | color: $red;
188 | text-shadow: 1px 1px $blue;
189 | }
190 | .card-text {
191 | font-family: $third-font;
192 | font-size: 1.5em;
193 | color: $blue;
194 | margin-bottom: 0.5em;
195 | }
196 | }
197 | transition: all 0.3s linear;
198 | // border: 1px solid $yellow;
199 | // border-radius: 1em;
200 | &:hover {
201 | // box-shadow: 0 0.2px 0.9px rgba(0, 0, 0, 0.055), 0 0.6px 2.2px rgba(0, 0, 0, 0.112), 0 1.1px 4.1px rgba(0, 0, 0, 0.173), 0 2px 7.4px rgba(0, 0, 0, 0.245), 0 3.8px 13.8px rgba(0, 0, 0, 0.35), 0 9px 33px rgba(0, 0, 0, 0.54);
202 | // filter: hue-rotate(50deg);
203 | // transform: scale(1.5);
204 | }
205 | }
206 | }
207 | .videos {
208 | margin-top: 5em;
209 | .container {
210 | .row {
211 | .trailerTitle {
212 | font-family: $secondary-font;
213 | color: $yellow;
214 | font-weight: bold;
215 | display: block;
216 | width: 100%;
217 | text-align: left;
218 | margin-left: 0.3em;
219 | }
220 | }
221 | .video {
222 | margin-left: -0.8em;
223 | }
224 | }
225 | }
226 | .leftTitle {
227 | font-weight: bold;
228 | }
229 | }
230 |
231 | @media (min-width: 300px) and (max-width: 524px) {
232 | .Details {
233 | .firstImg {
234 | height: 30vh;
235 | width: 100%;
236 | }
237 | .row {
238 | .col {
239 | position: relative;
240 | img {
241 | width: 20em;
242 | margin-bottom: 2em;
243 | }
244 | .hearts {
245 | position: absolute;
246 | top: 70%;
247 | left: 57%;
248 | background: $yellow;
249 | color: $white;
250 | font-size: 3em;
251 | width: 2em;
252 | height: 2em;
253 | border-radius: 50%;
254 | text-align: center;
255 | line-height: 2em;
256 | transition: all 0.3s linear;
257 | &:hover {
258 | transform: scale(1.2);
259 | }
260 | }
261 | }
262 | .col {
263 | .row {
264 | p {
265 | padding: 1em;
266 | font-size: 2em;
267 | font-family: $third-font;
268 | text-align: left;
269 | color: $blue;
270 | }
271 | }
272 | }
273 | }
274 | .row {
275 | padding: 1.5em;
276 | .col {
277 | p {
278 | font-size: 1.5em;
279 | }
280 | }
281 | }
282 | .row {
283 | .card {
284 | width: 10em !important;
285 | }
286 | }
287 | }
288 | }
289 |
290 | @media (min-width: 768px) and (max-width: 992px) {
291 | .Details {
292 | .firstImg {
293 | height: 30vh;
294 | width: 100%;
295 | }
296 | .row {
297 | .col {
298 | position: relative;
299 | .hearts {
300 | position: absolute;
301 | top: 50%;
302 | left: 65%;
303 | }
304 | }
305 | }
306 | .row {
307 | padding: 1.5em;
308 | .col {
309 | p {
310 | font-size: 1.5em;
311 | }
312 | }
313 | }
314 | .row {
315 | .card {
316 | width: 10em !important;
317 | }
318 | }
319 | }
320 | }
321 |
322 | @media (min-width: 993px) and (max-width: 1100px) {
323 | .Details {
324 | .firstImg {
325 | height: 50vh;
326 | width: 100%;
327 | }
328 | }
329 | }
--------------------------------------------------------------------------------
/src/Components/MoviesComing/MoviesComing.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Container, Row, Col } from "react-bootstrap";
3 | import { MovieContext } from "../../Context";
4 | import LoadMore from "../loadMore/LoadMore";
5 |
6 | import DefaultMovieComponent from "../defaultMovieComponent/DefaultMovieComponent";
7 |
8 | const MoviesComing = () => {
9 | const { coming } = useContext(MovieContext);
10 |
11 | return (
12 |
13 |
14 |
15 | <>
16 | <>
17 |
18 | >
19 |
20 | >
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default React.memo(MoviesComing);
28 |
--------------------------------------------------------------------------------
/src/Components/MoviesNow/MoviesNow.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Container, Row, Col } from "react-bootstrap";
3 | import { MovieContext } from "../../Context";
4 |
5 | import DefaultMovieComponent from "../defaultMovieComponent/DefaultMovieComponent";
6 |
7 | const MoviesNow = () => {
8 | const { now, visible, loadMore } = useContext(MovieContext);
9 | return (
10 |
11 |
12 |
13 | <>
14 | <>
15 |
16 | >
17 | <>
18 | {/* if value visible is smaller than popular.length then add button */}
19 | {visible < now.length && (
20 |
21 | Load more
22 |
23 | )}
24 | >
25 | >
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default React.memo(MoviesNow);
33 |
--------------------------------------------------------------------------------
/src/Components/MoviesPopular/MoviesPopular.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext }from "react";
2 | import { Container, Row, Col } from "react-bootstrap";
3 | import { MovieContext } from "../../Context";
4 |
5 | import DefaultMovieComponent from "../defaultMovieComponent/DefaultMovieComponent"
6 |
7 | const MoviesPopular = () => {
8 | const {
9 | popular,
10 | visible,
11 | loadMore
12 | } = useContext(MovieContext);
13 |
14 | return (
15 |
16 |
17 |
18 | <>
19 | <>
20 |
21 | >
22 | <>
23 | {/* if value visible is smaller than popular.length then add button */}
24 | {visible < popular.length && (
25 |
26 | Load more
27 |
28 | )}
29 | >
30 | >
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default React.memo(MoviesPopular);
38 |
--------------------------------------------------------------------------------
/src/Components/MoviesTop/MoviesTop.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Container, Row, Col } from "react-bootstrap";
3 | import { MovieContext } from "../../Context";
4 |
5 | import DefaultMovieComponent from "../defaultMovieComponent/DefaultMovieComponent"
6 |
7 | const MoviesTop = () => {
8 | const { top, visible, loadMore } = useContext(MovieContext);
9 |
10 | return (
11 |
12 |
13 |
14 | <>
15 | <>
16 |
17 | >
18 | <>
19 | {/* if value visible is smaller than popular.length then add button */}
20 | {visible < top.length && (
21 |
22 | Load more
23 |
24 | )}
25 | >
26 | >
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | export default React.memo(MoviesTop);
34 |
--------------------------------------------------------------------------------
/src/Components/Nav/Nav.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .NavTop {
3 | font-family: $secondary-font;
4 | text-transform: uppercase;
5 | background: $white;
6 | .navbar {
7 | margin: 0 auto;
8 | &-brand {
9 | img {
10 | width: 6em;
11 | }
12 | }
13 | span {
14 | font-family: $secondary-font;
15 | color: $red;
16 | margin-right: 0.5em;
17 | // -webkit-background-clip: text;
18 | // -webkit-text-fill-color: transparent; linear-gradient(146deg, rgba(1, 22, 39, 1) 22%, rgba(238, 14, 81, 1) 100%);
19 | }
20 | // .fas {
21 | // background: $white;
22 | // }
23 | &-toggler {
24 | // height: 2em;
25 | font-size: 4em;
26 | color: $red;
27 | border: none;
28 | transition: all 0.5s ease-in-out;
29 | &:focus,
30 | &:active {
31 | outline: none !important;
32 | }
33 | // &:hover {
34 | // background: rgb(224, 224, 224);
35 | // }
36 | }
37 | .container {
38 | // border: 1px solid $white;
39 | // height: 100%;
40 | }
41 | &-nav {
42 | margin: 0 auto !important;
43 | .nav-link {
44 | font-size: 1.5em;
45 | color: $red;
46 | font-weight: bold;
47 | transition: all 0.5s ease-in-out;
48 | &:hover,
49 | &:focus {
50 | color: $blue;
51 | }
52 | }
53 | .userSign {
54 | width: 8em;
55 | .signOut {
56 | color: $yellow;
57 | float: left;
58 | }
59 | .fa-user-astronaut {
60 | float: right;
61 | color: $yellow;
62 | font-size: 2em;
63 | margin-top: -0.2em;
64 | margin-left: 0.5em;
65 | transition: all 0.5s ease-in-out;
66 | &:hover {
67 | color: $blue;
68 | }
69 | }
70 | }
71 | }
72 | &-collapse {
73 | margin: 0 auto;
74 | }
75 | }
76 | }
77 |
78 | @media (min-width: 300px) and (max-width:991.98px) {
79 | .NavTop {
80 | .navbar {
81 | &-toggler {
82 | // height: 2em;
83 | font-size: 4em;
84 | }
85 | &-nav {
86 | .nav-link {
87 | position: absolute;
88 | top: 50%;
89 | padding-right: 1em !important;
90 | }
91 | .userSign {
92 | width: 8em;
93 | position: absolute;
94 | left: 230%;
95 | // margin-bottom: 10em;
96 | .fa-user-astronaut {
97 | float: right;
98 | color: $yellow;
99 | font-size: 1.5em;
100 | margin-top: 0.05em;
101 | }
102 | }
103 | }
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/src/Components/Nav/NavTop.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Navbar, Nav, Image, Container } from "react-bootstrap";
3 | import "./Nav.scss";
4 | import { Link } from "react-router-dom";
5 | import Search from "../search/Search";
6 | import { MovieContext } from "../../Context";
7 | import { auth } from "../firebase/firebase.utils";
8 |
9 | const NavTop = () => {
10 | const {
11 | clearVisible,
12 | getPopular,
13 | refreshPage,
14 | clearSearch,
15 | currentUser,
16 | } = useContext(MovieContext);
17 |
18 | return (
19 |
20 |
21 | {
25 | clearVisible();
26 | getPopular();
27 | refreshPage();
28 | clearSearch();
29 | }}
30 | >
31 |
35 |
36 |
37 |
38 | {/* MENU */}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {
50 | clearVisible();
51 | getPopular();
52 | refreshPage();
53 | }}
54 | >
55 | Home
56 |
57 |
58 | {/* if currentUser has a user, it will return true. If its null(initial state) it will return false(null is falsy value) */}
59 | {currentUser ? (
60 |
61 |
auth.signOut()} className="signOut">
62 | Sign Out
63 |
64 |
65 |
66 |
67 |
68 |
69 | ) : (
70 | Sign in
71 | )}
72 |
73 |
74 |
75 |
76 |
77 | );
78 | };
79 |
80 | export default NavTop;
81 |
--------------------------------------------------------------------------------
/src/Components/Search/Search.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { MovieContext } from "../../Context";
3 | import { Form, Button, FormControl } from "react-bootstrap";
4 |
5 | import "./Search.scss";
6 |
7 | const Search = () => {
8 | const { handleSubmit, handleChange, openModal } = useContext(MovieContext);
9 |
10 | return (
11 |
25 | );
26 | };
27 |
28 | export default Search;
29 |
--------------------------------------------------------------------------------
/src/Components/Search/Search.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .form {
3 | position: absolute;
4 | &-inline {
5 | font-size: 2em;
6 | width: 100%;
7 | .form-control {
8 | font-family: $secondary-font;
9 | font-size: 1em;
10 | width: calc(100% - 2em);
11 | margin: 0;
12 | background: none;
13 | color: $red;
14 | padding: 10px 10px 10px 10px;
15 | display: block;
16 | border: none;
17 | border-radius: 0;
18 | text-transform: capitalize;
19 | // border-bottom: 1px solid #28214d;
20 | &::placeholder {
21 | color: $red;
22 | font-family: $secondary-font;
23 | }
24 | &:focus {
25 | box-shadow: none;
26 | }
27 | input[type="text"] {
28 | letter-spacing: 0.3em;
29 | }
30 | }
31 | button {
32 | background: $red;
33 | border: none;
34 | font-size: 1em;
35 | color: $white;
36 | }
37 | }
38 | }
39 |
40 | @media (min-width: 300px) and (max-width:779px) {
41 | .form {
42 | position: absolute;
43 | &-inline {
44 | padding-top: 3em;
45 | margin-bottom: 1em;
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Components/SignIn/SignIn.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Container, Row } from "react-bootstrap";
3 | import SignInComponent from "./signInComponent/SignInComponent";
4 | import SignUpComponent from "./signUpComponent/SignUpComponent";
5 | import "./SignIn.scss";
6 |
7 | const SignIn = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default SignIn;
19 |
--------------------------------------------------------------------------------
/src/Components/SignIn/SignIn.scss:
--------------------------------------------------------------------------------
1 | .signin {
2 | padding-bottom: 30em;
3 | }
--------------------------------------------------------------------------------
/src/Components/SignIn/SignInComponent/SignInComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useHistory } from "react-router-dom";
3 | import { Col, Button, Form } from "react-bootstrap";
4 | import { auth, signInWithGoogle } from "../../firebase/firebase.utils";
5 |
6 | import "./SignInComponent.scss";
7 |
8 | const SignInComponent = () => {
9 | let history = useHistory();
10 | const [input, setInput] = useState({});
11 |
12 | const handleSubmit = async (event) => {
13 | event.preventDefault();
14 |
15 | try {
16 | //wait for the auth to response
17 | await auth.signInWithEmailAndPassword(input.email, input.password);
18 | //if theres a user like that, it will sign in and then set the states to empty
19 | setInput({})
20 | history.push("/");
21 | } catch (error) {
22 | alert(error);
23 | }
24 | };
25 |
26 | const handleChange = (e) =>
27 | setInput({
28 | ...input,
29 | [e.target.name]: e.target.value,
30 | });
31 |
32 | return (
33 |
34 | I already have an account
35 | Sign in with your email and password
36 |
38 | Email address
39 |
47 |
48 |
49 | Password
50 |
57 |
58 |
59 |
65 | SIGN IN
66 |
67 |
74 | SIGN IN WITH GOOGLE
75 |
76 |
77 |
78 | );
79 | };
80 |
81 | export default SignInComponent;
82 |
--------------------------------------------------------------------------------
/src/Components/SignIn/SignInComponent/SignInComponent.scss:
--------------------------------------------------------------------------------
1 | @import "../../../index.scss";
2 | .signInComponent {
3 | .signInTitle {
4 | font-family: $primary-font;
5 | color: $blue;
6 | }
7 | .subtitle {
8 | font-family: $secondary-font;
9 | color: $blue;
10 | }
11 | .form-control {
12 | background: none;
13 | background-color: $white;
14 | color: $blue;
15 | font-size: 1.8em;
16 | padding: 10px 10px 10px 5px;
17 | display: block;
18 | width: 100%;
19 | border: none;
20 | border-radius: 0;
21 | border-bottom: 1px solid $blue;
22 | &:focus {
23 | outline: none;
24 | }
25 | input[type='password'] {
26 | letter-spacing: 0.3em;
27 | }
28 | }
29 | .form-label {
30 | font-family: $secondary-font;
31 | display: flex;
32 | justify-content: flex-start;
33 | color: $red;
34 | }
35 | .loginButton {
36 | font-family: $secondary-font;
37 | border: none;
38 | background: $blue;
39 | color: $white;
40 | transition: all 0.5s ease-in-out;
41 | &:hover {
42 | background: $red;
43 | }
44 | }
45 | .googleButton {
46 | font-family: $secondary-font;
47 | transition: all 0.5s ease-in-out;
48 | }
49 | }
--------------------------------------------------------------------------------
/src/Components/SignIn/SignUpComponent/SignUpComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useHistory } from "react-router-dom";
3 | import { Col, Button, Form } from "react-bootstrap";
4 | import {
5 | auth,
6 | createUserProfileDocument,
7 | } from "../../firebase/firebase.utils.js";
8 |
9 | import "./SignUpComponent.scss";
10 |
11 | const SignUpComponent = () => {
12 | let history = useHistory();
13 | const [input, setInput] = useState({});
14 |
15 | const handleSubmit = async (e) => {
16 | e.preventDefault();
17 |
18 | const { displayName } = input;
19 |
20 | try {
21 | //user is what we create with email and password with creating user function, and giving it auth
22 | const { user } = await auth.createUserWithEmailAndPassword(
23 | input.email,
24 | input.password
25 | );
26 |
27 | //wait for function to create a user with firstName
28 | await createUserProfileDocument(user, { displayName });
29 |
30 | //once its done, set states to initial
31 | setInput({});
32 | history.push("/");
33 | } catch (error) {
34 | alert(error);
35 | }
36 | };
37 |
38 | const handleChange = (e) =>
39 | setInput({
40 | ...input,
41 | [e.target.name]: e.target.value,
42 | });
43 |
44 | return (
45 |
46 | I don't have an account
47 | Sign up with your email and password
48 |
50 | First Name
51 |
59 |
60 |
61 | Last Name
62 |
69 |
70 |
71 | Email address
72 |
79 |
80 |
81 | Password
82 |
89 |
90 |
91 |
97 | SIGN UP
98 |
99 |
100 |
101 |
102 | );
103 | };
104 |
105 | export default SignUpComponent;
106 |
--------------------------------------------------------------------------------
/src/Components/SignIn/SignUpComponent/SignUpComponent.scss:
--------------------------------------------------------------------------------
1 | @import "../../../index.scss";
2 | .signUpComponent {
3 | .signUpTitle {
4 | font-family: $primary-font;
5 | color: $blue;
6 | }
7 | .subtitle {
8 | font-family: $secondary-font;
9 | color: $blue;
10 | }
11 | .form-control {
12 | background: none;
13 | background-color: $white;
14 | color: $blue;
15 | font-size: 1.8em;
16 | padding: 10px 10px 10px 5px;
17 | display: block;
18 | width: 100%;
19 | border: none;
20 | border-radius: 0;
21 | border-bottom: 1px solid $blue;
22 | &:focus {
23 | outline: none;
24 | }
25 | input[type='password'] {
26 | letter-spacing: 0.3em;
27 | }
28 | }
29 | .form-label {
30 | font-family: $secondary-font;
31 | display: flex;
32 | justify-content: flex-start;
33 | color: $red;
34 | }
35 | .signUpButton {
36 | font-family: $secondary-font;
37 | border: none;
38 | background: $blue;
39 | color: $white;
40 | transition: all 0.5s ease-in-out;
41 | &:hover {
42 | background: $red;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Components/User/User.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { MovieContext } from "../../Context";
3 | import { Container, Row, Col } from "react-bootstrap";
4 |
5 | import "./User.scss";
6 |
7 | const User = () => {
8 | const { currentUser, favorite, addFavorite } = useContext(MovieContext);
9 |
10 | return (
11 |
12 | {currentUser ? (
13 |
14 |
15 | Welcome, {currentUser.displayName}!
16 | your favorite movies:
17 |
18 |
19 |
20 | {favorite.length > 0 ? (
21 | favorite.map(movie => {
22 | return (
23 |
27 |
33 |
34 |
addFavorite(movie)}
37 | >
38 | {favorite.includes(movie) ? (
39 |
44 | ) : (
45 |
46 | )}
47 |
48 |
49 | {/* {movie} */}
50 |
51 | );
52 | })
53 | ) : (
54 | no movies added to favorites
55 | )}
56 |
57 |
58 | ) : (
59 | You need to sign in to view data
60 | )}
61 |
62 | );
63 | };
64 |
65 | export default User;
66 |
--------------------------------------------------------------------------------
/src/Components/User/User.scss:
--------------------------------------------------------------------------------
1 | @import "../../index.scss";
2 | .User {
3 | padding-bottom: 30em;
4 | .firstRow {
5 | display: flex;
6 | flex-direction: column;
7 | text-align: left;
8 | .userTitle {
9 | float: left;
10 | letter-spacing: 0.2em;
11 | font-family: $primary-font;
12 | font-size: 5em;
13 | color: $yellow;
14 | text-transform: uppercase;
15 | text-shadow: 2px 2px $blue;
16 | font-weight: bold;
17 | // margin: 0 auto;
18 | }
19 | }
20 | .userText {
21 | color: $blue;
22 | text-decoration: none;
23 | font-size: 3em;
24 | text-transform: capitalize;
25 | font-family: $primary-font;
26 | // margin-top: 0.7em;
27 | }
28 | .hearts {
29 | position: absolute;
30 | top: 78%;
31 | left: 60%;
32 | background: $yellow;
33 | color: $white;
34 | font-size: 3em;
35 | width: 2em;
36 | height: 2em;
37 | border-radius: 50%;
38 | text-align: center;
39 | line-height: 2em;
40 | transition: all 0.3s linear;
41 | &:hover {
42 | transform: scale(1.2);
43 | }
44 | }
45 | }
46 |
47 | @media (min-width: 300px) and (max-width:750px) {
48 | .User {
49 | padding-bottom: 30em;
50 | padding-left: 5em;
51 | .firstRow {
52 | padding: 2em;
53 | display: flex;
54 | flex-direction: column;
55 | text-align: left;
56 | .userTitle {
57 | float: left;
58 | letter-spacing: 0.2em;
59 | font-family: $primary-font;
60 | font-size: 4em;
61 | color: $yellow;
62 | text-transform: uppercase;
63 | text-shadow: 2px 2px $blue;
64 | font-weight: bold;
65 | // margin: 0 auto;
66 | }
67 | }
68 | .userText {
69 | color: $blue;
70 | text-decoration: none;
71 | font-size: 3em;
72 | text-transform: capitalize;
73 | font-family: $primary-font;
74 | // margin-top: 0.7em;
75 | }
76 | .userImg {
77 | width: 10em;
78 | }
79 | .hearts {
80 | position: absolute;
81 | top: 70%;
82 | left: 45%;
83 | background: $yellow;
84 | color: $white;
85 | font-size: 2em;
86 | width: 2em;
87 | height: 2em;
88 | border-radius: 50%;
89 | text-align: center;
90 | line-height: 2em;
91 | transition: all 0.3s linear;
92 | &:hover {
93 | transform: scale(1.2);
94 | }
95 | }
96 | }
97 | }
98 |
99 | @media (min-width: 751px) and (max-width:999.8px) {
100 | .User {
101 | padding-bottom: 30em;
102 | padding-left: 5em;
103 | .firstRow {
104 | padding: 2em;
105 | display: flex;
106 | flex-direction: column;
107 | text-align: left;
108 | .userTitle {
109 | float: left;
110 | letter-spacing: 0.2em;
111 | font-family: $primary-font;
112 | font-size: 4em;
113 | color: $yellow;
114 | text-transform: uppercase;
115 | text-shadow: 2px 2px $blue;
116 | font-weight: bold;
117 | // margin: 0 auto;
118 | }
119 | }
120 | .userText {
121 | color: $blue;
122 | text-decoration: none;
123 | font-size: 3em;
124 | text-transform: capitalize;
125 | font-family: $primary-font;
126 | // margin-top: 0.7em;
127 | }
128 | .userImg {
129 | width: 15em;
130 | }
131 | .hearts {
132 | position: absolute;
133 | top: 75%;
134 | left: 50%;
135 | background: $yellow;
136 | color: $white;
137 | font-size: 2em;
138 | width: 2em;
139 | height: 2em;
140 | border-radius: 50%;
141 | text-align: center;
142 | line-height: 2em;
143 | transition: all 0.3s linear;
144 | &:hover {
145 | transform: scale(1.2);
146 | }
147 | }
148 | }
149 | }
150 |
151 | @media (min-width: 999.99px) and (max-width:2000px) {
152 | .User {
153 | padding-bottom: 30em;
154 | padding-left: 5em;
155 | .firstRow {
156 | padding: 2em;
157 | display: flex;
158 | flex-direction: column;
159 | text-align: left;
160 | .userTitle {
161 | float: left;
162 | letter-spacing: 0.2em;
163 | font-family: $primary-font;
164 | font-size: 5em;
165 | color: $yellow;
166 | text-transform: uppercase;
167 | text-shadow: 2px 2px $blue;
168 | font-weight: bold;
169 | // margin: 0 auto;
170 | }
171 | }
172 | .col {
173 | position: relative;
174 | .userText {
175 | color: $blue;
176 | text-decoration: none;
177 | font-size: 3em;
178 | text-transform: capitalize;
179 | font-family: $primary-font;
180 | // margin-top: 0.7em;
181 | }
182 | .userImg {
183 | width: 20em;
184 | }
185 | .hearts {
186 | position: absolute;
187 | top: 80%;
188 | left: 60%;
189 | background: $yellow;
190 | color: $white;
191 | font-size: 2em;
192 | width: 2em;
193 | height: 2em;
194 | border-radius: 50%;
195 | text-align: center;
196 | line-height: 2em;
197 | transition: all 0.3s linear;
198 | &:hover {
199 | transform: scale(1.2);
200 | }
201 | }
202 | }
203 | }
204 | }
--------------------------------------------------------------------------------
/src/Components/defaultMovieComponent/DefaultMovieComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { MovieContext } from "../../Context";
3 | import { Link } from "react-router-dom";
4 | import LazyLoad from "react-lazyload";
5 |
6 | const DefaultMovieComponent = ({ movieType }) => {
7 | const {
8 | visible,
9 | handleClick,
10 | refreshPage,
11 | addFavorite,
12 | favorite,
13 | currentUser,
14 | } = useContext(MovieContext);
15 | return (
16 | <>
17 | {movieType &&
18 | movieType.slice(0, visible).map((movie) => {
19 | return (
20 |
21 |
{
25 | handleClick(movie.id);
26 | refreshPage();
27 | }}
28 | >
29 |
30 |
35 |
36 |
37 |
38 |
39 |
{movie.vote_average}
40 |
41 |
42 |
addFavorite(movie.poster_path)}
45 | >
46 | {currentUser ? (
47 | favorite.includes(movie.poster_path) ? (
48 |
53 | ) : (
54 |
55 | )
56 | ) : (
57 |
58 |
59 |
60 | )}
61 |
62 |
63 |
64 | );
65 | })}
66 | >
67 | );
68 | };
69 |
70 | export default DefaultMovieComponent;
71 |
--------------------------------------------------------------------------------
/src/Context.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, createContext } from "react";
2 | import { TMDB_KEY } from "./config.js";
3 | import axios from "axios";
4 | import {
5 | auth,
6 | createUserProfileDocument
7 | } from "../src/components/firebase/firebase.utils.js";
8 | import { Persist } from "react-persist";
9 |
10 | export const MovieContext = createContext();
11 |
12 | class MovieContextProvider extends Component {
13 | constructor(props) {
14 | super(props);
15 | this.state = {
16 | //empty array because we are fetching an array from tmdb database
17 | trending: [],
18 | popular: [],
19 | now: [],
20 | coming: [],
21 | top: [],
22 | details: "",
23 | genres: [],
24 | cast: [],
25 | id: "",
26 | companies: [],
27 | countries: [],
28 | similar: [],
29 | videos: [],
30 | //there are two different arrays for searching movies
31 | //first is to fetch, second is to search and see results
32 | movies: [],
33 | moviesResult: [],
34 | //modal is closed at first, after state is true, component will show
35 | modalOpen: false,
36 | //number of movies that will be visible at first in homepage
37 | visible: 10,
38 | //state to understand if page is refreshed
39 | pageRefreshed: false,
40 | currentUser: null,
41 | //favorite movies array to store favorite movies
42 | favorite: []
43 | };
44 | }
45 |
46 | //we need this to sign out, currently user is not signed out
47 | unsubscribeFromAuth = null;
48 |
49 | componentDidMount = () => {
50 | this.getTrending();
51 | this.getPopular();
52 | this.cleanState();
53 | this.handleClick();
54 | this.searchMovie();
55 | this.clearSearch();
56 |
57 | // set the currentusers state as signed in user with google
58 | //userAuth comes from firebase
59 | this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
60 | //if userAuth exists(have any value besides null)
61 | if (userAuth) {
62 | //userRef is waiting for the function we created in firebase utils that created a snapshot, which takes userAuth as value
63 | const userRef = await createUserProfileDocument(userAuth);
64 |
65 | userRef.onSnapshot(snapShot => {
66 | this.setState({
67 | currentUser: {
68 | id: snapShot.id,
69 | ...snapShot.data()
70 | }
71 | });
72 | });
73 | } else {
74 | //if user logs out then state will be userAuth(if theres no userAuth then its null)
75 | this.setState({
76 | currentUser: userAuth
77 | });
78 | }
79 | });
80 | };
81 |
82 | //this is how user will sign out
83 | componentWillUnmount() {
84 | this.unsubscribeFromAuth();
85 | }
86 |
87 | //cleans the states for movie lists so they wouldnt stack up
88 | cleanState = () => {
89 | this.setState({
90 | popular: [],
91 | now: [],
92 | coming: [],
93 | top: []
94 | });
95 | };
96 |
97 | getTrending = () => {
98 | axios
99 | .get(
100 | `https://api.themoviedb.org/3/trending/movie/day?api_key=${TMDB_KEY}`
101 | )
102 | .then(response => {
103 | const apiResponse = response.data;
104 | this.setState({
105 | trending: apiResponse.results
106 | });
107 | // console.log(apiResponse.results);
108 | })
109 | .catch(error => {
110 | console.log(error);
111 | });
112 | };
113 |
114 | getPopular = () => {
115 | this.cleanState();
116 | axios
117 | .get(
118 | `https://api.themoviedb.org/3/movie/popular?api_key=${TMDB_KEY}&language=en-US&page=1`
119 | )
120 | .then(response => {
121 | const apiResponse = response.data;
122 | this.setState({
123 | popular: apiResponse.results
124 | });
125 | // console.log(apiResponse.results);
126 | })
127 | .catch(error => {
128 | console.log(error);
129 | });
130 | };
131 |
132 | getNow = () => {
133 | this.cleanState();
134 | axios
135 | .get(
136 | `
137 | https://api.themoviedb.org/3/movie/now_playing?api_key=${TMDB_KEY}&language=en-US&page=1`
138 | )
139 | .then(response => {
140 | const apiResponse = response.data;
141 | this.setState({
142 | now: apiResponse.results
143 | });
144 | // console.log(apiResponse.results);
145 | })
146 | .catch(error => {
147 | console.log(error);
148 | });
149 | // console.log("worked");
150 | };
151 |
152 | getComing = () => {
153 | this.cleanState();
154 | axios
155 | .get(
156 | `
157 | https://api.themoviedb.org/3/movie/upcoming?api_key=${TMDB_KEY}&language=en-US&page=1`
158 | )
159 | .then(response => {
160 | const apiResponse = response.data;
161 | this.setState({
162 | coming: apiResponse.results
163 | });
164 | // console.log(apiResponse.results);
165 | })
166 | .catch(error => {
167 | console.log(error);
168 | });
169 | };
170 |
171 | getTop = () => {
172 | this.cleanState();
173 | axios
174 | .get(
175 | `
176 | https://api.themoviedb.org/3/movie/top_rated?api_key=${TMDB_KEY}&language=en-US&page=1`
177 | )
178 | .then(response => {
179 | const apiResponse = response.data;
180 | this.setState({
181 | top: apiResponse.results
182 | });
183 | // console.log(apiResponse.results);
184 | })
185 | .catch(error => {
186 | console.log(error);
187 | });
188 | };
189 |
190 | getDetails = () => {
191 | axios
192 | .get(
193 | `https://api.themoviedb.org/3/movie/${this.state.id}?api_key=${TMDB_KEY}&language=en-US`
194 | )
195 | .then(response => {
196 | const apiResponse = response.data;
197 | // console.log(this.state.id);
198 | this.setState(
199 | {
200 | details: apiResponse,
201 | genres: apiResponse.genres,
202 | companies: apiResponse.production_companies,
203 | countries: apiResponse.production_countries
204 | },
205 | () => console.log(apiResponse)
206 | );
207 | })
208 | .catch(error => {
209 | console.log(error);
210 | });
211 | };
212 |
213 | getCast = () => {
214 | axios
215 | .get(
216 | `https://api.themoviedb.org/3/movie/${this.state.id}/credits?api_key=${TMDB_KEY}&language=en-US`
217 | )
218 | .then(response => {
219 | const apiResponse = response.data;
220 | this.setState({
221 | cast: apiResponse.cast
222 | });
223 | // console.log(apiResponse.cast);
224 | })
225 | .catch(error => {
226 | console.log(error);
227 | });
228 | };
229 |
230 | getSimilar = () => {
231 | axios
232 | .get(
233 | `https://api.themoviedb.org/3/movie/${this.state.id}/similar?api_key=${TMDB_KEY}&language=en-US&page=1`
234 | )
235 | .then(response => {
236 | const apiResponse = response.data;
237 | this.setState({
238 | similar: apiResponse.results
239 | });
240 | // console.log(apiResponse.results);
241 | })
242 | .catch(error => {
243 | console.log(error);
244 | });
245 | };
246 |
247 | getVideos = () => {
248 | axios
249 | .get(
250 | `https://api.themoviedb.org/3/movie/${this.state.id}/videos?api_key=${TMDB_KEY}&language=en-US&page=1`
251 | )
252 | .then(response => {
253 | const apiResponse = response.data;
254 | this.setState({
255 | videos: apiResponse.results
256 | });
257 | // console.log(apiResponse.results);
258 | })
259 | .catch(error => {
260 | console.log(error);
261 | });
262 | };
263 |
264 | //search from movies state, store to moviesResult array in state
265 | searchMovie = () => {
266 | axios
267 | .get(
268 | `https://api.themoviedb.org/3/search/movie?api_key=${TMDB_KEY}&query=${this.state.movies}&language=en-US&page=1&include_adult=false`
269 | )
270 | .then(response => {
271 | const apiResponse = response.data;
272 | this.setState({
273 | moviesResult: apiResponse.results
274 | });
275 | // console.log(apiResponse.results);
276 | })
277 | .catch(error => {
278 | console.log(error);
279 | });
280 | };
281 |
282 | //this will get the id of clicked element and set the id state with id it got from the element
283 | //https://stackoverflow.com/questions/44325272/getting-the-id-of-a-clicked-element-from-rendered-list
284 | handleClick = id => {
285 | this.setState(
286 | {
287 | id: id
288 | },
289 | // how to put two callback functions within setState
290 | // https://stackoverflow.com/questions/53788156/passing-multiple-functions-as-callback-in-setstate
291 | () => {
292 | this.getDetails();
293 | this.getCast();
294 | this.getSimilar();
295 | this.getVideos();
296 | }
297 | );
298 | };
299 | // gets the value of inputs
300 | handleChange = e => {
301 | this.setState(
302 | {
303 | movies: e.target.value
304 | },
305 | () => {
306 | this.searchMovie();
307 | }
308 | );
309 | };
310 |
311 | handleSubmit = e => {
312 | e.preventDefault();
313 | this.searchMovie();
314 | //.reset() to reset searchbar
315 | e.target.reset();
316 | };
317 |
318 | openModal = () => {
319 | this.setState({
320 | modalOpen: true
321 | });
322 | };
323 |
324 | closeModal = () => {
325 | this.setState({
326 | modalOpen: false
327 | });
328 | };
329 |
330 | clearSearch = () => {
331 | this.setState({
332 | movies: [],
333 | moviesResult: []
334 | });
335 | };
336 |
337 | //clears the state of visible to go back to initial state of 10 movies
338 | clearVisible = () => {
339 | this.setState({
340 | visible: 10,
341 | now: [],
342 | coming: [],
343 | top: []
344 | });
345 | };
346 |
347 | //https://codepen.io/grantdotlocal/pen/zReNgE
348 | loadMore = () => {
349 | this.setState(prev => {
350 | return { visible: prev.visible + 5 };
351 | });
352 | };
353 |
354 | //using !this.state.pageRefreshed so pageRefreshed would always be opposite of it, on every click
355 | refreshPage = () => {
356 | this.setState({
357 | pageRefreshed: !this.state.pageRefreshed
358 | });
359 | };
360 |
361 | addFavorite = movie => {
362 | const { favorite } = this.state;
363 | let copyFavorites = [...favorite];
364 | //if it doesnt include, add
365 | if (!favorite.includes(movie)) {
366 | copyFavorites.push(movie);
367 | this.setState({ favorite: copyFavorites });
368 | //if it includes, remove
369 | //https://stackoverflow.com/questions/5767325/how-do-i-remove-a-particular-element-from-an-array-in-javascript
370 | } else {
371 | copyFavorites = copyFavorites.filter(eachMovie => eachMovie !== movie);
372 | this.setState({ favorite: copyFavorites });
373 | }
374 | };
375 |
376 | render() {
377 | return (
378 |
402 | {this.props.children}
403 | this.setState(data)}
408 | />
409 |
410 | );
411 | }
412 | }
413 |
414 | export default MovieContextProvider;
415 |
--------------------------------------------------------------------------------
/src/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | //Add this to index.js so everytime a link is clinked, it will take you to top of the opened page.
5 | export default function ScrollToTop() {
6 | const { pathname } = useLocation();
7 |
8 | useEffect(() => {
9 | window.scrollTo(0, 0);
10 | }, [pathname]);
11 |
12 | return null;
13 | }
14 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | export const TMDB_KEY = `df5896d2675776ebb8278ac1635595af`
--------------------------------------------------------------------------------
/src/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/favicon.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.scss";
4 | import App from "./App";
5 | import ScrollToTop from "./ScrollToTop";
6 | import { BrowserRouter as Router } from "react-router-dom";
7 | import * as serviceWorker from "./serviceWorker";
8 |
9 | //scrollto top to scroll to top
10 | //https://stackoverflow.com/questions/36904185/react-router-scroll-to-top-on-every-transition
11 | //https://reacttraining.com/react-router/web/guides/scroll-restoration/scroll-to-top
12 | ReactDOM.render(
13 |
14 |
15 |
16 | ,
17 | document.getElementById("root")
18 | );
19 |
20 | // If you want your app to work offline and load faster, you can change
21 | // unregister() to register() below. Note this comes with some pitfalls.
22 | // Learn more about service workers: https://bit.ly/CRA-PWA
23 | serviceWorker.unregister();
24 |
--------------------------------------------------------------------------------
/src/index.scss:
--------------------------------------------------------------------------------
1 | /*** fonts ****/
2 |
3 | $primary-font: "PT Sans",
4 | sans-serif;
5 | $secondary-font: "Lato",
6 | sans-serif;
7 | $third-font: "Metrophobic",
8 | sans-serif;
9 |
10 | /*** colors ***/
11 |
12 | $blue: #011627;
13 | $red: #EE0E51;
14 | $turq: #41EAD4;
15 | $white: #FDFFFC;
16 | $yellow: #FF9F1C;
17 |
18 | /**** *****/
19 |
20 | html {
21 | font-size: 62.5%;
22 | }
23 |
24 | body {
25 | margin: 0;
26 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
27 | -webkit-font-smoothing: antialiased;
28 | -moz-osx-font-smoothing: grayscale;
29 | background: $white;
30 | }
31 |
32 | code {
33 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
34 | }
35 |
36 | .homePageMovies {
37 | .card {
38 | display: inline-block;
39 | width: 20em;
40 | margin: 1.1em;
41 | &-img {
42 | width: 100%;
43 | transition: all 0.3s linear;
44 | &:hover {
45 | box-shadow: 0 0.2px 0.9px rgba(0, 0, 0, 0.055), 0 0.6px 2.2px rgba(0, 0, 0, 0.112), 0 1.1px 4.1px rgba(0, 0, 0, 0.173), 0 2px 7.4px rgba(0, 0, 0, 0.245), 0 3.8px 13.8px rgba(0, 0, 0, 0.35), 0 9px 33px rgba(0, 0, 0, 0.54);
46 | filter: hue-rotate(50deg);
47 | }
48 | }
49 | .voteAverage {
50 | position: absolute;
51 | top: 85%;
52 | left: 10%;
53 | background: $yellow;
54 | width: 3em;
55 | height: 3em;
56 | border-radius: 50%;
57 | text-align: center;
58 | line-height: 3em;
59 | }
60 | .hearts {
61 | position: absolute;
62 | top: 85%;
63 | left: 77%;
64 | background: $yellow;
65 | width: 3em;
66 | height: 3em;
67 | border-radius: 50%;
68 | text-align: center;
69 | line-height: 3em;
70 | transition: all 0.3s linear;
71 | &:hover {
72 | transform: scale(1.5);
73 | }
74 | }
75 | }
76 | .load-more {
77 | display: block;
78 | border: 0;
79 | margin: 2em auto;
80 | padding: 1em 3.75em;
81 | font-size: 1.5em;
82 | background-color: $red;
83 | color: $white;
84 | font-weight: 500;
85 | text-transform: uppercase;
86 | font-family: $secondary-font;
87 | cursor: pointer;
88 | border-radius: 0.25em;
89 | transition: all 0.5s ease-in-out;
90 | &:focus {
91 | outline: 1px solid $white;
92 | }
93 | &:hover {
94 | background: $blue
95 | }
96 | }
97 | }
98 |
99 | .fade-in {
100 | animation: fadeIn 0.5s ease-in;
101 | }
102 |
103 | @keyframes fadeIn {
104 | from {
105 | opacity: 0;
106 | }
107 | to {
108 | opacity: 1;
109 | }
110 | }
111 |
112 | @media (min-width: 300px) and (max-width:991.98px) {
113 | .homePageMovies {
114 | margin-top: -1em !important;
115 | .card {
116 | width: 12em;
117 | .voteAverage {
118 | top: 80%;
119 | }
120 | .hearts {
121 | top: 80%;
122 | left: 67%;
123 | }
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/src/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/logo.png
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/logo_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/urlDev/react-scriptbase/0233faa4a912f44d8e12dccb7fe55093e785425e/src/logo_transparent.png
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' }
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready.then(registration => {
134 | registration.unregister();
135 | });
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------