Files
lidify/frontend/features/podcast/hooks/usePodcastData.ts
2025-12-25 18:58:06 -06:00

140 lines
4.3 KiB
TypeScript

"use client";
import { useState, useEffect, useRef } from "react";
import { useParams, useRouter } from "next/navigation";
import { usePodcastQuery } from "@/hooks/useQueries";
import { useAuth } from "@/lib/auth-context";
import { api } from "@/lib/api";
import { Podcast, PodcastPreview, SimilarPodcast, Episode } from "../types";
export function usePodcastData() {
const params = useParams();
const router = useRouter();
const { isAuthenticated } = useAuth();
const podcastId = params.id as string;
// Use React Query hook for podcast
const { data: podcast, isLoading: isPodcastLoading } =
usePodcastQuery(podcastId);
// State for preview mode, subscription, and similar podcasts
const [previewData, setPreviewData] = useState<PodcastPreview | null>(null);
const [previewLoadState, setPreviewLoadState] = useState<'idle' | 'loading' | 'done' | 'error'>('idle');
const [similarPodcasts, setSimilarPodcasts] = useState<SimilarPodcast[]>([]);
const [sortOrder, setSortOrder] = useState<"newest" | "oldest">(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem(
`lidify_podcast_sort_order_${podcastId}`
);
return (saved as "newest" | "oldest") || "newest";
}
return "newest";
});
// Get raw cover URL
const rawCoverUrl = podcast?.coverUrl || previewData?.coverUrl;
const heroImage = rawCoverUrl ? api.getCoverArtUrl(rawCoverUrl, 1200) : null;
// Load similar podcasts when podcast data is available
useEffect(() => {
if (podcast && isAuthenticated) {
loadSimilarPodcasts();
}
}, [podcast?.id, isAuthenticated]);
// Handle preview mode if podcast is not subscribed
useEffect(() => {
if (isPodcastLoading) return;
// If query returned no data, try preview mode
if (!podcast && isAuthenticated && previewLoadState === 'idle') {
loadPreviewData();
}
}, [isPodcastLoading, podcast, isAuthenticated, podcastId, previewLoadState]);
// Save sort order to localStorage when it changes
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem(
`lidify_podcast_sort_order_${podcastId}`,
sortOrder
);
}
}, [sortOrder, podcastId]);
async function loadSimilarPodcasts() {
try {
const similar = await api.getSimilarPodcasts(podcastId);
setSimilarPodcasts(similar);
} catch (error) {
console.error("Failed to load similar podcasts:", error);
}
}
async function loadPreviewData() {
setPreviewLoadState('loading');
try {
const preview = await api.previewPodcast(podcastId);
setPreviewData(preview);
setPreviewLoadState('done');
// If already subscribed, redirect
if (preview.isSubscribed && preview.subscribedPodcastId) {
router.replace(`/podcasts/${preview.subscribedPodcastId}`);
}
} catch (error) {
console.error("Failed to load preview:", error);
setPreviewLoadState('error');
}
}
// Computed values
const displayData = podcast || (previewData ? {
title: previewData.title,
author: previewData.author,
description: previewData.description,
coverUrl: previewData.coverUrl,
genres: previewData.genres,
episodes: [],
} : null);
const inProgressEpisodes = podcast
? podcast.episodes.filter(
(ep: Episode) =>
ep.progress &&
!ep.progress.isFinished &&
ep.progress.currentTime > 0
)
: [];
// Sort episodes based on selected order
const sortedEpisodes = podcast
? [...podcast.episodes].sort((a: Episode, b: Episode) => {
const dateA = new Date(a.publishedAt).getTime();
const dateB = new Date(b.publishedAt).getTime();
return sortOrder === "newest" ? dateB - dateA : dateA - dateB;
})
: [];
// Determine loading state:
// - Loading if podcast query is loading
// - Loading if podcast query done but no podcast and we haven't tried/finished preview yet
const needsPreviewLoad = !podcast && previewLoadState === 'idle';
const isLoadingPreview = previewLoadState === 'loading';
const isLoading = isPodcastLoading || needsPreviewLoad || isLoadingPreview;
return {
podcastId,
podcast: podcast as Podcast | undefined,
previewData,
displayData,
isLoading,
heroImage,
similarPodcasts,
sortOrder,
setSortOrder,
inProgressEpisodes,
sortedEpisodes,
};
}