Send a changed feed's row to the page instead of it reloading the list (#105)
After a feed was checked, failed or downloaded a file, and after every item read, the page fetched /api/feeds whole, about 60 ms for 160 rows, though one row had changed. The live event stream now knows who is connected and, after an event that changes a feed, sends that person its row (feed_row), built by the same code as the list (feed_rows, with Db::feed_list asked for one feed). Marking an item read answers with the feed's row. The page puts the row in place and redraws once a frame. A routine skip of a feed not due sends nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,6 @@ let sse;
|
||||
function connect(){
|
||||
sse=new EventSource('/api/events');
|
||||
const soon=(fn,ms=500)=>{ let t; return ()=>{ clearTimeout(t); t=setTimeout(fn,ms); }; };
|
||||
const refreshFeeds=soon(()=>loadFeeds(true));
|
||||
const refreshEntries=soon(()=>{ if(S.feed) loadEntries(); });
|
||||
// Every scan's events reach everyone; only this person's feeds are theirs to show or refresh.
|
||||
const mine=id=>S.feeds.some(f=>f.id===id);
|
||||
@@ -22,7 +21,7 @@ function connect(){
|
||||
// Said only for a file on screen, as one downloaded by hand is: the scheduled downloads of
|
||||
// everyone's feeds used to announce themselves to everyone.
|
||||
if(bar){ bar.classList.remove('live'); toast('Downloaded '+ev.path.split('/').pop()); }
|
||||
refreshEntries(); refreshFeeds();
|
||||
refreshEntries();
|
||||
}
|
||||
else if(ev.ev==='download_error'){
|
||||
const bar=document.querySelector(`.dlbar[data-bar="${ev.enclosure}"]`);
|
||||
@@ -35,14 +34,19 @@ function connect(){
|
||||
else if(ev.ev==='feed_done'){
|
||||
setScanning(ev.feed,false);
|
||||
if(!mine(ev.feed)) return;
|
||||
refreshFeeds(); if(ev.feed===S.feed||S.feed===':all') refreshEntries();
|
||||
if(ev.feed===S.feed||S.feed===':all') refreshEntries();
|
||||
}
|
||||
// No toast: a scan of every feed raised one per failure, to everyone. The feed list's
|
||||
// red ! marks the feed instead, and its page says why.
|
||||
else if(ev.ev==='feed_error'){ setScanning(ev.feed,false); if(mine(ev.feed)) refreshFeeds(); }
|
||||
else if(ev.ev==='feed_error') setScanning(ev.feed,false);
|
||||
// The server sends a feed's new row after anything changes it (counts, error, last check),
|
||||
// only to those who subscribe; the page used to reload the whole list after each event.
|
||||
else if(ev.ev==='feed_row') patchFeed(ev.row);
|
||||
// Only a scan that checked something: the scheduler scans every minute, due or not, and
|
||||
// every open page reloaded the whole list each time (#103).
|
||||
else if(ev.ev==='scan_done'){ scanning.clear(); paintScanning(); if(ev.feeds){ refreshFeeds(); refreshEntries(); } }
|
||||
// Rows came as feed_row events. ponytail: a feed an OPML drops stays in the list until the
|
||||
// page reloads; reload the list here when a scan synced an OPML if that ever matters.
|
||||
else if(ev.ev==='scan_done'){ scanning.clear(); paintScanning(); if(ev.feeds) refreshEntries(); }
|
||||
};
|
||||
sse.onerror=()=>{ sse.close(); setTimeout(connect,4000); };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
/* ---------------- feeds ---------------- */
|
||||
/// One feed's row from the server, in place of the old one (or added, for a feed an OPML just
|
||||
/// listed). A scan sends dozens; the list is redrawn once a frame, not once each.
|
||||
let rowsQueued=0;
|
||||
function patchFeed(row){
|
||||
const i=S.feeds.findIndex(f=>f.id===row.id);
|
||||
if(i<0) S.feeds.push(row); else S.feeds[i]=row;
|
||||
if(!rowsQueued) rowsQueued=requestAnimationFrame(()=>{ rowsQueued=0; renderFeeds(); });
|
||||
}
|
||||
|
||||
async function loadFeeds(keepSel?: boolean){
|
||||
S.feeds = await api('/api/feeds');
|
||||
// Only for a hint in a feed's settings, so once, on the page's first load, not on every reload
|
||||
|
||||
@@ -153,7 +153,7 @@ function setRead(e,read){
|
||||
const w: {read: boolean, done?: number}={read}; readWrites.set(readKey(e),w);
|
||||
return api(`/api/entries/${encodeURIComponent(e.feed_id)}/${encodeURIComponent(e.guid)}/flags`,
|
||||
{method:'POST',body:JSON.stringify({read})})
|
||||
.then(()=>{ w.done=performance.now(); loadFeeds(true); });
|
||||
.then(row=>{ w.done=performance.now(); if(row) patchFeed(row); });
|
||||
}
|
||||
|
||||
/// Opening an item is reading it. The row is redrawn where it stands rather than the list
|
||||
|
||||
Reference in New Issue
Block a user