diff --git a/src/pages/podcasts/index.tsx b/src/pages/podcasts/index.tsx index a80a30cd..a9cf24c3 100644 --- a/src/pages/podcasts/index.tsx +++ b/src/pages/podcasts/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import Layout from '@theme/Layout'; import type { ReactElement } from 'react'; import { useHistory } from '@docusaurus/router'; @@ -8,6 +8,7 @@ interface PodcastData { id: string; spotifyUrl: string; type: 'episode' | 'show' | 'playlist'; + title?: string; // Add optional title here } // Function to extract Spotify ID from URL @@ -36,90 +37,75 @@ const podcastUrls: string[] = [ "https://open.spotify.com/episode/21yp6PDe1XN8B1goR5qMI3?si=k6JURkMRTQq2Ltbujq9qLw", ]; -const podcastData: PodcastData[] = podcastUrls.map((url, index) => ({ +// Initialize podcast data without titles first +const initialPodcastData: PodcastData[] = podcastUrls.map((url, index) => ({ id: String(index + 1), spotifyUrl: url, - type: getSpotifyContentType(url) + type: getSpotifyContentType(url), })); -interface SpotifyTitleProps { - spotifyUrl: string; - type: 'episode' | 'show' | 'playlist'; -} - -// Fetches the podcast/show/episode title from Spotify oEmbed API -const SpotifyTitle: React.FC = ({ spotifyUrl, type }) => { - const [title, setTitle] = React.useState(''); - const [loading, setLoading] = React.useState(true); - - React.useEffect(() => { - let cancelled = false; - setLoading(true); - fetch(`https://open.spotify.com/oembed?url=${encodeURIComponent(spotifyUrl)}`) - .then(res => res.json()) - .then(data => { - if (!cancelled) { - setTitle(data.title); - setLoading(false); - } - }) - .catch(() => { - if (!cancelled) { - setTitle(''); - setLoading(false); - } - }); - return () => { cancelled = true; }; - }, [spotifyUrl]); - - return ( -
- {loading ? ( -
-
-
-
- ) : ( - <> -
- - {type === 'episode' ? '🎙️' : type === 'show' ? '📻' : '🎵'} - - {type.charAt(0).toUpperCase() + type.slice(1)} -
-

- {title || `${type.charAt(0).toUpperCase() + type.slice(1)} #${Math.floor(Math.random() * 100) + 1}`} -

- - )} +// Component to display Spotify title and type badge for each podcast +const SpotifyTitle: React.FC<{ title?: string; type: 'episode' | 'show' | 'playlist' }> = ({ title, type }) => ( +
+
+ + {type === 'episode' ? '🎙️' : type === 'show' ? '📻' : '🎵'} + + {type.charAt(0).toUpperCase() + type.slice(1)}
- ); -}; +

{title || 'Loading title...'}

