UI Updates (Cancel Import, Move Artist, Manual Import from Artist)

Ability to cancel an import lookup/search at any point.
Ability to move artist path from Artist Edit or bulk move from Mass Editor.
Trigger manual import for Artist path from Artist Detail page.
Pulled from Sonarr
This commit is contained in:
Qstick 2017-12-29 22:23:04 -05:00
parent 5fae202760
commit d8c89f5bbd
79 changed files with 1075 additions and 376 deletions

View file

@ -19,6 +19,12 @@
height: 35px;
}
.loadingButton {
composes: importButton;
margin-left: 10px;
}
.loading {
composes: loading from 'Components/Loading/LoadingIndicator.css';

View file

@ -2,6 +2,7 @@ import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { inputTypes, kinds } from 'Helpers/Props';
import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import CheckInput from 'Components/Form/CheckInput';
@ -117,7 +118,8 @@ class ImportArtistFooter extends Component {
isMetadataProfileIdMixed,
showLanguageProfile,
showMetadataProfile,
onImportPress
onImportPress,
onCancelLookupPress
} = this.props;
const {
@ -227,10 +229,21 @@ class ImportArtistFooter extends Component {
{
isLookingUpArtist &&
<LoadingIndicator
className={styles.loading}
size={24}
/>
<Button
className={styles.loadingButton}
kind={kinds.WARNING}
onPress={onCancelLookupPress}
>
Cancel Processing
</Button>
}
{
isLookingUpArtist &&
<LoadingIndicator
className={styles.loading}
size={24}
/>
}
{
@ -261,7 +274,8 @@ ImportArtistFooter.propTypes = {
showLanguageProfile: PropTypes.bool.isRequired,
showMetadataProfile: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
onImportPress: PropTypes.func.isRequired
onImportPress: PropTypes.func.isRequired,
onCancelLookupPress: PropTypes.func.isRequired
};
export default ImportArtistFooter;

View file

@ -2,6 +2,7 @@ import _ from 'lodash';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import ImportArtistFooter from './ImportArtistFooter';
import { cancelLookupArtist } from 'Store/Actions/importArtistActions';
function isMixed(items, selectedIds, defaultValue, key) {
return _.some(items, (artist) => {
@ -23,11 +24,11 @@ function createMapStateToProps() {
albumFolder: defaultAlbumFolder
} = addArtist.defaults;
const items = importArtist.items;
const isLookingUpArtist = _.some(importArtist.items, (artist) => {
return !artist.isPopulated && artist.error == null;
});
const {
isLookingUpArtist,
isImporting,
items
} = importArtist;
const isMonitorMixed = isMixed(items, selectedIds, defaultMonitor, 'monitor');
const isQualityProfileIdMixed = isMixed(items, selectedIds, defaultQualityProfileId, 'qualityProfileId');
@ -37,8 +38,8 @@ function createMapStateToProps() {
return {
selectedCount: selectedIds.length,
isImporting: importArtist.isImporting,
isLookingUpArtist,
isImporting,
defaultMonitor,
defaultQualityProfileId,
defaultLanguageProfileId,
@ -54,4 +55,8 @@ function createMapStateToProps() {
);
}
export default connect(createMapStateToProps)(ImportArtistFooter);
const mapDispatchToProps = {
onCancelLookupPress: cancelLookupArtist
};
export default connect(createMapStateToProps, mapDispatchToProps)(ImportArtistFooter);

View file

@ -10,12 +10,6 @@ class ImportArtistTable extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this._table = null;
}
componentDidMount() {
const {
unmappedFolders,
@ -101,22 +95,11 @@ class ImportArtistTable extends Component {
return;
}
});
// Forces the table to re-render if the selected state
// has changed otherwise it will be stale.
if (prevProps.selectedState !== selectedState && this._table) {
this._table.forceUpdateGrid();
}
}
//
// Control
setTableRef = (ref) => {
this._table = ref;
}
rowRenderer = ({ key, rowIndex, style }) => {
const {
rootFolderId,
@ -156,6 +139,7 @@ class ImportArtistTable extends Component {
showLanguageProfile,
showMetadataProfile,
scrollTop,
selectedState,
onSelectAllChange,
onScroll
} = this.props;
@ -166,7 +150,6 @@ class ImportArtistTable extends Component {
return (
<VirtualTable
ref={this.setTableRef}
items={items}
contentBody={contentBody}
isSmallScreen={isSmallScreen}
@ -183,6 +166,7 @@ class ImportArtistTable extends Component {
onSelectAllChange={onSelectAllChange}
/>
}
selectedState={selectedState}
onScroll={onScroll}
/>
);

View file

@ -66,6 +66,5 @@
.searchInput {
composes: text from 'Components/Form/TextInput.css';
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0;
}

View file

@ -5,6 +5,7 @@ import TetherComponent from 'react-tether';
import { icons, kinds } from 'Helpers/Props';
import Icon from 'Components/Icon';
import SpinnerIcon from 'Components/SpinnerIcon';
import FormInputButton from 'Components/Form/FormInputButton';
import Link from 'Components/Link/Link';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import TextInput from 'Components/Form/TextInput';
@ -99,6 +100,10 @@ class ImportArtistSelectArtist extends Component {
});
}
onRefreshPress = () => {
this.props.onSearchInputChange(this.state.term);
}
onArtistSelect = (foreignArtistId) => {
this.setState({ isOpen: false });
@ -116,7 +121,8 @@ class ImportArtistSelectArtist extends Component {
isPopulated,
error,
items,
queued
queued,
isLookingUpArtist
} = this.props;
const errorMessage = error &&
@ -137,7 +143,7 @@ class ImportArtistSelectArtist extends Component {
onPress={this.onPress}
>
{
queued && !isPopulated &&
isLookingUpArtist && queued && !isPopulated &&
<LoadingIndicator
className={styles.loading}
size={20}
@ -206,10 +212,7 @@ class ImportArtistSelectArtist extends Component {
<div className={styles.content}>
<div className={styles.searchContainer}>
<div className={styles.searchIconContainer}>
<SpinnerIcon
name={icons.SEARCH}
isSpinning={isFetching}
/>
<Icon name={icons.SEARCH} />
</div>
<TextInput
@ -218,6 +221,16 @@ class ImportArtistSelectArtist extends Component {
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}>
@ -253,6 +266,7 @@ ImportArtistSelectArtist.propTypes = {
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
queued: PropTypes.bool.isRequired,
isLookingUpArtist: PropTypes.bool.isRequired,
onSearchInputChange: PropTypes.func.isRequired,
onArtistSelect: PropTypes.func.isRequired
};

View file

@ -9,9 +9,13 @@ import ImportArtistSelectArtist from './ImportArtistSelectArtist';
function createMapStateToProps() {
return createSelector(
(state) => state.importArtist.isLookingUpArtist,
createImportArtistItemSelector(),
(item) => {
return item;
(isLookingUpArtist, item) => {
return {
isLookingUpArtist,
...item
};
}
);
}
@ -29,7 +33,8 @@ class ImportArtistSelectArtistConnector extends Component {
onSearchInputChange = (term) => {
this.props.queueLookupArtist({
name: this.props.id,
term
term,
topOfQueue: true
});
}