Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -172,6 +172,21 @@ if __name__ == '__main__':
|
|
| 172 |
margin-bottom: 1rem;
|
| 173 |
}
|
| 174 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
.user-controls {
|
| 176 |
display: flex;
|
| 177 |
justify-content: space-between;
|
|
@@ -237,19 +252,43 @@ if __name__ == '__main__':
|
|
| 237 |
}
|
| 238 |
|
| 239 |
.card {
|
| 240 |
-
border: 1px solid #
|
| 241 |
-
border-radius:
|
| 242 |
padding: 1rem;
|
| 243 |
width: 300px;
|
| 244 |
-
box-shadow:
|
| 245 |
position: relative;
|
| 246 |
-
background-color: #
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
}
|
| 248 |
|
| 249 |
.card a {
|
| 250 |
text-decoration: none;
|
| 251 |
-
color: #
|
| 252 |
word-break: break-all;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
}
|
| 254 |
|
| 255 |
.like-button {
|
|
@@ -258,18 +297,40 @@ if __name__ == '__main__':
|
|
| 258 |
right: 1rem;
|
| 259 |
border: none;
|
| 260 |
background: transparent;
|
| 261 |
-
font-size: 1.
|
| 262 |
cursor: pointer;
|
| 263 |
-
transition:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
}
|
| 265 |
|
| 266 |
.like-button.liked {
|
| 267 |
-
color:
|
|
|
|
| 268 |
}
|
| 269 |
|
| 270 |
.like-button.not-liked {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
color: white;
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
}
|
| 274 |
|
| 275 |
.status-message {
|
|
@@ -312,6 +373,23 @@ if __name__ == '__main__':
|
|
| 312 |
margin-top: 1rem;
|
| 313 |
}
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
@media (max-width: 768px) {
|
| 316 |
.user-controls {
|
| 317 |
flex-direction: column;
|
|
@@ -325,6 +403,12 @@ if __name__ == '__main__':
|
|
| 325 |
.card {
|
| 326 |
width: 100%;
|
| 327 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
}
|
| 329 |
</style>
|
| 330 |
</head>
|
|
@@ -351,12 +435,19 @@ if __name__ == '__main__':
|
|
| 351 |
</div>
|
| 352 |
</div>
|
| 353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
<div class="filter-controls">
|
| 355 |
<label>
|
| 356 |
-
<input type="checkbox" id="showOnlyLiked" />
|
| 357 |
-
๋ด๊ฐ ์ข์์ํ URL๋ง ๋ณด๊ธฐ
|
| 358 |
-
</label>
|
| 359 |
-
<label style="margin-left: 1rem;">
|
| 360 |
<input type="text" id="searchInput" placeholder="URL ๋๋ ์ ๋ชฉ์ผ๋ก ๊ฒ์" style="width: 250px;" />
|
| 361 |
</label>
|
| 362 |
</div>
|
|
@@ -378,17 +469,23 @@ if __name__ == '__main__':
|
|
| 378 |
cardsContainer: document.getElementById('cardsContainer'),
|
| 379 |
loadingIndicator: document.getElementById('loadingIndicator'),
|
| 380 |
statusMessage: document.getElementById('statusMessage'),
|
| 381 |
-
showOnlyLiked: document.getElementById('showOnlyLiked'),
|
| 382 |
searchInput: document.getElementById('searchInput'),
|
| 383 |
loginSection: document.getElementById('loginSection'),
|
| 384 |
-
loggedInSection: document.getElementById('loggedInSection')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
};
|
| 386 |
|
| 387 |
// ์ ํ๋ฆฌ์ผ์ด์
์ํ
|
| 388 |
const state = {
|
| 389 |
username: null,
|
| 390 |
likedURLs: {},
|
| 391 |
-
|
|
|
|
|
|
|
| 392 |
};
|
| 393 |
|
| 394 |
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง ํค
|
|
@@ -413,6 +510,15 @@ if __name__ == '__main__':
|
|
| 413 |
localStorage.setItem(key, JSON.stringify(state.likedURLs));
|
| 414 |
}
|
| 415 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
// ๋ก๋ฉ ์ํ ํ์ ํจ์
|
| 417 |
function setLoading(isLoading) {
|
| 418 |
state.isLoading = isLoading;
|
|
@@ -451,6 +557,7 @@ if __name__ == '__main__':
|
|
| 451 |
elements.currentUser.textContent = data.username;
|
| 452 |
elements.loginSection.style.display = 'none';
|
| 453 |
elements.loggedInSection.style.display = 'block';
|
|
|
|
| 454 |
|
| 455 |
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์ ์ข์์ ์ ๋ณด ๋ก๋
|
| 456 |
state.likedURLs = loadLikesFromStorage();
|
|
@@ -492,6 +599,7 @@ if __name__ == '__main__':
|
|
| 492 |
elements.currentUser.textContent = state.username;
|
| 493 |
elements.loginSection.style.display = 'none';
|
| 494 |
elements.loggedInSection.style.display = 'block';
|
|
|
|
| 495 |
|
| 496 |
showMessage(`${state.username}๋์ผ๋ก ๋ก๊ทธ์ธ๋์์ต๋๋ค.`);
|
| 497 |
|
|
@@ -522,11 +630,13 @@ if __name__ == '__main__':
|
|
| 522 |
if (data.success) {
|
| 523 |
state.username = null;
|
| 524 |
state.likedURLs = {};
|
|
|
|
| 525 |
|
| 526 |
elements.currentUser.textContent = '๋ก๊ทธ์ธ๋์ง ์์';
|
| 527 |
elements.tokenInput.value = '';
|
| 528 |
elements.loginSection.style.display = 'block';
|
| 529 |
elements.loggedInSection.style.display = 'none';
|
|
|
|
| 530 |
|
| 531 |
showMessage('๋ก๊ทธ์์๋์์ต๋๋ค.');
|
| 532 |
|
|
@@ -549,27 +659,13 @@ if __name__ == '__main__':
|
|
| 549 |
const response = await fetch('/api/urls');
|
| 550 |
const urls = await handleApiResponse(response);
|
| 551 |
|
| 552 |
-
|
| 553 |
-
const showOnlyLiked = elements.showOnlyLiked.checked;
|
| 554 |
-
const searchText = elements.searchInput.value.toLowerCase();
|
| 555 |
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
// ์ข์์ ํํฐ๋ง
|
| 560 |
-
if (showOnlyLiked && !state.likedURLs[url]) {
|
| 561 |
-
return false;
|
| 562 |
-
}
|
| 563 |
-
|
| 564 |
-
// ๊ฒ์ ํํฐ๋ง
|
| 565 |
-
if (searchText && !url.toLowerCase().includes(searchText) && !title.toLowerCase().includes(searchText)) {
|
| 566 |
-
return false;
|
| 567 |
-
}
|
| 568 |
-
|
| 569 |
-
return true;
|
| 570 |
-
});
|
| 571 |
|
| 572 |
-
|
|
|
|
| 573 |
} catch (error) {
|
| 574 |
console.error('URL ๋ชฉ๋ก ๋ก๋ ์ค๋ฅ:', error);
|
| 575 |
showMessage(`URL ๋ก๋ ์ค๋ฅ: ${error.message}`, true);
|
|
@@ -578,8 +674,32 @@ if __name__ == '__main__':
|
|
| 578 |
}
|
| 579 |
}
|
| 580 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 581 |
// ์ข์์ ํ ๊ธ
|
| 582 |
-
function toggleLike(url,
|
| 583 |
if (!state.username) {
|
| 584 |
showMessage('์ข์์๋ฅผ ํ๋ ค๋ฉด ํ๊น
ํ์ด์ค API ํ ํฐ์ผ๋ก ์ธ์ฆ์ด ํ์ํฉ๋๋ค.', true);
|
| 585 |
return;
|
|
@@ -594,22 +714,33 @@ if __name__ == '__main__':
|
|
| 594 |
// ์ํ ํ ๊ธ
|
| 595 |
if (isCurrentlyLiked) {
|
| 596 |
delete state.likedURLs[url];
|
| 597 |
-
|
| 598 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 599 |
showMessage('์ข์์๋ฅผ ์ทจ์ํ์ต๋๋ค.');
|
| 600 |
} else {
|
| 601 |
state.likedURLs[url] = true;
|
| 602 |
-
|
| 603 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
showMessage('์ข์์๋ฅผ ์ถ๊ฐํ์ต๋๋ค.');
|
| 605 |
}
|
| 606 |
|
| 607 |
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅ
|
| 608 |
saveLikesToStorage();
|
| 609 |
|
| 610 |
-
//
|
| 611 |
-
|
| 612 |
-
|
|
|
|
|
|
|
|
|
|
| 613 |
}
|
| 614 |
} catch (error) {
|
| 615 |
console.error('์ข์์ ํ ๊ธ ์ค๋ฅ:', error);
|
|
@@ -638,10 +769,17 @@ if __name__ == '__main__':
|
|
| 638 |
|
| 639 |
// ์นด๋ ์์ฑ
|
| 640 |
const card = document.createElement('div');
|
| 641 |
-
card.className =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 642 |
|
| 643 |
// ์ ๋ชฉ
|
| 644 |
const titleEl = document.createElement('h3');
|
|
|
|
| 645 |
titleEl.textContent = title;
|
| 646 |
card.appendChild(titleEl);
|
| 647 |
|
|
@@ -656,10 +794,11 @@ if __name__ == '__main__':
|
|
| 656 |
const likeBtn = document.createElement('button');
|
| 657 |
likeBtn.className = `like-button ${isLiked ? 'liked' : 'not-liked'}`;
|
| 658 |
likeBtn.textContent = 'โฅ';
|
|
|
|
| 659 |
|
| 660 |
likeBtn.addEventListener('click', function(e) {
|
| 661 |
e.preventDefault();
|
| 662 |
-
toggleLike(url,
|
| 663 |
});
|
| 664 |
card.appendChild(likeBtn);
|
| 665 |
|
|
@@ -668,6 +807,18 @@ if __name__ == '__main__':
|
|
| 668 |
});
|
| 669 |
}
|
| 670 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 671 |
// ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ค์
|
| 672 |
elements.loginButton.addEventListener('click', () => {
|
| 673 |
login(elements.tokenInput.value);
|
|
@@ -682,14 +833,17 @@ if __name__ == '__main__':
|
|
| 682 |
}
|
| 683 |
});
|
| 684 |
|
| 685 |
-
//
|
| 686 |
-
elements.showOnlyLiked.addEventListener('change', loadUrls);
|
| 687 |
elements.searchInput.addEventListener('input', () => {
|
| 688 |
-
// ์
๋ ฅ ์ง์ฐ ์ฒ๋ฆฌ (ํ์ดํํ ๋๋ง๋ค
|
| 689 |
clearTimeout(state.searchTimeout);
|
| 690 |
-
state.searchTimeout = setTimeout(
|
| 691 |
});
|
| 692 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 693 |
// ์ด๊ธฐํ
|
| 694 |
checkSessionStatus();
|
| 695 |
</script>
|
|
|
|
| 172 |
margin-bottom: 1rem;
|
| 173 |
}
|
| 174 |
|
| 175 |
+
.stats-bar {
|
| 176 |
+
background-color: #e9f7ef;
|
| 177 |
+
padding: 1rem;
|
| 178 |
+
border-radius: 5px;
|
| 179 |
+
margin-bottom: 1rem;
|
| 180 |
+
display: flex;
|
| 181 |
+
justify-content: space-between;
|
| 182 |
+
align-items: center;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
.stats-bar .liked-count {
|
| 186 |
+
font-weight: bold;
|
| 187 |
+
color: #e74c3c;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
.user-controls {
|
| 191 |
display: flex;
|
| 192 |
justify-content: space-between;
|
|
|
|
| 252 |
}
|
| 253 |
|
| 254 |
.card {
|
| 255 |
+
border: 1px solid #ddd;
|
| 256 |
+
border-radius: 8px;
|
| 257 |
padding: 1rem;
|
| 258 |
width: 300px;
|
| 259 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 260 |
position: relative;
|
| 261 |
+
background-color: #fff;
|
| 262 |
+
transition: all 0.3s ease;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.card:hover {
|
| 266 |
+
transform: translateY(-5px);
|
| 267 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
.card.liked {
|
| 271 |
+
border-color: #e74c3c;
|
| 272 |
+
background-color: #fff9f9;
|
| 273 |
}
|
| 274 |
|
| 275 |
.card a {
|
| 276 |
text-decoration: none;
|
| 277 |
+
color: #2980b9;
|
| 278 |
word-break: break-all;
|
| 279 |
+
display: block;
|
| 280 |
+
margin-top: 0.5rem;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
.card a:hover {
|
| 284 |
+
text-decoration: underline;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
.card-title {
|
| 288 |
+
margin-top: 0;
|
| 289 |
+
color: #333;
|
| 290 |
+
font-size: 1.2rem;
|
| 291 |
+
padding-right: 30px; /* ์ข์์ ๋ฒํผ ๊ณต๊ฐ ํ๋ณด */
|
| 292 |
}
|
| 293 |
|
| 294 |
.like-button {
|
|
|
|
| 297 |
right: 1rem;
|
| 298 |
border: none;
|
| 299 |
background: transparent;
|
| 300 |
+
font-size: 1.8rem;
|
| 301 |
cursor: pointer;
|
| 302 |
+
transition: all 0.2s;
|
| 303 |
+
z-index: 2;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
.like-button:hover {
|
| 307 |
+
transform: scale(1.2);
|
| 308 |
}
|
| 309 |
|
| 310 |
.like-button.liked {
|
| 311 |
+
color: #e74c3c;
|
| 312 |
+
text-shadow: 0 0 5px rgba(231, 76, 60, 0.3);
|
| 313 |
}
|
| 314 |
|
| 315 |
.like-button.not-liked {
|
| 316 |
+
color: #ccc;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
.like-label {
|
| 320 |
+
position: absolute;
|
| 321 |
+
top: 10px;
|
| 322 |
+
left: 10px;
|
| 323 |
+
background-color: #e74c3c;
|
| 324 |
color: white;
|
| 325 |
+
padding: 2px 8px;
|
| 326 |
+
border-radius: 10px;
|
| 327 |
+
font-size: 0.7rem;
|
| 328 |
+
font-weight: bold;
|
| 329 |
+
display: none;
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
.card.liked .like-label {
|
| 333 |
+
display: block;
|
| 334 |
}
|
| 335 |
|
| 336 |
.status-message {
|
|
|
|
| 373 |
margin-top: 1rem;
|
| 374 |
}
|
| 375 |
|
| 376 |
+
.view-toggle {
|
| 377 |
+
margin-bottom: 1rem;
|
| 378 |
+
display: flex;
|
| 379 |
+
gap: 0.5rem;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
.view-toggle button {
|
| 383 |
+
background-color: #f8f9fa;
|
| 384 |
+
color: #333;
|
| 385 |
+
border: 1px solid #ddd;
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
.view-toggle button.active {
|
| 389 |
+
background-color: #4CAF50;
|
| 390 |
+
color: white;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
@media (max-width: 768px) {
|
| 394 |
.user-controls {
|
| 395 |
flex-direction: column;
|
|
|
|
| 403 |
.card {
|
| 404 |
width: 100%;
|
| 405 |
}
|
| 406 |
+
|
| 407 |
+
.stats-bar {
|
| 408 |
+
flex-direction: column;
|
| 409 |
+
gap: 0.5rem;
|
| 410 |
+
align-items: flex-start;
|
| 411 |
+
}
|
| 412 |
}
|
| 413 |
</style>
|
| 414 |
</head>
|
|
|
|
| 435 |
</div>
|
| 436 |
</div>
|
| 437 |
|
| 438 |
+
<!-- ์ข์์ ํต๊ณ ๋ฐ ํ์ ํ ๊ธ -->
|
| 439 |
+
<div id="statsBar" class="stats-bar" style="display: none;">
|
| 440 |
+
<div>
|
| 441 |
+
์ด <span id="totalCount">0</span>๊ฐ ์ค <span id="likedCount" class="liked-count">0</span>๊ฐ ์ข์์ ํจ
|
| 442 |
+
</div>
|
| 443 |
+
<div class="view-toggle">
|
| 444 |
+
<button id="allViewBtn" class="active">์ ์ฒด ๋ณด๊ธฐ</button>
|
| 445 |
+
<button id="likedViewBtn">์ข์์๋ง ๋ณด๊ธฐ</button>
|
| 446 |
+
</div>
|
| 447 |
+
</div>
|
| 448 |
+
|
| 449 |
<div class="filter-controls">
|
| 450 |
<label>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
<input type="text" id="searchInput" placeholder="URL ๋๋ ์ ๋ชฉ์ผ๋ก ๊ฒ์" style="width: 250px;" />
|
| 452 |
</label>
|
| 453 |
</div>
|
|
|
|
| 469 |
cardsContainer: document.getElementById('cardsContainer'),
|
| 470 |
loadingIndicator: document.getElementById('loadingIndicator'),
|
| 471 |
statusMessage: document.getElementById('statusMessage'),
|
|
|
|
| 472 |
searchInput: document.getElementById('searchInput'),
|
| 473 |
loginSection: document.getElementById('loginSection'),
|
| 474 |
+
loggedInSection: document.getElementById('loggedInSection'),
|
| 475 |
+
statsBar: document.getElementById('statsBar'),
|
| 476 |
+
totalCount: document.getElementById('totalCount'),
|
| 477 |
+
likedCount: document.getElementById('likedCount'),
|
| 478 |
+
allViewBtn: document.getElementById('allViewBtn'),
|
| 479 |
+
likedViewBtn: document.getElementById('likedViewBtn')
|
| 480 |
};
|
| 481 |
|
| 482 |
// ์ ํ๋ฆฌ์ผ์ด์
์ํ
|
| 483 |
const state = {
|
| 484 |
username: null,
|
| 485 |
likedURLs: {},
|
| 486 |
+
allURLs: [],
|
| 487 |
+
isLoading: false,
|
| 488 |
+
viewMode: 'all' // 'all' ๋๋ 'liked'
|
| 489 |
};
|
| 490 |
|
| 491 |
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง ํค
|
|
|
|
| 510 |
localStorage.setItem(key, JSON.stringify(state.likedURLs));
|
| 511 |
}
|
| 512 |
|
| 513 |
+
// ์ข์์ ํต๊ณ ์
๋ฐ์ดํธ
|
| 514 |
+
function updateLikeStats() {
|
| 515 |
+
const totalCount = state.allURLs.length;
|
| 516 |
+
const likedCount = Object.keys(state.likedURLs).length;
|
| 517 |
+
|
| 518 |
+
elements.totalCount.textContent = totalCount;
|
| 519 |
+
elements.likedCount.textContent = likedCount;
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
// ๋ก๋ฉ ์ํ ํ์ ํจ์
|
| 523 |
function setLoading(isLoading) {
|
| 524 |
state.isLoading = isLoading;
|
|
|
|
| 557 |
elements.currentUser.textContent = data.username;
|
| 558 |
elements.loginSection.style.display = 'none';
|
| 559 |
elements.loggedInSection.style.display = 'block';
|
| 560 |
+
elements.statsBar.style.display = 'flex';
|
| 561 |
|
| 562 |
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง์์ ์ข์์ ์ ๋ณด ๋ก๋
|
| 563 |
state.likedURLs = loadLikesFromStorage();
|
|
|
|
| 599 |
elements.currentUser.textContent = state.username;
|
| 600 |
elements.loginSection.style.display = 'none';
|
| 601 |
elements.loggedInSection.style.display = 'block';
|
| 602 |
+
elements.statsBar.style.display = 'flex';
|
| 603 |
|
| 604 |
showMessage(`${state.username}๋์ผ๋ก ๋ก๊ทธ์ธ๋์์ต๋๋ค.`);
|
| 605 |
|
|
|
|
| 630 |
if (data.success) {
|
| 631 |
state.username = null;
|
| 632 |
state.likedURLs = {};
|
| 633 |
+
state.allURLs = [];
|
| 634 |
|
| 635 |
elements.currentUser.textContent = '๋ก๊ทธ์ธ๋์ง ์์';
|
| 636 |
elements.tokenInput.value = '';
|
| 637 |
elements.loginSection.style.display = 'block';
|
| 638 |
elements.loggedInSection.style.display = 'none';
|
| 639 |
+
elements.statsBar.style.display = 'none';
|
| 640 |
|
| 641 |
showMessage('๋ก๊ทธ์์๋์์ต๋๋ค.');
|
| 642 |
|
|
|
|
| 659 |
const response = await fetch('/api/urls');
|
| 660 |
const urls = await handleApiResponse(response);
|
| 661 |
|
| 662 |
+
state.allURLs = urls;
|
|
|
|
|
|
|
| 663 |
|
| 664 |
+
// ํํฐ๋ง ๋ฐ ๋ ๋๋ง
|
| 665 |
+
filterAndRenderCards();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 666 |
|
| 667 |
+
// ์ข์์ ํต๊ณ ์
๋ฐ์ดํธ
|
| 668 |
+
updateLikeStats();
|
| 669 |
} catch (error) {
|
| 670 |
console.error('URL ๋ชฉ๋ก ๋ก๋ ์ค๋ฅ:', error);
|
| 671 |
showMessage(`URL ๋ก๋ ์ค๋ฅ: ${error.message}`, true);
|
|
|
|
| 674 |
}
|
| 675 |
}
|
| 676 |
|
| 677 |
+
// ํํฐ๋ง ๋ฐ ์นด๋ ๋ ๋๋ง
|
| 678 |
+
function filterAndRenderCards() {
|
| 679 |
+
const searchText = elements.searchInput.value.toLowerCase();
|
| 680 |
+
|
| 681 |
+
// ํํฐ๋ง ์ ์ฉ
|
| 682 |
+
const filteredUrls = state.allURLs.filter(item => {
|
| 683 |
+
const { url, title } = item;
|
| 684 |
+
|
| 685 |
+
// ์ข์์ ํํฐ๋ง (์ข์์๋ง ๋ณด๊ธฐ ๋ชจ๋)
|
| 686 |
+
if (state.viewMode === 'liked' && !state.likedURLs[url]) {
|
| 687 |
+
return false;
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
// ๊ฒ์ ํํฐ๋ง
|
| 691 |
+
if (searchText && !url.toLowerCase().includes(searchText) && !title.toLowerCase().includes(searchText)) {
|
| 692 |
+
return false;
|
| 693 |
+
}
|
| 694 |
+
|
| 695 |
+
return true;
|
| 696 |
+
});
|
| 697 |
+
|
| 698 |
+
renderCards(filteredUrls);
|
| 699 |
+
}
|
| 700 |
+
|
| 701 |
// ์ข์์ ํ ๊ธ
|
| 702 |
+
function toggleLike(url, card) {
|
| 703 |
if (!state.username) {
|
| 704 |
showMessage('์ข์์๋ฅผ ํ๋ ค๋ฉด ํ๊น
ํ์ด์ค API ํ ํฐ์ผ๋ก ์ธ์ฆ์ด ํ์ํฉ๋๋ค.', true);
|
| 705 |
return;
|
|
|
|
| 714 |
// ์ํ ํ ๊ธ
|
| 715 |
if (isCurrentlyLiked) {
|
| 716 |
delete state.likedURLs[url];
|
| 717 |
+
card.classList.remove('liked');
|
| 718 |
+
const likeBtn = card.querySelector('.like-button');
|
| 719 |
+
if (likeBtn) {
|
| 720 |
+
likeBtn.classList.remove('liked');
|
| 721 |
+
likeBtn.classList.add('not-liked');
|
| 722 |
+
}
|
| 723 |
showMessage('์ข์์๋ฅผ ์ทจ์ํ์ต๋๋ค.');
|
| 724 |
} else {
|
| 725 |
state.likedURLs[url] = true;
|
| 726 |
+
card.classList.add('liked');
|
| 727 |
+
const likeBtn = card.querySelector('.like-button');
|
| 728 |
+
if (likeBtn) {
|
| 729 |
+
likeBtn.classList.add('liked');
|
| 730 |
+
likeBtn.classList.remove('not-liked');
|
| 731 |
+
}
|
| 732 |
showMessage('์ข์์๋ฅผ ์ถ๊ฐํ์ต๋๋ค.');
|
| 733 |
}
|
| 734 |
|
| 735 |
// ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅ
|
| 736 |
saveLikesToStorage();
|
| 737 |
|
| 738 |
+
// ์ข์์ ํต๊ณ ์
๋ฐ์ดํธ
|
| 739 |
+
updateLikeStats();
|
| 740 |
+
|
| 741 |
+
// ์ข์์๋ง ๋ณด๊ธฐ ๋ชจ๋์ธ ๊ฒฝ์ฐ ๋ชฉ๋ก ๋ค์ ๋ ๋๋ง
|
| 742 |
+
if (state.viewMode === 'liked') {
|
| 743 |
+
filterAndRenderCards();
|
| 744 |
}
|
| 745 |
} catch (error) {
|
| 746 |
console.error('์ข์์ ํ ๊ธ ์ค๋ฅ:', error);
|
|
|
|
| 769 |
|
| 770 |
// ์นด๋ ์์ฑ
|
| 771 |
const card = document.createElement('div');
|
| 772 |
+
card.className = `card ${isLiked ? 'liked' : ''}`;
|
| 773 |
+
|
| 774 |
+
// ์ข์์ ๋ผ๋ฒจ
|
| 775 |
+
const likeLabel = document.createElement('span');
|
| 776 |
+
likeLabel.className = 'like-label';
|
| 777 |
+
likeLabel.textContent = '์ข์์';
|
| 778 |
+
card.appendChild(likeLabel);
|
| 779 |
|
| 780 |
// ์ ๋ชฉ
|
| 781 |
const titleEl = document.createElement('h3');
|
| 782 |
+
titleEl.className = 'card-title';
|
| 783 |
titleEl.textContent = title;
|
| 784 |
card.appendChild(titleEl);
|
| 785 |
|
|
|
|
| 794 |
const likeBtn = document.createElement('button');
|
| 795 |
likeBtn.className = `like-button ${isLiked ? 'liked' : 'not-liked'}`;
|
| 796 |
likeBtn.textContent = 'โฅ';
|
| 797 |
+
likeBtn.title = isLiked ? '์ข์์ ์ทจ์' : '์ข์์';
|
| 798 |
|
| 799 |
likeBtn.addEventListener('click', function(e) {
|
| 800 |
e.preventDefault();
|
| 801 |
+
toggleLike(url, card);
|
| 802 |
});
|
| 803 |
card.appendChild(likeBtn);
|
| 804 |
|
|
|
|
| 807 |
});
|
| 808 |
}
|
| 809 |
|
| 810 |
+
// ๋ชจ๋ ๋ณ๊ฒฝ ํจ์
|
| 811 |
+
function changeViewMode(mode) {
|
| 812 |
+
state.viewMode = mode;
|
| 813 |
+
|
| 814 |
+
// ๋ฒํผ ์ํ ์
๋ฐ์ดํธ
|
| 815 |
+
elements.allViewBtn.classList.toggle('active', mode === 'all');
|
| 816 |
+
elements.likedViewBtn.classList.toggle('active', mode === 'liked');
|
| 817 |
+
|
| 818 |
+
// ์นด๋ ๋ค์ ๋ ๋๋ง
|
| 819 |
+
filterAndRenderCards();
|
| 820 |
+
}
|
| 821 |
+
|
| 822 |
// ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ค์
|
| 823 |
elements.loginButton.addEventListener('click', () => {
|
| 824 |
login(elements.tokenInput.value);
|
|
|
|
| 833 |
}
|
| 834 |
});
|
| 835 |
|
| 836 |
+
// ๊ฒ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋
|
|
|
|
| 837 |
elements.searchInput.addEventListener('input', () => {
|
| 838 |
+
// ์
๋ ฅ ์ง์ฐ ์ฒ๋ฆฌ (ํ์ดํํ ๋๋ง๋ค ํํฐ๋ง ๋ฐฉ์ง)
|
| 839 |
clearTimeout(state.searchTimeout);
|
| 840 |
+
state.searchTimeout = setTimeout(filterAndRenderCards, 300);
|
| 841 |
});
|
| 842 |
|
| 843 |
+
// ๋ณด๊ธฐ ๋ชจ๋ ์ ํ ๋ฒํผ
|
| 844 |
+
elements.allViewBtn.addEventListener('click', () => changeViewMode('all'));
|
| 845 |
+
elements.likedViewBtn.addEventListener('click', () => changeViewMode('liked'));
|
| 846 |
+
|
| 847 |
// ์ด๊ธฐํ
|
| 848 |
checkSessionStatus();
|
| 849 |
</script>
|