/**
* SceneGridSkeleton — placeholder grid pokazywany podczas ŁADOWANIA listy scen,
* zamiast spinnera. Rysuje się natychmiast (zero danych), więc ekran "ożywa" w ms
* nawet gdy backend liczy kilka sekund — percepcyjna szybkość jak w dużych apkach
* (YT/Netflix pokazują szkielety/placeholdery zanim dane dojdą).
*
* Layout 1:1 z SceneTile (2-col, thumb 16:9 + linia tytułu + linia meta), żeby
* przejście skeleton→content nie "skakało". Subtelny pulse (Animated opacity loop),
* bez zewnętrznych zależności.
*/
import React from 'react';
import { Animated, StyleSheet, View } from 'react-native';
import { theme } from '../theme';
function usepulse() {
const v = React.useRef(new Animated.Value(0.4)).current;
React.useEffect(() => {
const loop = Animated.loop(
Animated.sequence([
Animated.timing(v, { toValue: 0.85, duration: 700, useNativeDriver: true }),
Animated.timing(v, { toValue: 0.4, duration: 700, useNativeDriver: true }),
]),
);
loop.start();
return () => loop.stop();
}, [v]);
return v;
}
function SkeletonTile({ opacity }: { opacity: Animated.Value }) {
return (
);
}
/** `count` kafelków (parzysta liczba dla 2-col siatki). */
export function SceneGridSkeleton({ count = 8 }: { count?: number }) {
const opacity = usepulse();
const rows = Math.ceil(count / 2);
return (
{Array.from({ length: rows }).map((_, r) => (
))}
);
}
const styles = StyleSheet.create({
grid: { marginTop: 2 },
row: { flexDirection: 'row', gap: 10, marginBottom: 14 },
tile: { flex: 1 },
thumb: {
width: '100%',
aspectRatio: 16 / 9,
borderRadius: 6,
backgroundColor: theme.bgElevated,
},
lineTitle: {
height: 12,
borderRadius: 4,
backgroundColor: theme.bgElevated,
marginTop: 8,
width: '85%',
},
lineMeta: {
height: 9,
borderRadius: 4,
backgroundColor: theme.bgElevated,
marginTop: 6,
width: '55%',
},
});