migrate sql:
```
ALTER TABLE posts ADD COLUMN `time` datetime NULL;
ALTER TABLE posts ADD COLUMN `title` text NULL;
ALTER TABLE posts ADD COLUMN `text` text NULL;
UPDATE posts SET
time = (SELECT a.time FROM artworks a WHERE a.id = posts.artwork_posts),
title = (SELECT a.title FROM artworks a WHERE a.id = posts.artwork_posts),
text = (SELECT a.text FROM artworks a WHERE a.id = posts.artwork_posts)
WHERE EXISTS (SELECT 1 FROM artworks a WHERE a.id = posts.artwork_posts);
ALTER TABLE artworks DROP COLUMN time;
ALTER TABLE artworks DROP COLUMN title;
ALTER TABLE artworks DROP COLUMN text;
```
configs:
add `CheckArtworkIntervalInSeconds` flag to set check artwork task interval
database:
add `migrate.sql` file
artwork:
move `time`, `title` and `text` fields to `post` schema
file:
set `id_str` and `filename` fields immutable
add `source_url` optional field
post:
set `platform` field immutable
add `time`, `title` and `text` optional from `artwork` schema
social:
set `platform` field immutable
tag:
set `text` field immutable
action(new):
add action package to create and update `file`, `post`, `social` and `tag` info
api:
artworks:
adjust GET method order option to match the database
files:
adjust GET method with `artwork/tags` edge
rename GET method respond `images` field to `files`
collect:
refactor to work with new action package
task:
artwork(new):
create `CheckArtworks` function to check and update artwork info
social:
refactor `CheckSocials` to work with new action package
router:
add `/api/task/artworks/check` endpoint for `CheckArtworks` task
src/web:
artwork noscript:
adjust order option to match the database
file noscript:
rename execute template struct `Images` field to `Files`
web:
scripts:
refactor to work with new database schema
styles:
adjust some style
use `<button>` to replace `<span>` to allow keyboard control
templates:
adjust to work with new database schema
use `<button>` to replace `<span>` to allow keyboard control
159 lines
4.0 KiB
JavaScript
159 lines
4.0 KiB
JavaScript
let gallery;
|
|
let sentinel;
|
|
let counter;
|
|
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";
|
|
const limit = window.innerWidth > 1600 ? 50 : 20;
|
|
|
|
function resetGallery() {
|
|
imgviewer.resetList();
|
|
gallery.replaceChildren(gallery.firstElementChild);
|
|
height = urlParams.get("height") || (window.innerWidth > 1600 ? 320 : 220);
|
|
offset = 0;
|
|
hasMore = true;
|
|
loadMore();
|
|
}
|
|
|
|
async function loadMore() {
|
|
if (loading || !hasMore) return;
|
|
|
|
observer.unobserve(sentinel);
|
|
loading = true;
|
|
|
|
try {
|
|
const res = await fetch(`/api/artworks?offset=${offset}&limit=${limit}&sort=${sort}`);
|
|
const data = await res.json();
|
|
|
|
switch (res.status) {
|
|
case 200:
|
|
break;
|
|
case 401:
|
|
alert("Session expired");
|
|
return;
|
|
case 500:
|
|
if (data.error) {
|
|
hasMore = false;
|
|
alert(`${data.msg}: ${data.error}`);
|
|
return
|
|
}
|
|
}
|
|
|
|
appendArtworks(data.artworks);
|
|
|
|
hasMore = data.offset + data.limit < data.total;
|
|
offset += data.limit;
|
|
counter.textContent = data.total;
|
|
if (!sort) sort = data.sort;
|
|
} catch (err) {
|
|
console.error(err);
|
|
hasMore = false;
|
|
} finally {
|
|
loading = false;
|
|
|
|
requestAnimationFrame(() => {
|
|
observer.observe(sentinel);
|
|
});
|
|
}
|
|
}
|
|
|
|
function appendArtworks(artworks) {
|
|
imgviewer.appendArtworks(artworks);
|
|
const fragment = document.createDocumentFragment();
|
|
|
|
for (const artwork of artworks) {
|
|
let image = artwork.edges.files[0];
|
|
const jWidth = image.width * height / image.height;
|
|
const jPadding = image.height / image.width * 100;
|
|
const wrapper = document.createElement("div");
|
|
wrapper.style.width = jWidth + "px";
|
|
wrapper.style.flexGrow = jWidth;
|
|
|
|
const i = document.createElement("i");
|
|
i.style.paddingBottom = jPadding + "%";
|
|
if (artwork.edges.files.length > 1) {
|
|
const span = document.createElement("span");
|
|
const p = document.createElement("p");
|
|
p.textContent = `${artwork.edges.files.length} pic`;
|
|
span.appendChild(p);
|
|
i.appendChild(span);
|
|
}
|
|
wrapper.appendChild(i);
|
|
|
|
const img = document.createElement("img");
|
|
img.loading = "lazy";
|
|
img.decoding = "async";
|
|
if (image.thumbed) {
|
|
img.src = `/thumbnails/${image.id_str}.jpg`;
|
|
img.dataset.highres = `/files/${image.id_str}.${image.ext}`;
|
|
} else {
|
|
img.src = `/files/${image.id_str}.${image.ext}`;
|
|
}
|
|
|
|
img.dataset.id_str = image.id_str;
|
|
img.dataset.artwork_id = artwork.id;
|
|
|
|
wrapper.appendChild(img);
|
|
imgviewer.bindClickEvents(img);
|
|
fragment.appendChild(wrapper);
|
|
}
|
|
|
|
gallery.appendChild(fragment);
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
gallery = document.getElementById("gallery");
|
|
sentinel = document.getElementById("sentinel");
|
|
counter = document.getElementById("artwork-count");
|
|
select = document.getElementById('sort-select')
|
|
|
|
select.value = sort
|
|
|
|
select.addEventListener('change', () => {
|
|
urlParams.set("sort", sort = select.value);
|
|
history.replaceState(null, null, `/artworks?${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);
|