2025-09-18 17:55:32 +09:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html
|
|
|
|
|
xmlns:th="http://www.thymeleaf.org"
|
|
|
|
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
2025-09-19 16:32:24 +09:00
|
|
|
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
|
2025-09-18 17:55:32 +09:00
|
|
|
layout:decorate="~{layout/default_layout}">
|
|
|
|
|
<head>
|
|
|
|
|
<title>Bookmarks</title>
|
2025-09-19 16:32:24 +09:00
|
|
|
<style>
|
2025-09-23 15:37:51 +09:00
|
|
|
/* --- [수정] 북마크 이미지 영역 스크롤 및 카드 크기 고정 --- */
|
|
|
|
|
|
|
|
|
|
/* 1. 각 북마크 카드의 전체 높이를 720px로 고정합니다. */
|
|
|
|
|
.swiper-slide .box.feature {
|
|
|
|
|
height: 720px;
|
|
|
|
|
padding-bottom: 50px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 2. 여러 이미지를 담는 컨테이너의 스타일을 정의합니다. */
|
|
|
|
|
.image-flick-container {
|
|
|
|
|
height: 450px; /* 이미지 영역의 높이를 450px로 고정합니다. (값 조절 가능) */
|
|
|
|
|
overflow-y: auto; /* 이 높이를 넘어가는 이미지는 세로 스크롤됩니다. */
|
|
|
|
|
border: 1px solid #eee;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
background-color: #f0f0f0; /* 스크롤 영역 배경색을 살짝 추가 */
|
|
|
|
|
margin-bottom: 1em; /* 이미지 영역과 텍스트 영역 사이의 간격 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 3. 스크롤 영역 내부의 이미지 스타일을 지정합니다. */
|
|
|
|
|
.image-flick-container img {
|
|
|
|
|
width: 100%; /* 이미지를 컨테이너 너비에 맞춥니다. */
|
|
|
|
|
height: auto; /* 이미지 비율을 유지합니다. */
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 10px; /* 이미지들 사이에 약간의 간격을 줍니다. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.image-flick-container img:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 4. (선택) 스크롤바 디자인을 개선합니다. */
|
|
|
|
|
.image-flick-container::-webkit-scrollbar {
|
|
|
|
|
width: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.image-flick-container::-webkit-scrollbar-track {
|
|
|
|
|
background: #e9e9e9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.image-flick-container::-webkit-scrollbar-thumb {
|
|
|
|
|
background: #bbb;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.image-flick-container::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
background: #999;
|
2025-09-19 16:32:24 +09:00
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css" />
|
|
|
|
|
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
|
|
|
|
|
<script>
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
|
const swiper = new Swiper('.bookmark-swiper', {
|
|
|
|
|
loop: false,
|
|
|
|
|
pagination: {
|
|
|
|
|
el: '.swiper-pagination',
|
|
|
|
|
clickable: true,
|
|
|
|
|
},
|
|
|
|
|
navigation: {
|
|
|
|
|
nextEl: '.swiper-button-next',
|
|
|
|
|
prevEl: '.swiper-button-prev',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
2025-09-18 17:55:32 +09:00
|
|
|
</head>
|
|
|
|
|
<th:block layout:fragment="content">
|
|
|
|
|
<section class="wrapper style2">
|
|
|
|
|
<div class="container">
|
|
|
|
|
<header class="major">
|
|
|
|
|
<h2>Bookmarks</h2>
|
|
|
|
|
<p>다른 사용자들이 저장한 유용한 페이지들을 둘러보세요.</p>
|
2025-09-19 16:32:24 +09:00
|
|
|
<div class="filter-controls" style="margin-bottom: 2em; text-align: center;">
|
|
|
|
|
<div style="margin-bottom: 1em;">
|
|
|
|
|
<strong>카테고리:</strong>
|
|
|
|
|
<a th:href="@{/bookmarks}" th:classappend="${currentCategory == null && currentTag == null} ? 'button small' : 'button alt small'">전체</a>
|
|
|
|
|
<a th:each="cat : ${allCategories}"
|
|
|
|
|
th:href="@{/bookmarks(category=${cat})}"
|
|
|
|
|
th:text="${cat}"
|
|
|
|
|
th:classappend="${currentCategory == cat} ? 'button small' : 'button alt small'"></a>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<strong>태그:</strong>
|
|
|
|
|
<a th:each="tg : ${allTags}"
|
|
|
|
|
th:href="@{/bookmarks(tag=${tg})}"
|
|
|
|
|
th:text="'#' + ${tg}"
|
|
|
|
|
th:classappend="${currentTag == tg} ? 'button small' : 'button alt small'"></a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-18 17:55:32 +09:00
|
|
|
</header>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="wrapper style1">
|
|
|
|
|
<div class="container">
|
2025-09-19 16:32:24 +09:00
|
|
|
<div class="swiper bookmark-swiper">
|
|
|
|
|
<div class="swiper-wrapper">
|
|
|
|
|
<div class="swiper-slide" th:each="bookmark : ${bookmarksPage.content}">
|
|
|
|
|
<section class="box feature" style="margin: 0; height: 100%; display: flex; flex-direction: column;">
|
2025-09-18 17:55:32 +09:00
|
|
|
|
2025-09-19 16:32:24 +09:00
|
|
|
<div th:switch="${bookmark.bookmarkType}">
|
2025-09-23 15:37:51 +09:00
|
|
|
<div th:case="'IMAGE'" class="image-flick-container">
|
|
|
|
|
<th:block th:each="image : ${bookmark.images}" th:if="${image.isVisible}">
|
|
|
|
|
<img th:src="${apiBaseUrl + image.url}" alt="Bookmark Image" />
|
|
|
|
|
</th:block>
|
2025-09-18 17:55:32 +09:00
|
|
|
</div>
|
2025-09-23 15:37:51 +09:00
|
|
|
<div th:case="'VIDEO'" class="video-container bookmark-image-container">
|
|
|
|
|
<th:block th:each="image : ${bookmark.images}" th:if="${image.isVisible and #lists.size(bookmark.images) > 0}">
|
|
|
|
|
<video controls style="width: 100%;">
|
|
|
|
|
<source th:src="${apiBaseUrl + image.url}" type="video/mp4">
|
|
|
|
|
</video>
|
|
|
|
|
</th:block>
|
2025-09-18 17:55:32 +09:00
|
|
|
</div>
|
2025-09-19 16:32:24 +09:00
|
|
|
<a th:case="'URL'"
|
|
|
|
|
href="javascript:void(0);"
|
|
|
|
|
th:data-url="${bookmark.url}"
|
|
|
|
|
th:data-title="${bookmark.title}"
|
2025-09-23 15:37:51 +09:00
|
|
|
onclick="showBookmarkOptions(this)" class="bookmark-image-container">
|
|
|
|
|
<th:block th:each="image : ${bookmark.images}" th:if="${image.isVisible}">
|
|
|
|
|
<img th:src="${apiBaseUrl + mage.url}" alt="Bookmark Image" />
|
|
|
|
|
</th:block>
|
2025-09-19 16:32:24 +09:00
|
|
|
</a>
|
2025-09-18 17:55:32 +09:00
|
|
|
</div>
|
|
|
|
|
|
2025-09-19 16:32:24 +09:00
|
|
|
<div class="inner" style="flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between;">
|
|
|
|
|
<div>
|
|
|
|
|
<header>
|
|
|
|
|
<h3 th:text="${bookmark.title}">북마크 제목</h3>
|
|
|
|
|
<p th:if="${bookmark.userComment}" th:text="${bookmark.userComment}"></p>
|
|
|
|
|
</header>
|
2025-09-23 15:37:51 +09:00
|
|
|
<div class="bookmark-meta-container" style="margin: 1em 0;">
|
|
|
|
|
<div th:if="${bookmark.category != null and !#strings.isEmpty(bookmark.category)}">
|
|
|
|
|
<span class="tag-item" th:text="${bookmark.category}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
<div th:if="${bookmark.tags != null and !#lists.isEmpty(bookmark.tags)}">
|
|
|
|
|
<span th:each="tag : ${bookmark.tags}" class="tag-item" th:text="'#' + ${tag}"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-19 16:32:24 +09:00
|
|
|
<p th:text="${#strings.abbreviate(bookmark.description, 100)}"></p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<div class="vote-controls" style="margin-top: 1em; text-align: center;" th:data-bookmark-id="${bookmark.id}">
|
|
|
|
|
<button class="button small" onclick="handleBookmarkVote(this, 'like')">
|
|
|
|
|
👍 Like (<span class="like-count" th:text="${bookmark.voteCount}">0</span>)
|
|
|
|
|
</button>
|
|
|
|
|
<button class="button small" onclick="handleBookmarkVote(this, 'unlike')">
|
|
|
|
|
👎 Unlike (<span class="unlike-count" th:text="${bookmark.unlikeCount}">0</span>)
|
|
|
|
|
</button>
|
|
|
|
|
<button class="button alt small" th:onclick="toggleCommentSection([[${bookmark.id}]])">
|
|
|
|
|
💬 Comments
|
|
|
|
|
</button>
|
2025-09-23 15:37:51 +09:00
|
|
|
<button class="button alt small"
|
|
|
|
|
th:if="${#authentication.principal != null && #authentication.principal instanceof T(org.springframework.security.core.userdetails.UserDetails) && #authentication.principal.username == bookmark.userId}"
|
|
|
|
|
th:data-bookmark-id="${bookmark.id}"
|
|
|
|
|
onclick="openBookmarkEditPopup(this)">
|
|
|
|
|
✏️ 수정
|
|
|
|
|
</button>
|
2025-09-19 16:32:24 +09:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<section class="comment-section" th:id="|comment-section-${bookmark.id}|" style="display: none; margin-top: 1em; text-align: left;">
|
|
|
|
|
|
|
|
|
|
<th:block sec:authorize="isAuthenticated()">
|
|
|
|
|
<div class="comment-form-container">
|
|
|
|
|
<textarea th:id="|comment-input-${bookmark.id}|" placeholder="댓글을 입력하세요..." style="width: 100%;"></textarea>
|
|
|
|
|
<button th:onclick="submitBookmarkComment([[${bookmark.id}]])" class="button small" style="margin-top: 0.5em;">등록</button>
|
|
|
|
|
</div>
|
|
|
|
|
</th:block>
|
|
|
|
|
|
|
|
|
|
<div sec:authorize="isAnonymous()" style="padding: 1em; text-align: center; border: 1px dashed #ccc; margin-bottom: 1em;">
|
2025-09-23 15:37:51 +09:00
|
|
|
<p style="margin:0;">댓글을 작성하려면 <a href="javascript:void(0);" class="open-login-popup" to="#loginPopup">로그인</a>이 필요합니다.</p>
|
2025-09-19 16:32:24 +09:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div th:id="|comments-list-${bookmark.id}|" style="margin-top: 1em;">
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
2025-09-18 17:55:32 +09:00
|
|
|
</div>
|
2025-09-19 16:32:24 +09:00
|
|
|
<div class="swiper-pagination"></div>
|
|
|
|
|
<div class="swiper-button-prev"></div>
|
|
|
|
|
<div class="swiper-button-next"></div>
|
2025-09-18 17:55:32 +09:00
|
|
|
</div>
|
|
|
|
|
|
2025-09-19 16:32:24 +09:00
|
|
|
<div th:if="${bookmarksPage.empty}">
|
|
|
|
|
<p style="text-align: center;">아직 저장된 페이지가 없습니다.</p>
|
|
|
|
|
</div>
|
2025-09-18 17:55:32 +09:00
|
|
|
</div>
|
|
|
|
|
</section>
|
2025-09-23 17:37:22 +09:00
|
|
|
<div class="container" style="text-align:center;">
|
|
|
|
|
<ins class="adsbygoogle"
|
|
|
|
|
style="display:block"
|
|
|
|
|
data-ad-client="ca-pub-9504446465764716" data-ad-slot="1234567890" data-ad-format="auto"
|
|
|
|
|
data-full-width-responsive="true"></ins>
|
|
|
|
|
<script>
|
|
|
|
|
(adsbygoogle = window.adsbygoogle || []).push({});
|
|
|
|
|
</script>
|
|
|
|
|
</div>
|
2025-09-18 17:55:32 +09:00
|
|
|
</th:block>
|
|
|
|
|
</html>
|