diff --git a/src/pages/podcasts/details.css b/src/pages/podcasts/details.css index 534932f4..0952fc77 100644 --- a/src/pages/podcasts/details.css +++ b/src/pages/podcasts/details.css @@ -206,6 +206,15 @@ html[data-theme='light'] { justify-content: center; } +.nav-action-button:active { + box-shadow: 0 1px 8px rgba(40,50,70,0.06); +} + +.nav-action-button.favorite.favorited { + color: #ff4d4d; /* red heart */ + background: #fff0f0; /* soft pink background when liked */ +} + .nav-action-button:hover { background: var(--details-bg-card-hover); color: var(--details-text-primary); diff --git a/src/pages/podcasts/details.tsx b/src/pages/podcasts/details.tsx index 9adbaf72..d699c5fb 100644 --- a/src/pages/podcasts/details.tsx +++ b/src/pages/podcasts/details.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import Layout from '@theme/Layout'; import type { ReactElement } from 'react'; import { useLocation, useHistory } from '@docusaurus/router'; @@ -85,7 +85,26 @@ export default function PodcastDetails(): ReactElement { const history = useHistory(); const state = location.state as LocationState; const podcast = state?.podcast; - + + const [favorites, setFavorites] = useState(() => { + if (typeof window !== "undefined") { + const saved = localStorage.getItem("podcast-favorites"); + return saved ? JSON.parse(saved) : []; + } + return []; + }); + const isFavorited = podcast ? favorites.includes(podcast.id) : false; + const toggleFavorite = () => { + if (!podcast) return; + setFavorites(prev => { + const updated = prev.includes(podcast.id) + ? prev.filter(id => id !== podcast.id) + : [...prev, podcast.id]; + localStorage.setItem("podcast-favorites", JSON.stringify(updated)); + return updated; + }); + }; + // Enhanced descriptions with categories const descriptions = { episode: [ @@ -128,7 +147,7 @@ export default function PodcastDetails(): ReactElement { history.goBack(); }; - const handleShare = async () => { + const handleShare = async (podcast?: PodcastData) => { if (navigator.share) { try { await navigator.share({ @@ -174,13 +193,25 @@ export default function PodcastDetails(): ReactElement { Back to Podcasts
- - -
+ + + {/* Main Content */} @@ -247,7 +278,7 @@ export default function PodcastDetails(): ReactElement {
- diff --git a/src/pages/podcasts/index.css b/src/pages/podcasts/index.css index 2fb6c736..a7c677ad 100644 --- a/src/pages/podcasts/index.css +++ b/src/pages/podcasts/index.css @@ -359,6 +359,7 @@ html[data-theme='light'] { transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); cursor: pointer; position: relative; + z-index: 1; overflow: hidden; animation: fadeInUp 0.6s ease-out both; } @@ -404,12 +405,40 @@ html[data-theme='light'] { gap: 8px; opacity: 0; transition: opacity 0.3s ease; + position: relative; + z-index: 10; + pointer-events: auto; } .enhanced-podcast-card:hover .card-actions { opacity: 1; } +.action-btn.share { + background-color: rgba(123, 124, 128, 0.15); /* same subtle bg as unfavorited */ + border: 1px solid rgba(123, 124, 128, 0.3); /* same border */ + transition: all 0.3s ease; + color: inherit; /* keep the existing icon color */ + border-radius: 8px; + width: 36px; + height: 36px; + cursor: pointer; + font-size: 14px; + position: relative; + z-index: 15; + user-select: none; +} + +.action-btn.share:hover { + background-color: rgba(123, 124, 128, 0.25); /* slightly darker on hover */ + border-color: rgba(123, 124, 128, 0.5); + transform: scale(1.1); +} + +.action-btn.share:active { + transform: scale(0.95); +} + .action-btn { width: 36px; height: 36px; @@ -419,12 +448,35 @@ html[data-theme='light'] { cursor: pointer; transition: all 0.3s ease; font-size: 14px; + position: relative; + z-index: 15; + pointer-events: auto !important; + user-select: none; } .action-btn:hover { background: var(--podcast-bg-card-hover); transform: scale(1.1); } +/* Enhanced favorite button styling */ +.action-btn.favorite { + transition: all 0.2s ease; + border: 1px solid transparent; +} + +.action-btn.favorite:active { + transform: scale(0.95); +} + +.action-btn.favorite.unfavorited { + background-color: rgba(123, 124, 128, 0.15); /* light purple-blue tint */ + color: white; /* keep the heart white */ + border-color: rgba(123, 124, 128, 0.3); +} + +.action-btn.favorite.unfavorited:hover { + background-color: rgba(123, 124, 128, 0.15); +} /* Podcast Title */ .podcast-title { diff --git a/src/pages/podcasts/index.tsx b/src/pages/podcasts/index.tsx index 2db7ff09..8676c83b 100644 --- a/src/pages/podcasts/index.tsx +++ b/src/pages/podcasts/index.tsx @@ -99,8 +99,18 @@ const SpotifyTitle: React.FC = ({ spotifyUrl, type }) => { 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 [searchTerm, setSearchTerm] = useState(""); + 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 podcastsPerPage = 9; // Filter podcasts based on search and filter @@ -136,17 +146,46 @@ export default function Podcasts(): ReactElement { } }; - const handlePodcastClick = (podcast: PodcastData, event: React.MouseEvent | React.KeyboardEvent) => { + const handleFavorite = (podcast: PodcastData, event: React.MouseEvent) => { + // Prevent card click when clicking favorite button + event.stopPropagation(); + + 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 + if (typeof window !== "undefined") { + localStorage.setItem("podcast-favorites", JSON.stringify(newFavorites)); + } + + return newFavorites; + }); + }; + + const handlePodcastClick = ( + podcast: PodcastData, + event: React.MouseEvent | React.KeyboardEvent + ) => { const target = event.target as HTMLElement; - if (target.tagName === 'IFRAME' || target.closest('.podcast-embed')) { + + // 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.classList.contains("action-btn") || + target.classList.contains("favorite") || + target.classList.contains("share") + ) { return; } - history.push('/podcasts/details', { podcast }); - }; - React.useEffect(() => { - setCurrentPage(1); - }, [searchTerm, selectedFilter]); + history.push("/podcasts/details", { podcast }); + }; return ( @@ -164,7 +203,7 @@ export default function Podcasts(): ReactElement {

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 */}
@@ -247,20 +286,47 @@ export default function Podcasts(): ReactElement { style={{ animationDelay: `${index * 0.1}s` }} >
- -
-
- +
e.stopPropagation()}>