New: Bulk Select Quality Interactive Import

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
pull/908/head
Qstick 6 years ago
parent c673058a10
commit cf1210a7f9

@ -16,25 +16,21 @@
} }
.leftButtons, .leftButtons,
.centerButtons,
.rightButtons { .rightButtons {
display: flex; display: flex;
flex: 1 2 20%; flex: 1 0 50%;
flex-wrap: wrap; flex-wrap: wrap;
} }
.centerButtons {
justify-content: center;
flex: 2 1 60%;
}
.rightButtons { .rightButtons {
justify-content: flex-end; justify-content: flex-end;
} }
.importMode { .importMode,
.bulkSelect {
composes: select from '~Components/Form/SelectInput.css'; composes: select from '~Components/Form/SelectInput.css';
margin-right: 10px;
width: auto; width: auto;
} }
@ -45,7 +41,6 @@
@media only screen and (max-width: $breakpointSmall) { @media only screen and (max-width: $breakpointSmall) {
.footer { .footer {
.leftButtons, .leftButtons,
.centerButtons,
.rightButtons { .rightButtons {
flex-direction: column; flex-direction: column;
} }
@ -54,10 +49,6 @@
align-items: flex-start; align-items: flex-start;
} }
.centerButtons {
align-items: center;
}
.rightButtons { .rightButtons {
align-items: flex-end; align-items: flex-end;
} }

@ -20,6 +20,7 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalFooter from 'Components/Modal/ModalFooter'; import ModalFooter from 'Components/Modal/ModalFooter';
import Table from 'Components/Table/Table'; import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal'; import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal'; import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal';
import SelectAlbumReleaseModal from 'InteractiveImport/AlbumRelease/SelectAlbumReleaseModal'; import SelectAlbumReleaseModal from 'InteractiveImport/AlbumRelease/SelectAlbumReleaseModal';
@ -76,6 +77,17 @@ const filterExistingFilesOptions = {
NEW: 'new' NEW: 'new'
}; };
const importModeOptions = [
{ key: 'move', value: 'Move Files' },
{ key: 'copy', value: 'Copy Files' }
];
const SELECT = 'select';
const ARTIST = 'artist';
const ALBUM = 'album';
const ALBUM_RELEASE = 'albumRelease';
const QUALITY = 'quality';
const replaceExistingFilesOptions = { const replaceExistingFilesOptions = {
COMBINE: 'combine', COMBINE: 'combine',
DELETE: 'delete' DELETE: 'delete'
@ -95,9 +107,7 @@ class InteractiveImportModalContent extends Component {
lastToggled: null, lastToggled: null,
selectedState: {}, selectedState: {},
invalidRowsSelected: [], invalidRowsSelected: [],
isSelectArtistModalOpen: false, selectModalOpen: null,
isSelectAlbumModalOpen: false,
isSelectAlbumReleaseModalOpen: false,
albumsImported: [], albumsImported: [],
isConfirmImportModalOpen: false, isConfirmImportModalOpen: false,
showClearTracks: false, showClearTracks: false,
@ -204,16 +214,8 @@ class InteractiveImportModalContent extends Component {
this.props.onImportModeChange(value); this.props.onImportModeChange(value);
} }
onSelectArtistPress = () => { onSelectModalSelect = ({ value }) => {
this.setState({ isSelectArtistModalOpen: true }); this.setState({ selectModalOpen: value });
}
onSelectAlbumPress = () => {
this.setState({ isSelectAlbumModalOpen: true });
}
onSelectAlbumReleasePress = () => {
this.setState({ isSelectAlbumReleaseModalOpen: true });
} }
onClearTrackMappingPress = () => { onClearTrackMappingPress = () => {
@ -232,16 +234,8 @@ class InteractiveImportModalContent extends Component {
this.props.saveInteractiveImportItem({ id: this.getSelectedIds() }); this.props.saveInteractiveImportItem({ id: this.getSelectedIds() });
} }
onSelectArtistModalClose = () => { onSelectModalClose = () => {
this.setState({ isSelectArtistModalOpen: false }); this.setState({ selectModalOpen: null });
}
onSelectAlbumModalClose = () => {
this.setState({ isSelectAlbumModalOpen: false });
}
onSelectAlbumReleaseModalClose = () => {
this.setState({ isSelectAlbumReleaseModalOpen: false });
} }
onConfirmImportModalClose = () => { onConfirmImportModalClose = () => {
@ -280,9 +274,7 @@ class InteractiveImportModalContent extends Component {
allUnselected, allUnselected,
selectedState, selectedState,
invalidRowsSelected, invalidRowsSelected,
isSelectArtistModalOpen, selectModalOpen,
isSelectAlbumModalOpen,
isSelectAlbumReleaseModalOpen,
albumsImported, albumsImported,
isConfirmImportModalOpen, isConfirmImportModalOpen,
showClearTracks, showClearTracks,
@ -293,11 +285,20 @@ class InteractiveImportModalContent extends Component {
const selectedItem = selectedIds.length ? _.find(items, { id: selectedIds[0] }) : null; const selectedItem = selectedIds.length ? _.find(items, { id: selectedIds[0] }) : null;
const errorMessage = getErrorMessage(error, 'Unable to load manual import items'); const errorMessage = getErrorMessage(error, 'Unable to load manual import items');
const importModeOptions = [ const bulkSelectOptions = [
{ key: 'move', value: 'Move Files' }, { key: SELECT, value: 'Select...', disabled: true },
{ key: 'copy', value: 'Copy Files' } { key: ALBUM, value: 'Select Album' },
{ key: ALBUM_RELEASE, value: 'Select Album Release' },
{ key: QUALITY, value: 'Select Quality' }
]; ];
if (allowArtistChange) {
bulkSelectOptions.splice(1, 0, {
key: ARTIST,
value: 'Select Artist'
});
}
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader> <ModalHeader>
@ -429,41 +430,25 @@ class InteractiveImportModalContent extends Component {
<ModalFooter className={styles.footer}> <ModalFooter className={styles.footer}>
<div className={styles.leftButtons}> <div className={styles.leftButtons}>
{ {
!downloadId && showImportMode && !downloadId && showImportMode ?
<SelectInput <SelectInput
className={styles.importMode} className={styles.importMode}
name="importMode" name="importMode"
value={importMode} value={importMode}
values={importModeOptions} values={importModeOptions}
onChange={this.onImportModeChange} onChange={this.onImportModeChange}
/> /> :
} null
</div>
<div className={styles.centerButtons}>
{
allowArtistChange &&
<Button
onPress={this.onSelectArtistPress}
isDisabled={!selectedIds.length}
>
Select Artist
</Button>
} }
<Button <SelectInput
onPress={this.onSelectAlbumPress} className={styles.bulkSelect}
isDisabled={!selectedIds.length} name="select"
> value={SELECT}
Select Album values={bulkSelectOptions}
</Button>
<Button
onPress={this.onSelectAlbumReleasePress}
isDisabled={!selectedIds.length} isDisabled={!selectedIds.length}
> onChange={this.onSelectModalSelect}
Select Release />
</Button>
{ {
showClearTracks ? ( showClearTracks ? (
@ -505,23 +490,32 @@ class InteractiveImportModalContent extends Component {
</ModalFooter> </ModalFooter>
<SelectArtistModal <SelectArtistModal
isOpen={isSelectArtistModalOpen} isOpen={selectModalOpen === ARTIST}
ids={selectedIds} ids={selectedIds}
onModalClose={this.onSelectArtistModalClose} onModalClose={this.onSelectModalClose}
/> />
<SelectAlbumModal <SelectAlbumModal
isOpen={isSelectAlbumModalOpen} isOpen={selectModalOpen === ALBUM}
ids={selectedIds} ids={selectedIds}
artistId={selectedItem && selectedItem.artist && selectedItem.artist.id} artistId={selectedItem && selectedItem.artist && selectedItem.artist.id}
onModalClose={this.onSelectAlbumModalClose} onModalClose={this.onSelectModalClose}
/> />
<SelectAlbumReleaseModal <SelectAlbumReleaseModal
isOpen={isSelectAlbumReleaseModalOpen} isOpen={selectModalOpen === ALBUM_RELEASE}
importIdsByAlbum={_.chain(items).filter((x) => x.album).groupBy((x) => x.album.id).mapValues((x) => x.map((y) => y.id)).value()} importIdsByAlbum={_.chain(items).filter((x) => x.album).groupBy((x) => x.album.id).mapValues((x) => x.map((y) => y.id)).value()}
albums={_.chain(items).filter((x) => x.album).keyBy((x) => x.album.id).mapValues((x) => ({ matchedReleaseId: x.albumReleaseId, album: x.album })).values().value()} albums={_.chain(items).filter((x) => x.album).keyBy((x) => x.album.id).mapValues((x) => ({ matchedReleaseId: x.albumReleaseId, album: x.album })).values().value()}
onModalClose={this.onSelectAlbumReleaseModalClose} onModalClose={this.onSelectModalClose}
/>
<SelectQualityModal
isOpen={selectModalOpen === QUALITY}
ids={selectedIds}
qualityId={0}
proper={false}
real={false}
onModalClose={this.onSelectModalClose}
/> />
<ConfirmImportModal <ConfirmImportModal

@ -331,7 +331,7 @@ class InteractiveImportRow extends Component {
<SelectQualityModal <SelectQualityModal
isOpen={isSelectQualityModalOpen} isOpen={isSelectQualityModalOpen}
id={id} ids={[id]}
qualityId={quality ? quality.quality.id : 0} qualityId={quality ? quality.quality.id : 0}
proper={quality ? quality.revision.version > 1 : false} proper={quality ? quality.revision.version > 1 : false}
real={quality ? quality.revision.real > 0 : false} real={quality ? quality.revision.real > 0 : false}

@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import getQualities from 'Utilities/Quality/getQualities'; import getQualities from 'Utilities/Quality/getQualities';
import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions'; import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions'; import { updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import SelectQualityModalContent from './SelectQualityModalContent'; import SelectQualityModalContent from './SelectQualityModalContent';
function createMapStateToProps() { function createMapStateToProps() {
@ -30,8 +30,8 @@ function createMapStateToProps() {
} }
const mapDispatchToProps = { const mapDispatchToProps = {
fetchQualityProfileSchema, dispatchFetchQualityProfileSchema: fetchQualityProfileSchema,
updateInteractiveImportItem dispatchUpdateInteractiveImportItems: updateInteractiveImportItems
}; };
class SelectQualityModalContentConnector extends Component { class SelectQualityModalContentConnector extends Component {
@ -41,7 +41,7 @@ class SelectQualityModalContentConnector extends Component {
componentDidMount = () => { componentDidMount = () => {
if (!this.props.isPopulated) { if (!this.props.isPopulated) {
this.props.fetchQualityProfileSchema(); this.props.dispatchFetchQualityProfileSchema();
} }
} }
@ -57,8 +57,8 @@ class SelectQualityModalContentConnector extends Component {
real: real ? 1 : 0 real: real ? 1 : 0
}; };
this.props.updateInteractiveImportItem({ this.props.dispatchUpdateInteractiveImportItems({
id: this.props.id, ids: this.props.ids,
quality: { quality: {
quality, quality,
revision revision
@ -82,13 +82,13 @@ class SelectQualityModalContentConnector extends Component {
} }
SelectQualityModalContentConnector.propTypes = { SelectQualityModalContentConnector.propTypes = {
id: PropTypes.number.isRequired, ids: PropTypes.arrayOf(PropTypes.number).isRequired,
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired, isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchQualityProfileSchema: PropTypes.func.isRequired, dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
updateInteractiveImportItem: PropTypes.func.isRequired, dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };

@ -78,21 +78,22 @@ export const persistState = [
// //
// Actions Types // Actions Types
export const FETCH_INTERACTIVE_IMPORT_ITEMS = 'FETCH_INTERACTIVE_IMPORT_ITEMS'; export const FETCH_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/fetchInteractiveImportItems';
export const UPDATE_INTERACTIVE_IMPORT_ITEM = 'UPDATE_INTERACTIVE_IMPORT_ITEM'; export const SAVE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/saveInteractiveImportItem';
export const SAVE_INTERACTIVE_IMPORT_ITEM = 'SAVE_INTERACTIVE_IMPORT_ITEM'; export const SET_INTERACTIVE_IMPORT_SORT = 'interactiveImport/setInteractiveImportSort';
export const SET_INTERACTIVE_IMPORT_SORT = 'SET_INTERACTIVE_IMPORT_SORT'; export const UPDATE_INTERACTIVE_IMPORT_ITEM = 'interactiveImport/updateInteractiveImportItem';
export const CLEAR_INTERACTIVE_IMPORT = 'CLEAR_INTERACTIVE_IMPORT'; export const UPDATE_INTERACTIVE_IMPORT_ITEMS = 'interactiveImport/updateInteractiveImportItems';
export const ADD_RECENT_FOLDER = 'ADD_RECENT_FOLDER'; export const CLEAR_INTERACTIVE_IMPORT = 'interactiveImport/clearInteractiveImport';
export const REMOVE_RECENT_FOLDER = 'REMOVE_RECENT_FOLDER'; export const ADD_RECENT_FOLDER = 'interactiveImport/addRecentFolder';
export const SET_INTERACTIVE_IMPORT_MODE = 'SET_INTERACTIVE_IMPORT_MODE'; export const REMOVE_RECENT_FOLDER = 'interactiveImport/removeRecentFolder';
export const SET_INTERACTIVE_IMPORT_MODE = 'interactiveImport/setInteractiveImportMode';
export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'FETCH_INTERACTIVE_IMPORT_ALBUMS';
export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'SET_INTERACTIVE_IMPORT_ALBUMS_SORT'; export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/fetchInteractiveImportAlbums';
export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'CLEAR_INTERACTIVE_IMPORT_ALBUMS'; export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'interactiveImport/clearInteractiveImportAlbumsSort';
export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/clearInteractiveImportAlbums';
export const FETCH_INTERACTIVE_IMPORT_TRACKFILES = 'FETCH_INTERACTIVE_IMPORT_TRACKFILES';
export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'CLEAR_INTERACTIVE_IMPORT_TRACKFILES'; export const FETCH_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/fetchInteractiveImportTrackFiles';
export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/clearInteractiveImportTrackFiles';
// //
// Action Creators // Action Creators
@ -100,6 +101,7 @@ export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'CLEAR_INTERACTIVE_IMPORT_TRA
export const fetchInteractiveImportItems = createThunk(FETCH_INTERACTIVE_IMPORT_ITEMS); export const fetchInteractiveImportItems = createThunk(FETCH_INTERACTIVE_IMPORT_ITEMS);
export const setInteractiveImportSort = createAction(SET_INTERACTIVE_IMPORT_SORT); export const setInteractiveImportSort = createAction(SET_INTERACTIVE_IMPORT_SORT);
export const updateInteractiveImportItem = createAction(UPDATE_INTERACTIVE_IMPORT_ITEM); export const updateInteractiveImportItem = createAction(UPDATE_INTERACTIVE_IMPORT_ITEM);
export const updateInteractiveImportItems = createAction(UPDATE_INTERACTIVE_IMPORT_ITEMS);
export const saveInteractiveImportItem = createThunk(SAVE_INTERACTIVE_IMPORT_ITEM); export const saveInteractiveImportItem = createThunk(SAVE_INTERACTIVE_IMPORT_ITEM);
export const clearInteractiveImport = createAction(CLEAR_INTERACTIVE_IMPORT); export const clearInteractiveImport = createAction(CLEAR_INTERACTIVE_IMPORT);
export const addRecentFolder = createAction(ADD_RECENT_FOLDER); export const addRecentFolder = createAction(ADD_RECENT_FOLDER);
@ -177,6 +179,23 @@ export const reducers = createHandleActions({
return newState; return newState;
}, },
[UPDATE_INTERACTIVE_IMPORT_ITEMS]: (state, { payload }) => {
const ids = payload.ids;
const newState = Object.assign({}, state);
const items = [...newState.items];
ids.forEach((id) => {
const index = items.findIndex((item) => item.id === id);
const item = Object.assign({}, items[index], payload);
items.splice(index, 1, item);
});
newState.items = items;
return newState;
},
[ADD_RECENT_FOLDER]: function(state, { payload }) { [ADD_RECENT_FOLDER]: function(state, { payload }) {
const folder = payload.folder; const folder = payload.folder;
const recentFolder = { folder, lastUsed: moment().toISOString() }; const recentFolder = { folder, lastUsed: moment().toISOString() };

Loading…
Cancel
Save