252 lines
7.0 KiB
JavaScript
252 lines
7.0 KiB
JavaScript
let gallery;
|
|
let sentinel;
|
|
let select;
|
|
let observer;
|
|
|
|
let offset = 0;
|
|
let loading = false;
|
|
let hasMore = true;
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
let height = urlParams.get("height") || (window.innerWidth > 1600 ? 320 : 220);
|
|
let sort = urlParams.get("sort") || "time";
|
|
let tags = urlParams.get("tags") || "",
|
|
or_tags = urlParams.get("or_tags") || "",
|
|
not_tags = urlParams.get("not_tags") || "";
|
|
let socials = urlParams.get("socials") || "",
|
|
or_socials = urlParams.get("or_socials") || "",
|
|
not_socials = urlParams.get("not_socials") || "";
|
|
let platforms = urlParams.get("platforms") || "",
|
|
or_platforms = urlParams.get("or_platforms") || "",
|
|
not_platforms = urlParams.get("not_platforms") || "";
|
|
let formats = urlParams.get("formats") || "",
|
|
or_formats = urlParams.get("or_formats") || "",
|
|
not_formats = urlParams.get("not_formats") || "";
|
|
const limit = window.innerWidth > 1200 ? 50 : 20;
|
|
|
|
function resetGallery() {
|
|
imgviewer.resetList();
|
|
gallery.replaceChildren();
|
|
height = urlParams.get("height") || (window.innerWidth > 1600 ? 320 : 220);
|
|
offset = 0;
|
|
hasMore = true;
|
|
loadMore();
|
|
}
|
|
|
|
async function loadMore() {
|
|
if (loading || !hasMore) return;
|
|
|
|
let url = `/api/files?offset=${offset}&limit=${limit}&sort=${sort}`
|
|
observer.unobserve(sentinel);
|
|
loading = true;
|
|
|
|
if (tags) url += `&tags=${tags}`
|
|
if (or_tags) url += `&or_tags=${or_tags}`
|
|
if (not_tags) url += `¬_tags=${not_tags}`
|
|
if (socials) url += `&socials=${socials}`
|
|
if (or_socials) url += `&or_socials=${or_socials}`
|
|
if (not_socials) url += `¬_socials=${not_socials}`
|
|
if (platforms) url += `&platforms=${platforms}`
|
|
if (or_platforms) url += `&or_platforms=${or_platforms}`
|
|
if (not_platforms) url += `¬_platforms=${not_platforms}`
|
|
if (formats) url += `&formats=${formats}`
|
|
if (or_formats) url += `&or_formats=${or_formats}`
|
|
if (not_formats) url += `¬_formats=${not_formats}`
|
|
|
|
try {
|
|
const res = await fetch(url);
|
|
const data = await res.json();
|
|
|
|
switch (res.status) {
|
|
case 200:
|
|
break;
|
|
case 401:
|
|
alert(`Session expired: ${data.msg}: ${data.error}`);
|
|
return;
|
|
case 500:
|
|
if (data.error) {
|
|
hasMore = false;
|
|
alert(`${data.msg}: ${data.error}`);
|
|
return
|
|
}
|
|
}
|
|
|
|
appendFiles(data.files);
|
|
|
|
hasMore = sort != "random" && data.files.length == limit;
|
|
offset += data.files.length;
|
|
if (!sort) sort = data.sort;
|
|
} catch (err) {
|
|
console.error(err);
|
|
hasMore = false;
|
|
} finally {
|
|
loading = false;
|
|
|
|
// 加载完成下一帧重新观察,强制刷新交叉状态
|
|
requestAnimationFrame(() => {
|
|
observer.observe(sentinel);
|
|
});
|
|
}
|
|
}
|
|
|
|
function appendFiles(files) {
|
|
imgviewer.appendFiles(files);
|
|
const fragment = document.createDocumentFragment();
|
|
|
|
for (const file of files) {
|
|
const jWidth = file.width * height / file.height;
|
|
const jPadding = file.height / file.width * 100;
|
|
const wrapper = document.createElement("div");
|
|
wrapper.style.width = jWidth + "px";
|
|
wrapper.style.flexGrow = jWidth;
|
|
|
|
const i = document.createElement("i");
|
|
i.className = "info";
|
|
i.style.paddingBottom = jPadding + "%";
|
|
wrapper.appendChild(i);
|
|
|
|
const imgs = document.createElement("div");
|
|
imgs.className = "imgs";
|
|
|
|
const img = document.createElement("img");
|
|
if (gallery.childElementCount < 50) {
|
|
img.fetchPriority = "high";
|
|
} else {
|
|
img.loading = "lazy";
|
|
}
|
|
img.decoding = "async";
|
|
img.tabIndex = "0";
|
|
img.src = file.thumbed ? `/thumbnails/${file.id}.jpg` : `/files/${file.id}.${file.ext}`;
|
|
|
|
let artwork = file.edges.artwork;
|
|
img.id = img.alt = `trpic_artwork_${artwork.id}_${file.id}`;
|
|
img.dataset.id = file.id;
|
|
img.dataset.artwork_id = artwork.id;
|
|
imgs.appendChild(img);
|
|
|
|
if (file.thumbhash) {
|
|
const thumb = document.createElement("img");
|
|
thumb.alt = "thumbhash";
|
|
thumb.classList.add("thumbhash");
|
|
thumb.src = thumbHashToDataURL(file.thumbhash);
|
|
imgs.appendChild(thumb);
|
|
|
|
if (artwork.sensitive > 1) {
|
|
img.tabIndex = "-1";
|
|
const m = document.createElement("a");
|
|
m.tabIndex = "0";
|
|
m.textContent = "spoiler";
|
|
m.href = `javascript:`;
|
|
m.classList.add(artwork.sensitive > 2 ? "sensitive" : "nsfw");
|
|
imgs.appendChild(m);
|
|
m.addEventListener("click", (e) => {
|
|
thumb.style.opacity = m.style.opacity = 0;
|
|
m.style.pointerEvents = "none";
|
|
if (e.ctrlKey) {
|
|
thumb.remove();
|
|
m.remove();
|
|
img.focus({ preventScroll: true });
|
|
img.tabIndex = "0";
|
|
} else {
|
|
setTimeout(() => {
|
|
thumb.style.opacity = m.style.opacity = "";
|
|
m.style.pointerEvents = "";
|
|
m.tabIndex = "0";
|
|
img.blur();
|
|
img.tabIndex = "-1";
|
|
}, 15000)
|
|
m.tabIndex = "-1";
|
|
img.tabIndex = "0";
|
|
img.focus({ preventScroll: true });
|
|
}
|
|
})
|
|
} else {
|
|
img.onload = () => {
|
|
img.onload = null;
|
|
thumb.style.opacity = 0;
|
|
setTimeout(() => {
|
|
thumb.remove();
|
|
}, 1000)
|
|
};
|
|
}
|
|
}
|
|
|
|
wrapper.appendChild(imgs);
|
|
imgviewer.bindClickEvents(img);
|
|
fragment.appendChild(wrapper);
|
|
|
|
if (artwork.edges.socials) {
|
|
const tr = document.createElement("p");
|
|
tr.className = "top-right";
|
|
tr.textContent = artwork.edges.socials[0].display_name;
|
|
i.appendChild(tr);
|
|
}
|
|
|
|
const br = document.createElement("p");
|
|
br.className = "bottom-right";
|
|
switch (sort) {
|
|
case "size":
|
|
case "size-down":
|
|
br.textContent = `${file.ext} | ${file.size_str}`;
|
|
break;
|
|
case "resolution":
|
|
case "resolution-down":
|
|
br.textContent = `${file.width}x${file.height}px`;
|
|
break;
|
|
}
|
|
if (br.textContent) {
|
|
i.appendChild(br);
|
|
}
|
|
}
|
|
|
|
gallery.appendChild(fragment);
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
gallery = document.getElementById("gallery");
|
|
sentinel = document.getElementById("sentinel");
|
|
select = document.getElementById('sort-select')
|
|
|
|
select.value = sort
|
|
|
|
select.addEventListener('change', () => {
|
|
urlParams.set("sort", sort = select.value);
|
|
history.replaceState(null, null, `/files?${urlParams.toString()}`);
|
|
resetGallery()
|
|
})
|
|
|
|
observer = new IntersectionObserver(
|
|
(entries) => {
|
|
if (entries[0].isIntersecting) {
|
|
loadMore();
|
|
}
|
|
},
|
|
{
|
|
root: null,
|
|
rootMargin: "800px", // 提前 800px 加载
|
|
threshold: 0,
|
|
}
|
|
);
|
|
|
|
observer.observe(sentinel);
|
|
});
|
|
|
|
document.addEventListener("error", (e) => {
|
|
let target = e.target;
|
|
const tagName = target.tagName || "";
|
|
const curTimes = Number(target.dataset.retryTimes) || 0;
|
|
if (tagName.toLowerCase() === "img") {
|
|
if (curTimes >= 5) {
|
|
target.alt = "loading failed";
|
|
} else {
|
|
setTimeout(() => {
|
|
target.dataset.retryTimes = curTimes + 1;
|
|
target.src = target.src;
|
|
}, 10000);
|
|
}
|
|
} else {
|
|
target = null;
|
|
}
|
|
}, true);
|