mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-27 20:44:00 +02:00
New: Many UI Updates and Performance Tweaks
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
composes: input from 'Components/Form/TextInput.css';
|
||||
composes: input from '~Components/Form/TextInput.css';
|
||||
|
||||
height: 46px;
|
||||
border-radius: 0;
|
||||
|
@@ -99,7 +99,7 @@ class AddNewMovie extends Component {
|
||||
className={styles.searchInput}
|
||||
name="movieLookup"
|
||||
value={term}
|
||||
placeholder="eg. Some Movie, tmdb:####, imdb:####"
|
||||
placeholder="eg. The Dark Knight, tmdb:155, imdb:tt0468569"
|
||||
onChange={this.onSearchInputChange}
|
||||
/>
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import AddNewMovie from './AddNewMovie';
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.addMovie,
|
||||
(state) => state.routing.location,
|
||||
(state) => state.router.location,
|
||||
(addMovie, location) => {
|
||||
const { params } = parseUrl(location.search);
|
||||
|
||||
|
@@ -36,24 +36,24 @@
|
||||
}
|
||||
|
||||
.searchForMissingEpisodesContainer {
|
||||
composes: container from 'Components/Form/CheckInput.css';
|
||||
composes: container from '~Components/Form/CheckInput.css';
|
||||
|
||||
flex: 0 1 0;
|
||||
}
|
||||
|
||||
.searchForMissingEpisodesInput {
|
||||
composes: input from 'Components/Form/CheckInput.css';
|
||||
composes: input from '~Components/Form/CheckInput.css';
|
||||
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.modalFooter {
|
||||
composes: modalFooter from 'Components/Modal/ModalFooter.css';
|
||||
composes: modalFooter from '~Components/Modal/ModalFooter.css';
|
||||
}
|
||||
|
||||
.addButton {
|
||||
@add-mixin truncate;
|
||||
composes: button from 'Components/Link/SpinnerButton.css';
|
||||
composes: button from '~Components/Link/SpinnerButton.css';
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
|
@@ -97,7 +97,7 @@ class ImportMovie extends Component {
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<PageContent title="Import Series">
|
||||
<PageContent title="Import Movies">
|
||||
<PageContentBodyConnector
|
||||
ref={this.setContentBodyRef}
|
||||
onScroll={this.onScroll}
|
||||
@@ -115,7 +115,7 @@ class ImportMovie extends Component {
|
||||
{
|
||||
!rootFoldersError && rootFoldersPopulated && !unmappedFolders.length &&
|
||||
<div>
|
||||
All series in {path} have been imported
|
||||
All movies in {path} have been imported
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.importButton {
|
||||
composes: button from 'Components/Link/SpinnerButton.css';
|
||||
composes: button from '~Components/Link/SpinnerButton.css';
|
||||
|
||||
height: 35px;
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
.loading {
|
||||
composes: loading from 'Components/Loading/LoadingIndicator.css';
|
||||
composes: loading from '~Components/Loading/LoadingIndicator.css';
|
||||
|
||||
margin: 0 10px 0 12px;
|
||||
text-align: left;
|
||||
|
@@ -1,25 +1,25 @@
|
||||
.folder {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 1 0 200px;
|
||||
}
|
||||
|
||||
.monitor {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 1 200px;
|
||||
min-width: 185px;
|
||||
}
|
||||
|
||||
.qualityProfile {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 1 250px;
|
||||
min-width: 170px;
|
||||
}
|
||||
|
||||
.movie {
|
||||
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
|
||||
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
|
||||
|
||||
flex: 0 1 400px;
|
||||
min-width: 300px;
|
||||
|
@@ -1,30 +1,30 @@
|
||||
.selectInput {
|
||||
composes: input from 'Components/Form/CheckInput.css';
|
||||
composes: input from '~Components/Form/CheckInput.css';
|
||||
}
|
||||
|
||||
.folder {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 1 0 200px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.monitor {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 1 200px;
|
||||
min-width: 185px;
|
||||
}
|
||||
|
||||
.qualityProfile {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 1 250px;
|
||||
min-width: 170px;
|
||||
}
|
||||
|
||||
.series {
|
||||
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
|
||||
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
|
||||
|
||||
flex: 0 1 400px;
|
||||
min-width: 300px;
|
||||
|
@@ -1,3 +1,3 @@
|
||||
.input {
|
||||
composes: input from 'Components/Form/CheckInput.css';
|
||||
composes: input from '~Components/Form/CheckInput.css';
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
}
|
||||
|
||||
.button {
|
||||
composes: link from 'Components/Link/Link.css';
|
||||
composes: link from '~Components/Link/Link.css';
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
composes: input from 'Components/Form/TextInput.css';
|
||||
composes: input from '~Components/Form/TextInput.css';
|
||||
|
||||
border-radius: 0;
|
||||
}
|
||||
|
@@ -34,6 +34,8 @@ class ImportMovieSelectMovie extends Component {
|
||||
super(props, context);
|
||||
|
||||
this._movieLookupTimeout = null;
|
||||
this._buttonRef = {};
|
||||
this._contentRef = {};
|
||||
|
||||
this.state = {
|
||||
term: props.id,
|
||||
@@ -44,14 +46,6 @@ class ImportMovieSelectMovie extends Component {
|
||||
//
|
||||
// Control
|
||||
|
||||
_setButtonRef = (ref) => {
|
||||
this._buttonRef = ref;
|
||||
}
|
||||
|
||||
_setContentRef = (ref) => {
|
||||
this._contentRef = ref;
|
||||
}
|
||||
|
||||
_addListener() {
|
||||
window.addEventListener('click', this.onWindowClick);
|
||||
}
|
||||
@@ -64,14 +58,18 @@ class ImportMovieSelectMovie extends Component {
|
||||
// Listeners
|
||||
|
||||
onWindowClick = (event) => {
|
||||
const button = ReactDOM.findDOMNode(this._buttonRef);
|
||||
const content = ReactDOM.findDOMNode(this._contentRef);
|
||||
const button = ReactDOM.findDOMNode(this._buttonRef.current);
|
||||
const content = ReactDOM.findDOMNode(this._contentRef.current);
|
||||
|
||||
if (!button) {
|
||||
if (!button || !content) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!button.contains(event.target) && content && !content.contains(event.target) && this.state.isOpen) {
|
||||
if (
|
||||
!button.contains(event.target) &&
|
||||
!content.contains(event.target) &&
|
||||
this.state.isOpen
|
||||
) {
|
||||
this.setState({ isOpen: false });
|
||||
this._removeListener();
|
||||
}
|
||||
@@ -120,7 +118,7 @@ class ImportMovieSelectMovie extends Component {
|
||||
isPopulated,
|
||||
error,
|
||||
items,
|
||||
queued,
|
||||
isQueued,
|
||||
isLookingUpMovie
|
||||
} = this.props;
|
||||
|
||||
@@ -134,124 +132,145 @@ class ImportMovieSelectMovie extends Component {
|
||||
element: styles.tether
|
||||
}}
|
||||
{...tetherOptions}
|
||||
>
|
||||
<Link
|
||||
ref={this._setButtonRef}
|
||||
className={styles.button}
|
||||
component="div"
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{
|
||||
isLookingUpMovie && queued && !isPopulated &&
|
||||
<LoadingIndicator
|
||||
className={styles.loading}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
renderTarget={
|
||||
(ref) => {
|
||||
this._buttonRef = ref;
|
||||
|
||||
{
|
||||
isPopulated && selectedMovie && isExistingMovie &&
|
||||
<Icon
|
||||
className={styles.warningIcon}
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
/>
|
||||
}
|
||||
return (
|
||||
<div ref={ref}>
|
||||
<Link
|
||||
className={styles.button}
|
||||
component="div"
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{
|
||||
isLookingUpMovie && isQueued && !isPopulated ?
|
||||
<LoadingIndicator
|
||||
className={styles.loading}
|
||||
size={20}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && selectedMovie &&
|
||||
<ImportMovieTitle
|
||||
title={selectedMovie.title}
|
||||
year={selectedMovie.year}
|
||||
network={selectedMovie.network}
|
||||
isExistingMovie={isExistingMovie}
|
||||
/>
|
||||
}
|
||||
{
|
||||
isPopulated && selectedMovie && isExistingMovie ?
|
||||
<Icon
|
||||
className={styles.warningIcon}
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !selectedMovie &&
|
||||
<div>
|
||||
<Icon
|
||||
className={styles.warningIcon}
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
/>
|
||||
{
|
||||
isPopulated && selectedMovie ?
|
||||
<ImportMovieTitle
|
||||
title={selectedMovie.title}
|
||||
year={selectedMovie.year}
|
||||
network={selectedMovie.network}
|
||||
isExistingMovie={isExistingMovie}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
isPopulated && !selectedMovie ?
|
||||
<div>
|
||||
<Icon
|
||||
className={styles.warningIcon}
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
/>
|
||||
|
||||
No match found!
|
||||
</div>
|
||||
}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
<Icon
|
||||
className={styles.warningIcon}
|
||||
title={errorMessage}
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
/>
|
||||
{
|
||||
!isFetching && !!error ?
|
||||
<div>
|
||||
<Icon
|
||||
className={styles.warningIcon}
|
||||
title={errorMessage}
|
||||
name={icons.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
/>
|
||||
|
||||
Search failed, please try again later.
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
<div className={styles.dropdownArrowContainer}>
|
||||
<Icon
|
||||
name={icons.CARET_DOWN}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
renderElement={
|
||||
(ref) => {
|
||||
this._contentRef = ref;
|
||||
|
||||
<div className={styles.dropdownArrowContainer}>
|
||||
<Icon
|
||||
name={icons.CARET_DOWN}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
if (!this.state.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
this.state.isOpen &&
|
||||
<div
|
||||
ref={this._setContentRef}
|
||||
className={styles.contentContainer}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.searchContainer}>
|
||||
<div className={styles.searchIconContainer}>
|
||||
<Icon name={icons.SEARCH} />
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={styles.contentContainer}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.searchContainer}>
|
||||
<div className={styles.searchIconContainer}>
|
||||
<Icon name={icons.SEARCH} />
|
||||
</div>
|
||||
|
||||
<TextInput
|
||||
className={styles.searchInput}
|
||||
name={`${name}_textInput`}
|
||||
value={this.state.term}
|
||||
onChange={this.onSearchInputChange}
|
||||
/>
|
||||
|
||||
<FormInputButton
|
||||
kind={kinds.DEFAULT}
|
||||
spinnerIcon={icons.REFRESH}
|
||||
canSpin={true}
|
||||
isSpinning={isFetching}
|
||||
onPress={this.onRefreshPress}
|
||||
>
|
||||
<Icon name={icons.REFRESH} />
|
||||
</FormInputButton>
|
||||
</div>
|
||||
|
||||
<TextInput
|
||||
className={styles.searchInput}
|
||||
name={`${name}_textInput`}
|
||||
value={this.state.term}
|
||||
onChange={this.onSearchInputChange}
|
||||
/>
|
||||
|
||||
<FormInputButton
|
||||
kind={kinds.DEFAULT}
|
||||
spinnerIcon={icons.REFRESH}
|
||||
canSpin={true}
|
||||
isSpinning={isFetching}
|
||||
onPress={this.onRefreshPress}
|
||||
>
|
||||
<Icon name={icons.REFRESH} />
|
||||
</FormInputButton>
|
||||
</div>
|
||||
|
||||
<div className={styles.results}>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<ImportMovieSearchResultConnector
|
||||
key={item.tmdbId}
|
||||
tmdbId={item.tmdbId}
|
||||
title={item.title}
|
||||
year={item.year}
|
||||
studio={item.studio}
|
||||
onPress={this.onMovieSelect}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
<div className={styles.results}>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<ImportMovieSearchResultConnector
|
||||
key={item.tmdbId}
|
||||
tmdbId={item.tmdbId}
|
||||
title={item.title}
|
||||
year={item.year}
|
||||
studio={item.studio}
|
||||
onPress={this.onMovieSelect}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</TetherComponent>
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -264,7 +283,7 @@ ImportMovieSelectMovie.propTypes = {
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
queued: PropTypes.bool.isRequired,
|
||||
isQueued: PropTypes.bool.isRequired,
|
||||
isLookingUpMovie: PropTypes.bool.isRequired,
|
||||
onSearchInputChange: PropTypes.func.isRequired,
|
||||
onMovieSelect: PropTypes.func.isRequired
|
||||
@@ -274,7 +293,7 @@ ImportMovieSelectMovie.defaultProps = {
|
||||
isFetching: true,
|
||||
isPopulated: false,
|
||||
items: [],
|
||||
queued: true
|
||||
isQueued: true
|
||||
};
|
||||
|
||||
export default ImportMovieSelectMovie;
|
||||
|
@@ -1,18 +1,18 @@
|
||||
.link {
|
||||
composes: link from 'Components/Link/Link.css';
|
||||
composes: link from '~Components/Link/Link.css';
|
||||
|
||||
display: block;
|
||||
}
|
||||
|
||||
.freeSpace,
|
||||
.unmappedFolders {
|
||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 45px;
|
||||
}
|
||||
|
@@ -76,7 +76,7 @@ class ImportMovieSelectFolder extends Component {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<PageContent title="Import Series">
|
||||
<PageContent title="Import Movies">
|
||||
<PageContentBodyConnector>
|
||||
{
|
||||
isFetching && !isPopulated &&
|
||||
@@ -99,7 +99,7 @@ class ImportMovieSelectFolder extends Component {
|
||||
Some tips to ensure the import goes smoothly:
|
||||
<ul>
|
||||
<li className={styles.tip}>
|
||||
Make sure your files include the quality in the name. eg. <span className={styles.code}>episode.s02e15.bluray.mkv</span>
|
||||
Make sure your files include the quality in the name. eg. <span className={styles.code}>movie.2008.bluray.mkv</span>
|
||||
</li>
|
||||
<li className={styles.tip}>
|
||||
Point Radarr to the folder containing all of your movies not a specific one. eg. <span className={styles.code}>"{isWindows ? 'C:\\movies' : '/movies'}"</span> and not <span className={styles.code}>"{isWindows ? 'C:\\movies\\the matrix' : '/movies/the matrix'}"</span>
|
||||
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { push } from 'react-router-redux';
|
||||
import { push } from 'connected-react-router';
|
||||
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
|
||||
import { fetchRootFolders, addRootFolder, deleteRootFolder } from 'Store/Actions/rootFolderActions';
|
||||
import ImportMovieSelectFolder from './ImportMovieSelectFolder';
|
||||
|
Reference in New Issue
Block a user