diff --git a/src/app/me/[slug]/drafts/page.tsx b/src/app/drafts/[slug]/page.tsx similarity index 100% rename from src/app/me/[slug]/drafts/page.tsx rename to src/app/drafts/[slug]/page.tsx diff --git a/src/app/me/[slug]/layout.tsx b/src/app/me/[slug]/layout.tsx new file mode 100644 index 00000000..aab2ccd4 --- /dev/null +++ b/src/app/me/[slug]/layout.tsx @@ -0,0 +1,139 @@ +import Header from '@/components/shared/Header'; +import { + getHistory, + getMemosByUserId, + getRankInfoByUserId, + getStatsByUserId, +} from '@/service/me'; +import LayoutWrapper from '@/components/shared/LayoutWrapper'; +import { checkUser, getUserInfo } from '@/service/auth'; +import { cookies } from 'next/headers'; +import { ACCESS_TOKEN, MY_TAG_MAX_COUNT, REFRESH_TOKEN } from '@/utils/const'; +import MeSideBar from '@/components/me/MeSideBar'; +import { getUserTags } from '@/service/tag'; +import MeTagsContainer from '@/components/me/MeTagsContainer'; +import { getFollowers, getFollowings } from '@/service/follow'; +import FollowListModalProvider from '@/context/FollowListModalProvider'; +import FollowListModal from '@/components/me/FollowListModal'; +import { getNotificationsTop30 } from '@/service/notification'; +import { getScoreInfoByUserId } from '@/service/rank'; +import { + getCoverletterInfoByUserId, + getCoverletterVisibleMode, +} from '@/service/coverletter'; +import { notFound } from 'next/navigation'; + +type Props = { + children: React.ReactNode; + params: { + slug: string; + }; +}; + +export default async function MeLayout({ children, params: { slug } }: Props) { + const accessToken = cookies().get(ACCESS_TOKEN)?.value; + const refreshToken = cookies().get(REFRESH_TOKEN)?.value; + const cookie = `${ACCESS_TOKEN}=${accessToken}; ${REFRESH_TOKEN}=${refreshToken}`; + const userId = Number(slug); + + const userInfo = await getUserInfo(userId, cookie); + if (userInfo.userId === undefined) { + notFound(); + } + + const memosData = getMemosByUserId(userId); + const historyData = getHistory( + userId, + new Date().getFullYear(), + new Date().getMonth() + 1, + true + ); + const myData = checkUser(cookie); + const tagData = getUserTags(slug, MY_TAG_MAX_COUNT); + const followingData = getFollowings(slug); + const followerData = getFollowers(slug); + const userRankData = getRankInfoByUserId(userId); + const userStatsData = getStatsByUserId(userId); + const userScoreData = getScoreInfoByUserId(userId); + const coverletterVisibleModeData = getCoverletterVisibleMode(); + + const [ + history, + { id, isLogin, authority }, + tags, + followings, + followers, + userRankInfo, + userStats, + { dailyScore }, + coverletterIsVisible, + ] = await Promise.all([ + historyData, + myData, + tagData, + followingData, + followerData, + userRankData, + userStatsData, + userScoreData, + coverletterVisibleModeData, + ]); + + const myUserInfo = await getUserInfo(id); + + const notifications = isLogin ? await getNotificationsTop30(cookie) : []; + + const isCoverletterCreated = await getCoverletterInfoByUserId( + userId, + cookie + ).then((res) => { + if ('code' in res) { + return false; + } + return true; + }); + + return ( +
+
+ +
+ + + + +
+ {tags.length >= 2 && ( + + )} + {children} +
+
+
+
+ ); +} diff --git a/src/app/me/[slug]/like/layout.tsx b/src/app/me/[slug]/like/layout.tsx new file mode 100644 index 00000000..928e539e --- /dev/null +++ b/src/app/me/[slug]/like/layout.tsx @@ -0,0 +1,21 @@ +import MeNavigator from '@/components/me/MeNavigator'; + +type Props = { + children: React.ReactNode; + params: { + slug: string; + }; +}; + +export default async function MeLikeLayout({ + children, + params: { slug }, +}: Props) { + const userId = +slug; + return ( +
+ + {children} +
+ ); +} diff --git a/src/app/me/[slug]/like/memo/page.tsx b/src/app/me/[slug]/like/memo/page.tsx new file mode 100644 index 00000000..f4e61f25 --- /dev/null +++ b/src/app/me/[slug]/like/memo/page.tsx @@ -0,0 +1,21 @@ +import MeMemosContainer from '@/components/me/MeMemosContainer'; +import MePostCategories from '@/components/me/MePostCategories'; +import { getLikedMemosByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MeLikedMemoPage({ params: { slug } }: Props) { + const userId = +slug; + const memos = await getLikedMemosByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/like/page.tsx b/src/app/me/[slug]/like/page.tsx new file mode 100644 index 00000000..2b65ee7d --- /dev/null +++ b/src/app/me/[slug]/like/page.tsx @@ -0,0 +1,21 @@ +import MeMemosContainer from '@/components/me/MeMemosContainer'; +import MePostCategories from '@/components/me/MePostCategories'; +import { getLikedMemosByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MeLikepage({ params: { slug } }: Props) { + const userId = +slug; + const memos = await getLikedMemosByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/like/question/page.tsx b/src/app/me/[slug]/like/question/page.tsx new file mode 100644 index 00000000..9d9393b2 --- /dev/null +++ b/src/app/me/[slug]/like/question/page.tsx @@ -0,0 +1,29 @@ +import MePostCategories from '@/components/me/MePostCategories'; +import MeQuestionsContainer from '@/components/me/MeQuestionsContainer'; +import { getLikedQuestionsByUserId, getQuestionsByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MeLikedQuestionPage({ params: { slug } }: Props) { + const userId = +slug; + const questions = await getLikedQuestionsByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/like/series/page.tsx b/src/app/me/[slug]/like/series/page.tsx new file mode 100644 index 00000000..d6c956fc --- /dev/null +++ b/src/app/me/[slug]/like/series/page.tsx @@ -0,0 +1,25 @@ +import MePostCategories from '@/components/me/MePostCategories'; +import MeSeriesContainer from '@/components/me/MeSeriesContainer'; +import { getLikedSeriesByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MeLikedSeriesPage({ params: { slug } }: Props) { + const userId = +slug; + const series = await getLikedSeriesByUserId(userId); + + return ( + <> + + {' '} + + ); +} diff --git a/src/app/me/[slug]/page.tsx b/src/app/me/[slug]/page.tsx index cddea564..cc5b7a55 100644 --- a/src/app/me/[slug]/page.tsx +++ b/src/app/me/[slug]/page.tsx @@ -1,28 +1,7 @@ -import Header from '@/components/shared/Header'; -import { - getHistory, - getMemosByUserId, - getRankInfoByUserId, - getStatsByUserId, -} from '@/service/me'; -import LayoutWrapper from '@/components/shared/LayoutWrapper'; -import { checkUser, getUserInfo } from '@/service/auth'; -import MeMainContainer from '@/components/me/MeMainContainer'; -import { cookies } from 'next/headers'; -import { ACCESS_TOKEN, MY_TAG_MAX_COUNT, REFRESH_TOKEN } from '@/utils/const'; -import MeSideBar from '@/components/me/MeSideBar'; -import { getUserTags } from '@/service/tag'; -import MeTagsContainer from '@/components/me/MeTagsContainer'; -import { getFollowers, getFollowings } from '@/service/follow'; -import FollowListModalProvider from '@/context/FollowListModalProvider'; -import FollowListModal from '@/components/me/FollowListModal'; -import { getNotificationsTop30 } from '@/service/notification'; -import { getScoreInfoByUserId } from '@/service/rank'; -import { - getCoverletterInfoByUserId, - getCoverletterVisibleMode, -} from '@/service/coverletter'; -import { notFound } from 'next/navigation'; +import { getMemosByUserId } from '@/service/me'; +import MeNavigator from '@/components/me/MeNavigator'; +import MePostCategories from '@/components/me/MePostCategories'; +import MeMemosContainer from '@/components/me/MeMemosContainer'; type Props = { params: { @@ -31,124 +10,16 @@ type Props = { }; export default async function MePage({ params: { slug } }: Props) { - const accessToken = cookies().get(ACCESS_TOKEN)?.value; - const refreshToken = cookies().get(REFRESH_TOKEN)?.value; - const cookie = `${ACCESS_TOKEN}=${accessToken}; ${REFRESH_TOKEN}=${refreshToken}`; const userId = Number(slug); - - const userInfo = await getUserInfo(userId, cookie); - if (userInfo.userId === undefined) { - notFound(); - } - - const memosData = getMemosByUserId(userId); - const historyData = getHistory( - userId, - new Date().getFullYear(), - new Date().getMonth() + 1, - true - ); - const myData = checkUser(cookie); - const tagData = getUserTags(slug, MY_TAG_MAX_COUNT); - const followingData = getFollowings(slug); - const followerData = getFollowers(slug); - const userRankData = getRankInfoByUserId(userId); - const userStatsData = getStatsByUserId(userId); - const userScoreData = getScoreInfoByUserId(userId); - const coverletterVisibleModeData = getCoverletterVisibleMode(); - - const [ - memos, - history, - { id, isLogin, authority }, - tags, - followings, - followers, - userRankInfo, - userStats, - { dailyScore }, - coverletterIsVisible, - ] = await Promise.all([ - memosData, - historyData, - myData, - tagData, - followingData, - followerData, - userRankData, - userStatsData, - userScoreData, - coverletterVisibleModeData, - ]); - - const myUserInfo = await getUserInfo(id); - - const notifications = isLogin ? await getNotificationsTop30(cookie) : []; - - const isCoverletterCreated = await getCoverletterInfoByUserId( - userId, - cookie - ).then((res) => { - if ('code' in res) { - return false; - } - return true; - }); + const memos = await getMemosByUserId(userId); return ( -
-
- -
- - - - -
- {tags.length >= 2 && ( - - )} - -
-
-
+
+ + <> + + +
); } - -export async function generateMetadata({ params: { slug } }: Props) { - const userId = Number(slug); - const { nickname } = await getUserInfo(userId); - if (nickname === undefined) { - notFound(); - } - - return { - title: `${nickname} - 인포럼`, - description: `${nickname}의 마이페이지입니다.`, - }; -} diff --git a/src/app/me/[slug]/post/layout.tsx b/src/app/me/[slug]/post/layout.tsx new file mode 100644 index 00000000..02f412ef --- /dev/null +++ b/src/app/me/[slug]/post/layout.tsx @@ -0,0 +1,21 @@ +import MeNavigator from '@/components/me/MeNavigator'; + +type Props = { + children: React.ReactNode; + params: { + slug: string; + }; +}; + +export default async function MePostLayout({ + children, + params: { slug }, +}: Props) { + const userId = +slug; + return ( +
+ + {children} +
+ ); +} diff --git a/src/app/me/[slug]/post/memo/page.tsx b/src/app/me/[slug]/post/memo/page.tsx new file mode 100644 index 00000000..6de87884 --- /dev/null +++ b/src/app/me/[slug]/post/memo/page.tsx @@ -0,0 +1,21 @@ +import MeMemosContainer from '@/components/me/MeMemosContainer'; +import MePostCategories from '@/components/me/MePostCategories'; +import { getMemosByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MePostMemoPage({ params: { slug } }: Props) { + const userId = +slug; + const memos = await getMemosByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/post/page.tsx b/src/app/me/[slug]/post/page.tsx new file mode 100644 index 00000000..5525413d --- /dev/null +++ b/src/app/me/[slug]/post/page.tsx @@ -0,0 +1,21 @@ +import MeMemosContainer from '@/components/me/MeMemosContainer'; +import MePostCategories from '@/components/me/MePostCategories'; +import { getMemosByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MePostPage({ params: { slug } }: Props) { + const userId = +slug; + const memos = await getMemosByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/post/question/page.tsx b/src/app/me/[slug]/post/question/page.tsx new file mode 100644 index 00000000..e0971868 --- /dev/null +++ b/src/app/me/[slug]/post/question/page.tsx @@ -0,0 +1,29 @@ +import MePostCategories from '@/components/me/MePostCategories'; +import MeQuestionsContainer from '@/components/me/MeQuestionsContainer'; +import { getQuestionsByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MePostQuestionPage({ params: { slug } }: Props) { + const userId = +slug; + const questions = await getQuestionsByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/post/series/page.tsx b/src/app/me/[slug]/post/series/page.tsx new file mode 100644 index 00000000..69559af0 --- /dev/null +++ b/src/app/me/[slug]/post/series/page.tsx @@ -0,0 +1,25 @@ +import MePostCategories from '@/components/me/MePostCategories'; +import MeSeriesContainer from '@/components/me/MeSeriesContainer'; +import { getSeriesByUserId } from '@/service/me'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MePostSeriesPage({ params: { slug } }: Props) { + const userId = +slug; + const series = await getSeriesByUserId(userId); + + return ( + <> + + + + ); +} diff --git a/src/app/me/[slug]/question_answered/page.tsx b/src/app/me/[slug]/question_answered/page.tsx new file mode 100644 index 00000000..2b62469d --- /dev/null +++ b/src/app/me/[slug]/question_answered/page.tsx @@ -0,0 +1,27 @@ +import { getAnswerdQuestionsByUserId } from '@/service/me'; +import MeNavigator from '@/components/me/MeNavigator'; +import MeQuestionsContainer from '@/components/me/MeQuestionsContainer'; + +type Props = { + params: { + slug: string; + }; +}; + +export default async function MeQuestionAnsweredPage({ + params: { slug }, +}: Props) { + const userId = +slug; + const questions = await getAnswerdQuestionsByUserId(userId); + + return ( +
+ + +
+ ); +} diff --git a/src/components/me/MeMainContainer.tsx b/src/components/me/MeMainContainer.tsx deleted file mode 100644 index 11037567..00000000 --- a/src/components/me/MeMainContainer.tsx +++ /dev/null @@ -1,229 +0,0 @@ -'use client'; - -import { Memo } from '@/types/memo'; -import MemosGrid from '../memo/MemosGrid'; -import BarBtn from '@/components/shared/btn/BarBtn'; -import { useEffect, useRef, useState } from 'react'; -import { - getAnswerdQuestionsByUserId, - getLikedMemosByUserId, - getLikedQuestionsByUserId, - getLikedSeriesByUserId, - getMemosByUserId, - getQuestionsByUserId, - getSeriesByUserId, -} from '@/service/me'; -import CategoryBtn from '../shared/btn/CategoryBtn'; -import { Question } from '@/types/question'; -import QuestionsList from '../question/QuestionsList'; -import { Series } from '@/types/series'; -import SeriesGrid from '../series/SeriesGrid'; -import useObserver from '@/hooks/useObserver'; - -type Props = { - memos: Memo[]; - userId: number; -}; - -type BigCategory = 'my' | 'answered' | 'liked'; -type PostType = 'memo' | 'question' | 'series'; - -export default function MeMainContainer({ memos, userId }: Props) { - const [memoData, setMemoData] = useState(memos); - const [questionData, setQuestionData] = useState([]); - const [seriesData, setSeriesData] = useState([]); - const [selectedBigCategory, setSelectedBigCategory] = - useState('my'); - const [selectedPostType, setSelectedPostType] = useState('memo'); - const [pageNum, setPageNum] = useState(0); - const [isEnd, setIsEnd] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const isInitialMount = useRef(true); - - const handleClick = async (type: BigCategory) => { - if (type === selectedBigCategory) return; - setIsLoading(true); - setPageNum(0); - setIsEnd(false); - if (type === 'answered') { - const questions = await getAnswerdQuestionsByUserId(userId); - setQuestionData(questions); - } else { - const memos = - type === 'my' - ? await getMemosByUserId(userId) - : await getLikedMemosByUserId(userId); - setMemoData(memos); - setSelectedPostType('memo'); - } - setIsLoading(false); - setSelectedBigCategory(type); - }; - - const handlePostCategotyClick = async (type: PostType) => { - if (type === selectedPostType) return; - setIsLoading(true); - setPageNum(0); - setIsEnd(false); - let data; - switch (type) { - case 'memo': - data = - selectedBigCategory === 'my' - ? await getMemosByUserId(userId) - : await getLikedMemosByUserId(userId); - setMemoData(data); - break; - case 'question': - data = - selectedBigCategory === 'my' - ? await getQuestionsByUserId(userId) - : await getLikedQuestionsByUserId(userId); - setQuestionData(data); - break; - case 'series': - data = - selectedBigCategory === 'my' - ? await getSeriesByUserId(userId) - : await getLikedSeriesByUserId(userId); - setSeriesData(data); - break; - default: - throw new Error('알맞은 타입이 아님'); - } - setIsLoading(false); - setSelectedPostType(type); - }; - - const fetchPosts = () => { - if (isLoading || isEnd) return; - setIsLoading(true); - if (selectedBigCategory === 'answered') { - getAnswerdQuestionsByUserId(userId, pageNum) - .then((questions) => { - setIsLoading(false); - if (!questions.length) setIsEnd(true); - else setQuestionData([...questionData, ...questions]); - }) - .catch(() => setIsLoading(false)); - } else { - let data; - switch (selectedPostType) { - case 'memo': - data = - selectedBigCategory === 'my' - ? getMemosByUserId(userId, pageNum) - : getLikedMemosByUserId(userId, pageNum); - data - .then((memos) => { - setIsLoading(false); - if (!memos.length) setIsEnd(true); - else { - setMemoData([...memoData, ...memos]); - } - }) - .catch(() => setIsLoading(false)); - break; - case 'question': - data = - selectedBigCategory === 'my' - ? getQuestionsByUserId(userId, pageNum) - : getLikedQuestionsByUserId(userId, pageNum); - data - .then((questions) => { - setIsLoading(false); - if (!questions.length) setIsEnd(true); - else { - setQuestionData([...questionData, ...questions]); - } - }) - .catch(() => setIsLoading(false)); - break; - case 'series': - data = - selectedBigCategory === 'my' - ? getSeriesByUserId(userId, pageNum) - : getLikedSeriesByUserId(userId, pageNum); - data - .then((seriesArray) => { - setIsLoading(false); - if (!seriesArray.length) setIsEnd(true); - else { - setSeriesData([...seriesData, ...seriesArray]); - } - }) - .catch(() => setIsLoading(false)); - break; - default: - throw new Error('알맞은 타입이 아님'); - } - } - }; - - useEffect(() => { - if (isInitialMount.current) { - isInitialMount.current = false; - } else { - fetchPosts(); - } - }, [pageNum]); - - const onIntersect: IntersectionObserverCallback = ([entry]) => { - if (isEnd || isLoading) return; - entry.isIntersecting && setPageNum(pageNum + 1); - }; - - const { setTarget } = useObserver({ onIntersect }); - - return ( -
-
- handleClick('my')} - isSelected={selectedBigCategory === 'my'} - /> - handleClick('answered')} - isSelected={selectedBigCategory === 'answered'} - /> - handleClick('liked')} - isSelected={selectedBigCategory === 'liked'} - /> -
- {selectedBigCategory !== 'answered' && ( -
- handlePostCategotyClick('memo')} - isSelected={selectedPostType === 'memo'} - /> - handlePostCategotyClick('question')} - isSelected={selectedPostType === 'question'} - /> - handlePostCategotyClick('series')} - isSelected={selectedPostType === 'series'} - /> -
- )} - {selectedBigCategory !== 'answered' && selectedPostType === 'memo' && ( - - )} - {(selectedBigCategory === 'answered' || - selectedPostType === 'question') && ( - - )} - {selectedBigCategory !== 'answered' && selectedPostType === 'series' && ( - - )} - {isEnd ? <> :
} -
- ); -} diff --git a/src/components/me/MeMemosContainer.tsx b/src/components/me/MeMemosContainer.tsx new file mode 100644 index 00000000..8391b11e --- /dev/null +++ b/src/components/me/MeMemosContainer.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { useInfinityScroll } from '@/hooks/useInfinityScroll'; +import MemosGrid from '../memo/MemosGrid'; +import { Memo } from '@/types/memo'; +import { getLikedMemosByUserId, getMemosByUserId } from '@/service/me'; + +type Props = { + memos: Memo[]; + userId: number; + bigCategory: 'post' | 'like'; +}; + +export default function MeMemosContainer({ + memos, + userId, + bigCategory, +}: Props) { + const [data, isEnd, setTarget] = useInfinityScroll(memos, (pageNum) => + bigCategory === 'post' + ? getMemosByUserId(userId, pageNum) + : getLikedMemosByUserId(userId, pageNum) + ); + + return ( + <> + + {isEnd ? <> :
} + + ); +} diff --git a/src/components/me/MeNavigator.tsx b/src/components/me/MeNavigator.tsx new file mode 100644 index 00000000..0603f190 --- /dev/null +++ b/src/components/me/MeNavigator.tsx @@ -0,0 +1,28 @@ +import BarLink from '../shared/BarLink'; + +type Props = { + userId: number; + type: 'post' | 'like' | 'question_answered'; +}; + +export default function MeNavigator({ userId, type }: Props) { + return ( +
+ + + +
+ ); +} diff --git a/src/components/me/MePostCategories.tsx b/src/components/me/MePostCategories.tsx new file mode 100644 index 00000000..58df4952 --- /dev/null +++ b/src/components/me/MePostCategories.tsx @@ -0,0 +1,33 @@ +import CategoryLink from '../shared/CategoryLink'; + +type Props = { + userId: number; + selected: 'memo' | 'question' | 'series'; + bigCategory: 'post' | 'like'; +}; + +export default function MePostCategories({ + userId, + selected, + bigCategory, +}: Props) { + return ( +
+ + + +
+ ); +} diff --git a/src/components/me/MeQuestionsContainer.tsx b/src/components/me/MeQuestionsContainer.tsx new file mode 100644 index 00000000..95ca1ae7 --- /dev/null +++ b/src/components/me/MeQuestionsContainer.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { useInfinityScroll } from '@/hooks/useInfinityScroll'; +import { + getAnswerdQuestionsByUserId, + getLikedQuestionsByUserId, + getQuestionsByUserId, +} from '@/service/me'; +import { Question } from '@/types/question'; +import QuestionsList from '@/components/question/QuestionsList'; + +type Props = { + questions: Question[]; + userId: number; + bigCategory: 'post' | 'like' | 'answered'; +}; + +export default function MeQuestionsContainer({ + questions, + userId, + bigCategory, +}: Props) { + const [data, isEnd, setTarget] = useInfinityScroll(questions, (pageNum) => { + switch (bigCategory) { + case 'post': + return getQuestionsByUserId(userId, pageNum); + case 'like': + return getLikedQuestionsByUserId(userId, pageNum); + case 'answered': + return getAnswerdQuestionsByUserId(userId, pageNum); + } + }); + + return ( + <> + + {isEnd ? <> :
} + + ); +} diff --git a/src/components/me/MeSeriesContainer.tsx b/src/components/me/MeSeriesContainer.tsx new file mode 100644 index 00000000..e45a6b55 --- /dev/null +++ b/src/components/me/MeSeriesContainer.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { useInfinityScroll } from '@/hooks/useInfinityScroll'; +import { getLikedSeriesByUserId, getSeriesByUserId } from '@/service/me'; +import { Series } from '@/types/series'; +import SeriesGrid from '@/components/series/SeriesGrid'; + +type Props = { + seriesArr: Series[]; + userId: number; + bigCategory: 'post' | 'like'; +}; + +export default function MeSeriesContainer({ + seriesArr, + userId, + bigCategory, +}: Props) { + const [data, isEnd, setTarget] = useInfinityScroll(seriesArr, (pageNum) => + bigCategory === 'post' + ? getSeriesByUserId(userId, pageNum) + : getLikedSeriesByUserId(userId, pageNum) + ); + + return ( + <> + + {isEnd ? <> :
} + + ); +} diff --git a/src/components/memo/MemosContainer.tsx b/src/components/memo/MemosContainer.tsx index 42bf693a..583f25a5 100644 --- a/src/components/memo/MemosContainer.tsx +++ b/src/components/memo/MemosContainer.tsx @@ -3,11 +3,9 @@ import { Memo } from '@/types/memo'; import { Orderby } from '@/types'; import MemosGrid from './MemosGrid'; -import { useEffect, useRef, useState } from 'react'; import { getMemos } from '@/service/memos'; -import useObserver from '@/hooks/useObserver'; -import { MEMO_NUMBER_PER_PAGE_FOR_INFINITY_SCROLL } from '@/utils/const'; import CategoryLink from '../shared/CategoryLink'; +import { useInfinityScroll } from '@/hooks/useInfinityScroll'; type Props = { memos: Memo[]; @@ -15,42 +13,9 @@ type Props = { }; export default function MemosContainer({ memos, type }: Props) { - const [memoData, setMemodata] = useState(memos); - const [pageNum, setPageNum] = useState(1); - const [isEnd, setIsEnd] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const isInitialMount = useRef(true); - - const fetchMemos = () => { - if (isLoading || isEnd) return; - setIsLoading(true); - getMemos('month', type, pageNum, MEMO_NUMBER_PER_PAGE_FOR_INFINITY_SCROLL) - .then((data) => { - setIsLoading(false); - if (!data.length) setIsEnd(true); - else { - setMemodata([...memoData, ...data]); - } - }) - .catch(() => { - setIsLoading(false); - }); - }; - - useEffect(() => { - if (isInitialMount.current) { - isInitialMount.current = false; - } else { - fetchMemos(); - } - }, [pageNum]); - - const onIntersect: IntersectionObserverCallback = ([entry]) => { - if (isEnd || isLoading) return; - entry.isIntersecting && setPageNum(pageNum + 1); - }; - - const { setTarget } = useObserver({ onIntersect }); + const [data, isEnd, setTarget] = useInfinityScroll(memos, (pageNum) => + getMemos('month', type, pageNum) + ); return (
@@ -68,7 +33,7 @@ export default function MemosContainer({ memos, type }: Props) { isSelected={type === 'hot'} />
- + {isEnd ? <> :
}
); diff --git a/src/components/shared/BarLink.tsx b/src/components/shared/BarLink.tsx new file mode 100644 index 00000000..9402d896 --- /dev/null +++ b/src/components/shared/BarLink.tsx @@ -0,0 +1,22 @@ +import Link from 'next/link'; + +type Props = { + text: string; + isSelected: boolean; + href: string; +}; + +export default function BarLink({ text, isSelected, href }: Props) { + return ( + + {text} + + ); +} diff --git a/src/components/shared/Header.tsx b/src/components/shared/Header.tsx index 599a17af..c9acc819 100644 --- a/src/components/shared/Header.tsx +++ b/src/components/shared/Header.tsx @@ -291,7 +291,7 @@ export default function Header({ className="p-2 px-3 tracking-wider hover:bg-gray-200" onClick={() => { checkUser().then((data) => - router.push(`/me/${data.id}/drafts`) + router.push(`/drafts/${data.id}`) ); setIsActive(false); }} diff --git a/src/hooks/useInfinityScroll.ts b/src/hooks/useInfinityScroll.ts new file mode 100644 index 00000000..1f57918a --- /dev/null +++ b/src/hooks/useInfinityScroll.ts @@ -0,0 +1,51 @@ +import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'; +import useObserver from '@/hooks/useObserver'; + +export const useInfinityScroll = ( + initialData: T[], + fn: (c: number) => Promise +): [ + data: T[], + isEnd: boolean, + setTarget: Dispatch> +] => { + const [data, setData] = useState(initialData); + const [pageNum, setPageNum] = useState(1); + const [isEnd, setIsEnd] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const isInitialMount = useRef(true); + + const fetchData = () => { + if (isLoading || isEnd) return; + setIsLoading(true); + + fn(pageNum) + .then((item) => { + setIsLoading(false); + if (!item.length) setIsEnd(true); + else { + setData([...data, ...item]); + } + }) + .catch(() => { + setIsLoading(false); + }); + }; + + useEffect(() => { + if (isInitialMount.current) { + isInitialMount.current = false; + } else { + fetchData(); + } + }, [pageNum]); + + const onIntersect: IntersectionObserverCallback = ([entry]) => { + if (isEnd || isLoading) return; + entry.isIntersecting && setPageNum(pageNum + 1); + }; + + const { setTarget } = useObserver({ onIntersect }); + + return [data, isEnd, setTarget]; +};