diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index abcbe85..0acf5dd 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -51,7 +51,8 @@ "loginBtn": "Log in" }, "profile": { - "welcome": "Welcome to your profile," + "welcome": "Welcome to your profile,", + "favorite": "List of favorite movies" }, "userMenu": { "welcome": "Welcome", diff --git a/public/locales/uk/translation.json b/public/locales/uk/translation.json index 174fe00..a41751d 100644 --- a/public/locales/uk/translation.json +++ b/public/locales/uk/translation.json @@ -53,7 +53,8 @@ "loginBtn": "Вхід" }, "profile": { - "welcome": "Ласкаво просимо до профілю," + "welcome": "Ласкаво просимо до профілю,", + "favorite": "Список улюблених фільмів" }, "userMenu": { "welcome": "Раді бачити", diff --git a/src/components/App.jsx b/src/components/App.jsx index 2a4ad77..a9cc51d 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -2,13 +2,15 @@ import { Navigate, Route, Routes } from 'react-router-dom'; import SheredLayout from './SheredLayout/SheredLayout'; import { ThemeProvider } from 'styled-components'; import { GlobalStyles, darkTheme, lightTheme } from 'constants/themes'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { selectTheme } from 'redux/selectors'; -import { lazy } from 'react'; +import { Suspense, lazy, useEffect } from 'react'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import PublicRoute from 'routes/PublicRoutes'; import PrivateRoutes from 'routes/PrivateRoutes'; +import { refreshUser } from 'redux/authorization/authOperations'; +import Loader from './Loader/Loader'; // import '../i18n'; const Home = lazy(() => import('pages/Home')); @@ -23,50 +25,57 @@ const Review = lazy(() => import('components/Review/Review')); export const App = () => { const theme = useSelector(selectTheme); + const dispatch = useDispatch(); + useEffect(() => { + dispatch(refreshUser()); + }, [dispatch]); return ( <> - - }> - } /> - } /> - }> - } /> - } /> - } /> + }> + + }> + } /> + } /> + }> + } /> + } /> + } /> + + + + + } + /> + + + + } + /> + + + + } + /> + + } /> - - - - } - /> - - - - } - /> - - - - } - /> + + - } /> - - diff --git a/src/components/Favorite/Favorite.jsx b/src/components/Favorite/Favorite.jsx new file mode 100644 index 0000000..8ead020 --- /dev/null +++ b/src/components/Favorite/Favorite.jsx @@ -0,0 +1,30 @@ +import { IoHeartOutline, IoHeartSharp } from 'react-icons/io5'; + +import React, { useState } from 'react'; +import { FavBtn } from './Favorite.styled'; +import { useDispatch } from 'react-redux'; +import { addToFavorite } from 'redux/favorites/favoritesSlice'; + +const Favorite = ({ movie }) => { + const dispatch = useDispatch(); + const [isFav, setFav] = useState(); + + return ( + <> + { + setFav(!isFav); + dispatch(addToFavorite(movie)); + }} + > + {!isFav ? ( + + ) : ( + + )} + + + ); +}; + +export default Favorite; diff --git a/src/components/Favorite/Favorite.styled.js b/src/components/Favorite/Favorite.styled.js new file mode 100644 index 0000000..ba4d117 --- /dev/null +++ b/src/components/Favorite/Favorite.styled.js @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +export const FavBtn = styled.button` + display: block; + width: 36px; + height: 36px; + background: transparent; + margin-left: auto; + border: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +`; diff --git a/src/components/Languages/Languages.jsx b/src/components/Languages/Languages.jsx index 546393f..f3f834d 100644 --- a/src/components/Languages/Languages.jsx +++ b/src/components/Languages/Languages.jsx @@ -43,7 +43,7 @@ const Languages = () => { {locales.map(({ code, title, country_code }) => ( { i18n.changeLanguage(code); dispatch(setLenguage(code)); diff --git a/src/components/MoviesList/MoviesList.jsx b/src/components/MoviesList/MoviesList.jsx index 0a2dd7b..7a95e7a 100644 --- a/src/components/MoviesList/MoviesList.jsx +++ b/src/components/MoviesList/MoviesList.jsx @@ -6,7 +6,9 @@ import { Poster, Title, PosterWrap, + FavWrap, } from './MoviesList.styled'; +import Favorite from 'components/Favorite/Favorite'; const MoviesList = ({ movies }) => { const location = useLocation(); @@ -28,6 +30,9 @@ const MoviesList = ({ movies }) => { {title} + + + ))} diff --git a/src/components/MoviesList/MoviesList.styled.js b/src/components/MoviesList/MoviesList.styled.js index dbb1f3f..37c4bbb 100644 --- a/src/components/MoviesList/MoviesList.styled.js +++ b/src/components/MoviesList/MoviesList.styled.js @@ -21,6 +21,7 @@ export const MovieItem = styled.li` background-color: ${props => props.theme.movieWrap}; border-radius: var(--radii); overflow: hidden; + position: relative; @media (min-width: 767px) { flex-basis: calc((100% - 2 * 15px) / 3); } @@ -58,6 +59,16 @@ export const Poster = styled.img` /* width: 100%; */ transition: 500ms cubic-bezier(0.4, 0, 0.2, 1); `; +export const FavWrap = styled.div` + position: absolute; + z-index: 20; + top: 10px; + right: 10px; + width: 36px; + height: 36px; + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.3); +`; export const Title = styled.h1` font-size: 18px; margin-top: 10px; diff --git a/src/pages/Profile.jsx b/src/pages/Profile.jsx index 60d25db..e72a72e 100644 --- a/src/pages/Profile.jsx +++ b/src/pages/Profile.jsx @@ -1,15 +1,15 @@ +import MoviesList from 'components/MoviesList/MoviesList'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import { selectUserName } from 'redux/selectors'; +import { selectFavorite } from 'redux/selectors'; const Profile = () => { const { t } = useTranslation(); - const userName = useSelector(selectUserName); + const movies = useSelector(selectFavorite); return ( <> -

- {t('profile.welcome')} {userName} -

+

{t('profile.favorite')}

+ {movies && } ); }; diff --git a/src/redux/favorites/favoritesSlice.js b/src/redux/favorites/favoritesSlice.js new file mode 100644 index 0000000..e30b513 --- /dev/null +++ b/src/redux/favorites/favoritesSlice.js @@ -0,0 +1,20 @@ +const { createSlice } = require('@reduxjs/toolkit'); + +const favoriteSlise = createSlice({ + name: 'favorites', + initialState: { + movies: [], + isLoading: false, + }, + reducers: { + addToFavorite(state, action) { + state.movies.push(action.payload); + }, + deleteFromFavorite(state, action) { + state.movies = state.movies.filter(el => el.id !== action.payload.id); + }, + }, +}); + +export const { addToFavorite, deleteFromFavorite } = favoriteSlise.actions; +export const favoriteReducer = favoriteSlise.reducer; diff --git a/src/redux/lenguages/lenguageSlice.js b/src/redux/lenguages/lenguageSlice.js index 1af7049..f453692 100644 --- a/src/redux/lenguages/lenguageSlice.js +++ b/src/redux/lenguages/lenguageSlice.js @@ -6,7 +6,7 @@ console.log(currntLang); const lenguageSlice = createSlice({ name: 'lenguage', initialState: { - lenguage: currntLang, + lenguage: currntLang || 'en', }, reducers: { setLenguage(state, action) { diff --git a/src/redux/selectors.js b/src/redux/selectors.js index eb9d18e..96c61ec 100644 --- a/src/redux/selectors.js +++ b/src/redux/selectors.js @@ -10,12 +10,13 @@ export const selectSearchIsLoading = state => state.searchMovies.isLoading; export const selectSearchMoviePage = state => state.searchMovies.page; export const selectSearchTotalPages = state => state.searchMovies.totalPages; -export const selectTheme = state => state.theme.theme; -export const selectLenguage = state => state.lenguage.lenguage; - export const selectUser = state => state.auth.user; export const selectEmail = state => state.auth.user.email; export const selectUserName = state => state.auth.user.name; export const selectToken = state => state.auth.token; export const selectIsLoggedIn = state => state.auth.isLoggedIn; export const selectIsRefreshing = state => state.auth.isRefreshing; + +export const selectTheme = state => state.theme.theme; +export const selectLenguage = state => state.lenguage.lenguage; +export const selectFavorite = state => state.favorite.movies; diff --git a/src/redux/store.js b/src/redux/store.js index 8b79b2a..72cef8c 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -14,6 +14,7 @@ import { } from 'redux-persist'; import { authReducer } from './authorization/authSlice'; import { lenguageReducer } from './lenguages/lenguageSlice'; +import { favoriteReducer } from './favorites/favoritesSlice'; const themePersistConfig = { key: 'theme', @@ -28,12 +29,17 @@ const lenguagePresistConfig = { key: 'lenguage', storage, }; +const favoritePersistConfig = { + key: 'favorite', + storage, +}; const reduser = combineReducers({ movies: moviesReducer, searchMovies: searchMoviesReducer, theme: persistReducer(themePersistConfig, themeReducer), auth: persistReducer(authPersistCongig, authReducer), lenguage: persistReducer(lenguagePresistConfig, lenguageReducer), + favorite: persistReducer(favoritePersistConfig, favoriteReducer), }); export const store = configureStore({ reducer: reduser,