// Wspólny komponent karty sceny dla list ulubionych (PerformerScenesScreen + // StudioScenesScreen). Identyczny shape co SceneRow w ScenesScreen — thumb // po lewej, tytuł / data · studio / performers / sources w kolumnie po prawej. // Dodatkowo: NEW badge gdy `seenSince` < scene.created_at. import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import * as Haptics from 'expo-haptics'; import React from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import type { RootStackParamList } from '../navigation'; import { theme } from '../theme'; import type { SceneOut } from '../types'; import { Thumb } from './Thumb'; interface Props { scene: SceneOut; /** Pokazuje NEW badge gdy `scene.created_at > seenSince`. */ seenSince?: string; /** Co wyświetlić w drugiej linijce — performers (dla studio scenes) lub studio (dla performer scenes). */ secondLine?: 'performers' | 'studio'; } export function FavoriteSceneRow({ scene, seenSince, secondLine = 'studio' }: Props) { const navigation = useNavigation>(); const [isPreviewing, setIsPreviewing] = React.useState(false); const animatedUrl = scene.playback_sources.find((s) => s.animated_thumbnail_url) ?.animated_thumbnail_url; const staticUrl = scene.playback_sources.find((s) => s.thumbnail_url)?.thumbnail_url; const displayUrl = isPreviewing && animatedUrl ? animatedUrl : staticUrl ?? animatedUrl; const startPreview = () => { if (!animatedUrl) return; setIsPreviewing(true); Haptics.selectionAsync().catch(() => {}); }; const isNew = !!(seenSince && scene.created_at && scene.created_at > seenSince); const dim = scene.finished === true; // Drugi rządek (data + studio LUB data + performers) const dateStr = scene.release_date ?? null; let secondLineText: string | null = null; if (secondLine === 'studio') { const studio = scene.studio?.name ?? null; secondLineText = [dateStr, studio].filter(Boolean).join(' · ') || null; } else { const performers = scene.performers .slice(0, 3) .map((p) => p.canonical_name) .join(', '); secondLineText = [dateStr, performers].filter(Boolean).join(' · ') || null; } return ( navigation.navigate('SceneDetail', { id: scene.id })} onLongPress={startPreview} onPressOut={() => setIsPreviewing(false)} delayLongPress={180} > {scene.is_favorite ? ( ) : null} {isNew ? ( NEW ) : null} {scene.title} {secondLineText ? ( {secondLineText} ) : null} {[...new Set(scene.external_refs.map((r) => r.source))].join(' · ')} {scene.playback_sources.length > 0 ? ` ▶ ${scene.playback_sources.length}` : ''} {dim ? ' ✓ watched' : ''} ); } const styles = StyleSheet.create({ row: { flexDirection: 'row', backgroundColor: theme.card, borderColor: theme.border, borderWidth: 1, borderRadius: 8, padding: 10, marginBottom: 10, gap: 12, position: 'relative', }, rowDimmed: { opacity: 0.55 }, thumbnail: { width: 110, aspectRatio: 16 / 9, flexShrink: 0 }, rowContent: { flex: 1, justifyContent: 'center' }, rowTitle: { color: theme.fg, fontWeight: '600', marginBottom: 4 }, rowMuted: { color: theme.muted, fontSize: 13, marginBottom: 4 }, rowSources: { color: theme.accent, fontSize: 11, textTransform: 'uppercase', marginTop: 2, }, favBadge: { position: 'absolute', top: 12, left: 12, backgroundColor: 'rgba(0,0,0,0.6)', width: 22, height: 22, borderRadius: 11, alignItems: 'center', justifyContent: 'center', }, favBadgeText: { color: theme.accent, fontSize: 13, fontWeight: '800' }, newBadge: { position: 'absolute', top: 8, right: 8, backgroundColor: theme.accent, paddingHorizontal: 6, paddingVertical: 1, borderRadius: 4, shadowColor: theme.accent, shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0.5, shadowRadius: 4, elevation: 3, }, newBadgeText: { color: theme.bg, fontSize: 10, fontWeight: '800', letterSpacing: 0.5 }, });