fix(news): auto-retry news fetch when backend Celery task is in-flight

When a bill page opens with no stored articles, the backend queues a
fetch_news_for_bill Celery task and returns immediately. Added a retry
loop (up to 3x, 6 s apart) driven off newsArticles state so articles
populate without a manual refresh. Fixed broken useEffect dependency
([billId] → [newsArticles]) that caused the timer to never fire.
News is now fetched via a separate useBillNews query (staleTime: 0)
independent of the cached bill detail response.

Co-Authored-By: Jack Levy
This commit is contained in:
Jack Levy
2026-03-01 03:17:17 -05:00
parent d5711312b8
commit 12a3eac48f
2 changed files with 21 additions and 4 deletions

View File

@@ -1,9 +1,9 @@
"use client";
import { use } from "react";
import { use, useEffect, useRef } from "react";
import Link from "next/link";
import { ArrowLeft, ExternalLink, User } from "lucide-react";
import { useBill, useBillTrend } from "@/lib/hooks/useBills";
import { useBill, useBillNews, useBillTrend } from "@/lib/hooks/useBills";
import { BriefPanel } from "@/components/bills/BriefPanel";
import { ActionTimeline } from "@/components/bills/ActionTimeline";
import { TrendChart } from "@/components/bills/TrendChart";
@@ -17,6 +17,23 @@ export default function BillDetailPage({ params }: { params: Promise<{ id: strin
const { data: bill, isLoading } = useBill(billId);
const { data: trendData } = useBillTrend(billId, 30);
const { data: newsArticles, refetch: refetchNews } = useBillNews(billId);
// When the bill page is opened with no stored articles, the backend queues
// a Celery news-fetch task that takes a few seconds to complete.
// Retry up to 3 times (every 6 s) so articles appear without a manual refresh.
// newsRetryRef resets on bill navigation so each bill gets its own retry budget.
const newsRetryRef = useRef(0);
useEffect(() => { newsRetryRef.current = 0; }, [billId]);
useEffect(() => {
if (newsArticles === undefined || newsArticles.length > 0) return;
if (newsRetryRef.current >= 3) return;
const timer = setTimeout(() => {
newsRetryRef.current += 1;
refetchNews();
}, 6000);
return () => clearTimeout(timer);
}, [newsArticles]); // eslint-disable-line react-hooks/exhaustive-deps
if (isLoading) {
return <div className="text-center py-20 text-muted-foreground">Loading bill...</div>;
@@ -85,7 +102,7 @@ export default function BillDetailPage({ params }: { params: Promise<{ id: strin
</div>
<div className="space-y-4">
<TrendChart data={trendData} />
<NewsPanel articles={bill.news_articles} />
<NewsPanel articles={newsArticles} />
</div>
</div>
</div>