1- import WinnerCard from '@/components/WinnerCard/WinnerCard' ;
2- import {
3- getVoteResultByPollId ,
4- getVoteResultByPollIdResponse ,
5- } from './../apis/vote/getVoteResultByPollId' ;
61import { useEffect , useState } from 'react' ;
7- import { getLatestPollIds } from '@/apis/poll/getPollLatest' ;
8- import { CommentResponse } from '@/apis/comment/getComment' ;
9- import { getComments } from '@/apis/comment/getAllComments' ;
2+ import { useNavigate } from 'react-router-dom' ;
3+ import { useSelector } from 'react-redux' ;
4+ import { RootState } from '@/store' ;
5+ import { useComment } from '@/hook/useComment' ;
6+ import { useMediaQuery } from 'react-responsive' ;
7+ import WinnerCard from '@/components/WinnerCard/WinnerCard' ;
108import CommentCard from '@/components/Comment/Comment' ;
119import CharacterCard from '@/components/Character/Character' ;
12- import { useMediaQuery } from 'react-responsive' ;
1310import CommentInputField from '@/components/Comment/CommentInputField' ;
1411import Loading from '@/components/Loading/Loading' ;
15- import { postComment } from '@/apis/comment/postComment' ;
16- import { useRef } from 'react' ;
17- import { useSelector } from 'react-redux' ;
18- import { RootState } from '@/store' ;
1912import { IMAGES } from '@/constants/imagePath' ;
2013
2114const Comment = ( ) => {
22- // 사용자 정보
2315 const user = useSelector ( ( state : RootState ) => state . auth . user ) ! ;
24-
2516 const isMobile = useMediaQuery ( { maxWidth : 768 } ) ;
17+ const navigate = useNavigate ( ) ;
2618
27- // 결과 조회
28- const [ results , setResults ] = useState < getVoteResultByPollIdResponse [ ] > ( [ ] ) ;
29- const [ pollIds , setPollIds ] = useState < number [ ] > ( [ ] ) ;
30-
31- // 댓글 조회
32- const [ comments , setComments ] = useState < CommentResponse [ ] > ( [ ] ) ;
33-
34- // 댓글 입력창 상태 추가
35- const [ commentText , setCommentText ] = useState ( '' ) ;
3619 const [ isLoading , setIsLoading ] = useState ( true ) ;
20+ const [ error , setError ] = useState < unknown > ( null ) ;
21+
22+ const {
23+ results,
24+ resultsLoading,
25+ comments,
26+ commentsLoading,
27+ commentText,
28+ setCommentText,
29+ commentsEndRef,
30+ submitComment,
31+ refreshComments,
32+ } = useComment ( { onError : setError } ) ;
3733
38- // 댓글 맨 아래로 내리기
39- const commentsEndRef = useRef < HTMLDivElement > ( null ) ;
40-
41- // 최신 pollIds 불러오기
4234 useEffect ( ( ) => {
43- const fetchAllResults = async ( ) => {
44- try {
45- const res = await getLatestPollIds ( ) ;
46- console . log ( '최신 투표 id' , res ) ;
47- setPollIds ( res ) ;
48- } catch ( error ) {
49- console . error ( '투표 결과 불러오기 실패 : ' , error ) ;
50- }
51- } ;
52- fetchAllResults ( ) ;
53- } , [ ] ) ;
35+ setIsLoading ( resultsLoading || commentsLoading ) ;
36+ } , [ resultsLoading , commentsLoading ] ) ;
5437
55- // pollIds가 갱신되면 결과 가져오기
5638 useEffect ( ( ) => {
57- const fetchResultsByPollIds = async ( ) => {
58- try {
59- if ( pollIds . length === 0 ) return ;
60- setIsLoading ( true ) ; // 로딩 시작
61- const res = await Promise . all ( pollIds . map ( ( pollId ) => getVoteResultByPollId ( { pollId } ) ) ) ;
62- setResults ( res ) ;
63- } catch ( error ) {
64- console . log ( '투표 결과 불러오기 실패 : ' , error ) ;
65- } finally {
66- setIsLoading ( false ) ; // 로딩 끝
67- }
68- } ;
69-
70- fetchResultsByPollIds ( ) ;
71- } , [ pollIds ] ) ;
72-
73- // 댓글 불러오기
74- useEffect ( ( ) => {
75- const getAllComments = async ( ) => {
76- try {
77- const res = await getComments ( ) ;
78- setComments ( res ) ;
79- } catch ( error ) {
80- console . log ( '댓글 불러오기 실패 : ' , error ) ;
81- }
82- } ;
83- getAllComments ( ) ;
84- } , [ ] ) ;
85-
86- // 댓글 등록 후 스크롤 이동 호출
87- const scrollToBottom = ( ) => {
88- commentsEndRef . current ?. scrollIntoView ( { behavior : 'auto' } ) ;
89- } ;
39+ if ( error ) navigate ( '/main' ) ;
40+ } , [ error , navigate ] ) ;
9041
91- // 댓글 목록이 바뀔 때마다 스크롤을 아래로
9242 useEffect ( ( ) => {
9343 if ( comments . length > 0 ) {
9444 requestAnimationFrame ( ( ) => {
@@ -97,75 +47,31 @@ const Comment = () => {
9747 }
9848 } , [ comments ] ) ;
9949
100- // 리사이즈 이벤트 감지 후 스크롤 내리기
10150 useEffect ( ( ) => {
10251 const handleResize = ( ) => {
103- // 댓글이 존재하면 맨 아래로 스크롤
10452 if ( comments . length > 0 ) {
10553 commentsEndRef . current ?. scrollIntoView ( { behavior : 'auto' } ) ;
10654 }
10755 } ;
108-
10956 window . addEventListener ( 'resize' , handleResize ) ;
110- return ( ) => {
111- window . removeEventListener ( 'resize' , handleResize ) ;
112- } ;
57+ return ( ) => window . removeEventListener ( 'resize' , handleResize ) ;
11358 } , [ comments ] ) ;
11459
115- // 댓글 등록
116- const handleSubmitComment = async ( ) => {
117- if ( ! commentText . trim ( ) ) {
118- alert ( '내용을 입력해주세요' ) ;
119- return ;
120- }
121- try {
122- console . log ( '댓글 내용 : ' , commentText ) ;
123- await postComment ( {
124- user_id : user . userId ,
125- comment_text : commentText ,
126- } ) ;
127-
128- const updatedComments = await getComments ( ) ;
129- setComments ( updatedComments ) ;
130- setCommentText ( '' ) ;
131- scrollToBottom ( ) ;
132- } catch ( error ) {
133- console . log ( '댓글 등록 실패' , error ) ;
134- }
135- } ;
136-
137- // 댓글 목록 새로고침 함수
138- const refreshComments = async ( ) => {
139- try {
140- const res = await getComments ( ) ;
141- setComments ( res ) ;
142- } catch ( error ) {
143- console . error ( '댓글 갱신 실패' , error ) ;
144- }
145- } ;
60+ if ( isLoading ) return < Loading /> ;
14661
14762 return (
148- < div className = "md:h-full flex flex-col justify-center items-center" >
149- { /* 제목 */ }
150- < div className = "flex justify-center items-center gap-3 md:gap-7 md:pt-0 pt-5" >
151- < img src = { IMAGES . POPPER_LEFT } className = "w-[40px] md:w-[100px]" alt = "popper-left" />
152- < h1 className = "text-2xl font-pm md:text-3xl" > 투표 결과</ h1 >
153- < img src = { IMAGES . POPPER_RIGHT } className = "w-[40px] md:hidden" alt = "popper-right" />
154- </ div >
155-
156- < div className = "px-5 py-10 flex flex-col justify-center items-center md:flex-row md:p-3 gap-10 md:gap-30" >
157- { /* 카드 영역 */ }
158- < div className = "w-full md:w-auto flex justify-center relative" >
159- { /* 로딩 스피너 */ }
160- { isLoading && (
161- < div className = "md:w-[400px] absolute inset-0 flex bg-white/80 z-10" >
162- < Loading />
163- </ div >
164- ) }
63+ < div className = "md:pt-0 pt-130 relative w-full h-full min-h-screen flex justify-center items-center" >
64+ < div className = "md:h-full flex flex-col justify-center items-center z-10" >
65+ < div className = "flex justify-center items-center gap-3 md:gap-7 md:pt-0 pt-5" >
66+ < img src = { IMAGES . POPPER_LEFT } className = "w-[40px] md:w-[50px]" alt = "popper-left" />
67+ < h1 className = "text-2xl font-pm md:text-3xl" > 투표 결과</ h1 >
68+ < img src = { IMAGES . POPPER_RIGHT } className = "w-[40px] md:hidden" alt = "popper-right" />
69+ </ div >
16570
166- < div className = "grid grid-cols-2 grid-rows-2 gap-6 md:gap-x-10 md:gap-y-0" >
167- { ! isLoading &&
168- results . map ( ( poll , idx ) => (
71+ < div className = "px-5 py-10 flex flex-col justify-center items-center md:flex-row md:p-3 gap-10 md:gap-30" >
72+ < div className = "w-full md:w-auto flex justify-center relative" >
73+ < div className = "grid grid-cols-2 grid-rows-2 gap-6 md:gap-x-10 md:gap-y-0" >
74+ { results . map ( ( poll , idx ) => (
16975 < div
17076 key = { poll . pollId }
17177 className = { `w-full ${ ! isMobile && idx % 2 === 1 ? 'mt-7' : '' } ` }
@@ -178,42 +84,39 @@ const Comment = () => {
17884 />
17985 </ div >
18086 ) ) }
87+ </ div >
18188 </ div >
182- </ div >
183-
184- { /* 댓글 영역 */ }
185- < div className = " h-[550px] md:h-[550px] border border-gray-300 rounded-2xl flex flex-col justify-center items-center px-4 py-2" >
186- { /* 스크롤 가능한 댓글 리스트 */ }
187- < div className = "flex-1 overflow-y-auto pr-2" >
188- { comments . length === 0 ? (
189- < div className = "pl-10 w-full flex items-center justify-center h-full" >
190- < CharacterCard />
191- </ div >
192- ) : (
193- < div className = "flex flex-col items-center gap-2 w-full" >
194- { comments . map ( ( comment ) => (
195- < CommentCard
196- key = { comment . comment_id }
197- nickname = { comment . random_nickname }
198- commentText = { comment . comment_text }
199- commentId = { comment . comment_id }
200- onUpdate = { refreshComments }
201- />
202- ) ) }
20389
204- < div ref = { commentsEndRef } />
205- </ div >
206- ) }
207- </ div >
90+ < div className = "h-[550px] md:h-[550px] border border-gray-300 rounded-2xl flex flex-col justify-center items-center px-4 py-2" >
91+ < div className = "flex-1 overflow-y-auto pr-2 custom-scrollbar" >
92+ { isLoading ? null : comments . length === 0 ? (
93+ < div className = "pl-10 w-full flex items-center justify-center h-full" >
94+ < CharacterCard />
95+ </ div >
96+ ) : (
97+ < div className = "flex flex-col items-center gap-2 w-full" >
98+ { comments . map ( ( comment ) => (
99+ < CommentCard
100+ key = { comment . comment_id }
101+ nickname = { comment . random_nickname }
102+ commentText = { comment . comment_text }
103+ commentId = { comment . comment_id }
104+ onUpdate = { refreshComments }
105+ />
106+ ) ) }
107+ < div ref = { commentsEndRef } />
108+ </ div >
109+ ) }
110+ </ div >
208111
209- { /* 고정된 입력창 */ }
210- < div className = "mt-4" >
211- < CommentInputField
212- nickname = { user . randomNickname }
213- comment = { commentText }
214- onChangeComment = { ( e ) => setCommentText ( e . target . value ) }
215- onSubmit = { handleSubmitComment } // 이 부분 추가
216- / >
112+ < div className = "mt-4" >
113+ < CommentInputField
114+ nickname = { user . randomNickname }
115+ comment = { commentText }
116+ onChangeComment = { ( e ) => setCommentText ( e . target . value ) }
117+ onSubmit = { async ( ) => submitComment ( user . userId ) }
118+ />
119+ </ div >
217120 </ div >
218121 </ div >
219122 </ div >
0 commit comments