mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Improve "heart" ui
This commit is contained in:
@@ -619,6 +619,7 @@ public class ChannelDirectoryService {
|
|||||||
return applyMarketplaceHearts(entries, sessionUsername);
|
return applyMarketplaceHearts(entries, sessionUsername);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
public Optional<ScriptMarketplaceEntry> toggleMarketplaceHeart(String scriptId, String sessionUsername) {
|
public Optional<ScriptMarketplaceEntry> toggleMarketplaceHeart(String scriptId, String sessionUsername) {
|
||||||
if (scriptId == null || scriptId.isBlank() || sessionUsername == null || sessionUsername.isBlank()) {
|
if (scriptId == null || scriptId.isBlank() || sessionUsername == null || sessionUsername.isBlank()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
|||||||
@@ -302,17 +302,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-hearts {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
color: rgba(248, 113, 113, 0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-hearts i {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-actions {
|
.modal .modal-inner .marketplace-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -322,12 +311,57 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal .modal-inner .marketplace-heart-count {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: rgba(248, 113, 113, 0.9);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-inner .marketplace-heart-count i {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-heart-button.active {
|
.modal .modal-inner .marketplace-heart-button.active {
|
||||||
border-color: rgba(248, 113, 113, 0.5);
|
border-color: rgba(248, 113, 113, 0.5);
|
||||||
background: rgba(248, 113, 113, 0.12);
|
background: rgba(248, 113, 113, 0.12);
|
||||||
color: #fecdd3;
|
color: #fecdd3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal .modal-inner .marketplace-heart-button.is-animating .icon {
|
||||||
|
animation: marketplace-heart-pop 320ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-inner .marketplace-heart-count.is-animating {
|
||||||
|
animation: marketplace-heart-count 260ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes marketplace-heart-pop {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
45% {
|
||||||
|
transform: scale(1.25) rotate(-8deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes marketplace-heart-count {
|
||||||
|
0% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
45% {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-empty,
|
.modal .modal-inner .marketplace-empty,
|
||||||
.modal .modal-inner .marketplace-loading {
|
.modal .modal-inner .marketplace-loading {
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
|
|||||||
@@ -782,33 +782,39 @@ export function createCustomAssetModal({
|
|||||||
description.textContent = entry.description || "No description provided.";
|
description.textContent = entry.description || "No description provided.";
|
||||||
const meta = document.createElement("small");
|
const meta = document.createElement("small");
|
||||||
meta.textContent = entry.broadcaster ? `By ${entry.broadcaster}` : "";
|
meta.textContent = entry.broadcaster ? `By ${entry.broadcaster}` : "";
|
||||||
const hearts = document.createElement("small");
|
|
||||||
hearts.className = "marketplace-hearts";
|
|
||||||
const heartIcon = document.createElement("i");
|
|
||||||
heartIcon.className = "fa-solid fa-heart";
|
|
||||||
const heartCount = document.createElement("span");
|
|
||||||
heartCount.textContent = String(entry.heartsCount ?? 0);
|
|
||||||
hearts.appendChild(heartIcon);
|
|
||||||
hearts.appendChild(heartCount);
|
|
||||||
content.appendChild(title);
|
content.appendChild(title);
|
||||||
content.appendChild(description);
|
content.appendChild(description);
|
||||||
content.appendChild(meta);
|
content.appendChild(meta);
|
||||||
content.appendChild(hearts);
|
|
||||||
|
|
||||||
const actions = document.createElement("div");
|
const actions = document.createElement("div");
|
||||||
actions.className = "marketplace-actions";
|
actions.className = "marketplace-actions";
|
||||||
|
const heartCountWrapper = document.createElement("div");
|
||||||
|
heartCountWrapper.className = "marketplace-heart-count";
|
||||||
|
const heartCountIcon = document.createElement("i");
|
||||||
|
heartCountIcon.className = "fa-solid fa-heart";
|
||||||
|
const heartCount = document.createElement("span");
|
||||||
|
heartCount.textContent = String(entry.heartsCount ?? 0);
|
||||||
|
heartCountWrapper.appendChild(heartCountIcon);
|
||||||
|
heartCountWrapper.appendChild(heartCount);
|
||||||
const heartButton = document.createElement("button");
|
const heartButton = document.createElement("button");
|
||||||
heartButton.type = "button";
|
heartButton.type = "button";
|
||||||
heartButton.className = "icon-button marketplace-heart-button";
|
heartButton.className = "icon-button marketplace-heart-button";
|
||||||
heartButton.setAttribute("aria-label", "Heart script");
|
heartButton.setAttribute("aria-label", "Heart script");
|
||||||
updateMarketplaceHeartButton(heartButton, entry);
|
updateMarketplaceHeartButton(heartButton, entry);
|
||||||
heartButton.addEventListener("click", () => toggleMarketplaceHeart(entry, heartCount));
|
heartButton.addEventListener("click", () =>
|
||||||
|
toggleMarketplaceHeart(entry, {
|
||||||
|
button: heartButton,
|
||||||
|
count: heartCount,
|
||||||
|
countWrapper: heartCountWrapper,
|
||||||
|
})
|
||||||
|
);
|
||||||
const importButton = document.createElement("button");
|
const importButton = document.createElement("button");
|
||||||
importButton.type = "button";
|
importButton.type = "button";
|
||||||
importButton.className = "icon-button";
|
importButton.className = "icon-button";
|
||||||
importButton.setAttribute("aria-label", "Import script");
|
importButton.setAttribute("aria-label", "Import script");
|
||||||
importButton.innerHTML = '<i class="icon fa-solid fa-cloud-download"></i>';
|
importButton.innerHTML = '<i class="icon fa-solid fa-cloud-download"></i>';
|
||||||
importButton.addEventListener("click", () => importMarketplaceScript(entry));
|
importButton.addEventListener("click", () => importMarketplaceScript(entry));
|
||||||
|
actions.appendChild(heartCountWrapper);
|
||||||
actions.appendChild(heartButton);
|
actions.appendChild(heartButton);
|
||||||
actions.appendChild(importButton);
|
actions.appendChild(importButton);
|
||||||
|
|
||||||
@@ -855,10 +861,11 @@ export function createCustomAssetModal({
|
|||||||
button.innerHTML = `<i class="icon ${iconClass}"></i>`;
|
button.innerHTML = `<i class="icon ${iconClass}"></i>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMarketplaceHeart(entry, countElement) {
|
function toggleMarketplaceHeart(entry, elements = {}) {
|
||||||
if (!entry?.id) {
|
if (!entry?.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
animateMarketplaceHeart(elements);
|
||||||
fetch(`/api/marketplace/scripts/${entry.id}/heart`, {
|
fetch(`/api/marketplace/scripts/${entry.id}/heart`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -872,10 +879,14 @@ export function createCustomAssetModal({
|
|||||||
.then((updated) => {
|
.then((updated) => {
|
||||||
entry.heartsCount = updated.heartsCount ?? entry.heartsCount ?? 0;
|
entry.heartsCount = updated.heartsCount ?? entry.heartsCount ?? 0;
|
||||||
entry.hearted = updated.hearted ?? entry.hearted;
|
entry.hearted = updated.hearted ?? entry.hearted;
|
||||||
if (countElement) {
|
if (elements.count) {
|
||||||
countElement.textContent = String(entry.heartsCount ?? 0);
|
elements.count.textContent = String(entry.heartsCount ?? 0);
|
||||||
}
|
}
|
||||||
renderMarketplace();
|
if (elements.button) {
|
||||||
|
updateMarketplaceHeartButton(elements.button, entry);
|
||||||
|
}
|
||||||
|
animateMarketplaceHeart(elements);
|
||||||
|
setTimeout(() => renderMarketplace(), 300);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -883,6 +894,19 @@ export function createCustomAssetModal({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function animateMarketplaceHeart({ button, countWrapper } = {}) {
|
||||||
|
if (button) {
|
||||||
|
button.classList.remove("is-animating");
|
||||||
|
void button.offsetWidth;
|
||||||
|
button.classList.add("is-animating");
|
||||||
|
}
|
||||||
|
if (countWrapper) {
|
||||||
|
countWrapper.classList.remove("is-animating");
|
||||||
|
void countWrapper.offsetWidth;
|
||||||
|
countWrapper.classList.add("is-animating");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function debounce(fn, wait = 150) {
|
function debounce(fn, wait = 150) {
|
||||||
let timeout;
|
let timeout;
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user