Full Code of Azazel5/NetflixClone for AI

master 327558846a62 cached
87 files
112.2 KB
30.1k tokens
4 symbols
1 requests
Download .txt
Repository: Azazel5/NetflixClone
Branch: master
Commit: 327558846a62
Files: 87
Total size: 112.2 KB

Directory structure:
gitextract_mptik5yv/

├── .gitignore
├── README.md
├── cypress/
│   ├── integration/
│   │   ├── browse-home.spec.js
│   │   ├── browse-navbar.spec.js
│   │   ├── landing-section.spec.js
│   │   ├── login-form.spec.js
│   │   └── sign-in-flow.spec.js
│   ├── plugins/
│   │   └── index.js
│   └── support/
│       ├── commands.js
│       └── index.js
├── cypress.json
├── jsconfig.json
├── package.json
├── public/
│   ├── 404.html
│   ├── index.html
│   └── manifest.json
└── src/
    ├── App.js
    ├── assets/
    │   └── images/
    │       └── index.js
    ├── baseAxios.js
    ├── components/
    │   ├── Modals/
    │   │   ├── ProfileModal/
    │   │   │   ├── ProfileModal.css
    │   │   │   └── ProfileModal.js
    │   │   └── VideoModal/
    │   │       ├── VideoModal.css
    │   │       └── VideoModal.js
    │   ├── Navigation/
    │   │   └── Dropdown/
    │   │       ├── Dropdown.css
    │   │       └── Dropdown.js
    │   ├── StaticPages/
    │   │   ├── ErrorPage/
    │   │   │   ├── ErrorPage.css
    │   │   │   └── ErrorPage.js
    │   │   ├── LoadingScreen/
    │   │   │   ├── LoadingScreen.css
    │   │   │   └── LoadingScreen.js
    │   │   └── NotFoundPage/
    │   │       ├── NotFoundPage.css
    │   │       └── NotFoundPage.js
    │   ├── UI/
    │   │   ├── Button/
    │   │   │   ├── Button.css
    │   │   │   └── Button.js
    │   │   ├── CircularSoundButton/
    │   │   │   ├── CircularSoundButton.css
    │   │   │   └── CircularSoundButton.js
    │   │   ├── DarkComponent/
    │   │   │   └── DarkComponent.js
    │   │   ├── FAQComponent/
    │   │   │   ├── FAQComponent.css
    │   │   │   └── FAQComponent.js
    │   │   └── ProfileCard/
    │   │       ├── ProfileCard.css
    │   │       └── ProfileCard.js
    │   └── Video/
    │       ├── TopTrailerComponent/
    │       │   ├── TopTrailerComponent.css
    │       │   └── TopTrailerComponent.js
    │       ├── VideoCard/
    │       │   ├── VideoCard.css
    │       │   └── VideoCard.js
    │       └── VideoCarousel/
    │           ├── VideoCarousel.css
    │           └── VideoCarousel.js
    ├── containers/
    │   ├── Browse/
    │   │   ├── Browse.js
    │   │   ├── BrowseContent/
    │   │   │   ├── BrowseContent.css
    │   │   │   └── BrowseContent.js
    │   │   ├── SearchContent/
    │   │   │   ├── SearchContent.css
    │   │   │   └── SearchContent.js
    │   │   └── routes/
    │   │       ├── Home.js
    │   │       ├── LatestVideo.js
    │   │       ├── List.js
    │   │       ├── Movies.js
    │   │       ├── Tv.js
    │   │       └── index.js
    │   ├── LandingSection/
    │   │   ├── LandingSection.css
    │   │   ├── LandingSection.js
    │   │   └── LandingSectionTexts.js
    │   ├── Login/
    │   │   ├── Login.css
    │   │   └── Login.js
    │   ├── NavBar/
    │   │   ├── NavBar.css
    │   │   └── NavBar.js
    │   └── Search/
    │       ├── Search.css
    │       └── Search.js
    ├── context/
    │   └── Authentication.js
    ├── hoc/
    │   ├── Layout.js
    │   └── ScrollToTop/
    │       └── ScrollToTop.js
    ├── hooks/
    │   ├── useDropdown.js
    │   ├── useHoverStyleButton.js
    │   ├── useNavbar.js
    │   └── useVideoInfoHandlers.js
    ├── index.js
    ├── store/
    │   └── reducers/
    │       ├── slices/
    │       │   ├── latestVideoSlice.js
    │       │   ├── moviesByGenreSlice.js
    │       │   ├── netflixOriginalsSlice.js
    │       │   ├── topratedSlice.js
    │       │   ├── trendingSlice.js
    │       │   └── tvByGenreSlice.js
    │       └── store.js
    ├── styles.css
    └── utils/
        ├── animations.js
        ├── sorting.js
        ├── time.js
        ├── transformations.js
        └── validation.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea/
.vscode/
node_modules/
build
.DS_Store
*.tgz
my-app*
template/src/__tests__/__snapshots__/
lerna-debug.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/.changelog
.npm/
yarn.lock
.env

================================================
FILE: README.md
================================================
# Netflix-Clone

![DemoGif](flixdemo.gif)

A Netflix clone I created for the sake of practicing React and Redux. It features design 
patterns recommended by the documentation. Some of the tools used include: <br />

* Hooks (and custom hooks)
* React Router
* Redux Toolkit 
* Context API 
* Responsive web design 
* Cypress end-to-end testing 

<br />

It is a work in progress, and my first real project with React. Any tips on how to better write the 
code, manage the folder structure, etc would be really appreciated. <br />

The future of this project: <br />

* Integrate it with a Django backend
* Create an authentication flow
* Add REST API endpoints for every user-related event, such as adding Netflix profiles

## Architecture Diagram 

![CloneFlow](https://user-images.githubusercontent.com/36729591/90905326-08c9c400-e39e-11ea-977c-76212f63b2b6.png)


================================================
FILE: cypress/integration/browse-home.spec.js
================================================
/// <reference types="Cypress" />

describe('<Home /> -> <BrowseContent />', () => {
    beforeEach(() => {
        localStorage.setItem('profileSelected', true)
        cy.visit('/browse')
        cy.window()
            .its('store')
            .invoke('getState')
            .as('reduxState')

    })

    it('fetches trending, top-rated, and netflix originals successfully', () => {
        cy.get('@reduxState')
            .its('trending')
            .its('ids')
            .should('have.length', 20)

        cy.get('@reduxState')
            .its('toprated')
            .its('ids')
            .should('have.length', 20)

        cy.get('@reduxState')
            .its('netflixOriginals')
            .its('ids')
            .should('have.length', 20)
    })

    it('Ensure that the first video of trending section is placed on the <Video /> topTrailer', () => {
        cy.get('@reduxState')
            .its('trending')
            .its('ids')
            .then($idArray => {
                const firstElem = $idArray[0]
                cy.get('@reduxState')
                    .its('trending')
                    .its('entities')
                    .then(($entities => {
                        const item = Cypress._.find($entities, (element => element.id === firstElem))
                        cy.get('.VideoComponent')
                            .invoke('attr', 'style')
                            .then(style => {
                                expect(style).to.include(item.poster_path)
                            })
                    })) 
            })
    })
})

================================================
FILE: cypress/integration/browse-navbar.spec.js
================================================
/// <reference types="Cypress" />

describe('<Search /> and <Dropdown />', () => {
    beforeEach(() => {
        localStorage.setItem('profileSelected', true)
        cy.visit('/browse')
    })

    context('<Dropdown />', () => {
        beforeEach(() => {
            cy.get('.OptionsContainer .Dropdown > svg')
                .as('dropbox')
        })

        it('finds floating box on hover and fails to find it on hover off', () => {
            cy.get('@dropbox')
                .trigger('mouseover')
                .then(() => {
                    cy.get('.FloatingBox')
                        .should('exist')
                })
                .trigger('mouseleave', 'bottom')
                .then(() => {
                    cy.get('FloatingBox')
                        .should('not.exist')
                })
        })

        it('logs out and removes the local storage token on sign out press', () => {
            cy.get('@dropbox')
                .trigger('mouseover')
                .get('.FloatingBox')
                .find('span')
                .contains('Sign out of Netflix')
                .click()
                .then(() => {
                    expect(localStorage.getItem('profileSelected')).to.not.exist
                    cy.location().should(loc => {
                        expect(loc.pathname).to.eq('/')
                    })
                })
        })
    })

    context('<Search />', () => {
        beforeEach(() => {
            cy.get('.SearchBox')
                .click()
        })
        it('opens search box on click and closes on background click', () => {
            cy.get('.Holder')
                .should('exist')

            cy.get('.NavBar')
                .click('center')
                .find('.Holder')
                .should('not.exist')
        })

        it('cross becomes visible after typing and invisible when input length is 0', () => {
            cy.get('.Holder')
                .children('input')
                .type('Some movie title')

            cy.get('.Holder')
                .children('svg')
                .last()
                .click()
                .should('not.exist')
        })
    })
})

================================================
FILE: cypress/integration/landing-section.spec.js
================================================
/// <reference types="Cypress" />

describe('<LandingSection />', () => {
    beforeEach(() => {
        cy.visit('/')
    })

    context('<FAQComponent />', () => {
        it('leaves number of components intact even after pressing all boxes', () => {
            cy.get('.faqComponent')
                .click({ multiple: true })
                .should('have.length', 5)
        })

        it('has a set number of FAQ boxes if not clicked', () => {
            cy.get('.faqComponent')
                .should('have.length', 5)
        })

        it('opens an inner box on click', () => {
            cy.get('.tv-inner > .faqComponent')
                .first()
                .click()

            cy.get('.faqComponent')
                .should('have.length', 6)
        })
    })

    context('Sign In Button', () => {
        it("navigates to the login page on sign in button click", () => {
            cy.get('.Button')
                .contains('Sign In')
                .click()

            cy.location().should(loc => {
                expect(loc.pathname).to.eq('/login')
            })
        })
    })

})

================================================
FILE: cypress/integration/login-form.spec.js
================================================
/// <reference types="Cypress" />

describe('<Login />', () => {
    beforeEach(() => {
        cy.visit('/login')

        cy.get('input[name="email"]')
            .as('inputEmail')

        cy.get('input[name="password"]')
            .as('passwordInput')
    })

    it('fails to submit form without any input and shows error spans for both inputs', () => {
        cy.get('.Button')
            .contains('Sign In')
            .click()

        cy.location().should(loc => {
            expect(loc.pathname).to.eq('/login')
        })

        cy.get('form')
            .children('span')
            .should('have.length', 2)
    })

    it('shows a span text on email input focus and focus lost', () => {
        const spanText = 'Please enter a valid email or phone number.'
        cy.get('@inputEmail')
            .focus()
            .blur()

        cy.get('form')
            .children('span')
            .should('have.length', 1)
            .contains(spanText)
    })

    it('changes span text on password validation passed', () => {
        const spanText = 'Your password must contain between 4 and 60 characters.'
        cy.get('@passwordInput')
            .focus()
            .blur()

        cy.get('form')
            .children('span')
            .as('text')

        cy.get('@text')
            .should('have.length', 1)
            .contains(spanText)

        cy.get('@passwordInput')
            .type('123')

        cy.get('@text')
            .contains(spanText)

        cy.get('@passwordInput')
            .type('4')

        cy.get('@text')
            .should('not.exist')
    })

    it('logs in on valid input', () => {
        cy.get('@inputEmail')
            .type('example@example.com')

        cy.get('@passwordInput')
            .type('1234')

        cy.get('.Button')
            .contains('Sign In')
            .click()

        cy.location().should(loc => {
            expect(loc.pathname).to.eq('/browse')
        })
    })
})

================================================
FILE: cypress/integration/sign-in-flow.spec.js
================================================
/// <reference types="Cypress" />

describe('<Browse />', () => {
    beforeEach(() => {
        cy.validLogin()
    })

    it('opens browse section if local storage profile selection is set', () => {
        localStorage.setItem('profileSelected', true)

        cy.get('.Button')
            .contains('Sign In')
            .click()

        cy.get('.BrowseContent')
            .should('exist')
    })

    it('opens modal if local storage is not set, and continues to browse section', () => {
        cy.clearLocalStorage()
        
        cy.get('.Button')
            .contains('Sign In')
            .click()

        cy.get('.BrowseContent')
            .should('not.exist')

        cy.get('.ProfileDiv')
            .should('exist')
            .find('.ProfileCard')
            .as('profileCard')
            .should('have.length', 4)

        cy.get('@profileCard')
            .first()
            .click()
            .should(($profileCard) => {
                expect(localStorage.getItem('profileSelected')).to.exist
                expect($profileCard).to.not.exist
            })

        cy.get('.BrowseContent')
            .should('exist')
    })
})

================================================
FILE: cypress/plugins/index.js
================================================
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
 * @type {Cypress.PluginConfig}
 */
module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
}


================================================
FILE: cypress/support/commands.js
================================================
Cypress.Commands.add('validLogin', () => {
    cy.visit('/login')

    cy.get('input[name="email"]')
        .type('example@example.com')

    cy.get('input[name="password"]')
        .type('1234')
})

================================================
FILE: cypress/support/index.js
================================================
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')


================================================
FILE: cypress.json
================================================
{
    "baseUrl": "http://localhost:3000"
}

================================================
FILE: jsconfig.json
================================================
{
    "compilerOptions": {
        "baseUrl": "src"
    },
    "include": [
        "src"
    ]
}

