77 fetchCategories ,
88 Category ,
99 UserInfo ,
10+ getLoginState ,
1011} from '../utils/api' ;
1112import {
1213 getStoredUserInfo ,
@@ -19,12 +20,14 @@ import {
1920 setStoredTitle ,
2021 setStoredCategory ,
2122 setStoredFileNames ,
23+ getStoredDescription ,
2224} from '../utils/storage' ;
2325import config from '../../config' ;
2426
2527import styles from './popup.module.css' ;
2628import '../styles/reset.css' ;
2729import VisibilityToggle from '../components/VisibilityToggle/VisibilityToggle' ;
30+ import { urlToDescription } from '../utils/urlToDescription' ;
2831
2932const Popup = ( ) => {
3033 const [ userInfo , setUserInfo ] = useState < UserInfo > ( {
@@ -39,16 +42,32 @@ const Popup = () => {
3942 const [ selectedCategoryId , setSelectedCategoryId ] = useState <
4043 number | undefined
4144 > ( undefined ) ;
45+ const [ description , setDescription ] = useState < string [ ] > ( [ ] ) ;
4246 const [ username , setUsername ] = useState ( '' ) ;
4347 const [ password , setPassword ] = useState ( '' ) ;
4448
49+ const [ attachUrl , setAttachUrl ] = useState ( true ) ;
50+
51+ useEffect ( ( ) => {
52+ const checkLogin = async ( ) => {
53+ try {
54+ getLoginState ( ) ;
55+ } catch ( error ) {
56+ await handleLogout ( ) ;
57+ }
58+ } ;
59+
60+ checkLogin ( ) ;
61+ } , [ ] ) ;
62+
4563 useEffect ( ( ) => {
4664 const initializePopup = async ( ) => {
4765 const storedUserInfo = await getStoredUserInfo ( ) ;
4866 const storedSourceCodes = await getStoredSourceCodes ( ) ;
4967 const storedTitle = await getStoredTitle ( ) ;
5068 const storedCategoryId = await getStoredCategory ( ) ;
5169 const storedFileNames = await getStoredFileNames ( ) ;
70+ const storedDescription = await getStoredDescription ( ) ;
5271
5372 if ( storedUserInfo && storedUserInfo . memberId ) {
5473 setUserInfo ( storedUserInfo ) ;
@@ -66,6 +85,7 @@ const Popup = () => {
6685
6786 setTitle ( storedTitle ) ;
6887 setSelectedCategoryId ( storedCategoryId ) ;
88+ setDescription ( storedDescription ) ;
6989 } ;
7090
7191 initializePopup ( ) ;
@@ -119,7 +139,8 @@ const Popup = () => {
119139 memberId : undefined ,
120140 } ) ;
121141 setCategories ( [ ] ) ;
122- alert ( '로그아웃 성공!' ) ;
142+ resetTemplateData ( ) ;
143+ alert ( '로그아웃 되었어요' ) ;
123144 } ) ;
124145 } catch ( error ) {
125146 console . error ( '로그아웃 에러: ' , error ) ;
@@ -153,20 +174,31 @@ const Popup = () => {
153174 const handleRemoveSourceCode = ( index : number ) => {
154175 const newSourceCodes = [ ...sourceCodes ] ;
155176 const newFileNames = [ ...fileNames ] ;
177+ const newDescription = [ ...description ] ;
156178 newSourceCodes . splice ( index , 1 ) ;
157179 newFileNames . splice ( index , 1 ) ;
180+ newDescription . splice ( index , 1 ) ;
158181 setSourceCodes ( newSourceCodes ) ;
159182 setFileNames ( newFileNames ) ;
160183 setStoredSourceCodes ( newSourceCodes ) ;
161184 } ;
162185
186+ const resetTemplateData = ( ) => {
187+ setTitle ( '' ) ;
188+ setFileNames ( [ ] ) ;
189+ setSelectedCategoryId ( undefined ) ;
190+ setSourceCodes ( [ ] ) ;
191+ setStoredTitle ( '' ) ;
192+ setStoredCategory ( categories [ 0 ] . id ) ;
193+ setStoredFileNames ( [ ] ) ;
194+ setDescription ( [ ] ) ;
195+ chrome . storage . local . remove ( 'sourceCodes' ) ;
196+ chrome . storage . local . remove ( 'description' ) ;
197+ } ;
198+
163199 const handleUpload = async ( ) => {
164- if (
165- ! title ||
166- fileNames . some ( ( fileName ) => ! fileName ) ||
167- ! selectedCategoryId
168- ) {
169- alert ( '제목, 파일명 및 카테고리를 선택해주세요.' ) ;
200+ if ( ! title || fileNames . some ( ( fileName ) => ! fileName ) ) {
201+ alert ( '제목 및 파일명을 입력해주세요.' ) ;
170202 return ;
171203 }
172204
@@ -177,14 +209,14 @@ const Popup = () => {
177209
178210 const requestBody = {
179211 title,
180- description : '사용자가 생성한 코드 템플릿 ',
212+ description : attachUrl ? urlToDescription ( description ) : ' ',
181213 sourceCodes : sourceCodes . map ( ( code , index ) => ( {
182214 filename : fileNames [ index ] ,
183215 content : code ,
184216 ordinal : index + 1 ,
185217 } ) ) ,
186218 thumbnailOrdinal : 1 ,
187- categoryId : selectedCategoryId ,
219+ categoryId : selectedCategoryId ? selectedCategoryId : categories [ 0 ] . id ,
188220 tags : [ ] ,
189221 visibility : isPrivate ? 'PRIVATE' : 'PUBLIC' ,
190222 } ;
@@ -205,17 +237,12 @@ const Popup = () => {
205237 '소스코드가 성공적으로 업로드되었어요! 코드잽에서 확인해볼까요?'
206238 )
207239 ) {
208- chrome . tabs . create ( { url : 'https://www.code-zap.com/my-templates' } ) ;
240+ chrome . tabs . create ( {
241+ url : `https://www.code-zap.com/members/${ userInfo . memberId } /templates` ,
242+ } ) ;
209243 }
210244
211- setTitle ( '' ) ;
212- setFileNames ( [ ] ) ;
213- setSelectedCategoryId ( undefined ) ;
214- setSourceCodes ( [ ] ) ;
215- setStoredTitle ( '' ) ;
216- setStoredCategory ( categories [ 0 ] . id ) ;
217- setStoredFileNames ( [ ] ) ;
218- chrome . storage . local . remove ( 'sourceCodes' ) ;
245+ resetTemplateData ( ) ;
219246 } else {
220247 alert ( '소스코드 업로드에 실패했어요. 잠시 후 다시 시도해주세요.' ) ;
221248 }
@@ -225,6 +252,43 @@ const Popup = () => {
225252 }
226253 } ;
227254
255+ const handleCheckboxChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
256+ setAttachUrl ( e . target . checked ) ;
257+ } ;
258+
259+ const handleAddCategory = async ( ) => {
260+ let newCategoryName = prompt ( '새로운 카테고리명을 입력해주세요:' ) ;
261+ while ( newCategoryName ) {
262+ try {
263+ const response = await fetch ( `${ config . API_BASE_URL } /categories` , {
264+ method : 'POST' ,
265+ headers : {
266+ 'Content-Type' : 'application/json' ,
267+ } ,
268+ credentials : 'include' ,
269+ body : JSON . stringify ( { name : newCategoryName } ) ,
270+ } ) ;
271+
272+ if ( response . ok ) {
273+ alert ( '카테고리가 추가되었습니다!' ) ;
274+ const body = await response . json ( ) ;
275+
276+ await loadCategories ( userInfo . memberId ! ) ;
277+ setSelectedCategoryId ( body . id ) ;
278+ break ;
279+ } else if ( response . status === 409 ) {
280+ newCategoryName = prompt ( '중복된 카테고리입니다. 다시 입력해주세요:' ) ;
281+ } else {
282+ alert ( '카테고리 추가에 실패했습니다. 다시 시도해주세요.' ) ;
283+ break ;
284+ }
285+ } catch ( error ) {
286+ console . error ( '카테고리 추가 중 에러 발생:' , error ) ;
287+ break ;
288+ }
289+ }
290+ } ;
291+
228292 return (
229293 < >
230294 { userInfo . memberId !== undefined ? (
@@ -242,25 +306,44 @@ const Popup = () => {
242306 onChange = { handleTitleChange }
243307 />
244308 < div className = { styles . categoryVisibilityContainer } >
245- < select
246- className = { styles . categorySelect }
247- value = { selectedCategoryId || '' }
248- onChange = { handleCategoryChange }
249- >
250- < option value = '' disabled >
251- 카테고리를 선택해주세요
252- </ option >
253- { categories . map ( ( category ) => (
254- < option key = { category . id } value = { category . id } >
255- { category . name }
309+ < div className = { styles . categoryContainer } >
310+ < select
311+ className = { styles . categorySelect }
312+ value = { selectedCategoryId || '' }
313+ onChange = { handleCategoryChange }
314+ >
315+ < option value = '' disabled >
316+ 카테고리를 선택해주세요
256317 </ option >
257- ) ) }
258- </ select >
318+ { categories . map ( ( category ) => (
319+ < option key = { category . id } value = { category . id } >
320+ { category . name }
321+ </ option >
322+ ) ) }
323+ </ select >
324+ < button
325+ className = { styles . addCategoryButton }
326+ onClick = { handleAddCategory }
327+ >
328+ +
329+ </ button >
330+ </ div >
259331 < VisibilityToggle
260332 isPrivate = { isPrivate }
261333 toggleVisibility = { toggleVisibility }
262334 />
263335 </ div >
336+ < div className = { styles . checkboxContainer } >
337+ < label className = { styles . checkboxLabel } >
338+ < input
339+ type = 'checkbox'
340+ className = { styles . checkboxInput }
341+ checked = { attachUrl }
342+ onChange = { handleCheckboxChange }
343+ />
344+ < span className = { styles . checkboxText } > 출처 url 첨부</ span >
345+ </ label >
346+ </ div >
264347 { sourceCodes . length === 0 && (
265348 < div > 원하는 소스코드를 드래그 후 우클릭 하여 추가해보세요</ div >
266349 ) }
0 commit comments