Add infinite scroll, gone/private tiles, and live-only scrape strip

This commit is contained in:
Fisto
2026-09-16 14:09:55 +02:00
parent d522681553
commit 3da0da0ca7
6 changed files with 54 additions and 11 deletions
+37 -8
View File
@@ -24,6 +24,8 @@
authors: null,
errors: [],
loaded: false,
loading: false,
more: false,
stats: null,
metaFields: JSON.parse(
localStorage.getItem("vk-media-meta-fields") ??
@@ -227,24 +229,29 @@
renderStatus();
}
async function loadMedia(reset) {
if (!state.loaded) return;
const PAGE_SIZE = 60;
async function loadMedia(reset) {
if (!state.loaded || state.loading) return;
state.loading = true;
if (reset) {
state.items = [];
state.offset = 0;
state.more = false;
}
try {
const items = await window.VK.api.get(
`/api/media?limit=60&offset=${state.offset}&${filterQuery()}`
`/api/media?limit=${PAGE_SIZE}&offset=${state.offset}&${filterQuery()}`
);
state.items = state.items.concat(items);
state.offset += items.length;
state.more = items.length >= PAGE_SIZE;
} catch (error) {
setStatus(`media load failed: ${error.message}`);
renderGrid();
return;
} finally {
state.loading = false;
}
renderGrid();
@@ -281,7 +288,8 @@
function renderScrapes() {
scrapeList.replaceChildren();
const active = state.scrapes.filter(scrape => ["queued", "running", "paused"].includes(scrape.state));
// Only live jobs: a paused scrape is not running and belongs in History, not the strip.
const active = state.scrapes.filter(scrape => ["queued", "running"].includes(scrape.state));
for (const scrape of active) {
const row = el("div", "vk-media-scrape");
@@ -421,7 +429,14 @@
}
function placeholder(media, item) {
media.replaceChildren(el("span", "vk-media-card-kind", item.kind.toUpperCase()));
const error = item.previewError ?? "";
let label = item.kind.toUpperCase();
if (/\b403\b/.test(error)) label = "PRIVATE";
else if (/\b(404|410)\b/.test(error)) label = "GONE";
const node = el("span", "vk-media-card-kind", label);
if (error) node.title = error;
media.replaceChildren(node);
}
/** Only real video files can be framed; embed pages (YouTube etc.) never are. */
@@ -535,7 +550,15 @@
}
for (const item of state.items) grid.append(renderCard(item));
if (!state.items.length) grid.append(el("div", "vk-media-status", "No media matches the filters."));
if (!state.items.length) {
grid.append(el("div", "vk-media-status", "No media matches the filters."));
} else if (state.more) {
const more = el("button", "btn btn-secondary vk-media-load-more", "Load more");
more.type = "button";
more.addEventListener("click", () => void loadMedia(false));
grid.append(more);
}
}
function filterSelect(options, value, onChange) {
@@ -774,6 +797,12 @@
if (state.view === "media") void loadScrapes();
}, 4000);
// Infinite scroll: fetch the next page before the grid bottom is reached.
grid.addEventListener("scroll", () => {
if (state.loading || !state.more) return;
if (grid.scrollTop + grid.clientHeight >= grid.scrollHeight - 240) void loadMedia(false);
});
// ---- Scrape popup: server or group-chat trees ----
const scrapeModal = document.createElement("div");