================================================
FILE: package.json
================================================
{
  "name": "netflix-clone",
  "version": "1.0.0",
  "description": "",
  "homepage": "https://azazel5.github.io/NetflixClone/",
  "keywords": [],
  "main": "src/index.js",
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "1.2.29",
    "@fortawesome/free-solid-svg-icons": "5.13.1",
    "@fortawesome/react-fontawesome": "0.1.11",
    "@material-ui/core": "4.11.0",
    "@reduxjs/toolkit": "^1.4.0",
    "axios": "^0.19.2",
    "react": "16.12.0",
    "react-device-detect": "^1.13.1",
    "react-dom": "16.12.0",
    "react-modal": "^3.11.2",
    "react-redux": "^7.2.0",
    "react-router": "5.2.0",
    "react-router-dom": "5.2.0",
    "react-scripts": "3.0.1",
    "react-transition-group": "^4.4.1",
    "redux": "^4.0.5",
    "redux-thunk": "^2.3.0",
    "reselect": "^4.0.0"
  },
  "devDependencies": {
    "cypress": "^4.12.1",
    "gh-pages": "^3.1.0",
    "typescript": "3.8.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "cypress": "cypress open",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}


================================================
FILE: public/404.html
================================================
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Single Page Apps for GitHub Pages</title>
    <script type="text/javascript">
      // Single Page Apps for GitHub Pages
      // https://github.com/rafrex/spa-github-pages
      // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
      // ----------------------------------------------------------------------
      // This script takes the current url and converts the path and query
      // string into just a query string, and then redirects the browser
      // to the new url with only a query string and hash fragment,
      // e.g. http://www.foo.tld/one/two?a=b&c=d#qwe, becomes
      // http://www.foo.tld/?p=/one/two&q=a=b~and~c=d#qwe
      // Note: this 404.html file must be at least 512 bytes for it to work
      // with Internet Explorer (it is currently > 512 bytes)

      // If you're creating a Project Pages site and NOT using a custom domain,
      // then set segmentCount to 1 (enterprise users may need to set it to > 1).
      // This way the code will only replace the route part of the path, and not
      // the real directory in which the app resides, for example:
      // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
      // https://username.github.io/repo-name/?p=/one/two&q=a=b~and~c=d#qwe
      // Otherwise, leave segmentCount as 0.
      var segmentCount = 1;

      var l = window.location;
      l.replace(
        l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
        l.pathname.split('/').slice(0, 1 + segmentCount).join('/') + '/?p=/' +
        l.pathname.slice(1).split('/').slice(segmentCount).join('/').replace(/&/g, '~and~') +
        (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
        l.hash
      );

    </script>
  </head>
  <body>
  </body>
</html>

================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
	<meta name="theme-color" content="#000000">
	<!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
	<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
	<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
	<!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
  <title>Netflix Clone</title>
  <script type="text/javascript">
    // Single Page Apps for GitHub Pages
    // https://github.com/rafrex/spa-github-pages
    // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
    // ----------------------------------------------------------------------
    // This script checks to see if a redirect is present in the query string
    // and converts it back into the correct url and adds it to the
    // browser's history using window.history.replaceState(...),
    // which won't cause the browser to attempt to load the new url.
    // When the single page app is loaded further down in this file,
    // the correct url will be waiting in the browser's history for
    // the single page app to route accordingly.
    (function(l) {
      if (l.search) {
        var q = {};
        l.search.slice(1).split('&').forEach(function(v) {
          var a = v.split('=');
          q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');
        });
        if (q.p !== undefined) {
          window.history.replaceState(null, null,
            l.pathname.slice(0, -1) + (q.p || '') +
            (q.q ? ('?' + q.q) : '') +
            l.hash
          );
        }
      }
    }(window.location))
  </script>
  <!-- End Single Page Apps for GitHub Pages -->
</head>

<body>
	<noscript>
		You need to enable JavaScript to run this app.
	</noscript>
	<div id="root"></div>
	<!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
</body>

</html>

================================================
FILE: public/manifest.json
================================================
{
    "short_name": "React App",
    "name": "Create React App Sample",
    "icons": [
        {
            "src": "favicon.ico",
            "sizes": "64x64 32x32 24x24 16x16",
            "type": "image/x-icon"
        }
    ],
    "start_url": "./index.html",
    "display": "standalone",
    "theme_color": "#000000",
    "background_color": "#ffffff"
}

================================================
FILE: src/App.js
================================================
import React, { useContext } from "react";
import "./styles.css";

import LandingSection from "containers/LandingSection/LandingSection";
import Login from "containers/Login/Login";
import Browse from 'containers/Browse/Browse'
import { Switch, Route, Redirect } from "react-router-dom";
import { AuthenticationContext } from 'context/Authentication'
import NotFoundPage from 'components/StaticPages/NotFoundPage/NotFoundPage'

export default function App() {
  const authContext = useContext(AuthenticationContext)

  const checkAuthAndSetBrowseComponent = (propsObject) => {
    return (authContext.authenticated || localStorage.getItem('profileSelected')) ?
      <Browse {...propsObject} /> :
      <Redirect to="/login" />
  }

  return (
    <div className="App">
      <Switch>
        <Route exact path="/browse" render={() => checkAuthAndSetBrowseComponent({ route: '/browse' })}>
        </Route>
        <Route exact path="/browse/tv" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/tv' })}>
        </Route>
        <Route exact path="/browse/movies" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/movies' })}>
        </Route>
        <Route exact path="/browse/latest" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/latest' })}>
        </Route>
        <Route exact path="/browse/list" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/list' })}>
        </Route>
        <Route exact path="/search" render={() => checkAuthAndSetBrowseComponent({ route: '/search' })}>
        </Route>
        <Route exact path="/login" component={Login}>
        </Route>
        <Route exact path="/" component={LandingSection}>
        </Route>
        <Route exact component={NotFoundPage}>
        </Route>
      </Switch>
    </div >
  );
}


================================================
FILE: src/assets/images/index.js
================================================
import Weird from './weird.png'
import Profile from './profile.jpg'
import Smile from './smile.png'
import Normal from './normal.jpg'
import NetflixLogo from './netflix.png'
import LoginBackground from './landingPage.jpg'

export {
    Weird, 
    Profile, 
    Smile, 
    Normal,
    NetflixLogo, 
    LoginBackground
} 

================================================
FILE: src/baseAxios.js
================================================
import axios from 'axios'

const axe = axios.create({
    baseURL: 'https://api.themoviedb.org/3/'
})

export default axe 

================================================
FILE: src/components/Modals/ProfileModal/ProfileModal.css
================================================
.ProfileModal {
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background: #141414;
    border: none;
    border-radius: none;
    padding: 0;
    outline: none;
}

.ProfileModal > img {
    height: 45px;
    width: 140px;
    margin-left: 20px;
    padding: 8px
}

.ProfileDiv {
    width: 60%;
    margin: 0 auto;
    text-align: center;
    position: relative;
    top: 30%;
    transform: translateY(-50%);
}

.ProfileDiv > h1 {
    color: rgb(255, 255, 255);
    font-weight: lighter;
    font-size: 6.5vw;
}

.horizontalComp {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 30px;
    flex-wrap: wrap;
}

.ProfileButton {
    border: 1px solid transparent;
    border-color: grey;
    background-color: transparent;
    color: grey;
    text-transform: uppercase;
    padding: .5em 1.5em;
    letter-spacing: 2px;
    font-size: .9em;
    outline: 0;
}

.ProfileButton:hover {
    border-color: white;
    color: white;
}

@media(min-width: 600px) {
    .ProfileDiv > h1 {
        font-size: 3.5vw;
    }
}

================================================
FILE: src/components/Modals/ProfileModal/ProfileModal.js
================================================
import React from 'react'
import Modal from 'react-modal'
import { NetflixLogo } from 'assets/images/'
import ProfileCard from 'components/UI/ProfileCard/ProfileCard'
import './ProfileModal.css'

import {
    Weird,
    Profile,
    Smile,
    Normal
} from 'assets/images/'


if (process.env.NODE_ENV !== 'test') {
    Modal.setAppElement('#root');
}

const profileModal = props => {
    const { modalOpen, profileClickHandler } = props

    return (
        <Modal
            className="ProfileModal"
            isOpen={modalOpen}
            contentLabel="Modal is open"
            shouldCloseOnEsc={false}
        >
            <img src={NetflixLogo} alt="Logo" />
            <div className="ProfileDiv">
                <h1>Who's watching?</h1>

                <div className="horizontalComp">
                    <ProfileCard profileImage={Profile} username="Pushpa" onClick={profileClickHandler} />
                    <ProfileCard profileImage={Weird} username="Shammy" onClick={profileClickHandler} />
                    <ProfileCard profileImage={Smile} username="PQ" onClick={profileClickHandler} />
                    <ProfileCard profileImage={Normal} username="Subhanga" onClick={profileClickHandler} />
                </div>

                <button className={"ProfileButton"}>
                    MANAGE PROFILES
                </button>
            </div>
        </Modal>

    )
}

export default profileModal

================================================
FILE: src/components/Modals/VideoModal/VideoModal.css
================================================
.ModalStyles {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    height: 45%;
    width: 100%;
    background: rgba(0,0,0,0.966);
    border: none;
    outline: none;
    font-size: 0.8rem;
    padding: 0;
}

.VideoDetailSection {
    height: 100%;
}

.shadowedSection > * {
    margin-bottom: 1rem;
}

.shadowedSection {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: linear-gradient(90deg, rgba(0,0,0,0.966) 55%, transparent);
    color: white;
    padding: 18px 0 12px 4%;
}

.shadowedSection div {
    width: 65%;
    color: #999;
} 

.horizontalStyles {
    display: flex;
}

.horizontalStyles > button {
    margin-right: 10px;
}

.Overview {
    overflow: scroll;
}

@media(min-width: 600px) {
    .ModalStyles {
        height: 60%;
        font-size: 1rem;
    }

    .shadowedSection {
        background: linear-gradient(90deg, rgba(0,0,0,0.966) 35%, transparent);
    }

    .shadowedSection div {
        width: 40%;
    } 

    .shadowedSection > * {
        margin-bottom: 2rem;
    }
    
}

@media(min-width: 1650px) {
    .ModalStyles {
        font-size: 1.6rem;
    }
}

================================================
FILE: src/components/Modals/VideoModal/VideoModal.js
================================================
import React from 'react'
import './VideoModal.css'

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTimes } from '@fortawesome/free-solid-svg-icons'
import Modal from 'react-modal'
import { getSeasonsOrMovieLength } from 'utils/time'
import { faPlay, faPlus } from '@fortawesome/free-solid-svg-icons'
import Button from 'components/UI/Button/Button'
import useHoverStyleButton from 'hooks/useHoverStyleButton'


if (process.env.NODE_ENV !== 'test') {
    Modal.setAppElement('#root');
}

// Don't move this to css; it has to be here for the shouldCloseOnOverlay prop to work 
const overlayStyle = {
    overlay: {
        backgroundColor: 'rgba(17,17,17,0.7)'
    }
}

const VideoModal = props => {
    const { videoDetailModal, closeModalHandler, videoInfo } = props
    const [buttonHovered, onButtonHoverHandler] = useHoverStyleButton({
        'playButton': true,
        'plusButton': true
    })

    const {
        vote_average, seasons, runtime,
        backdrop_path, poster_path, title, name,
        release_date, first_air_date,
        overview
    } = videoInfo

    const voteAverage = vote_average * 10
    const voteStyle = { color: voteAverage > 49 ? '#46d369' : 'red' }
    const videoTime = getSeasonsOrMovieLength(seasons, runtime)

    const styles = {
        backgroundImage: `url(https://image.tmdb.org/t/p/original/${backdrop_path || poster_path}`,
        backgroundSize: 'cover'
    }

    return (
        <Modal
            className="ModalStyles"
            style={overlayStyle}
            isOpen={videoDetailModal}
            contentLabel="Modal is open"
            shouldCloseOnOverlayClick
            onRequestClose={closeModalHandler}
        >
            <div className="VideoDetailSection" style={styles}>
                <FontAwesomeIcon onClick={closeModalHandler} style={{ color: 'white', float: 'right', padding: '14px', cursor: 'pointer' }}
                    size="2x"
                    icon={faTimes}
                />
                <div className="shadowedSection">
                    <h1>{title || name}</h1>
                    <div className="horizontalStyles">
                        <span style={voteStyle}>{`Rating: ${voteAverage}%`} &nbsp;</span>
                        <span>{(release_date || first_air_date).substring(0, 4)} &nbsp;</span>
                        {videoTime}
                    </div>
                    <div className="Overview">{overview}</div>
                    <div className="horizontalStyles">
                        <Button
                            backgroundColor="#fff"
                            textColor="rgb(24, 24, 24)"
                            playButton
                            height="38px"
                            width="138px"
                            image
                            icon={faPlay}
                            onButtonHover={() => onButtonHoverHandler('playButton')}
                            hoverStatus={buttonHovered['playButton']}>
                            Play
                        </Button>

                        <Button
                            backgroundColor="rgba(133, 133, 133, 0.6)"
                            textColor="white"
                            height="38px"
                            width="138px"
                            playButton
                            image
                            icon={faPlus}
                            onButtonHover={() => onButtonHoverHandler('plusButton')}
                            hoverStatus={buttonHovered['plusButton']}>
                            My List
                        </Button>
                    </div>
                </div>
            </div>
        </Modal>
    )
}

export default React.memo(VideoModal)

================================================
FILE: src/components/Navigation/Dropdown/Dropdown.css
================================================
.Dropdown {
    position: relative;
    display: inline-block;
    z-index: 100;
    cursor: pointer;
}

.FloatingBox {
    position: absolute;
    margin-left: 0;
    padding: 0;
    border: solid 1px rgba(255,255,255,.15);
    display: flex;
    flex-direction: column;
    background: #141414;
}

.FloatingBox > * {
    padding: 1vh;
}

.FloatingBox span:hover {
    text-decoration: underline;
}


================================================
FILE: src/components/Navigation/Dropdown/Dropdown.js
================================================
import React from 'react'
import './Dropdown.css'

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons";

const Dropdown = props => {
    const {
        iconHoveredInHandler, iconHoveredOutHandler, dropdown,
        floatingBoxHoveredInHandler, floatingBoxHoveredOutHandler, content,
        boxSizing, onItemClickCloseBoxHandler
    } = props

    const { iconHovered, floatingBoxHovered } = dropdown
    return (
        <div className="Dropdown">
            <FontAwesomeIcon
                size="lg"
                icon={faAngleDown}
                color="white"
                onMouseOver={iconHoveredInHandler}
                onMouseLeave={iconHoveredOutHandler}
            />

            {(iconHovered || floatingBoxHovered) && (
                <div className="FloatingBox" style={boxSizing}
                    onMouseOver={floatingBoxHoveredInHandler}
                    onMouseLeave={floatingBoxHoveredOutHandler}
                    onClick={onItemClickCloseBoxHandler}
                >
                    {content}
                </div>)}
        </div>
    )
}

export default Dropdown

================================================
FILE: src/components/StaticPages/ErrorPage/ErrorPage.css
================================================
.ErrorPage {
    height: 100vh;
    background: #141414;
    color: white;
    padding: 95px;
}

.ErrorPage-Items {
    text-align: center;
}

================================================
FILE: src/components/StaticPages/ErrorPage/ErrorPage.js
================================================
import React from 'react'
import './ErrorPage.css'

/**
 * I have to check for error message or simply an error because of I've handled errors in the 
 * reducers. The RTK documentation recommends checking for a promise rejection with value or 
 * with an error; thus, when it rejects with value, there will be a status_message, and if that
 * doesn't happen, there will simply be an error object 
 */
const ErrorPage = props => {
    const { errors } = props

    let errorObjs
    if (Array.isArray(errors)) {
        errorObjs = errors[0] || errors[1] || errors[2]
    }

    const errorType = errorObjs || errors
    const errorMessage = errorType.message ? errorType.message : errorType
    return (
        <div className="ErrorPage">
            <div className="ErrorPage-Items">
                <h2>No matching tiles found.</h2>
                <h2>{errorMessage}</h2>
            </div>
        </div>
    )
}

export default ErrorPage

================================================
FILE: src/components/StaticPages/LoadingScreen/LoadingScreen.css
================================================
.LoadingScreen {
    background: #141414;
    min-height: 100vh;
}

================================================
FILE: src/components/StaticPages/LoadingScreen/LoadingScreen.js
================================================
import React from 'react'
import './LoadingScreen.css'

const loadingScreen = props => {
    return (
        <div className="LoadingScreen">
        </div>
    )
}

export default loadingScreen

================================================
FILE: src/components/StaticPages/NotFoundPage/NotFoundPage.css
================================================
.Parent {
  position: relative;
  height: 100vh;
}

.Parent .notfound {
  position: absolute;
  left: 50%;
  top: 50%;
  -webkit-transform: translate(-50%, -50%);
      -ms-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
}

.notfound {
  max-width: 460px;
  width: 100%;
  text-align: center;
  line-height: 1.4;
}

.notfound .notfound-404 {
  position: relative;
  width: 180px;
  height: 180px;
  margin: 0px auto 50px;
}

.notfound .notfound-404>div:first-child {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: #ffa200;
  -webkit-transform: rotate(45deg);
      -ms-transform: rotate(45deg);
          transform: rotate(45deg);
  border: 5px dashed #000;
  border-radius: 5px;
}

.notfound .notfound-404>div:first-child:before {
  content: '';
  position: absolute;
  left: -5px;
  right: -5px;
  bottom: -5px;
  top: -5px;
  -webkit-box-shadow: 0px 0px 0px 5px rgba(0, 0, 0, 0.1) inset;
          box-shadow: 0px 0px 0px 5px rgba(0, 0, 0, 0.1) inset;
  border-radius: 5px;
}

.notfound .notfound-404 h1 {
  font-family: 'Cabin', sans-serif;
  color: #000;
  font-weight: 700;
  margin: 0;
  font-size: 90px;
  position: absolute;
  top: 50%;
  -webkit-transform: translate(-50%, -50%);
      -ms-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  left: 50%;
  text-align: center;
  height: 40px;
  line-height: 40px;
}

.notfound h2 {
  font-family: 'Cabin', sans-serif;
  font-size: 33px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 7px;
}

.notfound p {
  font-family: 'Cabin', sans-serif;
  font-size: 16px;
  color: #000;
  font-weight: 400;
}

.notfound a {
  font-family: 'Cabin', sans-serif;
  display: inline-block;
  padding: 10px 25px;
  background-color: #8f8f8f;
  border: none;
  border-radius: 40px;
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  text-decoration: none;
  -webkit-transition: 0.2s all;
  transition: 0.2s all;
}

.notfound a:hover {
  background-color: #2c2c2c;
}


================================================
FILE: src/components/StaticPages/NotFoundPage/NotFoundPage.js
================================================
import React from 'react'
import './NotFoundPage.css'

const notFoundPage = () => {
	return (
		<div class="Parent">
			<div class="notfound">
				<div class="notfound-404">
					<div></div>
					<h1>404</h1>
				</div>
				<h2>Page not found</h2>
				<p>The page you are looking for might have been removed, had its name changed, or doesn't exist.</p>
				<a href="/NetflixClone/login">Back to login</a>
			</div>
		</div>
	)
}

export default notFoundPage

================================================
FILE: src/components/UI/Button/Button.css
================================================
.Button {
    display: inline-block;
    border-radius: 3px;
    border: 0;
    outline: 0;
    font-size: 1rem; 
}

@media(max-height: 600px) {
    .Button {
        font-size: 0.9rem;
    }
}

================================================
FILE: src/components/UI/Button/Button.js
================================================
import React from "react";
import './Button.css'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

/**
 * A custom button component. It has a set color scheme and takes in height, eeight, and image
 * as props. Netflix seems to use a standard button component with different widths.
 */
const button = props => {
  let iconHolder = null;
  const {
    playButton, buttonSize, icon,
    height, width, backgroundColor, textColor,
    image, onButtonHover, hoverStatus
  } = props

  if (image) {
    iconHolder = (
      <FontAwesomeIcon
        style={playButton ? { marginRight: '15px' } : { marginLeft: "5px" }}
        size={buttonSize}
        icon={icon}
      />
    );
  }

  let orderButton = (
    <>
      {props.children}
      {iconHolder}
    </>
  )

  if (playButton) {
    orderButton = (
      <>
        {iconHolder}
        {props.children}
      </>
    )
  }

  const conditionalStyles = {
    height: height,
    width: width,
    backgroundColor: backgroundColor,
    color: textColor,
    opacity: !hoverStatus && '80%'
  };

  return (
    <button
      className="Button" style={conditionalStyles}
      onMouseEnter={onButtonHover}
      onMouseLeave={onButtonHover}>
      {orderButton}
    </button>
  );
};

export default button;



================================================
FILE: src/components/UI/CircularSoundButton/CircularSoundButton.css
================================================
.RoundButton {
    display: inline-block;
    border: 1px solid white;
    border-radius: 50%;
    background: transparent;
    color: white;
    text-align: center;
    font-size: 16px;
    outline: none;
}

@media(min-width: 1650px) {
    .RoundButton {
       font-size: 32px;
    }
}

================================================
FILE: src/components/UI/CircularSoundButton/CircularSoundButton.js
================================================
import React from 'react'
import './CircularSoundButton.css'

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faVolumeUp, faVolumeMute } from "@fortawesome/free-solid-svg-icons";

