fix: sync server-rendered watchlist state to client
This commit is contained in:
@@ -316,8 +316,8 @@ func (h *AnimeHandler) HandleScheduleSection(c *gin.Context) {
|
||||
watchlistMap := h.watchlistMapForAnimes(c.Request.Context(), userID, animes)
|
||||
|
||||
c.HTML(http.StatusOK, "schedule.gohtml", gin.H{
|
||||
"_fragment": "schedule_section",
|
||||
"Animes": animes,
|
||||
"_fragment": "schedule_section",
|
||||
"Animes": animes,
|
||||
"WatchlistMap": watchlistMap,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,3 +58,13 @@ body {
|
||||
background-color: var(--color-background);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
[data-watchlist-toggle] .watchlist-icon,
|
||||
[data-watchlist-toggle] .watchlist-icon path {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
[data-watchlist-toggle][data-watchlist-state='in'] .watchlist-icon,
|
||||
[data-watchlist-toggle][data-watchlist-state='in'] .watchlist-icon path {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ const syncIconsForId = (id: number): void => {
|
||||
const malId = toInt(button.dataset.malId);
|
||||
if (malId !== id) return;
|
||||
button.classList.toggle('in-watchlist', shouldBeInWatchlist);
|
||||
button.dataset.watchlistState = shouldBeInWatchlist ? 'in' : 'out';
|
||||
button.setAttribute(
|
||||
'aria-label',
|
||||
shouldBeInWatchlist ? 'Remove from Watchlist' : 'Add to Watchlist'
|
||||
@@ -109,8 +110,18 @@ const closeClosestDropdown = (from: HTMLElement): void => {
|
||||
});
|
||||
};
|
||||
|
||||
const toggleWatchlist = async (id: number, title: string): Promise<void> => {
|
||||
const toggleWatchlist = async (
|
||||
id: number,
|
||||
title: string,
|
||||
renderedState: string | undefined
|
||||
): Promise<void> => {
|
||||
if (inflight.has(id)) return;
|
||||
if (renderedState === 'in') {
|
||||
watchlistIds.add(id);
|
||||
} else if (renderedState === 'out') {
|
||||
watchlistIds.delete(id);
|
||||
}
|
||||
|
||||
const isInWatchlist = watchlistIds.has(id);
|
||||
|
||||
setBusy(id, true);
|
||||
@@ -250,6 +261,20 @@ const initWatchlist = (ids: number[]): void => {
|
||||
});
|
||||
};
|
||||
|
||||
const getRenderedWatchlistIds = (): number[] => {
|
||||
const ids = new Set<number>();
|
||||
|
||||
document
|
||||
.querySelectorAll<HTMLElement>('[data-watchlist-toggle][data-watchlist-state="in"][data-mal-id]')
|
||||
.forEach(button => {
|
||||
const id = toInt(button.dataset.malId);
|
||||
if (id === null) return;
|
||||
ids.add(id);
|
||||
});
|
||||
|
||||
return Array.from(ids);
|
||||
};
|
||||
|
||||
const onReady = (fn: () => void): void => {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', fn, { once: true });
|
||||
@@ -272,7 +297,7 @@ const installDelegatedHandlers = (): void => {
|
||||
const id = toInt(toggleButton.getAttribute('data-mal-id') ?? undefined);
|
||||
if (id === null) return;
|
||||
const title = toggleButton.getAttribute('data-watchlist-title') ?? 'anime';
|
||||
void toggleWatchlist(id, title);
|
||||
void toggleWatchlist(id, title, toggleButton.dataset.watchlistState);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -322,5 +347,10 @@ onReady(() => {
|
||||
}
|
||||
}
|
||||
|
||||
const renderedWatchlistIds = getRenderedWatchlistIds();
|
||||
if (renderedWatchlistIds.length > 0) {
|
||||
initWatchlist(renderedWatchlistIds);
|
||||
}
|
||||
|
||||
installDelegatedHandlers();
|
||||
});
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
data-watchlist-toggle
|
||||
data-mal-id="{{$anime.MalID}}"
|
||||
data-watchlist-title="{{$anime.DisplayTitle}}"
|
||||
data-watchlist-state="{{if $isWatchlist}}in{{else}}out{{end}}"
|
||||
class="text-accent hover:text-accent/80 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent disabled:opacity-50 {{if $isWatchlist}}in-watchlist{{end}}"
|
||||
aria-label="{{if $isWatchlist}}Remove from Watchlist{{else}}Add to Watchlist{{end}}"
|
||||
>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
data-watchlist-toggle
|
||||
data-mal-id="{{$anime.MalID}}"
|
||||
data-watchlist-title="{{$anime.DisplayTitle}}"
|
||||
data-watchlist-state="{{if (index $.WatchlistMap $anime.MalID)}}in{{else}}out{{end}}"
|
||||
class="shrink-0 text-accent hover:text-accent/80 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent disabled:opacity-50 {{if (index $.WatchlistMap $anime.MalID)}}in-watchlist{{end}}"
|
||||
aria-label="{{if (index $.WatchlistMap $anime.MalID)}}Remove from Watchlist{{else}}Add to Watchlist{{end}}"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user