mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
New: Add support for left/right arrows on the movie details to navigate through movies
New: Add support for ctrl+home and ctrl+end to jump to the top and bottom of the movie index New: Add redirect to previous movie instead of index when deleting one
This commit is contained in:
parent
870a39278c
commit
7f814a3cb9
@ -20,18 +20,26 @@ function getShortcuts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getShortcutKey(combo, isOsx) {
|
function getShortcutKey(combo, isOsx) {
|
||||||
const comboMatch = combo.match(/(.+?)\+(.)/);
|
const comboMatch = combo.match(/(.+?)\+(.*)/);
|
||||||
|
|
||||||
if (!comboMatch) {
|
if (!comboMatch) {
|
||||||
return combo;
|
return combo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifier = comboMatch[1];
|
const modifier = comboMatch[1];
|
||||||
const key = comboMatch[2];
|
let key = comboMatch[2];
|
||||||
let osModifier = modifier;
|
let osModifier = modifier;
|
||||||
|
|
||||||
if (modifier === 'mod') {
|
if (modifier === 'mod') {
|
||||||
osModifier = isOsx ? 'cmd' : 'ctrl';
|
osModifier = isOsx ? 'cmd' : 'Ctrl';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'home') {
|
||||||
|
key = isOsx ? '↑' : 'Home';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'end') {
|
||||||
|
key = isOsx ? '↓' : 'End';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${osModifier} + ${key}`;
|
return `${osModifier} + ${key}`;
|
||||||
|
@ -1,31 +1,52 @@
|
|||||||
import Mousetrap from 'mousetrap';
|
import Mousetrap from 'mousetrap';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import getDisplayName from 'Helpers/getDisplayName';
|
import getDisplayName from 'Helpers/getDisplayName';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
export const shortcuts = {
|
export const shortcuts = {
|
||||||
OPEN_KEYBOARD_SHORTCUTS_MODAL: {
|
OPEN_KEYBOARD_SHORTCUTS_MODAL: {
|
||||||
key: '?',
|
key: '?',
|
||||||
name: 'Open This Modal'
|
name: translate('OpenThisModal')
|
||||||
},
|
},
|
||||||
|
|
||||||
CLOSE_MODAL: {
|
CLOSE_MODAL: {
|
||||||
key: 'Esc',
|
key: 'Esc',
|
||||||
name: 'Close Current Modal'
|
name: translate('CloseCurrentModal')
|
||||||
},
|
},
|
||||||
|
|
||||||
ACCEPT_CONFIRM_MODAL: {
|
ACCEPT_CONFIRM_MODAL: {
|
||||||
key: 'Enter',
|
key: 'Enter',
|
||||||
name: 'Accept Confirmation Modal'
|
name: translate('AcceptConfirmationModal')
|
||||||
},
|
},
|
||||||
|
|
||||||
MOVIE_SEARCH_INPUT: {
|
MOVIE_SEARCH_INPUT: {
|
||||||
key: 's',
|
key: 's',
|
||||||
name: 'Focus Search Box'
|
name: translate('FocusSearchBox')
|
||||||
},
|
},
|
||||||
|
|
||||||
SAVE_SETTINGS: {
|
SAVE_SETTINGS: {
|
||||||
key: 'mod+s',
|
key: 'mod+s',
|
||||||
name: 'Save Settings'
|
name: translate('SaveSettings')
|
||||||
|
},
|
||||||
|
|
||||||
|
SCROLL_TOP: {
|
||||||
|
key: 'mod+home',
|
||||||
|
name: translate('MovieIndexScrollTop')
|
||||||
|
},
|
||||||
|
|
||||||
|
SCROLL_BOTTOM: {
|
||||||
|
key: 'mod+end',
|
||||||
|
name: translate('MovieIndexScrollBottom')
|
||||||
|
},
|
||||||
|
|
||||||
|
DETAILS_NEXT: {
|
||||||
|
key: '→',
|
||||||
|
name: translate('MovieDetailsNextMovie')
|
||||||
|
},
|
||||||
|
|
||||||
|
DETAILS_PREVIOUS: {
|
||||||
|
key: '←',
|
||||||
|
name: translate('MovieDetailsPreviousMovie')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ function DeleteMovieModal(props) {
|
|||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
onModalClose,
|
onModalClose,
|
||||||
|
previousMovie,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ function DeleteMovieModal(props) {
|
|||||||
<DeleteMovieModalContentConnector
|
<DeleteMovieModalContentConnector
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
|
previousMovie={previousMovie}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
@ -27,7 +29,8 @@ function DeleteMovieModal(props) {
|
|||||||
|
|
||||||
DeleteMovieModal.propTypes = {
|
DeleteMovieModal.propTypes = {
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired,
|
||||||
|
previousMovie: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeleteMovieModal;
|
export default DeleteMovieModal;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { push } from 'connected-react-router';
|
||||||
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';
|
||||||
@ -16,7 +17,8 @@ function createMapStateToProps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
deleteMovie
|
deleteMovie,
|
||||||
|
push
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeleteMovieModalContentConnector extends Component {
|
class DeleteMovieModalContentConnector extends Component {
|
||||||
@ -32,6 +34,10 @@ class DeleteMovieModalContentConnector extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.props.onModalClose(true);
|
this.props.onModalClose(true);
|
||||||
|
|
||||||
|
if (this.props.previousMovie) {
|
||||||
|
this.props.push(this.props.previousMovie);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -50,7 +56,9 @@ class DeleteMovieModalContentConnector extends Component {
|
|||||||
DeleteMovieModalContentConnector.propTypes = {
|
DeleteMovieModalContentConnector.propTypes = {
|
||||||
movieId: PropTypes.number.isRequired,
|
movieId: PropTypes.number.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired,
|
onModalClose: PropTypes.func.isRequired,
|
||||||
deleteMovie: PropTypes.func.isRequired
|
deleteMovie: PropTypes.func.isRequired,
|
||||||
|
push: PropTypes.func.isRequired,
|
||||||
|
previousMovie: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(DeleteMovieModalContentConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(DeleteMovieModalContentConnector);
|
||||||
|
@ -30,6 +30,7 @@ import ExtraFileTable from 'MovieFile/Extras/ExtraFileTable';
|
|||||||
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
|
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
|
||||||
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
||||||
import fonts from 'Styles/Variables/fonts';
|
import fonts from 'Styles/Variables/fonts';
|
||||||
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import formatRuntime from 'Utilities/Date/formatRuntime';
|
import formatRuntime from 'Utilities/Date/formatRuntime';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@ -91,6 +92,7 @@ class MovieDetails extends Component {
|
|||||||
window.addEventListener('touchend', this.onTouchEnd);
|
window.addEventListener('touchend', this.onTouchEnd);
|
||||||
window.addEventListener('touchcancel', this.onTouchCancel);
|
window.addEventListener('touchcancel', this.onTouchCancel);
|
||||||
window.addEventListener('touchmove', this.onTouchMove);
|
window.addEventListener('touchmove', this.onTouchMove);
|
||||||
|
window.addEventListener('keyup', this.onKeyUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -173,6 +175,15 @@ class MovieDetails extends Component {
|
|||||||
this.setState({ titleWidth: width });
|
this.setState({ titleWidth: width });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onKeyUp = (event) => {
|
||||||
|
if (event.keyCode === keyCodes.LEFT_ARROW) {
|
||||||
|
this.props.onGoToMovie(this.props.previousMovie.titleSlug);
|
||||||
|
}
|
||||||
|
if (event.keyCode === keyCodes.RIGHT_ARROW) {
|
||||||
|
this.props.onGoToMovie(this.props.nextMovie.titleSlug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onTouchStart = (event) => {
|
onTouchStart = (event) => {
|
||||||
const touches = event.touches;
|
const touches = event.touches;
|
||||||
const touchStart = touches[0].pageX;
|
const touchStart = touches[0].pageX;
|
||||||
@ -716,6 +727,7 @@ class MovieDetails extends Component {
|
|||||||
isOpen={isDeleteMovieModalOpen}
|
isOpen={isDeleteMovieModalOpen}
|
||||||
movieId={id}
|
movieId={id}
|
||||||
onModalClose={this.onDeleteMovieModalClose}
|
onModalClose={this.onDeleteMovieModalClose}
|
||||||
|
previousMovie={`/movie/${previousMovie.titleSlug}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InteractiveImportModal
|
<InteractiveImportModal
|
||||||
|
@ -16,6 +16,7 @@ import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
|||||||
import MovieEditorFooter from 'Movie/Editor/MovieEditorFooter.js';
|
import MovieEditorFooter from 'Movie/Editor/MovieEditorFooter.js';
|
||||||
import OrganizeMovieModal from 'Movie/Editor/Organize/OrganizeMovieModal';
|
import OrganizeMovieModal from 'Movie/Editor/Organize/OrganizeMovieModal';
|
||||||
import NoMovie from 'Movie/NoMovie';
|
import NoMovie from 'Movie/NoMovie';
|
||||||
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@ -75,6 +76,8 @@ class MovieIndex extends Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.setJumpBarItems();
|
this.setJumpBarItems();
|
||||||
this.setSelectedState();
|
this.setSelectedState();
|
||||||
|
|
||||||
|
window.addEventListener('keyup', this.onKeyUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@ -241,6 +244,18 @@ class MovieIndex extends Component {
|
|||||||
this.setState({ jumpToCharacter });
|
this.setState({ jumpToCharacter });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onKeyUp = (event) => {
|
||||||
|
const jumpBarItems = this.state.jumpBarItems.order;
|
||||||
|
if (event.path.length === 4) {
|
||||||
|
if (event.keyCode === keyCodes.HOME && event.ctrlKey) {
|
||||||
|
this.setState({ jumpToCharacter: jumpBarItems[0] });
|
||||||
|
}
|
||||||
|
if (event.keyCode === keyCodes.END && event.ctrlKey) {
|
||||||
|
this.setState({ jumpToCharacter: jumpBarItems[jumpBarItems.length - 1] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onSelectAllChange = ({ value }) => {
|
onSelectAllChange = ({ value }) => {
|
||||||
this.setState(selectAll(this.state.selectedState, value));
|
this.setState(selectAll(this.state.selectedState, value));
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,9 @@ export const ENTER = 13;
|
|||||||
export const SHIFT = 16;
|
export const SHIFT = 16;
|
||||||
export const CONTROL = 17;
|
export const CONTROL = 17;
|
||||||
export const ESCAPE = 27;
|
export const ESCAPE = 27;
|
||||||
|
export const HOME = 36;
|
||||||
|
export const END = 35;
|
||||||
export const UP_ARROW = 38;
|
export const UP_ARROW = 38;
|
||||||
export const DOWN_ARROW = 40;
|
export const DOWN_ARROW = 40;
|
||||||
|
export const LEFT_ARROW = 37;
|
||||||
|
export const RIGHT_ARROW = 39;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"About": "About",
|
"About": "About",
|
||||||
|
"AcceptConfirmationModal": "Accept Confirmation Modal",
|
||||||
"Actions": "Actions",
|
"Actions": "Actions",
|
||||||
"Activity": "Activity",
|
"Activity": "Activity",
|
||||||
"Added": "Added",
|
"Added": "Added",
|
||||||
@ -96,6 +97,7 @@
|
|||||||
"CloneIndexer": "Clone Indexer",
|
"CloneIndexer": "Clone Indexer",
|
||||||
"CloneProfile": "Clone Profile",
|
"CloneProfile": "Clone Profile",
|
||||||
"Close": "Close",
|
"Close": "Close",
|
||||||
|
"CloseCurrentModal": "Close Current Modal",
|
||||||
"Collection": "Collection",
|
"Collection": "Collection",
|
||||||
"ColonReplacement": "Colon Replacement",
|
"ColonReplacement": "Colon Replacement",
|
||||||
"ColonReplacementFormatHelpText": "Change how Radarr handles colon replacement",
|
"ColonReplacementFormatHelpText": "Change how Radarr handles colon replacement",
|
||||||
@ -251,6 +253,7 @@
|
|||||||
"FilterPlaceHolder": "Search movies",
|
"FilterPlaceHolder": "Search movies",
|
||||||
"FirstDayOfWeek": "First Day of Week",
|
"FirstDayOfWeek": "First Day of Week",
|
||||||
"Fixed": "Fixed",
|
"Fixed": "Fixed",
|
||||||
|
"FocusSearchBox": "Focus Search Box",
|
||||||
"Folder": "Folder",
|
"Folder": "Folder",
|
||||||
"Folders": "Folders",
|
"Folders": "Folders",
|
||||||
"FollowPerson": "Follow Person",
|
"FollowPerson": "Follow Person",
|
||||||
@ -436,6 +439,10 @@
|
|||||||
"MovieNaming": "Movie Naming",
|
"MovieNaming": "Movie Naming",
|
||||||
"Movies": "Movies",
|
"Movies": "Movies",
|
||||||
"MoviesSelectedInterp": "{0} Movie(s) Selected",
|
"MoviesSelectedInterp": "{0} Movie(s) Selected",
|
||||||
|
"MovieDetailsNextMovie": "Movie Details: Next Movie",
|
||||||
|
"MovieDetailsPreviousMovie": "Movie Details: Previous Movie",
|
||||||
|
"MovieIndexScrollBottom": "Movie Index: Scroll Bottom",
|
||||||
|
"MovieIndexScrollTop": "Movie Index: Scroll Top",
|
||||||
"MovieTitle": "Movie Title",
|
"MovieTitle": "Movie Title",
|
||||||
"MovieTitleHelpText": "The title of the movie to exclude (can be anything meaningful)",
|
"MovieTitleHelpText": "The title of the movie to exclude (can be anything meaningful)",
|
||||||
"MovieYear": "Movie Year",
|
"MovieYear": "Movie Year",
|
||||||
@ -469,6 +476,7 @@
|
|||||||
"OnRenameHelpText": "On Rename",
|
"OnRenameHelpText": "On Rename",
|
||||||
"OnUpgradeHelpText": "Be notified when movies are upgraded to a better quality",
|
"OnUpgradeHelpText": "Be notified when movies are upgraded to a better quality",
|
||||||
"OpenBrowserOnStart": "Open browser on start",
|
"OpenBrowserOnStart": "Open browser on start",
|
||||||
|
"OpenThisModal": "Open This Modal",
|
||||||
"Options": "Options",
|
"Options": "Options",
|
||||||
"Organize": "Organize",
|
"Organize": "Organize",
|
||||||
"OrganizeAndRename": "Organize & Rename",
|
"OrganizeAndRename": "Organize & Rename",
|
||||||
@ -615,6 +623,7 @@
|
|||||||
"Runtime": "Runtime",
|
"Runtime": "Runtime",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"SaveChanges": "Save Changes",
|
"SaveChanges": "Save Changes",
|
||||||
|
"SaveSettings": "Save Settings",
|
||||||
"Scheduled": "Scheduled",
|
"Scheduled": "Scheduled",
|
||||||
"ScriptPath": "Script Path",
|
"ScriptPath": "Script Path",
|
||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
|
Loading…
Reference in New Issue
Block a user