Search/static/js/dynamicscrollingimages.js
partisan 2929b7781a
All checks were successful
Run Integration Tests / test (push) Successful in 28s
added cool loading animation for dynamic scrolling
2025-04-23 13:18:12 +02:00

185 lines
No EOL
7 KiB
JavaScript

// dynamicscrollingimages.js
(function() {
// Add loading effects to image and title
function addLoadingEffects(imgElement) {
const title = imgElement.closest('.image').querySelector('.img_title');
imgElement.classList.add('loading-image');
title.classList.add('title-loading');
}
function removeLoadingEffects(imgElement) {
const title = imgElement.closest('.image').querySelector('.img_title');
imgElement.classList.remove('loading-image');
title.classList.remove('title-loading');
if (imgElement.src.endsWith('/images/missing.svg')) {
imgElement.closest('.image').remove();
}
}
// Modified handleImageError with theme-consistent error handling
function handleImageError(imgElement, retryCount = 3, retryDelay = 1000) {
const container = imgElement.closest('.image');
const title = container.querySelector('.img_title');
if (retryCount > 0) {
setTimeout(() => {
imgElement.src = imgElement.getAttribute('data-full');
imgElement.onerror = () => handleImageError(imgElement, retryCount - 1, retryDelay);
}, retryDelay);
} else {
imgElement.classList.remove('loading-image');
title.classList.remove('title-loading');
container.style.display = 'none';
}
}
// Rest of your existing code with minor additions
const imageStatusInterval = 500;
const scrollThreshold = 500;
const loadingIndicator = document.getElementById('message-bottom-right'); let loadingTimer;
let isFetching = false;
let page = parseInt(document.getElementById('template-data').getAttribute('data-page')) || 1;
let query = document.getElementById('template-data').getAttribute('data-query');
let hardCacheEnabled = document.getElementById('template-data').getAttribute('data-hard-cache-enabled') === 'true';
let noMoreImages = false;
let imageElements = [];
let imageIds = [];
let imageStatusTimer;
function showLoadingMessage() {
loadingIndicator.classList.add('visible');
}
function hideLoadingMessage() {
loadingIndicator.classList.remove('visible');
}
function ensureScrollable() {
if (noMoreImages) return;
if (document.body.scrollHeight <= window.innerHeight) {
fetchNextPage();
}
}
function fetchNextPage() {
if (isFetching || noMoreImages) return;
loadingTimer = setTimeout(() => {
showLoadingMessage();
}, 150);
isFetching = true;
page += 1;
fetch(`/search?q=${encodeURIComponent(query)}&t=image&p=${page}&ajax=true`)
.then(response => response.text())
.then(html => {
clearTimeout(loadingTimer);
hideLoadingMessage();
let tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
let newImages = tempDiv.querySelectorAll('.image');
if (newImages.length > 0) {
let resultsContainer = document.querySelector('.images');
newImages.forEach(imageDiv => {
let clonedImageDiv = imageDiv.cloneNode(true);
resultsContainer.appendChild(clonedImageDiv);
let img = clonedImageDiv.querySelector('img');
if (img && img.getAttribute('data-id')) {
addLoadingEffects(img);
if (hardCacheEnabled) {
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
img.onerror = () => handleImageError(img);
imageElements.push(img);
imageIds.push(img.getAttribute('data-id'));
} else {
img.src = img.getAttribute('data-full');
img.onload = () => removeLoadingEffects(img);
img.onerror = () => handleImageError(img);
}
}
});
if (hardCacheEnabled) checkImageStatus();
ensureScrollable();
} else {
noMoreImages = true;
}
isFetching = false;
})
.catch(error => {
clearTimeout(loadingTimer);
hideLoadingMessage();
console.error('Fetch error:', error);
isFetching = false;
});
}
function checkImageStatus() {
if (!hardCacheEnabled || imageIds.length === 0) return;
fetch(`/image_status?image_ids=${imageIds.join(',')}`)
.then(response => response.json())
.then(statusMap => {
const pendingImages = [];
const pendingIds = [];
imageElements.forEach(img => {
const id = img.getAttribute('data-id');
if (statusMap[id]) {
img.src = statusMap[id];
img.onload = () => removeLoadingEffects(img);
img.onerror = () => handleImageError(img);
} else {
pendingImages.push(img);
pendingIds.push(id);
}
});
imageElements = pendingImages;
imageIds = pendingIds;
ensureScrollable();
})
.catch(error => {
console.error('Status check error:', error);
});
}
// Initialize with loading effects
document.querySelectorAll('img[data-id]').forEach(img => {
const id = img.getAttribute('data-id');
if (id) {
addLoadingEffects(img);
imageElements.push(img);
imageIds.push(id);
if (hardCacheEnabled) {
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
} else {
img.src = img.getAttribute('data-full');
img.onload = () => removeLoadingEffects(img);
}
img.onerror = () => handleImageError(img);
}
});
// Rest of your existing code remains unchanged
if (hardCacheEnabled) {
imageStatusTimer = setInterval(checkImageStatus, imageStatusInterval);
checkImageStatus();
}
window.addEventListener('load', ensureScrollable);
window.addEventListener('scroll', () => {
if (isFetching || noMoreImages) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - scrollThreshold) {
fetchNextPage();
}
});
window.addEventListener('beforeunload', () => {
if (imageStatusTimer) clearInterval(imageStatusTimer);
});
})();