feat(VideoLibrary): enhance cover rendering and loading skeletons

This commit is contained in:
shiyu
2025-12-17 09:59:48 +08:00
parent 724f551b00
commit 6a1ff3afa6

View File

@@ -141,43 +141,88 @@ export const VideoLibraryApp: React.FC<AppOpenComponentProps> = () => {
const renderCover = (item: VideoLibraryItem) => { const renderCover = (item: VideoLibraryItem) => {
const label = item.type === 'tv' ? 'TV' : 'Movie'; const label = item.type === 'tv' ? 'TV' : 'Movie';
const subtitle = item.type === 'tv' const coverUrl = tmdbImage(item.poster_path, 'w342') || tmdbImage(item.backdrop_path, 'w780');
? `${item.seasons_count || 0} ${t('Seasons')} · ${item.episodes_count || 0} ${t('Episodes')}`
: (item.year ? String(item.year) : '');
return ( return (
<div <div
style={{ style={{
height: 150, position: 'relative',
width: '100%',
aspectRatio: '2 / 3',
background: 'linear-gradient(135deg, #0b1020 0%, #22314a 55%, #2b3b5c 100%)', background: 'linear-gradient(135deg, #0b1020 0%, #22314a 55%, #2b3b5c 100%)',
display: 'flex', overflow: 'hidden',
flexDirection: 'column',
justifyContent: 'space-between',
padding: 12,
color: 'rgba(255,255,255,0.92)',
}} }}
> >
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}> {coverUrl ? (
<Image
src={coverUrl}
alt={item.title || label}
preview={false}
wrapperStyle={{ width: '100%', height: '100%', display: 'block' }}
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
fallback="/logo.svg"
/>
) : (
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'rgba(255,255,255,0.7)',
}}
>
<VideoCameraOutlined style={{ fontSize: 28 }} />
</div>
)}
<div
style={{
position: 'absolute',
inset: 0,
background: 'linear-gradient(180deg, rgba(0,0,0,0.48) 0%, rgba(0,0,0,0.12) 40%, rgba(0,0,0,0.45) 100%)',
pointerEvents: 'none',
}}
/>
<div
style={{
position: 'absolute',
top: 10,
left: 10,
right: 10,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: 8,
pointerEvents: 'none',
}}
>
<Tag color={item.type === 'tv' ? 'geekblue' : 'gold'} style={{ marginInlineEnd: 0 }}> <Tag color={item.type === 'tv' ? 'geekblue' : 'gold'} style={{ marginInlineEnd: 0 }}>
{label} {label}
</Tag> </Tag>
<VideoCameraOutlined style={{ fontSize: 18, opacity: 0.9 }} /> <VideoCameraOutlined style={{ fontSize: 18, opacity: 0.9, color: 'rgba(255,255,255,0.9)' }} />
</div> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<PlayCircleOutlined style={{ fontSize: 32, opacity: 0.9 }} /> <div
<div style={{ minWidth: 0 }}> style={{
<Typography.Text position: 'absolute',
strong left: 10,
style={{ color: 'rgba(255,255,255,0.92)', display: 'block' }} bottom: 10,
ellipsis width: 36,
> height: 36,
{item.title || '--'} borderRadius: 999,
</Typography.Text> background: 'rgba(2,6,23,0.55)',
{subtitle && ( border: '1px solid rgba(255,255,255,0.22)',
<Typography.Text style={{ color: 'rgba(255,255,255,0.72)', fontSize: 12 }}> display: 'flex',
{subtitle} alignItems: 'center',
</Typography.Text> justifyContent: 'center',
)} color: 'rgba(255,255,255,0.92)',
</div> boxShadow: token.boxShadowTertiary,
pointerEvents: 'none',
}}
>
<PlayCircleOutlined style={{ fontSize: 20 }} />
</div> </div>
</div> </div>
); );
@@ -543,14 +588,18 @@ export const VideoLibraryApp: React.FC<AppOpenComponentProps> = () => {
{loading ? ( {loading ? (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(210px, 1fr))', gap: 12 }}> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(210px, 1fr))', gap: 12 }}>
{Array.from({ length: 10 }).map((_, idx) => ( {Array.from({ length: 10 }).map((_, idx) => (
<Card <Card
key={idx} key={idx}
size="small" size="small"
style={{ borderRadius: 12, overflow: 'hidden' }} style={{ borderRadius: 12, overflow: 'hidden' }}
cover={<Skeleton.Image active style={{ width: '100%', height: 150 }} />} cover={(
> <div style={{ width: '100%', aspectRatio: '2 / 3' }}>
<Skeleton active paragraph={{ rows: 2 }} /> <Skeleton.Image active style={{ width: '100%', height: '100%' }} />
</Card> </div>
)}
>
<Skeleton active paragraph={{ rows: 2 }} />
</Card>
))} ))}
</div> </div>
) : filtered.length === 0 ? ( ) : filtered.length === 0 ? (
@@ -604,7 +653,7 @@ export const VideoLibraryApp: React.FC<AppOpenComponentProps> = () => {
title={detailTitle || selected?.title || t('Details')} title={detailTitle || selected?.title || t('Details')}
open={detailOpen} open={detailOpen}
onClose={closeDetail} onClose={closeDetail}
width={900} width="100%"
destroyOnHidden destroyOnHidden
getContainer={false} getContainer={false}
styles={{ body: { padding: 0 } }} styles={{ body: { padding: 0 } }}