diff --git a/src/pages/answer/AnswerContainer.js b/src/pages/answer/AnswerContainer.js index c8faf50..9644c18 100644 --- a/src/pages/answer/AnswerContainer.js +++ b/src/pages/answer/AnswerContainer.js @@ -2,12 +2,12 @@ import { useState, useEffect, useContext } from 'react'; import ReactPlayer from 'react-player'; -import { useNavigate } from 'react-router-dom'; - +import { useNavigate, useParams } from 'react-router-dom'; +import useIntersect from 'hooks/useIntersect'; import { getSubjectsOnQuestions, getSubject } from '../../api/api.subjects.js'; import { deleteQuestion, createAnswer } from '../../api/api.questions'; import { updateAnswersPartial } from '../../api/api.answers'; - +import FeedSkeleton from 'components/feed/FeedSkeleton'; import PopOverMenu from 'components/modal/PopOverMenu'; import * as S from '../post/PostStyle'; import * as Layout from 'components/answerFeedCard/FeedCardLayout'; @@ -21,41 +21,97 @@ import { pathState } from 'components/common/pathState.js'; import { PagePath } from 'context/PathContext.js'; import handleExtractVideoId from 'utils/ExtractYoutubeId.js'; +const DEFAULT_LIMIT = 0; +const DEFAULT_OFFSET = 0; export default function Answer() { + const [questionCount, setQuestionCount] = useState(0); const [questionList, setQuestionList] = useState([]); - const [answererProfile, setAnswererProfile] = useState({}); const [isOn, setIsOn] = useState(true); const [isMenuOpen, setMenuOpen] = useState(false); const [menuSelected, setMenuSelected] = useState(false); const [isCopied, setIsCopied] = useState(false); + + const [pageLimit, setPageLimit] = useState(DEFAULT_LIMIT); + const [pageOffset, setPageOffset] = useState(DEFAULT_OFFSET); + const [hasNext, setHasNext] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const LocalId = window.localStorage.getItem('id'); const YOUTUBE_BASE = 'https://www.youtube.com/watch?v='; + const target = useIntersect(handleIntersection, hasNext); + const isEmptyQuestions = questionCount === 0; const { setIsPath, setSelectUserId, userTitleData } = useContext(PagePath); const navigate = useNavigate(); - const handleRenderSubjectsOnQ = async (id) => { + async function handleIntersection(entry) { + if (!target.current || isLoading) return; + try { - const { results } = await getSubjectsOnQuestions(id); - setQuestionList(results); + setIsLoading(true); + const res = await getSubjectsOnQuestions(LocalId, pageLimit, pageOffset); + const { next, previous, results } = res; + + if (previous === null) { + return; // 초기 렌더링 콜백함수 호출될때 중복으로 데이터 불러오지 않기 + } + console.log(isLoading); // 삭제예정 + setQuestionList((prev) => [...prev, ...results]); + + if (next === null) { + setHasNext(false); + return; + } + + /** + * next가 null이 아닐때만 수행하기 + * next 값이 있을때는 next를 기준으로 offset이 결정되지만, + * 더이상 받을 데이터가 없을때는 (=next가 null일때) offset을 마지막 offset으로 유지(업데이트x) + * 다시 post 진입하면 처음 상태(offset = 0) + */ + + const nextSearchParams = new URLSearchParams(new URL(next).search); + setPageOffset(nextSearchParams.get('offset')); } catch (error) { console.log(error); + } finally { + setIsLoading(false); } - }; + } - /* const handleRenderSubjectProfile = async (id) => { + const handleLoaded = async (LocalId) => { try { - const result = await getSubject(id); - const { name, imageSource } = result; + const res = await getSubjectsOnQuestions(LocalId, pageLimit, pageOffset); + const { count, next, results } = res; + console.log(hasNext); + setQuestionCount(count); + setQuestionList(results); + + if (!next) return; - setAnswererProfile({ ...answererProfile, name, imageSource }); + // 처음 보여지는 page 외 추가 page가 있는 경우 실행 + const nextSearchParams = new URLSearchParams(new URL(next).search); + + setHasNext(true); + setPageLimit(nextSearchParams.get('limit')); + setPageOffset(nextSearchParams.get('offset')); } catch (error) { console.log(error); + } finally { + setIsLoading(false); } - }; */ - + }; + // const handleRenderSubjectsOnQ = async (id) => { + // try { + // const { results } = await getSubjectsOnQuestions(id); + // setQuestionList(results); + // } catch (error) { + // console.log(error); + // } + // }; + console.log(hasNext); const handleAllDeleteQuestionList = async (id) => { try { const { results } = await getSubjectsOnQuestions(id); @@ -67,6 +123,7 @@ export default function Answer() { }); setQuestionList([]); + setQuestionCount(0); } catch (error) { console.log(error); } @@ -147,8 +204,7 @@ export default function Answer() { useEffect(() => { setSelectUserId(LocalId); - handleRenderSubjectsOnQ(LocalId); - /* handleRenderSubjectProfile(LocalId); */ + handleLoaded(LocalId); if (pathState()) { setIsPath(true); } else { @@ -169,10 +225,10 @@ export default function Answer() { - {questionList ? `${questionList.length}개의 질문이 있습니다` : `아직 질문이 없습니다`} + {questionList ? `${questionCount}개의 질문이 있습니다` : `아직 질문이 없습니다`} - {!questionList ? ( + {!isLoading && isEmptyQuestions ? ( ) : ( <> @@ -181,74 +237,74 @@ export default function Answer() { const isSelected = question?.id == menuSelected; const isRejected = question?.answer?.isRejected === true; - - const key = handleExtractVideoId(question?.answer?.content); const youtubeURL = YOUTUBE_BASE + key; - return ( - - {isMenuOpen && isSelected && ( - + + {isMenuOpen && isSelected && ( + + )} + + - )} - - - - - - - - - {question?.answer ? ( - <> - - 답변 완료 - {!isRejected ? ( - - {question.answer.content} - - {question.answer.content.includes(YOUTUBE_BASE) && ( - - )} - - ) : ( - 답변 거절 - )} - - ) : ( - <> - 미답변 - - - )} - - - - - + + + + + + + {question?.answer ? ( + <> + + 답변 완료 + {!isRejected ? ( + + {question.answer.content} + + {question.answer.content.includes(YOUTUBE_BASE) && ( + + )} + + ) : ( + 답변 거절 + )} + + ) : ( + <> + 미답변 + + + )} + + + + + + {isLoading && } + ); })} @@ -257,6 +313,7 @@ export default function Answer() { {isCopied && } + ); } diff --git a/src/pages/post/PostStyle.js b/src/pages/post/PostStyle.js index df57574..fe8f9d9 100644 --- a/src/pages/post/PostStyle.js +++ b/src/pages/post/PostStyle.js @@ -118,5 +118,6 @@ export const CreateQuestionButton = styled.button` `; export const Target = styled.div` - height: 1px; + width: 100%; + height: 50px; `;