mirror of
https://github.com/Radarr/Radarr
synced 2026-05-08 11:10:44 +02:00
Fixed: Scroll position not reset when navigating on small screens
This commit is contained in:
parent
4b85fab05b
commit
459a92335a
8 changed files with 60 additions and 89 deletions
|
|
@ -224,8 +224,6 @@ class Collection extends Component {
|
|||
view,
|
||||
onSortSelect,
|
||||
onFilterSelect,
|
||||
initialScrollTop,
|
||||
onScroll,
|
||||
isRefreshingCollections,
|
||||
isSaving,
|
||||
isAdding,
|
||||
|
|
@ -307,7 +305,7 @@ class Collection extends Component {
|
|||
ref={this.scrollerRef}
|
||||
className={styles.contentBody}
|
||||
innerClassName={styles[`${view}InnerContentBody`]}
|
||||
onScroll={onScroll}
|
||||
scrollPositionKey="movieCollections"
|
||||
>
|
||||
{
|
||||
isFetching && !isPopulated &&
|
||||
|
|
@ -336,7 +334,6 @@ class Collection extends Component {
|
|||
onSelectedChange={this.onSelectedChange}
|
||||
onSelectAllChange={this.onSelectAllChange}
|
||||
selectedState={selectedState}
|
||||
scrollTop={initialScrollTop}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -377,7 +374,6 @@ class Collection extends Component {
|
|||
}
|
||||
|
||||
Collection.propTypes = {
|
||||
initialScrollTop: PropTypes.number,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
|
|
@ -395,7 +391,6 @@ Collection.propTypes = {
|
|||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
onSortSelect: PropTypes.func.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired,
|
||||
onUpdateSelectedPress: PropTypes.func.isRequired,
|
||||
onRefreshMovieCollectionsPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withScrollPosition from 'Components/withScrollPosition';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import {
|
||||
fetchMovieCollections,
|
||||
|
|
@ -12,7 +11,6 @@ import {
|
|||
setMovieCollectionsSort
|
||||
} from 'Store/Actions/movieCollectionActions';
|
||||
import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
import createCollectionClientSideCollectionItemsSelector from 'Store/Selectors/createCollectionClientSideCollectionItemsSelector';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
|
|
@ -82,10 +80,6 @@ class CollectionConnector extends Component {
|
|||
//
|
||||
// Listeners
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
scrollPositions.movieCollections = scrollTop;
|
||||
};
|
||||
|
||||
onUpdateSelectedPress = (payload) => {
|
||||
this.props.onUpdateSelectedPress(payload);
|
||||
};
|
||||
|
|
@ -105,7 +99,6 @@ class CollectionConnector extends Component {
|
|||
<Collection
|
||||
{...otherProps}
|
||||
onViewSelect={this.onViewSelect}
|
||||
onScroll={this.onScroll}
|
||||
onUpdateSelectedPress={this.onUpdateSelectedPress}
|
||||
/>
|
||||
);
|
||||
|
|
@ -121,7 +114,4 @@ CollectionConnector.propTypes = {
|
|||
dispatchClearQueueDetails: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withScrollPosition(
|
||||
connect(createMapStateToProps, createMapDispatchToProps)(CollectionConnector),
|
||||
'movieCollections'
|
||||
);
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(CollectionConnector);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { ForwardedRef, forwardRef, ReactNode, useCallback } from 'react';
|
||||
import Scroller, { OnScroll } from 'Components/Scroller/Scroller';
|
||||
import useScrollPosition from 'Helpers/Hooks/useScrollPosition';
|
||||
import { isLocked } from 'Utilities/scrollLock';
|
||||
import styles from './PageContentBody.css';
|
||||
|
||||
|
|
@ -7,7 +8,7 @@ interface PageContentBodyProps {
|
|||
className?: string;
|
||||
innerClassName?: string;
|
||||
children: ReactNode;
|
||||
initialScrollTop?: number;
|
||||
scrollPositionKey?: string;
|
||||
onScroll?: (payload: OnScroll) => void;
|
||||
}
|
||||
|
||||
|
|
@ -17,26 +18,32 @@ const PageContentBody = forwardRef(
|
|||
className = styles.contentBody,
|
||||
innerClassName = styles.innerContentBody,
|
||||
children,
|
||||
scrollPositionKey,
|
||||
onScroll,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const onScrollWrapper = useCallback(
|
||||
const { initialScrollTop, onScroll: onScrollMemo } =
|
||||
useScrollPosition(scrollPositionKey);
|
||||
|
||||
const handleScroll = useCallback(
|
||||
(payload: OnScroll) => {
|
||||
if (onScroll && !isLocked()) {
|
||||
onScroll(payload);
|
||||
if (isLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
onScrollMemo(payload);
|
||||
onScroll?.(payload);
|
||||
},
|
||||
[onScroll]
|
||||
[onScroll, onScrollMemo]
|
||||
);
|
||||
|
||||
return (
|
||||
<Scroller
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
className={className}
|
||||
scrollDirection="vertical"
|
||||
onScroll={onScrollWrapper}
|
||||
initialScrollTop={initialScrollTop}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<div className={innerClassName}>{children}</div>
|
||||
</Scroller>
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
|
||||
interface WrappedComponentProps {
|
||||
initialScrollTop: number;
|
||||
}
|
||||
|
||||
interface ScrollPositionProps {
|
||||
history: RouteComponentProps['history'];
|
||||
location: RouteComponentProps['location'];
|
||||
match: RouteComponentProps['match'];
|
||||
}
|
||||
|
||||
function withScrollPosition(
|
||||
WrappedComponent: React.FC<WrappedComponentProps>,
|
||||
scrollPositionKey: string
|
||||
) {
|
||||
function ScrollPosition(props: ScrollPositionProps) {
|
||||
const { history } = props;
|
||||
|
||||
const initialScrollTop =
|
||||
history.action === 'POP' ? scrollPositions[scrollPositionKey] : 0;
|
||||
|
||||
return <WrappedComponent {...props} initialScrollTop={initialScrollTop} />;
|
||||
}
|
||||
|
||||
return ScrollPosition;
|
||||
}
|
||||
|
||||
export default withScrollPosition;
|
||||
|
|
@ -259,8 +259,6 @@ class DiscoverMovie extends Component {
|
|||
onSortSelect,
|
||||
onFilterSelect,
|
||||
onViewSelect,
|
||||
initialScrollTop,
|
||||
onScroll,
|
||||
onAddMoviesPress,
|
||||
isSyncingLists,
|
||||
...otherProps
|
||||
|
|
@ -370,7 +368,7 @@ class DiscoverMovie extends Component {
|
|||
ref={this.scrollerRef}
|
||||
className={styles.contentBody}
|
||||
innerClassName={styles[`${view}InnerContentBody`]}
|
||||
onScroll={onScroll}
|
||||
scrollPositionKey="discoverMovie"
|
||||
>
|
||||
{
|
||||
isFetching && !isPopulated &&
|
||||
|
|
@ -399,7 +397,6 @@ class DiscoverMovie extends Component {
|
|||
onSelectedChange={this.onSelectedChange}
|
||||
onSelectAllChange={this.onSelectAllChange}
|
||||
selectedState={selectedState}
|
||||
scrollTop={initialScrollTop}
|
||||
{...otherProps}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -444,7 +441,6 @@ class DiscoverMovie extends Component {
|
|||
}
|
||||
|
||||
DiscoverMovie.propTypes = {
|
||||
initialScrollTop: PropTypes.number,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
|
|
@ -465,7 +461,6 @@ DiscoverMovie.propTypes = {
|
|||
onSortSelect: PropTypes.func.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onViewSelect: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired,
|
||||
onAddMoviesPress: PropTypes.func.isRequired,
|
||||
onExcludeMoviesPress: PropTypes.func.isRequired,
|
||||
onImportListSyncPress: PropTypes.func.isRequired,
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@ import React, { Component } from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import withScrollPosition from 'Components/withScrollPosition';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import { addImportListExclusions, addMovies, clearAddMovie, fetchDiscoverMovies, setListMovieFilter, setListMovieSort, setListMovieTableOption, setListMovieView } from 'Store/Actions/discoverMovieActions';
|
||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import createDiscoverMovieClientSideCollectionItemsSelector from 'Store/Selectors/createDiscoverMovieClientSideCollectionItemsSelector';
|
||||
|
|
@ -106,10 +104,6 @@ class DiscoverMovieConnector extends Component {
|
|||
this.props.dispatchSetListMovieView(view);
|
||||
};
|
||||
|
||||
onScroll = ({ scrollTop }) => {
|
||||
scrollPositions.discoverMovie = scrollTop;
|
||||
};
|
||||
|
||||
onAddMoviesPress = ({ ids, addOptions }) => {
|
||||
this.props.dispatchAddMovies(ids, addOptions);
|
||||
};
|
||||
|
|
@ -126,7 +120,6 @@ class DiscoverMovieConnector extends Component {
|
|||
<DiscoverMovie
|
||||
{...this.props}
|
||||
onViewSelect={this.onViewSelect}
|
||||
onScroll={this.onScroll}
|
||||
onAddMoviesPress={this.onAddMoviesPress}
|
||||
onExcludeMoviesPress={this.onExcludeMoviesPress}
|
||||
onSyncListsPress={this.onSyncListsPress}
|
||||
|
|
@ -146,7 +139,4 @@ DiscoverMovieConnector.propTypes = {
|
|||
dispatchAddImportListExclusions: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default withScrollPosition(
|
||||
connect(createMapStateToProps, createMapDispatchToProps)(DiscoverMovieConnector),
|
||||
'discoverMovie'
|
||||
);
|
||||
export default connect(createMapStateToProps, createMapDispatchToProps)(DiscoverMovieConnector);
|
||||
|
|
|
|||
35
frontend/src/Helpers/Hooks/useScrollPosition.ts
Normal file
35
frontend/src/Helpers/Hooks/useScrollPosition.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router';
|
||||
import { OnScroll } from 'Components/Scroller/Scroller';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
|
||||
function useScrollPosition(key?: string) {
|
||||
const { pathname } = useLocation();
|
||||
const { action } = useHistory();
|
||||
|
||||
// Reset window scroll on PUSH/REPLACE (mobile's scroll container).
|
||||
// Skip POP so the browser (mobile) and memorized `initialScrollTop` (desktop inner) can restore.
|
||||
useEffect(() => {
|
||||
if (action !== 'POP') {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}, [pathname, action]);
|
||||
|
||||
const initialScrollTop = useMemo(
|
||||
() => (key && action === 'POP' ? scrollPositions[key] ?? 0 : 0),
|
||||
[key, action]
|
||||
);
|
||||
|
||||
const onScroll = useCallback(
|
||||
({ scrollTop }: OnScroll) => {
|
||||
if (key) {
|
||||
scrollPositions[key] = scrollTop;
|
||||
}
|
||||
},
|
||||
[key]
|
||||
);
|
||||
|
||||
return { initialScrollTop, onScroll };
|
||||
}
|
||||
|
||||
export default useScrollPosition;
|
||||
|
|
@ -21,7 +21,6 @@ import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
|||
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import withScrollPosition from 'Components/withScrollPosition';
|
||||
import { align, icons, kinds } from 'Helpers/Props';
|
||||
import { DESCENDING } from 'Helpers/Props/sortDirections';
|
||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||
|
|
@ -35,7 +34,6 @@ import {
|
|||
setMovieView,
|
||||
} from 'Store/Actions/movieIndexActions';
|
||||
import { fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import scrollPositions from 'Store/scrollPositions';
|
||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||
import createMovieClientSideCollectionItemsSelector from 'Store/Selectors/createMovieClientSideCollectionItemsSelector';
|
||||
|
|
@ -72,11 +70,7 @@ function getViewComponent(view: string) {
|
|||
return MovieIndexTable;
|
||||
}
|
||||
|
||||
interface MovieIndexProps {
|
||||
initialScrollTop?: number;
|
||||
}
|
||||
|
||||
const MovieIndex = withScrollPosition((props: MovieIndexProps) => {
|
||||
function MovieIndex() {
|
||||
const history = useHistory();
|
||||
|
||||
const {
|
||||
|
|
@ -186,13 +180,9 @@ const MovieIndex = withScrollPosition((props: MovieIndexProps) => {
|
|||
[setJumpToCharacter]
|
||||
);
|
||||
|
||||
const onScroll = useCallback(
|
||||
({ scrollTop }: { scrollTop: number }) => {
|
||||
setJumpToCharacter(undefined);
|
||||
scrollPositions.movieIndex = scrollTop;
|
||||
},
|
||||
[setJumpToCharacter]
|
||||
);
|
||||
const onScroll = useCallback(() => {
|
||||
setJumpToCharacter(undefined);
|
||||
}, [setJumpToCharacter]);
|
||||
|
||||
const jumpBarItems: PageJumpBarItems = useMemo(() => {
|
||||
// Reset if not sorting by sortTitle
|
||||
|
|
@ -345,7 +335,7 @@ const MovieIndex = withScrollPosition((props: MovieIndexProps) => {
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
innerClassName={styles[`${view}InnerContentBody`]}
|
||||
initialScrollTop={props.initialScrollTop}
|
||||
scrollPositionKey="movieIndex"
|
||||
onScroll={onScroll}
|
||||
>
|
||||
{isFetching && !isPopulated ? <LoadingIndicator /> : null}
|
||||
|
|
@ -407,6 +397,6 @@ const MovieIndex = withScrollPosition((props: MovieIndexProps) => {
|
|||
</PageContent>
|
||||
</SelectProvider>
|
||||
);
|
||||
}, 'movieIndex');
|
||||
}
|
||||
|
||||
export default MovieIndex;
|
||||
|
|
|
|||
Loading…
Reference in a new issue