Mass Editor size and options

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
pull/1807/head
Qstick 3 years ago
parent adc47c51e0
commit 796eba82ac

@ -6,10 +6,13 @@ import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { align, sortDirections } from 'Helpers/Props';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import { align, icons, sortDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
@ -20,52 +23,6 @@ import ArtistEditorRowConnector from './ArtistEditorRowConnector';
import RetagArtistModal from './AudioTags/RetagArtistModal';
import OrganizeArtistModal from './Organize/OrganizeArtistModal';
function getColumns(showMetadataProfile) {
return [
{
name: 'status',
isSortable: true,
isVisible: true
},
{
name: 'sortName',
label: 'Name',
isSortable: true,
isVisible: true
},
{
name: 'qualityProfileId',
label: 'Quality Profile',
isSortable: true,
isVisible: true
},
{
name: 'metadataProfileId',
label: 'Metadata Profile',
isSortable: true,
isVisible: showMetadataProfile
},
{
name: 'albumFolder',
label: 'Album Folder',
isSortable: true,
isVisible: true
},
{
name: 'path',
label: 'Path',
isSortable: true,
isVisible: true
},
{
name: 'tags',
label: 'Tags',
isSortable: false,
isVisible: true
}
];
}
class ArtistEditor extends Component {
//
@ -80,8 +37,7 @@ class ArtistEditor extends Component {
lastToggled: null,
selectedState: {},
isOrganizingArtistModalOpen: false,
isRetaggingArtistModalOpen: false,
columns: getColumns(props.showMetadataProfile)
isRetaggingArtistModalOpen: false
};
}
@ -161,6 +117,7 @@ class ArtistEditor extends Component {
error,
totalItems,
items,
columns,
selectedFilterKey,
filters,
customFilters,
@ -172,7 +129,7 @@ class ArtistEditor extends Component {
deleteError,
isOrganizingArtist,
isRetaggingArtist,
showMetadataProfile,
onTableOptionChange,
onSortPress,
onFilterSelect
} = this.props;
@ -180,8 +137,7 @@ class ArtistEditor extends Component {
const {
allSelected,
allUnselected,
selectedState,
columns
selectedState
} = this.state;
const selectedArtistIds = this.getSelectedIds();
@ -191,6 +147,18 @@ class ArtistEditor extends Component {
<PageToolbar>
<PageToolbarSection />
<PageToolbarSection alignContent={align.RIGHT}>
<TableOptionsModalWrapper
columns={columns}
onTableOptionChange={onTableOptionChange}
>
<PageToolbarButton
label="Options"
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
<PageToolbarSeparator />
<FilterMenu
alignMenu={align.RIGHT}
selectedFilterKey={selectedFilterKey}
@ -261,7 +229,8 @@ class ArtistEditor extends Component {
deleteError={deleteError}
isOrganizingArtist={isOrganizingArtist}
isRetaggingArtist={isRetaggingArtist}
showMetadataProfile={showMetadataProfile}
columns={columns}
showMetadataProfile={columns.find((column) => column.name === 'metadataProfileId').isVisible}
onSaveSelected={this.onSaveSelected}
onOrganizeArtistPress={this.onOrganizeArtistPress}
onRetagArtistPress={this.onRetagArtistPress}
@ -290,6 +259,7 @@ ArtistEditor.propTypes = {
error: PropTypes.object,
totalItems: PropTypes.number.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all),
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
@ -301,7 +271,7 @@ ArtistEditor.propTypes = {
deleteError: PropTypes.object,
isOrganizingArtist: PropTypes.bool.isRequired,
isRetaggingArtist: PropTypes.bool.isRequired,
showMetadataProfile: PropTypes.bool.isRequired,
onTableOptionChange: PropTypes.func.isRequired,
onSortPress: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired,
onSaveSelected: PropTypes.func.isRequired

@ -3,7 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { saveArtistEditor, setArtistEditorFilter, setArtistEditorSort } from 'Store/Actions/artistEditorActions';
import { saveArtistEditor, setArtistEditorFilter, setArtistEditorSort, setArtistEditorTableOption } from 'Store/Actions/artistEditorActions';
import { executeCommand } from 'Store/Actions/commandActions';
import { fetchRootFolders } from 'Store/Actions/settingsActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
@ -12,15 +12,13 @@ import ArtistEditor from './ArtistEditor';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.metadataProfiles,
createClientSideCollectionSelector('artist', 'artistEditor'),
createCommandExecutingSelector(commandNames.RENAME_ARTIST),
createCommandExecutingSelector(commandNames.RETAG_ARTIST),
(metadataProfiles, artist, isOrganizingArtist, isRetaggingArtist) => {
(artist, isOrganizingArtist, isRetaggingArtist) => {
return {
isOrganizingArtist,
isRetaggingArtist,
showMetadataProfile: metadataProfiles.items.length > 1,
...artist
};
}
@ -30,6 +28,7 @@ function createMapStateToProps() {
const mapDispatchToProps = {
dispatchSetArtistEditorSort: setArtistEditorSort,
dispatchSetArtistEditorFilter: setArtistEditorFilter,
dispatchSetArtistEditorTableOption: setArtistEditorTableOption,
dispatchSaveArtistEditor: saveArtistEditor,
dispatchFetchRootFolders: fetchRootFolders,
dispatchExecuteCommand: executeCommand
@ -55,6 +54,10 @@ class ArtistEditorConnector extends Component {
this.props.dispatchSetArtistEditorFilter({ selectedFilterKey });
}
onTableOptionChange = (payload) => {
this.props.dispatchSetArtistEditorTableOption(payload);
}
onSaveSelected = (payload) => {
this.props.dispatchSaveArtistEditor(payload);
}
@ -76,6 +79,7 @@ class ArtistEditorConnector extends Component {
onSortPress={this.onSortPress}
onFilterSelect={this.onFilterSelect}
onSaveSelected={this.onSaveSelected}
onTableOptionChange={this.onTableOptionChange}
/>
);
}
@ -84,6 +88,7 @@ class ArtistEditorConnector extends Component {
ArtistEditorConnector.propTypes = {
dispatchSetArtistEditorSort: PropTypes.func.isRequired,
dispatchSetArtistEditorFilter: PropTypes.func.isRequired,
dispatchSetArtistEditorTableOption: PropTypes.func.isRequired,
dispatchSaveArtistEditor: PropTypes.func.isRequired,
dispatchFetchRootFolders: PropTypes.func.isRequired,
dispatchExecuteCommand: PropTypes.func.isRequired

@ -143,7 +143,7 @@ class ArtistEditorFooter extends Component {
isDeleting,
isOrganizingArtist,
isRetaggingArtist,
showMetadataProfile,
columns,
onOrganizeArtistPress,
onRetagArtistPress
} = this.props;
@ -190,71 +190,110 @@ class ArtistEditorFooter extends Component {
/>
</div>
<div className={styles.inputContainer}>
<ArtistEditorFooterLabel
label="Quality Profile"
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
/>
<QualityProfileSelectInputConnector
name="qualityProfileId"
value={qualityProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
{
showMetadataProfile &&
<div className={styles.inputContainer}>
<ArtistEditorFooterLabel
label="Metadata Profile"
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
/>
<MetadataProfileSelectInputConnector
name="metadataProfileId"
value={metadataProfileId}
includeNoChange={true}
includeNone={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'qualityProfileId') {
return (
<div
key={name}
className={styles.inputContainer}
>
<ArtistEditorFooterLabel
label="Quality Profile"
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
/>
<QualityProfileSelectInputConnector
name="qualityProfileId"
value={qualityProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
);
}
if (name === 'metadataProfileId') {
return (
<div
key={name}
className={styles.inputContainer}
>
<ArtistEditorFooterLabel
label="Metadata Profile"
isSaving={isSaving && metadataProfileId !== NO_CHANGE}
/>
<MetadataProfileSelectInputConnector
name="metadataProfileId"
value={metadataProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
);
}
if (name === 'albumFolder') {
return (
<div
key={name}
className={styles.inputContainer}
>
<ArtistEditorFooterLabel
label="Album Folder"
isSaving={isSaving && albumFolder !== NO_CHANGE}
/>
<SelectInput
name="albumFolder"
value={albumFolder}
values={albumFolderOptions}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
);
}
if (name === 'path') {
return (
<div
key={name}
className={styles.inputContainer}
>
<ArtistEditorFooterLabel
label="Root Folder"
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
/>
<RootFolderSelectInputConnector
name="rootFolderPath"
value={rootFolderPath}
includeNoChange={true}
isDisabled={!selectedCount}
selectedValueOptions={{ includeFreeSpace: false }}
onChange={this.onInputChange}
/>
</div>
);
}
return null;
})
}
<div className={styles.inputContainer}>
<ArtistEditorFooterLabel
label="Album Folder"
isSaving={isSaving && albumFolder !== NO_CHANGE}
/>
<SelectInput
name="albumFolder"
value={albumFolder}
values={albumFolderOptions}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
<div className={styles.inputContainer}>
<ArtistEditorFooterLabel
label="Root Folder"
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
/>
<RootFolderSelectInputConnector
name="rootFolderPath"
value={rootFolderPath}
includeNoChange={true}
isDisabled={!selectedCount}
selectedValueOptions={{ includeFreeSpace: false }}
onChange={this.onInputChange}
/>
</div>
<div className={styles.buttonContainer}>
<div className={styles.buttonContainerContent}>
<ArtistEditorFooterLabel
@ -342,6 +381,7 @@ ArtistEditorFooter.propTypes = {
isOrganizingArtist: PropTypes.bool.isRequired,
isRetaggingArtist: PropTypes.bool.isRequired,
showMetadataProfile: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onSaveSelected: PropTypes.func.isRequired,
onOrganizeArtistPress: PropTypes.func.isRequired,
onRetagArtistPress: PropTypes.func.isRequired

@ -1,4 +1,3 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ArtistNameLink from 'Artist/ArtistNameLink';
@ -8,6 +7,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import TagListConnector from 'Components/TagListConnector';
import formatBytes from 'Utilities/Number/formatBytes';
import styles from './ArtistEditorRow.css';
class ArtistEditorRow extends Component {
@ -35,6 +35,7 @@ class ArtistEditorRow extends Component {
qualityProfile,
albumFolder,
path,
statistics,
tags,
columns,
isSaving,
@ -51,50 +52,105 @@ class ArtistEditorRow extends Component {
onSelectedChange={onSelectedChange}
/>
<ArtistStatusCell
artistType={artistType}
monitored={monitored}
status={status}
isSaving={isSaving}
onMonitoredPress={onArtistMonitoredPress}
/>
<TableRowCell className={styles.title}>
<ArtistNameLink
foreignArtistId={foreignArtistId}
artistName={artistName}
/>
</TableRowCell>
<TableRowCell>
{qualityProfile.name}
</TableRowCell>
{
_.find(columns, { name: 'metadataProfileId' }).isVisible &&
<TableRowCell>
{metadataProfile.name}
</TableRowCell>
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'status') {
return (
<ArtistStatusCell
key={name}
artistType={artistType}
monitored={monitored}
status={status}
isSaving={isSaving}
onMonitoredPress={onArtistMonitoredPress}
/>
);
}
if (name === 'sortName') {
return (
<TableRowCell
key={name}
className={styles.title}
>
<ArtistNameLink
foreignArtistId={foreignArtistId}
artistName={artistName}
/>
</TableRowCell>
);
}
if (name === 'qualityProfileId') {
return (
<TableRowCell key={name}>
{qualityProfile.name}
</TableRowCell>
);
}
if (name === 'metadataProfileId') {
return (
<TableRowCell key={name}>
{metadataProfile.name}
</TableRowCell>
);
}
if (name === 'albumFolder') {
return (
<TableRowCell
key={name}
className={styles.albumFolder}
>
<CheckInput
name="albumFolder"
value={albumFolder}
isDisabled={true}
onChange={this.onAlbumFolderChange}
/>
</TableRowCell>
);
}
if (name === 'path') {
return (
<TableRowCell key={name}>
{path}
</TableRowCell>
);
}
if (name === 'sizeOnDisk') {
return (
<TableRowCell key={name}>
{formatBytes(statistics.sizeOnDisk)}
</TableRowCell>
);
}
if (name === 'tags') {
return (
<TableRowCell key={name}>
<TagListConnector
tags={tags}
/>
</TableRowCell>
);
}
return null;
})
}
<TableRowCell className={styles.albumFolder}>
<CheckInput
name="albumFolder"
value={albumFolder}
isDisabled={true}
onChange={this.onAlbumFolderChange}
/>
</TableRowCell>
<TableRowCell>
{path}
</TableRowCell>
<TableRowCell>
<TagListConnector
tags={tags}
/>
</TableRowCell>
</TableRow>
);
}
@ -111,6 +167,7 @@ ArtistEditorRow.propTypes = {
qualityProfile: PropTypes.object.isRequired,
albumFolder: PropTypes.bool.isRequired,
path: PropTypes.string.isRequired,
statistics: PropTypes.object.isRequired,
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
isSaving: PropTypes.bool.isRequired,

@ -8,6 +8,7 @@ import { set, updateItem } from './baseActions';
import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
//
// Variables
@ -30,6 +31,58 @@ export const defaultState = {
filters,
filterPredicates,
columns: [
{
name: 'status',
columnLabel: 'Status',
isSortable: true,
isVisible: true,
isModifiable: false
},
{
name: 'sortName',
label: 'Name',
isSortable: true,
isVisible: true
},
{
name: 'qualityProfileId',
label: 'Quality Profile',
isSortable: true,
isVisible: true
},
{
name: 'metadataProfileId',
label: 'Metadata Profile',
isSortable: true,
isVisible: false
},
{
name: 'albumFolder',
label: 'Album Folder',
isSortable: true,
isVisible: true
},
{
name: 'path',
label: 'Path',
isSortable: true,
isVisible: true
},
{
name: 'sizeOnDisk',
label: 'Size on Disk',
isSortable: true,
isVisible: false
},
{
name: 'tags',
label: 'Tags',
isSortable: false,
isVisible: true
}
],
filterBuilderProps: [
{
name: 'monitored',
@ -65,6 +118,12 @@ export const defaultState = {
label: 'Root Folder Path',
type: filterBuilderTypes.EXACT
},
{
name: 'sizeOnDisk',
label: 'Size on Disk',
type: filterBuilderTypes.NUMBER,
valueType: filterBuilderValueTypes.BYTES
},
{
name: 'tags',
label: 'Tags',
@ -90,6 +149,7 @@ export const SET_ARTIST_EDITOR_SORT = 'artistEditor/setArtistEditorSort';
export const SET_ARTIST_EDITOR_FILTER = 'artistEditor/setArtistEditorFilter';
export const SAVE_ARTIST_EDITOR = 'artistEditor/saveArtistEditor';
export const BULK_DELETE_ARTIST = 'artistEditor/bulkDeleteArtist';
export const SET_ARTIST_EDITOR_TABLE_OPTION = 'artistEditor/setArtistEditorTableOption';
//
// Action Creators
@ -98,6 +158,7 @@ export const setArtistEditorSort = createAction(SET_ARTIST_EDITOR_SORT);
export const setArtistEditorFilter = createAction(SET_ARTIST_EDITOR_FILTER);
export const saveArtistEditor = createThunk(SAVE_ARTIST_EDITOR);
export const bulkDeleteArtist = createThunk(BULK_DELETE_ARTIST);
export const setArtistEditorTableOption = createAction(SET_ARTIST_EDITOR_TABLE_OPTION);
//
// Action Handlers
@ -181,6 +242,7 @@ export const actionHandlers = handleThunks({
export const reducers = createHandleActions({
[SET_ARTIST_EDITOR_TABLE_OPTION]: createSetTableOptionReducer(section),
[SET_ARTIST_EDITOR_SORT]: createSetClientSideCollectionSortReducer(section),
[SET_ARTIST_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(section)

Loading…
Cancel
Save