From 992922dd59daded2b12064437a0690c3784a0b0b Mon Sep 17 00:00:00 2001 From: iitzIrFan Date: Fri, 23 May 2025 18:21:17 +0530 Subject: [PATCH 1/3] feat: Implement podcast page with Spotify integration and pagination --- docusaurus.config.ts | 2 +- src/pages/podcasts/index.css | 176 +++++++++++++++++++++++++++++++++++ src/pages/podcasts/index.tsx | 120 ++++++++++++++++++++++++ 3 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 src/pages/podcasts/index.css create mode 100644 src/pages/podcasts/index.tsx diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 49882505..3648297a 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -199,7 +199,7 @@ const config: Config = { }, { label: "🎙️ Podcast", - to: "https://open.spotify.com/show/6oPJ7ZBlN7y34yiSMguIda", + to: "podcasts/", }, ], }, diff --git a/src/pages/podcasts/index.css b/src/pages/podcasts/index.css new file mode 100644 index 00000000..3e6c491c --- /dev/null +++ b/src/pages/podcasts/index.css @@ -0,0 +1,176 @@ +.podcast-container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +.podcast-subtitle { + color: #666; + font-size: 1.2rem; + margin-bottom: 2rem; + text-align: center; +} + +h1 { + text-align: center; + margin-bottom: 1rem; +} + +.podcast-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + margin-top: 2rem; +} + +.podcast-card { + background: #fff; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; +} + +.podcast-card:hover { + transform: translateY(-4px); +} + +.podcast-content { + height: 100%; +} + +.podcast-info { + padding: 1rem; + background: rgba(0, 0, 0, 0.05); + border-radius: 8px 8px 0 0; +} + +.podcast-info h3 { + margin: 0 0 0.5rem 0; + font-size: 1.2rem; + color: #333; +} + +.podcast-description { + font-size: 0.9rem; + color: #666; + margin-bottom: 0.5rem; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} + +.podcast-publisher { + font-size: 0.8rem; + color: #888; + margin: 0; + font-style: italic; +} + +.podcast-embed { + height: 100%; + border-radius: 0 0 12px 12px; + overflow: hidden; +} + +.podcast-embed iframe { + border: none; + width: 100%; + height: 100%; + min-height: 352px; + background: #282828; + border-radius: 12px; +} + +.pagination { + display: flex; + justify-content: center; + gap: 0.5rem; + margin-top: 2rem; + padding: 1rem; +} + +.pagination-button { + padding: 0.5rem 1rem; + border: 1px solid #ddd; + background: white; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s ease; +} + +.pagination-button:hover { + background: #f0f0f0; +} + +.pagination-button.active { + background: #0066cc; + color: white; + border-color: #0066cc; +} + +@media (max-width: 968px) { + .podcast-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + .podcast-grid { + grid-template-columns: 1fr; + } + + .podcast-container { + padding: 1rem; + } +} + +/* Podcast Details Page Styles */ +.back-button { + margin-bottom: 1rem; + padding: 0.5rem 1rem; + border: none; + background-color: #f0f0f0; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + color: #333; + display: inline-flex; + align-items: center; + transition: background-color 0.2s; +} + +.back-button:hover { + background-color: #e0e0e0; +} + +.podcast-details { + max-width: 800px; + margin: 0 auto; + padding-top: 1rem; +} + +.podcast-embed-large { + margin-bottom: 2rem; +} + +.podcast-transcript { + background: #fff; + padding: 2rem; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.podcast-transcript h2 { + margin-bottom: 1rem; + color: #333; +} + +.podcast-card { + cursor: pointer; +} \ No newline at end of file diff --git a/src/pages/podcasts/index.tsx b/src/pages/podcasts/index.tsx new file mode 100644 index 00000000..e709bbb0 --- /dev/null +++ b/src/pages/podcasts/index.tsx @@ -0,0 +1,120 @@ +import React, { useState } from 'react'; +import Layout from '@theme/Layout'; +import type { ReactElement } from 'react'; +import { useHistory } from '@docusaurus/router'; +import './index.css'; + +interface PodcastData { + id: string; + spotifyUrl: string; + type: 'episode' | 'show' | 'playlist'; +} + +// Function to extract Spotify ID from URL +const getSpotifyEmbedId = (url: string): string => { + const match = url.match(/(?:spotify\.com|open\.spotify\.com)\/(episode|show|playlist)\/([a-zA-Z0-9]+)/); + return match ? match[2] : url; +}; + +// Function to determine content type from URL +const getSpotifyContentType = (url: string): 'episode' | 'show' | 'playlist' => { + const match = url.match(/(?:spotify\.com|open\.spotify\.com)\/(episode|show|playlist)/); + return (match?.[1] as 'episode' | 'show' | 'playlist') || 'show'; +}; + +// Add your podcasts here +const podcastUrls: string[] = [ + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", + "https://open.spotify.com/playlist/5EClSKDUVtkZMIRi6wgW5u?si=c7b90bfc62544845&pt=c4580022e1bd545fde3a71a58efe2d1a", +]; + +const podcastData: PodcastData[] = podcastUrls.map((url, index) => ({ + id: String(index + 1), + spotifyUrl: url, + type: getSpotifyContentType(url) +})); + +export default function Podcasts(): ReactElement { const history = useHistory(); + const [currentPage, setCurrentPage] = useState(1); + const podcastsPerPage = 9; + + // Calculate podcasts for current page + const indexOfLastPodcast = currentPage * podcastsPerPage; + const indexOfFirstPodcast = indexOfLastPodcast - podcastsPerPage; + const currentPodcasts = podcastData.slice(indexOfFirstPodcast, indexOfLastPodcast); + const totalPages = Math.ceil(podcastData.length / podcastsPerPage); + + const handlePageChange = (pageNumber: number) => { + setCurrentPage(pageNumber); + }; + + const handlePodcastClick = (podcast: PodcastData, event: React.MouseEvent | React.KeyboardEvent) => { + const target = event.target as HTMLElement; + if (target.tagName === 'IFRAME' || target.className === 'podcast-embed') { + return; + } + history.push('/podcasts/details', { podcast }); + }; + + return ( + +
+

Discover Top Podcasts

+

Stream the best podcasts from your favorite stations

+
+ {currentPodcasts.map((podcast) => ( +
handlePodcastClick(podcast, e)} + role="button" + tabIndex={0} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handlePodcastClick(podcast, e); + } + }} + > +
+
+

Podcast

+
+
e.stopPropagation()}> +