diff options
| -rw-r--r-- | layouts/_partials/posts/action-menu/comment-button.html | 86 | ||||
| -rw-r--r-- | layouts/_partials/posts/action-menu/like-button.html | 122 | ||||
| -rw-r--r-- | layouts/_partials/scripts.html | 4 | ||||
| -rw-r--r-- | layouts/_partials/scripts/action-menu-js.html | 170 |
4 files changed, 177 insertions, 205 deletions
diff --git a/layouts/_partials/posts/action-menu/comment-button.html b/layouts/_partials/posts/action-menu/comment-button.html index 563bf4bd..d646ca27 100644 --- a/layouts/_partials/posts/action-menu/comment-button.html +++ b/layouts/_partials/posts/action-menu/comment-button.html @@ -1,4 +1,3 @@ -{{ /* button html */}} <button class="dark:invert flex items-center justify-center gap-1.5 min-w-12 h-12 px-2 border-none bg-transparent text-gray-600 cursor-pointer relative transition-all duration-200 hover:bg-black/5 hover:rounded-lg group" id="comments-toggle" title="{{ T "actionMenu.comment" }}" data-tooltip> <img src="/img/comments.svg" width="20" height="20" @@ -7,32 +6,67 @@ <span class="text-xs font-bold text-gray-600 min-w-4 text-center bg-white/90 rounded-xl px-1.5 py-0.5 shadow-sm dark:text-gray-400 dark:bg-black/70" id="comment-count">...</span> </button> -{{ /* button functionality */}} <script> +// initialize button features and comment count document.addEventListener('DOMContentLoaded', () => { - const commentsToggle = document.getElementById('comments-toggle'); - const commentsSection = document.getElementById('comments-section'); - - if (!commentsToggle || !commentsSection) return; - - // click on toggle - commentsToggle.addEventListener('click', function() { - const isHidden = commentsSection.classList.contains('hidden'); - const entranceAnimation = 'animate-slide-in-down'; - const exitAnimation = 'animate-slide-out-up'; - - if (isHidden) { - // swtich on - commentsSection.classList.remove('hidden', exitAnimation); - commentsSection.classList.add(entranceAnimation); - } else { - // switch off - commentsSection.classList.remove(entranceAnimation); - commentsSection.classList.add(exitAnimation); - setTimeout(() => { - commentsSection.classList.add('hidden'); - }, 200); - } - }); + + function initButtonFeature() { + const commentsToggle = document.getElementById('comments-toggle'); + const commentsSection = document.getElementById('comments-section'); + + if (!commentsToggle || !commentsSection) return; + + // click on toggle + commentsToggle.addEventListener('click', function() { + const isHidden = commentsSection.classList.contains('hidden'); + const entranceAnimation = 'animate-slide-in-down'; + const exitAnimation = 'animate-slide-out-up'; + + if (isHidden) { + // swtich on + commentsSection.classList.remove('hidden', exitAnimation); + commentsSection.classList.add(entranceAnimation); + } else { + // switch off + commentsSection.classList.remove(entranceAnimation); + commentsSection.classList.add(exitAnimation); + setTimeout(() => { + commentsSection.classList.add('hidden'); + }, 200); + } + }); + } + + function initCommentCount() { + const commentCountEl = document.getElementById('comment-count'); + if (!commentCountEl) return; + + const observer = new MutationObserver(() => { + // check if giscus is loaded + const giscusIframe = document.querySelector('iframe.giscus-frame'); + if (!giscusIframe) return; + + giscusIframe.addEventListener('load', () => { + // receive message from giscus iframe + window.addEventListener('message', function(event) { + if (event.origin !== 'https://giscus.app') return; + const giscusData = event.data; + if (giscusData.giscus?.discussion) { + const discussion = giscusData.giscus.discussion; + const totalComments = (discussion.totalCommentCount || 0) + (discussion.totalReplyCount || 0); + // set comment count + commentCountEl.textContent = totalComments.toString(); + // if no discussion found, set to 0 + } else commentCountEl.textContent = '0'; + }); + }); + + observer.disconnect(); + }); + observer.observe(document.body, { childList: true, subtree: true }); + } + + initButtonFeature(); + initCommentCount(); }); </script> diff --git a/layouts/_partials/posts/action-menu/like-button.html b/layouts/_partials/posts/action-menu/like-button.html index c4c50c91..c9e793b8 100644 --- a/layouts/_partials/posts/action-menu/like-button.html +++ b/layouts/_partials/posts/action-menu/like-button.html @@ -1,17 +1,18 @@ +{{ $pageID := .File.UniqueID | urlize }} <!-- 点赞按钮 --> -<div class="relative" id="like-container-{{ .File.UniqueID | urlize }}"> +<div class="relative" id="like-container-{{ $pageID }}"> <button class="dark:invert flex items-center justify-center gap-1.5 min-w-12 h-12 px-2 border-none bg-transparent text-gray-600 cursor-pointer relative transition-all duration-200 hover:bg-black/5 hover:rounded-lg group animate-count-infinite" - onclick="sendLike('{{ .File.UniqueID | urlize }}')" - id="like-btn-{{ .File.UniqueID | urlize }}" + onclick="sendLike('{{ $pageID }}')" + id="like-btn-{{ $pageID }}" title="{{ T "actionMenu.like" }}" data-liked-title="{{ T "actionMenu.liked" }}" data-tooltip> <img src="/img/heart-organ.svg" width="20" height="20" alt="{{ T "actionMenu.like" }}" - class="brightness-40 flex-shrink-0 transition-all duration-300 dark:brightness-70 dark:contrast-120" id="like-icon-{{ .File.UniqueID | urlize }}" /> - <span class="text-xs font-bold text-gray-600 min-w-4 text-center bg-white/90 rounded-xl px-1.5 py-0.5 shadow-sm dark:text-gray-400 dark:bg-black/70" id="like-count-{{ .File.UniqueID | urlize }}">...</span> + class="brightness-40 flex-shrink-0 transition-all duration-300 dark:brightness-70 dark:contrast-120" id="like-icon-{{ $pageID }}" /> + <span class="text-xs font-bold text-gray-600 min-w-4 text-center bg-white/90 rounded-xl px-1.5 py-0.5 shadow-sm dark:text-gray-400 dark:bg-black/70" id="like-count-{{ $pageID }}">...</span> </button> </div> @@ -24,3 +25,114 @@ color: #8B0000 !important; } </style> + +<script> +document.addEventListener('DOMContentLoaded', () => { + // get page ID to fetch and update like data + const pageId = "{{ $pageID }}"; + + // get required elements + const elements = { + count: document.getElementById("like-count-" + pageId), + btn: document.getElementById("like-btn-" + pageId), + container: document.getElementById("like-container-" + pageId), + icon: document.getElementById("like-icon-" + pageId) + }; + + // return if no page ID found or not in production or no elements + if ( + !pageId || window.location.host !== 'www.geedea.pro' || + !elements.count || !elements.btn || !elements.container + ) return; + + function setLikedState() { + // update classes + elements.icon?.classList.add('liked-heart'); + elements.icon?.classList.add('animate-heart-beat'); + elements.count?.classList.add('liked-count'); + elements.btn.disabled = true; + elements.container.classList.add('liked'); + // update tooltip + elements.btn.setAttribute('title', + elements.container.classList.contains('liked') + ? elements.btn.getAttribute('data-liked-title') + : '为这篇文章献上心脏' + ); + } + + async function fetchLikes() { + try { + const response = await fetch("https://like.guhub.cn/like/" + pageId); + const data = await response.json(); + elements.count.innerText = data.likes ?? 0; + if (localStorage.getItem("liked_" + pageId)) { + setLikedState(); + updateOrganFarm(data.likes); + } + } catch(e) { + console.log(e); + elements.count.innerText = "❌"; + } + } + + window.sendLike = async function(pageId) { + if (localStorage.getItem("liked_" + pageId)) return; + + try { + const response = await fetch("https://like.guhub.cn/like/" + pageId, { method: "POST" }); + const data = await response.json(); + localStorage.setItem("liked_" + pageId, "1"); + elements.count.innerText = data.likes ?? 1; + updateOrganFarm(data.likes); + setLikedState(); + } catch(e) { + console.log(e); + alert("点赞失败,请稍后再试"); + } + }; + + function updateOrganFarm(count) { + const organFarm = document.getElementById("organ-farm"); + const organCount = count || parseInt(elements.count.innerText); + const organImages = [ + "https://image.guhub.cn/picgo2025/oragn-3.png", + "https://image.guhub.cn/picgo2025/organ-1.png", + "https://image.guhub.cn/picgo2025/organ-2.png" + ]; + + if (!organFarm) return; + if (organFarm.classList.contains("hidden")) { + organFarm.classList.remove("hidden"); + organFarm.classList.add("animate-slide-in-down"); + } + + organFarm.innerHTML = ""; + for (let i = 0; i < organCount; i++) { + const img = document.createElement("img"); + img.className = "transition hover:scale-150 dark:invert"; + + const sizeRange = [6, 10]; + const size = Math.floor(Math.random() * (sizeRange[1] - sizeRange[0] + 1)) + sizeRange[0]; + img.classList.add(`w-${size}`, `h-${size}`); + + img.src = organImages[Math.floor(Math.random() * organImages.length)]; + // randomly offset and rotate + img.classList.add("rotate-" + Math.floor(Math.random() * 5)); + img.classList.add("translate-x-" + Math.floor(Math.random() * 5)); + img.classList.add("translate-y-" + Math.floor(Math.random() * 5)); + img.classList.add("opacity" + (100 - Math.floor(Math.random() * 3)*10)); + + organFarm.appendChild(img); + } + } + + // fetch likes when action menu is visible + const observer = new IntersectionObserver((entries, obs) => { + if (entries[0].isIntersecting) { + fetchLikes(); + obs.disconnect(); + } + }); + observer.observe(document.getElementById("action-menu")); +}); +</script> diff --git a/layouts/_partials/scripts.html b/layouts/_partials/scripts.html index 2b84ea3c..edfb51b3 100644 --- a/layouts/_partials/scripts.html +++ b/layouts/_partials/scripts.html @@ -14,7 +14,3 @@ {{ partial "scripts/littlefoot-js" . }} {{ partial "scripts/libra-js" . }} {{ partial "scripts/aplayer-js" . }} - -{{ if .File }} - {{ partial "scripts/action-menu-js" (.File.UniqueID | urlize) }} -{{ end }} diff --git a/layouts/_partials/scripts/action-menu-js.html b/layouts/_partials/scripts/action-menu-js.html deleted file mode 100644 index dca99369..00000000 --- a/layouts/_partials/scripts/action-menu-js.html +++ /dev/null @@ -1,170 +0,0 @@ -{{ $pageId := . }} -<script defer> -/** - * ================= - * Initialize all Action Menu features - * - comment count - * - like - * ================= - */ - -/** - * ================= - * Fetch and set Comment Count - * ================= - */ - -function initializeCommentCount() { - const commentCountEl = document.getElementById('comment-count'); - if (!commentCountEl) return; - - const observer = new MutationObserver(() => { - // check if giscus is loaded - const giscusIframe = document.querySelector('iframe.giscus-frame'); - if (!giscusIframe) return; - - giscusIframe.addEventListener('load', () => { - // receive message from giscus iframe - window.addEventListener('message', function(event) { - if (event.origin !== 'https://giscus.app') return; - const giscusData = event.data; - if (giscusData.giscus?.discussion) { - const discussion = giscusData.giscus.discussion; - const totalComments = (discussion.totalCommentCount || 0) + (discussion.totalReplyCount || 0); - // set comment count - commentCountEl.textContent = totalComments.toString(); - // if no discussion found, set to 0 - } else commentCountEl.textContent = '0'; - }); - }); - - observer.disconnect(); - }); - observer.observe(document.body, { childList: true, subtree: true }); -} - -/** - * ================= - * Initialize like functionality - * ================= - */ - -function initializeLikes(pageId) { - if (window.location.host !== 'www.geedea.pro') return; - - const elements = { - count: document.getElementById("like-count-" + pageId), - btn: document.getElementById("like-btn-" + pageId), - container: document.getElementById("like-container-" + pageId), - icon: document.getElementById("like-icon-" + pageId) - }; - - if (!elements.count || !elements.btn || !elements.container) return; - - function setLikedState() { - // update classes - elements.icon?.classList.add('liked-heart'); - elements.icon?.classList.add('animate-heart-beat'); - elements.count?.classList.add('liked-count'); - elements.btn.disabled = true; - elements.container.classList.add('liked'); - // update tooltip - elements.btn.setAttribute('title', - elements.container.classList.contains('liked') - ? elements.btn.getAttribute('data-liked-title') - : '为这篇文章献上心脏' - ); - } - - async function fetchLikes() { - try { - const response = await fetch("https://like.guhub.cn/like/" + pageId); - const data = await response.json(); - elements.count.innerText = data.likes ?? 0; - if (localStorage.getItem("liked_" + pageId)) { - setLikedState(); - updateOrganFarm(data.likes); - } - } catch(e) { - console.log(e); - elements.count.innerText = "❌"; - } - } - - window.sendLike = async function(pageId) { - if (localStorage.getItem("liked_" + pageId)) return; - - try { - const response = await fetch("https://like.guhub.cn/like/" + pageId, { method: "POST" }); - const data = await response.json(); - localStorage.setItem("liked_" + pageId, "1"); - elements.count.innerText = data.likes ?? 1; - updateOrganFarm(data.likes); - setLikedState(); - } catch(e) { - console.log(e); - alert("点赞失败,请稍后再试"); - } - }; - - function updateOrganFarm(count) { - const organFarm = document.getElementById("organ-farm"); - const organCount = count || parseInt(elements.count.innerText); - const organImages = [ - "https://image.guhub.cn/picgo2025/oragn-3.png", - "https://image.guhub.cn/picgo2025/organ-1.png", - "https://image.guhub.cn/picgo2025/organ-2.png" - ]; - - if (!organFarm) return; - if (organFarm.classList.contains("hidden")) { - organFarm.classList.remove("hidden"); - organFarm.classList.add("animate-slide-in-down"); - } - - organFarm.innerHTML = ""; - for (let i = 0; i < organCount; i++) { - const img = document.createElement("img"); - img.className = "transition hover:scale-150 dark:invert"; - - const sizeRange = [6, 10]; - const size = Math.floor(Math.random() * (sizeRange[1] - sizeRange[0] + 1)) + sizeRange[0]; - img.classList.add(`w-${size}`, `h-${size}`); - - img.src = organImages[Math.floor(Math.random() * organImages.length)]; - // randomly offset and rotate - img.classList.add("rotate-" + Math.floor(Math.random() * 5)); - img.classList.add("translate-x-" + Math.floor(Math.random() * 5)); - img.classList.add("translate-y-" + Math.floor(Math.random() * 5)); - img.classList.add("opacity" + (100 - Math.floor(Math.random() * 3)*10)); - - organFarm.appendChild(img); - } - } - - // fetch likes when action menu is visible - const observer = new IntersectionObserver((entries, obs) => { - if (entries[0].isIntersecting) { - fetchLikes(); - obs.disconnect(); - } - }); - observer.observe(document.getElementById("action-menu")); -} - -/** - * ================= - * Load features after DOMContentLoaded - * ================= - */ - -function initializeFeatures() { - const pageId = "{{ $pageId }}"; - if (!pageId) return; - - initializeLikes(pageId); - initializeCommentCount(); -} - -document.addEventListener('DOMContentLoaded', initializeFeatures); -</script> |
