[
  {
    "path": ".gitignore",
    "content": ".idea/\n.vscode/\nnode_modules/\nbuild\n.DS_Store\n*.tgz\nmy-app*\ntemplate/src/__tests__/__snapshots__/\nlerna-debug.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n/.changelog\n.npm/\nyarn.lock\n.env"
  },
  {
    "path": "README.md",
    "content": "# Netflix-Clone\n\n![DemoGif](flixdemo.gif)\n\nA Netflix clone I created for the sake of practicing React and Redux. It features design \npatterns recommended by the documentation. Some of the tools used include: <br />\n\n* Hooks (and custom hooks)\n* React Router\n* Redux Toolkit \n* Context API \n* Responsive web design \n* Cypress end-to-end testing \n\n<br />\n\nIt is a work in progress, and my first real project with React. Any tips on how to better write the \ncode, manage the folder structure, etc would be really appreciated. <br />\n\nThe future of this project: <br />\n\n* Integrate it with a Django backend\n* Create an authentication flow\n* Add REST API endpoints for every user-related event, such as adding Netflix profiles\n\n## Architecture Diagram \n\n![CloneFlow](https://user-images.githubusercontent.com/36729591/90905326-08c9c400-e39e-11ea-977c-76212f63b2b6.png)\n"
  },
  {
    "path": "cypress/integration/browse-home.spec.js",
    "content": "/// <reference types=\"Cypress\" />\n\ndescribe('<Home /> -> <BrowseContent />', () => {\n    beforeEach(() => {\n        localStorage.setItem('profileSelected', true)\n        cy.visit('/browse')\n        cy.window()\n            .its('store')\n            .invoke('getState')\n            .as('reduxState')\n\n    })\n\n    it('fetches trending, top-rated, and netflix originals successfully', () => {\n        cy.get('@reduxState')\n            .its('trending')\n            .its('ids')\n            .should('have.length', 20)\n\n        cy.get('@reduxState')\n            .its('toprated')\n            .its('ids')\n            .should('have.length', 20)\n\n        cy.get('@reduxState')\n            .its('netflixOriginals')\n            .its('ids')\n            .should('have.length', 20)\n    })\n\n    it('Ensure that the first video of trending section is placed on the <Video /> topTrailer', () => {\n        cy.get('@reduxState')\n            .its('trending')\n            .its('ids')\n            .then($idArray => {\n                const firstElem = $idArray[0]\n                cy.get('@reduxState')\n                    .its('trending')\n                    .its('entities')\n                    .then(($entities => {\n                        const item = Cypress._.find($entities, (element => element.id === firstElem))\n                        cy.get('.VideoComponent')\n                            .invoke('attr', 'style')\n                            .then(style => {\n                                expect(style).to.include(item.poster_path)\n                            })\n                    })) \n            })\n    })\n})"
  },
  {
    "path": "cypress/integration/browse-navbar.spec.js",
    "content": "/// <reference types=\"Cypress\" />\n\ndescribe('<Search /> and <Dropdown />', () => {\n    beforeEach(() => {\n        localStorage.setItem('profileSelected', true)\n        cy.visit('/browse')\n    })\n\n    context('<Dropdown />', () => {\n        beforeEach(() => {\n            cy.get('.OptionsContainer .Dropdown > svg')\n                .as('dropbox')\n        })\n\n        it('finds floating box on hover and fails to find it on hover off', () => {\n            cy.get('@dropbox')\n                .trigger('mouseover')\n                .then(() => {\n                    cy.get('.FloatingBox')\n                        .should('exist')\n                })\n                .trigger('mouseleave', 'bottom')\n                .then(() => {\n                    cy.get('FloatingBox')\n                        .should('not.exist')\n                })\n        })\n\n        it('logs out and removes the local storage token on sign out press', () => {\n            cy.get('@dropbox')\n                .trigger('mouseover')\n                .get('.FloatingBox')\n                .find('span')\n                .contains('Sign out of Netflix')\n                .click()\n                .then(() => {\n                    expect(localStorage.getItem('profileSelected')).to.not.exist\n                    cy.location().should(loc => {\n                        expect(loc.pathname).to.eq('/')\n                    })\n                })\n        })\n    })\n\n    context('<Search />', () => {\n        beforeEach(() => {\n            cy.get('.SearchBox')\n                .click()\n        })\n        it('opens search box on click and closes on background click', () => {\n            cy.get('.Holder')\n                .should('exist')\n\n            cy.get('.NavBar')\n                .click('center')\n                .find('.Holder')\n                .should('not.exist')\n        })\n\n        it('cross becomes visible after typing and invisible when input length is 0', () => {\n            cy.get('.Holder')\n                .children('input')\n                .type('Some movie title')\n\n            cy.get('.Holder')\n                .children('svg')\n                .last()\n                .click()\n                .should('not.exist')\n        })\n    })\n})"
  },
  {
    "path": "cypress/integration/landing-section.spec.js",
    "content": "/// <reference types=\"Cypress\" />\n\ndescribe('<LandingSection />', () => {\n    beforeEach(() => {\n        cy.visit('/')\n    })\n\n    context('<FAQComponent />', () => {\n        it('leaves number of components intact even after pressing all boxes', () => {\n            cy.get('.faqComponent')\n                .click({ multiple: true })\n                .should('have.length', 5)\n        })\n\n        it('has a set number of FAQ boxes if not clicked', () => {\n            cy.get('.faqComponent')\n                .should('have.length', 5)\n        })\n\n        it('opens an inner box on click', () => {\n            cy.get('.tv-inner > .faqComponent')\n                .first()\n                .click()\n\n            cy.get('.faqComponent')\n                .should('have.length', 6)\n        })\n    })\n\n    context('Sign In Button', () => {\n        it(\"navigates to the login page on sign in button click\", () => {\n            cy.get('.Button')\n                .contains('Sign In')\n                .click()\n\n            cy.location().should(loc => {\n                expect(loc.pathname).to.eq('/login')\n            })\n        })\n    })\n\n})"
  },
  {
    "path": "cypress/integration/login-form.spec.js",
    "content": "/// <reference types=\"Cypress\" />\n\ndescribe('<Login />', () => {\n    beforeEach(() => {\n        cy.visit('/login')\n\n        cy.get('input[name=\"email\"]')\n            .as('inputEmail')\n\n        cy.get('input[name=\"password\"]')\n            .as('passwordInput')\n    })\n\n    it('fails to submit form without any input and shows error spans for both inputs', () => {\n        cy.get('.Button')\n            .contains('Sign In')\n            .click()\n\n        cy.location().should(loc => {\n            expect(loc.pathname).to.eq('/login')\n        })\n\n        cy.get('form')\n            .children('span')\n            .should('have.length', 2)\n    })\n\n    it('shows a span text on email input focus and focus lost', () => {\n        const spanText = 'Please enter a valid email or phone number.'\n        cy.get('@inputEmail')\n            .focus()\n            .blur()\n\n        cy.get('form')\n            .children('span')\n            .should('have.length', 1)\n            .contains(spanText)\n    })\n\n    it('changes span text on password validation passed', () => {\n        const spanText = 'Your password must contain between 4 and 60 characters.'\n        cy.get('@passwordInput')\n            .focus()\n            .blur()\n\n        cy.get('form')\n            .children('span')\n            .as('text')\n\n        cy.get('@text')\n            .should('have.length', 1)\n            .contains(spanText)\n\n        cy.get('@passwordInput')\n            .type('123')\n\n        cy.get('@text')\n            .contains(spanText)\n\n        cy.get('@passwordInput')\n            .type('4')\n\n        cy.get('@text')\n            .should('not.exist')\n    })\n\n    it('logs in on valid input', () => {\n        cy.get('@inputEmail')\n            .type('example@example.com')\n\n        cy.get('@passwordInput')\n            .type('1234')\n\n        cy.get('.Button')\n            .contains('Sign In')\n            .click()\n\n        cy.location().should(loc => {\n            expect(loc.pathname).to.eq('/browse')\n        })\n    })\n})"
  },
  {
    "path": "cypress/integration/sign-in-flow.spec.js",
    "content": "/// <reference types=\"Cypress\" />\n\ndescribe('<Browse />', () => {\n    beforeEach(() => {\n        cy.validLogin()\n    })\n\n    it('opens browse section if local storage profile selection is set', () => {\n        localStorage.setItem('profileSelected', true)\n\n        cy.get('.Button')\n            .contains('Sign In')\n            .click()\n\n        cy.get('.BrowseContent')\n            .should('exist')\n    })\n\n    it('opens modal if local storage is not set, and continues to browse section', () => {\n        cy.clearLocalStorage()\n        \n        cy.get('.Button')\n            .contains('Sign In')\n            .click()\n\n        cy.get('.BrowseContent')\n            .should('not.exist')\n\n        cy.get('.ProfileDiv')\n            .should('exist')\n            .find('.ProfileCard')\n            .as('profileCard')\n            .should('have.length', 4)\n\n        cy.get('@profileCard')\n            .first()\n            .click()\n            .should(($profileCard) => {\n                expect(localStorage.getItem('profileSelected')).to.exist\n                expect($profileCard).to.not.exist\n            })\n\n        cy.get('.BrowseContent')\n            .should('exist')\n    })\n})"
  },
  {
    "path": "cypress/plugins/index.js",
    "content": "/// <reference types=\"cypress\" />\n// ***********************************************************\n// This example plugins/index.js can be used to load plugins\n//\n// You can change the location of this file or turn off loading\n// the plugins file with the 'pluginsFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/plugins-guide\n// ***********************************************************\n\n// This function is called when a project is opened or re-opened (e.g. due to\n// the project's config changing)\n\n/**\n * @type {Cypress.PluginConfig}\n */\nmodule.exports = (on, config) => {\n  // `on` is used to hook into various events Cypress emits\n  // `config` is the resolved Cypress config\n}\n"
  },
  {
    "path": "cypress/support/commands.js",
    "content": "Cypress.Commands.add('validLogin', () => {\n    cy.visit('/login')\n\n    cy.get('input[name=\"email\"]')\n        .type('example@example.com')\n\n    cy.get('input[name=\"password\"]')\n        .type('1234')\n})"
  },
  {
    "path": "cypress/support/index.js",
    "content": "// ***********************************************************\n// This example support/index.js is processed and\n// loaded automatically before your test files.\n//\n// This is a great place to put global configuration and\n// behavior that modifies Cypress.\n//\n// You can change the location of this file or turn off\n// automatically serving support files with the\n// 'supportFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/configuration\n// ***********************************************************\n\n// Import commands.js using ES2015 syntax:\nimport './commands'\n\n// Alternatively you can use CommonJS syntax:\n// require('./commands')\n"
  },
  {
    "path": "cypress.json",
    "content": "{\n    \"baseUrl\": \"http://localhost:3000\"\n}"
  },
  {
    "path": "jsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"baseUrl\": \"src\"\n    },\n    \"include\": [\n        \"src\"\n    ]\n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"netflix-clone\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"homepage\": \"https://azazel5.github.io/NetflixClone/\",\n  \"keywords\": [],\n  \"main\": \"src/index.js\",\n  \"dependencies\": {\n    \"@fortawesome/fontawesome-svg-core\": \"1.2.29\",\n    \"@fortawesome/free-solid-svg-icons\": \"5.13.1\",\n    \"@fortawesome/react-fontawesome\": \"0.1.11\",\n    \"@material-ui/core\": \"4.11.0\",\n    \"@reduxjs/toolkit\": \"^1.4.0\",\n    \"axios\": \"^0.19.2\",\n    \"react\": \"16.12.0\",\n    \"react-device-detect\": \"^1.13.1\",\n    \"react-dom\": \"16.12.0\",\n    \"react-modal\": \"^3.11.2\",\n    \"react-redux\": \"^7.2.0\",\n    \"react-router\": \"5.2.0\",\n    \"react-router-dom\": \"5.2.0\",\n    \"react-scripts\": \"3.0.1\",\n    \"react-transition-group\": \"^4.4.1\",\n    \"redux\": \"^4.0.5\",\n    \"redux-thunk\": \"^2.3.0\",\n    \"reselect\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"cypress\": \"^4.12.1\",\n    \"gh-pages\": \"^3.1.0\",\n    \"typescript\": \"3.8.3\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"cypress\": \"cypress open\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\",\n    \"predeploy\": \"npm run build\",\n    \"deploy\": \"gh-pages -d build\"\n  },\n  \"browserslist\": [\n    \">0.2%\",\n    \"not dead\",\n    \"not ie <= 11\",\n    \"not op_mini all\"\n  ]\n}\n"
  },
  {
    "path": "public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Single Page Apps for GitHub Pages</title>\n    <script type=\"text/javascript\">\n      // Single Page Apps for GitHub Pages\n      // https://github.com/rafrex/spa-github-pages\n      // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License\n      // ----------------------------------------------------------------------\n      // This script takes the current url and converts the path and query\n      // string into just a query string, and then redirects the browser\n      // to the new url with only a query string and hash fragment,\n      // e.g. http://www.foo.tld/one/two?a=b&c=d#qwe, becomes\n      // http://www.foo.tld/?p=/one/two&q=a=b~and~c=d#qwe\n      // Note: this 404.html file must be at least 512 bytes for it to work\n      // with Internet Explorer (it is currently > 512 bytes)\n\n      // If you're creating a Project Pages site and NOT using a custom domain,\n      // then set segmentCount to 1 (enterprise users may need to set it to > 1).\n      // This way the code will only replace the route part of the path, and not\n      // the real directory in which the app resides, for example:\n      // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes\n      // https://username.github.io/repo-name/?p=/one/two&q=a=b~and~c=d#qwe\n      // Otherwise, leave segmentCount as 0.\n      var segmentCount = 1;\n\n      var l = window.location;\n      l.replace(\n        l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +\n        l.pathname.split('/').slice(0, 1 + segmentCount).join('/') + '/?p=/' +\n        l.pathname.slice(1).split('/').slice(segmentCount).join('/').replace(/&/g, '~and~') +\n        (l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +\n        l.hash\n      );\n\n    </script>\n  </head>\n  <body>\n  </body>\n</html>"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n\t<meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no\">\n\t<meta name=\"theme-color\" content=\"#000000\">\n\t<!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n\t<link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\">\n\t<link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\">\n\t<!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n  <title>Netflix Clone</title>\n  <script type=\"text/javascript\">\n    // Single Page Apps for GitHub Pages\n    // https://github.com/rafrex/spa-github-pages\n    // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License\n    // ----------------------------------------------------------------------\n    // This script checks to see if a redirect is present in the query string\n    // and converts it back into the correct url and adds it to the\n    // browser's history using window.history.replaceState(...),\n    // which won't cause the browser to attempt to load the new url.\n    // When the single page app is loaded further down in this file,\n    // the correct url will be waiting in the browser's history for\n    // the single page app to route accordingly.\n    (function(l) {\n      if (l.search) {\n        var q = {};\n        l.search.slice(1).split('&').forEach(function(v) {\n          var a = v.split('=');\n          q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');\n        });\n        if (q.p !== undefined) {\n          window.history.replaceState(null, null,\n            l.pathname.slice(0, -1) + (q.p || '') +\n            (q.q ? ('?' + q.q) : '') +\n            l.hash\n          );\n        }\n      }\n    }(window.location))\n  </script>\n  <!-- End Single Page Apps for GitHub Pages -->\n</head>\n\n<body>\n\t<noscript>\n\t\tYou need to enable JavaScript to run this app.\n\t</noscript>\n\t<div id=\"root\"></div>\n\t<!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n</body>\n\n</html>"
  },
  {
    "path": "public/manifest.json",
    "content": "{\n    \"short_name\": \"React App\",\n    \"name\": \"Create React App Sample\",\n    \"icons\": [\n        {\n            \"src\": \"favicon.ico\",\n            \"sizes\": \"64x64 32x32 24x24 16x16\",\n            \"type\": \"image/x-icon\"\n        }\n    ],\n    \"start_url\": \"./index.html\",\n    \"display\": \"standalone\",\n    \"theme_color\": \"#000000\",\n    \"background_color\": \"#ffffff\"\n}"
  },
  {
    "path": "src/App.js",
    "content": "import React, { useContext } from \"react\";\nimport \"./styles.css\";\n\nimport LandingSection from \"containers/LandingSection/LandingSection\";\nimport Login from \"containers/Login/Login\";\nimport Browse from 'containers/Browse/Browse'\nimport { Switch, Route, Redirect } from \"react-router-dom\";\nimport { AuthenticationContext } from 'context/Authentication'\nimport NotFoundPage from 'components/StaticPages/NotFoundPage/NotFoundPage'\n\nexport default function App() {\n  const authContext = useContext(AuthenticationContext)\n\n  const checkAuthAndSetBrowseComponent = (propsObject) => {\n    return (authContext.authenticated || localStorage.getItem('profileSelected')) ?\n      <Browse {...propsObject} /> :\n      <Redirect to=\"/login\" />\n  }\n\n  return (\n    <div className=\"App\">\n      <Switch>\n        <Route exact path=\"/browse\" render={() => checkAuthAndSetBrowseComponent({ route: '/browse' })}>\n        </Route>\n        <Route exact path=\"/browse/tv\" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/tv' })}>\n        </Route>\n        <Route exact path=\"/browse/movies\" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/movies' })}>\n        </Route>\n        <Route exact path=\"/browse/latest\" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/latest' })}>\n        </Route>\n        <Route exact path=\"/browse/list\" render={() => checkAuthAndSetBrowseComponent({ route: '/browse/list' })}>\n        </Route>\n        <Route exact path=\"/search\" render={() => checkAuthAndSetBrowseComponent({ route: '/search' })}>\n        </Route>\n        <Route exact path=\"/login\" component={Login}>\n        </Route>\n        <Route exact path=\"/\" component={LandingSection}>\n        </Route>\n        <Route exact component={NotFoundPage}>\n        </Route>\n      </Switch>\n    </div >\n  );\n}\n"
  },
  {
    "path": "src/assets/images/index.js",
    "content": "import Weird from './weird.png'\nimport Profile from './profile.jpg'\nimport Smile from './smile.png'\nimport Normal from './normal.jpg'\nimport NetflixLogo from './netflix.png'\nimport LoginBackground from './landingPage.jpg'\n\nexport {\n    Weird, \n    Profile, \n    Smile, \n    Normal,\n    NetflixLogo, \n    LoginBackground\n} "
  },
  {
    "path": "src/baseAxios.js",
    "content": "import axios from 'axios'\n\nconst axe = axios.create({\n    baseURL: 'https://api.themoviedb.org/3/'\n})\n\nexport default axe "
  },
  {
    "path": "src/components/Modals/ProfileModal/ProfileModal.css",
    "content": ".ProfileModal {\n    top: 0;\n    left: 0;\n    height: 100%;\n    width: 100%;\n    background: #141414;\n    border: none;\n    border-radius: none;\n    padding: 0;\n    outline: none;\n}\n\n.ProfileModal > img {\n    height: 45px;\n    width: 140px;\n    margin-left: 20px;\n    padding: 8px\n}\n\n.ProfileDiv {\n    width: 60%;\n    margin: 0 auto;\n    text-align: center;\n    position: relative;\n    top: 30%;\n    transform: translateY(-50%);\n}\n\n.ProfileDiv > h1 {\n    color: rgb(255, 255, 255);\n    font-weight: lighter;\n    font-size: 6.5vw;\n}\n\n.horizontalComp {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    margin-bottom: 30px;\n    flex-wrap: wrap;\n}\n\n.ProfileButton {\n    border: 1px solid transparent;\n    border-color: grey;\n    background-color: transparent;\n    color: grey;\n    text-transform: uppercase;\n    padding: .5em 1.5em;\n    letter-spacing: 2px;\n    font-size: .9em;\n    outline: 0;\n}\n\n.ProfileButton:hover {\n    border-color: white;\n    color: white;\n}\n\n@media(min-width: 600px) {\n    .ProfileDiv > h1 {\n        font-size: 3.5vw;\n    }\n}"
  },
  {
    "path": "src/components/Modals/ProfileModal/ProfileModal.js",
    "content": "import React from 'react'\nimport Modal from 'react-modal'\nimport { NetflixLogo } from 'assets/images/'\nimport ProfileCard from 'components/UI/ProfileCard/ProfileCard'\nimport './ProfileModal.css'\n\nimport {\n    Weird,\n    Profile,\n    Smile,\n    Normal\n} from 'assets/images/'\n\n\nif (process.env.NODE_ENV !== 'test') {\n    Modal.setAppElement('#root');\n}\n\nconst profileModal = props => {\n    const { modalOpen, profileClickHandler } = props\n\n    return (\n        <Modal\n            className=\"ProfileModal\"\n            isOpen={modalOpen}\n            contentLabel=\"Modal is open\"\n            shouldCloseOnEsc={false}\n        >\n            <img src={NetflixLogo} alt=\"Logo\" />\n            <div className=\"ProfileDiv\">\n                <h1>Who's watching?</h1>\n\n                <div className=\"horizontalComp\">\n                    <ProfileCard profileImage={Profile} username=\"Pushpa\" onClick={profileClickHandler} />\n                    <ProfileCard profileImage={Weird} username=\"Shammy\" onClick={profileClickHandler} />\n                    <ProfileCard profileImage={Smile} username=\"PQ\" onClick={profileClickHandler} />\n                    <ProfileCard profileImage={Normal} username=\"Subhanga\" onClick={profileClickHandler} />\n                </div>\n\n                <button className={\"ProfileButton\"}>\n                    MANAGE PROFILES\n                </button>\n            </div>\n        </Modal>\n\n    )\n}\n\nexport default profileModal"
  },
  {
    "path": "src/components/Modals/VideoModal/VideoModal.css",
    "content": ".ModalStyles {\n    position: absolute;\n    left: 50%;\n    top: 50%;\n    transform: translate(-50%, -50%);\n    height: 45%;\n    width: 100%;\n    background: rgba(0,0,0,0.966);\n    border: none;\n    outline: none;\n    font-size: 0.8rem;\n    padding: 0;\n}\n\n.VideoDetailSection {\n    height: 100%;\n}\n\n.shadowedSection > * {\n    margin-bottom: 1rem;\n}\n\n.shadowedSection {\n    display: flex;\n    flex-direction: column;\n    height: 100%;\n    background: linear-gradient(90deg, rgba(0,0,0,0.966) 55%, transparent);\n    color: white;\n    padding: 18px 0 12px 4%;\n}\n\n.shadowedSection div {\n    width: 65%;\n    color: #999;\n} \n\n.horizontalStyles {\n    display: flex;\n}\n\n.horizontalStyles > button {\n    margin-right: 10px;\n}\n\n.Overview {\n    overflow: scroll;\n}\n\n@media(min-width: 600px) {\n    .ModalStyles {\n        height: 60%;\n        font-size: 1rem;\n    }\n\n    .shadowedSection {\n        background: linear-gradient(90deg, rgba(0,0,0,0.966) 35%, transparent);\n    }\n\n    .shadowedSection div {\n        width: 40%;\n    } \n\n    .shadowedSection > * {\n        margin-bottom: 2rem;\n    }\n    \n}\n\n@media(min-width: 1650px) {\n    .ModalStyles {\n        font-size: 1.6rem;\n    }\n}"
  },
  {
    "path": "src/components/Modals/VideoModal/VideoModal.js",
    "content": "import React from 'react'\nimport './VideoModal.css'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faTimes } from '@fortawesome/free-solid-svg-icons'\nimport Modal from 'react-modal'\nimport { getSeasonsOrMovieLength } from 'utils/time'\nimport { faPlay, faPlus } from '@fortawesome/free-solid-svg-icons'\nimport Button from 'components/UI/Button/Button'\nimport useHoverStyleButton from 'hooks/useHoverStyleButton'\n\n\nif (process.env.NODE_ENV !== 'test') {\n    Modal.setAppElement('#root');\n}\n\n// Don't move this to css; it has to be here for the shouldCloseOnOverlay prop to work \nconst overlayStyle = {\n    overlay: {\n        backgroundColor: 'rgba(17,17,17,0.7)'\n    }\n}\n\nconst VideoModal = props => {\n    const { videoDetailModal, closeModalHandler, videoInfo } = props\n    const [buttonHovered, onButtonHoverHandler] = useHoverStyleButton({\n        'playButton': true,\n        'plusButton': true\n    })\n\n    const {\n        vote_average, seasons, runtime,\n        backdrop_path, poster_path, title, name,\n        release_date, first_air_date,\n        overview\n    } = videoInfo\n\n    const voteAverage = vote_average * 10\n    const voteStyle = { color: voteAverage > 49 ? '#46d369' : 'red' }\n    const videoTime = getSeasonsOrMovieLength(seasons, runtime)\n\n    const styles = {\n        backgroundImage: `url(https://image.tmdb.org/t/p/original/${backdrop_path || poster_path}`,\n        backgroundSize: 'cover'\n    }\n\n    return (\n        <Modal\n            className=\"ModalStyles\"\n            style={overlayStyle}\n            isOpen={videoDetailModal}\n            contentLabel=\"Modal is open\"\n            shouldCloseOnOverlayClick\n            onRequestClose={closeModalHandler}\n        >\n            <div className=\"VideoDetailSection\" style={styles}>\n                <FontAwesomeIcon onClick={closeModalHandler} style={{ color: 'white', float: 'right', padding: '14px', cursor: 'pointer' }}\n                    size=\"2x\"\n                    icon={faTimes}\n                />\n                <div className=\"shadowedSection\">\n                    <h1>{title || name}</h1>\n                    <div className=\"horizontalStyles\">\n                        <span style={voteStyle}>{`Rating: ${voteAverage}%`} &nbsp;</span>\n                        <span>{(release_date || first_air_date).substring(0, 4)} &nbsp;</span>\n                        {videoTime}\n                    </div>\n                    <div className=\"Overview\">{overview}</div>\n                    <div className=\"horizontalStyles\">\n                        <Button\n                            backgroundColor=\"#fff\"\n                            textColor=\"rgb(24, 24, 24)\"\n                            playButton\n                            height=\"38px\"\n                            width=\"138px\"\n                            image\n                            icon={faPlay}\n                            onButtonHover={() => onButtonHoverHandler('playButton')}\n                            hoverStatus={buttonHovered['playButton']}>\n                            Play\n                        </Button>\n\n                        <Button\n                            backgroundColor=\"rgba(133, 133, 133, 0.6)\"\n                            textColor=\"white\"\n                            height=\"38px\"\n                            width=\"138px\"\n                            playButton\n                            image\n                            icon={faPlus}\n                            onButtonHover={() => onButtonHoverHandler('plusButton')}\n                            hoverStatus={buttonHovered['plusButton']}>\n                            My List\n                        </Button>\n                    </div>\n                </div>\n            </div>\n        </Modal>\n    )\n}\n\nexport default React.memo(VideoModal)"
  },
  {
    "path": "src/components/Navigation/Dropdown/Dropdown.css",
    "content": ".Dropdown {\n    position: relative;\n    display: inline-block;\n    z-index: 100;\n    cursor: pointer;\n}\n\n.FloatingBox {\n    position: absolute;\n    margin-left: 0;\n    padding: 0;\n    border: solid 1px rgba(255,255,255,.15);\n    display: flex;\n    flex-direction: column;\n    background: #141414;\n}\n\n.FloatingBox > * {\n    padding: 1vh;\n}\n\n.FloatingBox span:hover {\n    text-decoration: underline;\n}\n"
  },
  {
    "path": "src/components/Navigation/Dropdown/Dropdown.js",
    "content": "import React from 'react'\nimport './Dropdown.css'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faAngleDown } from \"@fortawesome/free-solid-svg-icons\";\n\nconst Dropdown = props => {\n    const {\n        iconHoveredInHandler, iconHoveredOutHandler, dropdown,\n        floatingBoxHoveredInHandler, floatingBoxHoveredOutHandler, content,\n        boxSizing, onItemClickCloseBoxHandler\n    } = props\n\n    const { iconHovered, floatingBoxHovered } = dropdown\n    return (\n        <div className=\"Dropdown\">\n            <FontAwesomeIcon\n                size=\"lg\"\n                icon={faAngleDown}\n                color=\"white\"\n                onMouseOver={iconHoveredInHandler}\n                onMouseLeave={iconHoveredOutHandler}\n            />\n\n            {(iconHovered || floatingBoxHovered) && (\n                <div className=\"FloatingBox\" style={boxSizing}\n                    onMouseOver={floatingBoxHoveredInHandler}\n                    onMouseLeave={floatingBoxHoveredOutHandler}\n                    onClick={onItemClickCloseBoxHandler}\n                >\n                    {content}\n                </div>)}\n        </div>\n    )\n}\n\nexport default Dropdown"
  },
  {
    "path": "src/components/StaticPages/ErrorPage/ErrorPage.css",
    "content": ".ErrorPage {\n    height: 100vh;\n    background: #141414;\n    color: white;\n    padding: 95px;\n}\n\n.ErrorPage-Items {\n    text-align: center;\n}"
  },
  {
    "path": "src/components/StaticPages/ErrorPage/ErrorPage.js",
    "content": "import React from 'react'\nimport './ErrorPage.css'\n\n/**\n * I have to check for error message or simply an error because of I've handled errors in the \n * reducers. The RTK documentation recommends checking for a promise rejection with value or \n * with an error; thus, when it rejects with value, there will be a status_message, and if that\n * doesn't happen, there will simply be an error object \n */\nconst ErrorPage = props => {\n    const { errors } = props\n\n    let errorObjs\n    if (Array.isArray(errors)) {\n        errorObjs = errors[0] || errors[1] || errors[2]\n    }\n\n    const errorType = errorObjs || errors\n    const errorMessage = errorType.message ? errorType.message : errorType\n    return (\n        <div className=\"ErrorPage\">\n            <div className=\"ErrorPage-Items\">\n                <h2>No matching tiles found.</h2>\n                <h2>{errorMessage}</h2>\n            </div>\n        </div>\n    )\n}\n\nexport default ErrorPage"
  },
  {
    "path": "src/components/StaticPages/LoadingScreen/LoadingScreen.css",
    "content": ".LoadingScreen {\n    background: #141414;\n    min-height: 100vh;\n}"
  },
  {
    "path": "src/components/StaticPages/LoadingScreen/LoadingScreen.js",
    "content": "import React from 'react'\nimport './LoadingScreen.css'\n\nconst loadingScreen = props => {\n    return (\n        <div className=\"LoadingScreen\">\n        </div>\n    )\n}\n\nexport default loadingScreen"
  },
  {
    "path": "src/components/StaticPages/NotFoundPage/NotFoundPage.css",
    "content": ".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: 50%;\r\n  -webkit-transform: translate(-50%, -50%);\r\n      -ms-transform: translate(-50%, -50%);\r\n          transform: translate(-50%, -50%);\r\n}\r\n\r\n.notfound {\r\n  max-width: 460px;\r\n  width: 100%;\r\n  text-align: center;\r\n  line-height: 1.4;\r\n}\r\n\r\n.notfound .notfound-404 {\r\n  position: relative;\r\n  width: 180px;\r\n  height: 180px;\r\n  margin: 0px auto 50px;\r\n}\r\n\r\n.notfound .notfound-404>div:first-child {\r\n  position: absolute;\r\n  left: 0;\r\n  right: 0;\r\n  top: 0;\r\n  bottom: 0;\r\n  background: #ffa200;\r\n  -webkit-transform: rotate(45deg);\r\n      -ms-transform: rotate(45deg);\r\n          transform: rotate(45deg);\r\n  border: 5px dashed #000;\r\n  border-radius: 5px;\r\n}\r\n\r\n.notfound .notfound-404>div:first-child:before {\r\n  content: '';\r\n  position: absolute;\r\n  left: -5px;\r\n  right: -5px;\r\n  bottom: -5px;\r\n  top: -5px;\r\n  -webkit-box-shadow: 0px 0px 0px 5px rgba(0, 0, 0, 0.1) inset;\r\n          box-shadow: 0px 0px 0px 5px rgba(0, 0, 0, 0.1) inset;\r\n  border-radius: 5px;\r\n}\r\n\r\n.notfound .notfound-404 h1 {\r\n  font-family: 'Cabin', sans-serif;\r\n  color: #000;\r\n  font-weight: 700;\r\n  margin: 0;\r\n  font-size: 90px;\r\n  position: absolute;\r\n  top: 50%;\r\n  -webkit-transform: translate(-50%, -50%);\r\n      -ms-transform: translate(-50%, -50%);\r\n          transform: translate(-50%, -50%);\r\n  left: 50%;\r\n  text-align: center;\r\n  height: 40px;\r\n  line-height: 40px;\r\n}\r\n\r\n.notfound h2 {\r\n  font-family: 'Cabin', sans-serif;\r\n  font-size: 33px;\r\n  font-weight: 700;\r\n  text-transform: uppercase;\r\n  letter-spacing: 7px;\r\n}\r\n\r\n.notfound p {\r\n  font-family: 'Cabin', sans-serif;\r\n  font-size: 16px;\r\n  color: #000;\r\n  font-weight: 400;\r\n}\r\n\r\n.notfound a {\r\n  font-family: 'Cabin', sans-serif;\r\n  display: inline-block;\r\n  padding: 10px 25px;\r\n  background-color: #8f8f8f;\r\n  border: none;\r\n  border-radius: 40px;\r\n  color: #fff;\r\n  font-size: 14px;\r\n  font-weight: 700;\r\n  text-transform: uppercase;\r\n  text-decoration: none;\r\n  -webkit-transition: 0.2s all;\r\n  transition: 0.2s all;\r\n}\r\n\r\n.notfound a:hover {\r\n  background-color: #2c2c2c;\r\n}\r\n"
  },
  {
    "path": "src/components/StaticPages/NotFoundPage/NotFoundPage.js",
    "content": "import React from 'react'\r\nimport './NotFoundPage.css'\r\n\r\nconst notFoundPage = () => {\r\n\treturn (\r\n\t\t<div class=\"Parent\">\r\n\t\t\t<div class=\"notfound\">\r\n\t\t\t\t<div class=\"notfound-404\">\r\n\t\t\t\t\t<div></div>\r\n\t\t\t\t\t<h1>404</h1>\r\n\t\t\t\t</div>\r\n\t\t\t\t<h2>Page not found</h2>\r\n\t\t\t\t<p>The page you are looking for might have been removed, had its name changed, or doesn't exist.</p>\r\n\t\t\t\t<a href=\"/NetflixClone/login\">Back to login</a>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t)\r\n}\r\n\r\nexport default notFoundPage"
  },
  {
    "path": "src/components/UI/Button/Button.css",
    "content": ".Button {\n    display: inline-block;\n    border-radius: 3px;\n    border: 0;\n    outline: 0;\n    font-size: 1rem; \n}\n\n@media(max-height: 600px) {\n    .Button {\n        font-size: 0.9rem;\n    }\n}"
  },
  {
    "path": "src/components/UI/Button/Button.js",
    "content": "import React from \"react\";\nimport './Button.css'\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\n\n/**\n * A custom button component. It has a set color scheme and takes in height, eeight, and image\n * as props. Netflix seems to use a standard button component with different widths.\n */\nconst button = props => {\n  let iconHolder = null;\n  const {\n    playButton, buttonSize, icon,\n    height, width, backgroundColor, textColor,\n    image, onButtonHover, hoverStatus\n  } = props\n\n  if (image) {\n    iconHolder = (\n      <FontAwesomeIcon\n        style={playButton ? { marginRight: '15px' } : { marginLeft: \"5px\" }}\n        size={buttonSize}\n        icon={icon}\n      />\n    );\n  }\n\n  let orderButton = (\n    <>\n      {props.children}\n      {iconHolder}\n    </>\n  )\n\n  if (playButton) {\n    orderButton = (\n      <>\n        {iconHolder}\n        {props.children}\n      </>\n    )\n  }\n\n  const conditionalStyles = {\n    height: height,\n    width: width,\n    backgroundColor: backgroundColor,\n    color: textColor,\n    opacity: !hoverStatus && '80%'\n  };\n\n  return (\n    <button\n      className=\"Button\" style={conditionalStyles}\n      onMouseEnter={onButtonHover}\n      onMouseLeave={onButtonHover}>\n      {orderButton}\n    </button>\n  );\n};\n\nexport default button;\n\n"
  },
  {
    "path": "src/components/UI/CircularSoundButton/CircularSoundButton.css",
    "content": ".RoundButton {\n    display: inline-block;\n    border: 1px solid white;\n    border-radius: 50%;\n    background: transparent;\n    color: white;\n    text-align: center;\n    font-size: 16px;\n    outline: none;\n}\n\n@media(min-width: 1650px) {\n    .RoundButton {\n       font-size: 32px;\n    }\n}"
  },
  {
    "path": "src/components/UI/CircularSoundButton/CircularSoundButton.js",
    "content": "import React from 'react'\nimport './CircularSoundButton.css'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faVolumeUp, faVolumeMute } from \"@fortawesome/free-solid-svg-icons\";\n\nconst circularSoundButton = props => {\n    const { topTrailerSoundOn, topTrailerSoundButtonClickHandler } = props\n    return (\n        <button className=\"RoundButton\" onClick={topTrailerSoundButtonClickHandler}>\n            <FontAwesomeIcon icon={topTrailerSoundOn ? faVolumeUp : faVolumeMute} />\n        </button>\n    )\n}\n\nexport default circularSoundButton"
  },
  {
    "path": "src/components/UI/DarkComponent/DarkComponent.js",
    "content": "import React from \"react\";\n\nconst darkComponentTextAlignStyles = {\n  display: 'flex',\n  flexDirection: 'column',\n  textAlign: 'center'\n}\n\nconst darkComponent = props => {\n  const { topText, fontSize, bottomText, image } = props\n  return (\n    <>\n      <div style={darkComponentTextAlignStyles}>\n        {topText && (\n          <h3 style={{ fontSize: fontSize }}>{topText}</h3>\n        )}\n        {bottomText && (\n          <h3 style={{ fontSize: fontSize }}>{bottomText}</h3>\n        )}\n      </div>\n\n      {image && <img src={image} alt=\"dark component\" />}\n    </>\n  );\n};\n\nexport default darkComponent;\n"
  },
  {
    "path": "src/components/UI/FAQComponent/FAQComponent.css",
    "content": ".faqComponent {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0.8em 2.2em 0.8em 1.2em;\n  width: 100%;\n  margin-top: 10px;\n  background: #303030;\n  font-size: 20px;\n  cursor: pointer;\n}\n\n.faq-animation-enter {\n        opacity: 0;\n}\n\n.faq-animation-enter-active {\n        opacity: 1;\n\t-webkit-animation: swing-in-top-bck 0.6s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;\n\t        animation: swing-in-top-bck 0.6s cubic-bezier(0.175, 0.885, 0.320, 1.275) both;\n}\n\n.faq-animation-exit {\n        opacity: 1;\n}\n\n.faq-animation-exit-active {\n        opacity: 0;\n\t-webkit-animation: swing-out-top-bck 0.35s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;\n\t        animation: swing-out-top-bck 0.35s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;\n}\n\n@-webkit-keyframes swing-in-top-bck {\n  0% {\n    -webkit-transform: rotateX(70deg);\n            transform: rotateX(70deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 0;\n  }\n  100% {\n    -webkit-transform: rotateX(0deg);\n            transform: rotateX(0deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 1;\n  }\n}\n@keyframes swing-in-top-bck {\n  0% {\n    -webkit-transform: rotateX(70deg);\n            transform: rotateX(70deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 0;\n  }\n  100% {\n    -webkit-transform: rotateX(0deg);\n            transform: rotateX(0deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 1;\n  }\n}\n\n @-webkit-keyframes swing-out-top-bck {\n  0% {\n    -webkit-transform: rotateX(0deg);\n            transform: rotateX(0deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 1;\n  }\n  100% {\n    -webkit-transform: rotateX(-100deg);\n            transform: rotateX(-100deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 0;\n  }\n}\n\n@keyframes swing-out-top-bck {\n  0% {\n    -webkit-transform: rotateX(0deg);\n            transform: rotateX(0deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 1;\n  }\n  100% {\n    -webkit-transform: rotateX(-100deg);\n            transform: rotateX(-100deg);\n    -webkit-transform-origin: top;\n            transform-origin: top;\n    opacity: 0;\n  }\n}\n\n@media (min-width: 600px) {\n  .faqComponent {\n    width: 69%;\n  }\n}\n\n@media(min-width: 1650px) {\n    .faqComponent {\n        font-size: 1.75rem;\n      }      \n}\n"
  },
  {
    "path": "src/components/UI/FAQComponent/FAQComponent.js",
    "content": "import React from \"react\";\nimport \"./FAQComponent.css\";\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faPlus, faMinus } from \"@fortawesome/free-solid-svg-icons\";\nimport { CSSTransition } from 'react-transition-group'\n\nconst faqComponent = props => {\n  const { faqOpenHandler, text, boxOpen, boxText } = props\n  return (\n    <>\n      <div\n        className=\"faqComponent\"\n        onClick={faqOpenHandler}\n      >\n        <div>{text}</div>\n        <FontAwesomeIcon icon={boxOpen ? faMinus : faPlus} />\n      </div>\n\n      <CSSTransition in={boxOpen} classNames=\"faq-animation\" timeout={500} unmountOnExit>\n        <div className=\"faqComponent\" style={{ marginTop: \"1.5px\" }}>\n          {boxText}\n        </div>\n      </CSSTransition>\n    </>\n  );\n};\n\nexport default faqComponent;\n"
  },
  {
    "path": "src/components/UI/ProfileCard/ProfileCard.css",
    "content": ".ProfileCard {\n    width: 25%;\n    margin: 15px;\n    cursor: pointer;\n}\n\n.ProfileCardDropdown {\n    width: 90%;\n    display: flex;\n    align-items: center;\n}\n\n.ProfileCardDropdown > * {\n    margin-right: 10px;\n}\n\n.ProfileCardDropdown:hover span {\n    text-decoration: underline;\n}\n\n.ProfileCardDropdown img {\n    width: 20%;\n    height: 100%;\n}\n\n.ProfileCard span {\n    font-size: 3vw;\n    color: grey;\n}\n\n.ProfileCard img {\n    width: 100%;\n    height: 100%;\n    margin-bottom: 10px;\n    border: 3px solid transparent;\n}\n\n.ProfileCard:hover img {\n    border-color: white;\n}\n\n.ProfileCard:hover span {\n    color: white;\n}\n\n@media(min-width: 600px) {\n    .ProfileCard {\n        width: 18%;\n        height: 20%;\n    }\n\n    .ProfileCard span {\n        font-size: 1.3vw;\n    }\n}\n\n\n"
  },
  {
    "path": "src/components/UI/ProfileCard/ProfileCard.js",
    "content": "import React from 'react'\nimport './ProfileCard.css'\n\nconst ProfileCard = props => {\n    return (\n        <div className={props.dropdown ? \"ProfileCardDropdown\": \"ProfileCard\"} onClick={props.onClick}>\n            <img src={props.profileImage} alt=\"profile\" />\n            <span>{props.username}</span>\n        </div>\n    )\n}\n\nexport default ProfileCard"
  },
  {
    "path": "src/components/Video/TopTrailerComponent/TopTrailerComponent.css",
    "content": ".VideoComponent {\n    height: 580px;\n    width: 100%;\n    background-position: center;\n    background-repeat: no-repeat;\n    background-size: cover;\n    position: relative;\n}\n\n@media(min-width: 1650px) {\n    .VideoComponent {\n        height: 800px;\n    }\n}"
  },
  {
    "path": "src/components/Video/TopTrailerComponent/TopTrailerComponent.js",
    "content": "import React from 'react'\nimport './TopTrailerComponent.css'\n\nconst TopTrailerComponent = (props) => {\n    const backgroundPicture = {\n        backgroundImage: `url(${props.image})`\n    }\n    return (\n        <div className=\"VideoComponent\" style={backgroundPicture}>\n            {props.children}\n        </div>\n    )\n}\n\nexport default TopTrailerComponent"
  },
  {
    "path": "src/components/Video/VideoCard/VideoCard.css",
    "content": ".VideoCard {\n    display: flex;\n    align-items: center;\n    cursor: pointer;\n    height: 100%;\n    width: 100%;\n}\n\n.VideoCard:hover .VideoInfo {\n    display: block;\n}\n\n.NetflixOriginalCard:hover .VideoInfo {\n    display: block;\n}\n\n.VideoInfo {\n    display: none;\n    padding: 10px;\n    margin-top: 15px;\n    font-weight: 400;\n}\n\n.VideoInfo h6 {\n    margin: 0;\n}\n\n.horizontalStyle {\n    display: flex;\n    flex-wrap: wrap;\n}\n\n.horizontalStyle span {\n    font-size: 0.5em;\n}\n\n\n"
  },
  {
    "path": "src/components/Video/VideoCard/VideoCard.js",
    "content": "import React from 'react'\nimport './VideoCard.css'\nimport { getSeasonsOrMovieLength } from 'utils/time'\n\nconst videoCard = (props) => {\n    const {\n        name, poster_path, genres, runtime, seasons,\n        vote_average\n    } = props\n\n    const image = `url(https://image.tmdb.org/t/p/w500/${poster_path})`\n\n    const styles = {\n        backgroundImage: image,\n        backgroundSize: 'cover',\n    }\n\n    let timeSpan = getSeasonsOrMovieLength(seasons, runtime)\n    const genreList = genres && genres.map((genre, index) => (\n        <span key={genre.id}>\n            {genre.name} {index !== genres.length - 1 ? '●' : null} &nbsp;\n        </span>\n    ))\n\n    return (\n        <div className=\"VideoCard\" style={styles}>\n            {genreList ? <div className=\"VideoInfo\">\n                <h6>{name}</h6>\n                <div className=\"horizontalStyle\">\n                    <span>{vote_average} &nbsp;</span>\n                    {timeSpan}\n                </div>\n                <div className=\"horizontalStyle\">\n                    {genreList}\n                </div>\n            </div> : null}\n        </div>\n    )\n}\n\nexport default React.memo(videoCard)"
  },
  {
    "path": "src/components/Video/VideoCarousel/VideoCarousel.css",
    "content": ".CarouselParent {\n    position: relative;\n    margin-bottom: 30px;\n}\n\n.VideoCarousel {\n    color: #e5e5e5;\n    font-size: 2.4vw;\n    overflow-x: auto;\n    margin: 0 4% 0 4%;\n}\n\n.VideoCarousel::-webkit-scrollbar {\n    display: none;\n}\n\n.items {\n    display: inline-flex;\n    height: 80px;\n}\n\n.netflix-items {\n    display: inline-flex;\n    height: 300px;\n    overflow: x;\n}\n\n.item {\n    position: relative;\n    display: block;\n    transition: transform 500ms;\n    width: 100px;\n    height: 100%;\n    margin-right: 7px;\n}\n\n.items .item:focus,\n.items .item:hover {\n  transform: scale(1.1);\n}\n\n.netflix-item {\n    position: relative;\n    display: block;\n    transition: transform 500ms;\n    width: 130px;\n    height: 100%;\n    margin-right: 7px;\n}\n\n.netflix-items .netflix-item:focus,\n.netflix-items .netflix-item:hover {\n    transform: scale(1.1);\n}\n\n.CarouselParent:hover > .NavItem {\n    display: none;\n}\n\n.NavItem {\n    display: none;\n}\n\n@media(min-width: 600px) {\n    .VideoCarousel {\n        font-size: 1.4vw;\n    }\n    .items {\n        height: 140px;\n    }\n    .item {\n        width: 230px;\n        height: 100%;\n        margin-right: 7px;\n    }\n    .netflix-items {\n        height: 420px;\n    }\n    .netflix-item {\n        width: 230px;\n        height: 100%;\n        margin-right: 7px;\n    }\n    /* Different animation styles for the first and last items */\n    .items .item:first-child:focus,\n    .items .item:first-child:hover,\n    .items .item:last-child:focus,\n    .items .item:last-child:hover {\n        transform: scale(1.1);\n    }\n    .item:first-child:focus~.item,\n    .item:first-child:hover~.item {\n        transform: translateX(10%);\n    }\n    .item:focus~.item,\n    .item:hover~.item {\n        transform: translateX(20%);\n    }\n    .items .item:not(:first-child):not(:last-child):focus,\n    .items .item:not(:first-child):not(:last-child):hover {\n        transform: scale(1.3);\n    }\n    .netflix-item:focus~.netflix-item,\n    .netflix-item:hover~.netflix-item {\n        transform: translateX(10%);\n    }\n\n    .CarouselParent:hover > .NavItem {\n        display: block;\n        position: absolute;\n        top: 0;\n        bottom: 0;\n        width: 4%;\n        color: white;\n        background-color: transparent;\n        outline: thin;\n        border: none;\n    }\n\n    .CarouselParent:hover >  .NavItem:hover {\n        background-color: black;\n    }\n    \n    .NavItem {\n        display: none;\n    }\n    \n    .Left {\n        left: 0;\n    }\n    \n    .Right {\n        right: 0;\n    }\n}\n\n@media(min-width: 1650px) {\n    .items {\n        height: 240px;\n    }\n    .item {\n        width: 330px;\n        height: 100%;\n        margin-right: 7px;\n    }\n    .netflix-items {\n        height: 520px;\n    }\n    .netflix-item {\n        width: 330px;\n        height: 100%;\n        margin-right: 7px;\n    }\n}"
  },
  {
    "path": "src/components/Video/VideoCarousel/VideoCarousel.js",
    "content": "import React, { useState, useRef } from 'react'\nimport './VideoCarousel.css'\n\nimport VideoCard from '../VideoCard/VideoCard'\nimport { buildVideoMetadata } from 'utils/transformations'\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faChevronRight, faChevronLeft } from \"@fortawesome/free-solid-svg-icons\";\nimport { scrollTo } from 'utils/animations'\n\n\nconst VideoCarousel = props => {\n    const {\n        carouselVideo, carouselName,\n        carouselHoverHandler, videoInfo,\n        carouselClickHandler\n    } = props\n\n    const carouselRef = useRef()\n    const [disableScrolling, setDisableScrolling] = useState(false)\n\n    const isNetflixOriginalCard = carouselName === \"Netflix Originals\" ? true : false\n    const itemClass = []\n    const itemsClass = []\n    // Setting different transition styles for the netflix original card \n    if (!isNetflixOriginalCard) {\n        itemClass.push(\"item\")\n        itemsClass.push(\"items\")\n    } else {\n        itemClass.push(\"netflix-item\")\n        itemsClass.push(\"netflix-items\")\n    }\n\n    const scrollOnAbsoluteButtonClick = scrollOffset => {\n        setDisableScrolling(true)\n        scrollTo(carouselRef.current, carouselRef.current.scrollLeft + scrollOffset, 1250, () => {\n            setDisableScrolling(false)\n        })\n    }\n\n    /**\n     * The mediaType property only exists in the trending API call. For the sake of using the same \n     * function 'videoDetailRequest' for multiple individual API calls, I use this mediaType \n     * variable to judge whether a video is a TV show or a movie. This fails for API calls, such as\n     * top rated or netflix originals. Thus, I have created a 'hacky' way of determining that by\n     * checking if two properties exist: the first_air_date (only for tv shows) or the release_date\n     * (only for movies).\n     */\n    const videoCards = carouselVideo.map(item => {\n        const { mediaType, extraInfo } = buildVideoMetadata(item, videoInfo)\n        return (\n            item.poster_path && <div className={itemClass.join(' ')} key={item.id}\n                onClick={() => carouselClickHandler(item.id, mediaType)}\n                onMouseEnter={() => carouselHoverHandler(item.id, mediaType)}>\n                <VideoCard\n                    name={item.name || item.title}\n                    vote_average={item.vote_average}\n                    poster_path={item.poster_path}\n                    netflixOriginalCard={isNetflixOriginalCard}\n                    {...extraInfo}\n                />\n            </div>\n        )\n    })\n\n    return (\n        <div className=\"CarouselParent\">\n            <div className=\"VideoCarousel\" ref={carouselRef}>\n                <h4>{carouselName}</h4>\n                <div className={itemsClass.join(\" \")}>\n                    {videoCards}\n                </div>\n            </div >\n\n            <button\n                className=\"Left NavItem\"\n                disabled={disableScrolling}\n                onClick={() => scrollOnAbsoluteButtonClick(-1250)}>\n                <FontAwesomeIcon icon={faChevronLeft} size=\"2x\" />\n            </button>\n            <button\n                className=\"Right NavItem\"\n                disabled={disableScrolling}\n                onClick={() => scrollOnAbsoluteButtonClick(1250)}>\n                <FontAwesomeIcon icon={faChevronRight} size=\"2x\" />\n            </button>\n        </div>\n    )\n}\n\nexport default React.memo(VideoCarousel)"
  },
  {
    "path": "src/containers/Browse/Browse.js",
    "content": "import React, { useState } from 'react'\n\nimport ProfileModal from 'components/Modals/ProfileModal/ProfileModal'\nimport { useHistory } from 'react-router-dom'\nimport Layout from 'hoc/Layout'\nimport SearchContent from './SearchContent/SearchContent'\nimport { Home, Movies, Tv, LatestVideo, List } from './routes'\n\n/**\n * Remember: the component where you want to use the context is the one which you wrap\n * with the provider.\n */\nconst Browse = props => {\n    // Render different videoSections based on this route prop\n    const { route } = props\n    const initialState = localStorage.getItem('profileSelected') ? false : true\n    const [modal, setModal] = useState(initialState)\n    const history = useHistory()\n\n    const profileClickHandler = () => {\n        setModal(false)\n        localStorage.setItem('profileSelected', true)\n    }\n\n    let browseContent\n    if (route === '/browse') {\n        browseContent = <Home />\n    } else if (route === '/browse/movies') {\n        browseContent = <Movies />\n    } else if (route === '/browse/tv') {\n        browseContent = <Tv />\n    } else if (route === '/browse/latest') {\n        browseContent = <LatestVideo />\n    } else if(route === '/browse/list') {\n        browseContent = <List />\n    }\n    else if (route === '/search') {\n        browseContent = <SearchContent searchParam={history.location.search.substring(3)} />\n    }\n\n    return (\n        <>\n            <ProfileModal modalOpen={modal} profileClickHandler={profileClickHandler} />\n            {!modal && <Layout>\n                {browseContent}\n            </Layout>}\n        </>\n    )\n}\n\n/**\n * Remember the thing which gave you trouble here. Never mutate state directly. Since I didn't create\n * a copy of the trending state array, I kept splicing each item all over the place, which \n * caused unnecessary problems. \n */\n\nexport default Browse"
  },
  {
    "path": "src/containers/Browse/BrowseContent/BrowseContent.css",
    "content": ".TextsAndButtons {\n    display: flex;\n    align-items: flex-end;\n    justify-content: space-between;\n    color: white;\n    margin: 0 4% 0 4%;\n    height: 100%;\n}\n\n.verticalItem {\n    display: flex;\n    flex-direction: column;\n    justify-content: flex-end;\n    margin-bottom: 80px;\n    height: 200px;\n    max-width: 50%;\n}\n\n.verticalItem h3 {\n    margin: 0;\n    font-size: 3.6vw;\n}\n\n.verticalItem span {\n    font-size: 3.4vw;\n}\n\n.verticalItem > * {\n    padding: 10px;\n}\n\n.horizontalButtonsHolder {\n    display: flex;\n    align-items: center;\n    width: 100%;\n    height: 15%;\n}\n\n.horizontalButtonsHolder > * {\n    margin-right: 4px;\n    font-weight: bold;\n}\n\n.horizontalButtonsHolder button {\n    height: 37px;\n    width: 118px;\n    font-size: 0.8rem;\n}\n\n.Carousels {\n    padding-bottom: 10px;\n}\n\n@media(min-width: 600px) {\n    .horizontalButtonsHolder > * {\n        margin-right: 10px;\n        font-weight: bold;\n    }\n\n    .horizontalButtonsHolder button {\n        height:38px;\n        width: 138px;\n        font-size: 1rem;\n    }\n\n    .verticalItem span {\n        font-size: 1.4vw;\n    }\n\n    .verticalItem h3 {\n        font-size: 1.6vw;\n    }\n}\n\n@media(min-width: 1650px) {\n\n}"
  },
  {
    "path": "src/containers/Browse/BrowseContent/BrowseContent.js",
    "content": "import React, { useState } from 'react'\nimport './BrowseContent.css'\n\nimport TopTrailerComponent from 'components/Video/TopTrailerComponent/TopTrailerComponent'\nimport Button from 'components/UI/Button/Button'\nimport VideoCarousel from 'components/Video/VideoCarousel/VideoCarousel'\nimport { faPlay, faInfoCircle } from '@fortawesome/free-solid-svg-icons'\nimport { buildVideoModal } from 'utils/transformations'\nimport useVideoInfoHandlers from 'hooks/useVideoInfoHandlers'\nimport ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'\nimport useHoverStyleButton from 'hooks/useHoverStyleButton'\nimport CircularSoundButton from 'components/UI/CircularSoundButton/CircularSoundButton'\n\nconst BrowseContent = props => {\n    const [\n        videoInfo, videoInfoError, detailModal, cardClickHandler,\n        cardHoverHandler, closeModalHandler\n    ] = useVideoInfoHandlers()\n\n    const [buttonHovered, onButtonHoverHandler] = useHoverStyleButton({\n        'playButton': true,\n        'infoButton': true\n    })\n\n    const [topTrailerSoundOn, setTopTrailerSoundOn] = useState(true)\n\n    const { videoSections } = props\n    const [firstVideo, ...remainingVideos] = videoSections[0].videos\n    const imageUrl = firstVideo ? `https://image.tmdb.org/t/p/original/${firstVideo.poster_path}` : null\n\n    const detailModalComponent = buildVideoModal(detailModal, videoInfo, { closeModalHandler })\n\n    const carousels = videoSections.map((videoSection, index) => (\n        <VideoCarousel key={videoSection.title}\n            carouselName={videoSection.title}\n            carouselVideo={index === 0 ? remainingVideos : videoSection.videos}\n            carouselClickHandler={cardClickHandler}\n            carouselHoverHandler={cardHoverHandler}\n            videoInfo={videoInfo}\n            videoDetailModal={detailModal}\n        />\n    ))\n\n    const topTrailerSoundButtonClickHandler = () => setTopTrailerSoundOn(prevState => !prevState)\n\n    return (!videoInfoError ? (\n        <div className=\"BrowseContent\">\n            <TopTrailerComponent image={imageUrl} >\n                <div className=\"TextsAndButtons\">\n                    <div className=\"verticalItem\" >\n                        <h3 > {firstVideo ? (firstVideo.name || firstVideo.title) : null} </h3>\n                        <span > {firstVideo ? firstVideo.overview : null} </span>\n                        <div className=\"horizontalButtonsHolder\">\n                            <Button backgroundColor=\"#fff\"\n                                textColor=\"rgb(24, 24, 24)\"\n                                playButton image icon={faPlay}\n                                onButtonHover={\n                                    () => onButtonHoverHandler('playButton')}\n                                hoverStatus={buttonHovered['playButton']} >\n                                Play\n                            </Button>\n\n                            <Button backgroundColor=\"rgba(133, 133, 133, 0.6)\"\n                                textColor=\"white\"\n                                playButton image icon={faInfoCircle}\n                                onButtonHover={\n                                    () => onButtonHoverHandler('infoButton')}\n                                hoverStatus={buttonHovered['infoButton']} >\n                                More Info </Button>\n                        </div>\n                    </div>\n                    <div className=\"verticalItem\" >\n                        <CircularSoundButton\n                            topTrailerSoundButtonClickHandler={topTrailerSoundButtonClickHandler}\n                            topTrailerSoundOn={topTrailerSoundOn} />\n                    </div>\n                </div>\n            </TopTrailerComponent>\n            <div className=\"Carousels\">\n                {carousels}\n            </div>\n            {detailModalComponent}\n\n        </div>) :\n        <ErrorPage errors={videoInfoError} />\n    )\n}\n\nexport default BrowseContent"
  },
  {
    "path": "src/containers/Browse/SearchContent/SearchContent.css",
    "content": ".SearchContent {\n    padding: 95px 10px;\n    color: white;\n}\n\n.SearchGrid {\n    display: grid;\n    grid-template-columns: repeat(3, 31.5%);\n    grid-auto-rows: 80px;\n    grid-gap: 10px;\n    height: 100%;\n}\n\n.SearchGrid .GridItem:focus,\n.SearchGrid .GridItem:hover {\n  transform: scale(1.1);\n}\n\n.GridItem {\n    transition: transform 500ms;\n}\n\n.GridItem > * {\n    margin: 0;\n}\n\n@media(min-width: 600px) {\n    .SearchContent {\n        margin: 0 10px;\n    }   \n\n    .SearchGrid {\n        grid-template-columns: repeat(6, 16%);\n        grid-auto-rows: 140px;\n    }\n}\n\n@media(min-width: 1650px) {\n    .SearchContent {\n        font-size: 1.6vw;\n    }\n}"
  },
  {
    "path": "src/containers/Browse/SearchContent/SearchContent.js",
    "content": "import React, { useState, useEffect, useCallback } from 'react'\nimport './SearchContent.css'\n\nimport axios from 'baseAxios'\nimport VideoCard from 'components/Video/VideoCard/VideoCard'\nimport { debounce } from 'lodash'\nimport { buildVideoMetadata, buildVideoModal } from 'utils/transformations'\nimport { sortVideosByPopularity } from 'utils/sorting'\nimport useVideoInfoHandlers from 'hooks/useVideoInfoHandlers'\nimport ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'\n\nconst SearchContent = props => {\n    const [searchedVideoList, setSearchedVideoList] = useState([])\n    const [searchedError, setSearchedError] = useState(null)\n    const [loading, setLoading] = useState(true)\n    const { searchParam } = props\n    const [\n        videoInfo, videoInfoError, detailModal, cardClickHandler,\n        cardHoverHandler, closeModalHandler\n    ] = useVideoInfoHandlers()\n\n    const getSearchMovies = async (searchItem) => {\n        const movieUrl = `search/movie?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&query=${searchItem}&page=1&include_adult=false`\n        const tvUrl = `search/tv?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&page=1&query=${searchItem}&include_adult=false`\n\n        try {\n            const responses = await Promise.all(\n                [\n                    axios.get(movieUrl).then(response => response.data.results),\n                    axios.get(tvUrl).then(response => response.data.results)\n                ]\n            )\n\n            setSearchedVideoList([...responses[0], ...responses[1]])\n            setLoading(false)\n        } catch (error) {\n            setSearchedError(error)\n            setLoading(false)\n        }\n    }\n\n    const delayedAPICall = useCallback(debounce(getSearchMovies, 1000), [])\n\n    useEffect(() => {\n        delayedAPICall(searchParam)\n        return () => {\n            delayedAPICall.cancel()\n        }\n    }, [delayedAPICall, searchParam])\n\n    const detailModalComponent = buildVideoModal(detailModal, videoInfo, { closeModalHandler })\n\n    // we check if the video has a poster or a mediaType because these properties are missing in \n    // some tiles, and, generally, a missing mediaType means there is no video overview or \n    // information. It's an easy fix to skip these little known movies, as the API itself \n    // doesn't provide information. \n    let movieCards\n    if (!loading) {\n        searchedVideoList.sort(sortVideosByPopularity)\n        movieCards = searchedVideoList.map(video => {\n            const { mediaType, extraInfo } = buildVideoMetadata(video, videoInfo)\n            return video.poster_path && mediaType && (\n                <div className=\"GridItem\"\n                    key={video.id}\n                    onClick={\n                        () => cardClickHandler(video.id, mediaType)}\n                    onMouseEnter={\n                        () => cardHoverHandler(video.id, mediaType)} >\n                    <VideoCard name={video.name || video.title}\n                        vote_average={video.vote_average}\n                        poster_path={video.poster_path}\n                        netflixOriginalCard={false} {...extraInfo} />\n                </div>\n            )\n        })\n    }\n\n    return (\n        (!videoInfoError && !searchedError) ? (\n            <div className=\"SearchContent\">\n                <div className=\"SearchGrid\">\n                    {movieCards}\n                </div>\n                {detailModalComponent}\n            </div>) :\n            <ErrorPage errors={videoInfoError || searchedError} />\n    )\n}\n\nexport default SearchContent"
  },
  {
    "path": "src/containers/Browse/routes/Home.js",
    "content": "import React, { useEffect } from 'react'\n\nimport { useSelector, useDispatch } from 'react-redux'\nimport BrowseContent from '../BrowseContent/BrowseContent'\nimport ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'\nimport { fetchTrending, selectAllTrendingVideos, selectTrendingError } from 'store/reducers/slices/trendingSlice'\nimport { fetchTopRated, selectAllTopRatedVideos, selectTopRatedError } from 'store/reducers/slices/topratedSlice'\nimport { fetchNetflixOriginals, selectAllNetflixOriginals, selectNetflixError } from 'store/reducers/slices/netflixOriginalsSlice'\n\nconst Home = () => {\n    const trendingVideos = useSelector(selectAllTrendingVideos)\n    const topRatedVideos = useSelector(selectAllTopRatedVideos)\n    const netflixOriginals = useSelector(selectAllNetflixOriginals)\n\n    const trendingError = useSelector(selectTrendingError)\n    const topRatedError = useSelector(selectTopRatedError)\n    const netflixError = useSelector(selectNetflixError)\n    const dispatch = useDispatch()\n\n    useEffect(() => {\n        dispatch(fetchTrending())\n        dispatch(fetchTopRated())\n        dispatch(fetchNetflixOriginals())\n    }, [dispatch])\n\n\n    let videoSections = []\n    let component \n    if (!trendingError && !topRatedError && !netflixError) {\n        videoSections.push({ title: \"Trending\", videos: trendingVideos })\n        videoSections.push({ title: \"Top Rated\", videos: topRatedVideos })\n        videoSections.push({ title: \"Netflix Originals\", videos: netflixOriginals })\n        component = <BrowseContent videoSections={videoSections} />\n    } else {\n        component = (\n            <ErrorPage errors={[trendingError, topRatedError, netflixError]} />\n        )\n    }\n\n    return component\n}\n\nexport default Home"
  },
  {
    "path": "src/containers/Browse/routes/LatestVideo.js",
    "content": "import React, { useEffect } from 'react'\n\nimport { useSelector, useDispatch } from 'react-redux'\nimport { fetchLatestVideos, selectLatestVideos } from 'store/reducers/slices/latestVideoSlice'\nimport BrowseContent from '../BrowseContent/BrowseContent'\nimport LoadingScreen from 'components/StaticPages/LoadingScreen/LoadingScreen'\nimport ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'\n\nconst LatestVideo = () => {\n    const { latestVideos, status, error } = useSelector(selectLatestVideos)\n    const dispatch = useDispatch()\n\n    useEffect(() => {\n        if (status === 'idle') {\n            dispatch(fetchLatestVideos())\n        }\n    }, [dispatch, status])\n\n    let browseContent\n    if (status === 'success') {\n        browseContent = <BrowseContent videoSections={latestVideos} />\n    } else if (status === 'idle' || status === 'loading') {\n        browseContent = <LoadingScreen />\n    } else if (status === 'error') {\n        browseContent = <ErrorPage errors={error} />\n    }\n\n    return browseContent\n}\n\nexport default LatestVideo"
  },
  {
    "path": "src/containers/Browse/routes/List.js",
    "content": "import React from 'react'\n\nconst List = () => {\n    return (\n        <div style={{color: 'white', paddingTop: '95px', textAlign: 'center'}}>Coming soon</div>\n    )\n}\n\nexport default List"
  },
  {
    "path": "src/containers/Browse/routes/Movies.js",
    "content": "import React, { useEffect } from 'react'\nimport { useSelector, useDispatch } from 'react-redux'\n\nimport { fetchMoviesByGenre, selectMoviesByGenre } from 'store/reducers/slices/moviesByGenreSlice'\nimport BrowseContent from '../BrowseContent/BrowseContent'\nimport LoadingScreen from 'components/StaticPages/LoadingScreen/LoadingScreen'\nimport ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'\n\nconst Movies = () => {\n    const {genres, status, error} = useSelector(selectMoviesByGenre)\n    const dispatch = useDispatch()\n\n    useEffect(() => {\n        if (status === 'idle') {\n            dispatch(fetchMoviesByGenre())\n        }\n    }, [dispatch, status])\n\n    let browseContent\n    if (status === 'success') {\n        browseContent = <BrowseContent videoSections={genres} />\n    } else if (status === 'idle' || status === 'loading') {\n        browseContent = <LoadingScreen />\n    } else if(status === 'error') {\n        browseContent = <ErrorPage errors={error}/>\n    }\n\n    return browseContent\n}\n\nexport default Movies"
  },
  {
    "path": "src/containers/Browse/routes/Tv.js",
    "content": "import React, { useEffect } from 'react'\n\nimport { useSelector, useDispatch } from 'react-redux'\nimport { fetchTvShowsByGenres, selectTvByGenre } from 'store/reducers/slices/tvByGenreSlice'\nimport BrowseContent from '../BrowseContent/BrowseContent'\nimport LoadingScreen from 'components/StaticPages/LoadingScreen/LoadingScreen'\nimport ErrorPage from 'components/StaticPages/ErrorPage/ErrorPage'\n\nconst Tv = () => {\n    const {genres, status, error} = useSelector(selectTvByGenre)\n    const dispatch = useDispatch()\n\n    useEffect(() => {\n        if (status === 'idle') {\n            dispatch(fetchTvShowsByGenres())\n        }\n    }, [dispatch, status])\n\n    let browseContent\n    if (status === 'success') {\n        browseContent = <BrowseContent videoSections={genres} />\n    } else if (status === 'idle' || status === 'loading') {\n        browseContent = <LoadingScreen />\n    } else if(status === 'error') {\n        browseContent = <ErrorPage errors={error}/>\n    }\n\n    return browseContent\n}\n\nexport default Tv"
  },
  {
    "path": "src/containers/Browse/routes/index.js",
    "content": "import Home from './Home'\nimport Movies from './Movies'\nimport Tv from './Tv'\nimport LatestVideo from './LatestVideo'\nimport List from './List'\n\nexport {\n    Home,\n    Movies,\n    Tv,\n    LatestVideo, \n    List\n}\n"
  },
  {
    "path": "src/containers/LandingSection/LandingSection.css",
    "content": ".landingSection {\n  height: 470px;\n  width: 100%;\n  border-bottom: 8px solid #222;\n}\n\n.landingTexts {\n  display: flex;\n  width: 80%;\n  margin: auto;\n  flex-direction: column;\n  align-items: center;\n  position: relative;\n  top: calc(30% - 90px);\n  text-align: center;\n  color: #fff;\n}\n\n.landingTexts > * {\n  margin-top: 15px;\n}\n\n.landingTexts h3 {\n  margin-top: 0px;\n  font-weight: 400;\n}\n\n.ButtonSticker {\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n    width: 100%;\n}\n\n.TextField {\n  background-color: #fff;\n  width: 80%;\n  max-width: 500px;\n}\n\n.TextField:active {\n  color: white;\n  border: none;\n}\n\n.tv-section {\n  height: 410px;\n  background-color: black;\n  border-bottom: 8px solid #333;\n  color: #fff;\n}\n\n.tv-inner, .responsive-tv-inner {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  width: 80%;\n  margin: auto;\n}\n\n.tv-section img {\n  width: 70%;\n}\n\n.faq-section {\n  background-color: black;\n  border-bottom: 8px solid #333;\n  color: #fff;\n}\n\n.GetStartedComponent {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  margin-top: 30px;\n  width: 100%;\n}\n\n.GetStartedComponent > * {\n  margin-bottom: 15px;\n}\n\n.ButtonSticker button {\n    margin-top: 15px;\n}\n\n@media (min-width: 600px) {\n  .landingSection {\n    height: 620px;\n  }\n\n  .landingTexts {\n    width: 70%;\n    top: calc(40% - 45px);\n  }\n\n  .GetStartedComponent {\n    width: 100%;\n  }\n\n  .tv-section {\n    height: 450px;\n  }\n\n  .tv-section img {\n    width: 45%;\n  }\n}\n\n@media(min-width: 950px) {\n    .ButtonSticker {\n        display: flex;\n        flex-direction: row;\n        justify-content: center;\n        align-items: center;\n    }\n\n    .ButtonSticker button {\n        margin-top: 0;\n    }\n}\n\n@media(min-width: 1199px) {\n  .responsive-tv-inner {\n    flex-direction: row;\n    justify-content: space-between;\n  }\n}\n\n@media(min-width: 1650px) {\n    .landingSection,\n    .tv-section,\n    .faq-section {\n        font-size: 1.75rem;\n    }\n\n    .tv-section img {\n        width: 35%;\n    }      \n}\n"
  },
  {
    "path": "src/containers/LandingSection/LandingSection.js",
    "content": "import React, { useState } from \"react\";\nimport \"./LandingSection.css\";\n\nimport NavBar from \"../NavBar/NavBar\";\nimport LandingPage from \"assets/images/landingPage.jpg\";\nimport { TextField } from \"@material-ui/core\";\nimport Button from \"components/UI/Button/Button\";\nimport DarkComponent from \"components/UI/DarkComponent/DarkComponent\";\nimport FAQComponent from \"components/UI/FAQComponent/FAQComponent\";\nimport { Link } from \"react-router-dom\";\nimport { faChevronRight } from \"@fortawesome/free-solid-svg-icons\";\nimport { texualMaterial } from './LandingSectionTexts'\n\n/**\n * The 'homepage' of this project. Uses an object state to\n * dynamically determine which frequently asked box is open.\n * Renders components like navbars, darkComponents, etc and\n * passes the relevent props whenever needed.\n */\nconst LandingSection = props => {\n    const [faqBoxOpen, setFaqBoxOpen] = useState({});\n\n    const faqOpenHandler = boxNumber => {\n        setFaqBoxOpen(prevBoxState => ({\n            [boxNumber]: !prevBoxState[boxNumber]\n        }));\n    };\n\n    const darkComponents = texualMaterial.darkComponent.map(darkcomp => (\n        <div className=\"tv-section\" key={darkcomp.id}>\n            <div className=\"responsive-tv-inner\">\n                <DarkComponent\n                    topText={darkcomp.topText}\n                    bottomText={darkcomp.bottomText}\n                    image={darkcomp.image}\n                />\n            </div>\n        </div>\n    ))\n\n    const faqComponents = texualMaterial.faqComponent.map(faqcomp => (\n        <FAQComponent\n            key={faqcomp.id}\n            text={faqcomp.text}\n            boxOpen={faqBoxOpen[`box${faqcomp.id}`]}\n            faqOpenHandler={() => faqOpenHandler(`box${faqcomp.id}`)}\n            boxText={faqcomp.boxText}\n        />\n    ))\n\n    return (\n        <>\n            <div\n                className=\"landingSection\"\n                style={{ backgroundImage: `url(${LandingPage})` }}\n            >\n                <NavBar loginButton />\n                <div className=\"landingTexts\">\n                    <h1>Unlimited movies, TV shows, and more.</h1>\n                    <h3>Watch anywhere. Cancel anytime.</h3>\n                    <h3>\n                        Ready to watch? Enter your email to create or restart your\n                        membership.\n                     </h3>\n\n                    <div className=\"ButtonSticker\">\n                        <TextField\n                            className=\"TextField\"\n                            label=\"Email Address\"\n                            variant=\"filled\"\n                            color=\"secondary\"\n                        />\n\n                        <Link to=\"/login\">\n                            <Button\n                                height=\"56px\"\n                                width=\"150px\"\n                                image\n                                icon={faChevronRight}\n                                backgroundColor=\"#e50914\"\n                                textColor=\"#fff\"\n                                buttonSize=\"xs\"\n                            >\n                                GET STARTED\n                            </Button>\n                        </Link>\n                    </div>\n                </div>\n            </div>\n\n            {darkComponents}\n\n            <div className=\"faq-section\">\n                <div className=\"tv-inner\">\n                    <DarkComponent\n                        fontSize=\"2.5rem\"\n                        topText=\"Frequently Asked Questions\"\n                    />\n\n                    {faqComponents}\n\n                    <div className=\"GetStartedComponent\">\n                        <h3>\n                            Ready to watch? Enter your email to create or restart your\n                            membership.\n                        </h3>\n\n                        <div className=\"ButtonSticker\">\n                            <TextField\n                                className=\"TextField\"\n                                label=\"Email Address\"\n                                variant=\"filled\"\n                                color=\"secondary\"\n                            />\n\n                            <Link to=\"/login\">\n                                <Button\n                                    height=\"56px\"\n                                    width=\"150px\"\n                                    image\n                                    icon={faChevronRight}\n                                    backgroundColor=\"#e50914\"\n                                    textColor=\"#fff\"\n                                    buttonSize=\"xs\"\n                                >\n                                    GET STARTED\n                             </Button>\n                            </Link>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </>\n    );\n};\n\nexport default LandingSection;\n"
  },
  {
    "path": "src/containers/LandingSection/LandingSectionTexts.js",
    "content": "// A file for all texual material used in 'LandingSection.js'\nexport const texualMaterial = {\n    darkComponent: [\n        {\n            id: 1, \n            topText: \"Enjoy on your TV.\",\n            bottomText: \"Watch on Smart TVs, Playstation, Xbox, Chromecast, Apple TV, Blu-ray players, and more.\",\n            image: \"https://assets.nflxext.com/ffe/siteui/acquisition/ourStory/fuji/desktop/tv.png\"\n        },\n        {\n            id: 2,\n            topText: \"Download your shows to watch offline.\",\n            bottomText: \"Save your favorites easily and always have something to watch.\",\n            image: \"https://assets.nflxext.com/ffe/siteui/acquisition/ourStory/fuji/desktop/mobile-0819.jpg\"\n        },\n        {\n            id: 3, \n            topText: \"Watch everywhere.\",\n            bottomText: \"Stream unlimited movies and TV shows on your phone, tablet, laptop, and TV without paying more.\",\n            image: \"https://assets.nflxext.com/ffe/siteui/acquisition/ourStory/fuji/desktop/device-pile.png\"\n        },\n    ],\n\n    faqComponent: [\n        {\n            id: 1, \n            text: \"What is Netflix?\",\n            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!\"\n        },\n\n        {\n            id: 2, \n            text: \"How much does Netflix cost?\",\n            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.\",\n        },\n\n        {\n            id: 3, \n            text: \"Where can I watch?\",\n            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.\"\n        }, \n\n        {\n            id: 4, \n            text: \"How can I cancel?\",\n            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.\"\n        }, \n\n        {\n            id: 5, \n            text: \"What can I watch on Netflix?\",\n            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.\"\n        }\n    ]\n}"
  },
  {
    "path": "src/containers/Login/Login.css",
    "content": ".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.LoginCard {\n  background-color: rgba(0, 0, 0, 0.75);\n  border-radius: 4px;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n  height: calc(100% - 75px);\n  color: #fff;\n  padding: 40px 38px 40px;\n}\n\n.LoginCard form > * {\n  margin-top: 25px;\n}\n\n.LoginCard form span {\n    color: red;\n    font-size: 13px;\n}\n\n.LoginCard button {\n  margin-top: 25px;\n}\n\n.textField {\n  box-sizing: border-box;\n  border-radius: 2px;\n  width: 100%;\n}\n\n.textField input {\n  color: white;\n}\n\n.HorizontalDiv {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n@media (min-width: 600px) {\n    .LoginCard {\n      width: 450px;\n      height: 680px;\n      padding: 60px 68px 40px;\n      margin: auto;\n    }\n}\n\n@media(min-width: 1650px) {\n    .LoginCard {\n        width: 550px;\n        height: 880px;\n        padding: 60px 68px 40px;\n        margin: auto;\n    }\n\n    .LoginCard form span {\n        font-size: 20px;\n    }\n}"
  },
  {
    "path": "src/containers/Login/Login.js",
    "content": "import React, { useState, useContext } from \"react\";\nimport \"./Login.css\";\n\nimport { NetflixLogo, LoginBackground } from \"assets/images/\";\nimport { TextField } from \"@material-ui/core\";\nimport Button from \"components/UI/Button/Button\";\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\";\nimport Checkbox from \"@material-ui/core/Checkbox\";\nimport { useHistory } from \"react-router-dom\";\nimport { AuthenticationContext } from 'context/Authentication'\nimport { validEmailAndPhoneNumber } from 'utils/validation'\n\n\n/**\n * The login component, which validates the email and password\n * fields and uses a controlled form. Uses material UI for the\n * textfields.\n */\nconst Login = props => {\n  const [form, setForm] = useState({\n    email: {\n      value: '',\n      touched: false,\n      valid: false\n    },\n\n    password: {\n      value: '',\n      touched: false,\n      valid: false\n    },\n\n    onSubmitInvalid: false\n  })\n\n  const history = useHistory()\n  const authContext = useContext(AuthenticationContext)\n\n  const inputChangeHandler = event => {\n    const { name, value } = event.target;\n    if (name === \"email\") {\n      setForm(prevForm => ({\n        ...prevForm,\n        email: {\n          ...prevForm.email,\n          value: value, touched: true, valid: value.length > 0 && validEmailAndPhoneNumber(value)\n        }\n      }))\n\n    } else if (name === \"password\") {\n      setForm(prevForm => ({\n        ...prevForm,\n        password: {\n          ...prevForm.password, value: value, touched: true,\n          valid: value.length >= 4 && value.length <= 60\n        }\n      }))\n    }\n  };\n\n  // For setting error spans once any of the fields are touched.\n  const fieldBlurHandler = event => {\n    if (event.target.name === 'email') {\n      if (form.email.value === '') {\n        setForm(prevForm => ({\n          ...prevForm,\n          email: { ...prevForm.email, touched: true }\n        }))\n      }\n    }\n\n    if (event.target.name === 'password') {\n      if (form.password.value === '') {\n        setForm(prevForm => ({\n          ...prevForm,\n          password: { ...prevForm.password, touched: true }\n        }))\n      }\n    }\n  };\n\n  let [emailSpan, passwordSpan] = [null, null];\n\n  if ((!form.email.valid && form.email.touched) || (form.onSubmitInvalid && !form.email.valid)) {\n    emailSpan = <span>Please enter a valid email or phone number.</span>\n  }\n\n  if ((!form.password.valid && form.password.touched) || (form.onSubmitInvalid && !form.password.valid)) {\n    passwordSpan = <span>Your password must contain between 4 and 60 characters.</span>\n  }\n\n  const formSubmitHandler = (event) => {\n    event.preventDefault()\n    if (!form.email.valid || !form.password.valid) {\n      setForm(prevForm => ({ ...prevForm, onSubmitInvalid: true }))\n    } else {\n      authContext.login()\n      history.push(\"/browse\");\n    }\n  }\n\n  return (\n    <div\n      className=\"Login\"\n      style={{ backgroundImage: `url(${LoginBackground})` }}\n    >\n      <img src={NetflixLogo} alt=\"Logo\" />\n      <div className=\"LoginCard\">\n        <h1>Sign In</h1>\n        <form onSubmit={formSubmitHandler}>\n          <TextField\n            name=\"email\"\n            className=\"textField\"\n            label=\"Email or phone number\"\n            variant=\"filled\"\n            type=\"text\"\n            style={{ backgroundColor: \"#333\" }}\n            color=\"secondary\"\n            value={form.email.value}\n            onChange={inputChangeHandler}\n            onBlur={fieldBlurHandler}\n            autoComplete=\"off\"\n            InputLabelProps={{\n              style: { color: \"#8c8c8c\" }\n            }}\n          />\n\n          {emailSpan}\n\n          <TextField\n            name=\"password\"\n            className=\"textField\"\n            label=\"Password\"\n            variant=\"filled\"\n            type=\"password\"\n            style={{ backgroundColor: \"#333\" }}\n            color=\"secondary\"\n            value={form.password.value}\n            onChange={inputChangeHandler}\n            onBlur={fieldBlurHandler}\n            autoComplete=\"off\"\n            InputLabelProps={{\n              style: { color: \"#8c8c8c\" }\n            }}\n          />\n\n          {passwordSpan}\n\n          <Button\n            height=\"45px\" width=\"100%\"\n            backgroundColor=\"#e50914\"\n            textColor=\"#fff\">\n            Sign In\n          </Button>\n\n        </form>\n\n        <div className=\"HorizontalDiv\">\n          <FormControlLabel\n            style={{ marginLeft: \"-12px\" }}\n            control={\n              <Checkbox style={{ color: \"rgb(229, 9, 20)\" }} name=\"checkedB\" />\n            }\n            label=\"Remember Me\"\n          />\n          <span>Need help?</span>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default Login;\n"
  },
  {
    "path": "src/containers/NavBar/NavBar.css",
    "content": ".NavBar {\n  width: 100%;\n  height: 45px;\n  display: flex;\n  align-items: center;\n  position: absolute;\n  top: 0;\n  left: 0;\n  padding-top: 5px;\n  font-size: 3vw;\n  z-index: 100;\n  box-sizing: border-box;\n}\n\n.Sticky {\n  position: fixed;\n  top: 0;\n}\n\n.NavBar img {\n  height: 100%;\n  width: 108px;\n}\n\n.inactive {\n  color: #e5e5e5;\n  margin: 0;\n  padding-right: 15px;\n  cursor: pointer;\n  font-weight: lighter;\n  transition: color .4s;\n  text-decoration: none;\n}\n\n.NavBar a:hover {\n  color: grey;\n}\n\n.active {\n  font-weight: normal;\n  color: white;\n}\n\n.LinkContainer {\n  margin-right: auto;\n}\n\n.Horizontal {\n  display: none;\n}\n\n.Vertical {\n  display: block;\n}\n\n.ExtraOptions {\n  display: none;\n}\n\n.DropdownNav {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n}\n\n.OptionsContainer {\n  display: inline-flex;\n  align-items: center;\n  color: white;\n}\n\n.OptionsContainer > * {\n  color: white;\n  cursor: pointer;\n  margin-right: 15px;\n}\n\n.NavBar > * {\n  padding: 5px;\n}\n\n@media(min-width: 600px) {\n  .NavBar {\n    font-size: 1vw;\n  }\n\n  .NavBar img {\n    width: 168px;\n  }\n\n  .NavBar > * {\n    margin: 10px;\n  }\n\n  .OptionsContainer {\n    display: flex;\n    align-items: center;\n    color: white;\n    margin: 0;\n    height: 80%;\n  }\n\n  .LinkContainer {\n    margin-right: auto;\n  }\n\n  .OptionsContainer > * {\n    margin-right: 25px;\n  }\n\n  .ExtraOptions {\n    display: block;\n  }\n}\n\n@media(min-width: 860px) {\n    .Horizontal {\n        display: block;\n    }\n    \n    .Vertical {\n        display: none;\n    }    \n}\n\n@media(min-width: 1650px) {\n    .NavBar {\n        height: 90px;\n        font-size: 1.2vw;\n    }\n}"
  },
  {
    "path": "src/containers/NavBar/NavBar.js",
    "content": "import React, { useState, useEffect, useCallback } from \"react\";\nimport \"./NavBar.css\";\n\nimport { NetflixLogo } from \"assets/images/\";\nimport Button from \"components/UI/Button/Button\";\nimport { NavLink, Link } from \"react-router-dom\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faGift, faBell } from \"@fortawesome/free-solid-svg-icons\";\nimport Search from '../Search/Search'\n\n\nconst NavBar = props => {\n  const { navigation, profileDropdown, navDropdown, loginButton } = props\n  const [isNavbarAtTop, setIsNavbarAtTop] = useState(true)\n\n  const scrollNavbarStateHandler = useCallback(() => {\n    const navbarAtTop = window.scrollY < 45\n    if (navbarAtTop !== isNavbarAtTop) {\n      setIsNavbarAtTop(navbarAtTop)\n    }\n  }, [isNavbarAtTop])\n\n  useEffect(() => {\n    document.addEventListener('scroll', scrollNavbarStateHandler)\n    return () => {\n      document.removeEventListener('scroll', scrollNavbarStateHandler)\n    }\n  }, [scrollNavbarStateHandler])\n\n  let navTiles = null\n  let flexStyle = { justifyContent: 'space-between', backgroundColor: !isNavbarAtTop && 'black' }\n\n  if (navigation) {\n    navTiles = (\n      <>\n        <div className=\"LinkContainer\">\n          <div className=\"Horizontal\">\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse\" exact>Home</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/tv\" exact>TV Shows</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/movies\" exact>Movies</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/latest\" exact>Latest</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/list\" exact>My List</NavLink>\n          </div>\n          <div className=\"Vertical\">\n            {navDropdown}\n          </div>\n        </div>\n\n        <div className=\"OptionsContainer\">\n          <Search />\n          <span className=\"ExtraOptions\" style={{ fontWeight: '350' }}>KIDS</span>\n          <FontAwesomeIcon className=\"ExtraOptions\" size=\"lg\" icon={faGift} />\n          <FontAwesomeIcon className=\"ExtraOptions\" size=\"lg\" icon={faBell} />\n          {profileDropdown}\n        </div>\n      </>\n    )\n  }\n\n  return (\n    <div className=\"NavBar Sticky\" style={flexStyle}>\n      <img src={NetflixLogo} alt=\"Logo\" />\n      {navTiles}\n      {loginButton && <Link to=\"/login\">\n        <Button\n          height=\"34px\"\n          width=\"75px\"\n          backgroundColor=\"#e50914\"\n          textColor=\"#fff\"\n        >\n          Sign In\n      </Button>\n      </Link>}\n    </div>\n  );\n};\n\nexport default NavBar;\n"
  },
  {
    "path": "src/containers/Search/Search.css",
    "content": ".SearchBox {\n    display: inline-block;\n}\n\n.Holder {\n    display: flex;\n    align-items: center;\n    border: solid 1px rgba(255,255,255,.85);\n    height: 100%;\n    width: 100%;\n    background-color: black;\n    height: 30px;\n    width: 160px\n}\n\n.Holder svg {\n    padding: 15px 5px;\n}\n\n.Holder input {\n    border: none;\n    outline: none;\n    color: white;\n    width: 100%;\n    background-color: transparent;\n}\n\n.Holder input:hover {\n    color: white;\n}\n\n.search-animation-enter {\n    opacity: 0;\n}\n\n.search-animation-enter-active {\n    opacity: 1;\n    -webkit-animation: scale-up-hor-right 0.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;\n\tanimation: scale-up-hor-right 0.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;\n}\n\n.search-animation-exit {\n    opacity: 1;\n}\n\n.search-animation-exit-active {\n\t-webkit-animation: scale-down-hor-right 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;\n\t        animation: scale-down-hor-right 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;\n}\n\n\n@-webkit-keyframes scale-up-hor-right {\n0% {\n    -webkit-transform: scaleX(0.4);\n            transform: scaleX(0.4);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n100% {\n    -webkit-transform: scaleX(1);\n            transform: scaleX(1);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n}\n@keyframes scale-up-hor-right {\n0% {\n    -webkit-transform: scaleX(0.4);\n            transform: scaleX(0.4);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n100% {\n    -webkit-transform: scaleX(1);\n            transform: scaleX(1);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n}\n\n@-webkit-keyframes scale-down-hor-right {\n0% {\n    -webkit-transform: scaleX(1);\n            transform: scaleX(1);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n100% {\n    -webkit-transform: scaleX(0.3);\n            transform: scaleX(0.3);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n}\n@keyframes scale-down-hor-right {\n0% {\n    -webkit-transform: scaleX(1);\n            transform: scaleX(1);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n100% {\n    -webkit-transform: scaleX(0.3);\n            transform: scaleX(0.3);\n    -webkit-transform-origin: 100% 100%;\n            transform-origin: 100% 100%;\n}\n}\n\n@media(min-width: 600px) {\n    .Holder {\n        height: 30px;\n        width: 250px\n    }\n}\n\n@media(min-width: 1650px) {\n    .Holder {\n        height: 50px;\n        width: 400px;\n    }\n\n    .Holder svg {\n        padding: 35px 15px;\n    }\n\n    .Holder input {\n        font-size: 1.1vw;\n    }\n}"
  },
  {
    "path": "src/containers/Search/Search.js",
    "content": "import React, { useState, useEffect, useRef } from 'react'\nimport './Search.css'\nimport { useHistory } from 'react-router-dom'\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faSearch, faTimes } from \"@fortawesome/free-solid-svg-icons\";\nimport { CSSTransition } from 'react-transition-group'\n\nconst Search = () => {\n    const [searchBox, setSearchBox] = useState(false)\n    const [searchIcon, setSearchIcon] = useState(true)\n\n    const [searchText, setSearchText] = useState('')\n    const [searchChanged, setSearchChanged] = useState(false)\n    const searchBoxRef = useRef()\n    const history = useHistory()\n\n    useEffect(() => {\n        document.addEventListener('mousedown', outsideSearchClickListener, false)\n        return () => {\n            document.removeEventListener('mousedown', outsideSearchClickListener, false)\n        }\n    }, [])\n\n    useEffect(() => {\n        if (searchText.length > 0) {\n            history.push({\n                pathname: '/search',\n                search: `?q=${searchText}`\n            })\n\n        } else if (searchChanged && searchText.length === 0) {\n            history.replace({ pathname: '/browse' })\n        }\n    }, [history, searchText, searchChanged])\n\n    const searchClickHandler = () => {\n        setSearchBox(true)\n    }\n\n    const outsideSearchClickListener = event => {\n        if (searchBoxRef.current && !searchBoxRef.current.contains(event.target)) {\n            setSearchBox(false)\n        }\n    }\n\n    const searchTextChangeHandler = event => {\n        const textValue = event.target.value\n        setSearchText(textValue)\n        setSearchChanged(true)\n    }\n\n    const clickCrossHandler = () => {\n        setSearchText('')\n    }\n\n    const searchBar = (\n        <CSSTransition in={searchBox} classNames=\"search-animation\" timeout={500} unmountOnExit\n            onEnter={() => setSearchIcon(false)}\n            onExiting={() => setSearchBox(false)}\n            onExited={() => setSearchIcon(true)}>\n            <div className=\"Holder\">\n                <FontAwesomeIcon className=\"Icon\" size=\"lg\" icon={faSearch} />\n                <input autoFocus placeholder=\"Titles, people, genres\"\n                    onChange={searchTextChangeHandler} value={searchText} />\n                {searchText.length > 0 ?\n                    <FontAwesomeIcon onClick={clickCrossHandler} size=\"lg\" icon={faTimes} /> : null\n                }\n            </div>\n        </CSSTransition>\n    )\n\n    return (\n        <div className=\"SearchBox\" onClick={searchClickHandler} ref={searchBoxRef}>\n            {searchIcon && <FontAwesomeIcon size=\"lg\" icon={faSearch} />}\n            {searchBar}\n        </div>\n    )\n}\n\nexport default Search"
  },
  {
    "path": "src/context/Authentication.js",
    "content": "import React, { useState } from 'react'\n\nexport const AuthenticationContext = React.createContext({\n    authenticated: false,\n    login: () => { }, \n    logout: () => {}\n})\n\nconst AuthenticationContextProvider = (props) => {\n    const [authenticated, setAuthenticated] = useState(false)\n\n    const loginHandler = () => {\n        setAuthenticated(true)\n    }\n\n    const logoutHandler = () => {\n        setAuthenticated(false)\n    }\n\n    return (\n        <AuthenticationContext.Provider value={{\n            authenticated: authenticated, login: loginHandler, \n            logout: logoutHandler\n        }}>\n            {props.children}\n        </AuthenticationContext.Provider>\n    )\n}\n\nexport default AuthenticationContextProvider "
  },
  {
    "path": "src/hoc/Layout.js",
    "content": "import React from 'react'\nimport useNavbar from 'hooks/useNavbar'\n\n\nconst Layout = props => {\n    const navBar = useNavbar()\n    const style = {\n        background: '#141414',\n        minHeight: '100vh'\n    }\n    return (\n        <div style={style}>\n            {navBar}\n            {props.children}\n        </div>\n    )\n}\n\nexport default Layout"
  },
  {
    "path": "src/hoc/ScrollToTop/ScrollToTop.js",
    "content": "import { Component } from \"react\";\nimport { withRouter } from \"react-router-dom\";\n\n/**\n * A simple hoc as shown in the react router documentation\n * sample which does nothing but restore window scroll\n * position when going to and fro from links.\n */\nclass ScrollToTop extends Component {\n  componentDidUpdate(prevProps) {\n    if (this.props.location !== prevProps.location) {\n      window.scrollTo(0, 0);\n    }\n  }\n\n  render() {\n    return this.props.children;\n  }\n}\n\nexport default withRouter(ScrollToTop);\n"
  },
  {
    "path": "src/hooks/useDropdown.js",
    "content": "import React, { useState } from 'react'\n\nimport Dropdown from 'components/Navigation/Dropdown/Dropdown'\n\nconst UseDropDown = (content, boxSizing) => {\n    const [dropdown, setDropdown] = useState({\n        iconHovered: false,\n        floatingBoxHovered: false\n    })\n\n    const handlers = {\n        iconHoveredInHandler: () => {\n            setDropdown(prevDropdown => ({\n                ...prevDropdown,\n                iconHovered: true,\n            }))\n        },\n\n        iconHoveredOutHandler: () => {\n            setTimeout(() => {\n                setDropdown(prevDropdown => ({\n                    ...prevDropdown,\n                    iconHovered: false,\n                }))\n            }, 600)\n        },\n\n        floatingBoxHoveredInHandler: () => {\n            setDropdown(prevDropdown => ({\n                ...prevDropdown,\n                floatingBoxHovered: true,\n            }))\n        },\n\n        floatingBoxHoveredOutHandler: () => {\n            setDropdown(prevDropdown => ({\n                ...prevDropdown,\n                floatingBoxHovered: false,\n            }))\n        },\n\n        onItemClickCloseBoxHandler: () => {\n            setDropdown(false)\n        }\n    }\n\n    return (\n        <Dropdown\n            dropdown={dropdown} content={content}\n            {...handlers} boxSizing={boxSizing}\n        />\n    )\n}\n\nexport default UseDropDown"
  },
  {
    "path": "src/hooks/useHoverStyleButton.js",
    "content": "import { useState } from 'react'\n\nconst UseHoverStyleButton = (buttonsObj) => {\n    const [buttonHovered, setButtonHovered] = useState(buttonsObj)\n\n    const onButtonHoverHandler = (id) => {\n        setButtonHovered(prevHover => ({\n            ...prevHover,\n            [id]: !prevHover[id]\n        }))\n    }\n\n    return [buttonHovered, onButtonHoverHandler]\n}\n\nexport default UseHoverStyleButton"
  },
  {
    "path": "src/hooks/useNavbar.js",
    "content": "import React, { useContext } from 'react'\n\nimport NavBar from 'containers/NavBar/NavBar'\nimport { useHistory } from \"react-router-dom\";\nimport { AuthenticationContext } from 'context/Authentication'\nimport useDropdown from './useDropdown'\n\nimport ProfileCard from 'components/UI/ProfileCard/ProfileCard'\nimport { NavLink } from 'react-router-dom'\n\nimport {\n    Weird,\n    Profile,\n    Smile,\n    Normal\n} from '../assets/images/index'\n\nconst UseNavbar = () => {\n    const logoutHandler = () => {\n        localStorage.removeItem('profileSelected')\n        authContext.logout()\n        history.push('/')\n    }\n\n    const profileDropdownContent = (\n        <>\n            <ProfileCard\n                profileImage={Profile}\n                username=\"Pushpa\"\n                dropdown\n            />\n            <ProfileCard\n                profileImage={Weird}\n                username=\"Shammy\"\n                dropdown\n            />\n            <ProfileCard\n                profileImage={Smile}\n                username=\"PQ\"\n                dropdown\n            />\n            <ProfileCard\n                profileImage={Normal}\n                username=\"Subhanga\"\n                dropdown\n            />\n\n            <span style={{ borderBottom: '1px solid grey', marginBottom: '7px' }}>Manage Profiles</span>\n            <span>Account</span>\n            <span>Help Center</span>\n            <span onClick={logoutHandler}>Sign out of Netflix</span>\n        </>\n    )\n\n    const navLinks = (\n        <>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse\" exact>Home</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/tv\" exact>TV Shows</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/movies\" exact>Movies</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/latest\" exact>Latest</NavLink>\n            <NavLink className=\"inactive\" activeClassName=\"active\" to=\"/browse/list\" exact>My List</NavLink>\n        </>\n    )\n\n    const profileDropdown = useDropdown(profileDropdownContent,\n        { top: '42px', right: '0px', width: '20vh', height: '42vh' })\n\n    const navDropdown = useDropdown(navLinks,\n        { top: '15px', width: '100px' })\n\n    const authContext = useContext(AuthenticationContext)\n    const history = useHistory()\n\n    return (\n        <NavBar navigation profileDropdown={profileDropdown}\n            navDropdown={navDropdown} />\n    )\n}\n\nexport default UseNavbar"
  },
  {
    "path": "src/hooks/useVideoInfoHandlers.js",
    "content": "import { useState, useCallback } from 'react'\n\nimport { mediaTypeToVideoDetailTransformation } from 'utils/transformations'\nimport { isMobile } from 'react-device-detect'\n\n// A custom hook which sets all VideoCard/Carousel click/hover behavior \nconst UseVideoInfoHandlers = () => {\n    const [videoInfo, setVideoInfo] = useState()\n    const [videoInfoError, setVideoInfoError] = useState(null)\n    const [detailModal, setDetailModal] = useState(false)\n\n    const cardClickHandler = useCallback((videoId, mediaType) => {\n        if (!isMobile) {\n            setDetailModal(true)\n        } else {\n            mediaTypeToVideoDetailTransformation(videoId, mediaType)\n                .then(data => {\n                    setVideoInfo(data)\n                    setDetailModal(true)\n                })\n                .catch(error => {\n                    setVideoInfoError(error)\n                })\n        }\n    }, [])\n\n    const cardHoverHandler = useCallback((videoId, mediaType) => {\n        mediaTypeToVideoDetailTransformation(videoId, mediaType)\n            .then(data => {\n                setVideoInfo(data)\n            })\n            .catch(error => {\n                setVideoInfoError(error)\n            })\n    }, [])\n\n    const closeModalHandler = useCallback(() => {\n        setDetailModal(false)\n    }, [])\n\n    return [videoInfo, videoInfoError, detailModal, cardClickHandler, cardHoverHandler, closeModalHandler]\n}\n\nexport default UseVideoInfoHandlers"
  },
  {
    "path": "src/index.js",
    "content": "import React from \"react\";\nimport ReactDOM from \"react-dom\";\n\nimport App from \"App\";\nimport { BrowserRouter } from \"react-router-dom\";\nimport ScrollToTop from \"hoc/ScrollToTop/ScrollToTop\";\nimport AuthenticationContextProvider from 'context/Authentication'\nimport { Provider } from 'react-redux'\nimport store from 'store/reducers/store'\n\n\nconst app = (\n  <Provider store={store}>\n    <BrowserRouter basename={process.env.PUBLIC_URL}>\n      <React.StrictMode>\n        <ScrollToTop>\n          <AuthenticationContextProvider>\n            <App />\n          </AuthenticationContextProvider>\n        </ScrollToTop>\n      </React.StrictMode>\n    </BrowserRouter>\n  </Provider>\n);\n\nif(window.Cypress) {\n  window.store = store \n}\n\nconst rootElement = document.getElementById(\"root\");\nReactDOM.render(app, rootElement);\n"
  },
  {
    "path": "src/store/reducers/slices/latestVideoSlice.js",
    "content": "import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexport const fetchLatestVideos = createAsyncThunk('latestVideoSlice/fetchLatestVideos',\n    async (_, { rejectWithValue }) => {\n        try {\n            const response = await Promise.all([\n                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`)\n                    .then(response => ({ title: \"Latest Movies\", videos: response.data.results })),\n                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`)\n                    .then(response => ({ title: \"Latest TV Shows\", videos: response.data.results }))\n            ])\n\n            return response\n        } catch (error) {\n            if (!error.response) {\n                throw error\n            }\n\n            return rejectWithValue(error.response.data)\n        }\n    })\n\nconst initialState = {\n    latestVideos: [],\n    status: 'idle',\n    error: null\n}\n\nconst latestVideoSlice = createSlice({\n    name: 'latestVideos',\n    initialState: initialState,\n    extraReducers: {\n        [fetchLatestVideos.pending]: (state, _) => {\n            state.status = 'loading'\n        },\n\n        [fetchLatestVideos.fulfilled]: (state, action) => {\n            action.payload.forEach(latestVideo => {\n                state.latestVideos.push({ ...latestVideo })\n            })\n\n            state.status = 'success'\n        },\n\n        [fetchLatestVideos.rejected]: (state, action) => {\n            state.status = 'error'\n            if (action.payload) {\n                state.error = action.payload.status_message\n            } else {\n                state.error = action.error\n            }\n        }\n    }\n})\n\nexport const selectLatestVideos = state => state.latestVideos\n\nexport default latestVideoSlice.reducer"
  },
  {
    "path": "src/store/reducers/slices/moviesByGenreSlice.js",
    "content": "import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\nimport { genreTopVideoTransformation } from 'utils/transformations'\n\nconst fetchMovieGenres = async () => {\n    try {\n        const response = await axios.get(\n            `genre/movie/list?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`\n        )\n\n        return response.data.genres\n    } catch (error) {\n        throw new Error(error)\n    }\n}\n\nexport const fetchMoviesByGenre = createAsyncThunk('moviesByGenreSlice/fetchMoviesByGenre',\n    async (_, { rejectWithValue }) => {\n        try {\n            const genres = await fetchMovieGenres()\n            return await genreTopVideoTransformation(genres, 'movie')\n        } catch (error) {\n            if (!error.response) {\n                throw error\n            }\n            return rejectWithValue(error.response.data)\n        }\n    }\n)\n\nconst initalState = {\n    genres: [],\n    status: 'idle',\n    error: null\n}\n\nconst moviesByGenreSlice = createSlice({\n    name: 'moviesByGenre',\n    initialState: initalState,\n    extraReducers: {\n        [fetchMoviesByGenre.pending]: (state, _) => {\n            state.status = 'loading'\n        },\n\n        [fetchMoviesByGenre.fulfilled]: (state, action) => {\n            action.payload.forEach(genre => {\n                state.genres.push({ ...genre })\n            })\n\n            state.status = 'success'\n        },\n\n        [fetchMoviesByGenre.rejected]: (state, action) => {\n            state.status = 'error'\n            if (action.payload) {\n                state.error = action.payload.status_message\n            } else {\n                state.error = action.error\n            }\n        }\n    }\n})\n\nexport const selectMoviesByGenre = state => state.moviesByGenre\n\nexport default moviesByGenreSlice.reducer "
  },
  {
    "path": "src/store/reducers/slices/netflixOriginalsSlice.js",
    "content": "import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexport const netflixAdapter = createEntityAdapter()\n\nexport const fetchNetflixOriginals = createAsyncThunk('netflixOriginalsSlice/fetchNetflixOriginals',\n    async (_, { rejectWithValue }) => {\n        try {\n            const response = await axios.get(\n                `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`\n            )\n            return response.data.results\n        } catch (error) {\n            if (!error.response) {\n                throw error\n            }\n            return rejectWithValue(error.response.data)\n        }\n    })\n\nconst netflixOriginalsSlice = createSlice({\n    name: 'netflixOriginals',\n    initialState: netflixAdapter.getInitialState({ error: null }),\n    extraReducers: {\n        [fetchNetflixOriginals.fulfilled]: (state, action) => {\n            netflixAdapter.upsertMany(state, action.payload)\n        },\n\n        [fetchNetflixOriginals.rejected]: (state, action) => {\n            if (action.payload) {\n                state.error = action.payload.status_message\n            } else {\n                state.error = action.error\n            }\n        }\n    }\n})\n\nexport const {\n    selectAll: selectAllNetflixOriginals\n} = netflixAdapter.getSelectors(state => state.netflixOriginals)\nexport const selectNetflixError = state => state.netflixOriginals.error\n\nexport default netflixOriginalsSlice.reducer "
  },
  {
    "path": "src/store/reducers/slices/topratedSlice.js",
    "content": "import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexport const topratedAdapter = createEntityAdapter()\n\nexport const fetchTopRated = createAsyncThunk('topratedSlice/fetchTopRated',\n    async (_, { rejectWithValue }) => {\n        try {\n            const response = await axios.get(\n                `movie/top_rated?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US&page=1`\n            )\n            return response.data.results\n        } catch (error) {\n            if (!error.response) {\n                throw error\n            }\n\n            return rejectWithValue(error.response.data)\n        }\n    })\n\nconst topratedSlice = createSlice({\n    name: 'toprated',\n    initialState: topratedAdapter.getInitialState({ error: null }),\n    extraReducers: {\n        [fetchTopRated.fulfilled]: (state, action) => {\n            topratedAdapter.upsertMany(state, action.payload)\n        },\n\n        [fetchTopRated.rejected]: (state, action) => {\n            if (action.payload) {\n                state.error = action.payload.status_message\n            } else {\n                state.error = action.error\n            }        }\n    }\n})\n\nexport const {\n    selectAll: selectAllTopRatedVideos,\n} = topratedAdapter.getSelectors(state => state.toprated)\n\nexport const selectTopRatedError = state => state.toprated.error\n\nexport default topratedSlice.reducer\n"
  },
  {
    "path": "src/store/reducers/slices/trendingSlice.js",
    "content": "import { createSlice, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\n\nexport const trendingAdapter = createEntityAdapter()\n\nexport const fetchTrending = createAsyncThunk('trendingSlice/fetchTrending',\n    async (_, { rejectWithValue }) => {\n        try {\n            const response = await axios.get(\n                `trending/all/day?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}`\n            )\n            return response.data.results\n        } catch (error) {\n            if (!error.response) {\n                throw error\n            }\n\n            return rejectWithValue(error.response.data)\n        }\n    })\n\nconst trendingSlice = createSlice({\n    name: 'trending',\n    initialState: trendingAdapter.getInitialState({ error: null }),\n    reducers: {},\n    extraReducers: {\n        [fetchTrending.fulfilled]: (state, action) => {\n            trendingAdapter.upsertMany(state, action.payload)\n        },\n\n        [fetchTrending.rejected]: (state, action) => {\n            if (action.payload) {\n                state.error = action.payload.status_message\n            } else {\n                state.error = action.error\n            }\n        }\n    }\n})\n\nexport const {\n    selectAll: selectAllTrendingVideos,\n} = trendingAdapter.getSelectors(state => state.trending)\n\nexport const selectTrendingError = state => state.trending.error\n\nexport default trendingSlice.reducer\n"
  },
  {
    "path": "src/store/reducers/slices/tvByGenreSlice.js",
    "content": "import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'\nimport axios from 'baseAxios'\nimport { genreTopVideoTransformation } from 'utils/transformations'\n\n\nconst fetchTvGenres = async () => {\n    try {\n        const response = await axios.get(\n            `genre/tv/list?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`\n        )\n\n        return response.data.genres\n    } catch (error) {\n        throw new Error(error)\n    }\n}\n\nexport const fetchTvShowsByGenres = createAsyncThunk('tvByGenreSlice/fetchTvShowsByGenres',\n    async (_, { rejectWithValue }) => {\n        try {\n            const genres = await fetchTvGenres()\n            return await genreTopVideoTransformation(genres, 'tv')\n        } catch (error) {\n            if (!error.response) {\n                throw error\n            }\n\n            return rejectWithValue(error.response.data)\n        }\n    }\n)\n\nconst initalState = {\n    genres: [],\n    status: 'idle',\n    error: null\n}\n\nconst tvByGenreSlice = createSlice({\n    name: 'tvByGenre',\n    initialState: initalState,\n    extraReducers: {\n        [fetchTvShowsByGenres.pending]: (state, _) => {\n            state.status = 'loading'\n        },\n\n        [fetchTvShowsByGenres.fulfilled]: (state, action) => {\n            action.payload.forEach(genre => {\n                state.genres.push({ ...genre })\n            })\n\n            state.status = 'success'\n        },\n\n        [fetchTvShowsByGenres.rejected]: (state, action) => {\n            state.status = 'error'\n            if (action.payload) {\n                state.error = action.payload.status_message\n            } else {\n                state.error = action.error\n            }\n        }\n    }\n})\n\nexport const selectTvByGenre = state => state.tvByGenre\n\nexport default tvByGenreSlice.reducer "
  },
  {
    "path": "src/store/reducers/store.js",
    "content": "import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'\nimport trendingReducer from './slices/trendingSlice'\nimport topRatedReducer from './slices/topratedSlice'\nimport netflixOriginalsReducer from './slices/netflixOriginalsSlice'\nimport moviesByGenresReducer from './slices/moviesByGenreSlice'\nimport tvByGenresReducer from './slices/tvByGenreSlice'\nimport latestVideoReducer from './slices/latestVideoSlice'\n\nconst store = configureStore({\n    reducer: {\n        trending: trendingReducer,\n        toprated: topRatedReducer,\n        netflixOriginals: netflixOriginalsReducer,\n        moviesByGenre: moviesByGenresReducer,\n        tvByGenre: tvByGenresReducer,\n        latestVideos: latestVideoReducer\n    },\n    // Clear this in production, as it is done by default \n    middleware: [...getDefaultMiddleware({ immutableCheck: false })]\n})\n\nexport default store \n"
  },
  {
    "path": "src/styles.css",
    "content": "body {\n  margin: 0;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  -webkit-tap-highlight-color: transparent;\n}\n\nbody button {\n    cursor: pointer;\n}\n\n::-webkit-scrollbar {\n  display: none;\n}\n"
  },
  {
    "path": "src/utils/animations.js",
    "content": "export const scrollTo = (element, to, duration, scrollToDone = null) => {\n    Math.easeInOutQuad = (t, b, c, d) => {\n        t /= d / 2;\n        if (t < 1) return c / 2 * t * t + b;\n        t--;\n        return -c / 2 * (t * (t - 2) - 1) + b;\n    };\n\n    let start = element.scrollLeft,\n        change = to - start,\n        currentTime = 0,\n        increment = 20;\n\n    const animateScroll = () => {\n        currentTime += increment;\n        const val = Math.easeInOutQuad(currentTime, start, change, duration);\n        element.scrollLeft = val;\n        if (currentTime < duration) {\n            setTimeout(animateScroll, increment);\n        } else {\n            if (scrollToDone) scrollToDone();\n        }\n    };\n    animateScroll();\n}\n"
  },
  {
    "path": "src/utils/sorting.js",
    "content": "export const sortVideosByPopularity = (a, b) => {\n    if (a.popularity > b.popularity) {\n        return -1;\n    }\n    if (a.popularity < b.popularity) {\n        return 1;\n    }\n    return 0;\n}\n\n"
  },
  {
    "path": "src/utils/time.js",
    "content": "import React from 'react'\n\nconst convertTimeToHourMinuteFormat = timeInHours => {\n    var hours = Math.trunc(timeInHours / 60);\n    var minutes = timeInHours % 60;\n    return `${hours}h ${minutes}m`\n}\n\nexport const getSeasonsOrMovieLength = (seasons, runtime) => {\n    let timeSpan\n    if (runtime) {\n        timeSpan = <span>{convertTimeToHourMinuteFormat(runtime)}</span>\n    } else if (seasons) {\n        timeSpan = (\n            <span>\n                {seasons.length > 1 ? `${seasons.length} Seasons` : `${seasons.length} Season`}\n            </span>\n        )\n    }\n\n    return timeSpan\n}"
  },
  {
    "path": "src/utils/transformations.js",
    "content": "import React from 'react'\n\nimport axios from 'baseAxios'\nimport { isMobile } from 'react-device-detect'\nimport VideoModal from 'components/Modals/VideoModal/VideoModal'\n\n/**\n * \n * @param {*} genres: the genres\n * @param {*} apiCallType: whether the subsequent API calls will be made for tv or movies \n * \n * Takes a genre object and does creates a big chain of API calls to get each genre's top trending videos\n * \n * Fetches all genres and creates a trending movies API call for each. I push the response with \n * a title and content to make it easier to label the video carousels later. This Promise.all \n * function returns a large array, so I have to parse through the action.payload later \n * in the slice reducer.  \n */\n\nexport const genreTopVideoTransformation = async (genres, apiCallType) => {\n    let url\n    if (apiCallType === 'tv') {\n        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=`\n    } else if (apiCallType === 'movie') {\n        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=`\n    }\n\n    const genreRequestArray = []\n    genres.forEach(genre => {\n        let newUrlParser = url\n        newUrlParser += genre.id.toString()\n        genreRequestArray.push(axios.get(newUrlParser).then(response =>\n            ({ title: genre.name, videos: response.data.results })))\n    })\n\n    try {\n        return await Promise.all(genreRequestArray)\n    } catch (error) {\n        throw new Error(error)\n    }\n}\n\nexport const mediaTypeToVideoDetailTransformation = async (videoId, mediaType) => {\n    let requestURL;\n    if (mediaType === 'movie') {\n        requestURL = `movie/${videoId}?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`\n    } else if (mediaType === 'tv') {\n        requestURL = `tv/${videoId}?api_key=${process.env.REACT_APP_MOVIEDB_API_KEY}&language=en-US`\n    }\n\n    try {\n        const response = await axios.get(requestURL)\n        return response.data\n    } catch (error) {\n        throw new Error(error)\n    }\n}\n\nexport const buildVideoMetadata = (videoItem, selectedVideoInfo) => {\n    let mediaType\n    if (videoItem.media_type) {\n        mediaType = videoItem.media_type\n    } else {\n        if (videoItem.first_air_date) {\n            mediaType = 'tv'\n        } else if (videoItem.release_date) {\n            mediaType = 'movie'\n        }\n    }\n\n    let extraInfo = {}\n    if (!isMobile) {\n        if (selectedVideoInfo && selectedVideoInfo.id === videoItem.id) {\n            extraInfo['genres'] = selectedVideoInfo.genres\n            if (selectedVideoInfo.runtime) {\n                extraInfo['runtime'] = selectedVideoInfo.runtime\n            } else if (selectedVideoInfo.seasons) {\n                extraInfo['seasons'] = selectedVideoInfo.seasons\n            }\n        }\n    }\n\n    return { mediaType, extraInfo }\n}\n\nexport const buildVideoModal = (videoDetailModal, videoInfo, handlers) => {\n    let detailModalComponent\n    if (videoDetailModal && videoInfo) {\n        detailModalComponent = (\n            <VideoModal\n                videoDetailModal={videoDetailModal}\n                videoInfo={videoInfo}\n                {...handlers}\n            />\n        )\n    }\n\n    return detailModalComponent\n}"
  },
  {
    "path": "src/utils/validation.js",
    "content": "export const validEmailAndPhoneNumber = input => {\n    const phoneRegex = /^\\d{10}$/   // eslint-disable-next-line \n    const emailRegex = /^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/ \n    return input.match(phoneRegex) || input.match(emailRegex)\n}"
  }
]