Video: wire the eye/get button onto every card surface

A shared VideoGet.cardButton({kind,tmdbId,libraryId,title,poster,status,source})
picks the right control (person->eye, movie->get, show->eye when airing/unknown
else get) so every surface stays consistent. Injected into person filmography
(known-for + credits), search results (titles + people), and detail-page cast +
similar/collection rails. Each renderer hydrates watched state after render;
card roots get position:relative so the button anchors; reveal on hover.
This commit is contained in:
BoulderBadgeDad 2026-06-16 10:41:07 -07:00
parent 964675443b
commit 240e386871
5 changed files with 51 additions and 6 deletions

View file

@ -303,11 +303,14 @@
'<span class="vd-cast-name">' + esc(p.name) + '</span>' +
(p.character ? '<span class="vd-cast-char">' + esc(p.character) + '</span>' : '');
// Clickable → in-app person page when we have a TMDB person id.
var cb = (p.tmdb_id && window.VideoGet)
? VideoGet.cardButton({ kind: 'person', tmdbId: p.tmdb_id, title: p.name, poster: p.photo }) : '';
return p.tmdb_id
? '<a class="vd-cast-card vd-cast-card--link" href="/video-detail/tmdb/person/' + p.tmdb_id +
'" data-vd-person="' + p.tmdb_id + '">' + inner + '</a>'
'" data-vd-person="' + p.tmdb_id + '">' + cb + inner + '</a>'
: '<div class="vd-cast-card">' + inner + '</div>';
}).join('');
if (window.VideoWatchlist) VideoWatchlist.hydrate(castHost);
}
}
@ -440,8 +443,10 @@
: '<span class="vd-sim-poster vd-sim-poster--ph">🎬</span>';
var simKind = s.kind === 'movie' ? 'movie' : 'show';
var yr = s.year ? '<span class="vd-sim-year">' + esc(s.year) + '</span>' : '';
var cb = window.VideoGet ? VideoGet.cardButton({ kind: simKind, tmdbId: s.tmdb_id,
title: s.title, poster: s.poster, status: s.status, source: 'tmdb' }) : '';
return '<a class="vd-sim-card" href="/video-detail/tmdb/' + simKind + '/' + s.tmdb_id +
'" data-vd-sim="' + simKind + '" data-vd-sim-id="' + s.tmdb_id + '">' +
'" data-vd-sim="' + simKind + '" data-vd-sim-id="' + s.tmdb_id + '">' + cb +
poster + '<span class="vd-sim-title">' + esc(s.title) + '</span>' + yr + '</a>';
}
function renderRow(sectionSel, hostSel, items) {
@ -450,6 +455,7 @@
if (!items || !items.length) { sec.hidden = true; return; }
sec.hidden = false;
host.innerHTML = items.map(simCard).join('');
if (window.VideoWatchlist) VideoWatchlist.hydrate(host);
}
function renderExtras(kind, id, ex) {
@ -516,6 +522,7 @@
cs.hidden = false;
if (ct) ct.textContent = coll.name || 'Collection';
ch.innerHTML = coll.items.map(simCard).join('');
if (window.VideoWatchlist) VideoWatchlist.hydrate(ch);
} else { cs.hidden = true; }
}

View file

@ -156,5 +156,28 @@
});
}, true);
window.VideoGet = { btn: btn, isAiring: isAiring, open: openModal };
// Pick the right overlay control for any card: person → eye; movie → get;
// show → eye when airing (or status unknown = still followable), get when
// confirmed ended. One helper so every surface stays consistent.
function cardButton(o) {
if (!o || !o.kind) return '';
if (o.kind === 'person') {
return window.VideoWatchlist
? VideoWatchlist.btn({ kind: 'person', tmdbId: o.tmdbId, title: o.title, poster: o.poster }) : '';
}
if (o.kind === 'show') {
var airing = !o.status ? true : isAiring(o.status);
if (airing && o.tmdbId && window.VideoWatchlist) {
return VideoWatchlist.btn({ kind: 'show', tmdbId: o.tmdbId, title: o.title,
poster: o.poster, libraryId: o.libraryId });
}
return btn({ kind: 'show', source: o.source || 'tmdb', openId: o.libraryId || o.tmdbId, title: o.title });
}
if (o.kind === 'movie') {
return btn({ kind: 'movie', source: o.source || 'tmdb', openId: o.libraryId || o.tmdbId, title: o.title });
}
return '';
}
window.VideoGet = { btn: btn, isAiring: isAiring, open: openModal, cardButton: cardButton };
})();

View file

