fix: TV-Modal schwarze Seite (xREL skip), aufklappbare Sektionen, TV-Staffelinfo
This commit is contained in:
@@ -387,6 +387,47 @@ function DemnächstGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Suche ────────────────────────────────────────────────────────────────────
|
// ── Suche ────────────────────────────────────────────────────────────────────
|
||||||
|
// ── Aufklappbare Film/Serien-Sektion ─────────────────────────────────────────
|
||||||
|
function CollapsibleSection({ label, count, children, defaultOpen=true }) {
|
||||||
|
const [open, setOpen] = useState(defaultOpen);
|
||||||
|
return (
|
||||||
|
<div style={{marginBottom:16}}>
|
||||||
|
<button onClick={()=>setOpen(v=>!v)} style={{
|
||||||
|
display:'flex',alignItems:'center',gap:8,background:'none',border:'none',cursor:'pointer',
|
||||||
|
width:'100%',padding:'6px 0',marginBottom: open?10:0,
|
||||||
|
}}>
|
||||||
|
<span style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:11,letterSpacing:1}}>
|
||||||
|
{label} ({count})
|
||||||
|
</span>
|
||||||
|
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
|
||||||
|
<span style={{color:'rgba(255,255,255,0.3)',fontSize:11,transform:open?'rotate(90deg)':'none',transition:'transform 0.2s'}}>▶</span>
|
||||||
|
</button>
|
||||||
|
{open && children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SectionedGrid({ films, serien, favIds, xrelIds, onToggle, onOpenModal }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{films.length > 0 && (
|
||||||
|
<CollapsibleSection label="🎬 FILME" count={films.length}>
|
||||||
|
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
|
||||||
|
{films.map(m => <MovieCard key={'m'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
|
||||||
|
</div>
|
||||||
|
</CollapsibleSection>
|
||||||
|
)}
|
||||||
|
{serien.length > 0 && (
|
||||||
|
<CollapsibleSection label="📺 SERIEN" count={serien.length}>
|
||||||
|
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
|
||||||
|
{serien.map(m => <MovieCard key={'t'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
|
||||||
|
</div>
|
||||||
|
</CollapsibleSection>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function SucheGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
|
function SucheGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [results, setResults] = useState(null); // null=noch nicht gesucht
|
const [results, setResults] = useState(null); // null=noch nicht gesucht
|
||||||
@@ -465,26 +506,7 @@ function SucheGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
|
|||||||
{results !== null && results.length > 0 && (() => {
|
{results !== null && results.length > 0 && (() => {
|
||||||
const films = results.filter(m => m.media_type !== 'tv');
|
const films = results.filter(m => m.media_type !== 'tv');
|
||||||
const serien = results.filter(m => m.media_type === 'tv');
|
const serien = results.filter(m => m.media_type === 'tv');
|
||||||
return (
|
return <SectionedGrid films={films} serien={serien} favIds={favIds} xrelIds={xrelIds} onToggle={onToggle} onOpenModal={onOpenModal}/>;
|
||||||
<div>
|
|
||||||
{films.length > 0 && (
|
|
||||||
<div style={{marginBottom:20}}>
|
|
||||||
<div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:10,letterSpacing:1,marginBottom:8}}>🎬 FILME ({films.length})</div>
|
|
||||||
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
|
|
||||||
{films.map(m => <MovieCard key={'m'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{serien.length > 0 && (
|
|
||||||
<div>
|
|
||||||
<div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:10,letterSpacing:1,marginBottom:8}}>📺 SERIEN ({serien.length})</div>
|
|
||||||
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
|
|
||||||
{serien.map(m => <MovieCard key={'t'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -700,45 +722,38 @@ function FavoritenKalender({ onOpenModal }) {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{/* Filme */}
|
|
||||||
{filmeFavs.length > 0 && (
|
{filmeFavs.length > 0 && (
|
||||||
<div style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:11,fontWeight:700,letterSpacing:1,
|
<CollapsibleSection label="🎬 FILME" count={filmeFavs.length}>
|
||||||
borderBottom:'1px solid rgba(255,255,255,0.07)',paddingBottom:6}}>
|
|
||||||
🎬 FILME ({filmeFavs.length})
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{Object.entries(filmGroups).sort(([a],[b]) => a.localeCompare(b)).map(([key, group]) => (
|
{Object.entries(filmGroups).sort(([a],[b]) => a.localeCompare(b)).map(([key, group]) => (
|
||||||
<div key={'film-'+key}>
|
<div key={'film-'+key} style={{marginBottom:12}}>
|
||||||
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
|
<div style={{...S.head,marginBottom:6,display:'flex',alignItems:'center',gap:6}}>
|
||||||
<span>{group.label.toUpperCase()}</span>
|
<span>{group.label.toUpperCase()}</span>
|
||||||
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
|
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
|
||||||
<span style={{ fontSize:9 }}>{group.items.length} Film{group.items.length!==1?'e':''}</span>
|
<span style={{fontSize:9}}>{group.items.length}×</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display:'flex', flexDirection:'column', gap:8 }}>
|
<div style={{display:'flex',flexDirection:'column',gap:6}}>
|
||||||
{group.items.map(f => <FavCard key={f.tmdb_id} f={f} onOpenModal={onOpenModal} onRemove={removeFav}/>)}
|
{group.items.map(f => <FavCard key={f.tmdb_id} f={f} onOpenModal={onOpenModal} onRemove={removeFav}/>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* Serien */}
|
</CollapsibleSection>
|
||||||
{serienFavs.length > 0 && (
|
|
||||||
<div style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:11,fontWeight:700,letterSpacing:1,
|
|
||||||
borderBottom:'1px solid rgba(255,255,255,0.07)',paddingBottom:6,marginTop:8}}>
|
|
||||||
📺 SERIEN ({serienFavs.length})
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
{serienFavs.length > 0 && (
|
||||||
|
<CollapsibleSection label="📺 SERIEN" count={serienFavs.length}>
|
||||||
{Object.entries(serienGroups).sort(([a],[b]) => a.localeCompare(b)).map(([key, group]) => (
|
{Object.entries(serienGroups).sort(([a],[b]) => a.localeCompare(b)).map(([key, group]) => (
|
||||||
<div key={'serie-'+key}>
|
<div key={'serie-'+key} style={{marginBottom:12}}>
|
||||||
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
|
<div style={{...S.head,marginBottom:6,display:'flex',alignItems:'center',gap:6}}>
|
||||||
<span>{group.label.toUpperCase()}</span>
|
<span>{group.label.toUpperCase()}</span>
|
||||||
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
|
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
|
||||||
<span style={{ fontSize:9 }}>{group.items.length} Serie{group.items.length!==1?'n':''}</span>
|
<span style={{fontSize:9}}>{group.items.length}×</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display:'flex', flexDirection:'column', gap:8 }}>
|
<div style={{display:'flex',flexDirection:'column',gap:6}}>
|
||||||
{group.items.map(f => <FavCard key={f.tmdb_id} f={f} onOpenModal={onOpenModal} onRemove={removeFav}/>)}
|
{group.items.map(f => <FavCard key={f.tmdb_id} f={f} onOpenModal={onOpenModal} onRemove={removeFav}/>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* Alte groups-Schleife entfernt – DUMMY damit Code nicht bricht */}
|
</CollapsibleSection>
|
||||||
|
)}
|
||||||
{false && Object.entries({}).map(([key, group]) => (
|
{false && Object.entries({}).map(([key, group]) => (
|
||||||
<div key={key}>
|
<div key={key}>
|
||||||
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
|
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
|
||||||
@@ -813,7 +828,8 @@ function MovieModal({ tmdbId, mediaType='movie', onClose, mobile, onHasXrel, noY
|
|||||||
setDetail(d);
|
setDetail(d);
|
||||||
detailRef.current = d;
|
detailRef.current = d;
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
// Gecachte Releases laden sobald wir den Titel kennen
|
// Gecachte Releases laden (nur für Filme)
|
||||||
|
if (mediaType !== 'tv') {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
title: d.title || '',
|
title: d.title || '',
|
||||||
orig: d.original_title || '',
|
orig: d.original_title || '',
|
||||||
@@ -826,12 +842,13 @@ function MovieModal({ tmdbId, mediaType='movie', onClose, mobile, onHasXrel, noY
|
|||||||
if (r.xrel?.length > 0) { setXrel(r.xrel); onHasXrel?.(tmdbId); }
|
if (r.xrel?.length > 0) { setXrel(r.xrel); onHasXrel?.(tmdbId); }
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(e => { setError(e.message); setLoading(false); });
|
.catch(e => { setError(e.message); setLoading(false); });
|
||||||
const prev = document.body.style.overflow;
|
const prev = document.body.style.overflow;
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
return () => { document.body.style.overflow = prev; };
|
return () => { document.body.style.overflow = prev; };
|
||||||
}, [tmdbId]);
|
}, [tmdbId, mediaType]);
|
||||||
|
|
||||||
const searchXrel = async (forceRefresh = false) => {
|
const searchXrel = async (forceRefresh = false) => {
|
||||||
const d = detailRef.current;
|
const d = detailRef.current;
|
||||||
@@ -931,7 +948,9 @@ function MovieModal({ tmdbId, mediaType='movie', onClose, mobile, onHasXrel, noY
|
|||||||
)}
|
)}
|
||||||
<div style={{ display:'flex', gap:5, flexWrap:'wrap', marginBottom:8 }}>
|
<div style={{ display:'flex', gap:5, flexWrap:'wrap', marginBottom:8 }}>
|
||||||
{detail.vote_average > 0 && <Badge bg="rgba(255,230,109,0.15)" color="#ffe66d">★ {detail.vote_average.toFixed(1)}</Badge>}
|
{detail.vote_average > 0 && <Badge bg="rgba(255,230,109,0.15)" color="#ffe66d">★ {detail.vote_average.toFixed(1)}</Badge>}
|
||||||
{detail.runtime > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.7)">⏱ {detail.runtime} Min</Badge>}
|
{detail.media_type !== 'tv' && detail.runtime > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.7)">⏱ {detail.runtime} Min</Badge>}
|
||||||
|
{detail.media_type === 'tv' && detail.number_of_seasons > 0 && <Badge bg="rgba(96,165,250,0.15)" color="#60a5fa">📺 {detail.number_of_seasons} Staffel{detail.number_of_seasons!==1?'n':''}</Badge>}
|
||||||
|
{detail.media_type === 'tv' && detail.number_of_episodes > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.7)">{detail.number_of_episodes} Ep.</Badge>}
|
||||||
{fskStyle && <Badge bg={fskStyle.bg} color={fskStyle.color}>FSK {detail.fsk}</Badge>}
|
{fskStyle && <Badge bg={fskStyle.bg} color={fskStyle.color}>FSK {detail.fsk}</Badge>}
|
||||||
{detail.release_date && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.55)">🇩🇪 {fmtDE(detail.release_date)}</Badge>}
|
{detail.release_date && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.55)">🇩🇪 {fmtDE(detail.release_date)}</Badge>}
|
||||||
{detail.countries?.length > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.4)">🌍 {detail.countries.slice(0,2).join(' · ')}</Badge>}
|
{detail.countries?.length > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.4)">🌍 {detail.countries.slice(0,2).join(' · ')}</Badge>}
|
||||||
|
|||||||
Reference in New Issue
Block a user