Preload the next page in page mode
This commit is contained in:
+2
-1
@@ -45,7 +45,8 @@ mobile draft at 390px.
|
|||||||
number / total / Next, persists the page in localStorage, and scrolls to the next page one at
|
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
|
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
|
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
|
- [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`
|
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
|
both fail) and are marked `unavailable after recovery` so later runs skip them. The recovery
|
||||||
|
|||||||
+51
-11
@@ -36,6 +36,7 @@
|
|||||||
pageMode: localStorage.getItem("vk-media-page-mode") === "page" ? "page" : "infinite",
|
pageMode: localStorage.getItem("vk-media-page-mode") === "page" ? "page" : "infinite",
|
||||||
page: Math.max(1, Number(localStorage.getItem("vk-media-page")) || 1),
|
page: Math.max(1, Number(localStorage.getItem("vk-media-page")) || 1),
|
||||||
pageCooldownUntil: 0,
|
pageCooldownUntil: 0,
|
||||||
|
pagePrefetch: null,
|
||||||
draft: null,
|
draft: null,
|
||||||
prefetched: null,
|
prefetched: null,
|
||||||
prefetchPromise: null,
|
prefetchPromise: null,
|
||||||
@@ -414,9 +415,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const items = await window.VK.api.get(
|
const prefetched =
|
||||||
`/api/media?limit=${state.pageSize}&offset=${state.offset}&${filterQuery()}`
|
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) {
|
if (reset) {
|
||||||
for (const item of items) state.itemKeys.add(`${item.scrapeId}:${item.itemId}`);
|
for (const item of items) state.itemKeys.add(`${item.scrapeId}:${item.itemId}`);
|
||||||
@@ -442,13 +451,44 @@
|
|||||||
} else {
|
} else {
|
||||||
state.pageCooldownUntil = Date.now() + 2000;
|
state.pageCooldownUntil = Date.now() + 2000;
|
||||||
renderToolbar();
|
renderToolbar();
|
||||||
|
void prefetchNext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Warm the next page's JSON and preview images before the user reaches them. */
|
/** 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() {
|
async function prefetchNext() {
|
||||||
if (state.pageMode !== "infinite") return;
|
if (!state.loaded || state.loading) return;
|
||||||
if (!state.loaded || !state.more || state.prefetched || state.prefetchPromise || 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
|
state.prefetchPromise = window.VK.api
|
||||||
.get(`/api/media?limit=${state.pageSize}&offset=${state.offset}&${filterQuery()}`)
|
.get(`/api/media?limit=${state.pageSize}&offset=${state.offset}&${filterQuery()}`)
|
||||||
@@ -459,12 +499,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.prefetched = items;
|
state.prefetched = items;
|
||||||
for (const item of items) {
|
warmImages(items);
|
||||||
const src = item.thumbUrl || item.localUrl;
|
|
||||||
if (!src) continue;
|
|
||||||
const image = new Image();
|
|
||||||
image.src = src;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Preloading is best effort; the scroll handler falls back to a request.
|
// Preloading is best effort; the scroll handler falls back to a request.
|
||||||
@@ -564,6 +599,7 @@
|
|||||||
function setPageMode(mode) {
|
function setPageMode(mode) {
|
||||||
if (state.pageMode === mode) return;
|
if (state.pageMode === mode) return;
|
||||||
state.pageMode = mode;
|
state.pageMode = mode;
|
||||||
|
state.pagePrefetch = null;
|
||||||
localStorage.setItem("vk-media-page-mode", mode);
|
localStorage.setItem("vk-media-page-mode", mode);
|
||||||
state.prefetched = null;
|
state.prefetched = null;
|
||||||
state.page = 1;
|
state.page = 1;
|
||||||
@@ -573,6 +609,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function gotoPage(page) {
|
function gotoPage(page) {
|
||||||
|
state.pagePrefetch = null;
|
||||||
const count = pageCount();
|
const count = pageCount();
|
||||||
const wanted = Math.max(1, Math.round(Number(page) || 1));
|
const wanted = Math.max(1, Math.round(Number(page) || 1));
|
||||||
state.page = count ? Math.min(count, wanted) : wanted;
|
state.page = count ? Math.min(count, wanted) : wanted;
|
||||||
@@ -1476,6 +1513,7 @@
|
|||||||
|
|
||||||
function applyDraft() {
|
function applyDraft() {
|
||||||
state.page = 1;
|
state.page = 1;
|
||||||
|
state.pagePrefetch = null;
|
||||||
savePage();
|
savePage();
|
||||||
state.kind = state.draft.kind;
|
state.kind = state.draft.kind;
|
||||||
state.filters = {
|
state.filters = {
|
||||||
@@ -1996,6 +2034,7 @@
|
|||||||
state.more = false;
|
state.more = false;
|
||||||
state.page = 1;
|
state.page = 1;
|
||||||
state.pageCooldownUntil = 0;
|
state.pageCooldownUntil = 0;
|
||||||
|
state.pagePrefetch = null;
|
||||||
savePage();
|
savePage();
|
||||||
state.prefetched = null;
|
state.prefetched = null;
|
||||||
state.items = [];
|
state.items = [];
|
||||||
@@ -2042,6 +2081,7 @@
|
|||||||
overlay.querySelector("#media-close").addEventListener("click", hide);
|
overlay.querySelector("#media-close").addEventListener("click", hide);
|
||||||
overlay.querySelector("#media-refresh").addEventListener("click", () => {
|
overlay.querySelector("#media-refresh").addEventListener("click", () => {
|
||||||
state.completed.clear();
|
state.completed.clear();
|
||||||
|
state.pagePrefetch = null;
|
||||||
void loadScrapes();
|
void loadScrapes();
|
||||||
if (state.view === "media") void loadMedia(true);
|
if (state.view === "media") void loadMedia(true);
|
||||||
if (state.view === "bot") {
|
if (state.view === "bot") {
|
||||||
|
|||||||
Reference in New Issue
Block a user