@ -48,8 +48,10 @@
var id = owned ? c.library_id : c.tmdb_id;
var href = '/video-detail/' + source + '/' + c.kind + '/' + id;
var sub = [c.year, c.role].filter(Boolean).join(' · ');
var cb = window.VideoGet ? VideoGet.cardButton({ kind: c.kind, tmdbId: c.tmdb_id,
libraryId: c.library_id, title: c.title, poster: c.poster, status: c.status, source: source }) : '';
return '<a class="vsr-card" href="' + href + '" ' +
'data-vp-open="' + c.kind + '" data-vp-source="' + source + '" data-vp-cid="' + id + '">' +
'data-vp-open="' + c.kind + '" data-vp-source="' + source + '" data-vp-cid="' + id + '">' + cb +
'<div class="vsr-poster">' + img + ribbon +
'<span class="vsr-peek" aria-hidden="true">i</span></div>' +
'<div class="vsr-info"><span class="vsr-name" title="' + esc(c.title) + '">' + esc(c.title) +
@ -130,6 +132,7 @@
var top = filtered().slice(0, 10);
section.hidden = top.length < 3; // only worth a rail if there are a few
host.innerHTML = top.map(creditCard).join('');
if (window.VideoWatchlist) VideoWatchlist.hydrate(host);
}
function renderCredits() {
@ -144,6 +147,7 @@
credits.sort(function (a, b) { return (b.date || '').localeCompare(a.date || ''); });
}
host.innerHTML = credits.map(creditCard).join('');
if (window.VideoWatchlist) VideoWatchlist.hydrate(host);
if (empty) {
empty.hidden = credits.length > 0;
if (!credits.length) {

View file

@ -52,8 +52,10 @@
var id = owned ? it.library_id : it.tmdb_id;
var href = '/video-detail/' + source + '/' + it.kind + '/' + id;
var sub = [it.year, it.kind === 'movie' ? 'Movie' : 'TV'].filter(Boolean).join(' · ');
var cb = window.VideoGet ? VideoGet.cardButton({ kind: it.kind, tmdbId: it.tmdb_id,
libraryId: it.library_id, title: it.title, poster: it.poster, status: it.status, source: source }) : '';
return '<a class="vsr-card" href="' + href + '" ' +
'data-vsr-open="' + it.kind + '" data-vsr-source="' + source + '" data-vsr-id="' + id + '">' +
'data-vsr-open="' + it.kind + '" data-vsr-source="' + source + '" data-vsr-id="' + id + '">' + cb +
'<div class="vsr-poster">' + img + ribbon + rating +
'<span class="vsr-peek" aria-hidden="true">i</span></div>' +
'<div class="vsr-info"><span class="vsr-name" title="' + esc(it.title) + '">' + esc(it.title) +
@ -66,8 +68,10 @@
'onerror="this.outerHTML=\'<div class=&quot;vsr-poster-ph&quot;>👤</div>\'">'
: '<div class="vsr-poster-ph">👤</div>';
var sub = it.known_for ? it.known_for : (it.department || '');
var cb = window.VideoGet ? VideoGet.cardButton({ kind: 'person', tmdbId: it.tmdb_id,
title: it.title, poster: it.poster }) : '';
return '<a class="vsr-card vsr-card--person" href="#" ' +
'data-vsr-open="person" data-vsr-id="' + it.tmdb_id + '">' +
'data-vsr-open="person" data-vsr-id="' + it.tmdb_id + '">' + cb +
'<div class="vsr-poster">' + img + '</div>' +
'<div class="vsr-info vsr-info--center"><span class="vsr-name" title="' + esc(it.title) + '">' +
esc(it.title) + '</span><span class="vsr-sub">' + esc(sub) + '</span></div></a>';
@ -93,6 +97,7 @@
'</div></div>';
});
host.innerHTML = html;
if (window.VideoWatchlist) VideoWatchlist.hydrate(host);
}
// Idle state: a "Trending this week" rail so the page isn't a blank box.
@ -104,6 +109,7 @@
host.innerHTML = '<div class="vsr-group"><h2 class="vsr-group-title">' +
'<span class="vsr-group-ic" aria-hidden="true">🔥</span>Trending this week</h2>' +
'<div class="vsr-grid">' + trendingCache.map(titleCard).join('') + '</div></div>';
if (window.VideoWatchlist) VideoWatchlist.hydrate(host);
}
function loadTrending() {
if (trendingCache !== null) { if (!lastQuery) renderTrending(); return; }

View file

@ -1968,3 +1968,8 @@ body[data-side="video"] #soulsync-toggle { display: none; }
cursor: pointer; outline: none; transition: border-color 0.15s ease; }
.vwlp-sort:focus { border-color: rgba(var(--accent-rgb, 88 101 242), 0.6); }
.vwlp-sort option { background: #16161d; color: #fff; }
/* The eye/get button anchors to these card roots (other surfaces). Position is
additive (no flow change); buttons are absolute top-right within them. */
.vsr-card, .vd-sim-card, .vd-cast-card { position: relative; }
.vd-cast-card:hover .vget-btn { opacity: 1; transform: none; }