mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
817 lines
28 KiB
JavaScript
817 lines
28 KiB
JavaScript
(function () {
|
|
const RETRY_ATTEMPTS = 3;
|
|
const RETRY_DELAY = 1000;
|
|
|
|
// Common logging functions
|
|
const LOG = (...args) => console.log('[InfiniteScroll]', ...args);
|
|
const WARN = (...args) => console.warn('[InfiniteScroll]', ...args);
|
|
const ERR = (...args) => console.error('[InfiniteScroll]', ...args);
|
|
|
|
// Supported pages (same as headerTabs)
|
|
const SUPPORTED_PAGES = ['tv.html', 'movies.html', 'music.html'];
|
|
|
|
// Media type configurations - Read from centralized config or use defaults
|
|
const MEDIA_CONFIGS = {
|
|
'movies.html': {
|
|
includeItemTypes: 'Movie',
|
|
context: 'movies',
|
|
sortBy: 'SortName,ProductionYear'
|
|
},
|
|
'tv.html': {
|
|
includeItemTypes: 'Series',
|
|
context: 'tvshows',
|
|
sortBy: 'SortName,ProductionYear'
|
|
},
|
|
};
|
|
|
|
let userId = null;
|
|
const limit = 100;
|
|
|
|
let currentPage = null;
|
|
let currentTab = 0;
|
|
let startIndex = 0;
|
|
let loading = false;
|
|
let hasMore = true;
|
|
let totalRecordCount = null; // Track total items available from API
|
|
let retryCount = 0;
|
|
// scrollState removed - not needed
|
|
let scrollTimeout = null;
|
|
let isScrollTriggered = false; // Prevent multiple triggers
|
|
let isRequestInProgress = false; // Blocking flag to prevent multiple simultaneous requests
|
|
let hasInitialized = false; // Prevent multiple initializations
|
|
let isInitializing = false; // Prevent concurrent initializations
|
|
|
|
// Create loading indicator element
|
|
function createLoadingIndicator() {
|
|
const loadingDiv = document.createElement('div');
|
|
loadingDiv.className = 'infinite-scroll-loading';
|
|
loadingDiv.id = 'infinite-scroll-loading-indicator';
|
|
loadingDiv.innerHTML = `
|
|
<div class="spinner"></div>
|
|
<span>Loading more content...</span>
|
|
`;
|
|
return loadingDiv;
|
|
}
|
|
|
|
// Show loading indicator
|
|
function showLoadingIndicator() {
|
|
let loadingIndicator = document.getElementById('infinite-scroll-loading-indicator');
|
|
|
|
if (!loadingIndicator) {
|
|
loadingIndicator = createLoadingIndicator();
|
|
|
|
// Find the container and append the loading indicator as a sibling
|
|
const container = getContainer();
|
|
if (container && container.parentNode) {
|
|
container.parentNode.insertBefore(loadingIndicator, container.nextSibling);
|
|
} else {
|
|
// Fallback: append to body
|
|
document.body.appendChild(loadingIndicator);
|
|
}
|
|
}
|
|
|
|
// Show with animation
|
|
setTimeout(() => {
|
|
loadingIndicator.classList.add('show');
|
|
}, 10);
|
|
|
|
LOG('Loading indicator shown');
|
|
}
|
|
|
|
// Hide loading indicator
|
|
function hideLoadingIndicator() {
|
|
const loadingIndicator = document.getElementById('infinite-scroll-loading-indicator');
|
|
if (loadingIndicator) {
|
|
loadingIndicator.classList.remove('show');
|
|
|
|
// Remove from DOM after animation
|
|
setTimeout(() => {
|
|
if (loadingIndicator.parentNode) {
|
|
loadingIndicator.parentNode.removeChild(loadingIndicator);
|
|
}
|
|
}, 300);
|
|
|
|
LOG('Loading indicator hidden');
|
|
}
|
|
}
|
|
|
|
// Add CSS to hide paging controls when infinite scroll is active
|
|
function addInfiniteScrollCSS() {
|
|
// Check if infinite scroll is enabled in configuration
|
|
const isEnabled = window.KefinTweaksConfig?.scripts?.infiniteScroll !== false;
|
|
if (!isEnabled) {
|
|
LOG('Infinite scroll disabled in configuration, skipping CSS addition');
|
|
return;
|
|
}
|
|
|
|
const styleId = 'infinite-scroll-paging-hide';
|
|
|
|
// Check if style already exists
|
|
if (document.getElementById(styleId)) {
|
|
return;
|
|
}
|
|
|
|
const style = document.createElement('style');
|
|
style.id = styleId;
|
|
style.textContent = `
|
|
#moviesTab .listPaging>div,
|
|
#seriesTab .listPaging>div {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Infinite scroll loading indicator */
|
|
.infinite-scroll-loading {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
color: #666;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.infinite-scroll-loading.show {
|
|
opacity: 1;
|
|
}
|
|
|
|
.infinite-scroll-loading .spinner {
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 2px solid #f3f3f3;
|
|
border-top: 2px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: infinite-scroll-spin 1s linear infinite;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
@keyframes infinite-scroll-spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
`;
|
|
|
|
document.head.appendChild(style);
|
|
LOG('Added CSS to hide paging controls for infinite scroll');
|
|
}
|
|
|
|
// Wait for ApiClient to be available
|
|
function waitForApiClient() {
|
|
return new Promise((resolve) => {
|
|
if (typeof ApiClient !== 'undefined' && ApiClient._serverInfo && ApiClient._serverInfo.UserId) {
|
|
userId = ApiClient._serverInfo.UserId;
|
|
LOG('ApiClient ready, userId:', userId);
|
|
resolve();
|
|
} else {
|
|
LOG('Waiting for ApiClient...');
|
|
setTimeout(() => waitForApiClient().then(resolve), 100);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Wait for cardBuilder to be available
|
|
function waitForCardBuilder() {
|
|
return new Promise((resolve) => {
|
|
if (typeof window.cardBuilder !== 'undefined' && window.cardBuilder.buildCard) {
|
|
LOG('cardBuilder ready');
|
|
resolve();
|
|
} else {
|
|
LOG('Waiting for cardBuilder...');
|
|
setTimeout(() => waitForCardBuilder().then(resolve), 100);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Page detection function (same as headerTabs)
|
|
function getCurrentPage() {
|
|
const hash = window.location.hash;
|
|
const hashMatches = hash.match(/#\/([^?]+)/g);
|
|
|
|
if (!hashMatches || hashMatches.length === 0) {
|
|
LOG('No hash matches found for:', hash);
|
|
return null;
|
|
}
|
|
|
|
for (const match of hashMatches) {
|
|
const pageFromHash = match.replace('#/', '');
|
|
if (SUPPORTED_PAGES.includes(pageFromHash)) {
|
|
LOG('Found supported page:', pageFromHash);
|
|
return pageFromHash;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Get current tab index from URL
|
|
function getCurrentTab() {
|
|
const hash = window.location.hash;
|
|
let searchParams = '';
|
|
|
|
if (hash.includes('?')) {
|
|
searchParams = hash.split('?')[1];
|
|
} else {
|
|
searchParams = window.location.search.substring(1);
|
|
}
|
|
|
|
const urlParams = new URLSearchParams(searchParams);
|
|
const tabParam = urlParams.get('tab');
|
|
return tabParam ? parseInt(tabParam, 10) : 0;
|
|
}
|
|
|
|
// State persistence functions - REMOVED
|
|
// We don't need to store scroll state as it can cause issues with infinite scroll behavior
|
|
|
|
function getParentId() {
|
|
const match = window.location.hash.match(/topParentId=([^&]+)/);
|
|
return match ? match[1] : null;
|
|
}
|
|
|
|
// Get current sort settings from Jellyfin's localStorage keys
|
|
function getCurrentSortSettings() {
|
|
const userId = ApiClient.getCurrentUserId();
|
|
const parentId = getParentId();
|
|
const page = getCurrentPage();
|
|
|
|
if (!userId || !parentId || !page) {
|
|
LOG('Missing required IDs for sort detection:', { userId, parentId, page });
|
|
return null;
|
|
}
|
|
|
|
// Map page to media type
|
|
const mediaTypeMap = {
|
|
'movies.html': 'movies',
|
|
'tv.html': 'series'
|
|
};
|
|
|
|
const mediaType = mediaTypeMap[page];
|
|
if (!mediaType) {
|
|
LOG('Unsupported page for sort detection:', page);
|
|
return null;
|
|
}
|
|
|
|
// Build localStorage keys
|
|
const storageKey = `${userId}-${parentId}-${mediaType}`;
|
|
const filterKey = `${userId}-${parentId}-${mediaType}-filter`;
|
|
|
|
LOG('Looking for sort settings in localStorage keys:', { storageKey, filterKey });
|
|
|
|
try {
|
|
// Get main sort settings
|
|
const sortData = JSON.parse(localStorage.getItem(storageKey) || '{}');
|
|
const filterData = JSON.parse(localStorage.getItem(filterKey) || '{}');
|
|
|
|
LOG('Found sort data:', sortData);
|
|
LOG('Found filter data:', filterData);
|
|
|
|
return {
|
|
sortBy: sortData.SortBy || 'SortName,ProductionYear',
|
|
sortOrder: sortData.SortOrder || 'Ascending',
|
|
filters: filterData.Filters || '',
|
|
years: filterData.Years || '',
|
|
genres: filterData.Genres || '',
|
|
tags: filterData.Tags || '',
|
|
// Add other filter properties as needed
|
|
};
|
|
} catch (error) {
|
|
ERR('Error parsing sort settings from localStorage:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Get current alpha picker selection
|
|
function getCurrentAlphaPicker() {
|
|
const page = getCurrentPage();
|
|
if (!page) return null;
|
|
|
|
let activeTab = null;
|
|
if (page === 'tv.html') {
|
|
activeTab = document.querySelector("#seriesTab.is-active");
|
|
} else if (page === 'movies.html') {
|
|
activeTab = document.querySelector("#moviesTab.is-active");
|
|
}
|
|
|
|
if (!activeTab) return null;
|
|
|
|
const alphaPickerRow = activeTab.querySelector(".alphaPickerRow");
|
|
if (!alphaPickerRow) return null;
|
|
|
|
const selectedButton = alphaPickerRow.querySelector(".alphaPickerButton-selected");
|
|
if (!selectedButton) return null;
|
|
|
|
const dataValue = selectedButton.getAttribute('data-value');
|
|
LOG('Found alpha picker selection:', dataValue);
|
|
|
|
return dataValue;
|
|
}
|
|
|
|
// Get current start index based on existing items count
|
|
function getCurrentStartIndex() {
|
|
const container = getContainer();
|
|
if (!container) return 0;
|
|
|
|
const existingItemsCount = container.children.length;
|
|
LOG('Current start index based on existing items:', existingItemsCount);
|
|
|
|
return existingItemsCount;
|
|
}
|
|
|
|
// Update pagination display to show current range
|
|
function updatePaginationDisplay() {
|
|
const page = getCurrentPage();
|
|
if (!page) return;
|
|
|
|
let activeTab = null;
|
|
if (page === 'tv.html') {
|
|
activeTab = document.querySelector("#seriesTab.is-active");
|
|
} else if (page === 'movies.html') {
|
|
activeTab = document.querySelector("#moviesTab.is-active");
|
|
}
|
|
|
|
if (!activeTab) return;
|
|
|
|
const listPagings = activeTab.querySelectorAll(".listPaging");
|
|
if (!listPagings.length) return;
|
|
|
|
listPagings.forEach(listPaging => {
|
|
const span = listPaging.querySelector("span");
|
|
if (!span) return;
|
|
|
|
const container = getContainer();
|
|
if (!container) return;
|
|
|
|
const currentItemsCount = container.children.length;
|
|
const totalCount = totalRecordCount || '?';
|
|
|
|
// Update the display to show current range
|
|
span.textContent = `1-${currentItemsCount} of ${totalCount}`;
|
|
LOG('Updated pagination display:', { currentItemsCount, totalCount });
|
|
});
|
|
}
|
|
|
|
function getContainer() {
|
|
const libraryPage = document.querySelector(".libraryPage:not(.hide)");
|
|
if (!libraryPage) return null;
|
|
|
|
// Check if we're on the correct active tab
|
|
const page = getCurrentPage();
|
|
if (!page) return null;
|
|
|
|
let activeTab = null;
|
|
if (page === 'tv.html') {
|
|
activeTab = document.querySelector("#seriesTab.is-active");
|
|
} else if (page === 'movies.html') {
|
|
activeTab = document.querySelector("#moviesTab.is-active");
|
|
}
|
|
|
|
if (!activeTab) {
|
|
LOG('No active tab found for page:', page);
|
|
return null;
|
|
}
|
|
|
|
const container = activeTab.querySelector(".itemsContainer");
|
|
if (!container) {
|
|
LOG('No itemsContainer found in active tab:', activeTab);
|
|
return null;
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
async function loadMore() {
|
|
LOG('LoadMore called:', { loading, hasMore, isRequestInProgress, isScrollTriggered });
|
|
|
|
if (loading || !hasMore || isRequestInProgress) {
|
|
LOG('LoadMore blocked:', { loading, hasMore, isRequestInProgress });
|
|
return;
|
|
}
|
|
|
|
// Don't load more if there are no items in the container (page still loading)
|
|
const container = getContainer();
|
|
if (container && container.children.length === 0) {
|
|
LOG('LoadMore blocked: No items in container yet (page still loading)');
|
|
return;
|
|
}
|
|
|
|
LOG('Starting loadMore request...');
|
|
loading = true;
|
|
isRequestInProgress = true;
|
|
|
|
// Show loading indicator
|
|
showLoadingIndicator();
|
|
|
|
// Wait for ApiClient to be ready
|
|
if (!userId) {
|
|
LOG('Waiting for ApiClient before loading...');
|
|
await waitForApiClient();
|
|
}
|
|
|
|
// Wait for cardBuilder to be ready
|
|
await waitForCardBuilder();
|
|
|
|
const page = getCurrentPage();
|
|
const tab = getCurrentTab();
|
|
|
|
if (!page) {
|
|
WARN("No supported page detected");
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
hideLoadingIndicator();
|
|
return;
|
|
}
|
|
|
|
const parentId = getParentId();
|
|
if (!parentId) {
|
|
WARN("No topParentId found in URL");
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
hideLoadingIndicator();
|
|
return;
|
|
}
|
|
|
|
const mediaConfig = MEDIA_CONFIGS[page];
|
|
if (!mediaConfig) {
|
|
WARN("No media configuration found for page:", page);
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
hideLoadingIndicator();
|
|
return;
|
|
}
|
|
|
|
// Double-check ApiClient is available
|
|
if (typeof ApiClient === 'undefined') {
|
|
ERR('ApiClient is not available');
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
hideLoadingIndicator();
|
|
return;
|
|
}
|
|
|
|
// Get current start index based on existing items
|
|
startIndex = getCurrentStartIndex();
|
|
|
|
// Get current sort settings from localStorage
|
|
const sortSettings = getCurrentSortSettings();
|
|
let apiParams = {
|
|
SortBy: mediaConfig.sortBy, // fallback
|
|
SortOrder: "Ascending", // fallback
|
|
IncludeItemTypes: mediaConfig.includeItemTypes,
|
|
Recursive: true,
|
|
Fields: "PrimaryImageAspectRatio,MediaSourceCount",
|
|
ImageTypeLimit: 1,
|
|
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
|
ParentId: parentId,
|
|
StartIndex: startIndex,
|
|
Limit: limit
|
|
};
|
|
|
|
// Apply detected sort settings if available
|
|
if (sortSettings) {
|
|
LOG('Using detected sort settings:', sortSettings);
|
|
apiParams.SortBy = sortSettings.sortBy;
|
|
apiParams.SortOrder = sortSettings.sortOrder;
|
|
|
|
// Add filter parameters if they exist
|
|
if (sortSettings.filters) {
|
|
apiParams.Filters = sortSettings.filters;
|
|
}
|
|
if (sortSettings.years) {
|
|
apiParams.Years = sortSettings.years;
|
|
}
|
|
if (sortSettings.genres) {
|
|
apiParams.Genres = sortSettings.genres;
|
|
}
|
|
if (sortSettings.tags) {
|
|
apiParams.Tags = sortSettings.tags;
|
|
}
|
|
} else {
|
|
LOG('Using fallback sort settings');
|
|
}
|
|
|
|
// Add alpha picker support
|
|
const alphaPicker = getCurrentAlphaPicker();
|
|
if (alphaPicker) {
|
|
apiParams.NameStartsWith = alphaPicker;
|
|
LOG('Added alpha picker filter:', alphaPicker);
|
|
}
|
|
|
|
const url = ApiClient.getUrl("Users/" + userId + "/Items", apiParams);
|
|
|
|
LOG('Loading more items:', { page, tab, startIndex, limit, url });
|
|
|
|
try {
|
|
const result = await ApiClient.getJSON(url);
|
|
|
|
// Update total record count from API response
|
|
if (result.TotalRecordCount !== undefined) {
|
|
totalRecordCount = result.TotalRecordCount;
|
|
LOG('Updated total record count from API:', totalRecordCount);
|
|
}
|
|
|
|
if (result.Items && result.Items.length > 0) {
|
|
const container = getContainer();
|
|
if (container) {
|
|
// Check if cardBuilder is available
|
|
if (typeof window.cardBuilder === 'undefined' || !window.cardBuilder.buildCard) {
|
|
ERR('cardBuilder not available or buildCard method missing');
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
hideLoadingIndicator();
|
|
return;
|
|
}
|
|
|
|
LOG('Building cards:', {
|
|
itemCount: result.Items.length,
|
|
context: mediaConfig.context,
|
|
container: container
|
|
});
|
|
|
|
// Build each card individually and append to container
|
|
result.Items.forEach((item, index) => {
|
|
const card = window.cardBuilder.buildCard(item);
|
|
card.setAttribute('data-index', startIndex + index);
|
|
container.appendChild(card);
|
|
});
|
|
|
|
LOG('Cards built successfully');
|
|
}
|
|
startIndex += result.Items.length;
|
|
|
|
// Update pagination display
|
|
updatePaginationDisplay();
|
|
|
|
// Update hasMore based on total record count
|
|
if (totalRecordCount !== null) {
|
|
hasMore = startIndex < totalRecordCount;
|
|
LOG('hasMore updated based on total count:', {
|
|
startIndex,
|
|
totalRecordCount,
|
|
hasMore
|
|
});
|
|
} else {
|
|
// Fallback to old logic if no total count available
|
|
hasMore = result.Items.length >= limit;
|
|
LOG('hasMore updated using fallback logic:', {
|
|
itemsLength: result.Items.length,
|
|
limit,
|
|
hasMore
|
|
});
|
|
}
|
|
|
|
// Scroll state persistence removed - not needed
|
|
|
|
LOG('Loaded items successfully:', {
|
|
loaded: result.Items.length,
|
|
startIndex,
|
|
totalRecordCount,
|
|
hasMore
|
|
});
|
|
} else {
|
|
hasMore = false;
|
|
LOG('No more items to load (empty result)');
|
|
}
|
|
|
|
// Reset retry count on success
|
|
retryCount = 0;
|
|
|
|
} catch (err) {
|
|
ERR("Infinite scroll load error", err);
|
|
|
|
// Retry logic
|
|
if (retryCount < RETRY_ATTEMPTS) {
|
|
retryCount++;
|
|
WARN(`Retrying loadMore (attempt ${retryCount}/${RETRY_ATTEMPTS})`);
|
|
setTimeout(() => {
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
loadMore();
|
|
}, RETRY_DELAY * retryCount);
|
|
return;
|
|
} else {
|
|
ERR("Max retry attempts reached, giving up");
|
|
hasMore = false;
|
|
}
|
|
} finally {
|
|
loading = false;
|
|
isRequestInProgress = false;
|
|
|
|
// Hide loading indicator
|
|
hideLoadingIndicator();
|
|
}
|
|
}
|
|
|
|
function setupScrollWatcher() {
|
|
// Remove any existing scroll listener
|
|
window.removeEventListener("scroll", handleScroll);
|
|
|
|
// Add throttled scroll listener
|
|
window.addEventListener("scroll", handleScroll);
|
|
LOG('Scroll watcher setup complete');
|
|
}
|
|
|
|
function handleScroll() {
|
|
// Throttle scroll events - only check every 100ms
|
|
if (scrollTimeout) {
|
|
clearTimeout(scrollTimeout);
|
|
}
|
|
|
|
scrollTimeout = setTimeout(() => {
|
|
checkScrollPosition();
|
|
}, 100);
|
|
}
|
|
|
|
function checkScrollPosition() {
|
|
// Don't trigger if already loading, no more items, or request in progress
|
|
if (loading || !hasMore || isScrollTriggered || isRequestInProgress) {
|
|
LOG('Scroll check blocked:', { loading, hasMore, isScrollTriggered, isRequestInProgress });
|
|
return;
|
|
}
|
|
|
|
const container = getContainer();
|
|
if (!container) {
|
|
LOG('No container found in scroll check');
|
|
return;
|
|
}
|
|
|
|
// Don't trigger if there are no items in the container (page still loading)
|
|
const existingItemsCount = container.children.length;
|
|
if (existingItemsCount === 0) {
|
|
LOG('Scroll check blocked: No items in container yet (page still loading)');
|
|
return;
|
|
}
|
|
|
|
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
const windowHeight = window.innerHeight;
|
|
const documentHeight = document.documentElement.scrollHeight;
|
|
|
|
// Calculate distance from bottom - trigger when one viewport height away
|
|
const distanceFromBottom = documentHeight - (scrollTop + windowHeight);
|
|
const triggerThreshold = windowHeight; // One viewport height from bottom
|
|
|
|
LOG('Scroll check:', {
|
|
scrollTop,
|
|
windowHeight,
|
|
documentHeight,
|
|
distanceFromBottom,
|
|
triggerThreshold,
|
|
existingItemsCount,
|
|
loading,
|
|
hasMore,
|
|
isScrollTriggered,
|
|
isRequestInProgress
|
|
});
|
|
|
|
if (distanceFromBottom <= triggerThreshold) {
|
|
isScrollTriggered = true;
|
|
LOG('Triggering loadMore from scroll (one viewport height from bottom)');
|
|
loadMore().finally(() => {
|
|
// Reset trigger flag after load completes
|
|
setTimeout(() => {
|
|
isScrollTriggered = false;
|
|
LOG('Scroll trigger flag reset');
|
|
}, 1000); // Wait 1 second before allowing next trigger
|
|
});
|
|
}
|
|
}
|
|
|
|
// Reset state when navigating to a new library or tab
|
|
function reset() {
|
|
const page = getCurrentPage();
|
|
const tab = getCurrentTab();
|
|
|
|
LOG('Resetting infinite scroll state:', { page, tab });
|
|
|
|
// startIndex will be calculated dynamically based on existing items
|
|
hasMore = true;
|
|
totalRecordCount = null;
|
|
loading = false;
|
|
retryCount = 0;
|
|
isScrollTriggered = false;
|
|
isRequestInProgress = false;
|
|
|
|
// Scroll state restoration removed - not needed
|
|
|
|
// Clear container if switching pages/tabs
|
|
if (currentPage !== page || currentTab !== tab) {
|
|
const container = getContainer();
|
|
if (container) {
|
|
//container.innerHTML = '';
|
|
LOG('[REMOVED] Cleared container for new page/tab');
|
|
}
|
|
hasInitialized = false; // Reset initialization flag for new page/tab
|
|
isInitializing = false; // Reset initializing flag for new page/tab
|
|
}
|
|
|
|
currentPage = page;
|
|
currentTab = tab;
|
|
}
|
|
|
|
// Check if we should initialize infinite scroll
|
|
function shouldInitialize() {
|
|
const page = getCurrentPage();
|
|
const container = getContainer();
|
|
|
|
if (!page) {
|
|
return false;
|
|
}
|
|
|
|
if (!container) {
|
|
LOG('No active tab container found, waiting...');
|
|
return false;
|
|
}
|
|
|
|
// Double-check that we have the correct active tab
|
|
let activeTab = null;
|
|
if (page === 'tv.html') {
|
|
activeTab = document.querySelector("#seriesTab.is-active");
|
|
} else if (page === 'movies.html') {
|
|
activeTab = document.querySelector("#moviesTab.is-active");
|
|
}
|
|
|
|
if (!activeTab) {
|
|
LOG('No active tab found for page, skipping initialization:', page);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Initialize infinite scroll
|
|
async function initialize() {
|
|
if (!shouldInitialize() || hasInitialized || isInitializing) {
|
|
LOG('Skipping initialization:', { shouldInit: shouldInitialize(), hasInitialized, isInitializing });
|
|
return;
|
|
}
|
|
|
|
LOG('Initializing infinite scroll');
|
|
isInitializing = true;
|
|
hasInitialized = true;
|
|
|
|
try {
|
|
// Add CSS to hide paging controls
|
|
addInfiniteScrollCSS();
|
|
|
|
// Wait for ApiClient to be ready
|
|
if (!userId) {
|
|
LOG('Waiting for ApiClient before initialization...');
|
|
await waitForApiClient();
|
|
}
|
|
|
|
// Wait for cardBuilder to be ready
|
|
await waitForCardBuilder();
|
|
|
|
reset();
|
|
//loadMore(); // load first page
|
|
setupScrollWatcher();
|
|
|
|
// Update pagination display with initial state
|
|
updatePaginationDisplay();
|
|
} finally {
|
|
isInitializing = false;
|
|
}
|
|
}
|
|
|
|
// Scroll state loading removed - not needed
|
|
|
|
// Wait for page to mount
|
|
const observer = new MutationObserver(() => {
|
|
if (shouldInitialize()) {
|
|
initialize().catch(err => ERR('Initialization failed:', err));
|
|
}
|
|
});
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
|
|
// Also try to initialize immediately if page is already loaded
|
|
if (document.readyState === 'complete') {
|
|
setTimeout(() => {
|
|
initialize().catch(err => ERR('Initial initialization failed:', err));
|
|
}, 100);
|
|
}
|
|
|
|
// Cleanup function
|
|
function cleanup() {
|
|
window.removeEventListener("scroll", handleScroll);
|
|
if (scrollTimeout) {
|
|
clearTimeout(scrollTimeout);
|
|
}
|
|
|
|
// Remove the CSS that hides paging controls
|
|
const styleElement = document.getElementById('infinite-scroll-paging-hide');
|
|
if (styleElement) {
|
|
styleElement.remove();
|
|
LOG('Removed CSS for paging controls');
|
|
}
|
|
|
|
// Hide and remove loading indicator
|
|
hideLoadingIndicator();
|
|
|
|
LOG('Infinite scroll cleanup complete');
|
|
}
|
|
|
|
// Cleanup on page unload
|
|
window.addEventListener('beforeunload', cleanup);
|
|
})();
|