1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

New: Swipe on Movie Details Page

This commit is contained in:
Qstick 2020-07-04 03:13:24 -04:00
parent a08648272c
commit f37c7a9748
3 changed files with 154 additions and 48 deletions

View File

@ -202,7 +202,8 @@
}
@media only screen and (max-width: $breakpointLarge) {
.poster {
.poster,
.movieNavigationButtons {
display: none;
}
}

View File

@ -83,6 +83,20 @@ class MovieDetails extends Component {
};
}
componentDidMount() {
window.addEventListener('touchstart', this.onTouchStart);
window.addEventListener('touchend', this.onTouchEnd);
window.addEventListener('touchcancel', this.onTouchCancel);
window.addEventListener('touchmove', this.onTouchMove);
}
componentWillUnmount() {
window.removeEventListener('touchstart', this.onTouchStart);
window.removeEventListener('touchend', this.onTouchEnd);
window.removeEventListener('touchcancel', this.onTouchCancel);
window.removeEventListener('touchmove', this.onTouchMove);
}
//
// Listeners
@ -156,6 +170,58 @@ class MovieDetails extends Component {
this.setState({ titleWidth: width });
}
onTouchStart = (event) => {
const touches = event.touches;
const touchStart = touches[0].pageX;
const touchY = touches[0].pageY;
// Only change when swipe is on header, we need horizontal scroll on tables
if (touchY > 470) {
return;
}
if (touches.length !== 1) {
return;
}
if (
touchStart < 50 ||
this.props.isSidebarVisible ||
this.state.isEventModalOpen
) {
return;
}
this._touchStart = touchStart;
}
onTouchEnd = (event) => {
const touches = event.changedTouches;
const currentTouch = touches[0].pageX;
if (!this._touchStart) {
return;
}
if (currentTouch > this._touchStart && currentTouch - this._touchStart > 100) {
this.props.onGoToMovie(this.props.previousMovie.titleSlug);
} else if (currentTouch < this._touchStart && this._touchStart - currentTouch > 100) {
this.props.onGoToMovie(this.props.nextMovie.titleSlug);
}
this._touchStart = null;
}
onTouchCancel = (event) => {
this._touchStart = null;
}
onTouchMove = (event) => {
if (!this._touchStart) {
return;
}
}
//
// Render
@ -207,6 +273,8 @@ class MovieDetails extends Component {
selectedTabIndex
} = this.state;
const marqueeWidth = isSmallScreen ? titleWidth : (titleWidth - 150);
return (
<PageContent title={title}>
<PageToolbar>
@ -293,7 +361,7 @@ class MovieDetails extends Component {
/>
</div>
<div className={styles.title} style={{ width: (titleWidth - 150) }}>
<div className={styles.title} style={{ width: marqueeWidth }}>
<Marquee text={title} />
</div>
</div>
@ -665,6 +733,7 @@ MovieDetails.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
isSidebarVisible: PropTypes.bool.isRequired,
movieFilesError: PropTypes.object,
movieCreditsError: PropTypes.object,
extraFilesError: PropTypes.object,
@ -673,7 +742,8 @@ MovieDetails.propTypes = {
nextMovie: PropTypes.object.isRequired,
onMonitorTogglePress: PropTypes.func.isRequired,
onRefreshPress: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired
onSearchPress: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired
};
MovieDetails.defaultProps = {

View File

@ -2,6 +2,7 @@ import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { push } from 'connected-react-router';
import { createSelector } from 'reselect';
import { findCommand, isCommandExecuting } from 'Utilities/Command';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
@ -86,7 +87,8 @@ function createMapStateToProps() {
createAllMoviesSelector(),
createCommandsSelector(),
createDimensionsSelector(),
(titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions) => {
(state) => state.app.isSidebarVisible,
(titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions, isSidebarVisible) => {
const sortedMovies = _.orderBy(allMovies, 'sortTitle');
const movieIndex = _.findIndex(sortedMovies, { titleSlug });
const movie = sortedMovies[movieIndex];
@ -157,27 +159,59 @@ function createMapStateToProps() {
sizeOnDisk,
previousMovie,
nextMovie,
isSmallScreen: dimensions.isSmallScreen
isSmallScreen: dimensions.isSmallScreen,
isSidebarVisible
};
}
);
}
const mapDispatchToProps = {
fetchMovieFiles,
clearMovieFiles,
fetchMovieCredits,
clearMovieCredits,
fetchExtraFiles,
clearExtraFiles,
clearReleases,
cancelFetchReleases,
fetchNetImportSchema,
toggleMovieMonitored,
fetchQueueDetails,
clearQueueDetails,
executeCommand
};
function createMapDispatchToProps(dispatch, props) {
return {
dispatchFetchMovieFiles({ movieId }) {
dispatch(fetchMovieFiles({ movieId }));
},
dispatchClearMovieFiles() {
dispatch(clearMovieFiles());
},
dispatchFetchMovieCredits({ movieId }) {
dispatch(fetchMovieCredits({ movieId }));
},
dispatchClearMovieCredits() {
dispatch(clearMovieCredits());
},
dispatchFetchExtraFiles({ movieId }) {
dispatch(fetchExtraFiles({ movieId }));
},
dispatchClearExtraFiles() {
dispatch(clearExtraFiles());
},
dispatchClearReleases() {
dispatch(clearReleases());
},
dispatchCancelFetchReleases() {
dispatch(cancelFetchReleases());
},
dispatchFetchQueueDetails({ movieId }) {
dispatch(fetchQueueDetails({ movieId }));
},
dispatchClearQueueDetails() {
dispatch(clearQueueDetails());
},
dispatchFetchNetImportSchema() {
dispatch(fetchNetImportSchema());
},
dispatchToggleMovieMonitored(payload) {
dispatch(toggleMovieMonitored(payload));
},
dispatchExecuteCommand(payload) {
dispatch(executeCommand(payload));
},
onGoToMovie(titleSlug) {
dispatch(push(`${window.Radarr.urlBase}/movie/${titleSlug}`));
}
};
}
class MovieDetailsConnector extends Component {
@ -227,41 +261,41 @@ class MovieDetailsConnector extends Component {
populate = () => {
const movieId = this.props.id;
this.props.fetchMovieFiles({ movieId });
this.props.fetchExtraFiles({ movieId });
this.props.fetchMovieCredits({ movieId });
this.props.fetchQueueDetails({ movieId });
this.props.fetchNetImportSchema();
this.props.dispatchFetchMovieFiles({ movieId });
this.props.dispatchFetchExtraFiles({ movieId });
this.props.dispatchFetchMovieCredits({ movieId });
this.props.dispatchFetchQueueDetails({ movieId });
this.props.dispatchFetchNetImportSchema();
}
unpopulate = () => {
this.props.cancelFetchReleases();
this.props.clearMovieFiles();
this.props.clearExtraFiles();
this.props.clearMovieCredits();
this.props.clearQueueDetails();
this.props.clearReleases();
this.props.dispatchCancelFetchReleases();
this.props.dispatchClearMovieFiles();
this.props.dispatchClearExtraFiles();
this.props.dispatchClearMovieCredits();
this.props.dispatchClearQueueDetails();
this.props.dispatchClearReleases();
}
//
// Listeners
onMonitorTogglePress = (monitored) => {
this.props.toggleMovieMonitored({
this.props.dispatchToggleMovieMonitored({
movieId: this.props.id,
monitored
});
}
onRefreshPress = () => {
this.props.executeCommand({
this.props.dispatchExecuteCommand({
name: commandNames.REFRESH_MOVIE,
movieId: this.props.id
});
}
onSearchPress = () => {
this.props.executeCommand({
this.props.dispatchExecuteCommand({
name: commandNames.MOVIE_SEARCH,
movieIds: [this.props.id]
});
@ -291,19 +325,20 @@ MovieDetailsConnector.propTypes = {
isRenamingFiles: PropTypes.bool.isRequired,
isRenamingMovie: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
fetchMovieFiles: PropTypes.func.isRequired,
clearMovieFiles: PropTypes.func.isRequired,
fetchExtraFiles: PropTypes.func.isRequired,
clearExtraFiles: PropTypes.func.isRequired,
fetchMovieCredits: PropTypes.func.isRequired,
clearMovieCredits: PropTypes.func.isRequired,
clearReleases: PropTypes.func.isRequired,
cancelFetchReleases: PropTypes.func.isRequired,
toggleMovieMonitored: PropTypes.func.isRequired,
fetchQueueDetails: PropTypes.func.isRequired,
clearQueueDetails: PropTypes.func.isRequired,
fetchNetImportSchema: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired
dispatchFetchMovieFiles: PropTypes.func.isRequired,
dispatchClearMovieFiles: PropTypes.func.isRequired,
dispatchFetchExtraFiles: PropTypes.func.isRequired,
dispatchClearExtraFiles: PropTypes.func.isRequired,
dispatchFetchMovieCredits: PropTypes.func.isRequired,
dispatchClearMovieCredits: PropTypes.func.isRequired,
dispatchClearReleases: PropTypes.func.isRequired,
dispatchCancelFetchReleases: PropTypes.func.isRequired,
dispatchToggleMovieMonitored: PropTypes.func.isRequired,
dispatchFetchQueueDetails: PropTypes.func.isRequired,
dispatchClearQueueDetails: PropTypes.func.isRequired,
dispatchFetchNetImportSchema: PropTypes.func.isRequired,
dispatchExecuteCommand: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(MovieDetailsConnector);
export default connect(createMapStateToProps, createMapDispatchToProps)(MovieDetailsConnector);