Skip to content

Commit f2c8389

Browse files
authored
v0.0.5 - 출처, 카테고리 생성, 로그인 확인 기능 (#10)
* chore: v0.0.5 업데이트 * feat(background): 우클릭 저장 시, 해당 탭 URL 저장 * feat(popup): 템플릿 업로드 시, 출처를 description에 추가하는 기능 * feat(popup): 출처 첨부 체크박스 추가 * feat(popup): 카테고리 선택 없을 시, "카테고리 없음"으로 업로드 * feat(popup): 카테고리 생성 기능 * feat(popup): '/login/check' 요청으로 쿠키 없을 시, 로그인 해제 * refactor(LogoutButton): 로그아웃 버튼 스타일 변경
1 parent a5066bb commit f2c8389

File tree

9 files changed

+215
-43
lines changed

9 files changed

+215
-43
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codezap-chrome-extension",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"description": "코드잽 크롬 익스텐션입니다",
55
"scripts": {
66
"start": "webpack --watch --progress --config webpack.dev.js",

src/background/background.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { getStoredSourceCodes, setStoredSourceCodes } from '../utils/storage';
1+
import {
2+
getStoredDescription,
3+
getStoredSourceCodes,
4+
setStoredDescription,
5+
setStoredSourceCodes,
6+
} from '../utils/storage';
27

38
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
49
if (message.action === 'pageLoaded') {
@@ -36,11 +41,16 @@ chrome.contextMenus.onClicked.addListener((info, tab) => {
3641
})
3742
.then((selectionArray) => {
3843
const selectedText = selectionArray[0]?.result || '';
44+
const tabUrl = tab?.url;
3945

4046
if (selectedText) {
41-
getStoredSourceCodes().then((codes) => {
42-
setStoredSourceCodes([...codes, selectedText]).then(() => {
43-
chrome.action.openPopup();
47+
getStoredDescription().then((descriptions) => {
48+
setStoredDescription([...descriptions, tabUrl]).then(() => {
49+
getStoredSourceCodes().then((codes) => {
50+
setStoredSourceCodes([...codes, selectedText]).then(() => {
51+
chrome.action.openPopup();
52+
});
53+
});
4454
});
4555
});
4656
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
.logoutButton {
2-
background-color: #ff9500;
3-
color: white;
4-
border: none;
2+
background-color: white;
3+
color: #393e46;
4+
border: 1px solid #393e46;
5+
font-weight: 600;
6+
57
cursor: pointer;
68

7-
padding: 8px;
9+
padding: 5px 7px;
810
border-radius: 4px;
911
width: auto;
1012

1113
&:hover {
12-
background-color: #f6a739;
14+
background-color: #e5ebf5;
1315
}
1416
}

src/popup/popup.module.css

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
}
2222

2323
.categorySelect {
24+
height: 26px;
2425
border: 1px solid black;
2526
padding: 4px;
26-
border-radius: 4px;
27+
border-radius: 4px 0px 0px 4px;
2728
}
2829

2930
.titleInput {
@@ -128,3 +129,43 @@
128129
justify-content: space-between;
129130
align-items: center;
130131
}
132+
133+
.checkboxContainer {
134+
display: flex;
135+
align-items: center;
136+
}
137+
138+
.checkboxLabel {
139+
font-size: 12px;
140+
color: #333;
141+
}
142+
143+
.checkboxInput {
144+
width: 16px;
145+
height: 16px;
146+
margin-right: 5px;
147+
vertical-align: middle;
148+
}
149+
150+
.checkboxText {
151+
vertical-align: middle;
152+
}
153+
154+
.categoryContainer {
155+
display: flex;
156+
justify-content: center;
157+
align-items: center;
158+
}
159+
160+
.addCategoryButton {
161+
height: 26px;
162+
font-size: 16px;
163+
padding: 6px 8px;
164+
165+
border-top: 1px solid black;
166+
border-right: 1px solid black;
167+
border-bottom: 1px solid black;
168+
border-radius: 0px 4px 4px 0px;
169+
170+
background-color: white;
171+
}

src/popup/popup.tsx

Lines changed: 114 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
fetchCategories,
88
Category,
99
UserInfo,
10+
getLoginState,
1011
} from '../utils/api';
1112
import {
1213
getStoredUserInfo,
@@ -19,12 +20,14 @@ import {
1920
setStoredTitle,
2021
setStoredCategory,
2122
setStoredFileNames,
23+
getStoredDescription,
2224
} from '../utils/storage';
2325
import config from '../../config';
2426

2527
import styles from './popup.module.css';
2628
import '../styles/reset.css';
2729
import VisibilityToggle from '../components/VisibilityToggle/VisibilityToggle';
30+
import { urlToDescription } from '../utils/urlToDescription';
2831

2932
const 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
)}

src/static/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "코드잽 익스텐션",
4-
"version": "0.0.4",
4+
"version": "0.0.5",
55
"description": "코드잽 템플릿 업로드를 도와주는 익스텐션입니다.",
66
"permissions": ["contextMenus", "storage", "scripting", "activeTab"],
77
"host_permissions": ["https://code-zap.com/*", "https://www.code-zap.com/*"],

0 commit comments

Comments
 (0)