1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

infinitescroll

This commit is contained in:
gompa 2025-03-24 19:59:22 +01:00
parent 27febb599c
commit 61fb4a9c6b
5 changed files with 144 additions and 28 deletions

View file

@ -118,6 +118,7 @@ function loadForm(context, user, userSettings) {
context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos();
context.querySelector('#chkFadein').checked = userSettings.enableFastFadein();
context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash();
context.querySelector('#infscroll').checked = userSettings.enableInfiniteScroll();
context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops();
context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner();
@ -158,6 +159,7 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
userSettingsInstance.screensaverTime(context.querySelector('#txtScreensaverTime').value);
userSettingsInstance.libraryPageSize(context.querySelector('#txtLibraryPageSize').value);
userSettingsInstance.enableInfiniteScroll(context.querySelector('#infscroll').checked);
userSettingsInstance.maxDaysForNextUp(context.querySelector('#txtMaxDaysForNextUp').value);
userSettingsInstance.enableRewatchingInNextUp(context.querySelector('#chkRewatchingNextUp').checked);

View file

@ -239,6 +239,14 @@
<div class="fieldDescription">${LabelLibraryPageSizeHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldBackdrops">
<label>
<input type="checkbox" is="emby-checkbox" id="infscroll" />
<span>${infinitescroll}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${infinitescroll}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldBackdrops">
<label>
<input type="checkbox" is="emby-checkbox" id="chkBackdrops" />

View file

@ -81,6 +81,7 @@ export default function (view, params, tabContent) {
};
const reloadItems = () => {
if (isLoading) return;
loading.show();
isLoading = true;
const query = getQuery();
@ -109,7 +110,9 @@ export default function (view, params, tabContent) {
reloadItems();
}
window.scrollTo(0, 0);
if (!userSettings.enableInfiniteScroll()) {
window.scrollTo(0, 0);
}
this.alphaPicker?.updateControls(query);
let html;
const pagingHtml = libraryBrowser.getQueryPagingHtml({
@ -154,24 +157,51 @@ export default function (view, params, tabContent) {
});
}
let elems = tabContent.querySelectorAll('.paging');
if (!userSettings.enableInfiniteScroll()) {
let elems = tabContent.querySelectorAll('.paging');
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
}
elems = tabContent.querySelectorAll('.btnNextPage');
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
};
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
}
} else {
hasMoreitems = true;
// Check if we need to load more items
if (result.Items.length >= query.Limit) {
query.StartIndex += query.Limit;
} else if (query.NameStartsWith !== undefined) {
// no more items with the alphaPicker/NameStartsWith selection.
// increment ascii letter code and search with next letter
const nextletter = String.fromCharCode(query.NameStartsWith.charCodeAt(0) + 1);
// check if ascii code is smaler or equal to Z else disable loading more items
if (nextletter.charCodeAt(0) <= 90) {
query.NameStartsWith = nextletter;
//reset start index for new letter
query.StartIndex = 0;
} else {
hasMoreitems = false;
}
} else {
//if not searching with NameStartsWith set hasMoreitems false if no more items are found
hasMoreitems = false;
}
}
const itemsContainer = tabContent.querySelector('.itemsContainer');
itemsContainer.innerHTML = html;
if (userSettings.enableInfiniteScroll()) {
itemsContainer.innerHTML += html;
} else {
itemsContainer.innerHTML = html;
}
imageLoader.lazyChildren(itemsContainer);
userSettings.saveQuerySettings(getSavedQueryKey(), query);
loading.hide();
@ -185,7 +215,8 @@ export default function (view, params, tabContent) {
let pageData;
let isLoading = false;
let hasMoreitems = true;
const scrollController = new AbortController();
this.showFilterMenu = function () {
import('../../components/filterdialog/filterdialog').then(({ default: FilterDialog }) => {
const filterDialog = new FilterDialog({
@ -221,6 +252,7 @@ export default function (view, params, tabContent) {
delete query.NameLessThan;
}
query.StartIndex = 0;
itemsContainer.innerHTML = '';
reloadItems();
});
@ -284,6 +316,20 @@ export default function (view, params, tabContent) {
reloadItems();
});
if (userSettings.enableInfiniteScroll()) {
document.addEventListener('viewshow', () => {
// Stop the scroll event listener on view change
scrollController.abort();
}, { signal: scrollController.signal });
window.addEventListener('scroll', () => {
// check if tabelement is active else dont run reloaditems
if (window.scrollY >= document.documentElement.scrollHeight - 1000 && (!isLoading && hasMoreitems) && tabElement.classList.contains('is-active')) {
reloadItems();
}
}, { signal: scrollController.signal });
}
tabElement.querySelector('.btnPlayAll').addEventListener('click', playAll);
tabElement.querySelector('.btnShuffle').addEventListener('click', shuffle);
};

View file

@ -65,6 +65,7 @@ export default function (view, params, tabContent, options) {
};
const reloadItems = () => {
if (isLoading) return;
loading.show();
isLoading = true;
const query = getQuery();
@ -96,7 +97,9 @@ export default function (view, params, tabContent, options) {
reloadItems();
}
window.scrollTo(0, 0);
if (!userSettings.enableInfiniteScroll()) {
window.scrollTo(0, 0);
}
this.alphaPicker?.updateControls(query);
let html;
const pagingHtml = libraryBrowser.getQueryPagingHtml({
@ -136,24 +139,52 @@ export default function (view, params, tabContent, options) {
overlayPlayButton: true
});
}
let elems = tabContent.querySelectorAll('.paging');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].innerHTML = pagingHtml;
}
if (!userSettings.enableInfiniteScroll()) {
let elems = tabContent.querySelectorAll('.paging');
elems = tabContent.querySelectorAll('.btnNextPage');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onNextPageClick);
}
for (const elem of elems) {
elem.innerHTML = pagingHtml;
}
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (let i = 0, length = elems.length; i < length; i++) {
elems[i].addEventListener('click', onPreviousPageClick);
elems = tabContent.querySelectorAll('.btnNextPage');
for (const elem of elems) {
elem.addEventListener('click', onNextPageClick);
};
elems = tabContent.querySelectorAll('.btnPreviousPage');
for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick);
}
} else {
hasMoreitems = true;
// Check if we need to load more items
if (result.Items.length >= query.Limit) {
query.StartIndex += query.Limit;
} else if (query.NameStartsWith !== undefined) {
// no more items with the alphaPicker/NameStartsWith selection.
// increment ascii letter code and search with next letter
const nextletter = String.fromCharCode(query.NameStartsWith.charCodeAt(0) + 1);
// check if ascii code is smaller or equal to Z else disable loading more items
if (nextletter.charCodeAt(0) <= 90) {
query.NameStartsWith = nextletter;
//reset start index for new letter
query.StartIndex = 0;
} else {
hasMoreitems = false;
}
} else {
//if not searching with NameStartsWith set hasMoreitems false if no more items are found
hasMoreitems = false;
}
}
const itemsContainer = tabContent.querySelector('.itemsContainer');
itemsContainer.innerHTML = html;
if (userSettings.enableInfiniteScroll()) {
itemsContainer.innerHTML += html;
} else {
itemsContainer.innerHTML = html;
}
imageLoader.lazyChildren(itemsContainer);
userSettings.saveQuerySettings(getSavedQueryKey(), query);
loading.hide();
@ -167,7 +198,8 @@ export default function (view, params, tabContent, options) {
const data = {};
let isLoading = false;
let hasMoreitems = true;
const scrollController = new AbortController();
this.showFilterMenu = function () {
import('../../components/filterdialog/filterdialog').then(({ default: FilterDialog }) => {
const filterDialog = new FilterDialog({
@ -202,6 +234,7 @@ export default function (view, params, tabContent, options) {
delete query.NameLessThan;
}
query.StartIndex = 0;
itemsContainer.innerHTML = ''; // Clear the existing items
reloadItems();
});
this.alphaPicker = new AlphaPicker({
@ -228,6 +261,19 @@ export default function (view, params, tabContent, options) {
onViewStyleChange();
reloadItems();
});
if (userSettings.enableInfiniteScroll()) {
document.addEventListener('viewshow', () => {
// Stop the scroll event listener on view change
scrollController.abort();
}, { signal: scrollController.signal });
window.addEventListener('scroll', () => {
// check if tabelement is active else dont run reloaditems
if (window.scrollY >= document.documentElement.scrollHeight - 1000 && (!isLoading && hasMoreitems) && tabElement.classList.contains('is-active')) {
reloadItems();
}
}, { signal: scrollController.signal });
}
};
initPage(tabContent);

View file

@ -483,6 +483,19 @@ export class UserSettings {
}
}
/**
* Get or set infinite scroll .
* @param {boolean|undefined} [val] - enable infinite scroll
* @return {bool} infinite scroll enable.
*/
enableInfiniteScroll(val) {
if (val !== undefined) {
return this.set('enableInfiniteScroll', val, false);
}
return toBoolean(this.get('enableInfiniteScroll', false), false);
}
/**
* Get or set max days for next up list.
* @param {number|undefined} [val] - Max days for next up.
@ -703,6 +716,7 @@ export const setSubtitleAppearanceSettings = currentSettings.setSubtitleAppearan
export const getComicsPlayerSettings = currentSettings.getComicsPlayerSettings.bind(currentSettings);
export const setComicsPlayerSettings = currentSettings.setComicsPlayerSettings.bind(currentSettings);
export const setFilter = currentSettings.setFilter.bind(currentSettings);
export const enableInfiniteScroll = currentSettings.enableInfiniteScroll.bind(currentSettings);
export const getFilter = currentSettings.getFilter.bind(currentSettings);
export const customCss = currentSettings.customCss.bind(currentSettings);
export const disableCustomCss = currentSettings.disableCustomCss.bind(currentSettings);