[F] Edge cases for gallery pagination

This commit is contained in:
Azalea
2026-09-14 14:45:11 +00:00
parent a81bf58cc3
commit 8c7b7238bc
2 changed files with 39 additions and 16 deletions
+36 -15
View File
@@ -14,16 +14,46 @@
let loading = true;
let error: any = null;
// Monotonic token so a slow response can never overwrite a newer one
let requestSeq = 0;
function readPageFromUrl(): number {
const raw = new URL(window.location.toString()).searchParams.get("page");
return raw ? parseInt(raw, 10) || 1 : 1;
}
/**
* Keep the url in sync with the page the server actually served, since
* out-of-range pages are clamped server-side.
*/
function syncUrl(effectivePage: number) {
const url = new URL(window.location.toString());
const current = url.searchParams.get("page");
const target = effectivePage > 1 ? effectivePage.toString() : null;
if (current === target) return;
if (target === null) url.searchParams.delete("page");
else url.searchParams.set("page", target);
history.replaceState({}, "", url.toString());
}
async function loadPhotos(targetPage: number) {
const seq = ++requestSeq;
// Apply the target immediately so the pager and further clicks act on it
// instead of on the page that is still being fetched
page = targetPage;
loading = true;
error = null;
try {
photoData = await GAME.photos(targetPage, pageSize);
page = photoData.page;
const data = await GAME.photos(targetPage, pageSize);
if (seq !== requestSeq) return;
photoData = data;
page = data.page;
syncUrl(data.page);
} catch (e) {
if (seq !== requestSeq) return;
error = e;
} finally {
loading = false;
if (seq === requestSeq) loading = false;
}
}
@@ -38,18 +68,9 @@
}
onMount(() => {
const url = new URL(window.location.toString());
const pageParam = url.searchParams.get("page");
if (pageParam) {
page = parseInt(pageParam, 10) || 1;
}
loadPhotos(page);
loadPhotos(readPageFromUrl());
const onPopState = () => {
const currentUrl = new URL(window.location.toString());
const p = parseInt(currentUrl.searchParams.get("page") || "1", 10) || 1;
loadPhotos(p);
};
const onPopState = () => loadPhotos(readPageFromUrl());
window.addEventListener("popstate", onPopState);
return () => window.removeEventListener("popstate", onPopState);
@@ -66,7 +87,7 @@
{:else if error}
<Error {error}/>
{:else if photoData}
{#if photoData.total === 0 || photoData.photos.length === 0}
{#if photoData.total === 0}
<blockquote class="info">{t('maiphoto.none')}</blockquote>
{:else}
{#if photoData.totalPages > 1}
@@ -250,9 +250,11 @@ class Maimai2(
}
} else {
val pageSize = (size ?: 12).coerceIn(1, 100)
val pageNum = page.coerceAtLeast(1)
val total = files.size
val totalPages = if (total == 0) 0 else (total + pageSize - 1) / pageSize
// Clamp to the nearest valid page: out-of-range pages return the closest real page
// instead of an empty grid, and the offset can no longer overflow Int
val pageNum = page.coerceIn(1, maxOf(1, totalPages))
val pagedFiles = files.drop((pageNum - 1) * pageSize).take(pageSize)
val photos = pagedFiles.map {
photoHashMap.computeIfAbsent(it) { f -> myPhotoGetHash(f) }