11import { isAxiosError } from 'axios' ;
22import { motion } from 'motion/react' ;
3- import { useRef , useState } from 'react' ;
3+ import { useMemo , useRef , useState } from 'react' ;
44import { GrValidate } from 'react-icons/gr' ;
55import { IoClose , IoShareSocialOutline } from 'react-icons/io5' ;
66
@@ -15,12 +15,18 @@ import QuestionSection from '@/components/qna/QuestionSection';
1515import SessionSettingsDropdown from '@/components/qna/SessionSettingsDropdown' ;
1616
1717function QuestionList ( ) {
18- const { isHost, expired, questions, sessionId, sessionTitle, sessionToken, setExpired, setSelectedQuestionId } =
19- useSessionStore ( ) ;
18+ const isHost = useSessionStore ( ( state ) => state . isHost ) ;
19+ const expired = useSessionStore ( ( state ) => state . expired ) ;
20+ const questions = useSessionStore ( ( state ) => state . questions ) ;
21+ const sessionId = useSessionStore ( ( state ) => state . sessionId ) ;
22+ const sessionTitle = useSessionStore ( ( state ) => state . sessionTitle ) ;
23+ const sessionToken = useSessionStore ( ( state ) => state . sessionToken ) ;
24+ const setExpired = useSessionStore ( ( state ) => state . setExpired ) ;
25+ const setSelectedQuestionId = useSessionStore ( ( state ) => state . setSelectedQuestionId ) ;
2026
21- const socket = useSocket ( ) ;
27+ const addToast = useToastStore ( ( state ) => state . addToast ) ;
2228
23- const { addToast } = useToastStore ( ) ;
29+ const socket = useSocket ( ) ;
2430
2531 const { Modal : CreateQuestion , openModal : openCreateQuestionModal } = useModal ( < CreateQuestionModal /> ) ;
2632
@@ -62,83 +68,89 @@ function QuestionList() {
6268
6369 const buttonRef = useRef < HTMLButtonElement > ( null ) ;
6470
65- const sections = [
66- {
67- title : '고정된 질문' ,
68- initialOpen : true ,
69- questions : questions
70- . filter ( ( question ) => question . pinned && ! question . closed )
71- . sort ( ( a , b ) => b . likesCount - a . likesCount ) ,
72- } ,
73- {
74- title : '질문' ,
75- initialOpen : true ,
76- questions : questions
77- . filter ( ( question ) => ! question . pinned && ! question . closed )
78- . sort ( ( a , b ) => b . likesCount - a . likesCount ) ,
79- } ,
80- {
81- title : '답변 완료된 질문' ,
82- initialOpen : false ,
83- questions : questions
84- . filter ( ( question ) => question . closed )
85- . sort ( ( a , b ) => {
86- if ( a . pinned && ! b . pinned ) return - 1 ;
87- if ( ! a . pinned && b . pinned ) return 1 ;
88- return b . likesCount - a . likesCount ;
89- } ) ,
90- } ,
91- ] ;
92-
93- const sessionButtons = [
94- {
95- key : '공유' ,
96- button : (
97- < div className = 'flex w-full cursor-pointer flex-row items-center gap-2' >
98- < IoShareSocialOutline />
99- < p > 공유</ p >
100- </ div >
101- ) ,
102- onClick : async ( ) => {
103- const shareUrl = `${ window . location . origin } /session/${ sessionId } ` ;
104-
105- try {
106- await navigator . clipboard . writeText ( shareUrl ) ;
107- addToast ( {
108- type : 'SUCCESS' ,
109- message : '세션 링크가 클립보드에 복사되었습니다' ,
110- duration : 3000 ,
111- } ) ;
112- } catch ( err ) {
113- addToast ( {
114- type : 'ERROR' ,
115- message : '링크 복사에 실패했습니다' ,
116- duration : 3000 ,
117- } ) ;
118- }
71+ const sections = useMemo (
72+ ( ) => [
73+ {
74+ title : '고정된 질문' ,
75+ initialOpen : true ,
76+ questions : questions
77+ . filter ( ( question ) => question . pinned && ! question . closed )
78+ . sort ( ( a , b ) => b . likesCount - a . likesCount ) ,
11979 } ,
120- } ,
121- {
122- key : '호스트 설정' ,
123- button : (
124- < div className = 'flex w-full cursor-pointer flex-row items-center gap-2' >
125- < GrValidate />
126- < p > 호스트 설정</ p >
127- </ div >
128- ) ,
129- onClick : ( ) => openSessionParticipantsModal ( ) ,
130- } ,
131- {
132- key : '세션 종료' ,
133- button : (
134- < div className = 'flex w-full cursor-pointer flex-row items-center gap-2 text-red-600' >
135- < IoClose />
136- < p > 세션 종료</ p >
137- </ div >
138- ) ,
139- onClick : ( ) => openSessionTerminateModal ( ) ,
140- } ,
141- ] ;
80+ {
81+ title : '질문' ,
82+ initialOpen : true ,
83+ questions : questions
84+ . filter ( ( question ) => ! question . pinned && ! question . closed )
85+ . sort ( ( a , b ) => b . likesCount - a . likesCount ) ,
86+ } ,
87+ {
88+ title : '답변 완료된 질문' ,
89+ initialOpen : false ,
90+ questions : questions
91+ . filter ( ( question ) => question . closed )
92+ . sort ( ( a , b ) => {
93+ if ( a . pinned && ! b . pinned ) return - 1 ;
94+ if ( ! a . pinned && b . pinned ) return 1 ;
95+ return b . likesCount - a . likesCount ;
96+ } ) ,
97+ } ,
98+ ] ,
99+ [ questions ] ,
100+ ) ;
101+
102+ const sessionButtons = useMemo (
103+ ( ) => [
104+ {
105+ key : '공유' ,
106+ button : (
107+ < div className = 'flex w-full cursor-pointer flex-row items-center gap-2' >
108+ < IoShareSocialOutline />
109+ < p > 공유</ p >
110+ </ div >
111+ ) ,
112+ onClick : async ( ) => {
113+ const shareUrl = `${ window . location . origin } /session/${ sessionId } ` ;
114+
115+ try {
116+ await navigator . clipboard . writeText ( shareUrl ) ;
117+ addToast ( {
118+ type : 'SUCCESS' ,
119+ message : '세션 링크가 클립보드에 복사되었습니다' ,
120+ duration : 3000 ,
121+ } ) ;
122+ } catch ( err ) {
123+ addToast ( {
124+ type : 'ERROR' ,
125+ message : '링크 복사에 실패했습니다' ,
126+ duration : 3000 ,
127+ } ) ;
128+ }
129+ } ,
130+ } ,
131+ {
132+ key : '호스트 설정' ,
133+ button : (
134+ < div className = 'flex w-full cursor-pointer flex-row items-center gap-2' >
135+ < GrValidate />
136+ < p > 호스트 설정</ p >
137+ </ div >
138+ ) ,
139+ onClick : ( ) => openSessionParticipantsModal ( ) ,
140+ } ,
141+ {
142+ key : '세션 종료' ,
143+ button : (
144+ < div className = 'flex w-full cursor-pointer flex-row items-center gap-2 text-red-600' >
145+ < IoClose />
146+ < p > 세션 종료</ p >
147+ </ div >
148+ ) ,
149+ onClick : ( ) => openSessionTerminateModal ( ) ,
150+ } ,
151+ ] ,
152+ [ sessionId , addToast , openSessionParticipantsModal , openSessionTerminateModal ] ,
153+ ) ;
142154
143155 return (
144156 < >
0 commit comments