diff --git a/frontend/src/components/Layout.jsx b/frontend/src/components/Layout.jsx index 6a7fdd9..87a7aea 100644 --- a/frontend/src/components/Layout.jsx +++ b/frontend/src/components/Layout.jsx @@ -3,7 +3,7 @@ import { useQuery } from "@tanstack/react-query"; import { useRef, useEffect } from "react"; import { useAuth } from "../hooks/useAuth"; import SearchBar from "./SearchBar"; -import { getDownloads, getChannels, getActiveTasks, getMe } from "../api"; +import { getDownloads, getChannels, getActiveTasks, getDiscoveryStatus, getMe } from "../api"; function BottomNav({ newCount }) { const tabs = [ @@ -91,20 +91,29 @@ function DownloadIndicator() { refetchInterval: (query) => (query.state.data?.length > 0 ? 2000 : 10_000), }); + const { data: discStatus } = useQuery({ + queryKey: ["discovery-status"], + queryFn: () => getDiscoveryStatus().then((r) => r.data), + refetchInterval: (query) => (query.state.data?.progress?.running ? 10_000 : 60_000), + staleTime: 10_000, + }); + const activeDownloads = (downloads ?? []).filter( (d) => d.status === "pending" || d.status === "downloading" ); + const discRunning = discStatus?.progress?.running; + const discProgress = discStatus?.progress; - if (!activeDownloads.length && !tasks.length) return null; + if (!activeDownloads.length && !tasks.length && !discRunning) return null; - const totalActive = activeDownloads.length + tasks.length; + const totalActive = activeDownloads.length + tasks.length + (discRunning ? 1 : 0); - // Show download progress if there are active downloads, otherwise show task phase + // Primary label: download % > task phase > discovery let label; if (activeDownloads.length) { const pct = activeDownloads[0].progress_percent ?? 0; label = {pct.toFixed(0)}%; - } else { + } else if (tasks.length) { const task = tasks[0]; const pct = task.total > 0 ? Math.round((task.done / task.total) * 100) : null; label = ( @@ -112,12 +121,18 @@ function DownloadIndicator() { {pct !== null ? `${pct}%` : task.phase || "…"} ); + } else { + label = ( + + {discProgress ? `${discProgress.done}/${discProgress.total}` : "…"} + + ); } return (
@@ -163,6 +178,19 @@ function DownloadIndicator() {
); })} + {discRunning && discProgress && ( +
+
+

Discovering channels

+ {discProgress.done}/{discProgress.total} +
+

Finding channels… spaced over ~20 min

+
+
+
+
+ )}
diff --git a/frontend/src/pages/Discovery.jsx b/frontend/src/pages/Discovery.jsx index bcd8d8a..1b3ce05 100644 --- a/frontend/src/pages/Discovery.jsx +++ b/frontend/src/pages/Discovery.jsx @@ -218,7 +218,7 @@ export default function DiscoveryPage() { queryKey: ["discovery-status"], queryFn: () => getDiscoveryStatus().then(r => r.data), staleTime: 10_000, - refetchInterval: (query) => query.state.data?.progress?.running ? 10_000 : 60_000, + refetchInterval: (query) => (query.state.data?.progress?.running ? 10_000 : 60_000), }); const refreshMut = useMutation({ @@ -248,52 +248,37 @@ export default function DiscoveryPage() {
{/* Header */}
-
+

Discover

{discStatus && (

- {discStatus.progress?.running - ? `Finding channels… ${discStatus.progress.done} / ${discStatus.progress.total}` - : discStatus.pending_count > 0 - ? `${discStatus.pending_count} channel${discStatus.pending_count !== 1 ? "s" : ""} queued` - : "Queue empty"} + {discStatus.pending_count > 0 + ? `${discStatus.pending_count} channel${discStatus.pending_count !== 1 ? "s" : ""} queued` + : "Queue empty"} {discStatus.last_run ? ` · last refreshed ${new Date(discStatus.last_run + "Z").toLocaleDateString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" })}` : " · never refreshed"}

)} - {discStatus?.progress?.running && ( -
-
-
- )}
- {(refreshMut.isSuccess || discStatus?.progress?.running) && ( + {refreshMut.isSuccess && !discStatus?.progress?.running && (
- Discovery is running in the background — searches and channel fetches are spaced out over ~20 minutes to avoid hitting limits. New channels appear as each batch completes. Runs automatically every day. + Discovery is running — progress shows in the top bar. Searches are spaced out over ~20 minutes. Runs automatically every day.
)}