@@ -142,9 +142,9 @@ export function TopicSelector({
142142 "LAW_OR_POLICY" ,
143143 ]
144144
145- // Fetch trending topics
145+ // Fetch trending topics with retry logic
146146 useEffect ( ( ) => {
147- const fetchTopics = async ( ) => {
147+ const fetchTopics = async ( retryCount = 0 ) => {
148148 try {
149149 setIsLoading ( true )
150150 setError ( "" )
@@ -154,23 +154,44 @@ export function TopicSelector({
154154 ? `&topicTypes=${ selectedTopicTypes . join ( "," ) } `
155155 : ""
156156
157+ // Add cache-busting parameter to prevent stale cache issues
158+ const timestamp = Date . now ( )
157159 const url = enableDiversity
158- ? `/api/topics?limit=20&timeWindow=48&diverse=true${ topicTypesParam } `
159- : `/api/topics?limit=20&timeWindow=48${ topicTypesParam } `
160+ ? `/api/topics?limit=20&timeWindow=48&diverse=true&cache_bust= ${ timestamp } ${ topicTypesParam } `
161+ : `/api/topics?limit=20&timeWindow=48&cache_bust= ${ timestamp } ${ topicTypesParam } `
160162
161- const response = await fetch ( url )
163+ const response = await fetch ( url , {
164+ cache : "no-store" ,
165+ headers : {
166+ "Cache-Control" : "no-cache" ,
167+ Pragma : "no-cache" ,
168+ } ,
169+ } )
162170
163171 if ( ! response . ok ) {
164172 throw new Error ( `Failed to fetch topics: ${ response . status } ` )
165173 }
166174
167175 const data = await response . json ( )
168176 setTopics ( data . topics || [ ] )
177+ setError ( "" ) // Clear any previous errors
178+ setIsLoading ( false ) // Always clear loading on successful response
169179 } catch ( err ) {
170180 console . error ( "Error fetching topics:" , err )
171- setError ( err instanceof Error ? err . message : "Failed to load topics" )
172- } finally {
173- setIsLoading ( false )
181+
182+ // Retry logic: retry up to 3 times with exponential backoff
183+ if ( retryCount < 3 ) {
184+ const delay = Math . pow ( 2 , retryCount ) * 1000 // 1s, 2s, 4s
185+ console . log (
186+ `Retrying fetch topics in ${ delay } ms (attempt ${ retryCount + 1 } /3)`
187+ )
188+ setTimeout ( ( ) => {
189+ fetchTopics ( retryCount + 1 )
190+ } , delay )
191+ } else {
192+ setError ( err instanceof Error ? err . message : "Failed to load topics" )
193+ setIsLoading ( false ) // Clear loading on final failure
194+ }
174195 }
175196 }
176197
@@ -334,7 +355,49 @@ export function TopicSelector({
334355 < Button
335356 variant = "outline"
336357 size = "sm"
337- onClick = { ( ) => window . location . reload ( ) }
358+ onClick = { ( ) => {
359+ setError ( "" )
360+ setIsLoading ( true )
361+ // Trigger a new fetch by updating a dependency
362+ const timestamp = Date . now ( )
363+ const topicTypesParam =
364+ selectedTopicTypes . length > 0
365+ ? `&topicTypes=${ selectedTopicTypes . join ( "," ) } `
366+ : ""
367+ const url = enableDiversity
368+ ? `/api/topics?limit=20&timeWindow=48&diverse=true&cache_bust=${ timestamp } ${ topicTypesParam } `
369+ : `/api/topics?limit=20&timeWindow=48&cache_bust=${ timestamp } ${ topicTypesParam } `
370+
371+ fetch ( url , {
372+ cache : "no-store" ,
373+ headers : {
374+ "Cache-Control" : "no-cache" ,
375+ Pragma : "no-cache" ,
376+ } ,
377+ } )
378+ . then ( ( response ) => {
379+ if ( ! response . ok )
380+ throw new Error (
381+ `Failed to fetch topics: ${ response . status } `
382+ )
383+ return response . json ( )
384+ } )
385+ . then ( ( data ) => {
386+ setTopics ( data . topics || [ ] )
387+ setError ( "" )
388+ } )
389+ . catch ( ( err ) => {
390+ console . error ( "Error fetching topics:" , err )
391+ setError (
392+ err instanceof Error
393+ ? err . message
394+ : "Failed to load topics"
395+ )
396+ } )
397+ . finally ( ( ) => {
398+ setIsLoading ( false )
399+ } )
400+ } }
338401 >
339402 Try Again
340403 </ Button >
@@ -362,12 +425,12 @@ export function TopicSelector({
362425 < Card className = { `border-primary/20 ${ className } ` } >
363426 < CardContent className = "p-3" >
364427 < div className = "mb-3" >
365- < div className = "mb-2" >
366- < h3 className = "mb-2 flex items-center gap-2 text-base font-semibold sm:mb-0 " >
428+ < div className = "mb-2 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between " >
429+ < h3 className = "flex items-center gap-2 text-base font-semibold" >
367430 < TrendingUp className = "h-4 w-4" />
368431 { showSearchResults ? "Search Results" : "Trending Topics" }
369432 </ h3 >
370- < div className = "flex flex-wrap items-center gap-2 sm:justify-end " >
433+ < div className = "flex flex-wrap items-center gap-2" >
371434 { ! showSearchResults && (
372435 < Button
373436 variant = "ghost"
@@ -424,11 +487,6 @@ export function TopicSelector({
424487 { /* Topic Type Filter */ }
425488 { enableTopicTypeFilter && (
426489 < div className = "mb-3" >
427- < div className = "mb-1" >
428- < span className = "text-xs font-medium text-muted-foreground" >
429- Filter by type:
430- </ span >
431- </ div >
432490 < div className = "flex flex-wrap gap-1" >
433491 { availableTopicTypes . map ( ( type ) => {
434492 const isSelected = selectedTopicTypes . includes ( type )
0 commit comments