+
+); export default function Podcasts(): ReactElement { const history = useHistory(); const [currentPage, setCurrentPage] = useState(1); const [searchTerm, setSearchTerm] = useState(""); - const [selectedFilter, setSelectedFilter] = useState< - "all" | "episode" | "show" | "playlist" - >("all"); + const [selectedFilter, setSelectedFilter] = useState<"all" | "episode" | "show" | "playlist">("all"); const [favorites, setFavorites] = useState(() => { - // Load favorites from localStorage on component mount if (typeof window !== "undefined") { const saved = localStorage.getItem("podcast-favorites"); return saved ? JSON.parse(saved) : []; } return []; }); + const [podcasts, setPodcasts] = useState(initialPodcastData); const podcastsPerPage = 9; - // Filter podcasts based on search and filter - const filteredPodcasts = podcastData.filter(podcast => { + // Fetch all podcast titles once on mount + useEffect(() => { + let cancelled = false; + Promise.all( + podcasts.map(p => + fetch(`https://open.spotify.com/oembed?url=${encodeURIComponent(p.spotifyUrl)}`) + .then(res => res.json()) + .then(data => ({ id: p.id, title: data.title })) + .catch(() => ({ id: p.id, title: '' })) + ) + ).then(results => { + if (!cancelled) { + // Merge fetched titles into podcasts state + setPodcasts(prev => + prev.map(p => { + const found = results.find(r => r.id === p.id); + return found ? { ...p, title: found.title } : p; + }) + ); + } + }); + return () => { cancelled = true; }; + }, []); + + // Filter podcasts based on search and filter using title now + const filteredPodcasts = podcasts.filter(podcast => { const matchesFilter = selectedFilter === 'all' || podcast.type === selectedFilter; - return matchesFilter; + const matchesSearch = + searchTerm === '' || + (podcast.title && podcast.title.toLowerCase().includes(searchTerm.toLowerCase())); + return matchesFilter && matchesSearch; }); - // Calculate podcasts for current page + // Pagination calculations const indexOfLastPodcast = currentPage * podcastsPerPage; const indexOfFirstPodcast = indexOfLastPodcast - podcastsPerPage; const currentPodcasts = filteredPodcasts.slice(indexOfFirstPodcast, indexOfLastPodcast); @@ -137,8 +123,7 @@ export default function Podcasts(): ReactElement { title: `Check out this ${podcast.type}`, url: podcast.spotifyUrl, }); - } catch (err) { - // Fallback to clipboard + } catch { navigator.clipboard.writeText(podcast.spotifyUrl); } } else { @@ -147,20 +132,14 @@ export default function Podcasts(): ReactElement { }; const handleFavorite = (podcast: PodcastData, event: React.MouseEvent) => { - // Prevent card click when clicking favorite button event.stopPropagation(); - setFavorites((prev) => { + setFavorites(prev => { const isFavorited = prev.includes(podcast.id); - const newFavorites = isFavorited - ? prev.filter((id) => id !== podcast.id) // Remove from favorites - : [...prev, podcast.id]; // Add to favorites - - // Save to localStorage for persistence + const newFavorites = isFavorited ? prev.filter(id => id !== podcast.id) : [...prev, podcast.id]; if (typeof window !== "undefined") { localStorage.setItem("podcast-favorites", JSON.stringify(newFavorites)); } - return newFavorites; }); }; @@ -171,12 +150,11 @@ export default function Podcasts(): ReactElement { ) => { const target = event.target as HTMLElement; - // Prevent navigation if clicking on buttons or action area if ( target.tagName === "IFRAME" || target.closest(".podcast-embed") || - target.closest(".action-btn") || // Don't navigate if clicking buttons - target.closest(".card-actions") || // Don't navigate if clicking action area + target.closest(".action-btn") || + target.closest(".card-actions") || target.classList.contains("action-btn") || target.classList.contains("favorite") || target.classList.contains("share") @@ -197,17 +175,13 @@ export default function Podcasts(): ReactElement { 🎙️ Premium Audio Content
-

- Discover Top Podcasts -

+

Discover Top Podcasts

Stream the best podcasts from your favorite stations. Dive into episodes that inspire, educate, and entertain from leading voices in tech, business, and beyond.

- - {/* Stats */}
-
{podcastData.length}+
+
{podcasts.length}+
Episodes
@@ -230,7 +204,7 @@ export default function Podcasts(): ReactElement { type="text" placeholder="Search podcasts..." value={searchTerm} - onChange={(e) => setSearchTerm(e.target.value)} + onChange={e => setSearchTerm(e.target.value)} className="search-input" />
@@ -240,28 +214,28 @@ export default function Podcasts(): ReactElement { onClick={() => setSelectedFilter('all')} > 📊 - All ({podcastData.length}) + All ({podcasts.length})
@@ -275,10 +249,10 @@ export default function Podcasts(): ReactElement {
handlePodcastClick(podcast, e)} + onClick={e => handlePodcastClick(podcast, e)} role="button" tabIndex={0} - onKeyDown={(e) => { + onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { handlePodcastClick(podcast, e); } @@ -286,48 +260,49 @@ export default function Podcasts(): ReactElement { style={{ animationDelay: `${index * 0.1}s` }} >
- +
{ + onClick={e => { e.stopPropagation(); e.preventDefault(); }} - onMouseDown={(e) => { + onMouseDown={e => { e.stopPropagation(); }} > -
-
e.stopPropagation()}> +
e.stopPropagation()}>