Keep the feed list's icons when it redraws, rather than load each again (#134)
Checking every feed made all the feeds' icons in the feed list flash while it ran. A check sends each feed's new row as it reads the feed, and renderFeeds, run once a frame while they come in, emptied the list and built every row anew, every <img> with it: each icon went blank and was loaded and drawn again, many times a second through a scan of 150 feeds. The rows are still rebuilt, but the images already drawn go into the new ones in place of the fresh copies, matched by their markup, so only an icon that has changed is made anew. A browser test checks the rows are new and the images in them the same elements, and fails without this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Checking every feed no longer makes every icon in the feed list flash while it runs: the list keeps the icons it has already drawn.
|
||||
|
||||
## [0.10.0] - 2026-10-05
|
||||
|
||||
### Added
|
||||
|
||||
@@ -966,6 +966,24 @@ test('the Directory lists what everyone here reads, the most subscribed first, b
|
||||
await ctx.close();
|
||||
});
|
||||
|
||||
test('the feed list keeps the icons it has drawn when a feed\'s new row comes in (#134)', async ({ page }) => {
|
||||
await expect(page.locator('#feedlist .feed').first()).toBeVisible({ timeout: 20_000 });
|
||||
const kept = await page.evaluate(async () => {
|
||||
const frames = () => new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
||||
// Artwork that loads, on a feed of its own row: the fixtures' fails on purpose, for initials.
|
||||
const row = { ...S.feeds.find(f => !f.group && !S.feeds.some(c => c.group === f.id)), image: '/favicon.png' };
|
||||
patchFeed(row); await frames();
|
||||
const rows = [...document.querySelectorAll('#feedlist .feed')];
|
||||
const before = [...document.querySelectorAll('#feedlist .feed img')];
|
||||
// The same feed's new row again, as a check of every feed sends one for each feed it reads.
|
||||
patchFeed({ ...row }); await frames();
|
||||
const after = [...document.querySelectorAll('#feedlist .feed img')];
|
||||
return { drawn: before.length > 0, redrawn: !rows[0].isConnected, same: after.length === before.length && after.every((img, i) => img === before[i]) };
|
||||
});
|
||||
// The rows are new, the images in them the ones already drawn.
|
||||
expect(kept).toEqual({ drawn: true, redrawn: true, same: true });
|
||||
});
|
||||
|
||||
test('adding a feed scans it straight away', async ({ page }) => {
|
||||
await page.locator('#addFeed').click();
|
||||
await page.locator('#nurl').fill('http://127.0.0.1:8792/fresh.xml');
|
||||
|
||||
@@ -66,6 +66,11 @@ function renderFeeds(){
|
||||
const row=back&&list.querySelector(`[data-id="${CSS.escape(back[0])}"]`);
|
||||
if(row) (back[1]&&$('.chev',row)||row).focus();
|
||||
};
|
||||
// The images already drawn, to go into the rows that replace theirs. Made anew, every icon in
|
||||
// the list went blank and loaded again, once a frame through a check of every feed, as each
|
||||
// feed's new row came in (#134). Matched by their markup, so a changed one is made anew.
|
||||
const drawn=new Map<string,HTMLImageElement[]>();
|
||||
for(const img of $$('img',list)){ const k=img.outerHTML; if(!drawn.has(k)) drawn.set(k,[]); drawn.get(k).push(img); }
|
||||
list.innerHTML='';
|
||||
const unreadAll=S.feeds.reduce((n,f)=>n+(f.unread||0),0);
|
||||
const places=document.createElement('div');
|
||||
@@ -139,6 +144,7 @@ function renderFeeds(){
|
||||
`<span class="badge${unread?'':' zero'}" title="${unread} unread">${unread>999?'999+':unread}</span>`;
|
||||
el.onclick=()=>{ selectFeed(f.id); nav(false); };
|
||||
if(kids) $('.chev',el).onclick=ev=>{ ev.stopPropagation(); toggleGroup(f.id); };
|
||||
for(const img of $$('img',el)){ const was=drawn.get(img.outerHTML)?.shift(); if(was) img.replaceWith(was); }
|
||||
list.appendChild(el);
|
||||
}
|
||||
paintScanning();
|
||||
|
||||
Reference in New Issue
Block a user