Merge pull request #595 from Stremio/refactor/library-infinite-scroll

Library infinite scroll
This commit is contained in:
Tim 2024-03-13 18:19:44 +01:00 committed by GitHub
commit d6238014dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 31 additions and 58 deletions

View file

@ -5,11 +5,13 @@ const PropTypes = require('prop-types');
const classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react');
const NotFound = require('stremio/routes/NotFound');
const { Button, DelayedRenderer, Multiselect, MainNavBars, LibItem, Image, ModalDialog, PaginationInput, useProfile, useNotifications, routesRegexp, useBinaryState, withCoreSuspender } = require('stremio/common');
const { Button, DelayedRenderer, Multiselect, MainNavBars, LibItem, Image, ModalDialog, useProfile, useNotifications, routesRegexp, useOnScrollToBottom, useBinaryState, withCoreSuspender } = require('stremio/common');
const useLibrary = require('./useLibrary');
const useSelectableInputs = require('./useSelectableInputs');
const styles = require('./styles');
const SCROLL_TO_BOTTOM_TRESHOLD = 400;
function withModel(Library) {
const withModel = ({ urlParams, queryParams }) => {
const model = React.useMemo(() => {
@ -46,9 +48,21 @@ function withModel(Library) {
const Library = ({ model, urlParams, queryParams }) => {
const profile = useProfile();
const notifications = useNotifications();
const library = useLibrary(model, urlParams, queryParams);
const [typeSelect, sortSelect, paginationInput] = useSelectableInputs(library);
const [library, loadNextPage] = useLibrary(model, urlParams, queryParams);
const [typeSelect, sortSelect, hasNextPage] = useSelectableInputs(library);
const [inputsModalOpen, openInputsModal, closeInputsModal] = useBinaryState(false);
const scrollContainerRef = React.useRef(null);
const onScrollToBottom = React.useCallback(() => {
if (hasNextPage) {
loadNextPage();
}
}, [hasNextPage, loadNextPage]);
const onScroll = useOnScrollToBottom(onScrollToBottom, SCROLL_TO_BOTTOM_TRESHOLD);
React.useLayoutEffect(() => {
if (profile.auth !== null && library.selected && library.selected.request.page === 1) {
scrollContainerRef.current.scrollTop = 0;
}
}, [profile.auth, library.selected]);
return (
<MainNavBars className={styles['library-container']} route={model}>
<div className={styles['library-content']}>
@ -58,12 +72,6 @@ const Library = ({ model, urlParams, queryParams }) => {
<Multiselect {...typeSelect} className={styles['select-input-container']} />
<Multiselect {...sortSelect} className={styles['select-input-container']} />
<div className={styles['spacing']} />
{
paginationInput !== null ?
<PaginationInput {...paginationInput} className={styles['pagination-input']} />
:
null
}
<Button className={styles['filter-container']} title={'All filters'} onClick={openInputsModal}>
<Icon className={styles['filter-icon']} name={'filters'} />
</Button>
@ -107,7 +115,7 @@ const Library = ({ model, urlParams, queryParams }) => {
<div className={styles['message-label']}>Empty {model === 'library' ? 'Library' : 'Continue Watching'}</div>
</div>
:
<div className={classnames(styles['meta-items-container'], 'animation-fade-in')}>
<div ref={scrollContainerRef} className={classnames(styles['meta-items-container'], 'animation-fade-in')} onScroll={onScroll}>
{library.catalog.map((libItem, index) => (
<LibItem {...libItem} notifications={notifications} removable={model === 'library'} key={index} />
))}

View file

@ -7,13 +7,6 @@
multiselect-menu-container: menu-container;
}
:import('~stremio/common/PaginationInput/styles.less') {
pagination-prev-button-container: prev-button-container;
pagination-next-button-container: next-button-container;
pagination-button-icon: icon;
pagination-label: label;
}
:import('~stremio/common/ModalDialog/styles.less') {
selectable-inputs-modal-container: modal-dialog-container;
selectable-inputs-modal-content: modal-dialog-content;
@ -75,26 +68,6 @@
.spacing {
flex: 1;
}
.pagination-input {
flex: none;
height: 3rem;
margin-left: 1.5rem;
.pagination-prev-button-container, .pagination-next-button-container {
width: 3rem;
height: 3rem;
.pagination-button-icon {
width: 1rem;
height: 1rem;
}
}
.pagination-label {
width: 3rem;
}
}
}
.message-container {
@ -277,10 +250,6 @@
display: none;
}
.pagination-input {
margin-left: 0;
}
.filter-container {
display: flex;
}

View file

@ -1,9 +1,19 @@
// Copyright (C) 2017-2023 Smart code 203358507
const React = require('react');
const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const useLibrary = (model, urlParams, queryParams) => {
const { core } = useServices();
const loadNextPage = React.useCallback(() => {
core.transport.dispatch({
action: 'LibraryWithFilters',
args: {
action: 'LoadNextPage',
}
}, 'library');
}, []);
const action = React.useMemo(() => ({
action: 'Load',
args: {
@ -12,12 +22,12 @@ const useLibrary = (model, urlParams, queryParams) => {
request: {
type: typeof urlParams.type === 'string' ? urlParams.type : null,
sort: queryParams.has('sort') ? queryParams.get('sort') : undefined,
page: queryParams.has('page') ? parseInt(queryParams.get('page'), 10) : undefined
}
}
}
}), [urlParams, queryParams]);
return useModelState({ model, action });
const library = useModelState({ model, action });
return [library, loadNextPage];
};
module.exports = useLibrary;

View file

@ -32,21 +32,7 @@ const mapSelectableInputs = (library, t) => {
window.location = event.value;
}
};
const paginationInput = library.selectable.prevPage || library.selectable.nextPage ?
{
label: library.selected.request.page.toString(),
onSelect: (event) => {
if (event.value === 'prev' && library.selectable.prevPage) {
window.location = library.selectable.prevPage.deepLinks.library;
}
if (event.value === 'next' && library.selectable.nextPage) {
window.location = library.selectable.nextPage.deepLinks.library;
}
}
}
:
null;
return [typeSelect, sortSelect, paginationInput];
return [typeSelect, sortSelect, library.selectable.nextPage];
};
const useSelectableInputs = (library) => {