import { useIsFocused, useRoute } from '@react-navigation/native' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Pressable, StyleSheet, Text, View } from 'react-native' import Carousel from 'react-native-reanimated-carousel' import { responsiveHeight } from 'react-native-responsive-dimensions' import { projectsRef } from '../../config/firebase' import useDataFromRef from '../../hooks/useDataFromRef' import usePlayer from '../../hooks/usePlayer' import PlaybackItem from './components/PlaybackItem' import PlaybackCharts from './components/PlaybackCharts' import PlaybackTypeTabs from './components/PlaybackTypeTabs' import { Palette } from '../../styles' import { FONT_FAMILY } from '../../styles/Fonts' import { getPlaybackType, matchesPlaybackType, PLAYBACK_TYPE } from '../../utils/playbackType' const Playbacks = () => { const [activeIndex, setActiveIndex] = useState(0) const userCache = useRef(new Map()) const isFocused = useIsFocused() const wasFocusedRef = useRef(false) const route = useRoute() const { stop } = usePlayer() || {} const initialFocusProjectId = route?.params?.projectId || route?.params?.focusId || null const [focusProjectId, setFocusProjectId] = useState(initialFocusProjectId) const [viewMode, setViewMode] = useState(initialFocusProjectId ? 'feed' : 'charts') const [playbackType, setPlaybackType] = useState(PLAYBACK_TYPE.LIVE) const { data: rawPlaybacks = [], loadMore, hasMore, loading, } = useDataFromRef({ ref: projectsRef.where('playbackUrl', '!=', null), simpleRef: false, listener: false, usePagination: true, batchSize: 6, }) const filteredPlaybacks = useMemo( () => rawPlaybacks.filter((item) => matchesPlaybackType(item, playbackType)), [playbackType, rawPlaybacks] ) const focusIndex = useMemo(() => { if (!focusProjectId) return -1 return filteredPlaybacks.findIndex((p) => p?.id === focusProjectId) }, [filteredPlaybacks, focusProjectId]) const playbacks = useMemo(() => { if (focusIndex < 0) return filteredPlaybacks const target = filteredPlaybacks[focusIndex] const before = filteredPlaybacks.slice(0, focusIndex) const after = filteredPlaybacks.slice(focusIndex + 1) return [target, ...after, ...before] }, [filteredPlaybacks, focusIndex]) const onSnap = useCallback( (index) => { setActiveIndex(index) if (index >= (playbacks?.length || 0) - 6) { loadMore?.() } }, [playbacks?.length, loadMore] ) const triedLoadMoreRef = useRef(0) const focusLoadAttemptsRef = useRef(0) const hasAppliedFocusRef = useRef(false) useEffect(() => { focusLoadAttemptsRef.current = 0 hasAppliedFocusRef.current = false if (!focusProjectId) { setActiveIndex(0) } }, [focusProjectId]) useEffect(() => { if (!focusProjectId) return const focusedPlayback = rawPlaybacks.find((item) => item?.id === focusProjectId) if (focusedPlayback) { setPlaybackType(getPlaybackType(focusedPlayback)) } }, [focusProjectId, rawPlaybacks]) useEffect(() => { if (loadMore && triedLoadMoreRef.current < 6) { triedLoadMoreRef.current += 1 loadMore() } }, [loadMore, playbacks]) useEffect(() => { if (playbacks.length === 0 && hasMore && !loading) { loadMore?.() } }, [hasMore, loadMore, loading, playbacks.length]) useEffect(() => { if (!focusProjectId) return if (focusIndex >= 0 && !hasAppliedFocusRef.current) { hasAppliedFocusRef.current = true setActiveIndex(0) return } if (loadMore && focusLoadAttemptsRef.current < 6) { focusLoadAttemptsRef.current += 1 loadMore() } }, [focusIndex, focusProjectId, loadMore, playbacks]) useEffect(() => { const nextFocusId = route?.params?.projectId || route?.params?.focusId || null setFocusProjectId(nextFocusId) if (nextFocusId) { setViewMode('feed') } }, [route?.params?.focusId, route?.params?.projectId]) useEffect(() => { if (isFocused && !wasFocusedRef.current) { stop?.().catch(() => {}) } wasFocusedRef.current = isFocused }, [isFocused, stop]) useEffect(() => { if (viewMode !== 'feed') { return } setActiveIndex(0) }, [viewMode]) const showFeed = viewMode === 'feed' const handlePlaybackTypeChange = useCallback((nextType) => { setFocusProjectId(null) setPlaybackType(nextType) setActiveIndex(0) }, []) const openFeedForPlayback = useCallback((project) => { const nextId = project && typeof project === 'object' ? project?.id || project?.projectId || null : project || null if (!nextId) { return } if (project && typeof project === 'object') { setPlaybackType(getPlaybackType(project)) } setFocusProjectId(nextId) setViewMode('feed') }, []) if (!showFeed) { return ( ) } return ( ( )} /> {playbacks.length === 0 && !loading ? ( Aucun {playbackType === PLAYBACK_TYPE.AI ? 'playback IA' : 'playback live'} pour le moment. ) : null} setViewMode('charts')} accessibilityLabel="Revenir au classement des playbacks" > Classement ) } export default Playbacks const styles = StyleSheet.create({ feedHeader: { position: 'absolute', top: 32, left: 16, right: 16, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 8, zIndex: 20, }, backToCharts: { paddingVertical: 8, paddingHorizontal: 12, borderRadius: 12, backgroundColor: 'rgba(0,0,0,0.55)', zIndex: 20, }, backToChartsText: { color: Palette.white, fontSize: 12, fontFamily: FONT_FAMILY.InterSemiBold, }, emptyState: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 24, }, emptyStateText: { color: Palette.white, fontSize: 15, fontFamily: FONT_FAMILY.InterMedium, textAlign: 'center', }, })