feat: add image preview for large view cards

- Extract YouTube video thumbnails from URLs
- Display thumbnail images as background in large preview cards
- Add gradient overlay for better text contrast
- Fallback to icon placeholder for non-YouTube URLs
- Handle multiple YouTube URL formats (watch, youtu.be, shorts)
- Gracefully handle missing images with icon fallback
This commit is contained in:
Gigi
2025-10-03 10:16:22 +02:00
parent bd3193957c
commit 57c5be9907
3 changed files with 67 additions and 7 deletions

View File

@@ -12,6 +12,7 @@ import ContentWithResolvedProfiles from './ContentWithResolvedProfiles'
import { extractUrlsFromContent } from '../services/bookmarkHelpers'
import { classifyUrl } from '../utils/helpers'
import { ViewMode } from './Bookmarks'
import { getPreviewImage } from '../utils/imagePreview'
interface BookmarkItemProps {
bookmark: IndividualBookmark
@@ -124,14 +125,22 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
// Large preview view rendering
if (viewMode === 'large') {
const firstUrl = hasUrls ? extractedUrls[0] : null
const previewImage = firstUrl ? getPreviewImage(firstUrl, firstUrlClassification?.type || '') : null
return (
<div key={`${bookmark.id}-${index}`} className={`individual-bookmark large ${bookmark.isPrivate ? 'private-bookmark' : ''}`}>
{hasUrls && (
<div className="large-preview-image" onClick={() => onSelectUrl?.(extractedUrls[0])}>
{/* Placeholder for future image preview */}
<div className="preview-placeholder">
<FontAwesomeIcon icon={getIconForUrlType(extractedUrls[0])} />
</div>
<div
className="large-preview-image"
onClick={() => onSelectUrl?.(extractedUrls[0])}
style={previewImage ? { backgroundImage: `url(${previewImage})` } : undefined}
>
{!previewImage && (
<div className="preview-placeholder">
<FontAwesomeIcon icon={getIconForUrlType(extractedUrls[0])} />
</div>
)}
</div>
)}

View File

@@ -838,16 +838,28 @@ body {
width: 100%;
height: 180px;
background: #1a1a1a;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.2s ease;
transition: all 0.2s ease;
border-bottom: 1px solid #333;
position: relative;
}
.large-preview-image:hover {
background: #222;
opacity: 0.9;
}
.large-preview-image::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to bottom, transparent 60%, rgba(0,0,0,0.3) 100%);
pointer-events: none;
}
.preview-placeholder {

39
src/utils/imagePreview.ts Normal file
View File

@@ -0,0 +1,39 @@
// Utility to extract preview images from URLs
export const extractYouTubeVideoId = (url: string): string | null => {
// Handle various YouTube URL formats
const patterns = [
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
/youtube\.com\/shorts\/([^&\n?#]+)/,
]
for (const pattern of patterns) {
const match = url.match(pattern)
if (match && match[1]) {
return match[1]
}
}
return null
}
export const getYouTubeThumbnail = (url: string): string | null => {
const videoId = extractYouTubeVideoId(url)
if (!videoId) return null
// Use maxresdefault for best quality, falls back to hqdefault if not available
return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`
}
export const getPreviewImage = (url: string, type: string): string | null => {
// YouTube videos
if (type === 'youtube') {
return getYouTubeThumbnail(url)
}
// For other URLs, we would need to fetch OG tags
// but CORS will block us on localhost
// Return null for now and show placeholder
return null
}