Merge branch 'refactor/library-infinite-scroll' into feature-mark-library-item-as-watched

This commit is contained in:
Lachezar Lechev 2024-03-08 10:32:03 +02:00
commit de64604796
No known key found for this signature in database
GPG key ID: 69BDCB3ED8CE8037
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 classnames = require('classnames');
const { default: Icon } = require('@stremio/stremio-icons/react'); const { default: Icon } = require('@stremio/stremio-icons/react');
const NotFound = require('stremio/routes/NotFound'); 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 useLibrary = require('./useLibrary');
const useSelectableInputs = require('./useSelectableInputs'); const useSelectableInputs = require('./useSelectableInputs');
const styles = require('./styles'); const styles = require('./styles');
const SCROLL_TO_BOTTOM_TRESHOLD = 400;
function withModel(Library) { function withModel(Library) {
const withModel = ({ urlParams, queryParams }) => { const withModel = ({ urlParams, queryParams }) => {
const model = React.useMemo(() => { const model = React.useMemo(() => {
@ -46,9 +48,21 @@ function withModel(Library) {
const Library = ({ model, urlParams, queryParams }) => { const Library = ({ model, urlParams, queryParams }) => {
const profile = useProfile(); const profile = useProfile();
const notifications = useNotifications(); const notifications = useNotifications();
const library = useLibrary(model, urlParams, queryParams); const [library, loadNextPage] = useLibrary(model, urlParams, queryParams);
const [typeSelect, sortSelect, paginationInput] = useSelectableInputs(library); const [typeSelect, sortSelect, hasNextPage] = useSelectableInputs(library);
const [inputsModalOpen, openInputsModal, closeInputsModal] = useBinaryState(false); 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 (library.selected && library.selected.request.page === 1) {
scrollContainerRef.current.scrollTop = 0;
}
}, [library.selected]);
return ( return (
<MainNavBars className={styles['library-container']} route={model}> <MainNavBars className={styles['library-container']} route={model}>
<div className={styles['library-content']}> <div className={styles['library-content']}>
@ -58,12 +72,6 @@ const Library = ({ model, urlParams, queryParams }) => {
<Multiselect {...typeSelect} className={styles['select-input-container']} /> <Multiselect {...typeSelect} className={styles['select-input-container']} />
<Multiselect {...sortSelect} className={styles['select-input-container']} /> <Multiselect {...sortSelect} className={styles['select-input-container']} />
<div className={styles['spacing']} /> <div className={styles['spacing']} />
{
paginationInput !== null ?
<PaginationInput {...paginationInput} className={styles['pagination-input']} />
:
null
}
<Button className={styles['filter-container']} title={'All filters'} onClick={openInputsModal}> <Button className={styles['filter-container']} title={'All filters'} onClick={openInputsModal}>
<Icon className={styles['filter-icon']} name={'filters'} /> <Icon className={styles['filter-icon']} name={'filters'} />
</Button> </Button>
@ -107,7 +115,7 @@ const Library = ({ model, urlParams, queryParams }) => {
<div className={styles['message-label']}>Empty {model === 'library' ? 'Library' : 'Continue Watching'}</div> <div className={styles['message-label']}>Empty {model === 'library' ? 'Library' : 'Continue Watching'}</div>
</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) => ( {library.catalog.map((libItem, index) => (
<LibItem {...libItem} notifications={notifications} removable={model === 'library'} key={index} /> <LibItem {...libItem} notifications={notifications} removable={model === 'library'} key={index} />
))} ))}

View file

@ -7,13 +7,6 @@
multiselect-menu-container: menu-container; 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') { :import('~stremio/common/ModalDialog/styles.less') {
selectable-inputs-modal-container: modal-dialog-container; selectable-inputs-modal-container: modal-dialog-container;
selectable-inputs-modal-content: modal-dialog-content; selectable-inputs-modal-content: modal-dialog-content;
@ -75,26 +68,6 @@
.spacing { .spacing {
flex: 1; 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 { .message-container {
@ -277,10 +250,6 @@
display: none; display: none;
} }
.pagination-input {
margin-left: 0;
}
.filter-container { .filter-container {
display: flex; display: flex;
} }

View file

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

View file

@ -32,21 +32,7 @@ const mapSelectableInputs = (library, t) => {
window.location = event.value; window.location = event.value;
} }
}; };
const paginationInput = library.selectable.prevPage || library.selectable.nextPage ? return [typeSelect, sortSelect, 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];
}; };
const useSelectableInputs = (library) => { const useSelectableInputs = (library) => {