Merge pull request #55 from devandres-tech/migrate-to-typescript
Migrate to typescript
This commit is contained in:
9
custom.d.ts
vendored
Normal file
9
custom.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
declare module '*.svg' {
|
||||||
|
const content: any
|
||||||
|
export default content
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.png' {
|
||||||
|
const value: any
|
||||||
|
export = value
|
||||||
|
}
|
||||||
4402
package-lock.json
generated
4402
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -6,18 +6,19 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build:dev": "webpack --mode development --config webpack.config.js",
|
"build:dev": "webpack --mode development --config webpack.config.js",
|
||||||
"build:prod": "webpack --mode development",
|
"build:prod": "webpack --mode development",
|
||||||
"run:dev": "webpack serve --mode development --config webpack.config.js --open",
|
"start:dev": "webpack serve --mode development --config webpack.config.js --open",
|
||||||
"run:prod": "webpack serve --mode production --open",
|
"start:prod": "webpack serve --mode production --open",
|
||||||
"lint": "eslint --fix . && echo 'Lint complete.'"
|
"lint": "eslint --fix . && echo 'Lint complete.'"
|
||||||
},
|
},
|
||||||
"engines": {
|
|
||||||
"node": "v14.15.4",
|
|
||||||
"npm": "7.24.0",
|
|
||||||
"watch": "watch 'clear && npm run -s test | tap-nirvana && npm run -s lint' src"
|
|
||||||
},
|
|
||||||
"author": "Andres Alcocer",
|
"author": "Andres Alcocer",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@reduxjs/toolkit": "^1.9.0",
|
||||||
|
"@types/jest": "^29.2.3",
|
||||||
|
"@types/node": "^18.11.9",
|
||||||
|
"@types/react": "^18.0.25",
|
||||||
|
"@types/react-dom": "^18.0.9",
|
||||||
|
"@types/react-redux": "^7.1.24",
|
||||||
"axios": "^1.2.0",
|
"axios": "^1.2.0",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"firebase": "^9.14.0",
|
"firebase": "^9.14.0",
|
||||||
@@ -31,7 +32,9 @@
|
|||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"redux-promise": "^0.6.0",
|
"redux-promise": "^0.6.0",
|
||||||
"redux-thunk": "^2.4.2",
|
"redux-thunk": "^2.4.2",
|
||||||
"swiper": "^8.4.5"
|
"swiper": "^8.4.5",
|
||||||
|
"ts-loader": "^9.4.1",
|
||||||
|
"typescript": "^4.9.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.2",
|
"@babel/core": "^7.20.2",
|
||||||
@@ -46,6 +49,7 @@
|
|||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.7.2",
|
"css-loader": "^6.7.2",
|
||||||
|
"css-minimizer-webpack-plugin": "^4.2.2",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
@@ -54,22 +58,19 @@
|
|||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.31.11",
|
"eslint-plugin-react": "^7.31.11",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-loader": "^4.2.0",
|
"html-loader": "^4.2.0",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"image-webpack-loader": "^8.1.0",
|
"image-webpack-loader": "^8.1.0",
|
||||||
"mini-css-extract-plugin": "^2.7.0",
|
"mini-css-extract-plugin": "^2.7.0",
|
||||||
"node-sass": "^8.0.0",
|
"node-sass": "^8.0.0",
|
||||||
"optimize-css-assets-webpack-plugin": "^6.0.1",
|
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"react-owl-carousel2": "^0.3.0",
|
|
||||||
"sass-loader": "^13.2.0",
|
"sass-loader": "^13.2.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"svg-inline-loader": "^0.8.2",
|
"svg-inline-loader": "^0.8.2",
|
||||||
"svg-react-loader": "^0.4.6",
|
"svg-react-loader": "^0.4.6",
|
||||||
"tap-nirvana": "^1.1.0",
|
"tap-nirvana": "^1.1.0",
|
||||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
"terser-webpack-plugin": "^5.3.6",
|
||||||
"watch": "^1.0.2",
|
"watch": "^1.0.2",
|
||||||
"webpack": "^5.75.0",
|
"webpack": "^5.75.0",
|
||||||
"webpack-cli": "^5.0.0",
|
"webpack-cli": "^5.0.0",
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
/** base url to make requests to the the movie database */
|
|
||||||
const instance = axios.create({
|
|
||||||
baseURL: 'https://api.themoviedb.org/3',
|
|
||||||
});
|
|
||||||
|
|
||||||
export default instance;
|
|
||||||
8
src/axios-movies.ts
Normal file
8
src/axios-movies.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
/** base url to make requests to the movie database */
|
||||||
|
const instance = axios.create({
|
||||||
|
baseURL: 'https://api.themoviedb.org/3',
|
||||||
|
})
|
||||||
|
|
||||||
|
export default instance
|
||||||
@@ -1,13 +1,27 @@
|
|||||||
import React, { Component } from 'react'
|
import React from 'react'
|
||||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||||
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper'
|
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper'
|
||||||
|
|
||||||
import { useViewport } from '../hooks/useViewport'
|
import { useViewport } from '../hooks/useViewport'
|
||||||
|
|
||||||
|
import { IMovieDetails } from '../store/slices/movieDetailsSlice'
|
||||||
|
|
||||||
|
interface IDisplayMovie {
|
||||||
|
title: string
|
||||||
|
isNetflixMovies?: boolean
|
||||||
|
movies: IMovieDetails[]
|
||||||
|
selectMovieHandler?: (movie: IMovieDetails) => void
|
||||||
|
}
|
||||||
|
|
||||||
// install Swiper components
|
// install Swiper components
|
||||||
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y])
|
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y])
|
||||||
|
|
||||||
const DisplayMovieRow = ({ title, isNetflixMovies, movies, selectMovieHandler }) => {
|
const DisplayMovieRow = ({
|
||||||
|
title,
|
||||||
|
isNetflixMovies,
|
||||||
|
movies,
|
||||||
|
selectMovieHandler,
|
||||||
|
}: IDisplayMovie) => {
|
||||||
const [windowDimensions] = useViewport()
|
const [windowDimensions] = useViewport()
|
||||||
const { width } = windowDimensions
|
const { width } = windowDimensions
|
||||||
|
|
||||||
@@ -6,7 +6,12 @@ import MuteIcon from '../static/images/mute.svg'
|
|||||||
import UnmuteIcon from '../static/images/unmute.svg'
|
import UnmuteIcon from '../static/images/unmute.svg'
|
||||||
import ReactPlayer from 'react-player'
|
import ReactPlayer from 'react-player'
|
||||||
|
|
||||||
const Header = ({ movie: { name, overview } }) => {
|
interface IHeader {
|
||||||
|
name: string
|
||||||
|
overview: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header = ({ name, overview }: IHeader) => {
|
||||||
const [isMuted, setIsMuted] = useState(true)
|
const [isMuted, setIsMuted] = useState(true)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -33,7 +38,6 @@ const Header = ({ movie: { name, overview } }) => {
|
|||||||
<AddLogo className='header__container-btnMyList-add' />
|
<AddLogo className='header__container-btnMyList-add' />
|
||||||
My List
|
My List
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{isMuted ? (
|
{isMuted ? (
|
||||||
<MuteIcon
|
<MuteIcon
|
||||||
onClick={() => setIsMuted(false)}
|
onClick={() => setIsMuted(false)}
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import React, { useEffect } from 'react'
|
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
|
||||||
import * as movieActions from '../store/actions'
|
|
||||||
|
|
||||||
import Header from './Header'
|
|
||||||
import DisplayMovieRow from './DisplayMovieRow'
|
|
||||||
|
|
||||||
const MainContent = ({ selectMovieHandler }) => {
|
|
||||||
const { movieDetails } = useSelector((state) => state.movieDetails)
|
|
||||||
const netflixOriginals = useSelector((state) => state.netflixOriginals)
|
|
||||||
const trending = useSelector((state) => state.trending)
|
|
||||||
const topRated = useSelector((state) => state.topRated)
|
|
||||||
const actionMovies = useSelector((state) => state.action)
|
|
||||||
const comedyMovies = useSelector((state) => state.comedy)
|
|
||||||
const horrorMovies = useSelector((state) => state.horror)
|
|
||||||
const romanceMovies = useSelector((state) => state.romance)
|
|
||||||
const documentaries = useSelector((state) => state.documentary)
|
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(movieActions.fetchMovieDetails('tv', '63351'))
|
|
||||||
dispatch(movieActions.fetchNetflixOriginals())
|
|
||||||
dispatch(movieActions.fetchTrending())
|
|
||||||
dispatch(movieActions.fetchTopRated())
|
|
||||||
dispatch(movieActions.fetchActionMovies())
|
|
||||||
dispatch(movieActions.fetchComedyMovies())
|
|
||||||
dispatch(movieActions.fetchHorrorMovies())
|
|
||||||
dispatch(movieActions.fetchRomanceMovies())
|
|
||||||
dispatch(movieActions.fetchDocumentaries())
|
|
||||||
}, [dispatch])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='container'>
|
|
||||||
<Header movie={movieDetails} />
|
|
||||||
<div className='movieShowcase'>
|
|
||||||
<DisplayMovieRow
|
|
||||||
isNetflixMovies={true}
|
|
||||||
title='Netflix Originals'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={netflixOriginals.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Trending'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={trending.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Top Rated'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={topRated.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Action Movies'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={actionMovies.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Comedy'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={comedyMovies.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Horror Movies'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={horrorMovies.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Romance'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={romanceMovies.data}
|
|
||||||
/>
|
|
||||||
<DisplayMovieRow
|
|
||||||
title='Documentaries'
|
|
||||||
selectMovieHandler={selectMovieHandler}
|
|
||||||
movies={documentaries.data}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MainContent
|
|
||||||
96
src/components/MainContent.tsx
Normal file
96
src/components/MainContent.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import * as actionMoviesSlice from '../store/slices/actionMovieSlice'
|
||||||
|
import * as movieDetailsSlice from '../store/slices/movieDetailsSlice'
|
||||||
|
import * as netflixOriginalsSlice from '../store/slices/netflixOriginalsSlice'
|
||||||
|
import * as trendingSlice from '../store/slices/trendingSlice'
|
||||||
|
import * as topRatedSlice from '../store/slices/topRatedSlice'
|
||||||
|
import * as comedySlice from '../store/slices/comedyMoviesSlice'
|
||||||
|
import * as documentarySlice from '../store/slices/documentarySlice'
|
||||||
|
import * as horrorMoviesSlice from '../store/slices/horrorMoviesSlice'
|
||||||
|
import * as romanceMoviesSlice from '../store/slices/romanceMoviesSlice'
|
||||||
|
import { useAppSelector, useAppDispatch } from '../store'
|
||||||
|
|
||||||
|
import Header from './Header'
|
||||||
|
import DisplayMovieRow from './DisplayMovieRow'
|
||||||
|
|
||||||
|
const MainContent = ({ selectMovieHandler }: { selectMovieHandler: any }) => {
|
||||||
|
const { movieDetails } = useAppSelector((state) => state.movieDetails)
|
||||||
|
const netflixOriginals = useAppSelector((state) => state.netflixOriginals)
|
||||||
|
const trending = useAppSelector((state) => state.trending)
|
||||||
|
const topRated = useAppSelector((state) => state.topRated)
|
||||||
|
const actionMoviesState = useAppSelector((state) => state.action)
|
||||||
|
const comedyMovies = useAppSelector((state) => state.comedy)
|
||||||
|
const horrorMovies = useAppSelector((state) => state.horror)
|
||||||
|
const romanceMovies = useAppSelector((state) => state.romance)
|
||||||
|
const documentaries = useAppSelector((state) => state.documentary)
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(
|
||||||
|
movieDetailsSlice.getMovieDetailsAsync({
|
||||||
|
mediaType: 'tv',
|
||||||
|
mediaId: '63351',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
dispatch(netflixOriginalsSlice.getNetflixOriginalsAsync())
|
||||||
|
dispatch(trendingSlice.getTrendingAsync())
|
||||||
|
dispatch(topRatedSlice.getTopRatedAsync())
|
||||||
|
dispatch(actionMoviesSlice.getActionMoviesAsync())
|
||||||
|
dispatch(comedySlice.getComedyMoviesAsync())
|
||||||
|
dispatch(horrorMoviesSlice.getHorrorMoviesAsync())
|
||||||
|
dispatch(romanceMoviesSlice.getRomanceMoviesAsync())
|
||||||
|
dispatch(documentarySlice.getDocumentariesAsync())
|
||||||
|
}, [dispatch])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='container'>
|
||||||
|
<Header name={movieDetails.name} overview={movieDetails.overview} />
|
||||||
|
<div className='movieShowcase'>
|
||||||
|
<DisplayMovieRow
|
||||||
|
isNetflixMovies={true}
|
||||||
|
title='Netflix Originals'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={netflixOriginals.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Trending'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={trending.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Top Rated'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={topRated.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Action Movies'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={actionMoviesState.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Comedy'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={comedyMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Horror Movies'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={horrorMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Romance'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={romanceMovies.data}
|
||||||
|
/>
|
||||||
|
<DisplayMovieRow
|
||||||
|
title='Documentaries'
|
||||||
|
selectMovieHandler={selectMovieHandler}
|
||||||
|
movies={documentaries.data}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainContent
|
||||||
35
src/components/Modal.tsx
Normal file
35
src/components/Modal.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
interface IBackdrop {
|
||||||
|
toggleBackdrop?: () => void
|
||||||
|
show: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IModal extends IBackdrop {
|
||||||
|
backgroundImage: string
|
||||||
|
children: JSX.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
const Backdrop = ({ toggleBackdrop, show }: IBackdrop) =>
|
||||||
|
show ? <div onClick={toggleBackdrop} className='backdrop'></div> : null
|
||||||
|
|
||||||
|
const Modal = ({ show, toggleBackdrop, children, backgroundImage }: IModal) => {
|
||||||
|
const backgroundStyle = {
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundImage: `url(https://image.tmdb.org/t/p/original/${backgroundImage})`,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Backdrop show={show} toggleBackdrop={toggleBackdrop} />
|
||||||
|
<div
|
||||||
|
style={backgroundStyle}
|
||||||
|
className={show ? 'modal show' : 'modal hide'}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Modal
|
||||||
@@ -2,9 +2,10 @@ import React from 'react'
|
|||||||
|
|
||||||
import AddIcon from '../static/images/add.svg'
|
import AddIcon from '../static/images/add.svg'
|
||||||
import PlayIcon from '../static/images/play-button.svg'
|
import PlayIcon from '../static/images/play-button.svg'
|
||||||
|
import { IMovieDetails } from '../store/slices/movieDetailsSlice'
|
||||||
|
|
||||||
const MovieDetails = ({
|
const MovieDetails = (props: IMovieDetails) => {
|
||||||
movie: {
|
const {
|
||||||
title,
|
title,
|
||||||
name,
|
name,
|
||||||
vote_average,
|
vote_average,
|
||||||
@@ -15,8 +16,7 @@ const MovieDetails = ({
|
|||||||
number_of_episodes,
|
number_of_episodes,
|
||||||
number_of_seasons,
|
number_of_seasons,
|
||||||
overview,
|
overview,
|
||||||
},
|
} = props
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div className='modal__container'>
|
<div className='modal__container'>
|
||||||
<h1 className='modal__title'>{title || name}</h1>
|
<h1 className='modal__title'>{title || name}</h1>
|
||||||
@@ -7,7 +7,7 @@ import SearchLogo from '../static/images/search-icon.svg'
|
|||||||
import NetflixLogo from '../static/images/Netflix_Logo_RGB.png'
|
import NetflixLogo from '../static/images/Netflix_Logo_RGB.png'
|
||||||
import BellLogo from '../static/images/bell-logo.svg'
|
import BellLogo from '../static/images/bell-logo.svg'
|
||||||
import DropdownArrow from '../static/images/drop-down-arrow.svg'
|
import DropdownArrow from '../static/images/drop-down-arrow.svg'
|
||||||
import DropdownContent from '../components/DropdownContent'
|
import DropdownContent from './DropdownContent'
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@@ -16,7 +16,7 @@ const Navbar = () => {
|
|||||||
const [scrollDimensions] = useScroll()
|
const [scrollDimensions] = useScroll()
|
||||||
const { scrollY } = scrollDimensions
|
const { scrollY } = scrollDimensions
|
||||||
|
|
||||||
const onChange = async (event) => {
|
const onChange = async (event: any) => {
|
||||||
setUserInput(event.target.value)
|
setUserInput(event.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
const backdrop = ({ toggleBackdrop, show }) =>
|
|
||||||
show ? <div onClick={toggleBackdrop} className='backdrop'></div> : null
|
|
||||||
|
|
||||||
export default backdrop
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
import Backdrop from './Backdrop'
|
|
||||||
|
|
||||||
const Modal = ({ show, modalClosed, children, backgroundImage }) => {
|
|
||||||
const backgroundStyle = {
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${backgroundImage})`,
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Backdrop show={show} toggleBackdrop={modalClosed} />
|
|
||||||
<div
|
|
||||||
style={backgroundStyle}
|
|
||||||
className={show ? 'modal show' : 'modal hide'}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Modal
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import MovieGenre from './components/MovieGenre';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export function getMovieRows(movies, url) {
|
|
||||||
const movieRow = movies.map((movie) => {
|
|
||||||
let movieImageUrl =
|
|
||||||
'https://image.tmdb.org/t/p/w500/' + movie.backdrop_path;
|
|
||||||
if (
|
|
||||||
url === `/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
|
||||||
) {
|
|
||||||
movieImageUrl =
|
|
||||||
'https://image.tmdb.org/t/p/original/' + movie.poster_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (movie.poster_path && movie.backdrop_path !== null) {
|
|
||||||
const movieComponent = (
|
|
||||||
<MovieGenre
|
|
||||||
key={movie.id}
|
|
||||||
url={url}
|
|
||||||
posterUrl={movieImageUrl}
|
|
||||||
movie={movie}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return movieComponent;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return movieRow;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useDebounce } from './useDebounce'
|
import { useDebounce } from './useDebounce'
|
||||||
import { useScroll } from './useScroll'
|
import { useScroll } from './useScroll'
|
||||||
import { useViewport } from './useViewport'
|
import { useViewport } from './useViewport'
|
||||||
import { useWithRouter } from './useWithRouter'
|
|
||||||
|
|
||||||
export { useDebounce, useScroll, useViewport, useWithRouter }
|
export { useDebounce, useScroll, useViewport }
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
export const useDebounce = (value, delay) => {
|
export const useDebounce = (value: string, delay: number) => {
|
||||||
// State and setters for debounced value
|
// State and setters for debounced value
|
||||||
const [debouncedValue, setDebouncedValue] = useState(value)
|
const [debouncedValue, setDebouncedValue] = useState(value)
|
||||||
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Hind:400,500,700|Ramabhadra" rel="stylesheet">
|
<link
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
href="https://fonts.googleapis.com/css?family=Hind:400,500,700|Ramabhadra"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
<title>Netflix Clone</title>
|
<title>Netflix Clone</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import * as ReactDOM from 'react-dom/client'
|
import * as ReactDOM from 'react-dom/client'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { createStore, applyMiddleware } from 'redux'
|
|
||||||
import ReduxThunk from 'redux-thunk'
|
|
||||||
import '@babel/polyfill'
|
import '@babel/polyfill'
|
||||||
|
|
||||||
import reducers from './store/reducers'
|
import { store } from './store'
|
||||||
import AppRouter from './AppRouter'
|
import AppRouter from './AppRouter'
|
||||||
|
|
||||||
// Import Swiper styles
|
// Import Swiper styles
|
||||||
@@ -15,8 +13,6 @@ import 'swiper/css/pagination'
|
|||||||
// Import main sass file to apply global styles
|
// Import main sass file to apply global styles
|
||||||
import './static/sass/style.scss'
|
import './static/sass/style.scss'
|
||||||
|
|
||||||
const store = createStore(reducers, applyMiddleware(ReduxThunk))
|
|
||||||
|
|
||||||
const app = (
|
const app = (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import MainContent from '../components/MainContent'
|
import MainContent from '../components/MainContent'
|
||||||
import Modal from '../components/UI/Modal'
|
import Modal from '../components/Modal'
|
||||||
import ModalMovieDetails from '../components/ModalMovieDetails'
|
import ModalMovieDetails from '../components/ModalMovieDetails'
|
||||||
|
import { IMovieDetails } from '../store/slices/movieDetailsSlice'
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const [toggleModal, setToggleModal] = useState(false)
|
const [toggleModal, setToggleModal] = useState(false)
|
||||||
const [movieDetails, setMovieDetails] = useState({})
|
const [movieDetails, setMovieDetails] = useState<IMovieDetails>({
|
||||||
|
poster_path: '',
|
||||||
|
backdrop_path: '',
|
||||||
|
})
|
||||||
|
|
||||||
const selectMovieHandler = async (movie) => {
|
const selectMovieHandler = async (movie: IMovieDetails) => {
|
||||||
setToggleModal(true)
|
setToggleModal(true)
|
||||||
setMovieDetails(movie)
|
setMovieDetails(movie)
|
||||||
}
|
}
|
||||||
@@ -24,10 +28,10 @@ const Home = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Modal
|
<Modal
|
||||||
show={toggleModal}
|
show={toggleModal}
|
||||||
modalClosed={closeModal}
|
toggleBackdrop={closeModal}
|
||||||
backgroundImage={movieDetails.backdrop_path || movieDetails.poster_path}
|
backgroundImage={movieDetails.backdrop_path || movieDetails.poster_path}
|
||||||
>
|
>
|
||||||
<ModalMovieDetails movie={movieDetails} />
|
<ModalMovieDetails {...movieDetails} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { useAppSelector, useAppDispatch } from '../store'
|
||||||
import ModalMovieDetails from '../components/ModalMovieDetails'
|
import ModalMovieDetails from '../components/ModalMovieDetails'
|
||||||
import Modal from '../components/UI/Modal'
|
import Modal from '../components/Modal'
|
||||||
import { useDebounce } from '../hooks/useDebounce'
|
import { useDebounce } from '../hooks/useDebounce'
|
||||||
import * as movieActions from '../store/actions'
|
import * as searchSlice from '../store/slices/searchSlice'
|
||||||
|
import * as movieDetailsSlice from '../store/slices/movieDetailsSlice'
|
||||||
|
|
||||||
|
interface IMovie {
|
||||||
|
id: string
|
||||||
|
media_type?: string
|
||||||
|
backdrop_path?: string
|
||||||
|
}
|
||||||
|
|
||||||
// A custom hook that builds on useLocation to parse
|
// A custom hook that builds on useLocation to parse
|
||||||
// the query string for you.
|
// the query string for you.
|
||||||
@@ -16,13 +24,15 @@ const Search = () => {
|
|||||||
let query = useQuery()
|
let query = useQuery()
|
||||||
const debouncedSearchTerm = useDebounce(query.get('q'), 500)
|
const debouncedSearchTerm = useDebounce(query.get('q'), 500)
|
||||||
const [isToggleModal, setIsToggleModal] = useState(false)
|
const [isToggleModal, setIsToggleModal] = useState(false)
|
||||||
const { searchResults, isLoading } = useSelector((state) => state.searchMovie)
|
const { searchResults, isLoading } = useAppSelector(
|
||||||
const { movieDetails } = useSelector((state) => state.movieDetails)
|
(state) => state.searchMovie
|
||||||
const dispatch = useDispatch()
|
)
|
||||||
|
const { movieDetails } = useAppSelector((state) => state.movieDetails)
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (debouncedSearchTerm) {
|
if (debouncedSearchTerm) {
|
||||||
dispatch(movieActions.fetchSearchMovie(debouncedSearchTerm))
|
dispatch(searchSlice.searchItemsAsync(debouncedSearchTerm))
|
||||||
}
|
}
|
||||||
}, [debouncedSearchTerm])
|
}, [debouncedSearchTerm])
|
||||||
|
|
||||||
@@ -30,8 +40,13 @@ const Search = () => {
|
|||||||
setIsToggleModal(false)
|
setIsToggleModal(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSelectMovieHandler = (movie) => {
|
const onSelectMovieHandler = (movie: IMovie) => {
|
||||||
dispatch(movieActions.fetchMovieDetails(movie.media_type, movie.id))
|
dispatch(
|
||||||
|
movieDetailsSlice.getMovieDetailsAsync({
|
||||||
|
mediaType: movie.media_type,
|
||||||
|
mediaId: movie.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
setIsToggleModal(true)
|
setIsToggleModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +54,7 @@ const Search = () => {
|
|||||||
return searchResults.length > 0 ? (
|
return searchResults.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<div className='search-container'>
|
<div className='search-container'>
|
||||||
{searchResults.map((movie) => {
|
{searchResults.map((movie: IMovie) => {
|
||||||
if (movie.backdrop_path !== null && movie.media_type !== 'person') {
|
if (movie.backdrop_path !== null && movie.media_type !== 'person') {
|
||||||
const movieImageUrl =
|
const movieImageUrl =
|
||||||
'https://image.tmdb.org/t/p/w500' + movie.backdrop_path
|
'https://image.tmdb.org/t/p/w500' + movie.backdrop_path
|
||||||
@@ -58,12 +73,12 @@ const Search = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Modal
|
<Modal
|
||||||
show={isToggleModal}
|
show={isToggleModal}
|
||||||
modalClosed={onCloseModalHandler}
|
toggleBackdrop={onCloseModalHandler}
|
||||||
backgroundImage={
|
backgroundImage={
|
||||||
movieDetails.backdrop_path || movieDetails.poster_path
|
movieDetails.backdrop_path || movieDetails.poster_path
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ModalMovieDetails movie={movieDetails} />
|
<ModalMovieDetails {...movieDetails} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
import axios from '../../axios-movies'
|
|
||||||
|
|
||||||
export const FETCH_TRENDING = 'FETCH_TRENDING'
|
|
||||||
export const FETCH_NETFLIX_ORIGINALS = 'FETCH_NETFLIX_ORIGINALS'
|
|
||||||
export const FETCH_TOP_RATED = 'FETCH_TOP_RATED'
|
|
||||||
export const FETCH_ACTION_MOVIES = 'FETCH_ACTION_MOVIES'
|
|
||||||
export const FETCH_COMEDY_MOVIES = 'FETCH_COMEDY_MOVIES'
|
|
||||||
export const FETCH_HORROR_MOVIES = 'FETCH_HORROR_MOVIES'
|
|
||||||
export const FETCH_ROMANCE_MOVIES = 'FETCH_ROMANCE_MOVIES'
|
|
||||||
export const FETCH_DOCUMENTARIES = 'FETCH_DOCUMENTARIES'
|
|
||||||
// movie details
|
|
||||||
export const FETCH_MOVIE_DETAILS = 'FETCH_MOVIE_DETAILS'
|
|
||||||
export const FETCH_MOVIE_DETAILS_SUCCESS = 'FETCH_MOVIE_DETAILS_SUCCESS'
|
|
||||||
export const FETCH_MOVIE_DETAILS_FAIL = 'FETCH_MOVIE_DETAILS_FAIL'
|
|
||||||
// search
|
|
||||||
export const FETCH_SEARCH_MOVIE = 'FETCH_SEARCH_MOVIE'
|
|
||||||
export const FETCH_SEARCH_MOVIE_FAIL = 'FETCH_SEARCH_MOVIE_FAIL'
|
|
||||||
export const FETCH_SEARCH_MOVIE_SUCCESS = 'FETCH_SEARCH_MOVIE_SUCCESS'
|
|
||||||
|
|
||||||
const media_type = {
|
|
||||||
tv: 'tv',
|
|
||||||
movie: 'movie',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchMovieDetails = (mediaType, mediaId) => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
dispatch({ type: FETCH_MOVIE_DETAILS })
|
|
||||||
let urlPath
|
|
||||||
if (mediaType === media_type.movie)
|
|
||||||
urlPath = `/movie/${mediaId}?api_key=${process.env.API_KEY}`
|
|
||||||
if (mediaType === media_type.tv)
|
|
||||||
urlPath = `/tv/${mediaId}?api_key=${process.env.API_KEY}`
|
|
||||||
|
|
||||||
const request = await axios.get(urlPath)
|
|
||||||
dispatch({ type: FETCH_MOVIE_DETAILS_SUCCESS, payload: request })
|
|
||||||
} catch (error) {
|
|
||||||
console.log('error', error)
|
|
||||||
dispatch({ type: FETCH_MOVIE_DETAILS_FAIL })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchSearchMovie = (searchTerm) => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
dispatch({ type: FETCH_SEARCH_MOVIE })
|
|
||||||
const request = await axios.get(
|
|
||||||
`/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchTerm}`
|
|
||||||
)
|
|
||||||
dispatch({ type: FETCH_SEARCH_MOVIE_SUCCESS, payload: request })
|
|
||||||
} catch (error) {
|
|
||||||
dispatch({ type: FETCH_SEARCH_MOVIE_FAIL })
|
|
||||||
console.log('error', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchNetflixOriginals = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
|
||||||
)
|
|
||||||
|
|
||||||
dispatch({ type: FETCH_NETFLIX_ORIGINALS, payload: request })
|
|
||||||
} catch (error) {
|
|
||||||
console.log('error', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchTrending = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`
|
|
||||||
)
|
|
||||||
dispatch({ type: FETCH_TRENDING, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchTopRated = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`
|
|
||||||
)
|
|
||||||
dispatch({ type: FETCH_TOP_RATED, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchActionMovies = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
|
||||||
)
|
|
||||||
|
|
||||||
dispatch({ type: FETCH_ACTION_MOVIES, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchComedyMovies = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=35`
|
|
||||||
)
|
|
||||||
|
|
||||||
dispatch({ type: FETCH_COMEDY_MOVIES, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchHorrorMovies = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=27`
|
|
||||||
)
|
|
||||||
dispatch({ type: FETCH_HORROR_MOVIES, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchRomanceMovies = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=10749`
|
|
||||||
)
|
|
||||||
dispatch({ type: FETCH_ROMANCE_MOVIES, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchDocumentaries = () => {
|
|
||||||
return async (dispatch) => {
|
|
||||||
try {
|
|
||||||
const request = await axios.get(
|
|
||||||
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=99`
|
|
||||||
)
|
|
||||||
dispatch({ type: FETCH_DOCUMENTARIES, payload: request })
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
33
src/store/index.ts
Normal file
33
src/store/index.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
import TrendingReducer from './slices/trendingSlice'
|
||||||
|
import NetflixOriginalsReducer from './slices/netflixOriginalsSlice'
|
||||||
|
import TopRatedReducer from './slices/topRatedSlice'
|
||||||
|
import ActionMoviesReducer from './slices/actionMovieSlice'
|
||||||
|
import ComedyMoviesReducer from './slices/comedyMoviesSlice'
|
||||||
|
import HorrorMoviesReducer from './slices/horrorMoviesSlice'
|
||||||
|
import RomanceMoviesReducer from './slices/romanceMoviesSlice'
|
||||||
|
import DocumentaryReducer from './slices/documentarySlice'
|
||||||
|
import SearchMovieReducer from './slices/searchSlice'
|
||||||
|
import MovieDetailsReducer from './slices/movieDetailsSlice'
|
||||||
|
|
||||||
|
export const store = configureStore({
|
||||||
|
reducer: {
|
||||||
|
trending: TrendingReducer,
|
||||||
|
netflixOriginals: NetflixOriginalsReducer,
|
||||||
|
topRated: TopRatedReducer,
|
||||||
|
action: ActionMoviesReducer,
|
||||||
|
comedy: ComedyMoviesReducer,
|
||||||
|
horror: HorrorMoviesReducer,
|
||||||
|
romance: RomanceMoviesReducer,
|
||||||
|
searchMovie: SearchMovieReducer,
|
||||||
|
documentary: DocumentaryReducer,
|
||||||
|
movieDetails: MovieDetailsReducer,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof store.getState>
|
||||||
|
export type AppDispatch = typeof store.dispatch
|
||||||
|
export const useAppDispatch = () => useDispatch<AppDispatch>()
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { combineReducers } from 'redux'
|
|
||||||
import TrendingReducer from './reducerTrending'
|
|
||||||
import NetflixOriginalsReducer from './reducerNetflixOriginals'
|
|
||||||
import TopRatedReducer from './reducerTopRated'
|
|
||||||
import ActionMoviesReducer from './reducerActionMovies'
|
|
||||||
import ComedyMoviesReducer from './reducerComedyMovies'
|
|
||||||
import HorrorMoviesReducer from './reducerHorrorMovies'
|
|
||||||
import RomanceMoviesReducer from './reducerRomanceMovies'
|
|
||||||
import DocumentaryReducer from './reducerDocumentary'
|
|
||||||
import SearchMovieReducer from './reducerSearchMovie'
|
|
||||||
import MovieDetailsReducer from './reducerMovieDetails'
|
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
|
||||||
trending: TrendingReducer,
|
|
||||||
netflixOriginals: NetflixOriginalsReducer,
|
|
||||||
topRated: TopRatedReducer,
|
|
||||||
action: ActionMoviesReducer,
|
|
||||||
comedy: ComedyMoviesReducer,
|
|
||||||
horror: HorrorMoviesReducer,
|
|
||||||
romance: RomanceMoviesReducer,
|
|
||||||
documentary: DocumentaryReducer,
|
|
||||||
searchMovie: SearchMovieReducer,
|
|
||||||
movieDetails: MovieDetailsReducer,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default rootReducer
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_ACTION_MOVIES } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_ACTION_MOVIES:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_COMEDY_MOVIES } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_COMEDY_MOVIES:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_DOCUMENTARIES } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_DOCUMENTARIES:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_HORROR_MOVIES } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_HORROR_MOVIES:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import {
|
|
||||||
FETCH_MOVIE_DETAILS,
|
|
||||||
FETCH_MOVIE_DETAILS_SUCCESS,
|
|
||||||
FETCH_MOVIE_DETAILS_FAIL,
|
|
||||||
} from '../actions/index'
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
isLoading: false,
|
|
||||||
movieDetails: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (state = initialState, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_MOVIE_DETAILS:
|
|
||||||
return { ...state, isLoading: true }
|
|
||||||
case FETCH_MOVIE_DETAILS_FAIL:
|
|
||||||
return { ...state, isLoading: false }
|
|
||||||
case FETCH_MOVIE_DETAILS_SUCCESS:
|
|
||||||
const movieDetails = action.payload.data
|
|
||||||
return { ...state, movieDetails, isLoading: false }
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_NETFLIX_ORIGINALS } from '../actions/index'
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_NETFLIX_ORIGINALS:
|
|
||||||
const data = action.payload.data.results
|
|
||||||
return { ...state, data }
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_ROMANCE_MOVIES } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_ROMANCE_MOVIES:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import {
|
|
||||||
FETCH_SEARCH_MOVIE,
|
|
||||||
FETCH_SEARCH_MOVIE_FAIL,
|
|
||||||
FETCH_SEARCH_MOVIE_SUCCESS,
|
|
||||||
} from '../actions/index'
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
isLoading: false,
|
|
||||||
searchResults: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (state = initialState, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_SEARCH_MOVIE:
|
|
||||||
return { ...state, isLoading: true }
|
|
||||||
case FETCH_SEARCH_MOVIE_FAIL:
|
|
||||||
return { ...state, isLoading: false }
|
|
||||||
case FETCH_SEARCH_MOVIE_SUCCESS:
|
|
||||||
const searchResults = action.payload.data.results
|
|
||||||
return { ...state, searchResults, isLoading: false }
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_TOP_RATED } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_TOP_RATED:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { FETCH_TRENDING } from '../actions/index';
|
|
||||||
|
|
||||||
export default function (state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case FETCH_TRENDING:
|
|
||||||
const data = action.payload.data.results;
|
|
||||||
return { ...state, data };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
33
src/store/slices/actionMovieSlice.ts
Normal file
33
src/store/slices/actionMovieSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getActionMoviesAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('action/getActionMovies', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const actionMovieSlice = createSlice({
|
||||||
|
name: 'actionMovie',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getActionMoviesAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default actionMovieSlice.reducer
|
||||||
33
src/store/slices/comedyMoviesSlice.ts
Normal file
33
src/store/slices/comedyMoviesSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getComedyMoviesAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('comedy/getComedyMovies', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const comedyMovieSlice = createSlice({
|
||||||
|
name: 'comedyMovie',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getComedyMoviesAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default comedyMovieSlice.reducer
|
||||||
33
src/store/slices/documentarySlice.ts
Normal file
33
src/store/slices/documentarySlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDocumentariesAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('documentary/getDocumentaries', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=99`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const documentarySlice = createSlice({
|
||||||
|
name: 'actionMovie',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getDocumentariesAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default documentarySlice.reducer
|
||||||
33
src/store/slices/horrorMoviesSlice.ts
Normal file
33
src/store/slices/horrorMoviesSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHorrorMoviesAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('horror/getHorrorMovies', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=27`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const horrorMoviesSlice = createSlice({
|
||||||
|
name: 'horrorMovies',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getHorrorMoviesAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default horrorMoviesSlice.reducer
|
||||||
80
src/store/slices/movieDetailsSlice.ts
Normal file
80
src/store/slices/movieDetailsSlice.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
|
||||||
|
export interface IMovieDetails {
|
||||||
|
backdrop_path?: string
|
||||||
|
poster_path?: string
|
||||||
|
title?: any
|
||||||
|
name?: any
|
||||||
|
vote_average?: any
|
||||||
|
release_date?: any
|
||||||
|
first_air_date?: any
|
||||||
|
runtime?: any
|
||||||
|
episode_run_time?: any
|
||||||
|
number_of_episodes?: any
|
||||||
|
number_of_seasons?: any
|
||||||
|
overview?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInitialState {
|
||||||
|
isLoading: boolean
|
||||||
|
movieDetails: IMovieDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
const media_type = {
|
||||||
|
tv: 'tv',
|
||||||
|
movie: 'movie',
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: IInitialState = {
|
||||||
|
isLoading: true,
|
||||||
|
movieDetails: {
|
||||||
|
backdrop_path: '',
|
||||||
|
poster_path: '',
|
||||||
|
title: '',
|
||||||
|
name: '',
|
||||||
|
vote_average: '',
|
||||||
|
release_date: '',
|
||||||
|
first_air_date: '',
|
||||||
|
runtime: '',
|
||||||
|
episode_run_time: '',
|
||||||
|
number_of_episodes: '',
|
||||||
|
number_of_seasons: '',
|
||||||
|
overview: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMovieDetailsAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
{ mediaType: string; mediaId: string },
|
||||||
|
{ state: RootState }
|
||||||
|
>('movieDetails/getMovieDetailsAsync', async ({ mediaType, mediaId }) => {
|
||||||
|
let urlPath
|
||||||
|
if (mediaType === media_type.movie)
|
||||||
|
urlPath = `/movie/${mediaId}?api_key=${process.env.API_KEY}`
|
||||||
|
if (mediaType === media_type.tv)
|
||||||
|
urlPath = `/tv/${mediaId}?api_key=${process.env.API_KEY}`
|
||||||
|
const response = await axios.get(urlPath)
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
|
||||||
|
const movieDetailsSlice = createSlice({
|
||||||
|
name: 'movieDetails',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getMovieDetailsAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.isLoading = false
|
||||||
|
console.log('state ', state)
|
||||||
|
console.log('payload', payload)
|
||||||
|
state.movieDetails = payload
|
||||||
|
})
|
||||||
|
builder.addCase(getMovieDetailsAsync.pending, (state) => {
|
||||||
|
state.isLoading = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default movieDetailsSlice.reducer
|
||||||
36
src/store/slices/netflixOriginalsSlice.ts
Normal file
36
src/store/slices/netflixOriginalsSlice.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNetflixOriginalsAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('netflixOriginals/getNetflixOriginals', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/discover/tv?api_key=${process.env.API_KEY}&with_networks=213`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const netflixOriginalsSlice = createSlice({
|
||||||
|
name: 'netflixOriginals',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(
|
||||||
|
getNetflixOriginalsAsync.fulfilled,
|
||||||
|
(state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default netflixOriginalsSlice.reducer
|
||||||
33
src/store/slices/romanceMoviesSlice.ts
Normal file
33
src/store/slices/romanceMoviesSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRomanceMoviesAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('romance/getRomanceMovies', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/discover/movie?api_key=${process.env.API_KEY}&with_genres=28`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const romanceMovieSlice = createSlice({
|
||||||
|
name: 'romanceMovie',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getRomanceMoviesAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default romanceMovieSlice.reducer
|
||||||
37
src/store/slices/searchSlice.ts
Normal file
37
src/store/slices/searchSlice.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
searchResults: [{}],
|
||||||
|
isLoading: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const searchItemsAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
string,
|
||||||
|
{ state: RootState }
|
||||||
|
>('search/getSearchItems', async (searchTerm) => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchTerm}`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchSlice = createSlice({
|
||||||
|
name: 'search',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(searchItemsAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.isLoading = false
|
||||||
|
state.searchResults = payload
|
||||||
|
})
|
||||||
|
builder.addCase(searchItemsAsync.pending, (state) => {
|
||||||
|
state.isLoading = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default searchSlice.reducer
|
||||||
33
src/store/slices/topRatedSlice.ts
Normal file
33
src/store/slices/topRatedSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTopRatedAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('topRated/getTopRated', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/movie/top_rated?api_key=${process.env.API_KEY}&language=en-US`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const trendingSlice = createSlice({
|
||||||
|
name: 'topRated',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getTopRatedAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default trendingSlice.reducer
|
||||||
33
src/store/slices/trendingSlice.ts
Normal file
33
src/store/slices/trendingSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
import axios from '../../axios-movies'
|
||||||
|
import { RootState } from '../index'
|
||||||
|
import { IMovieDetails } from './movieDetailsSlice'
|
||||||
|
|
||||||
|
const initialState: { data: IMovieDetails[] } = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTrendingAsync = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ state: RootState }
|
||||||
|
>('trending/getTrending', async () => {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/trending/all/week?api_key=${process.env.API_KEY}&language=en-US`
|
||||||
|
)
|
||||||
|
return response.data.results
|
||||||
|
})
|
||||||
|
|
||||||
|
const trendingSlice = createSlice({
|
||||||
|
name: 'trending',
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(getTrendingAsync.fulfilled, (state, { payload }) => {
|
||||||
|
state.data = payload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default trendingSlice.reducer
|
||||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "es6",
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "react",
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"typeRoots": ["custom.d.ts", "node_modules/@types"],
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"files": ["custom.d.ts"],
|
||||||
|
"include": ["src/", "custom.d.ts"]
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
const HtmlWebPackPlugin = require('html-webpack-plugin')
|
const HtmlWebPackPlugin = require('html-webpack-plugin')
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin')
|
||||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
|
||||||
const dotenv = require('dotenv')
|
const dotenv = require('dotenv')
|
||||||
const webpack = require('webpack')
|
const webpack = require('webpack')
|
||||||
|
|
||||||
const prod =
|
const prod =
|
||||||
(process.env.NODE_ENV ? process.env.NODE_ENV : '').trim() === 'production'
|
(process.env.NODE_ENV ? process.env.NODE_ENV : '').trim() === 'production'
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
@@ -20,9 +23,13 @@ module.exports = () => {
|
|||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
clean: true,
|
clean: true,
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: 'single',
|
runtimeChunk: 'single',
|
||||||
moduleIds: 'deterministic',
|
moduleIds: 'deterministic',
|
||||||
|
minimizer: [`...`, new CssMinimizerPlugin()],
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
cacheGroups: {
|
cacheGroups: {
|
||||||
vendor: {
|
vendor: {
|
||||||
@@ -30,61 +37,18 @@ module.exports = () => {
|
|||||||
name: 'vendors',
|
name: 'vendors',
|
||||||
chunks: 'all',
|
chunks: 'all',
|
||||||
},
|
},
|
||||||
|
styles: {
|
||||||
|
name: 'styles',
|
||||||
|
type: 'css/mini-extract',
|
||||||
|
chunks: 'all',
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mode: prod ? 'production' : 'development',
|
mode: prod ? 'production' : 'development',
|
||||||
// Enable sourcemaps for debugging webpack's output.
|
// Enable sourcemaps for debugging webpack's output.
|
||||||
devtool: prod ? 'none' : 'eval-source-map',
|
devtool: prod ? 'none' : 'eval-source-map',
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.svg$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'svg-react-loader',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
include: /node_modules/,
|
|
||||||
use: ['style-loader', 'css-loader'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.scss$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: MiniCssExtractPlugin.loader,
|
|
||||||
options: {
|
|
||||||
publicPath: '../',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'css-loader',
|
|
||||||
'sass-loader',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(gif|png|jpe?g)$/i,
|
|
||||||
use: [
|
|
||||||
'file-loader',
|
|
||||||
{
|
|
||||||
loader: 'image-webpack-loader',
|
|
||||||
options: {
|
|
||||||
bypassOnDebug: true, // webpack@1.x
|
|
||||||
disable: true, // webpack@2.x and newer
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
devServer: {
|
devServer: {
|
||||||
historyApiFallback: true,
|
historyApiFallback: true,
|
||||||
},
|
},
|
||||||
@@ -108,9 +72,62 @@ module.exports = () => {
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: 'main.css',
|
filename: '[name].css',
|
||||||
}),
|
}),
|
||||||
new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
],
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'svg-react-loader',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/i,
|
||||||
|
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
options: {
|
||||||
|
publicPath: '../',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'css-loader',
|
||||||
|
'sass-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(gif|png|jpe?g)$/i,
|
||||||
|
use: [
|
||||||
|
'file-loader',
|
||||||
|
{
|
||||||
|
loader: 'image-webpack-loader',
|
||||||
|
options: {
|
||||||
|
bypassOnDebug: true,
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user