const circularSoundButton = props => {
    const { topTrailerSoundOn, topTrailerSoundButtonClickHandler } = props
    return (
        <button className="RoundButton" onClick={topTrailerSoundButtonClickHandler}>
            <FontAwesomeIcon icon={topTrailerSoundOn ? faVolumeUp : faVolumeMute} />
        </button>
    )
}

export default circularSoundButton

================================================
FILE: src/components/UI/DarkComponent/DarkComponent.js
================================================
import React from "react";

const darkComponentTextAlignStyles = {
  display: 'flex',
  flexDirection: 'column',
  textAlign: 'center'
}

const darkComponent = props => {
  const { topText, fontSize, bottomText, image } = props
  return (
    <>
      <div style={darkComponentTextAlignStyles}>
        {topText && (
          <h3 style={{ fontSize: fontSize }}>{topText}</h3>
        )}
        {bottomText && (
          <h3 style={{ fontSize: fontSize }}>{bottomText}</h3>
        )}
      </div>

      {image && <img src={image} alt="dark component" />}
    </>
  );
};

export default darkComponent;


================================================
FILE: src/components/UI/FAQComponent/FAQComponent.css
================================================
.faqComponent {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.8em 2.2em 0.8em 1.2em;
  width: 100%;
  margin-top: 10px;
  background: #303030;
  font-size: 20px;
  cursor: pointer;
}

.faq-animation-enter {
        opacity: 0;
}

.faq-animation-enter-active {
        opacity: 1;
	-webkit-animation: swing-in-top-bck 0.6s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;
	        animation: swing-in-top-bck 0.6s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;
}

.faq-animation-exit {
        opacity: 1;
}

.faq-animation-exit-active {
        opacity: 0;
	-webkit-animation: swing-out-top-bck 0.35s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;
	        animation: swing-out-top-bck 0.35s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;
}

