import React, { useState, useEffect } from 'react'; import { View, Text, FlatList, StyleSheet, TouchableOpacity, Image, ActivityIndicator } from 'react-native'; import { useQuery } from '@tanstack/react-query'; import { Audio } from 'expo-av'; import { Theme } from '../../constants/Theme'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; const API_URL = process.env.EXPO_PUBLIC_API_URL || 'http://216.158.230.94:8001/api/v1'; interface PodcastEpisode { id: string; title: string; description: string; audio_url: string; published_at: string; duration: string; image_url: string; } interface PodcastFeed { title: string; description: string; image_url: string; episodes: PodcastEpisode[]; } const fetchPodcasts = async (): Promise => { const response = await fetch(`${API_URL}/podcast/`); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }; export default function PodcastsScreen() { const { data, isLoading, error } = useQuery({ queryKey: ['podcasts'], queryFn: fetchPodcasts, }); const [sound, setSound] = useState(null); const [isPlaying, setIsPlaying] = useState(false); const [currentEpisodeId, setCurrentEpisodeId] = useState(null); useEffect(() => { return () => { if (sound) { sound.unloadAsync(); } }; }, [sound]); const playPodcast = async (audioUrl: string, episodeId: string) => { try { if (sound) { await sound.unloadAsync(); } const { sound: newSound } = await Audio.Sound.createAsync( { uri: audioUrl }, { shouldPlay: true } ); setSound(newSound); setCurrentEpisodeId(episodeId); setIsPlaying(true); newSound.setOnPlaybackStatusUpdate((status) => { if (status.isLoaded) { setIsPlaying(status.isPlaying); if (status.didJustFinish) { setIsPlaying(false); setCurrentEpisodeId(null); } } }); } catch (error) { console.error('Error playing sound:', error); } }; const pausePodcast = async () => { if (sound) { await sound.pauseAsync(); setIsPlaying(false); } }; const resumePodcast = async () => { if (sound) { await sound.playAsync(); setIsPlaying(true); } }; const handlePlayPause = (audioUrl: string, episodeId: string) => { if (currentEpisodeId === episodeId) { if (isPlaying) { pausePodcast(); } else { resumePodcast(); } } else { playPodcast(audioUrl, episodeId); } }; const renderItem = ({ item }: { item: PodcastEpisode }) => ( {item.title} {new Date(item.published_at).toLocaleDateString()} {item.duration} handlePlayPause(item.audio_url, item.id)} > ); if (isLoading) { return ( ); } if (error) { return ( Failed to load podcasts ); } return ( item.id} contentContainerStyle={styles.listContent} ListHeaderComponent={ Podcasts Listen to the latest episodes } /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: Theme.colors.background, }, center: { flex: 1, justifyContent: 'center', alignItems: 'center', }, listContent: { padding: Theme.spacing.m, }, header: { marginBottom: Theme.spacing.l, }, headerTitle: { fontSize: Theme.typography.fontSize.xxl, fontFamily: Theme.typography.fontFamily.heading, color: Theme.colors.text, fontWeight: 'bold', }, headerSubtitle: { fontSize: Theme.typography.fontSize.m, fontFamily: Theme.typography.fontFamily.body, color: Theme.colors.text, opacity: 0.7, }, card: { backgroundColor: Theme.colors.surface, borderRadius: Theme.borderRadius.m, padding: Theme.spacing.m, marginBottom: Theme.spacing.m, ...Theme.shadows.card, }, cardContent: { flexDirection: 'row', alignItems: 'center', }, thumbnail: { width: 60, height: 60, borderRadius: Theme.borderRadius.s, marginRight: Theme.spacing.m, backgroundColor: Theme.colors.secondary, }, textContainer: { flex: 1, marginRight: Theme.spacing.s, }, episodeTitle: { fontSize: Theme.typography.fontSize.m, fontFamily: Theme.typography.fontFamily.heading, color: Theme.colors.text, fontWeight: '600', marginBottom: 4, }, episodeDate: { fontSize: Theme.typography.fontSize.xs, fontFamily: Theme.typography.fontFamily.body, color: Theme.colors.text, opacity: 0.6, }, episodeDuration: { fontSize: Theme.typography.fontSize.xs, fontFamily: Theme.typography.fontFamily.body, color: Theme.colors.text, opacity: 0.6, }, playButton: { padding: 4, }, errorText: { color: Theme.colors.error || 'red', fontSize: Theme.typography.fontSize.m, }, });