Skip to content

Commit 7bf65e4

Browse files
Update index.tsx
1 parent d3f2e08 commit 7bf65e4

File tree

1 file changed

+85
-110
lines changed

1 file changed

+85
-110
lines changed

src/pages/podcasts/index.tsx

Lines changed: 85 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useEffect } from 'react';
22
import Layout from '@theme/Layout';
33
import type { ReactElement } from 'react';
44
import { useHistory } from '@docusaurus/router';
@@ -8,6 +8,7 @@ interface PodcastData {
88
id: string;
99
spotifyUrl: string;
1010
type: 'episode' | 'show' | 'playlist';
11+
title?: string; // Add optional title here
1112
}
1213

1314
// Function to extract Spotify ID from URL
@@ -36,90 +37,75 @@ const podcastUrls: string[] = [
3637
"https://open.spotify.com/episode/21yp6PDe1XN8B1goR5qMI3?si=k6JURkMRTQq2Ltbujq9qLw",
3738
];
3839

39-
const podcastData: PodcastData[] = podcastUrls.map((url, index) => ({
40+
// Initialize podcast data without titles first
41+
const initialPodcastData: PodcastData[] = podcastUrls.map((url, index) => ({
4042
id: String(index + 1),
4143
spotifyUrl: url,
42-
type: getSpotifyContentType(url)
44+
type: getSpotifyContentType(url),
4345
}));
4446

45-
interface SpotifyTitleProps {
46-
spotifyUrl: string;
47-
type: 'episode' | 'show' | 'playlist';
48-
}
49-
50-
// Fetches the podcast/show/episode title from Spotify oEmbed API
51-
const SpotifyTitle: React.FC<SpotifyTitleProps> = ({ spotifyUrl, type }) => {
52-
const [title, setTitle] = React.useState<string>('');
53-
const [loading, setLoading] = React.useState(true);
54-
55-
React.useEffect(() => {
56-
let cancelled = false;
57-
setLoading(true);
58-
fetch(`https://open.spotify.com/oembed?url=${encodeURIComponent(spotifyUrl)}`)
59-
.then(res => res.json())
60-
.then(data => {
61-
if (!cancelled) {
62-
setTitle(data.title);
63-
setLoading(false);
64-
}
65-
})
66-
.catch(() => {
67-
if (!cancelled) {
68-
setTitle('');
69-
setLoading(false);
70-
}
71-
});
72-
return () => { cancelled = true; };
73-
}, [spotifyUrl]);
74-
75-
return (
76-
<div className="podcast-title">
77-
{loading ? (
78-
<div className="title-skeleton">
79-
<div className="skeleton-line"></div>
80-
<div className="skeleton-line short"></div>
81-
</div>
82-
) : (
83-
<>
84-
<div className="podcast-type-badge">
85-
<span className="type-icon">
86-
{type === 'episode' ? '🎙️' : type === 'show' ? '📻' : '🎵'}
87-
</span>
88-
{type.charAt(0).toUpperCase() + type.slice(1)}
89-
</div>
90-
<h3 className="podcast-title-text">
91-
{title || `${type.charAt(0).toUpperCase() + type.slice(1)} #${Math.floor(Math.random() * 100) + 1}`}
92-
</h3>
93-
</>
94-
)}
47+
// Component to display Spotify title and type badge for each podcast
48+
const SpotifyTitle: React.FC<{ title?: string; type: 'episode' | 'show' | 'playlist' }> = ({ title, type }) => (
49+
<div className="podcast-title">
50+
<div className="podcast-type-badge">
51+
<span className="type-icon">
52+
{type === 'episode' ? '🎙️' : type === 'show' ? '📻' : '🎵'}
53+
</span>
54+
{type.charAt(0).toUpperCase() + type.slice(1)}
9555
</div>
96-
);
97-
};
56+
<h3 className="podcast-title-text">{title || 'Loading title...'}</h3>
57+
</div>
58+
);
9859

9960
export default function Podcasts(): ReactElement {
10061
const history = useHistory();
10162
const [currentPage, setCurrentPage] = useState(1);
10263
const [searchTerm, setSearchTerm] = useState("");
103-
const [selectedFilter, setSelectedFilter] = useState<
104-
"all" | "episode" | "show" | "playlist"
105-
>("all");
64+
const [selectedFilter, setSelectedFilter] = useState<"all" | "episode" | "show" | "playlist">("all");
10665
const [favorites, setFavorites] = useState<string[]>(() => {
107-
// Load favorites from localStorage on component mount
10866
if (typeof window !== "undefined") {
10967
const saved = localStorage.getItem("podcast-favorites");
11068
return saved ? JSON.parse(saved) : [];
11169
}
11270
return [];
11371
});
72+
const [podcasts, setPodcasts] = useState<PodcastData[]>(initialPodcastData);
11473
const podcastsPerPage = 9;
11574

116-
// Filter podcasts based on search and filter
117-
const filteredPodcasts = podcastData.filter(podcast => {
75+
// Fetch all podcast titles once on mount
76+
useEffect(() => {
77+
let cancelled = false;
78+
Promise.all(
79+
podcasts.map(p =>
80+
fetch(`https://open.spotify.com/oembed?url=${encodeURIComponent(p.spotifyUrl)}`)
81+
.then(res => res.json())
82+
.then(data => ({ id: p.id, title: data.title }))
83+
.catch(() => ({ id: p.id, title: '' }))
84+
)
85+
).then(results => {
86+
if (!cancelled) {
87+
// Merge fetched titles into podcasts state
88+
setPodcasts(prev =>
89+
prev.map(p => {
90+
const found = results.find(r => r.id === p.id);
91+
return found ? { ...p, title: found.title } : p;
92+
})
93+
);
94+
}
95+
});
96+
return () => { cancelled = true; };
97+
}, []);
98+
99+
// Filter podcasts based on search and filter using title now
100+
const filteredPodcasts = podcasts.filter(podcast => {
118101
const matchesFilter = selectedFilter === 'all' || podcast.type === selectedFilter;
119-
return matchesFilter;
102+
const matchesSearch =
103+
searchTerm === '' ||
104+
(podcast.title && podcast.title.toLowerCase().includes(searchTerm.toLowerCase()));
105+
return matchesFilter && matchesSearch;
120106
});
121107

122-
// Calculate podcasts for current page
108+
// Pagination calculations
123109
const indexOfLastPodcast = currentPage * podcastsPerPage;
124110
const indexOfFirstPodcast = indexOfLastPodcast - podcastsPerPage;
125111
const currentPodcasts = filteredPodcasts.slice(indexOfFirstPodcast, indexOfLastPodcast);
@@ -137,8 +123,7 @@ export default function Podcasts(): ReactElement {
137123
title: `Check out this ${podcast.type}`,
138124
url: podcast.spotifyUrl,
139125
});
140-
} catch (err) {
141-
// Fallback to clipboard
126+
} catch {
142127
navigator.clipboard.writeText(podcast.spotifyUrl);
143128
}
144129
} else {
@@ -147,20 +132,14 @@ export default function Podcasts(): ReactElement {
147132
};
148133

149134
const handleFavorite = (podcast: PodcastData, event: React.MouseEvent) => {
150-
// Prevent card click when clicking favorite button
151135
event.stopPropagation();
152136

153-
setFavorites((prev) => {
137+
setFavorites(prev => {
154138
const isFavorited = prev.includes(podcast.id);
155-
const newFavorites = isFavorited
156-
? prev.filter((id) => id !== podcast.id) // Remove from favorites
157-
: [...prev, podcast.id]; // Add to favorites
158-
159-
// Save to localStorage for persistence
139+
const newFavorites = isFavorited ? prev.filter(id => id !== podcast.id) : [...prev, podcast.id];
160140
if (typeof window !== "undefined") {
161141
localStorage.setItem("podcast-favorites", JSON.stringify(newFavorites));
162142
}
163-
164143
return newFavorites;
165144
});
166145
};
@@ -171,12 +150,11 @@ export default function Podcasts(): ReactElement {
171150
) => {
172151
const target = event.target as HTMLElement;
173152

174-
// Prevent navigation if clicking on buttons or action area
175153
if (
176154
target.tagName === "IFRAME" ||
177155
target.closest(".podcast-embed") ||
178-
target.closest(".action-btn") || // Don't navigate if clicking buttons
179-
target.closest(".card-actions") || // Don't navigate if clicking action area
156+
target.closest(".action-btn") ||
157+
target.closest(".card-actions") ||
180158
target.classList.contains("action-btn") ||
181159
target.classList.contains("favorite") ||
182160
target.classList.contains("share")
@@ -197,17 +175,13 @@ export default function Podcasts(): ReactElement {
197175
<span className="badge-icon">🎙️</span>
198176
<span className="badge-text">Premium Audio Content</span>
199177
</div>
200-
<h1 className="podcast-hero-title">
201-
Discover Top Podcasts
202-
</h1>
178+
<h1 className="podcast-hero-title">Discover Top Podcasts</h1>
203179
<p className="podcast-hero-description">
204180
Stream the best podcasts from your favorite stations. Dive into episodes that inspire, educate, and entertain from leading voices in tech, business, and beyond.
205181
</p>
206-
207-
{/* Stats */}
208182
<div className="podcast-stats">
209183
<div className="stat-item">
210-
<div className="stat-number">{podcastData.length}+</div>
184+
<div className="stat-number">{podcasts.length}+</div>
211185
<div className="stat-label">Episodes</div>
212186
</div>
213187
<div className="stat-item">
@@ -230,7 +204,7 @@ export default function Podcasts(): ReactElement {
230204
type="text"
231205
placeholder="Search podcasts..."
232206
value={searchTerm}
233-
onChange={(e) => setSearchTerm(e.target.value)}
207+
onChange={e => setSearchTerm(e.target.value)}
234208
className="search-input"
235209
/>
236210
</div>
@@ -240,28 +214,28 @@ export default function Podcasts(): ReactElement {
240214
onClick={() => setSelectedFilter('all')}
241215
>
242216
<span className="tab-icon">📊</span>
243-
All ({podcastData.length})
217+
All ({podcasts.length})
244218
</button>
245219
<button
246220
className={`filter-tab ${selectedFilter === 'episode' ? 'active' : ''}`}
247221
onClick={() => setSelectedFilter('episode')}
248222
>
249223
<span className="tab-icon">🎙️</span>
250-
Episodes ({podcastData.filter(p => p.type === 'episode').length})
224+
Episodes ({podcasts.filter(p => p.type === 'episode').length})
251225
</button>
252226
<button
253227
className={`filter-tab ${selectedFilter === 'show' ? 'active' : ''}`}
254228
onClick={() => setSelectedFilter('show')}
255229
>
256230
<span className="tab-icon">📻</span>
257-
Shows ({podcastData.filter(p => p.type === 'show').length})
231+
Shows ({podcasts.filter(p => p.type === 'show').length})
258232
</button>
259233
<button
260234
className={`filter-tab ${selectedFilter === 'playlist' ? 'active' : ''}`}
261235
onClick={() => setSelectedFilter('playlist')}
262236
>
263237
<span className="tab-icon">🎵</span>
264-
Playlists ({podcastData.filter(p => p.type === 'playlist').length})
238+
Playlists ({podcasts.filter(p => p.type === 'playlist').length})
265239
</button>
266240
</div>
267241
</div>
@@ -275,59 +249,60 @@ export default function Podcasts(): ReactElement {
275249
<div
276250
key={podcast.id}
277251
className="enhanced-podcast-card"
278-
onClick={(e) => handlePodcastClick(podcast, e)}
252+
onClick={e => handlePodcastClick(podcast, e)}
279253
role="button"
280254
tabIndex={0}
281-
onKeyDown={(e) => {
255+
onKeyDown={e => {
282256
if (e.key === 'Enter' || e.key === ' ') {
283257
handlePodcastClick(podcast, e);
284258
}
285259
}}
286260
style={{ animationDelay: `${index * 0.1}s` }}
287261
>
288262
<div className="podcast-card-header">
289-
<SpotifyTitle
290-
spotifyUrl={podcast.spotifyUrl}
291-
type={podcast.type}
292-
/>
263+
<SpotifyTitle title={podcast.title} type={podcast.type} />
293264
<div
294265
className="card-actions"
295-
onClick={(e) => {
266+
onClick={e => {
296267
e.stopPropagation();
297268
e.preventDefault();
298269
}}
299-
onMouseDown={(e) => {
270+
onMouseDown={e => {
300271
e.stopPropagation();
301272
}}
302273
>
303274
<button
304275
className={`action-btn favorite unfavorited ${
305-
favorites.includes(podcast.id) ? "favorited" : ""
276+
favorites.includes(podcast.id) ? 'favorited' : ''
306277
}`}
307278
title={
308279
favorites.includes(podcast.id)
309-
? "Remove from favorites"
310-
: "Add to favorites"
280+
? 'Remove from favorites'
281+
: 'Add to favorites'
311282
}
312-
onClick={(e) => {
283+
onClick={e => {
313284
e.preventDefault();
314285
e.stopPropagation();
315286
e.nativeEvent.stopImmediatePropagation();
316287
handleFavorite(podcast, e);
317288
}}
318289
>
319-
{favorites.includes(podcast.id) ? '🤍' : '❤️'}
290+
{favorites.includes(podcast.id) ? '🤍' : '❤️'}
320291
</button>
321-
<button className="action-btn share" title="Share podcast" onClick={(e) => {
292+
<button
293+
className="action-btn share"
294+
title="Share podcast"
295+
onClick={e => {
322296
e.stopPropagation();
323297
handleShare(podcast);
324-
}}>
298+
}}
299+
>
325300
🔗
326301
</button>
327302
</div>
328303
</div>
329304

330-
<div className="podcast-embed" onClick={(e) => e.stopPropagation()}>
305+
<div className="podcast-embed" onClick={e => e.stopPropagation()}>
331306
<iframe
332307
src={`https://open.spotify.com/embed/${podcast.type}/${getSpotifyEmbedId(podcast.spotifyUrl)}`}
333308
width="100%"
@@ -362,14 +337,14 @@ export default function Podcasts(): ReactElement {
362337
</button>
363338

364339
<div className="pagination-numbers">
365-
{Array.from({ length: totalPages }, (_, i) => i + 1).map((number) => (
366-
<button
367-
key={number}
340+
{Array.from({ length: totalPages }, (_, i) => i + 1).map(number => (
341+
<button
342+
key={number}
368343
className={`pagination-number ${currentPage === number ? 'active' : ''}`}
369-
onClick={() => handlePageChange(number)}
370-
>
371-
{number}
372-
</button>
344+
onClick={() => handlePageChange(number)}
345+
>
346+
{number}
347+
</button>
373348
))}
374349
</div>
375350

0 commit comments

Comments
 (0)