src/blackrock/useMarketFeed.ts
import {useCallback,useEffect,useRef,useState} from 'react';
import {api} from './api';
import {appendMarketPage,emptyMarketPages,type MarketPage} from './market-board-model';
/** Mount with a key for the query/sort/window. Pausing for item details keeps rows and scroll. */
export function useMarketFeed(path:string,active:boolean,automatic:boolean,atTop:()=>boolean){
const [state,setState]=useState(()=>({...emptyMarketPages(),loading:false,error:null as string|null}));
const current=useRef(state);current.current=state;
const failed=useRef<{offset:number;replace:boolean}|null>(null);
const alive=useRef(true),busy=useRef(false),controller=useRef<AbortController|null>(null),enabled=useRef(active);enabled.current=active;
useEffect(()=>{alive.current=true;return()=>{alive.current=false;controller.current?.abort();}},[]);
const request=useCallback(async(offset:number,replace=false)=>{
if(busy.current||!enabled.current)return false;
busy.current=true;failed.current=null;controller.current=new AbortController();setState(s=>({...s,loading:true,error:null}));
try{
const page=await api<MarketPage>(`${path}&offset=${offset}`,{signal:controller.current.signal});
if(!alive.current)return false;
const next=appendMarketPage(replace?emptyMarketPages():current.current,page,offset);
const value={...next,loading:false,error:null};current.current=value;setState(value);return true;
}catch(error){failed.current={offset,replace};if(alive.current)setState(s=>({...s,loading:false,error:(error as Error).message}));return false;}
finally{busy.current=false;}
},[path]);
useEffect(()=>{if(active&&!current.current.pages.length&&!current.current.error)void request(0,true);},[active,request]);
useEffect(()=>{
if(!active||!automatic)return;
const timer=setInterval(()=>{if(current.current.pages.length<=1&&atTop()&&document.visibilityState==='visible')void request(0,true);},60000);
return()=>clearInterval(timer);
},[active,automatic,request,atTop]);
return {...state,loadMore:()=>{const next=current.current.nextOffset;if(next!==null)void request(next);},retry:()=>{const last=failed.current;if(last)void request(last.offset,last.replace);},refresh:()=>request(0,true)};
}