1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-09-17 15:02:34 +02: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) { @media only screen and (max-width: $breakpointLarge) {
.poster { .poster,
.movieNavigationButtons {
display: none; 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 // Listeners
@ -156,6 +170,58 @@ class MovieDetails extends Component {
this.setState({ titleWidth: width }); 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 // Render
@ -207,6 +273,8 @@ class MovieDetails extends Component {
selectedTabIndex selectedTabIndex
} = this.state; } = this.state;
const marqueeWidth = isSmallScreen ? titleWidth : (titleWidth - 150);
return ( return (
<PageContent title={title}> <PageContent title={title}>
<PageToolbar> <PageToolbar>
@ -293,7 +361,7 @@ class MovieDetails extends Component {
/> />
</div> </div>
<div className={styles.title} style={{ width: (titleWidth - 150) }}> <div className={styles.title} style={{ width: marqueeWidth }}>
<Marquee text={title} /> <Marquee text={title} />
</div> </div>
</div> </div>
@ -665,6 +733,7 @@ MovieDetails.propTypes = {
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired, isPopulated: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
isSidebarVisible: PropTypes.bool.isRequired,
movieFilesError: PropTypes.object, movieFilesError: PropTypes.object,
movieCreditsError: PropTypes.object, movieCreditsError: PropTypes.object,
extraFilesError: PropTypes.object, extraFilesError: PropTypes.object,
@ -673,7 +742,8 @@ MovieDetails.propTypes = {
nextMovie: PropTypes.object.isRequired, nextMovie: PropTypes.object.isRequired,
onMonitorTogglePress: PropTypes.func.isRequired, onMonitorTogglePress: PropTypes.func.isRequired,
onRefreshPress: PropTypes.func.isRequired, onRefreshPress: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired onSearchPress: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired
}; };
MovieDetails.defaultProps = { MovieDetails.defaultProps = {

View File

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