feat: natural toast messages with anime title

This commit is contained in:
2026-05-15 02:22:20 +02:00
parent 74e2aa50fd
commit aa8df4fd54
4 changed files with 36 additions and 20 deletions

View File

@@ -31,13 +31,15 @@
html[data-theme="dark"] .theme-icon-dark { display: none; }
html[data-theme="dark"] .theme-icon-light { display: block; }
html[data-theme="light"] .theme-icon-light { display: none; }
html[data-theme="light"] .theme-icon-dark { display: block; }
html[data-theme="light"] .theme-icon-dark { display: block; }
</style>
<script type="module" src="/dist/static/theme.js" defer></script>
<template id="toast-template">
<div class="toast bg-foreground/10 border border-border flex items-center gap-3 px-4 py-3 shadow-lg transform transition-all duration-300 translate-y-2 opacity-0">
<span class="toast-message text-sm text-foreground"></span>
<button class="toast-close ml-2 opacity-50 hover:opacity-100" aria-label="Close">
<div class="toast pointer-events-auto w-[22rem] max-w-[calc(100vw-2rem)] bg-background shadow-2xl ring-1 ring-black/5 flex items-start gap-3 px-4 py-3 transform transition-all duration-300 translate-y-2 opacity-0">
<div class="min-w-0 flex-1">
<div class="toast-message text-sm font-medium text-foreground leading-snug"></div>
</div>
<button class="toast-close -mr-1 -mt-1 rounded-md p-1.5 opacity-70 hover:opacity-100 hover:bg-foreground/10 focus:outline-none focus:ring-2 focus:ring-ring" aria-label="Close">
<svg class="size-4 text-foreground-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
@@ -101,7 +103,7 @@
}
}
function toggleWatchlist(id, btn) {
function toggleWatchlist(id, title, btn) {
// determine action based on current watchlist state
const isInWatchlist = watchlistIds.has(id)
const url = isInWatchlist ? `/api/watchlist/${id}` : '/api/watchlist'
@@ -118,7 +120,7 @@
watchlistIds.delete(id)
btn.classList.remove('in-watchlist')
btn.setAttribute('aria-label', 'Add to Watchlist')
if (window.showToast) showToast({ message: 'Removed from watchlist' })
if (window.showToast) showToast({ message: `Removed ${title} from watchlist` })
// Update dropdown status if on anime page
syncWatchlistDropdown(id, false)
@@ -126,7 +128,7 @@
watchlistIds.add(id)
btn.classList.add('in-watchlist')
btn.setAttribute('aria-label', 'Remove from Watchlist')
if (window.showToast) showToast({ message: 'Added to watchlist' })
if (window.showToast) showToast({ message: `Added ${title} to watchlist` })
// Update dropdown status if on anime page
syncWatchlistDropdown(id, true)
@@ -168,12 +170,15 @@ if (window.showToast) showToast({ message: 'Something went wrong' })
watchlistIds.delete(id)
const card = btn.closest('.group').parentElement
if (card) card.remove()
if (window.showToast) showToast({ message: 'Removed from watchlist' })
setTimeout(() => location.reload(), 100)
} else {
if (window.showToast) showToast({ message: 'Failed to update watchlist' })
}
})
}
function updateWatchlist(id, status, display, btn) {
function updateWatchlist(id, status, display, title, btn) {
fetch('/api/watchlist', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -196,17 +201,22 @@ if (window.showToast) showToast({ message: 'Something went wrong' })
const dropdown = btn.closest('ui-dropdown');
if (dropdown) dropdown.close();
});
if (window.showToast) showToast({ message: `Marked ${title} as ${display}` });
} else {
if (window.showToast) showToast({ message: 'Failed to update watchlist' });
}
}).catch(() => {
if (window.showToast) showToast({ message: 'Something went wrong' });
});
}
function removeWatchlist(id, btn) {
function removeWatchlist(id, title, btn) {
fetch('/api/watchlist/' + id, { method: 'DELETE' }).then(res => {
if (res.ok) {
watchlistIds.delete(id);
document.getElementById('watchlist-status-display-' + id).textContent = 'Add to Watchlist';
syncRemoveButtonVisibility(id);
if (window.showToast) showToast({ message: 'Removed from watchlist' });
if (window.showToast) showToast({ message: `Removed ${title} from watchlist` });
document.querySelectorAll('.watchlist-icon').forEach(function(icon) {
const button = icon.closest('button');
if (button) {
@@ -220,7 +230,11 @@ if (window.showToast) showToast({ message: 'Something went wrong' })
const dropdown = btn.closest('ui-dropdown');
if (dropdown) dropdown.close();
}
} else {
if (window.showToast) showToast({ message: 'Failed to update watchlist' });
}
}).catch(() => {
if (window.showToast) showToast({ message: 'Something went wrong' });
});
}
</script>

View File

@@ -35,7 +35,7 @@
{{end}}
<div class="mt-auto flex items-center justify-start pb-2 pl-2">
<button type="button" data-mal-id="{{$anime.MalID}}" onclick="event.preventDefault(); event.stopPropagation(); toggleWatchlist({{$anime.MalID}}, this)" class="text-accent hover:text-accent/80 transition-colors focus:outline-none {{if $isWatchlist}}in-watchlist{{end}}" aria-label="{{if $isWatchlist}}Remove from Watchlist{{else}}Add to Watchlist{{end}}">
<button type="button" data-mal-id="{{$anime.MalID}}" onclick="event.preventDefault(); event.stopPropagation(); toggleWatchlist({{$anime.MalID}}, '{{$anime.DisplayTitle}}', this)" class="text-accent hover:text-accent/80 transition-colors focus:outline-none {{if $isWatchlist}}in-watchlist{{end}}" aria-label="{{if $isWatchlist}}Remove from Watchlist{{else}}Add to Watchlist{{end}}">
<svg class="size-6 shadow-black drop-shadow-md watchlist-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" /></svg>
</button>
</div>

View File

@@ -23,21 +23,22 @@
<div data-content class="hidden absolute z-50 min-w-[160px] bg-background-button rounded-none shadow-2xl left-0 top-full mt-2">
<div class="flex flex-col py-1">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'watching', 'Watching', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'watching', 'Watching', '{{$anime.DisplayTitle}}', this)">
<span class="font-medium text-sm text-foreground">Watching</span>
</button>
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'completed', 'Completed', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'completed', 'Completed', '{{$anime.DisplayTitle}}', this)">
<span class="font-medium text-sm text-foreground">Completed</span>
</button>
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'plan_to_watch', 'Plan to Watch', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'plan_to_watch', 'Plan to Watch', '{{$anime.DisplayTitle}}', this)">
<span class="font-medium text-sm text-foreground">Plan to Watch</span>
</button>
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'dropped', 'Dropped', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-foreground/10 focus:bg-foreground/10" onclick="updateWatchlist({{$anime.MalID}}, 'dropped', 'Dropped', '{{$anime.DisplayTitle}}', this)">
<span class="font-medium text-sm text-foreground">Dropped</span>
</button>
{{template "watchlist_remove_button" dict
"ID" $anime.MalID
"Title" $anime.DisplayTitle
"ContainerClass" "hidden"
"DividerClass" "my-1 h-px bg-border"
"ButtonClass" "flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-red-500/10 focus:bg-red-500/10"
@@ -55,7 +56,7 @@
{{define "watchlist_remove_button"}}
<div id="remove-watchlist-container-{{.ID}}" class="{{.ContainerClass}}">
<button class="{{.ButtonClass}}" onclick="removeWatchlist({{.ID}}, this)">
<button class="{{.ButtonClass}}" onclick="removeWatchlist({{.ID}}, '{{.Title}}', this)">
<span class="{{.SpanClass}}">Remove from Watchlist</span>
</button>
</div>

View File

@@ -25,20 +25,21 @@
</div>
<div data-content class="hidden absolute z-50 min-w-40 bg-background-button shadow-2xl right-0 top-full mt-2">
<div class="flex flex-col py-1">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'watching', 'Watching', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'watching', 'Watching', '{{$anime.Title}}', this)">
<span class="text-sm text-foreground">Watching</span>
</button>
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'completed', 'Completed', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'completed', 'Completed', '{{$anime.Title}}', this)">
<span class="text-sm text-foreground">Completed</span>
</button>
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'plan_to_watch', 'Plan to Watch', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'plan_to_watch', 'Plan to Watch', '{{$anime.Title}}', this)">
<span class="text-sm text-foreground">Plan to Watch</span>
</button>
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'dropped', 'Dropped', this)">
<button class="flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-surface-hover" onclick="updateWatchlist({{$anime.MalID}}, 'dropped', 'Dropped', '{{$anime.Title}}', this)">
<span class="text-sm text-foreground">Dropped</span>
</button>
{{template "watchlist_remove_button" dict
"ID" $anime.MalID
"Title" $anime.Title
"ContainerClass" "hidden"
"DividerClass" "border-t border-border my-1"
"ButtonClass" "flex w-full items-center px-5 py-2.5 transition-colors focus:outline-none hover:bg-red-500/10"