1
0
Fork 0
timelinize/frontend/resources/js/gallery.js
2025-10-04 13:14:25 -06:00

185 lines
5.5 KiB
JavaScript

var pageItems;
const galleryLimit = 100;
async function galleryPageMain() {
// set up the preview modal to work on our page
PreviewModal.prev = async (currentItem) => {
// TODO: These don't play nicely when semantic_text is filled out, is there a better way to do these?
const params = galleryFilterParams("prev", currentItem);
// params.limit = 1;
// params.sort = null; // let backend use smart sort
// if ($('#filter-sort').value == "ASC") {
// params.end_timestamp = currentItem.timestamp;
// } else {
// params.start_timestamp = currentItem.timestamp;
// }
const results = await app.SearchItems(params);
console.log("PREV RESULTS:", results, currentItem);
return results.items?.[0];
};
PreviewModal.next = async (currentItem) => {
const params = galleryFilterParams("next", currentItem);
// params.limit = 1;
// params.sort = null; // let backend use smart sort
// if ($('#filter-sort').value == "ASC") {
// params.start_timestamp = currentItem.timestamp;
// } else {
// params.end_timestamp = currentItem.timestamp;
// }
const results = await app.SearchItems(params);
console.log("NEXT RESULTS:", results, currentItem);
return results.items?.[0];
};
// perform search
const results = await app.SearchItems(galleryFilterParams());
console.log("RESULTS:", results)
// configure pagination links: enable next if we overflowed the search results limit,
// and enable prev if we are not on page 1; otherwise disable prev/next link(s)
for (const elem of $$('.pagination .page-next')) {
if (results.items?.length > galleryLimit) {
elem.classList.remove('disabled');
let newQS = new URLSearchParams(window.location.search);
newQS.set('page', currentPageNum() + 1);
elem.href = '?'+newQS.toString();
} else {
elem.classList.add('disabled');
elem.href = '';
}
}
for (const elem of $$('.pagination .page-prev')) {
if (currentPageNum() > 1) {
elem.classList.remove('disabled');
let newQS = new URLSearchParams(window.location.search);
if (currentPageNum() == 2) {
newQS.delete('page');
} else {
newQS.set('page', currentPageNum() - 1);
}
elem.href = '?'+newQS.toString();
} else {
elem.classList.add('disabled');
elem.href = '';
}
}
if (!results.items) {
return;
}
results.items.splice(galleryLimit); // strip the extra item that is only used for pagination purposes
// reset items
pageItems = {};
$('.filter-results').replaceChildren();
// render each item
for (let item of results.items) {
const elem = cloneTemplate('#tpl-media');
elem.id = `item-${item.id}`;
let mediaElem = itemContentElement(item, { thumbnail: true });
mediaElem.classList.add('card-img-top', 'overflow-hidden'); // overflow-hidden is needed for obfuscation mode...
/*
TODO: I kind of like this dreamy glow effect around each video (maybe image too?) -- maybe just on hover though?
<div style="position: relative; display: contents">
<video>...</video>
<div style="position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
box-shadow: inset 0px 0px 15px 20px white;
pointer-events: none;"></div>
</div>
*/
const dt = DateTime.fromISO(item.timestamp, { setZone: true });
elem.prepend(mediaElem);
elem.dataset.rowid = item.id;
$('.media-owner-avatar', elem).innerHTML = avatar(true, item.entity, "me-3");
$('.media-owner-name', elem).innerText = entityDisplayNameAndAttr(item.entity).name;
$('.media-timestamp', elem).innerText = `${dt.toLocaleString(DateTime.DATETIME_MED)} ${dt.toFormat("ZZZZ")}`;
if (item.score) {
$('.media-similarity-score', elem).innerHTML = `<b>${(item.score * 100).toFixed(3)}%</b> match`;
}
$('.filter-results').append(elem);
pageItems[item.id] = item;
}
};
// when preview modal is brought up, render item content
on('click', '.filter-results [data-bs-toggle=modal]', async e => {
const link = e.target.closest('[data-rowid]');
const itemID = link.dataset.rowid;
await PreviewModal.renderItem(pageItems[itemID]);
});
function galleryFilterParams(peekPrevOrNext, peekFromItem) {
const params = {
related: 1,
relations: [
// don't show motion pictures / live photos, since they are not
// considered their own item in a gallery sense, and perhaps
// more importantly, we don't want to have to generate a thumbnail
// for them (literally no need for a thumbnail of those, just
// wasted CPU time and storage space)
{
"not": true,
"relation_label": "motion"
}
],
flat: $('#include-attachments').checked,
offset: galleryLimit * (currentPageNum()-1),
limit: galleryLimit+1, // add one to know if there's a next page
sort: $('.date-sort').value,
};
commonFilterSearchParams(params);
// media types
params.data_type = [];
if ($('#format-images').checked) {
params.data_type.push("image/*");
}
if ($('#format-videos').checked) {
params.data_type.push("video/*");
}
// if peeking the previous or next page, adjust parameters accordingly
if (peekPrevOrNext == "prev") {
if (params.sort == "ASC") {
params.end_timestamp = peekFromItem.timestamp;
} else {
params.start_timestamp = peekFromItem.timestamp;
}
params.sort = null; // let backend use smart sort
params.limit = 1;
} else if (peekPrevOrNext == "next") {
if (params.sort == "ASC") {
params.start_timestamp = peekFromItem.timestamp;
} else {
params.end_timestamp = peekFromItem.timestamp;
}
params.sort = null; // let backend use smart sort
params.limit = 1;
}
console.log("PARAMS:", params)
return params;
}