mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-21 03:22:11 +00:00
Merge branch 'refactor/library-infinite-scroll' into feature-mark-library-item-as-watched
This commit is contained in:
commit
de64604796
4 changed files with 31 additions and 58 deletions
|
|
@ -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} />
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue