Preload the next page in page mode

This commit is contained in:
Fisto
2026-09-18 16:48:50 +02:00
parent 13ffad34d4
commit f22ab5ea5f
2 changed files with 53 additions and 12 deletions
+2 -1
View File
@@ -45,7 +45,8 @@ mobile draft at 390px.
number / total / Next, persists the page in localStorage, and scrolls to the next page one at
a time with a 2 s cooldown after each load. Same filter + page is stable while the archive is
unchanged. Infinite mode keeps the stream buffer; prefetch, buffer fill, and the buffer are
disabled in page mode.
disabled in page mode. Page mode pre-loads the next page's JSON and preview images, so
jumping one page renders instantly.
- [x] C56 Recovery completed. First run after the C55 fix recovered 1,117 of 1,240 Discord-CDN
previews (90%); the last 123 were confirmed genuinely gone (refresh + direct fetch and `/stream`
both fail) and are marked `unavailable after recovery` so later runs skip them. The recovery
+51 -11
View File
@@ -36,6 +36,7 @@
pageMode: localStorage.getItem("vk-media-page-mode") === "page" ? "page" : "infinite",
page: Math.max(1, Number(localStorage.getItem("vk-media-page")) || 1),
pageCooldownUntil: 0,
pagePrefetch: null,
draft: null,
prefetched: null,
prefetchPromise: null,
@@ -414,9 +415,17 @@
}
try {
const items = await window.VK.api.get(
`/api/media?limit=${state.pageSize}&offset=${state.offset}&${filterQuery()}`
);
const prefetched =
reset && state.pageMode === "page" && state.pagePrefetch?.page === state.page
? state.pagePrefetch
: null;
if (prefetched) state.pagePrefetch = null;
const items =
prefetched?.items ??
(await window.VK.api.get(
`/api/media?limit=${state.pageSize}&offset=${state.offset}&${filterQuery()}`
));
if (reset) {
for (const item of items) state.itemKeys.add(`${item.scrapeId}:${item.itemId}`);
@@ -442,13 +451,44 @@
} else {
state.pageCooldownUntil = Date.now() + 2000;
renderToolbar();
void prefetchNext();
}
}
/** Warm the next page's JSON and preview images before the user reaches them. */
function warmImages(items) {
for (const item of items) {
const src = item.thumbUrl || item.localUrl;
if (!src) continue;
const image = new Image();
image.src = src;
}
}
/**
* Warm the next slice in both modes. Infinite appends it when the user reaches the bottom;
* page mode keeps it ready so jumping one page renders instantly with its previews cached.
*/
async function prefetchNext() {
if (state.pageMode !== "infinite") return;
if (!state.loaded || !state.more || state.prefetched || state.prefetchPromise || state.loading) return;
if (!state.loaded || state.loading) return;
if (state.pageMode === "page") {
const page = state.page + 1;
if (!hasNextPage() || state.pagePrefetch?.page === page) return;
try {
const items = await window.VK.api.get(
`/api/media?limit=${state.pageSize}&offset=${(page - 1) * state.pageSize}&${filterQuery()}`
);
state.pagePrefetch = { page, items };
warmImages(items);
} catch {
// best effort
}
return;
}
if (!state.more || state.prefetched || state.prefetchPromise) return;
state.prefetchPromise = window.VK.api
.get(`/api/media?limit=${state.pageSize}&offset=${state.offset}&${filterQuery()}`)
@@ -459,12 +499,7 @@
}
state.prefetched = items;
for (const item of items) {
const src = item.thumbUrl || item.localUrl;
if (!src) continue;
const image = new Image();
image.src = src;
}
warmImages(items);
})
.catch(() => {
// Preloading is best effort; the scroll handler falls back to a request.
@@ -564,6 +599,7 @@
function setPageMode(mode) {
if (state.pageMode === mode) return;
state.pageMode = mode;
state.pagePrefetch = null;
localStorage.setItem("vk-media-page-mode", mode);
state.prefetched = null;
state.page = 1;
@@ -573,6 +609,7 @@
}
function gotoPage(page) {
state.pagePrefetch = null;
const count = pageCount();
const wanted = Math.max(1, Math.round(Number(page) || 1));
state.page = count ? Math.min(count, wanted) : wanted;
@@ -1476,6 +1513,7 @@
function applyDraft() {
state.page = 1;
state.pagePrefetch = null;
savePage();
state.kind = state.draft.kind;
state.filters = {
@@ -1996,6 +2034,7 @@
state.more = false;
state.page = 1;
state.pageCooldownUntil = 0;
state.pagePrefetch = null;
savePage();
state.prefetched = null;
state.items = [];
@@ -2042,6 +2081,7 @@
overlay.querySelector("#media-close").addEventListener("click", hide);
overlay.querySelector("#media-refresh").addEventListener("click", () => {
state.completed.clear();
state.pagePrefetch = null;
void loadScrapes();
if (state.view === "media") void loadMedia(true);
if (state.view === "bot") {