diff --git a/src/assets/menu-svgrepo-com.svg b/src/assets/menu-svgrepo-com.svg new file mode 100644 index 0000000..c644c2a --- /dev/null +++ b/src/assets/menu-svgrepo-com.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 95cb7ab..04ad433 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -4,9 +4,7 @@ import { Carousel } from "./Carousel"; import profileDefault from "../assets/user_default.jpg"; import StarRating from "./StarRating"; import CommentCard from "./CommentCard"; - import { CommentsResType, CommentType, MovieType } from "../type"; - import { MyStateType } from "../type"; import { Link, useOutletContext } from "react-router-dom"; import { defaultResponseHandler } from "../apis/custom"; diff --git a/src/components/RecentlyViewedContents.module.scss b/src/components/RecentlyViewedContents.module.scss new file mode 100644 index 0000000..d1aff9e --- /dev/null +++ b/src/components/RecentlyViewedContents.module.scss @@ -0,0 +1,122 @@ +@import "../utils/common.module.scss"; + +// 포지션 설정 +.fixedPositionCon { + @include pageContainer; + position: fixed; + top: calc(62px + ((100vh - 62px) / 2)); + left: 0; + right: 0; + + .relativePostionCon { + position: relative; + width: 100%; + + .absolutePostionCon { + position: absolute; + top: 50%; + left: 100%; + transform: translate(calc(-50%), -50%); + + margin-left: 180px; + @media (max-width: #{$max-screen + 700}) { + margin-left: calc((100vw - 100%) / 4); + } + @media (max-width: #{$max-screen + 480}) { + margin-left: 120px; + } + @media (max-width: #{$max-screen + 240}) { + margin-left: 60px; + } + @media (max-width: #{$medium-screen + 100}) { + margin-left: 20px; + } + } + } +} + +// 스타일 설정 +.listOpenIcon { + width: 60px; + height: 60px; + border-radius: 50%; + background-image: url("../assets/menu-svgrepo-com.svg"); + background-repeat: no-repeat; + background-position: center; + background-color: white; + box-shadow: 0 2px 10px #aaaaaa; +} + +.cardListCon { + padding-right: 10px; + + @media (max-width: #{$max-screen + 480}) { + transform: translate(calc(-10px - 50% + (100vw - 1560px) / 2)); + } + @media (max-width: #{$max-screen + 240}) { + transform: translate(calc(-10px - 50% + (100vw - 1440px) / 2)); + } + @media (max-width: #{$max-screen + 120}) { + transform: translate(calc(-50%)); + } +} +.cardList { + width: 200px; + max-height: 80vh; + padding: 0 20px; + border: 1px solid $light-gray; + border-radius: 10px; + background-color: #eeeeee; + overflow-y: scroll; + + @keyframes expandVertically { + 0% { + transform: scaleY(0); + } + 100% { + transform: scaleY(1); + } + } + animation: expandVertically 0.3s ease forwards; + + &::-webkit-scrollbar { + width: 10px; + } + &::-webkit-scrollbar-track { + border-radius: 10px; + } + &::-webkit-scrollbar-thumb { + background: $light-gray; + border-radius: 10px; + } + &::-webkit-scrollbar-thumb:hover { + background: #bbbbbb; + } + + header { + padding: 10px 0; + h2 { + font-size: 15px; + } + } + ul { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + li { + list-style-type: none; + + img { + width: 100%; + } + .contentTitleBox { + margin-top: -4px; + padding-bottom: 16px; + font-size: 14px; + font-weight: 500; + } + } + } +} diff --git a/src/components/RecentlyViewedContents.tsx b/src/components/RecentlyViewedContents.tsx new file mode 100644 index 0000000..bf7b6ef --- /dev/null +++ b/src/components/RecentlyViewedContents.tsx @@ -0,0 +1,47 @@ +import { useState } from "react"; +import styles from "./RecentlyViewedContents.module.scss"; +import { Link } from "react-router-dom"; +import useRecentContents from "../hooks/useRecentContents"; + +export default function RecentlyViewedContents() { + const [isOpenRecentContents, setIsOpenRecentContents] = useState(false); + const { recentContents } = useRecentContents(); + + return ( +
+
+
+ {isOpenRecentContents ? ( +
setIsOpenRecentContents(false)} + > +
+
+

최근 구경한 영화({recentContents.length}건)

+
+
    + {recentContents.map((content) => ( +
  • + + +
    + {`${content.title_ko} · ${content.directors?.[0].name}`} +
    + +
  • + ))} +
+
+
+ ) : ( +
setIsOpenRecentContents(true)} + >
+ )} +
+
+
+ ); +} diff --git a/src/hooks/useRecentContents.ts b/src/hooks/useRecentContents.ts new file mode 100644 index 0000000..a8067ae --- /dev/null +++ b/src/hooks/useRecentContents.ts @@ -0,0 +1,24 @@ +import { MovieType } from "../type"; + +export default function useRecentContents() { + let recentContents: Partial[]; + try { + recentContents = JSON.parse(localStorage.getItem("recentContents") ?? "[]"); + } catch (e) { + localStorage.removeItem("recentContents"); + recentContents = []; + } + + function addRecentContent(newContent: Partial) { + recentContents = recentContents.filter( + (content) => content.movieCD !== newContent.movieCD + ); + recentContents.push(newContent); + recentContents.length > 10 && recentContents.splice(0, 1); + + localStorage.setItem("recentContents", JSON.stringify(recentContents)); + } + + // 순서를 뒤집어 return + return { recentContents: recentContents.reverse(), addRecentContent }; +} diff --git a/src/pages/ContentPage.tsx b/src/pages/ContentPage.tsx index ef50c21..38e1b19 100644 --- a/src/pages/ContentPage.tsx +++ b/src/pages/ContentPage.tsx @@ -3,11 +3,11 @@ import Content from "../components/Content"; import styles from "./ContentPage.module.scss"; import { useEffect, useState } from "react"; import { MovieType } from "../type"; -// import { convertKeysToCamelCase } from "../utils/snackToCamel"; import { getContentRequest } from "../apis/content"; import { defaultResponseHandler } from "../apis/custom"; import { useAuthContext } from "../contexts/authContext"; import useChangeTitle from "../hooks/useChangeTitle"; +import useRecentContents from "../hooks/useRecentContents"; import useMoveScrollToTop from "../hooks/useMoveScrollToTop"; export default function ContentPage() { @@ -18,13 +18,15 @@ export default function ContentPage() { const { setTitle } = useChangeTitle(); const [refetch, setRefetch] = useState(false); const refetchContent = () => setRefetch(!refetch); + const { addRecentContent } = useRecentContents(); + useMoveScrollToTop(); + useEffect(() => { id && getContentRequest(id, accessToken ?? undefined) .then(defaultResponseHandler) .then((content: MovieType) => { - console.log(content); setTitle(content.title_ko + " - 와플피디아"); setContent(content); }) @@ -33,6 +35,11 @@ export default function ContentPage() { }); }, [id, accessToken, setTitle, refetch]); + // 최근조회영화 목록에 추가 + useEffect(() => { + content && addRecentContent(content); + }, [id, content]); + return (
{content && ( diff --git a/src/pages/Layout.tsx b/src/pages/Layout.tsx index 511b436..020e0e1 100644 --- a/src/pages/Layout.tsx +++ b/src/pages/Layout.tsx @@ -10,6 +10,7 @@ import { useAuthContext } from "../contexts/authContext"; import { defaultResponseHandler } from "../apis/custom"; import SettingModal from "../components/user/SettingModal"; import UserEditModal from "../components/user/UserEditModal"; +import RecentlyViewedContents from "../components/RecentlyViewedContents"; export type CurrentModalType = | null @@ -88,6 +89,7 @@ export default function Layout() { } satisfies OutletContextType } /> +