├── .gitignore
├── postcss.config.js
├── tailwind.config.js
├── src
├── index.html
├── utils
│ ├── gptConfig.js
│ ├── getValidatedInputs.js
│ ├── constants.js
│ ├── Firebase.js
│ └── Router.js
├── reduxStore
│ ├── index.js
│ ├── GptSlice
│ │ └── index.js
│ ├── TrendingMovie
│ │ └── index.js
│ └── authSlice
│ │ └── index.js
├── components
│ ├── MovieCard.js
│ ├── GptMovieSuggestions.js
│ ├── MovieLists.js
│ ├── TrailerCard.js
│ ├── Navbar.js
│ ├── TrailerBackround.js
│ └── SearchBar.js
├── index.css
├── HOC
│ └── PrivateRoute.js
├── index.js
├── api
│ └── auth.api.js
└── views
│ ├── GptPage.js
│ ├── LandingPage.js
│ └── Authentication.js
├── webpack.config.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | yarn.lock
3 | .parcel-cache
4 | dist
5 | yarn-error.log
6 | .env
7 | .env.production
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('tailwindcss'),
4 | require('autoprefixer'),
5 | // Add any other PostCSS plugins if needed
6 | ],
7 | };
8 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/**/*.{html,js,ts,jsx,tsx}",
5 | ],
6 | theme: {
7 | extend: {},
8 | },
9 | plugins: [],
10 | }
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Netflix
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/utils/gptConfig.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | // example
4 | // async function main() {
5 | // const chatCompletion = await openai.chat.completions.create({
6 | // messages: [{ role: 'user', content: 'Say this is a test' }],
7 | // model: 'gpt-3.5-turbo',
8 | // });
9 | // }
10 |
11 | // main();
12 |
--------------------------------------------------------------------------------
/src/reduxStore/index.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import authenticationSlice from "./authSlice";
3 | import movieSlice from "./TrendingMovie";
4 | import GptSlice from "./GptSlice"
5 | export const reduxStore = configureStore({
6 | reducer: {
7 | // todo
8 | auth: authenticationSlice,
9 | trending: movieSlice,
10 | gpt : GptSlice
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/src/utils/getValidatedInputs.js:
--------------------------------------------------------------------------------
1 | export const getValidatedInputs = (email, password) => {
2 | const emailTest = /^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$/gm.test(email);
3 | const passwordTest =
4 | /^((?=\S*?[A-Z])(?=\S*?[a-z])(?=\S*?[0-9]).{6,})\S$/.test(password);
5 |
6 | if (!emailTest) return "Please enter valid email address";
7 | if (!passwordTest) return "Please enter valid password";
8 |
9 | return "";
10 | };
11 |
--------------------------------------------------------------------------------
/src/components/MovieCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { IMG_CDN_URL } from "../utils/constants";
3 |
4 | const MovieCard = ({ item }) => {
5 | return item?.poster_path ? (
6 |
7 |

8 |
9 | ) : (
10 | <>>
11 | );
12 | };
13 |
14 | export default MovieCard;
15 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | #root {
5 | height: 100vh;
6 | margin: 0px;
7 | padding: 0px;
8 | width: 100vw;
9 | }
10 | *::-webkit-scrollbar {
11 | width: 0;
12 | height: 0;
13 | background-color: transparent;
14 | }
15 |
16 | &::-moz-scrollbar {
17 | visibility: hidden;
18 | }
19 | /*.ytp-chrome-top,
20 | .ytp-show-cards-title {
21 | display: none !important;
22 | } */
23 |
24 |
--------------------------------------------------------------------------------
/src/HOC/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useSelector } from "react-redux";
3 | import { Navigate, useLocation } from "react-router-dom";
4 |
5 | const PrivateRoute = ({ children }) => {
6 | const { isAuthenticated } = useSelector((state) => state.auth);
7 | const loc = useLocation();
8 |
9 | if (isAuthenticated) return children;
10 | return ;
11 | };
12 |
13 | export default PrivateRoute;
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { RouterProvider } from "react-router-dom";
4 | import { AppRoute } from "./utils/Router";
5 | import { Provider } from "react-redux";
6 | import { reduxStore } from "./reduxStore";
7 | import "./index.css";
8 |
9 | const root = ReactDOM.createRoot(document.getElementById("root"));
10 | root.render(
11 |
12 |
13 |
14 | );
--------------------------------------------------------------------------------
/src/components/GptMovieSuggestions.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MovieCard from "./MovieCard";
3 |
4 | const GptMovieSuggestions = ({ suggestedMovie }) => {
5 | return (
6 |
7 |
11 | {suggestedMovie?.[0]?.original_title}
12 |
13 |
14 | {suggestedMovie?.map((item) => {
15 | return ;
16 | })}
17 |
18 |
19 | );
20 | };
21 |
22 | export default GptMovieSuggestions;
23 |
--------------------------------------------------------------------------------
/src/components/MovieLists.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MovieCard from "./MovieCard";
3 |
4 | const MovieLists = ({ title, movies, isgptlist }) => {
5 | return (
6 |
7 |
12 | {title}
13 |
14 |
21 | {movies?.map((item) => {
22 | return ;
23 | })}
24 |
25 |
26 | );
27 | };
28 |
29 | export default MovieLists;
30 |
--------------------------------------------------------------------------------
/src/reduxStore/GptSlice/index.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const GptSlice = createSlice({
4 | name: "Gpt Slice",
5 | initialState: {
6 | isGptPage: false,
7 | searchResults: [],
8 | searching: false,
9 | },
10 | reducers: {
11 | handleGptPage: (state, action) => {
12 | state.isGptPage = !state.isGptPage;
13 | },
14 | setSearch: (state, action) => {
15 | state.searching = true;
16 | },
17 | setMoviesSearchResults: (state, action) => {
18 | state.searchResults = action.payload;
19 | state.searching = false;
20 | },
21 | },
22 | });
23 |
24 | export const { handleGptPage, setSearch, setMoviesSearchResults } =
25 | GptSlice.actions;
26 | export default GptSlice.reducer;
27 |
--------------------------------------------------------------------------------
/src/utils/constants.js:
--------------------------------------------------------------------------------
1 | export const LOGO =
2 | "https://cdn.cookielaw.org/logos/dd6b162f-1a32-456a-9cfe-897231c7763c/4345ea78-053c-46d2-b11e-09adaef973dc/Netflix_Logo_PMS.png";
3 | export const backgroundImage =
4 | "https://assets.nflxext.com/ffe/siteui/vlv3/77d35039-751f-4c3e-9c8d-1240c1ca6188/cf244808-d722-428f-80a9-052acdf158ec/IN-en-20231106-popsignuptwoweeks-perspective_alpha_website_large.jpg";
5 |
6 | export const TMDB_OPTIONS = {
7 | method: "GET",
8 | headers: {
9 | accept: "application/json",
10 | Authorization: `Bearer ${process.env.REACT_APP_TMDB_AUTHORIZATION}`,
11 | },
12 | };
13 | export const IMG_CDN_URL =
14 | "https://image.tmdb.org/t/p/w250https://image.tmdb.org/t/p/w500";
15 | export const NETFLIX_SERVER_BASE_URL ="https://api-netflix-online.onrender.com"
--------------------------------------------------------------------------------
/src/reduxStore/TrendingMovie/index.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | loading: false,
5 | error: "",
6 | trendingMovie: [],
7 | currentTrailer: {},
8 | popularMovie: [],
9 | nowPlayingMovie: [],
10 | };
11 | const movieSlice = createSlice({
12 | initialState,
13 | name: "TrendingMovie",
14 | reducers: {
15 | addTrendingMovie: (state, actions) => {
16 | state.trendingMovie = actions.payload;
17 | },
18 | addCurrentTrailer: (state, actions) => {
19 | state.currentTrailer = actions.payload;
20 | },
21 | addPopularMovie: (state, actions) => {
22 | state.popularMovie = actions.payload;
23 | },
24 | addNowPlayingMovie: (state, actions) => {
25 | state.nowPlayingMovie = actions.payload;
26 | },
27 | },
28 | });
29 | export const {
30 | addTrendingMovie,
31 | addCurrentTrailer,
32 | addNowPlayingMovie,
33 | addPopularMovie,
34 | } = movieSlice.actions;
35 | export default movieSlice.reducer;
36 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const Dotenv = require('dotenv-webpack');
2 | const path = require("path");
3 | const HtmlWebpack = require("html-webpack-plugin");
4 | module.exports = {
5 | resolve: {
6 | extensions: [".js", ".jsx", ".css"],
7 | },
8 | entry: "./src/index.js",
9 | output: {
10 | path: path.resolve(__dirname, "dist"),
11 | },
12 | devServer :{
13 | historyApiFallback: true,
14 | },
15 | plugins: [
16 | new HtmlWebpack({
17 | template: "./src/index.html",
18 | title : "index.html",
19 | }),
20 |
21 | new Dotenv(),
22 | ],
23 | module: {
24 | rules: [
25 | {
26 | test: /.(js|jsx)$/,
27 | exclude: /node_modules/,
28 | use: {
29 | loader: "babel-loader",
30 | options: {
31 | presets: ["@babel/preset-env", "@babel/preset-react"],
32 | },
33 | },
34 | },
35 | {
36 | test: /.css$/,
37 | use: ['style-loader', 'css-loader', 'postcss-loader'],
38 | },
39 | ],
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/src/utils/Firebase.js:
--------------------------------------------------------------------------------
1 | // Import the functions you need from the SDKs you need
2 | import { initializeApp } from "firebase/app";
3 | import { getAnalytics } from "firebase/analytics";
4 | import { getAuth } from "firebase/auth";
5 |
6 | // TODO: Add SDKs for Firebase products that you want to use
7 | // https://firebase.google.com/docs/web/setup#available-libraries
8 |
9 | // Your web app's Firebase configuration
10 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional
11 | const firebaseConfig = {
12 | apiKey: process.env.REACT_APP_FIREBASE_apiKey,
13 | authDomain: process.env.REACT_APP_FIREBASE_authDomain,
14 | projectId: process.env.REACT_APP_FIREBASE_projectId,
15 | storageBucket: process.env.REACT_APP_FIREBASE_storageBucket,
16 | messagingSenderId: process.env.REACT_APP_FIREBASE_messagingSenderId,
17 | appId: process.env.REACT_APP_FIREBASE_appId,
18 | measurementId: process.env.REACT_APP_FIREBASE_measurementId,
19 | };
20 | // Initialize Firebase
21 | const app = initializeApp(firebaseConfig);
22 | const analytics = getAnalytics(app);
23 |
24 | //////////////////////
25 | export const auth = getAuth();
26 |
--------------------------------------------------------------------------------
/src/utils/Router.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import { Outlet, createHashRouter } from "react-router-dom";
3 | const Login = React.lazy(() => import("../views/Authentication"));
4 | const LandingPage = React.lazy(() => import("../views/LandingPage"));
5 | const Navbar = React.lazy(() => import("../components/Navbar"));
6 | const PrivateRoute = React.lazy(() => import("../HOC/PrivateRoute"));
7 |
8 | function AppLayout() {
9 | return (
10 | <>
11 |
12 |
13 | >
14 | );
15 | }
16 |
17 | export const AppRoute = createHashRouter([
18 | {
19 | path: "/",
20 | element: (
21 |
22 |
23 |
24 | ),
25 | children: [
26 | {
27 | path: "/",
28 | element: (
29 |
30 | {" "}
31 |
32 |
33 | ),
34 | },
35 | ],
36 | },
37 | {
38 | path: "/authentication",
39 | element: (
40 |
41 | {" "}
42 |
43 |
44 | ),
45 | },
46 | ]);
47 |
--------------------------------------------------------------------------------
/src/reduxStore/authSlice/index.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | loading: false,
5 | errorMessage: "",
6 | user: {},
7 | isAuthenticated: false,
8 | };
9 | const authenticationSlice = createSlice({
10 | name: "authenticationSlice",
11 | initialState,
12 | reducers: {
13 | authLoading: (state, actions) => {
14 | state.loading = true;
15 | state.errorMessage = "";
16 | },
17 | authError: (state, actions) => {
18 | state.errorMessage = actions.payload;
19 | state.loading = false;
20 | },
21 | UserLoginSignIn: (state, actions) => {
22 | state.user = actions.payload;
23 | state.isAuthenticated = true;
24 | state.errorMessage = "";
25 | state.loading = false;
26 | },
27 | logOutUser: (state, actions) => {
28 | state.user = {};
29 | state.isAuthenticated = false;
30 | state.errorMessage = "";
31 | state.loading = false;
32 | },
33 | },
34 | });
35 |
36 | export const { authLoading, authError, UserLoginSignIn, logOutUser } =
37 | authenticationSlice.actions;
38 | export default authenticationSlice.reducer;
39 |
--------------------------------------------------------------------------------
/src/api/auth.api.js:
--------------------------------------------------------------------------------
1 | import {
2 | createUserWithEmailAndPassword,
3 | signInWithEmailAndPassword,
4 | signOut,
5 | } from "firebase/auth";
6 | import { auth } from "../utils/Firebase";
7 | import axios from "axios";
8 | import { TMDB_OPTIONS } from "../utils/constants";
9 |
10 | export const CreateNewUserApi = async ({ email, password }) => {
11 | return await createUserWithEmailAndPassword(auth, email, password);
12 | };
13 |
14 | export const signInUserApi = async ({ email, password }) => {
15 | return await signInWithEmailAndPassword(auth, email, password);
16 | };
17 |
18 | export const logoutUserApi = async () => {
19 | return await signOut(auth);
20 | };
21 |
22 | export const NowPlayingMovies = (signal) => {
23 | return axios.get("https://api.themoviedb.org/3/movie/now_playing", {
24 | ...TMDB_OPTIONS,
25 | signal,
26 | });
27 | };
28 |
29 | export const TrendingMovies = (signal) => {
30 | return axios.get("https://api.themoviedb.org/3/trending/movie/day?language=en-hi", {
31 | ...TMDB_OPTIONS,
32 | signal,
33 | });
34 | };
35 | export const PopularMovies = (signal) => {
36 | return axios.get("https://api.themoviedb.org/3/movie/popular", {
37 | ...TMDB_OPTIONS,
38 | signal,
39 | });
40 | };
41 |
42 | export const getVideoApi = (movie_id) => {
43 | return axios.get(
44 | `https://api.themoviedb.org/3/movie/${movie_id}/videos`,
45 | TMDB_OPTIONS
46 | );
47 | };
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/TrailerCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useSelector } from "react-redux";
3 |
4 | const TrailerCard = () => {
5 | const currentTrailer = useSelector((store) => store.trending?.currentTrailer);
6 | if (!currentTrailer?.movieDetail) return;
7 | const { original_title, overview } = currentTrailer?.movieDetail;
8 |
9 | return (
10 |
19 |
20 |
21 | {original_title}
22 |
23 |
{overview}
24 |
25 |
28 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default TrailerCard;
38 |
--------------------------------------------------------------------------------
/src/views/GptPage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { backgroundImage } from "../utils/constants";
3 | import SearchBar from "../components/SearchBar";
4 | import { useSelector } from "react-redux";
5 | import GptMovieSuggestions from "../components/GptMovieSuggestions";
6 |
7 | const GptPage = () => {
8 | const { searchResults } = useSelector((store) => store.gpt);
9 | return (
10 |
11 |
12 |

16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {searchResults.length && (
25 |
26 | {searchResults?.map((suggestedMovie) => {
27 | return (
28 |
32 | );
33 | })}
34 |
35 | )}
36 |
37 |
38 | );
39 | };
40 |
41 | export default GptPage;
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://deepumandal.github.io/netflix/",
3 | "name": "namaste_netflix_gpt",
4 | "version": "1.0.0",
5 | "description": "",
6 | "scripts": {
7 | "dev": "webpack-dev-server --mode development --open --hot",
8 | "build": "webpack --mode production",
9 | "predeploy": "npm run build",
10 | "deploy": "gh-pages -d dist"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@babel/core": "^7.23.3",
17 | "@babel/preset-env": "^7.23.3",
18 | "@babel/preset-react": "^7.23.3",
19 | "@reduxjs/toolkit": "^1.9.7",
20 | "autoprefixer": "^10.4.16",
21 | "axios": "^1.6.2",
22 | "babel-loader": "^9.1.3",
23 | "css-loader": "^6.8.1",
24 | "dotenv-webpack": "^8.0.1",
25 | "env": "^0.0.2",
26 | "firebase": "^10.6.0",
27 | "gh-pages": "^6.1.0",
28 | "html-webpack-plugin": "^5.5.3",
29 | "lodash": "^4.17.21",
30 | "openai": "^4.19.0",
31 | "postcss-loader": "^7.3.3",
32 | "react": "^18.2.0",
33 | "react-dom": "^18.2.0",
34 | "react-player": "^2.13.0",
35 | "react-redux": "^8.1.3",
36 | "react-router-dom": "^6.18.0",
37 | "react-youtube": "^10.1.0",
38 | "style-loader": "^3.3.3"
39 | },
40 | "devDependencies": {
41 | "buffer": "^6.0.3",
42 | "postcss": "^8.4.31",
43 | "process": "^0.11.10",
44 | "tailwindcss": "^3.3.5",
45 | "webpack": "^5.89.0",
46 | "webpack-cli": "^5.1.4",
47 | "webpack-dev-server": "^4.15.1"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { useDispatch, useSelector } from "react-redux";
4 |
5 | import { LOGO } from "../utils/constants";
6 | import { logoutUserApi } from "../api/auth.api";
7 | import { logOutUser } from "../reduxStore/authSlice";
8 | import { handleGptPage } from "../reduxStore/GptSlice";
9 |
10 | const Navbar = () => {
11 | const dispatch = useDispatch();
12 |
13 | const { loading } = useSelector((store) => store.auth);
14 | const { isGptPage } = useSelector((store) => store.gpt);
15 | const handleSignout = (e) => {
16 | e.preventDefault();
17 | logoutUserApi().then(() => {
18 | dispatch(logOutUser());
19 | });
20 | };
21 | const onClickGptPage = (e) => {
22 | e.preventDefault();
23 | dispatch(handleGptPage());
24 | };
25 | return (
26 | // bg-gradient-to-b from-[#000000]
27 |
28 |
29 |

30 |
31 |
32 |
38 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default Navbar;
50 |
--------------------------------------------------------------------------------
/src/components/TrailerBackround.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 | import { useSelector } from "react-redux";
3 | import YouTube from "react-youtube";
4 |
5 | const TrailerBackround = () => {
6 | const currentTrailer = useSelector((store) => store.trending?.currentTrailer);
7 |
8 | const handleOnload = (event) => {
9 | // const childDocument = event?.target?.contentWindow?.top?.document;
10 | // const childHtml = childDocument?.childNodes[1];
11 | // const childHead = childHtml?.childNodes[0];
12 | // const jsInjections = document.createElement("script");
13 | // jsInjections.setAttribute("defer", "");
14 | // jsInjections.innerHTML = `
15 | // const yt_title = document.getElementsByClassName("ytp-show-cards-title")
16 | // // yt_title.style.display = "none";
17 | // console.log("console from childHtml: " + yt_title.length)
18 | // `;
19 | // childHead.appendChild(jsInjections);
20 | // console.log("childDocument", event?.target?.contentWindow.postMessage());
21 | };
22 |
23 | if (!currentTrailer?.mediaDetail) return;
24 | const key = currentTrailer?.mediaDetail.key;
25 | return (
26 |
37 |
51 |
52 | );
53 | };
54 |
55 | export default TrailerBackround;
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Netflix Clone Project
2 |
3 | ## Features
4 |
5 | ### Authentication
6 | Users can create accounts, log in, and access content based on their authentication status. Secure user authentication mechanisms are implemented to ensure data privacy.
7 |
8 | ### Movie Search Feature
9 | The platform includes a robust search functionality allowing users to explore a vast library of movies. They can search for specific titles, genres, or keywords to discover content.
10 |
11 | ### Separate Server Source Code
12 | The server-side of this project is maintained in a separate repository. You can find the server source code at [Server Repository URL](https://github.com/deepumandal/netflix-server).
13 |
14 | ### GPT Search Support
15 | The Netflix clone integrates GPT (Generative Pre-trained Transformer) for enhanced search capabilities. GPT facilitates a more intuitive and sophisticated search experience, providing users with accurate and relevant results based on their queries.
16 |
17 | ## Repository Information
18 | - **Frontend Repository:** [Frontend Repository URL](https://github.com/deepumandal/Netflix)
19 | - **Server Repository:** [Server Repository URL](https://github.com/deepumandal/netflix-server)
20 |
21 | ## Getting Started
22 | To run this Netflix clone project locally, follow these steps:
23 | 1. Clone the frontend repository [Source](https://github.com/deepumandal/Netflix).
24 | 2. Clone the server repository [Source](https://github.com/deepumandal/netflix-server).
25 | 3. Follow the setup instructions provided in each repository's README file to set up the frontend and server environments.
26 | 4. Connect the frontend with the server by following the integration guidelines mentioned in the respective repositories.
27 |
28 | ## Usage
29 | Once the project is set up, users can navigate the Netflix clone, authenticate, explore movies using the search feature, and benefit from the GPT-powered search capabilities.
30 |
31 | ## Contributors
32 | This project was developed and maintained by [Deepak Mandal](https://github.com/deepumandal).
33 |
34 | ## License
35 | This repository is created solely for educational purposes and learning experience. It is not intended for any illegal or unauthorized activities.
36 |
--------------------------------------------------------------------------------
/src/components/SearchBar.js:
--------------------------------------------------------------------------------
1 | import React, { memo, useEffect, useRef } from "react";
2 | import { NETFLIX_SERVER_BASE_URL, TMDB_OPTIONS } from "../utils/constants";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import { setMoviesSearchResults, setSearch } from "../reduxStore/GptSlice";
5 | import axios from "axios";
6 | const SearchBar = () => {
7 | const userGptQuerry = useRef(); // current.value
8 | const dispatch = useDispatch();
9 | const { searching } = useSelector((store) => store.gpt);
10 | const searchMovieTMDB = async (movie) => {
11 | const data = await fetch(
12 | "https://api.themoviedb.org/3/search/movie?query=" +
13 | movie +
14 | "&include_adult=false&language=en-US&page=1",
15 | TMDB_OPTIONS
16 | );
17 | const json = await data.json();
18 |
19 | return json.results;
20 | };
21 |
22 | const handleClickOnSearchbtn = async (event) => {
23 | event.preventDefault();
24 | dispatch(setSearch());
25 |
26 | axios
27 | .post(NETFLIX_SERVER_BASE_URL + "/openai/connections", {
28 | question: userGptQuerry.current.value,
29 | })
30 | .then((res) => {
31 | const data = res.data;
32 | axios
33 | .post(NETFLIX_SERVER_BASE_URL + "/search/movie", data)
34 | .then((res) => {
35 | const { data } = res;
36 | console.log(data)
37 | dispatch(setMoviesSearchResults(data));
38 | });
39 | });
40 | };
41 |
42 | useEffect(() => {
43 | return () => {
44 | searching && dispatch(setSearch());
45 | };
46 | }, []);
47 | return (
48 |
65 | );
66 | };
67 |
68 | export default memo(SearchBar);
69 |
--------------------------------------------------------------------------------
/src/views/LandingPage.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import {
3 | NowPlayingMovies,
4 | PopularMovies,
5 | TrendingMovies,
6 | getVideoApi,
7 | } from "../api/auth.api";
8 | import { useDispatch, useSelector } from "react-redux";
9 | import {
10 | addCurrentTrailer,
11 | addNowPlayingMovie,
12 | addPopularMovie,
13 | addTrendingMovie,
14 | } from "../reduxStore/TrendingMovie";
15 | import TrailerBackround from "../components/TrailerBackround";
16 | import TrailerCard from "../components/TrailerCard";
17 | import MovieLists from "../components/MovieLists";
18 | import GptPage from "./GptPage";
19 |
20 | const LandingPage = () => {
21 | const dispatch = useDispatch();
22 |
23 | const { trendingMovie, popularMovie, nowPlayingMovie } =
24 | useSelector((store) => store.trending) || [];
25 | const { isGptPage } = useSelector((store) => store.gpt);
26 |
27 | const parseTrendingMovie = (movies) => {
28 | if (!movies.length) return;
29 | const currentTrendingMovie = movies[1];
30 |
31 | getVideoApi(currentTrendingMovie?.id).then((response) => {
32 | const { results } = response?.data;
33 |
34 | const currentTrailer = results.filter(
35 | (provider) => provider?.type === "Trailer"
36 | );
37 |
38 | dispatch(
39 | addCurrentTrailer({
40 | mediaDetail: currentTrailer[0],
41 | movieDetail: currentTrendingMovie,
42 | })
43 | );
44 | });
45 | };
46 |
47 | useEffect(() => {
48 | const AbroadController = new AbortController();
49 | if (!nowPlayingMovie.length) {
50 | NowPlayingMovies(AbroadController.signal).then((res) => {
51 | dispatch(addNowPlayingMovie(res?.data?.results));
52 | });
53 | }
54 | if (!trendingMovie.length) {
55 | TrendingMovies(AbroadController.signal).then((res) => {
56 | parseTrendingMovie(res?.data?.results);
57 | dispatch(addTrendingMovie(res?.data?.results));
58 | });
59 | }
60 | if (!popularMovie.length) {
61 | PopularMovies(AbroadController.signal).then((res) => {
62 | dispatch(addPopularMovie(res?.data?.results));
63 | });
64 | }
65 | return () => {
66 | AbroadController && AbroadController?.abort();
67 | };
68 | }, []);
69 | return (
70 |
71 | {isGptPage ? (
72 |
73 | ) : (
74 | <>
75 |
76 |
77 |
78 |
79 | {/* movie lists */}
80 |
81 |
82 |
83 |
84 |
85 | >
86 | )}
87 |
88 | );
89 | };
90 |
91 | export default LandingPage;
--------------------------------------------------------------------------------
/src/views/Authentication.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState } from "react";
2 | import { getValidatedInputs } from "../utils/getValidatedInputs";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import { useLocation, useNavigate } from "react-router-dom";
5 | import { CreateNewUserApi, signInUserApi } from "../api/auth.api";
6 | import {
7 | UserLoginSignIn,
8 | authLoading,
9 | authError,
10 | } from "../reduxStore/authSlice/index";
11 | import { LOGO, backgroundImage } from "../utils/constants";
12 |
13 | const Login = () => {
14 | const [isAlreadyUser, setIsAlreadyUser] = useState(true);
15 | // const [error, setError] = useState("");
16 | const userName = useRef();
17 | const userEmail = useRef();
18 | const userPassword = useRef();
19 | const dispatch = useDispatch();
20 | const loc = useLocation();
21 |
22 | const { errorMessage, loading } = useSelector((store) => store.auth);
23 | const navigate = useNavigate();
24 |
25 | const handleAddNewUser = () => {
26 | setIsAlreadyUser((prev) => !prev);
27 | };
28 |
29 | const onSubmit = (e) => {
30 | e.preventDefault();
31 |
32 | const email = userEmail.current?.value;
33 | const password = userPassword.current?.value;
34 |
35 | // validation check for password and email
36 | const inputsError = getValidatedInputs(email, password);
37 |
38 | if (inputsError) return dispatch(authError(inputsError)); // to immidiate stop the if inputs are wrong
39 | dispatch(authLoading());
40 | if (isAlreadyUser) {
41 | signInUserApi({ email, password })
42 | .then((response) => {
43 | const { displayName, stsTokenManager, phoneNumber, photoURL, uid } =
44 | response.user;
45 | const { refreshToken, accessToken, expirationTime } = stsTokenManager;
46 | dispatch(
47 | UserLoginSignIn({
48 | displayName,
49 | userToken: { refreshToken, accessToken, expirationTime },
50 | phoneNumber,
51 | photoURL,
52 | uid,
53 | email,
54 | })
55 | );
56 | navigate(loc.state?.pathname, { replace: true });
57 | })
58 | .catch((error) => {
59 | dispatch(authError(error.message));
60 | });
61 | } else {
62 | CreateNewUserApi({ email, password })
63 | .then((response) => {
64 | const { displayName, stsTokenManager, phoneNumber, photoURL, uid } =
65 | response.user;
66 | const { refreshToken, accessToken, expirationTime } = stsTokenManager;
67 | dispatch(
68 | UserLoginSignIn({
69 | displayName,
70 | userToken: { refreshToken, accessToken, expirationTime },
71 | phoneNumber,
72 | photoURL,
73 | uid,
74 | email,
75 | })
76 | );
77 | navigate(loc.state?.pathname, { replace: true });
78 | })
79 | .catch((error) => {
80 | dispatch(authError(error.message));
81 | });
82 | }
83 | };
84 |
85 | return (
86 |
87 |
88 |

89 |
90 |
91 |
92 |
93 |

94 |
95 | {/* form validiation */}
96 |
141 |
142 | );
143 | };
144 |
145 | export default Login;
146 |
--------------------------------------------------------------------------------