import { useEffect, useState, useCallback } from 'react'; import { View, Text, ScrollView, StyleSheet, Pressable, Dimensions, RefreshControl, } from 'react-native'; import Animated, { FadeIn, FadeInDown, useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Image } from 'expo-image'; import { Colors, Font, Spacing, Radius, TAB_BAR_HEIGHT, MINI_PLAYER_HEIGHT } from '../../constants/theme'; import { getTracks, getAlbums, Track, Album, coverUrl } from '../../services/api'; import { usePlayer } from '../../store'; const { width } = Dimensions.get('window'); const CARD_W = width * 0.42; export default function HomeScreen() { const [tracks, setTracks] = useState([]); const [albums, setAlbums] = useState([]); const [refresh, setRefresh] = useState(false); const insets = useSafeAreaInsets(); const { setQueue, queue } = usePlayer(); const load = useCallback(async () => { const [t, a] = await Promise.all([getTracks(), getAlbums()]); setTracks(t); setAlbums(a); }, []); useEffect(() => { load(); }, []); const onRefresh = async () => { setRefresh(true); await load(); setRefresh(false); }; const playTrack = (track: Track, index: number) => { setQueue(tracks, index); usePlayer.getState().setExpanded(true); }; return ( } showsVerticalScrollIndicator={false} > {/* Header */} Добро пожаловать NodeShift Music {/* Albums row */} {albums.length > 0 && ( Альбомы {albums.map((a, i) => ( {/* open album */}} /> ))} )} {/* Tracks list */} {tracks.length > 0 && ( Треки {tracks.map((t, i) => ( playTrack(t, i)} /> ))} )} ); } function AlbumCard({ album, index, onPress }: { album: Album; index: number; onPress: () => void }) { const scale = useSharedValue(1); const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] })); const cover = coverUrl(album.cover_url); return ( { scale.value = withSpring(0.95, { damping: 15 }); }} onPressOut={() => { scale.value = withSpring(1, { damping: 15 }); }} onPress={onPress} style={styles.albumCard} > {album.title} {album.artist} ); } function TrackRow({ track, index, onPress }: { track: Track; index: number; onPress: () => void }) { const { queue, index: qi } = usePlayer(); const isPlaying = queue[qi]?.id === track.id; const scale = useSharedValue(1); const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] })); const cover = coverUrl(track.cover_url); return ( { scale.value = withSpring(0.97, { damping: 15 }); }} onPressOut={() => { scale.value = withSpring(1, { damping: 15 }); }} onPress={onPress} style={[styles.trackRow, isPlaying && styles.trackRowActive]} > {track.title} {track.artist} {fmt(track.duration)} ); } function fmt(s: number) { const m = Math.floor(s / 60); const sec = Math.floor(s % 60); return `${m}:${sec < 10 ? '0' : ''}${sec}`; } const styles = StyleSheet.create({ scroll: { flex: 1, backgroundColor: Colors.bg }, content: { paddingHorizontal: Spacing.md }, greeting: { color: Colors.text3, fontSize: Font.sm, fontWeight: '600', marginBottom: 4 }, title: { color: Colors.text, fontSize: Font.xxxl, fontWeight: '800', letterSpacing: -1, marginBottom: Spacing.xl }, section: { marginBottom: Spacing.xl }, sectionTitle: { color: Colors.text, fontSize: Font.lg, fontWeight: '700', marginBottom: Spacing.md }, hScroll: { paddingRight: Spacing.md, gap: Spacing.md }, albumCard: { width: CARD_W }, albumCover: { width: CARD_W, height: CARD_W, borderRadius: Radius.md, marginBottom: Spacing.sm, backgroundColor: Colors.elevated }, albumTitle: { color: Colors.text, fontSize: Font.sm, fontWeight: '600' }, albumArtist: { color: Colors.text2, fontSize: Font.xs, marginTop: 2 }, trackRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10, paddingHorizontal: Spacing.sm, borderRadius: Radius.md, gap: Spacing.md }, trackRowActive: { backgroundColor: Colors.surface }, trackCover: { width: 48, height: 48, borderRadius: Radius.sm, backgroundColor: Colors.elevated }, trackInfo: { flex: 1 }, trackTitle: { color: Colors.text, fontSize: Font.md, fontWeight: '600' }, trackArtist:{ color: Colors.text2, fontSize: Font.sm, marginTop: 2 }, trackDur: { color: Colors.text3, fontSize: Font.xs }, });