@-webkit-keyframes swing-in-top-bck {
  0% {
    -webkit-transform: rotateX(70deg);
            transform: rotateX(70deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 0;
  }
  100% {
    -webkit-transform: rotateX(0deg);
            transform: rotateX(0deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 1;
  }
}
@keyframes swing-in-top-bck {
  0% {
    -webkit-transform: rotateX(70deg);
            transform: rotateX(70deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 0;
  }
  100% {
    -webkit-transform: rotateX(0deg);
            transform: rotateX(0deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 1;
  }
}

 @-webkit-keyframes swing-out-top-bck {
  0% {
    -webkit-transform: rotateX(0deg);
            transform: rotateX(0deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 1;
  }
  100% {
    -webkit-transform: rotateX(-100deg);
            transform: rotateX(-100deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 0;
  }
}

@keyframes swing-out-top-bck {
  0% {
    -webkit-transform: rotateX(0deg);
            transform: rotateX(0deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 1;
  }
  100% {
    -webkit-transform: rotateX(-100deg);
            transform: rotateX(-100deg);
    -webkit-transform-origin: top;
            transform-origin: top;
    opacity: 0;
  }
}

@media (min-width: 600px) {
  .faqComponent {
    width: 69%;
  }
}

@media(min-width: 1650px) {
    .faqComponent {
        font-size: 1.75rem;
      }      
}


================================================
FILE: src/components/UI/FAQComponent/FAQComponent.js
================================================
import React from "react";
import "./FAQComponent.css";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons";
import { CSSTransition } from 'react-transition-group'

const faqComponent = props => {
  const { faqOpenHandler, text, boxOpen, boxText } = props
  return (
    <>
      <div
        className="faqComponent"
        onClick={faqOpenHandler}
      >
        <div>{text}</div>
        <FontAwesomeIcon icon={boxOpen ? faMinus : faPlus} />
      </div>

      <CSSTransition in={boxOpen} classNames="faq-animation" timeout={500} unmountOnExit>
        <div className="faqComponent" style={{ marginTop: "1.5px" }}>
          {boxText}
        </div>
      </CSSTransition>
    </>
  );
};

export default faqComponent;


================================================
FILE: src/components/UI/ProfileCard/ProfileCard.css
================================================
.ProfileCard {
    width: 25%;
    margin: 15px;
    cursor: pointer;
}

.ProfileCardDropdown {
    width: 90%;
    display: flex;
    align-items: center;
}

.ProfileCardDropdown > * {
    margin-right: 10px;
}

.ProfileCardDropdown:hover span {
    text-decoration: underline;
}

.ProfileCardDropdown img {
    width: 20%;
    height: 100%;
}

.ProfileCard span {
    font-size: 3vw;
    color: grey;
}

.ProfileCard img {
    width: 100%;
    height: 100%;
    margin-bottom: 10px;
    border: 3px solid transparent;
}

.ProfileCard:hover img {
    border-color: white;
}

.ProfileCard:hover span {
    color: white;
}

@media(min-width: 600px) {
    .ProfileCard {
        width: 18%;
        height: 20%;
    }

    .ProfileCard span {
        font-size: 1.3vw;
    }
}




================================================
FILE: src/components/UI/ProfileCard/ProfileCard.js
================================================
import React from 'react'
import './ProfileCard.css'

const ProfileCard = props => {
    return (
        <div className={props.dropdown ? "ProfileCardDropdown": "ProfileCard"} onClick={props.onClick}>
            <img src={props.profileImage} alt="profile" />
            <span>{props.username}</span>
        </div>
    )
}

export default ProfileCard

================================================
FILE: src/components/Video/TopTrailerComponent/TopTrailerComponent.css
================================================
.VideoComponent {
    height: 580px;
    width: 100%;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    position: relative;
}

@media(min-width: 1650px) {
    .VideoComponent {
        height: 800px;
    }
}

================================================
FILE: src/components/Video/TopTrailerComponent/TopTrailerComponent.js
================================================
import React from 'react'
import './TopTrailerComponent.css'

const TopTrailerComponent = (props) => {
    const backgroundPicture = {
        backgroundImage: `url(${props.image})`
    }
    return (
        <div className="VideoComponent" style={backgroundPicture}>
            {props.children}
        </div>
    )
}

export default TopTrailerComponent

================================================
FILE: src/components/Video/VideoCard/VideoCard.css
================================================
.VideoCard {
    display: flex;
    align-items: center;
    cursor: pointer;
    height: 100%;
    width: 100%;
}

.VideoCard:hover .VideoInfo {
    display: block;
}

.NetflixOriginalCard:hover .VideoInfo {
    display: block;
}

.VideoInfo {
    display: none;
    padding: 10px;
    margin-top: 15px;
    font-weight: 400;
}

.VideoInfo h6 {
    margin: 0;
}

.horizontalStyle {
    display: flex;
    flex-wrap: wrap;
}

.horizontalStyle span {
    font-size: 0.5em;
}




================================================
FILE: src/components/Video/VideoCard/VideoCard.js
================================================
import React from 'react'
import './VideoCard.css'
import { getSeasonsOrMovieLength } from 'utils/time'

const videoCard = (props) => {
    const {
        name, poster_path, genres, runtime, seasons,
        vote_average
    } = props

    const image = `url(https://image.tmdb.org/t/p/w500/${poster_path})`

    const styles = {
        backgroundImage: image,
        backgroundSize: 'cover',
    }

    let timeSpan = getSeasonsOrMovieLength(seasons, runtime)
    const genreList = genres && genres.map((genre, index) => (
        <span key={genre.id}>
            {genre.name} {index !== genres.length - 1 ? '●' : null} &nbsp;
        </span>
    ))

    return (
        <div className="VideoCard" style={styles}>
            {genreList ? <div className="VideoInfo">
                <h6>{name}</h6>
                <div className="horizontalStyle">
                    <span>{vote_average} &nbsp;</span>
                    {timeSpan}
                </div>
                <div className="horizontalStyle">
                    {genreList}
                </div>
            </div> : null}
        </div>
    )
}

export default React.memo(videoCard)

================================================
FILE: src/components/Video/VideoCarousel/VideoCarousel.css
================================================
.CarouselParent {
    position: relative;
    margin-bottom: 30px;
}

.VideoCarousel {
    color: #e5e5e5;
    font-size: 2.4vw;
    overflow-x: auto;
    margin: 0 4% 0 4%;
}

.VideoCarousel::-webkit-scrollbar {
    display: none;
}

.items {
    display: inline-flex;
    height: 80px;
}

.netflix-items {
    display: inline-flex;
    height: 300px;
    overflow: x;
}

.item {
    position: relative;
    display: block;
    transition: transform 500ms;
    width: 100px;
    height: 100%;
    margin-right: 7px;
}

.items .item:focus,
.items .item:hover {
  transform: scale(1.1);
}

.netflix-item {
    position: relative;
    display: block;
    transition: transform 500ms;
    width: 130px;
    height: 100%;
    margin-right: 7px;
}

.netflix-items .netflix-item:focus,
.netflix-items .netflix-item:hover {
    transform: scale(1.1);
}

.CarouselParent:hover > .NavItem {
    display: none;
}

.NavItem {
    display: none;
}

@media(min-width: 600px) {
    .VideoCarousel {
        font-size: 1.4vw;
    }
    .items {
        height: 140px;
    }
    .item {
        width: 230px;
        height: 100%;
        margin-right: 7px;
    }
    .netflix-items {
        height: 420px;
    }
    .netflix-item {
        width: 230px;
        height: 100%;
        margin-right: 7px;
    }
    /* Different animation styles for the first and last items */
    .items .item:first-child:focus,
    .items .item:first-child:hover,
    .items .item:last-child:focus,
    .items .item:last-child:hover {
        transform: scale(1.1);
    }
    .item:first-child:focus~.item,
    .item:first-child:hover~.item {
        transform: translateX(10%);
    }
    .item:focus~.item,
    .item:hover~.item {
        transform: translateX(20%);
    }
    .items .item:not(:first-child):not(:last-child):focus,
    .items .item:not(:first-child):not(:last-child):hover {
        transform: scale(1.3);
    }
    .netflix-item:focus~.netflix-item,
    .netflix-item:hover~.netflix-item {
        transform: translateX(10%);
    }

    .CarouselParent:hover > .NavItem {
        display: block;
        position: absolute;
        top: 0;
        bottom: 0;
        width: 4%;
        color: white;
        background-color: transparent;
        outline: thin;
        border: none;
    }

    .CarouselParent:hover >  .NavItem:hover {
        background-color: black;
    }
    
    .NavItem {
        display: none;
    }
    
    .Left {
        left: 0;
    }
    
    .Right {
        right: 0;
    }
}

@media(min-width: 1650px) {
    .items {
        height: 240px;
    }
    .item {
        width: 330px;
        height: 100%;
        margin-right: 7px;
    }
    .netflix-items {
        height: 520px;
    }
    .netflix-item {
        width: 330px;
        height: 100%;
        margin-right: 7px;
    }
}

================================================
FILE: src/components/Video/VideoCarousel/VideoCarousel.js
================================================
import React, { useState, useRef } from 'react'
import './VideoCarousel.css'

import VideoCard from '../VideoCard/VideoCard'
import { buildVideoMetadata } from 'utils/transformations'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronRight, faChevronLeft } from "@fortawesome/free-solid-svg-icons";
import { scrollTo } from 'utils/animations'


const VideoCarousel = props => {
    const {
        carouselVideo, carouselName,
        carouselHoverHandler, videoInfo,
        carouselClickHandler
    } = props

    const carouselRef = useRef()
    const [disableScrolling, setDisableScrolling] = useState(false)

    const isNetflixOriginalCard = carouselName === "Netflix Originals" ? true : false
    const itemClass = []
    const itemsClass = []
    // Setting different transition styles for the netflix original card 
    if (!isNetflixOriginalCard) {
        itemClass.push("item")
        itemsClass.push("items")
    } else {
        itemClass.push("netflix-item")
        itemsClass.push("netflix-items")
    }

    const scrollOnAbsoluteButtonClick = scrollOffset => {
        setDisableScrolling(true)
        scrollTo(carouselRef.current, carouselRef.current.scrollLeft + scrollOffset, 1250, () => {
            setDisableScrolling(false)
        })
    }

    /**
     * The mediaType property only exists in the trending API call. For the sake of using the same 
     * function 'videoDetailRequest' for multiple individual API calls, I use this mediaType 
     * variable to judge whether a video is a TV show or a movie. This fails for API calls, such as
     * top rated or netflix originals. Thus, I have created a 'hacky' way of determining that by
     * checking if two properties exist: the first_air_date (only for tv shows) or the release_date
     * (only for movies).
     */
    const videoCards = carouselVideo.map(item => {
        const { mediaType, extraInfo } = buildVideoMetadata(item, videoInfo)
        return (
            item.poster_path && <div className={itemClass.join(' ')} key={item.id}
                onClick={() => carouselClickHandler(item.id, mediaType)}
                onMouseEnter={() => carouselHoverHandler(item.id, mediaType)}>
                <VideoCard
                    name={item.name || item.title}
                    vote_average={item.vote_average}
                    poster_path={item.poster_path}
                    netflixOriginalCard={isNetflixOriginalCard}
                    {...extraInfo}
                />
            </div>
        )
    })

    return (
        <div className="CarouselParent">
            <div className="VideoCarousel" ref={carouselRef}>
                <h4>{carouselName}</h4>
                <div className={itemsClass.join(" ")}>
                    {videoCards}
                </div>
            </div >

            <button
                className="Left NavItem"
                disabled={disableScrolling}
                onClick={() => scrollOnAbsoluteButtonClick(-1250)}>
                <FontAwesomeIcon icon={faChevronLeft} size="2x" />
            </button>
            <button
                className="Right NavItem"
                disabled={disableScrolling}
                onClick={() => scrollOnAbsoluteButtonClick(1250)}>
                <FontAwesomeIcon icon={faChevronRight} size="2x" />
            </button>
        </div>
    )
}

export default React.memo(VideoCarousel)

================================================
FILE: src/containers/Browse/Browse.js
================================================
import React, { useState } from 'react'

import ProfileModal from 'components/Modals/ProfileModal/ProfileModal'
import { useHistory } from 'react-router-dom'
import Layout from 'hoc/Layout'
import SearchContent from './SearchContent/SearchContent'
import { Home, Movies, Tv, LatestVideo, List } from './routes'

/**
 * Remember: the component where you want to use the context is the one which you wrap
 * with the provider.
 */
const Browse = props => {
    // Render different videoSections based on this route prop
    const { route } = props
    const initialState = localStorage.getItem('profileSelected') ? false : true
    const [modal, setModal] = useState(initialState)
    const history = useHistory()

    const profileClickHandler = () => {
        setModal(false)
        localStorage.setItem('profileSelected', true)
    }

    let browseContent
    if (route === '/browse') {
        browseContent = <Home />
    } else if (route === '/browse/movies') {
        browseContent = <Movies />
    } else if (route === '/browse/tv') {
        browseContent = <Tv />
    } else if (route === '/browse/latest') {
        browseContent = <LatestVideo />
    } else if(route === '/browse/list') {
        browseContent = <List />
    }
    else if (route === '/search') {
        browseContent = <SearchContent searchParam={history.location.search.substring(3)} />
    }

    return (
        <>
            <ProfileModal modalOpen={modal} profileClickHandler={profileClickHandler} />
            {!modal && <Layout>
                {browseContent}
            </Layout>}
        </>
    )
}

/**
 * Remember the thing which gave you trouble here. Never mutate state directly. Since I didn't create
 * a copy of the trending state array, I kept splicing each item all over the place, which 
 * caused unnecessary problems. 
 */

export default Browse

================================================
FILE: src/containers/Browse/BrowseContent/BrowseContent.css
================================================
.TextsAndButtons {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    color: white;
    margin: 0 4% 0 4%;
    height: 100%;
}

.verticalItem {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    margin-bottom: 80px;
    height: 200px;
    max-width: 50%;
}

.verticalItem h3 {
    margin: 0;
    font-size: 3.6vw;
}

.verticalItem span {
    font-size: 3.4vw;
}

.verticalItem > * {
    padding: 10px;
}

.horizontalButtonsHolder {
    display: flex;
    align-items: center;
    width: 100%;
    height: 15%;
}

.horizontalButtonsHolder > * {
    margin-right: 4px;
    font-weight: bold;
}

.horizontalButtonsHolder button {
    height: 37px;
    width: 118px;
    font-size: 0.8rem;
}

.Carousels {
    padding-bottom: 10px;
}

@media(min-width: 600px) {
    .horizontalButtonsHolder > * {
        margin-right: 10px;
        font-weight: bold;
    }

    .horizontalButtonsHolder button {
        height:38px;
        width: 138px;
        font-size: 1rem;
    }

    .verticalItem span {
        font-size: 1.4vw;
    }

    .verticalItem h3 {
        font-size: 1.6vw;
    }
}

@media(min-width: 1650px) {

}

================================================
FILE: src/containers/Browse/BrowseContent/BrowseContent.js
================================================
import React, { useState } from 'react'
import './BrowseContent.css'

import TopTrailerComponent from 'components/Video/TopTrailerComponent/TopTrailerComponent'
import Button from 'components/UI/Button/Button'
import VideoCarousel from 'components/Video/VideoCarousel/VideoCarousel'
import { faPlay, faInfoCircle } from '@fortawesome/free-solid-svg-icons'
import { buildVideoModal } from 'utils/transformations'
import useVideoInfoHandlers from 'hooks/useVideoInfoHandlers'
import ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'
import useHoverStyleButton from 'hooks/useHoverStyleButton'
import CircularSoundButton from 'components/UI/CircularSoundButton/CircularSoundButton'

const BrowseContent = props => {
    const [
        videoInfo, videoInfoError, detailModal, cardClickHandler,
        cardHoverHandler, closeModalHandler
    ] = useVideoInfoHandlers()

    const [buttonHovered, onButtonHoverHandler] = useHoverStyleButton({
        'playButton': true,
        'infoButton': true
    })

    const [topTrailerSoundOn, setTopTrailerSoundOn] = useState(true)

    const { videoSections } = props
    const [firstVideo, ...remainingVideos] = videoSections[0].videos
    const imageUrl = firstVideo ? `https://image.tmdb.org/t/p/original/${firstVideo.poster_path}` : null

    const detailModalComponent = buildVideoModal(detailModal, videoInfo, { closeModalHandler })

    const carousels = videoSections.map((videoSection, index) => (
        <VideoCarousel key={videoSection.title}
            carouselName={videoSection.title}
            carouselVideo={index === 0 ? remainingVideos : videoSection.videos}
            carouselClickHandler={cardClickHandler}
            carouselHoverHandler={cardHoverHandler}
            videoInfo={videoInfo}
            videoDetailModal={detailModal}
        />
    ))

    const topTrailerSoundButtonClickHandler = () => setTopTrailerSoundOn(prevState => !prevState)

    return (!videoInfoError ? (
        <div className="BrowseContent">
            <TopTrailerComponent image={imageUrl} >
                <div className="TextsAndButtons">
                    <div className="verticalItem" >
                        <h3 > {firstVideo ? (firstVideo.name || firstVideo.title) : null} </h3>
                        <span > {firstVideo ? firstVideo.overview : null} </span>
                        <div className="horizontalButtonsHolder">
                            <Button backgroundColor="#fff"
                                textColor="rgb(24, 24, 24)"
                                playButton image icon={faPlay}
                                onButtonHover={
                                    () => onButtonHoverHandler('playButton')}
                                hoverStatus={buttonHovered['playButton']} >
                                Play
                            </Button>

                            <Button backgroundColor="rgba(133, 133, 133, 0.6)"
                                textColor="white"
                                playButton image icon={faInfoCircle}
                                onButtonHover={
                                    () => onButtonHoverHandler('infoButton')}
                                hoverStatus={buttonHovered['infoButton']} >
                                More Info </Button>
                        </div>
                    </div>
                    <div className="verticalItem" >
                        <CircularSoundButton
                            topTrailerSoundButtonClickHandler={topTrailerSoundButtonClickHandler}
                            topTrailerSoundOn={topTrailerSoundOn} />
                    </div>
                </div>
            </TopTrailerComponent>
            <div className="Carousels">
                {carousels}
            </div>
            {detailModalComponent}

        </div>) :
        <ErrorPage errors={videoInfoError} />
    )
}

export default BrowseContent

================================================
FILE: src/containers/Browse/SearchContent/SearchContent.css
================================================
.SearchContent {
    padding: 95px 10px;
    color: white;
}

.SearchGrid {
    display: grid;
    grid-template-columns: repeat(3, 31.5%);
    grid-auto-rows: 80px;
    grid-gap: 10px;
    height: 100%;
}

.SearchGrid .GridItem:focus,
.SearchGrid .GridItem:hover {
  transform: scale(1.1);
}

.GridItem {
    transition: transform 500ms;
}

.GridItem > * {
    margin: 0;
}

@media(min-width: 600px) {
    .SearchContent {
        margin: 0 10px;
    }   

    .SearchGrid {
        grid-template-columns: repeat(6, 16%);
        grid-auto-rows: 140px;
    }
}

@media(min-width: 1650px) {
    .SearchContent {
        font-size: 1.6vw;
    }
}

================================================
FILE: src/containers/Browse/SearchContent/SearchContent.js
================================================
import React, { useState, useEffect, useCallback } from 'react'
import './SearchContent.css'

import axios from 'baseAxios'
import VideoCard from 'components/Video/VideoCard/VideoCard'
import { debounce } from 'lodash'
import { buildVideoMetadata, buildVideoModal } from 'utils/transformations'
import { sortVideosByPopularity } from 'utils/sorting'
import useVideoInfoHandlers from 'hooks/useVideoInfoHandlers'
import ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'

const SearchContent = props => {
    const [searchedVideoList, setSearchedVideoList] = useState([])
    const [searchedError, setSearchedError] = useState(null)
    const [loading, setLoading] = useState(true)
    const { searchParam } = props
    const [
        videoInfo, videoInfoError, detailModal, cardClickHandler,
        cardHoverHandler, closeModalHandler
    ] = useVideoInfoHandlers()

    const getSearchMovies = async (searchItem) => {
        const movieUrl = `search/movie?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&query=${searchItem}&page=1&include_adult=false`
        const tvUrl = `search/tv?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&page=1&query=${searchItem}&include_adult=false`

        try {
            const responses = await Promise.all(
                [
                    axios.get(movieUrl).then(response => response.data.results),
                    axios.get(tvUrl).then(response => response.data.results)
                ]
            )

            setSearchedVideoList([...responses[0], ...responses[1]])
            setLoading(false)
        } catch (error) {
            setSearchedError(error)
            setLoading(false)
        }
    }

    const delayedAPICall = useCallback(debounce(getSearchMovies, 1000), [])

    useEffect(() => {
        delayedAPICall(searchParam)
        return () => {
            delayedAPICall.cancel()
        }
    }, [delayedAPICall, searchParam])

    const detailModalComponent = buildVideoModal(detailModal, videoInfo, { closeModalHandler })

    // we check if the video has a poster or a mediaType because these properties are missing in 
    // some tiles, and, generally, a missing mediaType means there is no video overview or 
    // information. It's an easy fix to skip these little known movies, as the API itself 
    // doesn't provide information. 
    let movieCards
    if (!loading) {
        searchedVideoList.sort(sortVideosByPopularity)
        movieCards = searchedVideoList.map(video => {
            const { mediaType, extraInfo } = buildVideoMetadata(video, videoInfo)
            return video.poster_path && mediaType && (
                <div className="GridItem"
                    key={video.id}
                    onClick={
                        () => cardClickHandler(video.id, mediaType)}
                    onMouseEnter={
                        () => cardHoverHandler(video.id, mediaType)} >
                    <VideoCard name={video.name || video.title}
                        vote_average={video.vote_average}
                        poster_path={video.poster_path}
                        netflixOriginalCard={false} {...extraInfo} />
                </div>
            )
        })
    }

    return (
        (!videoInfoError && !searchedError) ? (
            <div className="SearchContent">
                <div className="SearchGrid">
                    {movieCards}
                </div>
                {detailModalComponent}
            </div>) :
            <ErrorPage errors={videoInfoError || searchedError} />
    )
}

export default SearchContent

================================================
FILE: src/containers/Browse/routes/Home.js
================================================
import React, { useEffect } from 'react'

import { useSelector, useDispatch } from 'react-redux'
import BrowseContent from '../BrowseContent/BrowseContent'
import ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'
import { fetchTrending, selectAllTrendingVideos, selectTrendingError } from 'store/reducers/slices/trendingSlice'
import { fetchTopRated, selectAllTopRatedVideos, selectTopRatedError } from 'store/reducers/slices/topratedSlice'
import { fetchNetflixOriginals, selectAllNetflixOriginals, selectNetflixError } from 'store/reducers/slices/netflixOriginalsSlice'

const Home = () => {
    const trendingVideos = useSelector(selectAllTrendingVideos)
    const topRatedVideos = useSelector(selectAllTopRatedVideos)
    const netflixOriginals = useSelector(selectAllNetflixOriginals)

    const trendingError = useSelector(selectTrendingError)
    const topRatedError = useSelector(selectTopRatedError)
    const netflixError = useSelector(selectNetflixError)
    const dispatch = useDispatch()

    useEffect(() => {
        dispatch(fetchTrending())
        dispatch(fetchTopRated())
        dispatch(fetchNetflixOriginals())
    }, [dispatch])


    let videoSections = []
    let component 
    if (!trendingError && !topRatedError && !netflixError) {
        videoSections.push({ title: "Trending", videos: trendingVideos })
        videoSections.push({ title: "Top Rated", videos: topRatedVideos })
        videoSections.push({ title: "Netflix Originals", videos: netflixOriginals })
        component = <BrowseContent videoSections={videoSections} />
    } else {
        component = (
            <ErrorPage errors={[trendingError, topRatedError, netflixError]} />
        )
    }

    return component
}

export default Home

================================================
FILE: src/containers/Browse/routes/LatestVideo.js
================================================
import React, { useEffect } from 'react'

import { useSelector, useDispatch } from 'react-redux'
import { fetchLatestVideos, selectLatestVideos } from 'store/reducers/slices/latestVideoSlice'
import BrowseContent from '../BrowseContent/BrowseContent'
import LoadingScreen from 'components/StaticPages/LoadingScreen/LoadingScreen'
import ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'

const LatestVideo = () => {
    const { latestVideos, status, error } = useSelector(selectLatestVideos)
    const dispatch = useDispatch()

    useEffect(() => {
        if (status === 'idle') {
            dispatch(fetchLatestVideos())
        }
    }, [dispatch, status])

    let browseContent
    if (status === 'success') {
        browseContent = <BrowseContent videoSections={latestVideos} />
    } else if (status === 'idle' || status === 'loading') {
        browseContent = <LoadingScreen />
    } else if (status === 'error') {
        browseContent = <ErrorPage errors={error} />
    }

    return browseContent
}

export default LatestVideo

================================================
FILE: src/containers/Browse/routes/List.js
================================================
import React from 'react'

const List = () => {
    return (
        <div style={{color: 'white', paddingTop: '95px', textAlign: 'center'}}>Coming soon</div>
    )
}

export default List

================================================
FILE: src/containers/Browse/routes/Movies.js
================================================
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'

import { fetchMoviesByGenre, selectMoviesByGenre } from 'store/reducers/slices/moviesByGenreSlice'
import BrowseContent from '../BrowseContent/BrowseContent'
import LoadingScreen from 'components/StaticPages/LoadingScreen/LoadingScreen'
import ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'

const Movies = () => {
    const {genres, status, error} = useSelector(selectMoviesByGenre)
    const dispatch = useDispatch()

    useEffect(() => {
        if (status === 'idle') {
            dispatch(fetchMoviesByGenre())
        }
    }, [dispatch, status])

    let browseContent
    if (status === 'success') {
        browseContent = <BrowseContent videoSections={genres} />
    } else if (status === 'idle' || status === 'loading') {
        browseContent = <LoadingScreen />
    } else if(status === 'error') {
        browseContent = <ErrorPage errors={error}/>
    }

    return browseContent
}

export default Movies

================================================
FILE: src/containers/Browse/routes/Tv.js
================================================
import React, { useEffect } from 'react'

import { useSelector, useDispatch } from 'react-redux'
import { fetchTvShowsByGenres, selectTvByGenre } from 'store/reducers/slices/tvByGenreSlice'
import BrowseContent from '../BrowseContent/BrowseContent'
import LoadingScreen from 'components/StaticPages/LoadingScreen/LoadingScreen'
import ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'

const Tv = () => {
    const {genres, status, error} = useSelector(selectTvByGenre)
    const dispatch = useDispatch()

    useEffect(() => {
        if (status === 'idle') {
            dispatch(fetchTvShowsByGenres())
        }
    }, [dispatch, status])

    let browseContent
    if (status === 'success') {
        browseContent = <BrowseContent videoSections={genres} />
    } else if (status === 'idle' || status === 'loading') {
        browseContent = <LoadingScreen />
    } else if(status === 'error') {
        browseContent = <ErrorPage errors={error}/>
    }

    return browseContent
}

export default Tv

================================================
FILE: src/containers/Browse/routes/index.js
================================================
import Home from './Home'
import Movies from './Movies'
import Tv from './Tv'
import LatestVideo from './LatestVideo'
import List from './List'

export {
    Home,
    Movies,
    Tv,
    LatestVideo, 
    List
}


================================================
FILE: src/containers/LandingSection/LandingSection.css
================================================
.landingSection {
  height: 470px;
  width: 100%;
  border-bottom: 8px solid #222;
}

.landingTexts {
  display: flex;
  width: 80%;
  margin: auto;
  flex-direction: column;
  align-items: center;
  position: relative;
  top: calc(30% - 90px);
  text-align: center;
  color: #fff;
}

.landingTexts > * {
  margin-top: 15px;
}

.landingTexts h3 {
  margin-top: 0px;
  font-weight: 400;
}

.ButtonSticker {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 100%;
}

.TextField {
  background-color: #fff;
  width: 80%;
  max-width: 500px;
}

.TextField:active {
  color: white;
  border: none;
}

.tv-section {
  height: 410px;
  background-color: black;
  border-bottom: 8px solid #333;
  color: #fff;
}

.tv-inner, .responsive-tv-inner {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 80%;
  margin: auto;
}

.tv-section img {
  width: 70%;
}

.faq-section {
  background-color: black;
  border-bottom: 8px solid #333;
  color: #fff;
}

.GetStartedComponent {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin-top: 30px;
  width: 100%;
}

.GetStartedComponent > * {
  margin-bottom: 15px;
}

.ButtonSticker button {
    margin-top: 15px;
}

@media (min-width: 600px) {
  .landingSection {
    height: 620px;
  }

  .landingTexts {
    width: 70%;
    top: calc(40% - 45px);
  }

  .GetStartedComponent {
    width: 100%;
  }

  .tv-section {
    height: 450px;
  }

  .tv-section img {
    width: 45%;
  }
}

@media(min-width: 950px) {
    .ButtonSticker {
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
    }

    .ButtonSticker button {
        margin-top: 0;
    }
}

@media(min-width: 1199px) {
  .responsive-tv-inner {
    flex-direction: row;
    justify-content: space-between;
  }
}

@media(min-width: 1650px) {
    .landingSection,
    .tv-section,
    .faq-section {
        font-size: 1.75rem;
    }

    .tv-section img {
        width: 35%;
    }      
}


================================================
FILE: src/containers/LandingSection/LandingSection.js
================================================
import React, { useState } from "react";
import "./LandingSection.css";

import NavBar from "../NavBar/NavBar";
import LandingPage from "assets/images/landingPage.jpg";
import { TextField } from "@material-ui/core";
import Button from "components/UI/Button/Button";
import DarkComponent from "components/UI/DarkComponent/DarkComponent";
import FAQComponent from "components/UI/FAQComponent/FAQComponent";
import { Link } from "react-router-dom";
import { faChevronRight } from "@fortawesome/free-solid-svg-icons";
import { texualMaterial } from './LandingSectionTexts'

/**
 * The 'homepage' of this project. Uses an object state to
 * dynamically determine which frequently asked box is open.
 * Renders components like navbars, darkComponents, etc and
 * passes the relevent props whenever needed.
 */
const LandingSection = props => {
    const [faqBoxOpen, setFaqBoxOpen] = useState({});

    const faqOpenHandler = boxNumber => {
        setFaqBoxOpen(prevBoxState => ({
            [boxNumber]: !prevBoxState[boxNumber]
        }));
    };

    const darkComponents = texualMaterial.darkComponent.map(darkcomp => (
        <div className="tv-section" key={darkcomp.id}>
            <div className="responsive-tv-inner">
                <DarkComponent
                    topText={darkcomp.topText}
                    bottomText={darkcomp.bottomText}
                    image={darkcomp.image}
                />
            </div>
        </div>
    ))

    const faqComponents = texualMaterial.faqComponent.map(faqcomp => (
        <FAQComponent
            key={faqcomp.id}
            text={faqcomp.text}
            boxOpen={faqBoxOpen[`box${faqcomp.id}`]}
            faqOpenHandler={() => faqOpenHandler(`box${faqcomp.id}`)}
            boxText={faqcomp.boxText}
        />
    ))

    return (
        <>
            <div
                className="landingSection"
                style={{ backgroundImage: `url(${LandingPage})` }}
            >
                <NavBar loginButton />
                <div className="landingTexts">
                    <h1>Unlimited movies, TV shows, and more.</h1>
                    <h3>Watch anywhere. Cancel anytime.</h3>
                    <h3>
                        Ready to watch? Enter your email to create or restart your
                        membership.
                     </h3>

                    <div className="ButtonSticker">
                        <TextField
                            className="TextField"
                            label="Email Address"
                            variant="filled"
                            color="secondary"
                        />

                        <Link to="/login">
                            <Button
                                height="56px"
                                width="150px"
                                image
                                icon={faChevronRight}
                                backgroundColor="#e50914"
                                textColor="#fff"
                                buttonSize="xs"
                            >
                                GET STARTED
                            </Button>
                        </Link>
                    </div>
                </div>
            </div>

            {darkComponents}

            <div className="faq-section">
                <div className="tv-inner">
                    <DarkComponent
                        fontSize="2.5rem"
                        topText="Frequently Asked Questions"
                    />

                    {faqComponents}

                    <div className="GetStartedComponent">
                        <h3>
                            Ready to watch? Enter your email to create or restart your
                            membership.
                        </h3>

                        <div className="ButtonSticker">
                            <TextField
                                className="TextField"
                                label="Email Address"
                                variant="filled"
                                color="secondary"
                            />

                            <Link to="/login">
                                <Button
                                    height="56px"
                                    width="150px"
                                    image
                                    icon={faChevronRight}
                                    backgroundColor="#e50914"
                                    textColor="#fff"
                                    buttonSize="xs"
                                >
                                    GET STARTED
                             </Button>
                            </Link>
                        </div>
                    </div>
                </div>
            </div>
        </>
    );
};

export default LandingSection;


================================================
FILE: src/containers/LandingSection/LandingSectionTexts.js
================================================
// A file for all texual material used in 'LandingSection.js'
export const texualMaterial = {
    darkComponent: [
        {
            id: 1, 
            topText: "Enjoy on your TV.",
            bottomText: "Watch on Smart TVs, Playstation, Xbox, Chromecast, Apple TV, Blu-ray players, and more.",
            image: "https://assets.nflxext.com/ffe/siteui/acquisition/ourStory/fuji/desktop/tv.png"
        },
        {
            id: 2,
            topText: "Download your shows to watch offline.",
            bottomText: "Save your favorites easily and always have something to watch.",
            image: "https://assets.nflxext.com/ffe/siteui/acquisition/ourStory/fuji/desktop/mobile-0819.jpg"
        },
        {
            id: 3, 
            topText: "Watch everywhere.",
            bottomText: "Stream unlimited movies and TV shows on your phone, tablet, laptop, and TV without paying more.",
            image: "https://assets.nflxext.com/ffe/siteui/acquisition/ourStory/fuji/desktop/device-pile.png"
        },
    ],

    faqComponent: [
        {
            id: 1, 
            text: "What is Netflix?",
            boxText: "Netflix is a streaming service that offers a wide variety of award-winning TV shows, movies, anime, documentaries, and more on thousands of internet-connected devices. You can watch as much as you want, whenever you want without a single commercial – all for one low monthly price. There's always something new to discover and new TV shows and movies are added every week!"
        },

        {
            id: 2, 
            text: "How much does Netflix cost?",
            boxText: "Watch Netflix on your smartphone, tablet, Smart TV, laptop, or streaming device, all for one fixed monthly fee. Plans range from US$8.99 to US$15.99 a month. No extra costs, no contracts.",
        },

        {
            id: 3, 
            text: "Where can I watch?",
            boxText: "Watch anywhere, anytime, on an unlimited number of devices. Sign in with your Netflix account to watch instantly on the web at netflix.com from your personal computer or on any internet-connected device that offers the Netflix app, including smart TVs, smartphones, tablets, streaming media players and game consoles. You can also download your favorite shows with the iOS, Android, or Windows 10 app. Use downloads to watch while you're on the go and without an internet connection. Take Netflix with you anywhere."
        }, 

        {
            id: 4, 
            text: "How can I cancel?",
            boxText: "Netflix is flexible. There are no pesky contracts and no commitments. You can easily cancel your account online in two clicks. There are no cancellation fees – start or stop your account anytime."
        }, 

        {
            id: 5, 
            text: "What can I watch on Netflix?",
            boxText: "Netflix has an extensive library of feature films, documentaries, TV shows, anime, award-winning Netflix originals, and more. Watch as much as you want, anytime you want."
        }
    ]
}

================================================
FILE: src/containers/Login/Login.css
================================================
.Login {
  width: 100%;
  height: 100vh;
}

.Login img {
  height: 75px;
  width: 178px;
  margin-left: 20px;
}

.LoginCard {
  background-color: rgba(0, 0, 0, 0.75);
  border-radius: 4px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: calc(100% - 75px);
  color: #fff;
  padding: 40px 38px 40px;
}

.LoginCard form > * {
  margin-top: 25px;
}

.LoginCard form span {
    color: red;
    font-size: 13px;
}

.LoginCard button {
  margin-top: 25px;
}

.textField {
  box-sizing: border-box;
  border-radius: 2px;
  width: 100%;
}

.textField input {
  color: white;
}

.HorizontalDiv {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

@media (min-width: 600px) {
    .LoginCard {
      width: 450px;
      height: 680px;
      padding: 60px 68px 40px;
      margin: auto;
    }
}

@media(min-width: 1650px) {
    .LoginCard {
        width: 550px;
        height: 880px;
        padding: 60px 68px 40px;
        margin: auto;
    }

    .LoginCard form span {
        font-size: 20px;
    }
}

================================================
FILE: src/containers/Login/Login.js
================================================
import React, { useState, useContext } from "react";
import "./Login.css";

import { NetflixLogo, LoginBackground } from "assets/images/";
import { TextField } from "@material-ui/core";
import Button from "components/UI/Button/Button";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Checkbox from "@material-ui/core/Checkbox";
import { useHistory } from "react-router-dom";
import { AuthenticationContext } from 'context/Authentication'
import { validEmailAndPhoneNumber } from 'utils/validation'


/**
 * The login component, which validates the email and password
 * fields and uses a controlled form. Uses material UI for the
 * textfields.
 */
const Login = props => {
  const [form, setForm] = useState({
    email: {
      value: '',
      touched: false,
      valid: false
    },

    password: {
      value: '',
      touched: false,
      valid: false
    },

    onSubmitInvalid: false
  })

  const history = useHistory()
  const authContext = useContext(AuthenticationContext)

  const inputChangeHandler = event => {
    const { name, value } = event.target;
    if (name === "email") {
      setForm(prevForm => ({
        ...prevForm,
        email: {
          ...prevForm.email,
          value: value, touched: true, valid: value.length > 0 && validEmailAndPhoneNumber(value)
        }
      }))

    } else if (name === "password") {
      setForm(prevForm => ({
        ...prevForm,
        password: {
          ...prevForm.password, value: value, touched: true,
          valid: value.length >= 4 && value.length <= 60
        }
      }))
    }
  };

  // For setting error spans once any of the fields are touched.
  const fieldBlurHandler = event => {
    if (event.target.name === 'email') {
      if (form.email.value === '') {
        setForm(prevForm => ({
          ...prevForm,
          email: { ...prevForm.email, touched: true }
        }))
      }
    }

    if (event.target.name === 'password') {
      if (form.password.value === '') {
        setForm(prevForm => ({
          ...prevForm,
          password: { ...prevForm.password, touched: true }
        }))
      }
    }
  };

  let [emailSpan, passwordSpan] = [null, null];

  if ((!form.email.valid && form.email.touched) || (form.onSubmitInvalid && !form.email.valid)) {
    emailSpan = <span>Please enter a valid email or phone number.</span>
  }

  if ((!form.password.valid && form.password.touched) || (form.onSubmitInvalid && !form.password.valid)) {
    passwordSpan = <span>Your password must contain between 4 and 60 characters.</span>
  }

  const formSubmitHandler = (event) => {
    event.preventDefault()
    if (!form.email.valid || !form.password.valid) {
      setForm(prevForm => ({ ...prevForm, onSubmitInvalid: true }))
    } else {
      authContext.login()
      history.push("/browse");
    }
  }

  return (
    <div
      className="Login"
      style={{ backgroundImage: `url(${LoginBackground})` }}
    >
      <img src={NetflixLogo} alt="Logo" />
      <div className="LoginCard">
        <h1>Sign In</h1>
        <form onSubmit={formSubmitHandler}>
          <TextField
            name="email"
            className="textField"
            label="Email or phone number"
            variant="filled"
            type="text"
            style={{ backgroundColor: "#333" }}
            color="secondary"
            value={form.email.value}
            onChange={inputChangeHandler}
            onBlur={fieldBlurHandler}
            autoComplete="off"
            InputLabelProps={{
              style: { color: "#8c8c8c" }
            }}
          />

          {emailSpan}

          <TextField
            name="password"
            className="textField"
            label="Password"
            variant="filled"
            type="password"
            style={{ backgroundColor: "#333" }}
            color="secondary"
            value={form.password.value}
            onChange={inputChangeHandler}
            onBlur={fieldBlurHandler}
            autoComplete="off"
            InputLabelProps={{
              style: { color: "#8c8c8c" }
            }}
          />

          {passwordSpan}

          <Button
            height="45px" width="100%"
            backgroundColor="#e50914"
            textColor="#fff">
            Sign In
          </Button>

        </form>

        <div className="HorizontalDiv">
          <FormControlLabel
            style={{ marginLeft: "-12px" }}
            control={
              <Checkbox style={{ color: "rgb(229, 9, 20)" }} name="checkedB" />
            }
            label="Remember Me"
          />
          <span>Need help?</span>
        </div>
      </div>
    </div>
  );
};

export default Login;


================================================
FILE: src/containers/NavBar/NavBar.css
================================================
.NavBar {
  width: 100%;
  height: 45px;
  display: flex;
  align-items: center;
  position: absolute;
  top: 0;
  left: 0;
  padding-top: 5px;
  font-size: 3vw;
  z-index: 100;
  box-sizing: border-box;
}

.Sticky {
  position: fixed;
  top: 0;
}

.NavBar img {
  height: 100%;
  width: 108px;
}

.inactive {
  color: #e5e5e5;
  margin: 0;
  padding-right: 15px;
  cursor: pointer;
  font-weight: lighter;
  transition: color .4s;
  text-decoration: none;
}

.NavBar a:hover {
  color: grey;
}

.active {
  font-weight: normal;
  color: white;
}

.LinkContainer {
  margin-right: auto;
}

.Horizontal {
  display: none;
}

.Vertical {
  display: block;
}

.ExtraOptions {
  display: none;
}

.DropdownNav {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.OptionsContainer {
  display: inline-flex;
  align-items: center;
  color: white;
}

.OptionsContainer > * {
  color: white;
  cursor: pointer;
  margin-right: 15px;
}

.NavBar > * {
  padding: 5px;
}

@media(min-width: 600px) {
  .NavBar {
    font-size: 1vw;
  }

  .NavBar img {
    width: 168px;
  }

  .NavBar > * {
    margin: 10px;
  }

  .OptionsContainer {
    display: flex;
    align-items: center;
    color: white;
    margin: 0;
    height: 80%;
  }

  .LinkContainer {
    margin-right: auto;
  }

  .OptionsContainer > * {
    margin-right: 25px;
  }

  .ExtraOptions {
    display: block;
  }
}

@media(min-width: 860px) {
    .Horizontal {
        display: block;
    }
    
    .Vertical {
        display: none;
    }    
}

@media(min-width: 1650px) {
    .NavBar {
        height: 90px;
        font-size: 1.2vw;
    }
}

================================================
FILE: src/containers/NavBar/NavBar.js
================================================
import React, { useState, useEffect, useCallback } from "react";
import "./NavBar.css";

import { NetflixLogo } from "assets/images/";
import Button from "components/UI/Button/Button";
import { NavLink, Link } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faGift, faBell } from "@fortawesome/free-solid-svg-icons";
import Search from '../Search/Search'


const NavBar = props => {
  const { navigation, profileDropdown, navDropdown, loginButton } = props
  const [isNavbarAtTop, setIsNavbarAtTop] = useState(true)

  const scrollNavbarStateHandler = useCallback(() => {
    const navbarAtTop = window.scrollY < 45
    if (navbarAtTop !== isNavbarAtTop) {
      setIsNavbarAtTop(navbarAtTop)
    }
  }, [isNavbarAtTop])

  useEffect(() => {
    document.addEventListener('scroll', scrollNavbarStateHandler)
    return () => {
      document.removeEventListener('scroll', scrollNavbarStateHandler)
    }
  }, [scrollNavbarStateHandler])

  let navTiles = null
  let flexStyle = { justifyContent: 'space-between', backgroundColor: !isNavbarAtTop && 'black' }

  if (navigation) {
    navTiles = (
      <>
        <div className="LinkContainer">
          <div className="Horizontal">
            <NavLink className="inactive" activeClassName="active" to="/browse" exact>Home</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/tv" exact>TV Shows</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/movies" exact>Movies</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/latest" exact>Latest</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/list" exact>My List</NavLink>
          </div>
          <div className="Vertical">
            {navDropdown}
          </div>
        </div>

        <div className="OptionsContainer">
          <Search />
          <span className="ExtraOptions" style={{ fontWeight: '350' }}>KIDS</span>
          <FontAwesomeIcon className="ExtraOptions" size="lg" icon={faGift} />
          <FontAwesomeIcon className="ExtraOptions" size="lg" icon={faBell} />
          {profileDropdown}
        </div>
      </>
    )
  }

  return (
    <div className="NavBar Sticky" style={flexStyle}>
      <img src={NetflixLogo} alt="Logo" />
      {navTiles}
      {loginButton && <Link to="/login">
        <Button
          height="34px"
          width="75px"
          backgroundColor="#e50914"
          textColor="#fff"
        >
          Sign In
      </Button>
      </Link>}
    </div>
  );
};

export default NavBar;


================================================
FILE: src/containers/Search/Search.css
================================================
.SearchBox {
    display: inline-block;
}

.Holder {
    display: flex;
    align-items: center;
    border: solid 1px rgba(255,255,255,.85);
    height: 100%;
    width: 100%;
    background-color: black;
    height: 30px;
    width: 160px
}

.Holder svg {
    padding: 15px 5px;
}

.Holder input {
    border: none;
    outline: none;
    color: white;
    width: 100%;
    background-color: transparent;
}

.Holder input:hover {
    color: white;
}

.search-animation-enter {
    opacity: 0;
}

.search-animation-enter-active {
    opacity: 1;
    -webkit-animation: scale-up-hor-right 0.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
	animation: scale-up-hor-right 0.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}

.search-animation-exit {
    opacity: 1;
}

.search-animation-exit-active {
	-webkit-animation: scale-down-hor-right 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
	        animation: scale-down-hor-right 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}


@-webkit-keyframes scale-up-hor-right {
0% {
    -webkit-transform: scaleX(0.4);
            transform: scaleX(0.4);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
100% {
    -webkit-transform: scaleX(1);
            transform: scaleX(1);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
}
@keyframes scale-up-hor-right {
0% {
    -webkit-transform: scaleX(0.4);
            transform: scaleX(0.4);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
100% {
    -webkit-transform: scaleX(1);
            transform: scaleX(1);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
}

@-webkit-keyframes scale-down-hor-right {
0% {
    -webkit-transform: scaleX(1);
            transform: scaleX(1);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
100% {
    -webkit-transform: scaleX(0.3);
            transform: scaleX(0.3);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
}
@keyframes scale-down-hor-right {
0% {
    -webkit-transform: scaleX(1);
            transform: scaleX(1);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
100% {
    -webkit-transform: scaleX(0.3);
            transform: scaleX(0.3);
    -webkit-transform-origin: 100% 100%;
            transform-origin: 100% 100%;
}
}

@media(min-width: 600px) {
    .Holder {
        height: 30px;
        width: 250px
    }
}

@media(min-width: 1650px) {
    .Holder {
        height: 50px;
        width: 400px;
    }

    .Holder svg {
        padding: 35px 15px;
    }

    .Holder input {
        font-size: 1.1vw;
    }
}

================================================
FILE: src/containers/Search/Search.js
================================================
import React, { useState, useEffect, useRef } from 'react'
import './Search.css'
import { useHistory } from 'react-router-dom'

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch, faTimes } from "@fortawesome/free-solid-svg-icons";
import { CSSTransition } from 'react-transition-group'

const Search = () => {
    const [searchBox, setSearchBox] = useState(false)
    const [searchIcon, setSearchIcon] = useState(true)

    const [searchText, setSearchText] = useState('')
    const [searchChanged, setSearchChanged] = useState(false)
    const searchBoxRef = useRef()
    const history = useHistory()

    useEffect(() => {
        document.addEventListener('mousedown', outsideSearchClickListener, false)
        return () => {
            document.removeEventListener('mousedown', outsideSearchClickListener, false)
        }
    }, [])

    useEffect(() => {
        if (searchText.length > 0) {
            history.push({
                pathname: '/search',
                search: `?q=${searchText}`
            })

        } else if (searchChanged && searchText.length === 0) {
            history.replace({ pathname: '/browse' })
        }
    }, [history, searchText, searchChanged])

    const searchClickHandler = () => {
        setSearchBox(true)
    }

    const outsideSearchClickListener = event => {
        if (searchBoxRef.current && !searchBoxRef.current.contains(event.target)) {
            setSearchBox(false)
        }
    }

    const searchTextChangeHandler = event => {
        const textValue = event.target.value
        setSearchText(textValue)
        setSearchChanged(true)
    }

    const clickCrossHandler = () => {
        setSearchText('')
    }

    const searchBar = (
        <CSSTransition in={searchBox} classNames="search-animation" timeout={500} unmountOnExit
            onEnter={() => setSearchIcon(false)}
            onExiting={() => setSearchBox(false)}
            onExited={() => setSearchIcon(true)}>
            <div className="Holder">
                <FontAwesomeIcon className="Icon" size="lg" icon={faSearch} />
                <input autoFocus placeholder="Titles, people, genres"
                    onChange={searchTextChangeHandler} value={searchText} />
                {searchText.length > 0 ?
                    <FontAwesomeIcon onClick={clickCrossHandler} size="lg" icon={faTimes} /> : null
                }
            </div>
        </CSSTransition>
    )

    return (
        <div className="SearchBox" onClick={searchClickHandler} ref={searchBoxRef}>
            {searchIcon && <FontAwesomeIcon size="lg" icon={faSearch} />}
            {searchBar}
        </div>
    )
}

export default Search

================================================
FILE: src/context/Authentication.js
================================================
import React, { useState } from 'react'

export const AuthenticationContext = React.createContext({
    authenticated: false,
    login: () => { }, 
    logout: () => {}
})

const AuthenticationContextProvider = (props) => {
    const [authenticated, setAuthenticated] = useState(false)

    const loginHandler = () => {
        setAuthenticated(true)
    }

    const logoutHandler = () => {
        setAuthenticated(false)
    }

    return (
        <AuthenticationContext.Provider value={{
            authenticated: authenticated, login: loginHandler, 
            logout: logoutHandler
        }}>
            {props.children}
        </AuthenticationContext.Provider>
    )
}

export default AuthenticationContextProvider 

================================================
FILE: src/hoc/Layout.js
================================================
import React from 'react'
import useNavbar from 'hooks/useNavbar'


const Layout = props => {
    const navBar = useNavbar()
    const style = {
        background: '#141414',
        minHeight: '100vh'
    }
    return (
        <div style={style}>
            {navBar}
            {props.children}
        </div>
    )
}

export default Layout

================================================
FILE: src/hoc/ScrollToTop/ScrollToTop.js
================================================
import { Component } from "react";
import { withRouter } from "react-router-dom";

/**
 * A simple hoc as shown in the react router documentation
 * sample which does nothing but restore window scroll
 * position when going to and fro from links.
 */
class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      window.scrollTo(0, 0);
    }
  }

  render() {
    return this.props.children;
  }
}

export default withRouter(ScrollToTop);


================================================
FILE: src/hooks/useDropdown.js
================================================
import React, { useState } from 'react'

import Dropdown from 'components/Navigation/Dropdown/Dropdown'

const UseDropDown = (content, boxSizing) => {
    const [dropdown, setDropdown] = useState({
        iconHovered: false,
        floatingBoxHovered: false
    })

    const handlers = {
        iconHoveredInHandler: () => {
            setDropdown(prevDropdown => ({
                ...prevDropdown,
                iconHovered: true,
            }))
        },

        iconHoveredOutHandler: () => {
            setTimeout(() => {
                setDropdown(prevDropdown => ({
                    ...prevDropdown,
                    iconHovered: false,
                }))
            }, 600)
        },

        floatingBoxHoveredInHandler: () => {
            setDropdown(prevDropdown => ({
                ...prevDropdown,
                floatingBoxHovered: true,
            }))
        },

        floatingBoxHoveredOutHandler: () => {
            setDropdown(prevDropdown => ({
                ...prevDropdown,
                floatingBoxHovered: false,
            }))
        },

        onItemClickCloseBoxHandler: () => {
            setDropdown(false)
        }
    }

    return (
        <Dropdown
            dropdown={dropdown} content={content}
            {...handlers} boxSizing={boxSizing}
        />
    )
}

export default UseDropDown

================================================
FILE: src/hooks/useHoverStyleButton.js
================================================
import { useState } from 'react'

const UseHoverStyleButton = (buttonsObj) => {
    const [buttonHovered, setButtonHovered] = useState(buttonsObj)

    const onButtonHoverHandler = (id) => {
        setButtonHovered(prevHover => ({
            ...prevHover,
            [id]: !prevHover[id]
        }))
    }

    return [buttonHovered, onButtonHoverHandler]
}

export default UseHoverStyleButton

================================================
FILE: src/hooks/useNavbar.js
================================================
import React, { useContext } from 'react'

import NavBar from 'containers/NavBar/NavBar'
import { useHistory } from "react-router-dom";
import { AuthenticationContext } from 'context/Authentication'
import useDropdown from './useDropdown'

import ProfileCard from 'components/UI/ProfileCard/ProfileCard'
import { NavLink } from 'react-router-dom'

import {
    Weird,
    Profile,
    Smile,
    Normal
} from '../assets/images/index'

const UseNavbar = () => {
    const logoutHandler = () => {
        localStorage.removeItem('profileSelected')
        authContext.logout()
        history.push('/')
    }

    const profileDropdownContent = (
        <>
            <ProfileCard
                profileImage={Profile}
                username="Pushpa"
                dropdown
            />
            <ProfileCard
                profileImage={Weird}
                username="Shammy"
                dropdown
            />
            <ProfileCard
                profileImage={Smile}
                username="PQ"
                dropdown
            />
            <ProfileCard
                profileImage={Normal}
                username="Subhanga"
                dropdown
            />

            <span style={{ borderBottom: '1px solid grey', marginBottom: '7px' }}>Manage Profiles</span>
            <span>Account</span>
            <span>Help Center</span>
            <span onClick={logoutHandler}>Sign out of Netflix</span>
        </>
    )

    const navLinks = (
        <>
            <NavLink className="inactive" activeClassName="active" to="/browse" exact>Home</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/tv" exact>TV Shows</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/movies" exact>Movies</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/latest" exact>Latest</NavLink>
            <NavLink className="inactive" activeClassName="active" to="/browse/list" exact>My List</NavLink>
        </>
    )

    const profileDropdown = useDropdown(profileDropdownContent,
        { top: '42px', right: '0px', width: '20vh', height: '42vh' })

    const navDropdown = useDropdown(navLinks,
        { top: '15px', width: '100px' })

    const authContext = useContext(AuthenticationContext)
    const history = useHistory()

    return (
        <NavBar navigation profileDropdown={profileDropdown}
            navDropdown={navDropdown} />
    )
}

export default UseNavbar

================================================
FILE: src/hooks/useVideoInfoHandlers.js
================================================
import { useState, useCallback } from 'react'

import { mediaTypeToVideoDetailTransformation } from 'utils/transformations'
import { isMobile } from 'react-device-detect'

// A custom hook which sets all VideoCard/Carousel click/hover behavior 
const UseVideoInfoHandlers = () => {
    const [videoInfo, setVideoInfo] = useState()
    const [videoInfoError, setVideoInfoError] = useState(null)
    const [detailModal, setDetailModal] = useState(false)

    const cardClickHandler = useCallback((videoId, mediaType) => {
        if (!isMobile) {
            setDetailModal(true)
        } else {
            mediaTypeToVideoDetailTransformation(videoId, mediaType)
                .then(data => {
                    setVideoInfo(data)
                    setDetailModal(true)
                })
                .catch(error => {
                    setVideoInfoError(error)
                })
        }
    }, [])

    const cardHoverHandler = useCallback((videoId, mediaType) => {
        mediaTypeToVideoDetailTransformation(videoId, mediaType)
            .then(data => {
                setVideoInfo(data)
            })
            .catch(error => {
                setVideoInfoError(error)
            })
    }, [])

    const closeModalHandler = useCallback(() => {
        setDetailModal(false)
    }, [])

    return [videoInfo, videoInfoError, detailModal, cardClickHandler, cardHoverHandler, closeModalHandler]
}

export default UseVideoInfoHandlers

================================================
FILE: src/index.js
================================================
import React from "react";
import ReactDOM from "react-dom";

import App from "App";
import { BrowserRouter } from "react-router-dom";
import ScrollToTop from "hoc/ScrollToTop/ScrollToTop";
import AuthenticationContextProvider from 'context/Authentication'
import { Provider } from 'react-redux'
import store from 'store/reducers/store'


const app = (
  <Provider store={store}>
    <BrowserRouter basename={process.env.PUBLIC_URL}>
      <React.StrictMode>
        <ScrollToTop>
          <AuthenticationContextProvider>
            <App />
          </AuthenticationContextProvider>
        </ScrollToTop>
      </React.StrictMode>
    </BrowserRouter>
  </Provider>
);

if(window.Cypress) {
  window.store = store 
}

const rootElement = document.getElementById("root");
ReactDOM.render(app, rootElement);


================================================
FILE: src/store/reducers/slices/latestVideoSlice.js
================================================
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'baseAxios'

export const fetchLatestVideos = createAsyncThunk('latestVideoSlice/fetchLatestVideos',
    async (_, { rejectWithValue }) => {
        try {
            const response = await Promise.all([
                axios.get(`discover/movie?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&sort_by=primary_release_date.desc&include_adult=false&include_video=false&page=1&vote_average.gte=6`)
                    .then(response => ({ title: "Latest Movies", videos: response.data.results })),
                axios.get(`discover/tv?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&sort_by=first_air_date.desc&page=1&timezone=America%2FNew_York&vote_average.gte=6&include_null_first_air_dates=false`)
                    .then(response => ({ title: "Latest TV Shows", videos: response.data.results }))
            ])

            return response
        } catch (error) {
            if (!error.response) {
                throw error
            }

            return rejectWithValue(error.response.data)
        }
    })

const initialState = {
    latestVideos: [],
    status: 'idle',
    error: null
}

const latestVideoSlice = createSlice({
    name: 'latestVideos',
    initialState: initialState,
    extraReducers: {
        [fetchLatestVideos.pending]: (state, _) => {
            state.status = 'loading'
        },

        [fetchLatestVideos.fulfilled]: (state, action) => {
            action.payload.forEach(latestVideo => {
                state.latestVideos.push({ ...latestVideo })
            })

            state.status = 'success'
        },

        [fetchLatestVideos.rejected]: (state, action) => {
            state.status = 'error'
            if (action.payload) {
                state.error = action.payload.status_message
            } else {
                state.error = action.error
            }
        }
    }
})

export const selectLatestVideos = state => state.latestVideos

export default latestVideoSlice.reducer

================================================
FILE: src/store/reducers/slices/moviesByGenreSlice.js
================================================
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'baseAxios'
import { genreTopVideoTransformation } from 'utils/transformations'

const fetchMovieGenres = async () => {
    try {
        const response = await axios.get(
            `genre/movie/list?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`
        )

        return response.data.genres
    } catch (error) {
        throw new Error(error)
    }
}

export const fetchMoviesByGenre = createAsyncThunk('moviesByGenreSlice/fetchMoviesByGenre',
    async (_, { rejectWithValue }) => {
        try {
            const genres = await fetchMovieGenres()
            return await genreTopVideoTransformation(genres, 'movie')
        } catch (error) {
            if (!error.response) {
                throw error
            }
            return rejectWithValue(error.response.data)
        }
    }
)

const initalState = {
    genres: [],
    status: 'idle',
    error: null
}

const moviesByGenreSlice = createSlice({
    name: 'moviesByGenre',
    initialState: initalState,
    extraReducers: {
        [fetchMoviesByGenre.pending]: (state, _) => {
            state.status = 'loading'
        },

        [fetchMoviesByGenre.fulfilled]: (state, action) => {
            action.payload.forEach(genre => {
                state.genres.push({ ...genre })
            })

            state.status = 'success'
        },

        [fetchMoviesByGenre.rejected]: (state, action) => {
            state.status = 'error'
            if (action.payload) {
                state.error = action.payload.status_message
            } else {
                state.error = action.error
            }
        }
    }
})

export const selectMoviesByGenre = state => state.moviesByGenre

export default moviesByGenreSlice.reducer 

================================================
FILE: src/store/reducers/slices/netflixOriginalsSlice.js
================================================
import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'
import axios from 'baseAxios'

export const netflixAdapter = createEntityAdapter()

export const fetchNetflixOriginals = createAsyncThunk('netflixOriginalsSlice/fetchNetflixOriginals',
    async (_, { rejectWithValue }) => {
        try {
            const response = await axios.get(
                `discover/tv?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&sort_by=popularity.desc&page=1&timezone=America%2FNew_York&with_networks=213&include_null_first_air_dates=false`
            )
            return response.data.results
        } catch (error) {
            if (!error.response) {
                throw error
            }
            return rejectWithValue(error.response.data)
        }
    })

const netflixOriginalsSlice = createSlice({
    name: 'netflixOriginals',
    initialState: netflixAdapter.getInitialState({ error: null }),
    extraReducers: {
        [fetchNetflixOriginals.fulfilled]: (state, action) => {
            netflixAdapter.upsertMany(state, action.payload)
        },

        [fetchNetflixOriginals.rejected]: (state, action) => {
            if (action.payload) {
                state.error = action.payload.status_message
            } else {
                state.error = action.error
            }
        }
    }
})

export const {
    selectAll: selectAllNetflixOriginals
} = netflixAdapter.getSelectors(state => state.netflixOriginals)
export const selectNetflixError = state => state.netflixOriginals.error

export default netflixOriginalsSlice.reducer 

================================================
FILE: src/store/reducers/slices/topratedSlice.js
================================================
import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'
import axios from 'baseAxios'

export const topratedAdapter = createEntityAdapter()

export const fetchTopRated = createAsyncThunk('topratedSlice/fetchTopRated',
    async (_, { rejectWithValue }) => {
        try {
            const response = await axios.get(
                `movie/top_rated?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&page=1`
            )
            return response.data.results
        } catch (error) {
            if (!error.response) {
                throw error
            }

            return rejectWithValue(error.response.data)
        }
    })

const topratedSlice = createSlice({
    name: 'toprated',
    initialState: topratedAdapter.getInitialState({ error: null }),
    extraReducers: {
        [fetchTopRated.fulfilled]: (state, action) => {
            topratedAdapter.upsertMany(state, action.payload)
        },

        [fetchTopRated.rejected]: (state, action) => {
            if (action.payload) {
                state.error = action.payload.status_message
            } else {
                state.error = action.error
            }        }
    }
})

export const {
    selectAll: selectAllTopRatedVideos,
} = topratedAdapter.getSelectors(state => state.toprated)

export const selectTopRatedError = state => state.toprated.error

export default topratedSlice.reducer


================================================
FILE: src/store/reducers/slices/trendingSlice.js
================================================
import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'
import axios from 'baseAxios'

export const trendingAdapter = createEntityAdapter()

export const fetchTrending = createAsyncThunk('trendingSlice/fetchTrending',
    async (_, { rejectWithValue }) => {
        try {
            const response = await axios.get(
                `trending/all/day?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}`
            )
            return response.data.results
        } catch (error) {
            if (!error.response) {
                throw error
            }

            return rejectWithValue(error.response.data)
        }
    })

const trendingSlice = createSlice({
    name: 'trending',
    initialState: trendingAdapter.getInitialState({ error: null }),
    reducers: {},
    extraReducers: {
        [fetchTrending.fulfilled]: (state, action) => {
            trendingAdapter.upsertMany(state, action.payload)
        },

        [fetchTrending.rejected]: (state, action) => {
            if (action.payload) {
                state.error = action.payload.status_message
            } else {
                state.error = action.error
            }
        }
    }
})

export const {
    selectAll: selectAllTrendingVideos,
} = trendingAdapter.getSelectors(state => state.trending)

export const selectTrendingError = state => state.trending.error

export default trendingSlice.reducer


================================================
FILE: src/store/reducers/slices/tvByGenreSlice.js
================================================
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'baseAxios'
import { genreTopVideoTransformation } from 'utils/transformations'


const fetchTvGenres = async () => {
    try {
        const response = await axios.get(
            `genre/tv/list?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`
        )

        return response.data.genres
    } catch (error) {
        throw new Error(error)
    }
}

export const fetchTvShowsByGenres = createAsyncThunk('tvByGenreSlice/fetchTvShowsByGenres',
    async (_, { rejectWithValue }) => {
        try {
            const genres = await fetchTvGenres()
            return await genreTopVideoTransformation(genres, 'tv')
        } catch (error) {
            if (!error.response) {
                throw error
            }

            return rejectWithValue(error.response.data)
        }
    }
)

const initalState = {
    genres: [],
    status: 'idle',
    error: null
}

const tvByGenreSlice = createSlice({
    name: 'tvByGenre',
    initialState: initalState,
    extraReducers: {
        [fetchTvShowsByGenres.pending]: (state, _) => {
            state.status = 'loading'
        },

        [fetchTvShowsByGenres.fulfilled]: (state, action) => {
            action.payload.forEach(genre => {
                state.genres.push({ ...genre })
            })

            state.status = 'success'
        },

        [fetchTvShowsByGenres.rejected]: (state, action) => {
            state.status = 'error'
            if (action.payload) {
                state.error = action.payload.status_message
            } else {
                state.error = action.error
            }
        }
    }
})

export const selectTvByGenre = state => state.tvByGenre

export default tvByGenreSlice.reducer 

================================================
FILE: src/store/reducers/store.js
================================================
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import trendingReducer from './slices/trendingSlice'
import topRatedReducer from './slices/topratedSlice'
import netflixOriginalsReducer from './slices/netflixOriginalsSlice'
import moviesByGenresReducer from './slices/moviesByGenreSlice'
import tvByGenresReducer from './slices/tvByGenreSlice'
import latestVideoReducer from './slices/latestVideoSlice'

const store = configureStore({
    reducer: {
        trending: trendingReducer,
        toprated: topRatedReducer,
        netflixOriginals: netflixOriginalsReducer,
        moviesByGenre: moviesByGenresReducer,
        tvByGenre: tvByGenresReducer,
        latestVideos: latestVideoReducer
    },
    // Clear this in production, as it is done by default 
    middleware: [...getDefaultMiddleware({ immutableCheck: false })]
})

export default store 


================================================
FILE: src/styles.css
================================================
body {
  margin: 0;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  -webkit-tap-highlight-color: transparent;
}

body button {
    cursor: pointer;
}

::-webkit-scrollbar {
  display: none;
}


================================================
FILE: src/utils/animations.js
================================================
export const scrollTo = (element, to, duration, scrollToDone = null) => {
    Math.easeInOutQuad = (t, b, c, d) => {
        t /= d / 2;
        if (t < 1) return c / 2 * t * t + b;
        t--;
        return -c / 2 * (t * (t - 2) - 1) + b;
    };

    let start = element.scrollLeft,
        change = to - start,
        currentTime = 0,
        increment = 20;

    const animateScroll = () => {
        currentTime += increment;
        const val = Math.easeInOutQuad(currentTime, start, change, duration);
        element.scrollLeft = val;
        if (currentTime < duration) {
            setTimeout(animateScroll, increment);
        } else {
            if (scrollToDone) scrollToDone();
        }
    };
    animateScroll();
}


================================================
FILE: src/utils/sorting.js
================================================
export const sortVideosByPopularity = (a, b) => {
    if (a.popularity > b.popularity) {
        return -1;
    }
    if (a.popularity < b.popularity) {
        return 1;
    }
    return 0;
}



================================================
FILE: src/utils/time.js
================================================
import React from 'react'

const convertTimeToHourMinuteFormat = timeInHours => {
    var hours = Math.trunc(timeInHours / 60);
    var minutes = timeInHours % 60;
    return `${hours}h ${minutes}m`
}

export const getSeasonsOrMovieLength = (seasons, runtime) => {
    let timeSpan
    if (runtime) {
        timeSpan = <span>{convertTimeToHourMinuteFormat(runtime)}</span>
    } else if (seasons) {
        timeSpan = (
            <span>
                {seasons.length > 1 ? `${seasons.length} Seasons` : `${seasons.length} Season`}
            </span>
        )
    }

    return timeSpan
}

================================================
FILE: src/utils/transformations.js
================================================
import React from 'react'

import axios from 'baseAxios'
import { isMobile } from 'react-device-detect'
import VideoModal from 'components/Modals/VideoModal/VideoModal'

/**
 * 
 * @param {*} genres: the genres
 * @param {*} apiCallType: whether the subsequent API calls will be made for tv or movies 
 * 
 * Takes a genre object and does creates a big chain of API calls to get each genre's top trending videos
 * 
 * Fetches all genres and creates a trending movies API call for each. I push the response with 
 * a title and content to make it easier to label the video carousels later. This Promise.all 
 * function returns a large array, so I have to parse through the action.payload later 
 * in the slice reducer.  
 */

export const genreTopVideoTransformation = async (genres, apiCallType) => {
    let url
    if (apiCallType === 'tv') {
        url = `discover/tv?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&sort_by=popularity.desc&page=1&include_null_first_air_dates=false&with_genres=`
    } else if (apiCallType === 'movie') {
        url = `discover/movie?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=`
    }

    const genreRequestArray = []
    genres.forEach(genre => {
        let newUrlParser = url
        newUrlParser += genre.id.toString()
        genreRequestArray.push(axios.get(newUrlParser).then(response =>
            ({ title: genre.name, videos: response.data.results })))
    })

    try {
        return await Promise.all(genreRequestArray)
    } catch (error) {
        throw new Error(error)
    }
}

export const mediaTypeToVideoDetailTransformation = async (videoId, mediaType) => {
    let requestURL;
    if (mediaType === 'movie') {
        requestURL = `movie/${videoId}?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`
    } else if (mediaType === 'tv') {
        requestURL = `tv/${videoId}?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`
    }

    try {
        const response = await axios.get(requestURL)
        return response.data
    } catch (error) {
        throw new Error(error)
    }
}

export const buildVideoMetadata = (videoItem, selectedVideoInfo) => {
    let mediaType
    if (videoItem.media_type) {
        mediaType = videoItem.media_type
    } else {
        if (videoItem.first_air_date) {
            mediaType = 'tv'
        } else if (videoItem.release_date) {
            mediaType = 'movie'
        }
    }

    let extraInfo = {}
    if (!isMobile) {
        if (selectedVideoInfo && selectedVideoInfo.id === videoItem.id) {
            extraInfo['genres'] = selectedVideoInfo.genres
            if (selectedVideoInfo.runtime) {
                extraInfo['runtime'] = selectedVideoInfo.runtime
            } else if (selectedVideoInfo.seasons) {
                extraInfo['seasons'] = selectedVideoInfo.seasons
            }
        }
    }

    return { mediaType, extraInfo }
}

export const buildVideoModal = (videoDetailModal, videoInfo, handlers) => {
    let detailModalComponent
    if (videoDetailModal && videoInfo) {
        detailModalComponent = (
            <VideoModal
                videoDetailModal={videoDetailModal}
                videoInfo={videoInfo}
                {...handlers}
            />
        )
    }

    return detailModalComponent
}

================================================
FILE: src/utils/validation.js
================================================
export const validEmailAndPhoneNumber = input => {
    const phoneRegex = /^\d{10}$/   // eslint-disable-next-line 
    const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/ 
    return input.match(phoneRegex) || input.match(emailRegex)
}
Download .txt
gitextract_mptik5yv/

├── .gitignore
├── README.md
├── cypress/
│   ├── integration/
│   │   ├── browse-home.spec.js
│   │   ├── browse-navbar.spec.js
│   │   ├── landing-section.spec.js
│   │   ├── login-form.spec.js
│   │   └── sign-in-flow.spec.js
│   ├── plugins/
│   │   └── index.js
│   └── support/
│       ├── commands.js
│       └── index.js
├── cypress.json
├── jsconfig.json
├── package.json
├── public/
│   ├── 404.html
│   ├── index.html
│   └── manifest.json
└── src/
    ├── App.js
    ├── assets/
    │   └── images/
    │       └── index.js
    ├── baseAxios.js
    ├── components/
    │   ├── Modals/
    │   │   ├── ProfileModal/
    │   │   │   ├── ProfileModal.css
    │   │   │   └── ProfileModal.js
    │   │   └── VideoModal/
    │   │       ├── VideoModal.css
    │   │       └── VideoModal.js
    │   ├── Navigation/
    │   │   └── Dropdown/
    │   │       ├── Dropdown.css
    │   │       └── Dropdown.js
    │   ├── StaticPages/
    │   │   ├── ErrorPage/
    │   │   │   ├── ErrorPage.css
    │   │   │   └── ErrorPage.js
    │   │   ├── LoadingScreen/
    │   │   │   ├── LoadingScreen.css
    │   │   │   └── LoadingScreen.js
    │   │   └── NotFoundPage/
    │   │       ├── NotFoundPage.css
    │   │       └── NotFoundPage.js
    │   ├── UI/
    │   │   ├── Button/
    │   │   │   ├── Button.css
    │   │   │   └── Button.js
    │   │   ├── CircularSoundButton/
    │   │   │   ├── CircularSoundButton.css
    │   │   │   └── CircularSoundButton.js
    │   │   ├── DarkComponent/
    │   │   │   └── DarkComponent.js
    │   │   ├── FAQComponent/
    │   │   │   ├── FAQComponent.css
    │   │   │   └── FAQComponent.js
    │   │   └── ProfileCard/
    │   │       ├── ProfileCard.css
    │   │       └── ProfileCard.js
    │   └── Video/
    │       ├── TopTrailerComponent/
    │       │   ├── TopTrailerComponent.css
    │       │   └── TopTrailerComponent.js
    │       ├── VideoCard/
    │       │   ├── VideoCard.css
    │       │   └── VideoCard.js
    │       └── VideoCarousel/
    │           ├── VideoCarousel.css
    │           └── VideoCarousel.js
    ├── containers/
    │   ├── Browse/
    │   │   ├── Browse.js
    │   │   ├── BrowseContent/
    │   │   │   ├── BrowseContent.css
    │   │   │   └── BrowseContent.js
    │   │   ├── SearchContent/
    │   │   │   ├── SearchContent.css
    │   │   │   └── SearchContent.js
    │   │   └── routes/
    │   │       ├── Home.js
    │   │       ├── LatestVideo.js
    │   │       ├── List.js
    │   │       ├── Movies.js
    │   │       ├── Tv.js
    │   │       └── index.js
    │   ├── LandingSection/
    │   │   ├── LandingSection.css
    │   │   ├── LandingSection.js
    │   │   └── LandingSectionTexts.js
    │   ├── Login/
    │   │   ├── Login.css
    │   │   └── Login.js
    │   ├── NavBar/
    │   │   ├── NavBar.css
    │   │   └── NavBar.js
    │   └── Search/
    │       ├── Search.css
    │       └── Search.js
    ├── context/
    │   └── Authentication.js
    ├── hoc/
    │   ├── Layout.js
    │   └── ScrollToTop/
    │       └── ScrollToTop.js
    ├── hooks/
    │   ├── useDropdown.js
    │   ├── useHoverStyleButton.js
    │   ├── useNavbar.js
    │   └── useVideoInfoHandlers.js
    ├── index.js
    ├── store/
    │   └── reducers/
    │       ├── slices/
    │       │   ├── latestVideoSlice.js
    │       │   ├── moviesByGenreSlice.js
    │       │   ├── netflixOriginalsSlice.js
    │       │   ├── topratedSlice.js
    │       │   ├── trendingSlice.js
    │       │   └── tvByGenreSlice.js
    │       └── store.js
    ├── styles.css
    └── utils/
        ├── animations.js
        ├── sorting.js
        ├── time.js
        ├── transformations.js
        └── validation.js
Download .txt
SYMBOL INDEX (4 symbols across 2 files)

FILE: src/App.js
  function App (line 11) | function App() {

FILE: src/hoc/ScrollToTop/ScrollToTop.js
  class ScrollToTop (line 9) | class ScrollToTop extends Component {
    method componentDidUpdate (line 10) | componentDidUpdate(prevProps) {
    method render (line 16) | render() {
Condensed preview — 87 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (126K chars).
[
  {
    "path": ".gitignore",
    "chars": 193,
    "preview": ".idea/\n.vscode/\nnode_modules/\nbuild\n.DS_Store\n*.tgz\nmy-app*\ntemplate/src/__tests__/__snapshots__/\nlerna-debug.log\nnpm-de"
  },
  {
    "path": "README.md",
    "chars": 865,
    "preview": "# Netflix-Clone\n\n![DemoGif](flixdemo.gif)\n\nA Netflix clone I created for the sake of practicing React and Redux. It feat"
  },
  {
    "path": "cypress/integration/browse-home.spec.js",
    "chars": 1597,
    "preview": "/// <reference types=\"Cypress\" />\n\ndescribe('<Home /> -> <BrowseContent />', () => {\n    beforeEach(() => {\n        loca"
  },
  {
    "path": "cypress/integration/browse-navbar.spec.js",
    "chars": 2203,
    "preview": "/// <reference types=\"Cypress\" />\n\ndescribe('<Search /> and <Dropdown />', () => {\n    beforeEach(() => {\n        localS"
  },
  {
    "path": "cypress/integration/landing-section.spec.js",
    "chars": 1126,
    "preview": "/// <reference types=\"Cypress\" />\n\ndescribe('<LandingSection />', () => {\n    beforeEach(() => {\n        cy.visit('/')\n "
  },
  {
    "path": "cypress/integration/login-form.spec.js",
    "chars": 1984,
    "preview": "/// <reference types=\"Cypress\" />\n\ndescribe('<Login />', () => {\n    beforeEach(() => {\n        cy.visit('/login')\n\n    "
  },
  {
    "path": "cypress/integration/sign-in-flow.spec.js",
    "chars": 1173,
    "preview": "/// <reference types=\"Cypress\" />\n\ndescribe('<Browse />', () => {\n    beforeEach(() => {\n        cy.validLogin()\n    })\n"
  },
  {
    "path": "cypress/plugins/index.js",
    "chars": 718,
    "preview": "/// <reference types=\"cypress\" />\n// ***********************************************************\n// This example plugins"
  },
  {
    "path": "cypress/support/commands.js",
    "chars": 200,
    "preview": "Cypress.Commands.add('validLogin', () => {\n    cy.visit('/login')\n\n    cy.get('input[name=\"email\"]')\n        .type('exam"
  },
  {
    "path": "cypress/support/index.js",
    "chars": 670,
    "preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
  },
  {
    "path": "cypress.json",
    "chars": 42,
    "preview": "{\n    \"baseUrl\": \"http://localhost:3000\"\n}"
  },
  {
    "path": "jsconfig.json",
    "chars": 97,
    "preview": "{\n    \"compilerOptions\": {\n        \"baseUrl\": \"src\"\n    },\n    \"include\": [\n        \"src\"\n    ]\n}"
  },
  {
    "path": "package.json",
    "chars": 1275,
    "preview": "{\n  \"name\": \"netflix-clone\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"homepage\": \"https://azazel5.github.io/Netflix"
  },
  {
    "path": "public/404.html",
    "chars": 1853,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Single Page Apps for GitHub Pages</title>\n    <scr"
  },
  {
    "path": "public/index.html",
    "chars": 2848,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n\t<meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, in"
  },
  {
    "path": "public/manifest.json",
    "chars": 358,
    "preview": "{\n    \"short_name\": \"React App\",\n    \"name\": \"Create React App Sample\",\n    \"icons\": [\n        {\n            \"src\": \"fav"
  },
  {
    "path": "src/App.js",
    "chars": 1806,
    "preview": "import React, { useContext } from \"react\";\nimport \"./styles.css\";\n\nimport LandingSection from \"containers/LandingSection"
  },
  {
    "path": "src/assets/images/index.js",
    "chars": 322,
    "preview": "import Weird from './weird.png'\nimport Profile from './profile.jpg'\nimport Smile from './smile.png'\nimport Normal from '"
  },
  {
    "path": "src/baseAxios.js",
    "chars": 122,
    "preview": "import axios from 'axios'\n\nconst axe = axios.create({\n    baseURL: 'https://api.themoviedb.org/3/'\n})\n\nexport default ax"
  },
  {
    "path": "src/components/Modals/ProfileModal/ProfileModal.css",
    "chars": 1073,
    "preview": ".ProfileModal {\n    top: 0;\n    left: 0;\n    height: 100%;\n    width: 100%;\n    background: #141414;\n    border: none;\n "
  },
  {
    "path": "src/components/Modals/ProfileModal/ProfileModal.js",
    "chars": 1437,
    "preview": "import React from 'react'\nimport Modal from 'react-modal'\nimport { NetflixLogo } from 'assets/images/'\nimport ProfileCar"
  },
  {
    "path": "src/components/Modals/VideoModal/VideoModal.css",
    "chars": 1168,
    "preview": ".ModalStyles {\n    position: absolute;\n    left: 50%;\n    top: 50%;\n    transform: translate(-50%, -50%);\n    height: 45"
  },
  {
    "path": "src/components/Modals/VideoModal/VideoModal.js",
    "chars": 3788,
    "preview": "import React from 'react'\nimport './VideoModal.css'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\ni"
  },
  {
    "path": "src/components/Navigation/Dropdown/Dropdown.css",
    "chars": 400,
    "preview": ".Dropdown {\n    position: relative;\n    display: inline-block;\n    z-index: 100;\n    cursor: pointer;\n}\n\n.FloatingBox {\n"
  },
  {
    "path": "src/components/Navigation/Dropdown/Dropdown.js",
    "chars": 1188,
    "preview": "import React from 'react'\nimport './Dropdown.css'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimp"
  },
  {
    "path": "src/components/StaticPages/ErrorPage/ErrorPage.css",
    "chars": 141,
    "preview": ".ErrorPage {\n    height: 100vh;\n    background: #141414;\n    color: white;\n    padding: 95px;\n}\n\n.ErrorPage-Items {\n    "
  },
  {
    "path": "src/components/StaticPages/ErrorPage/ErrorPage.js",
    "chars": 944,
    "preview": "import React from 'react'\nimport './ErrorPage.css'\n\n/**\n * I have to check for error message or simply an error because "
  },
  {
    "path": "src/components/StaticPages/LoadingScreen/LoadingScreen.css",
    "chars": 66,
    "preview": ".LoadingScreen {\n    background: #141414;\n    min-height: 100vh;\n}"
  },
  {
    "path": "src/components/StaticPages/LoadingScreen/LoadingScreen.js",
    "chars": 194,
    "preview": "import React from 'react'\nimport './LoadingScreen.css'\n\nconst loadingScreen = props => {\n    return (\n        <div class"
  },
  {
    "path": "src/components/StaticPages/NotFoundPage/NotFoundPage.css",
    "chars": 2158,
    "preview": ".Parent {\r\n  position: relative;\r\n  height: 100vh;\r\n}\r\n\r\n.Parent .notfound {\r\n  position: absolute;\r\n  left: 50%;\r\n  top"
  },
  {
    "path": "src/components/StaticPages/NotFoundPage/NotFoundPage.js",
    "chars": 476,
    "preview": "import React from 'react'\r\nimport './NotFoundPage.css'\r\n\r\nconst notFoundPage = () => {\r\n\treturn (\r\n\t\t<div class=\"Parent\""
  },
  {
    "path": "src/components/UI/Button/Button.css",
    "chars": 193,
    "preview": ".Button {\n    display: inline-block;\n    border-radius: 3px;\n    border: 0;\n    outline: 0;\n    font-size: 1rem; \n}\n\n@me"
  },
  {
    "path": "src/components/UI/Button/Button.js",
    "chars": 1279,
    "preview": "import React from \"react\";\nimport './Button.css'\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\n\n/**\n"
  },
  {
    "path": "src/components/UI/CircularSoundButton/CircularSoundButton.css",
    "chars": 287,
    "preview": ".RoundButton {\n    display: inline-block;\n    border: 1px solid white;\n    border-radius: 50%;\n    background: transpare"
  },
  {
    "path": "src/components/UI/CircularSoundButton/CircularSoundButton.js",
    "chars": 565,
    "preview": "import React from 'react'\nimport './CircularSoundButton.css'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontaw"
  },
  {
    "path": "src/components/UI/DarkComponent/DarkComponent.js",
    "chars": 606,
    "preview": "import React from \"react\";\n\nconst darkComponentTextAlignStyles = {\n  display: 'flex',\n  flexDirection: 'column',\n  textA"
  },
  {
    "path": "src/components/UI/FAQComponent/FAQComponent.css",
    "chars": 2502,
    "preview": ".faqComponent {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0.8em 2.2em 0.8em 1"
  },
  {
    "path": "src/components/UI/FAQComponent/FAQComponent.js",
    "chars": 808,
    "preview": "import React from \"react\";\nimport \"./FAQComponent.css\";\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome"
  },
  {
    "path": "src/components/UI/ProfileCard/ProfileCard.css",
    "chars": 777,
    "preview": ".ProfileCard {\n    width: 25%;\n    margin: 15px;\n    cursor: pointer;\n}\n\n.ProfileCardDropdown {\n    width: 90%;\n    disp"
  },
  {
    "path": "src/components/UI/ProfileCard/ProfileCard.js",
    "chars": 353,
    "preview": "import React from 'react'\nimport './ProfileCard.css'\n\nconst ProfileCard = props => {\n    return (\n        <div className"
  },
  {
    "path": "src/components/Video/TopTrailerComponent/TopTrailerComponent.css",
    "chars": 256,
    "preview": ".VideoComponent {\n    height: 580px;\n    width: 100%;\n    background-position: center;\n    background-repeat: no-repeat;"
  },
  {
    "path": "src/components/Video/TopTrailerComponent/TopTrailerComponent.js",
    "chars": 355,
    "preview": "import React from 'react'\nimport './TopTrailerComponent.css'\n\nconst TopTrailerComponent = (props) => {\n    const backgro"
  },
  {
    "path": "src/components/Video/VideoCard/VideoCard.css",
    "chars": 476,
    "preview": ".VideoCard {\n    display: flex;\n    align-items: center;\n    cursor: pointer;\n    height: 100%;\n    width: 100%;\n}\n\n.Vid"
  },
  {
    "path": "src/components/Video/VideoCard/VideoCard.js",
    "chars": 1156,
    "preview": "import React from 'react'\nimport './VideoCard.css'\nimport { getSeasonsOrMovieLength } from 'utils/time'\n\nconst videoCard"
  },
  {
    "path": "src/components/Video/VideoCarousel/VideoCarousel.css",
    "chars": 2805,
    "preview": ".CarouselParent {\n    position: relative;\n    margin-bottom: 30px;\n}\n\n.VideoCarousel {\n    color: #e5e5e5;\n    font-size"
  },
  {
    "path": "src/components/Video/VideoCarousel/VideoCarousel.js",
    "chars": 3432,
    "preview": "import React, { useState, useRef } from 'react'\nimport './VideoCarousel.css'\n\nimport VideoCard from '../VideoCard/VideoC"
  },
  {
    "path": "src/containers/Browse/Browse.js",
    "chars": 1856,
    "preview": "import React, { useState } from 'react'\n\nimport ProfileModal from 'components/Modals/ProfileModal/ProfileModal'\nimport {"
  },
  {
    "path": "src/containers/Browse/BrowseContent/BrowseContent.css",
    "chars": 1180,
    "preview": ".TextsAndButtons {\n    display: flex;\n    align-items: flex-end;\n    justify-content: space-between;\n    color: white;\n "
  },
  {
    "path": "src/containers/Browse/BrowseContent/BrowseContent.js",
    "chars": 3943,
    "preview": "import React, { useState } from 'react'\nimport './BrowseContent.css'\n\nimport TopTrailerComponent from 'components/Video/"
  },
  {
    "path": "src/containers/Browse/SearchContent/SearchContent.css",
    "chars": 645,
    "preview": ".SearchContent {\n    padding: 95px 10px;\n    color: white;\n}\n\n.SearchGrid {\n    display: grid;\n    grid-template-columns"
  },
  {
    "path": "src/containers/Browse/SearchContent/SearchContent.js",
    "chars": 3606,
    "preview": "import React, { useState, useEffect, useCallback } from 'react'\nimport './SearchContent.css'\n\nimport axios from 'baseAxi"
  },
  {
    "path": "src/containers/Browse/routes/Home.js",
    "chars": 1749,
    "preview": "import React, { useEffect } from 'react'\n\nimport { useSelector, useDispatch } from 'react-redux'\nimport BrowseContent fr"
  },
  {
    "path": "src/containers/Browse/routes/LatestVideo.js",
    "chars": 1051,
    "preview": "import React, { useEffect } from 'react'\n\nimport { useSelector, useDispatch } from 'react-redux'\nimport { fetchLatestVid"
  },
  {
    "path": "src/containers/Browse/routes/List.js",
    "chars": 186,
    "preview": "import React from 'react'\n\nconst List = () => {\n    return (\n        <div style={{color: 'white', paddingTop: '95px', te"
  },
  {
    "path": "src/containers/Browse/routes/Movies.js",
    "chars": 1031,
    "preview": "import React, { useEffect } from 'react'\nimport { useSelector, useDispatch } from 'react-redux'\n\nimport { fetchMoviesByG"
  },
  {
    "path": "src/containers/Browse/routes/Tv.js",
    "chars": 1015,
    "preview": "import React, { useEffect } from 'react'\n\nimport { useSelector, useDispatch } from 'react-redux'\nimport { fetchTvShowsBy"
  },
  {
    "path": "src/containers/Browse/routes/index.js",
    "chars": 213,
    "preview": "import Home from './Home'\nimport Movies from './Movies'\nimport Tv from './Tv'\nimport LatestVideo from './LatestVideo'\nim"
  },
  {
    "path": "src/containers/LandingSection/LandingSection.css",
    "chars": 2105,
    "preview": ".landingSection {\n  height: 470px;\n  width: 100%;\n  border-bottom: 8px solid #222;\n}\n\n.landingTexts {\n  display: flex;\n "
  },
  {
    "path": "src/containers/LandingSection/LandingSection.js",
    "chars": 4942,
    "preview": "import React, { useState } from \"react\";\nimport \"./LandingSection.css\";\n\nimport NavBar from \"../NavBar/NavBar\";\nimport L"
  },
  {
    "path": "src/containers/LandingSection/LandingSectionTexts.js",
    "chars": 3050,
    "preview": "// A file for all texual material used in 'LandingSection.js'\nexport const texualMaterial = {\n    darkComponent: [\n     "
  },
  {
    "path": "src/containers/Login/Login.css",
    "chars": 1066,
    "preview": ".Login {\n  width: 100%;\n  height: 100vh;\n}\n\n.Login img {\n  height: 75px;\n  width: 178px;\n  margin-left: 20px;\n}\n\n.LoginC"
  },
  {
    "path": "src/containers/Login/Login.js",
    "chars": 4712,
    "preview": "import React, { useState, useContext } from \"react\";\nimport \"./Login.css\";\n\nimport { NetflixLogo, LoginBackground } from"
  },
  {
    "path": "src/containers/NavBar/NavBar.css",
    "chars": 1647,
    "preview": ".NavBar {\n  width: 100%;\n  height: 45px;\n  display: flex;\n  align-items: center;\n  position: absolute;\n  top: 0;\n  left:"
  },
  {
    "path": "src/containers/NavBar/NavBar.js",
    "chars": 2650,
    "preview": "import React, { useState, useEffect, useCallback } from \"react\";\nimport \"./NavBar.css\";\n\nimport { NetflixLogo } from \"as"
  },
  {
    "path": "src/containers/Search/Search.css",
    "chars": 2723,
    "preview": ".SearchBox {\n    display: inline-block;\n}\n\n.Holder {\n    display: flex;\n    align-items: center;\n    border: solid 1px r"
  },
  {
    "path": "src/containers/Search/Search.js",
    "chars": 2701,
    "preview": "import React, { useState, useEffect, useRef } from 'react'\nimport './Search.css'\nimport { useHistory } from 'react-route"
  },
  {
    "path": "src/context/Authentication.js",
    "chars": 729,
    "preview": "import React, { useState } from 'react'\n\nexport const AuthenticationContext = React.createContext({\n    authenticated: f"
  },
  {
    "path": "src/hoc/Layout.js",
    "chars": 345,
    "preview": "import React from 'react'\nimport useNavbar from 'hooks/useNavbar'\n\n\nconst Layout = props => {\n    const navBar = useNavb"
  },
  {
    "path": "src/hoc/ScrollToTop/ScrollToTop.js",
    "chars": 509,
    "preview": "import { Component } from \"react\";\nimport { withRouter } from \"react-router-dom\";\n\n/**\n * A simple hoc as shown in the r"
  },
  {
    "path": "src/hooks/useDropdown.js",
    "chars": 1365,
    "preview": "import React, { useState } from 'react'\n\nimport Dropdown from 'components/Navigation/Dropdown/Dropdown'\n\nconst UseDropDo"
  },
  {
    "path": "src/hooks/useHoverStyleButton.js",
    "chars": 396,
    "preview": "import { useState } from 'react'\n\nconst UseHoverStyleButton = (buttonsObj) => {\n    const [buttonHovered, setButtonHover"
  },
  {
    "path": "src/hooks/useNavbar.js",
    "chars": 2520,
    "preview": "import React, { useContext } from 'react'\n\nimport NavBar from 'containers/NavBar/NavBar'\nimport { useHistory } from \"rea"
  },
  {
    "path": "src/hooks/useVideoInfoHandlers.js",
    "chars": 1460,
    "preview": "import { useState, useCallback } from 'react'\n\nimport { mediaTypeToVideoDetailTransformation } from 'utils/transformatio"
  },
  {
    "path": "src/index.js",
    "chars": 810,
    "preview": "import React from \"react\";\nimport ReactDOM from \"react-dom\";\n\nimport App from \"App\";\nimport { BrowserRouter } from \"reac"
  },
  {
    "path": "src/store/reducers/slices/latestVideoSlice.js",
    "chars": 2069,
    "preview": "import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexport const fetchLatest"
  },
  {
    "path": "src/store/reducers/slices/moviesByGenreSlice.js",
    "chars": 1819,
    "preview": "import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\nimport { genreTopVideoTra"
  },
  {
    "path": "src/store/reducers/slices/netflixOriginalsSlice.js",
    "chars": 1602,
    "preview": "import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexp"
  },
  {
    "path": "src/store/reducers/slices/topratedSlice.js",
    "chars": 1426,
    "preview": "import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexp"
  },
  {
    "path": "src/store/reducers/slices/trendingSlice.js",
    "chars": 1424,
    "preview": "import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexp"
  },
  {
    "path": "src/store/reducers/slices/tvByGenreSlice.js",
    "chars": 1795,
    "preview": "import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\nimport { genreTopVideoTra"
  },
  {
    "path": "src/store/reducers/store.js",
    "chars": 880,
    "preview": "import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'\nimport trendingReducer from './slices/trendingSl"
  },
  {
    "path": "src/styles.css",
    "chars": 209,
    "preview": "body {\n  margin: 0;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  -webkit-tap-highlight-color: transp"
  },
  {
    "path": "src/utils/animations.js",
    "chars": 736,
    "preview": "export const scrollTo = (element, to, duration, scrollToDone = null) => {\n    Math.easeInOutQuad = (t, b, c, d) => {\n   "
  },
  {
    "path": "src/utils/sorting.js",
    "chars": 194,
    "preview": "export const sortVideosByPopularity = (a, b) => {\n    if (a.popularity > b.popularity) {\n        return -1;\n    }\n    if"
  },
  {
    "path": "src/utils/time.js",
    "chars": 594,
    "preview": "import React from 'react'\n\nconst convertTimeToHourMinuteFormat = timeInHours => {\n    var hours = Math.trunc(timeInHours"
  },
  {
    "path": "src/utils/transformations.js",
    "chars": 3397,
    "preview": "import React from 'react'\n\nimport axios from 'baseAxios'\nimport { isMobile } from 'react-device-detect'\nimport VideoModa"
  },
  {
    "path": "src/utils/validation.js",
    "chars": 251,
    "preview": "export const validEmailAndPhoneNumber = input => {\n    const phoneRegex = /^\\d{10}$/   // eslint-disable-next-line \n    "
  }
]

About this extraction

This page contains the full source code of the Azazel5/NetflixClone GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 87 files (112.2 KB), approximately 30.1k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!