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

View file

@ -239,6 +239,14 @@
<div class="fieldDescription">${LabelLibraryPageSizeHelp}</div> <div class="fieldDescription">${LabelLibraryPageSizeHelp}</div>
</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"> <div class="checkboxContainer checkboxContainer-withDescription fldBackdrops">
<label> <label>
<input type="checkbox" is="emby-checkbox" id="chkBackdrops" /> <input type="checkbox" is="emby-checkbox" id="chkBackdrops" />

View file

@ -81,6 +81,7 @@ export default function (view, params, tabContent) {
}; };
const reloadItems = () => { const reloadItems = () => {
if (isLoading) return;
loading.show(); loading.show();
isLoading = true; isLoading = true;
const query = getQuery(); const query = getQuery();
@ -109,7 +110,9 @@ export default function (view, params, tabContent) {
reloadItems(); reloadItems();
} }
if (!userSettings.enableInfiniteScroll()) {
window.scrollTo(0, 0); window.scrollTo(0, 0);
}
this.alphaPicker?.updateControls(query); this.alphaPicker?.updateControls(query);
let html; let html;
const pagingHtml = libraryBrowser.getQueryPagingHtml({ const pagingHtml = libraryBrowser.getQueryPagingHtml({
@ -154,6 +157,7 @@ export default function (view, params, tabContent) {
}); });
} }
if (!userSettings.enableInfiniteScroll()) {
let elems = tabContent.querySelectorAll('.paging'); let elems = tabContent.querySelectorAll('.paging');
for (const elem of elems) { for (const elem of elems) {
@ -163,15 +167,41 @@ export default function (view, params, tabContent) {
elems = tabContent.querySelectorAll('.btnNextPage'); elems = tabContent.querySelectorAll('.btnNextPage');
for (const elem of elems) { for (const elem of elems) {
elem.addEventListener('click', onNextPageClick); elem.addEventListener('click', onNextPageClick);
} };
elems = tabContent.querySelectorAll('.btnPreviousPage'); elems = tabContent.querySelectorAll('.btnPreviousPage');
for (const elem of elems) { for (const elem of elems) {
elem.addEventListener('click', onPreviousPageClick); 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'); const itemsContainer = tabContent.querySelector('.itemsContainer');
if (userSettings.enableInfiniteScroll()) {
itemsContainer.innerHTML += html;
} else {
itemsContainer.innerHTML = html; itemsContainer.innerHTML = html;
}
imageLoader.lazyChildren(itemsContainer); imageLoader.lazyChildren(itemsContainer);
userSettings.saveQuerySettings(getSavedQueryKey(), query); userSettings.saveQuerySettings(getSavedQueryKey(), query);
loading.hide(); loading.hide();
@ -185,7 +215,8 @@ export default function (view, params, tabContent) {
let pageData; let pageData;
let isLoading = false; let isLoading = false;
let hasMoreitems = true;
const scrollController = new AbortController();
this.showFilterMenu = function () { this.showFilterMenu = function () {
import('../../components/filterdialog/filterdialog').then(({ default: FilterDialog }) => { import('../../components/filterdialog/filterdialog').then(({ default: FilterDialog }) => {
const filterDialog = new FilterDialog({ const filterDialog = new FilterDialog({
@ -221,6 +252,7 @@ export default function (view, params, tabContent) {
delete query.NameLessThan; delete query.NameLessThan;
} }
query.StartIndex = 0; query.StartIndex = 0;
itemsContainer.innerHTML = '';
reloadItems(); reloadItems();
}); });
@ -284,6 +316,20 @@ export default function (view, params, tabContent) {
reloadItems(); 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('.btnPlayAll').addEventListener('click', playAll);
tabElement.querySelector('.btnShuffle').addEventListener('click', shuffle); tabElement.querySelector('.btnShuffle').addEventListener('click', shuffle);
}; };

View file

@ -65,6 +65,7 @@ export default function (view, params, tabContent, options) {
}; };
const reloadItems = () => { const reloadItems = () => {
if (isLoading) return;
loading.show(); loading.show();
isLoading = true; isLoading = true;
const query = getQuery(); const query = getQuery();
@ -96,7 +97,9 @@ export default function (view, params, tabContent, options) {
reloadItems(); reloadItems();
} }
if (!userSettings.enableInfiniteScroll()) {
window.scrollTo(0, 0); window.scrollTo(0, 0);
}
this.alphaPicker?.updateControls(query); this.alphaPicker?.updateControls(query);
let html; let html;
const pagingHtml = libraryBrowser.getQueryPagingHtml({ const pagingHtml = libraryBrowser.getQueryPagingHtml({
@ -136,24 +139,52 @@ export default function (view, params, tabContent, options) {
overlayPlayButton: true overlayPlayButton: true
}); });
} }
if (!userSettings.enableInfiniteScroll()) {
let elems = tabContent.querySelectorAll('.paging'); let elems = tabContent.querySelectorAll('.paging');
for (let i = 0, length = elems.length; i < length; i++) { for (const elem of elems) {
elems[i].innerHTML = pagingHtml; elem.innerHTML = pagingHtml;
} }
elems = tabContent.querySelectorAll('.btnNextPage'); elems = tabContent.querySelectorAll('.btnNextPage');
for (let i = 0, length = elems.length; i < length; i++) { for (const elem of elems) {
elems[i].addEventListener('click', onNextPageClick); elem.addEventListener('click', onNextPageClick);
} };
elems = tabContent.querySelectorAll('.btnPreviousPage'); elems = tabContent.querySelectorAll('.btnPreviousPage');
for (let i = 0, length = elems.length; i < length; i++) { for (const elem of elems) {
elems[i].addEventListener('click', onPreviousPageClick); 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'); const itemsContainer = tabContent.querySelector('.itemsContainer');
if (userSettings.enableInfiniteScroll()) {
itemsContainer.innerHTML += html;
} else {
itemsContainer.innerHTML = html; itemsContainer.innerHTML = html;
}
imageLoader.lazyChildren(itemsContainer); imageLoader.lazyChildren(itemsContainer);
userSettings.saveQuerySettings(getSavedQueryKey(), query); userSettings.saveQuerySettings(getSavedQueryKey(), query);
loading.hide(); loading.hide();
@ -167,7 +198,8 @@ export default function (view, params, tabContent, options) {
const data = {}; const data = {};
let isLoading = false; let isLoading = false;
let hasMoreitems = true;
const scrollController = new AbortController();
this.showFilterMenu = function () { this.showFilterMenu = function () {
import('../../components/filterdialog/filterdialog').then(({ default: FilterDialog }) => { import('../../components/filterdialog/filterdialog').then(({ default: FilterDialog }) => {
const filterDialog = new FilterDialog({ const filterDialog = new FilterDialog({
@ -202,6 +234,7 @@ export default function (view, params, tabContent, options) {
delete query.NameLessThan; delete query.NameLessThan;
} }
query.StartIndex = 0; query.StartIndex = 0;
itemsContainer.innerHTML = ''; // Clear the existing items
reloadItems(); reloadItems();
}); });
this.alphaPicker = new AlphaPicker({ this.alphaPicker = new AlphaPicker({
@ -228,6 +261,19 @@ export default function (view, params, tabContent, options) {
onViewStyleChange(); onViewStyleChange();
reloadItems(); 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); 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. * Get or set max days for next up list.
* @param {number|undefined} [val] - Max days for next up. * @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 getComicsPlayerSettings = currentSettings.getComicsPlayerSettings.bind(currentSettings);
export const setComicsPlayerSettings = currentSettings.setComicsPlayerSettings.bind(currentSettings); export const setComicsPlayerSettings = currentSettings.setComicsPlayerSettings.bind(currentSettings);
export const setFilter = currentSettings.setFilter.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 getFilter = currentSettings.getFilter.bind(currentSettings);
export const customCss = currentSettings.customCss.bind(currentSettings); export const customCss = currentSettings.customCss.bind(currentSettings);
export const disableCustomCss = currentSettings.disableCustomCss.bind(currentSettings); export const disableCustomCss = currentSettings.disableCustomCss.bind(currentSettings);