New: Filter tracks by title or number in Manual Import

(cherry picked from commit 73ed5f6ee27a9de1844d6e7caf26f765b082fe21)
This commit is contained in:
Andrew Champion 2020-07-10 11:25:24 +01:00 committed by bakerboy448
parent 6adbbf81ed
commit 95089080f4
5 changed files with 151 additions and 57 deletions

View file

@ -197,7 +197,6 @@ class ArtistDetails extends Component {
overview, overview,
links, links,
images, images,
artistType,
alternateTitles, alternateTitles,
tags, tags,
isSaving, isSaving,

View file

@ -0,0 +1,51 @@
.modalBody {
composes: modalBody from '~Components/Modal/ModalBody.css';
display: flex;
flex: 1 1 auto;
flex-direction: column;
}
.filterInput {
composes: input from '~Components/Form/TextInput.css';
flex: 0 0 auto;
margin-bottom: 20px;
}
.scroller {
flex: 1 1 auto;
}
.footer {
composes: modalFooter from '~Components/Modal/ModalFooter.css';
display: flex;
justify-content: space-between;
overflow: hidden;
}
.path {
margin-right: 20px;
color: var(--dimColor);
}
.buttons {
display: flex;
}
@media only screen and (max-width: $breakpointSmall) {
.footer {
display: block;
}
.path {
margin-right: 0;
margin-bottom: 10px;
}
.buttons {
justify-content: space-between;
flex-grow: 1;
}
}

View file

@ -0,0 +1,12 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'buttons': string;
'filterInput': string;
'footer': string;
'modalBody': string;
'path': string;
'scroller': string;
}
export const cssExports: CssExports;
export default cssExports;

View file

@ -1,15 +1,17 @@
import _ from 'lodash'; import _ from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import TextInput from 'Components/Form/TextInput';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ModalBody from 'Components/Modal/ModalBody'; import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent'; import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter'; import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import Scroller from 'Components/Scroller/Scroller';
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 { kinds } from 'Helpers/Props'; import { kinds, scrollDirections } from 'Helpers/Props';
import ExpandingFileDetails from 'TrackFile/ExpandingFileDetails'; import ExpandingFileDetails from 'TrackFile/ExpandingFileDetails';
import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
@ -17,6 +19,7 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll'; import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected'; import toggleSelected from 'Utilities/Table/toggleSelected';
import SelectTrackRow from './SelectTrackRow'; import SelectTrackRow from './SelectTrackRow';
import styles from './SelectTrackModalContent.css';
const columns = [ const columns = [
{ {
@ -65,11 +68,12 @@ class SelectTrackModalContent extends Component {
this.state = { this.state = {
allSelected: false, allSelected: false,
allUnselected: false, allUnselected: false,
filter: '',
lastToggled: null, lastToggled: null,
selectedState: init selectedState: init
}; };
props.onSortPress( props.sortKey, props.sortDirection ); props.onSortPress(props.sortKey, props.sortDirection);
} }
// //
@ -82,6 +86,10 @@ class SelectTrackModalContent extends Component {
// //
// Listeners // Listeners
onFilterChange = ({ value }) => {
this.setState({ filter: value.toLowerCase() });
};
onSelectAllChange = ({ value }) => { onSelectAllChange = ({ value }) => {
this.setState(selectAll(this.state.selectedState, value)); this.setState(selectAll(this.state.selectedState, value));
}; };
@ -119,8 +127,10 @@ class SelectTrackModalContent extends Component {
const { const {
allSelected, allSelected,
allUnselected, allUnselected,
filter,
selectedState selectedState
} = this.state; } = this.state;
const filterTrackNumber = parseInt(filter);
const errorMessage = getErrorMessage(error, 'Unable to load tracks'); const errorMessage = getErrorMessage(error, 'Unable to load tracks');
@ -141,63 +151,84 @@ class SelectTrackModalContent extends Component {
{translate('ManualImport')} - {translate('SelectTracks')} {translate('ManualImport')} - {translate('SelectTracks')}
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody
{ className={styles.modalBody}
isFetching && scrollDirection={scrollDirections.NONE}
<LoadingIndicator /> >
} <TextInput
className={styles.filterInput}
{ placeholder={translate('FilterTracksByTitleOrNumber')}
error && name="filter"
<div>{errorMessage}</div> value={filter}
} autoFocus={true}
onChange={this.onFilterChange}
<ExpandingFileDetails
audioTags={audioTags}
filename={filename}
rejections={rejections}
isExpanded={false}
/> />
{ <Scroller
isPopulated && !!items.length && className={styles.scroller}
<Table autoFocus={false}
columns={selectAllEnabled ? columns : selectAllBlankColumn.concat(columns)} >
selectAll={selectAllEnabled} {
allSelected={allSelected} isFetching ? <LoadingIndicator /> : null
allUnselected={allUnselected} }
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
onSelectAllChange={this.onSelectAllChange}
>
<TableBody>
{
items.map((item) => {
return (
<SelectTrackRow
key={item.id}
id={item.id}
mediumNumber={item.mediumNumber}
trackNumber={item.absoluteTrackNumber}
title={item.title}
hasFile={item.hasFile}
importSelected={otherSelected.concat(currentSelected).includes(item.id)}
isDisabled={otherSelected.includes(item.id)}
isSelected={selectedState[item.id]}
onSelectedChange={this.onSelectedChange}
/>
);
})
}
</TableBody>
</Table>
}
{ {
isPopulated && !items.length && error ? <div>{errorMessage}</div> : null
'No tracks were found for the selected album' }
}
<ExpandingFileDetails
audioTags={audioTags}
filename={filename}
rejections={rejections}
isExpanded={false}
/>
{
isPopulated && !!items.length ?
<Table
columns={selectAllEnabled ? columns : selectAllBlankColumn.concat(columns)}
selectAll={selectAllEnabled}
allSelected={allSelected}
allUnselected={allUnselected}
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
onSelectAllChange={this.onSelectAllChange}
>
<TableBody>
{
items.map((item) => {
return item.title.toLowerCase().includes(filter) ||
item.absoluteTrackNumber === filterTrackNumber ||
item.trackNumber === filterTrackNumber ?
(
<SelectTrackRow
key={item.id}
id={item.id}
mediumNumber={item.mediumNumber}
trackNumber={item.absoluteTrackNumber}
title={item.title}
hasFile={item.hasFile}
importSelected={otherSelected.concat(currentSelected).includes(item.id)}
isDisabled={otherSelected.includes(item.id)}
isSelected={selectedState[item.id]}
onSelectedChange={this.onSelectedChange}
/>
) :
null;
})
}
</TableBody>
</Table> :
null
}
{
isPopulated && !items.length ?
translate('NoTracksFoundForSelectedAlbum') :
null
}
</Scroller>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>

View file

@ -830,6 +830,7 @@
"NoMediumInformation": "No medium information is available.", "NoMediumInformation": "No medium information is available.",
"NoMinimumForAnyDuration": "No minimum for any duration", "NoMinimumForAnyDuration": "No minimum for any duration",
"NoMissingItems": "No missing items", "NoMissingItems": "No missing items",
"NoTracksFoundForSelectedAlbum": "No tracks were found for the selected album",
"NoResultsFound": "No results found", "NoResultsFound": "No results found",
"NoTagsHaveBeenAddedYet": "No tags have been added yet", "NoTagsHaveBeenAddedYet": "No tags have been added yet",
"NoTracksInThisMedium": "No tracks in this medium", "NoTracksInThisMedium": "No tracks in this medium",