mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 13:46:16 -08:00
perf(epg): render only the timeline programmes near the visible range (J3)
Selecting a live channel rendered every programme block of its schedule (about 240 for the Xtream mock) while the stream was starting: about 6,000 of J3's 6,199 renderer.domMutationsToPlaying. The ribbon now renders the blocks, ticks and day dividers within half a viewport of the visible range; the track keeps the full schedule's width, so positions, the scrollbar and scroll-to-now are unchanged. A resize reported before the scroll-to-now must not re-centre the window (it once jumped to the schedule's start and back); resizes only widen it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e8b181fcea
commit
67e0133f12
8 files changed
+702
-20
No files matched your search
@@ -107,9 +107,10 @@ test('@epg @xtream @electron removes uploaded guide data and restores provider E
|
||||
await expect
|
||||
.poll(() => timelineBlockTitles(app.mainWindow))
|
||||
.not.toContain('Temporary XMLTV Bulletin');
|
||||
// The programme on now: the ribbon renders the blocks around it.
|
||||
await expect
|
||||
.poll(() => timelineBlockTitles(app.mainWindow))
|
||||
.toContain(fixture.fullEpg[0].title);
|
||||
.toContain(fixture.shortEpg[0].title);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
await source.close();
|
||||
@@ -281,14 +282,33 @@ for (const timeZone of ['UTC', 'Europe/Berlin'] as const) {
|
||||
app.mainWindow.locator('app-epg-timeline')
|
||||
).toBeVisible({ timeout: 20000 });
|
||||
|
||||
// The timeline renders the full multi-day window as blocks,
|
||||
// sorted by start time (no per-day filtering — it scrolls).
|
||||
// The timeline lays out the full multi-day window sorted by start
|
||||
// time (no per-day filtering — it scrolls) and renders the blocks
|
||||
// near the visible range: an ordered run around the current
|
||||
// programme, and the last programme once scrolled to the end.
|
||||
const allTitles = [...fixture.fullEpg]
|
||||
.sort((a, b) => a.startTimestamp - b.startTimestamp)
|
||||
.map((listing) => listing.title);
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const titles = await timelineBlockTitles(app.mainWindow);
|
||||
return (
|
||||
titles.includes(currentProgram.title) &&
|
||||
isContiguousRun(titles, allTitles)
|
||||
);
|
||||
})
|
||||
.toBe(true);
|
||||
await app.mainWindow
|
||||
.locator('app-epg-timeline .epg-timeline__ribbon')
|
||||
.evaluate((ribbon) => {
|
||||
ribbon.scrollLeft = ribbon.scrollWidth;
|
||||
});
|
||||
await expect
|
||||
.poll(() => timelineBlockTitles(app.mainWindow))
|
||||
.toEqual(allTitles);
|
||||
.toContain(allTitles[allTitles.length - 1]);
|
||||
await app.mainWindow
|
||||
.locator('app-epg-timeline .epg-timeline__jump')
|
||||
.click();
|
||||
|
||||
// The current programme is highlighted as the "now" block.
|
||||
await expect(
|
||||
@@ -866,6 +886,19 @@ async function timelineBlockTitles(
|
||||
.then((titles) => titles.map((title) => title.trim()).filter(Boolean));
|
||||
}
|
||||
|
||||
/** Whether `run` is a non-empty, in-order slice of `all`. */
|
||||
function isContiguousRun(run: string[], all: string[]): boolean {
|
||||
if (run.length === 0) {
|
||||
return false;
|
||||
}
|
||||
for (let start = 0; start + run.length <= all.length; start++) {
|
||||
if (run.every((title, index) => all[start + index] === title)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function getProgressWidthPercent(
|
||||
row: ReturnType<typeof channelItemByTitle>
|
||||
) {
|
||||
|
||||
Reference in new issue
Block a user