11import Image from "next/image" ;
2- import {
3- ChangeEvent ,
4- Dispatch ,
5- MouseEvent ,
6- SetStateAction ,
7- useEffect ,
8- useRef ,
9- useState ,
10- } from "react" ;
2+ import { Dispatch , SetStateAction } from "react" ;
113
124import { useSelectedHint } from "@/components/atoms/selectedHint.atom" ;
13- import { useToastWrite } from "@/components/atoms/toast.atom" ;
14- import { subscribeLinkURL } from "@/admin-new/(consts)/sidebar" ;
15- import { useCreateHint } from "@/components/atoms/createHint.atom" ;
16- import { getStatus } from "@/utils/localStorage" ;
175
18- import { compressImage , convertToPng } from "./helpers/imageHelpers" ;
196import { GalleryImageProps } from "./consts/themeDrawerProps" ;
7+ import useImages from "./hooks/useImages" ;
208const ThemeDrawerAnswer = ( {
21- answerImages,
22- setAnswerImages,
9+ imageType,
10+ images,
11+ setImages,
2312} : {
24- answerImages : File [ ] ;
25- setAnswerImages : Dispatch < SetStateAction < File [ ] > > ;
13+ imageType : string ;
14+ images : File [ ] ;
15+ setImages : Dispatch < SetStateAction < File [ ] > > ;
2616} ) => {
27- const status = getStatus ( ) ;
28-
29- const [ selectedHint , setSelectedHint ] = useSelectedHint ( ) ;
30- const [ , setCreateHint ] = useCreateHint ( ) ;
31- const [ imgCnt , setImgCnt ] = useState < number > ( 3 ) ;
32- const setToast = useToastWrite ( ) ;
33- const answerRef = useRef < string > ( "" ) ;
34-
35- // 가지고 있던 이미지 갯수를 빼줘서 남은 imgCnt 계산
36- useEffect ( ( ) => {
37- if ( selectedHint . answerImageUrlList ) {
38- setImgCnt (
39- 3 - answerImages . length - selectedHint . answerImageUrlList . length
40- ) ;
41- return ;
42- }
43- setImgCnt ( 3 - answerImages . length ) ;
44- } , [ answerImages , selectedHint . answerImageUrlList ] ) ;
45-
46- // 이미지 파일 핸들러
47- const handleHintFileClick = ( e : MouseEvent < HTMLInputElement > ) => {
48- if ( imgCnt === 0 ) {
49- e . preventDefault ( ) ;
50- setToast ( {
51- isOpen : true ,
52- title : "이미지는 3개까지 추가할 수 있습니다." ,
53- text : "" ,
54- } ) ;
55- return ;
56- }
57- } ;
58- const handleAnswerFileChange = async ( e : ChangeEvent < HTMLInputElement > ) => {
59- if ( ! e . target . files ) {
60- return ;
61- }
62- const files : File [ ] = [ ] ;
63- const file = e . target . files [ 0 ] ;
64- if ( file . size > 5 * 1024 * 1024 ) {
65- try {
66- const compressedFile = await compressImage ( file ) ;
67-
68- if ( compressedFile . type !== "image/png" ) {
69- const pngFile = await convertToPng ( compressedFile ) ;
70- files . push ( pngFile ) ;
71- } else {
72- files . push ( compressedFile ) ;
73- }
74- } catch ( error ) {
75- console . error ( "Image compression failed" , error ) ;
76- files . push ( file ) ;
77- }
78- } else {
79- files . push ( file ) ;
80- }
81- setAnswerImages ( ( prev ) => [ ...prev , ...files ] ) ;
82- } ;
83-
84- const answerInputRef = useRef < HTMLInputElement > ( null ) ;
85-
86- const handleAnswerClick = ( e : MouseEvent < HTMLButtonElement > ) => {
87- if ( ! status ?. includes ( "SUBSCRIPTION" ) ) {
88- e . preventDefault ( ) ;
89- window . open ( subscribeLinkURL , "_blank" , "noopener,noreferrer" ) ;
90- return ;
91- }
92- answerInputRef . current ?. click ( ) ; // 숨겨진 input 클릭 트리거
93- } ;
94-
95- const handleAnswerChange = ( e : ChangeEvent < HTMLTextAreaElement > ) => {
96- answerRef . current = e . target . value ;
97- setCreateHint ( ( prev ) => ( { ...prev , answer : answerRef . current } ) ) ;
98- } ;
99-
100- // 힌트 추가 중 삭제 (로컬 이미지)
101- const deleteLocalAnswerImg = ( index : number ) => {
102- const newImages = [
103- ...answerImages . slice ( 0 , index ) ,
104- ...answerImages . slice ( index + 1 ) ,
105- ] ;
106- setAnswerImages ( newImages ) ;
107- if ( answerInputRef . current ) {
108- answerInputRef . current . value = "" ;
109- }
110- } ;
111- // 수정 시 삭제
112- const deleteServerAnswerImg = ( index : number ) => {
113- const answerServerImages = selectedHint . answerImageUrlList ;
114- const newImages = [
115- ...answerServerImages . slice ( 0 , index ) ,
116- ...answerServerImages . slice ( index + 1 ) ,
117- ] ;
118- setSelectedHint ( ( prev ) => ( { ...prev , answerImageUrlList : newImages } ) ) ;
119- if ( answerInputRef . current ) {
120- answerInputRef . current . value = "" ;
121- }
122- } ;
17+ const [ selectedHint ] = useSelectedHint ( ) ;
18+ const {
19+ handleFileInputClick,
20+ handleFileInputChange,
21+ handleAddImageBtnClick,
22+ handleTextAreaChange,
23+ deleteLocalImage,
24+ deleteServerImage,
25+ answerInputRef,
26+ } = useImages ( { imageType, images, setImages } ) ;
12327
12428 return (
12529 < div className = "drawer-answer" >
@@ -128,67 +32,64 @@ const ThemeDrawerAnswer = ({
12832 < button
12933 className = "secondary_button28"
13034 type = "button"
131- onClick = { handleAnswerClick }
35+ onClick = { handleAddImageBtnClick }
13236 >
13337 < Image { ...GalleryImageProps } />
13438 < input
13539 type = "file"
13640 multiple
137- onClick = { handleHintFileClick }
138- onChange = { handleAnswerFileChange }
41+ onClick = { handleFileInputClick }
42+ onChange = { handleFileInputChange }
13943 accept = "image/*"
14044 style = { { display : "none" } }
14145 ref = { answerInputRef }
14246 />
14347 이미지 추가
144- { ( answerImages . length > 0 ||
48+ { ( images . length > 0 ||
14549 selectedHint ?. answerImageUrlList ?. length > 0 ) &&
14650 `(${
147- ( answerImages . length || 0 ) +
51+ ( images . length || 0 ) +
14852 ( selectedHint . answerImageUrlList ?. length || 0 )
14953 } /3)`}
15054 </ button >
15155 </ div >
152- { selectedHint ?. answerImageUrlList ?. map ( ( src , idx ) => (
153- < div className = "drawer-images" key = { src } >
154- < div className = "drawer-image-box" >
56+ < div className = "drawer-images" >
57+ { selectedHint ?. answerImageUrlList ?. map ( ( src , idx ) => (
58+ < div className = "drawer-image-box" key = { src } >
15559 < img src = { src } alt = { `answer-preview-${ src } ` } />
15660 < div
15761 className = "drawer-image-dimmed"
158- onClick = { ( ) => deleteServerAnswerImg ( idx ) }
62+ onClick = { ( ) => deleteServerImage ( idx ) }
15963 >
16064 < button className = "button28" type = "button" >
16165 삭제하기
16266 </ button >
16367 </ div >
16468 </ div >
165- </ div >
166- ) ) }
167- { answerImages . length > 0 && (
168- < div className = "drawer-images" >
169- { answerImages . map ( ( file , index ) => (
69+ ) ) }
70+ { images . length > 0 &&
71+ images . map ( ( file , index ) => (
17072 < div key = { file . name } className = "drawer-image-box" >
17173 < img
17274 src = { URL . createObjectURL ( file ) }
17375 alt = { `answer-preview-${ index } ` }
17476 />
17577 < div
17678 className = "drawer-image-dimmed"
177- onClick = { ( ) => deleteLocalAnswerImg ( index ) }
79+ onClick = { ( ) => deleteLocalImage ( index ) }
17880 >
17981 < button className = "button28" type = "button" >
18082 삭제하기
18183 </ button >
18284 </ div >
18385 </ div >
18486 ) ) }
185- </ div >
186- ) }
87+ </ div >
18788
18889 < textarea
18990 className = "drawer-content-textarea"
19091 placeholder = "정답 내용을 입력해 주세요."
191- onChange = { handleAnswerChange }
92+ onChange = { handleTextAreaChange }
19293 defaultValue = { selectedHint . answer }
19394 />
19495 </ div >
0 commit comments