Show discovery progress in the top-bar indicator alongside downloads/tasks

The DownloadIndicator in Layout.jsx now queries discovery-status and shows
discovery progress the same way it shows active downloads and channel tasks:
- Spinning icon appears in the navbar when discovery is running
- Hover popover shows "Discovering channels" with X/Y count and a progress bar
- Polls every 10 s while running, 60 s when idle
- Primary label priority: download % > task phase > discovery X/Y

Discovery page header simplified: progress bar and verbose status removed
since the top-bar indicator now handles it. "Find more" button still
disables while running.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 02:58:13 +02:00
parent 1179b53f2e
commit bcbd552eab
2 changed files with 43 additions and 30 deletions

View File

@@ -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() {
<div className="flex flex-col gap-6">
{/* Header */}
<div className="flex items-center justify-between gap-4">
<div className="min-w-0 flex-1">
<div className="min-w-0">
<h1 className="font-display font-bold text-2xl text-zinc-100">Discover</h1>
{discStatus && (
<p className="text-xs text-zinc-500 mt-0.5">
{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"}
</p>
)}
{discStatus?.progress?.running && (
<div className="mt-1.5 h-1 w-48 rounded-full bg-zinc-800 overflow-hidden">
<div
className="h-full bg-accent rounded-full transition-all duration-500"
style={{ width: `${Math.round((discStatus.progress.done / discStatus.progress.total) * 100)}%` }}
/>
</div>
)}
</div>
<button
onClick={() => refreshMut.mutate()}
disabled={refreshMut.isPending || discStatus?.progress?.running}
className="shrink-0 flex items-center gap-2 text-sm font-medium px-4 py-2 bg-zinc-800 text-zinc-300 rounded-lg hover:bg-zinc-700 transition-colors disabled:opacity-60"
>
{discStatus?.progress?.running ? (
{refreshMut.isPending && (
<svg className="w-3.5 h-3.5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" />
</svg>
) : refreshMut.isPending ? (
<svg className="w-3.5 h-3.5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" />
</svg>
) : null}
)}
{discStatus?.progress?.running ? "Running…" : refreshMut.isSuccess ? "Queued ✓" : refreshMut.isPending ? "Queueing…" : "Find more"}
</button>
</div>
{(refreshMut.isSuccess || discStatus?.progress?.running) && (
{refreshMut.isSuccess && !discStatus?.progress?.running && (
<div className="px-4 py-3 rounded-xl bg-zinc-800/60 border border-zinc-700/50 text-sm text-zinc-400">
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.
</div>
)}